]> git.vomp.tv Git - vompclient.git/blob - teletextdecodervbiebu.cc
Rewritten vomp discovery protocol
[vompclient.git] / teletextdecodervbiebu.cc
1 /*
2     Copyright 2008 Marten Richter
3
4     This file is part of VOMP.
5
6     VOMP is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     VOMP is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with VOMP; if not, write to the Free Software
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 */
20
21
22 /* Portions from vdr osdteletext plugin "txtrender.c": */
23 /***************************************************************************
24  *                                                                         *
25  *   txtrender.c - Teletext display abstraction and teletext code          *
26  *                 renderer                                                *
27  *                                                                         *
28  *   This program is free software; you can redistribute it and/or modify  *
29  *   it under the terms of the GNU General Public License as published by  *
30  *   the Free Software Foundation; either version 2 of the License, or     *
31  *   (at your option) any later version.                                   *
32  *                                                                         *
33  *   Changelog:                                                            *
34  *     2005-03    initial version (c) Udo Richter                          *
35  *                                                                         *
36  ***************************************************************************/
37
38 #include "teletextdecodervbiebu.h"
39 #include "teletxt/tables.h"
40 #include "message.h"
41 #include "command.h"
42 #include "video.h"
43
44 #ifdef WIN32
45 #include <windows.h>
46 #endif
47
48 TeletextDecoderVBIEBU::TeletextDecoderVBIEBU()
49 {
50     selectedpage=0x100;
51     ourpage=false;
52     flags=lang=0; 
53   
54     CleanPage();
55     FirstG0CodePage=0;
56     SecondG0CodePage=0;
57     dirty=false;
58     txtview=NULL;
59     firstlineupdate=0;
60     gotcha=false;
61     char digits[]={'1','0','0'};
62     setKeyinDigits(digits,false);
63     isrecording=false;
64     for (int i=0;i<10;i++) record_pages[i]=-1;
65     
66
67 }
68
69 TeletextDecoderVBIEBU::~TeletextDecoderVBIEBU()
70 {
71
72
73 }
74
75 long long TeletextDecoderVBIEBU::SetStartOffset(long long curreftime, bool *rsync)
76 {
77   return 0;  
78 }
79
80 void TeletextDecoderVBIEBU::ResetTimeOffsets()
81 {
82
83 }
84
85
86
87 void TeletextDecoderVBIEBU::ResetDecoder()
88 {
89     gotcha=false;
90     ourpage=false;
91     firstlineupdate=0;
92     selectedpage=0x100;
93     CleanPage();
94     char digits[]={'1','0','0'};
95     setKeyinDigits(digits,false);
96     for (int i=0;i<10;i++) record_pages[i]=-1;
97   
98 }
99
100 void TeletextDecoderVBIEBU::setPage(unsigned int newpage)
101 {
102     selectedpage=newpage;
103     gotcha=false;
104     ourpage=false;
105     firstlineupdate=0;
106     for (int i=0;i<25;i++) {
107         memset(curpage[i],0,40);
108     }
109 }
110
111 void TeletextDecoderVBIEBU::CleanPage()
112 {
113     int x,y;
114     for (int i=0;i<25;i++) {
115         memset(curpage[i],0,40);
116     }
117     for (y=0;y<25;y++) {
118         for (x=0;x<40;x++) {
119             cTeletextChar c;
120             c.SetFGColor(ttcWhite);
121             c.SetBGColor(ttcBlack);
122             c.SetCharset(CHARSET_LATIN_G0);
123             c.SetChar(' ');
124             if (flags&0x60) {
125                 c.SetBoxedOut(true);    
126             }
127             setChar(x,y,c);
128         }
129     } 
130
131 }
132 void TeletextDecoderVBIEBU::setKeyinDigits(char digits[3],bool inKeying)
133 {
134     int x;
135     inkeying=inKeying;
136     for (x=0;x<3;x++) {
137         cTeletextChar c;
138         c.SetFGColor(ttcWhite);
139         c.SetBGColor(ttcBlack);
140         c.SetCharset(CHARSET_LATIN_G0);
141         c.SetChar(digits[x]);
142         if (flags&0x60) {
143             c.SetBoxedOut(true);    
144         }
145         setChar(x+3,0,c);
146         keyindigits[x]=digits[x];
147     }
148 }
149
150 void TeletextDecoderVBIEBU::PrepareMediaSample(const MediaPacketList& mplist, UINT samplepos)
151 {
152     mediapacket = mplist.front();
153 }
154
155 static ULLONG PTSDifference(ULLONG pts1, ULLONG pts2)
156 {
157   // Assume pts1, pts2 < 2^33; calculate pts1 - pts2
158   if (pts1 > pts2)
159     return pts1 - pts2;
160   else
161     return (1LL<<33) + pts1 - pts2;
162 }
163
164 UINT TeletextDecoderVBIEBU::DeliverMediaSample(UCHAR* buffer, UINT *samplepos)
165 {
166    if (mediapacket.type != MPTYPE_TELETEXT)
167    {
168        *samplepos= 0;
169        return 1; //Skip it! 
170    }
171    unsigned int headerstrip;
172    unsigned char txtdata[43];
173    headerstrip=buffer[mediapacket.pos_buffer+8]+9;
174    //headerstrip+=4; //substream id
175    unsigned int datapos=0;
176    unsigned int datatype=buffer[mediapacket.pos_buffer+headerstrip+0];
177    //Chris should we use here the pts data from mediapacket ?
178    // in this case the data fields should also be added in mediamvp
179    if (mediapacket.synched)
180     { // An entry exists in the work list
181         ULLONG nowPTS = Video::getInstance()->getCurrentTimestamp();
182        if (PTSDifference(mediapacket.pts, nowPTS) >= 1200*90000) {
183             *samplepos=0;
184             return 1;//bad data skip it
185         }
186         if (nowPTS < (mediapacket.pts-4000)  ) {
187             *samplepos=0;
188             return 0;
189         } 
190    }
191    
192    if (( datatype>=0x10 && datatype <=0x1F) ||
193        ( datatype>=0x99 && datatype <=0x9B)) {
194        //EBU VBI DATA
195        datapos++;
196        while (datapos< (mediapacket.length-headerstrip)) {
197            unsigned int unit_id=buffer[mediapacket.pos_buffer+headerstrip+datapos];
198            datapos++;
199            unsigned int unit_length=buffer[mediapacket.pos_buffer+headerstrip+datapos];
200            datapos++;
201            switch (unit_id) {
202                case 0x02:
203                case 0x03: {//Teletext with and without subtitles
204                    //call teletext decoder
205                    unsigned int field=buffer[mediapacket.pos_buffer+headerstrip+datapos];
206
207                    if ((datapos+44)< (mediapacket.length-headerstrip)) {
208                        for (int i=0;i<42;i++) {
209                            txtdata[i]=invtab[buffer[mediapacket.pos_buffer+headerstrip+datapos+2+i]];
210                        }
211                        DecodeTeletext(txtdata,field);
212                    }
213                           }break;
214                case  0xC0: //inverted Teletext
215                    //call teletext decoder
216                break;
217                
218                case 0xC3: //VPS
219                    //what to do with this, in the moment we do not need it
220                break;
221                case 0xC4: //WSS
222                    //what to do with this, in the moment we do not need it
223                break;
224                case 0xC5: //CC
225                    //what to do with this, in the moment we do not need it
226                break;
227                case 0xC6: //monochrome 4:2:2 samples, what is that? 
228                    //what to do with this, in the moment we do not need it
229                break;
230                default:
231                // case 0x00,0x01,0x04..0x7f,0x80..0xbf,0xc1,0xc2,0xc7..0xfe,0xff: //discard
232                    //discard
233                break;
234            };
235            datapos+=unit_length;
236        //    while (buffer[mediapacket.pos_buffer+headerstrip+datapos]==0xFF &&(datapos< (mediapacket.length-headerstrip))) {
237         //       datapos++; //stuffing bytes
238          //  }
239
240            
241        }
242        *samplepos=0;
243        return 1;
244
245
246    } else {
247            *samplepos= 0;
248            return 1; //Skip it! and discard data   
249    }
250
251    return 0;
252
253 }
254 // This part is inspired by the vdr-plugin-osdteletext of Udo Richter and Marcel Wiesweg
255 void TeletextDecoderVBIEBU::DecodeTeletext(const UCHAR* buffer, unsigned int field) //needs to be exactly 42 byte long!!
256 {
257    UCHAR hdrbuf[5];
258    for (int i=0;i<5;i++) hdrbuf[i]=(unhamtab[buffer[2*i]]&0xF) | ((unhamtab[buffer[2*i+1]]&0xF)<< 4);
259    int header=hdrbuf[0];
260    int magazin=header & 0x7;
261    int line = (header>>3) & 0x1f;
262    if (magazin==0) magazin=8;
263    if (line==0)
264    {
265        if (ourpage) {
266            gotcha=true;
267            inkeying=false;
268            RenderTeletextCode(false);
269        } else {
270            RenderTeletextCode(true);
271        }
272        int pagenumber=hdrbuf[1];
273        int pagemagazin=magazin<<8 | pagenumber;
274        int pagesubnumber=(hdrbuf[2]) || ((hdrbuf[3]<<8) & 0x3f7f);
275
276        if (pagemagazin == selectedpage) ourpage=true;
277        else ourpage=false;
278        if (isrecording) {
279            for (int i=0;i<10;i++) {
280                if (pagemagazin==record_pages[i]) break;
281                if (record_pages[i]==-1) {
282                    record_pages[i]=pagemagazin;
283                    break;
284                }
285            }
286        }
287        if (hdrbuf[3] &0x80) { //This is a subtitle
288             for (int i=0;i<10;i++) {
289                if (pagemagazin==record_pages[i]) break;
290                if (record_pages[i]==-1) {
291                    record_pages[i]=pagemagazin;
292                    break;
293                }
294            }
295        }
296
297
298        if (ourpage) {
299            lang=((hdrbuf[4]>>5) & 0x07);
300            flags=hdrbuf[2] & 0x80;
301            flags|=(hdrbuf[3]&0x40)|((hdrbuf[3]>>2)&0x20); //??????
302            flags|=((hdrbuf[4]<<4)&0x10)|((hdrbuf[4]<<2)&0x08)|(hdrbuf[4]&0x04)|((hdrbuf[4]>>1)&0x02)|((hdrbuf[4]>>4)&0x01);
303            for (int i=0;i<25;i++) {
304                memset(curpage[i],0,40);
305             }
306        }
307        
308        memcpy(curpage[line],buffer+2,40);
309    } else if (ourpage && (line<=25)) {
310        memcpy(curpage[line],buffer+2,40);
311        
312    }
313 }
314
315
316 /* from osdteletext plugin: (slightly adapted for vomp)*/
317 // Font tables
318
319 // teletext uses 7-bit numbers to identify a font set.
320 // There are three font sets involved:
321 // Primary G0, Secondary G0, and G2 font set.
322
323 // Font tables are organized in blocks of 8 fonts:
324
325 enumCharsets FontBlockG0_0000[8] = {
326     CHARSET_LATIN_G0_EN,
327     CHARSET_LATIN_G0_DE,
328     CHARSET_LATIN_G0_SV_FI,
329     CHARSET_LATIN_G0_IT,
330     CHARSET_LATIN_G0_FR,
331     CHARSET_LATIN_G0_PT_ES,
332     CHARSET_LATIN_G0_CZ_SK,
333     CHARSET_LATIN_G0
334 };
335
336 enumCharsets FontBlockG2Latin[8]={
337     CHARSET_LATIN_G2,
338     CHARSET_LATIN_G2,
339     CHARSET_LATIN_G2,
340     CHARSET_LATIN_G2,
341     CHARSET_LATIN_G2,
342     CHARSET_LATIN_G2,
343     CHARSET_LATIN_G2,
344     CHARSET_LATIN_G2
345 };
346
347 enumCharsets FontBlockG0_0001[8] = {
348     CHARSET_LATIN_G0_PL,
349     CHARSET_LATIN_G0_DE,
350     CHARSET_LATIN_G0_SV_FI,
351     CHARSET_LATIN_G0_IT,
352     CHARSET_LATIN_G0_FR,
353     CHARSET_LATIN_G0,
354     CHARSET_LATIN_G0_CZ_SK,
355     CHARSET_LATIN_G0
356 };
357
358 enumCharsets FontBlockG0_0010[8] = {
359     CHARSET_LATIN_G0_EN,
360     CHARSET_LATIN_G0_DE,
361     CHARSET_LATIN_G0_SV_FI,
362     CHARSET_LATIN_G0_IT,
363     CHARSET_LATIN_G0_FR,
364     CHARSET_LATIN_G0_PT_ES,
365     CHARSET_LATIN_G0_TR,
366     CHARSET_LATIN_G0
367 };
368
369
370 enumCharsets FontBlockG0_0011[8] = {
371     CHARSET_LATIN_G0,
372     CHARSET_LATIN_G0,
373     CHARSET_LATIN_G0,
374     CHARSET_LATIN_G0,
375     CHARSET_LATIN_G0,
376     CHARSET_LATIN_G0_SR_HR_SL,
377     CHARSET_LATIN_G0,
378     CHARSET_LATIN_G0_RO
379 };
380
381 enumCharsets FontBlockG0_0100[8] = {
382     CHARSET_CYRILLIC_G0_SR_HR,
383     CHARSET_LATIN_G0_DE,
384     CHARSET_LATIN_G0_EE,
385     CHARSET_LATIN_G0_LV_LT,
386     CHARSET_CYRILLIC_G0_RU_BG,
387     CHARSET_CYRILLIC_G0_UK,
388     CHARSET_LATIN_G0_CZ_SK,
389     CHARSET_INVALID
390 };
391
392 enumCharsets FontBlockG2_0100[8] = {
393     CHARSET_CYRILLIC_G2,
394     CHARSET_LATIN_G2,
395     CHARSET_LATIN_G2,
396     CHARSET_LATIN_G2,
397     CHARSET_CYRILLIC_G2,
398     CHARSET_CYRILLIC_G2,
399     CHARSET_LATIN_G2,
400     CHARSET_INVALID
401 };
402
403 enumCharsets FontBlockG0_0110[8] = {
404     CHARSET_INVALID,
405     CHARSET_INVALID,
406     CHARSET_INVALID,
407     CHARSET_INVALID,
408     CHARSET_INVALID,
409     CHARSET_INVALID,
410     CHARSET_LATIN_G0_TR,
411     CHARSET_GREEK_G0
412 };
413
414 enumCharsets FontBlockG2_0110[8] = {
415     CHARSET_INVALID,
416     CHARSET_INVALID,
417     CHARSET_INVALID,
418     CHARSET_INVALID,
419     CHARSET_INVALID,
420     CHARSET_INVALID,
421     CHARSET_LATIN_G2,
422     CHARSET_GREEK_G2
423 };
424
425 enumCharsets FontBlockG0_1000[8] = {
426     CHARSET_LATIN_G0_EN,
427     CHARSET_INVALID,
428     CHARSET_INVALID,
429     CHARSET_INVALID,
430     CHARSET_LATIN_G0_FR,
431     CHARSET_INVALID,
432     CHARSET_INVALID,
433     CHARSET_ARABIC_G0
434 };
435
436 enumCharsets FontBlockG2_1000[8] = {
437     CHARSET_ARABIC_G2,
438     CHARSET_INVALID,
439     CHARSET_INVALID,
440     CHARSET_INVALID,
441     CHARSET_ARABIC_G2,
442     CHARSET_INVALID,
443     CHARSET_INVALID,
444     CHARSET_ARABIC_G2
445 };
446
447 enumCharsets FontBlockG0_1010[8] = {
448     CHARSET_INVALID,
449     CHARSET_INVALID,
450     CHARSET_INVALID,
451     CHARSET_INVALID,
452     CHARSET_INVALID,
453     CHARSET_HEBREW_G0,
454     CHARSET_INVALID,
455     CHARSET_ARABIC_G0,
456 };
457
458 enumCharsets FontBlockG2_1010[8] = {
459     CHARSET_INVALID,
460     CHARSET_INVALID,
461     CHARSET_INVALID,
462     CHARSET_INVALID,
463     CHARSET_INVALID,
464     CHARSET_ARABIC_G2,
465     CHARSET_INVALID,
466     CHARSET_ARABIC_G2,
467 };
468
469 enumCharsets FontBlockInvalid[8] = {
470     CHARSET_INVALID,
471     CHARSET_INVALID,
472     CHARSET_INVALID,
473     CHARSET_INVALID,
474     CHARSET_INVALID,
475     CHARSET_INVALID,
476     CHARSET_INVALID,
477     CHARSET_INVALID
478 };
479
480
481
482 // The actual font table definition:
483 // Split the 7-bit number into upper 4 and lower 3 bits,
484 // use upper 4 bits for outer array,
485 // use lower 3 bits for inner array
486
487 struct structFontBlock {
488     enumCharsets *G0Block;
489     enumCharsets *G2Block;
490 };
491     
492 structFontBlock FontTable[16] = {
493     { FontBlockG0_0000, FontBlockG2Latin }, // 0000 block
494     { FontBlockG0_0001, FontBlockG2Latin }, // 0001 block
495     { FontBlockG0_0010, FontBlockG2Latin }, // 0010 block
496     { FontBlockG0_0011, FontBlockG2Latin }, // 0011 block
497     { FontBlockG0_0100, FontBlockG2_0100 }, // 0100 block
498     { FontBlockInvalid, FontBlockInvalid }, // 0101 block
499     { FontBlockG0_0110, FontBlockG2_0110 }, // 0110 block
500     { FontBlockInvalid, FontBlockInvalid }, // 0111 block
501     { FontBlockG0_1000, FontBlockG2_1000 }, // 1000 block
502     { FontBlockInvalid, FontBlockInvalid }, // 1001 block
503     { FontBlockG0_1010, FontBlockG2_1010 }, // 1010 block
504     { FontBlockInvalid, FontBlockInvalid }, // 1011 block
505     { FontBlockInvalid, FontBlockInvalid }, // 1100 block
506     { FontBlockInvalid, FontBlockInvalid }, // 1101 block
507     { FontBlockInvalid, FontBlockInvalid }, // 1110 block
508     { FontBlockInvalid, FontBlockInvalid }  // 1111 block
509 };
510
511 inline enumCharsets GetG0Charset(int codepage) {
512     return FontTable[codepage>>3].G0Block[codepage&7];
513 }
514 inline enumCharsets GetG2Charset(int codepage) {
515     return FontTable[codepage>>3].G2Block[codepage&7];
516 }
517
518 enum enumSizeMode {
519     // Possible size modifications of characters
520     sizeNormal,
521     sizeDoubleWidth,
522     sizeDoubleHeight,
523     sizeDoubleSize
524 };
525
526 void TeletextDecoderVBIEBU::RenderTeletextCode(bool renderfirstlineonly) {
527     int x,y;
528     bool EmptyNextLine=false;
529     // Skip one line, in case double height chars were/will be used
530
531     // Get code pages:
532     int LocalG0CodePage=(FirstG0CodePage & 0x78) 
533             | ((lang & 0x04)>>2) | (lang & 0x02) | ((lang & 0x01)<<2);
534     enumCharsets FirstG0=GetG0Charset(LocalG0CodePage);
535     enumCharsets SecondG0=GetG0Charset(SecondG0CodePage);
536     // Reserved for later use:
537     // enumCharsets FirstG2=GetG2Charset(LocalG0CodePage);
538     
539     for (y=0;y<24;(EmptyNextLine?y+=2:y++)) {
540         // Start of line: Set start of line defaults
541         
542         // Hold Mosaics mode: Remember last mosaic char/charset 
543         // for next spacing code
544         bool HoldMosaics=false;
545         unsigned char HoldMosaicChar=' ';
546         enumCharsets HoldMosaicCharset=FirstG0;
547
548         enumSizeMode Size=sizeNormal;
549         // Font size modification
550         bool SecondCharset=false;
551         // Use primary or secondary G0 charset
552         bool GraphicCharset=false;
553         // Graphics charset used?
554         bool SeparateGraphics=false;
555         // Use separated vs. contiguous graphics charset
556         bool NoNextChar=false;
557         // Skip display of next char, for double-width
558         EmptyNextLine=false;
559         // Skip next line, for double-height
560
561         cTeletextChar c;
562         // auto.initialized to everything off
563         c.SetFGColor(ttcWhite);
564         c.SetBGColor(ttcBlack);
565         c.SetCharset(FirstG0);
566         
567         if (y==0 && (flags&0x10)) {
568             if (!inkeying ) c.SetBoxedOut(true);
569             
570         }
571         if (flags&0x60) {
572            if (!(inkeying && y==0) ) c.SetBoxedOut(true);
573             
574         }
575         if (y==0) {
576             
577             for (x=0;x<8;x++) {
578                 cTeletextChar c2=c;
579                
580                 if (x>=3 && x<6){
581                     c2.SetChar(keyindigits[x-3]);
582
583                 }
584                 else
585                     c2.SetChar(' ');
586                 setChar(x,0,c2);
587             }
588         }
589
590         // Pre-scan for double-height and double-size codes
591         for (x=0;x<40;x++) {
592             if (y==0 && x<8) x=8;
593             if ((curpage[y][x] & 0x7f)==0x0D || (curpage[y][x] & 0x7f)==0x0F)
594                 EmptyNextLine=true;
595         }
596
597         // Move through line
598         for (x=0;x<40;x++) {
599             unsigned char ttc=curpage[y][x] & 0x7f;
600             // skip parity check
601
602             if (y==0 && x<8) continue;
603             if (y==0 && x<31 && renderfirstlineonly && gotcha && !inkeying) continue;
604             // no displayable data here...
605             
606 /*          // Debug only: Output line data and spacing codes
607             if (y==6) {
608                 if (ttc<0x20)
609                     printf("%s ",names[ttc]);
610                 else
611                     printf("%02x ",ttc);
612                 if (x==39) printf("\n");
613             }
614 */          
615             
616             // Handle all 'Set-At' spacing codes
617             switch (ttc) {
618             case 0x09: // Steady
619                 c.SetBlink(false);
620                 break;
621             case 0x0C: // Normal Size
622                 if (Size!=sizeNormal) {
623                     Size=sizeNormal;
624                     HoldMosaicChar=' ';
625                     HoldMosaicCharset=FirstG0;
626                 }                   
627                 break;
628             case 0x18: // Conceal
629                 c.SetConceal(true);
630                 break;
631             case 0x19: // Contiguous Mosaic Graphics
632                 SeparateGraphics=false;
633                 if (GraphicCharset)
634                     c.SetCharset(CHARSET_GRAPHICS_G1);
635                 break;
636             case 0x1A: // Separated Mosaic Graphics
637                 SeparateGraphics=true;
638                 if (GraphicCharset)
639                     c.SetCharset(CHARSET_GRAPHICS_G1_SEP);
640                 break;
641             case 0x1C: // Black Background
642                 c.SetBGColor(ttcBlack);
643                 break;
644             case 0x1D: // New Background
645                 c.SetBGColor(c.GetFGColor());
646                 break;
647             case 0x1E: // Hold Mosaic
648                 HoldMosaics=true;               
649                 break;
650             }
651
652             // temporary copy of character data:
653             cTeletextChar c2=c;
654             // c2 will be text character or space character or hold mosaic
655             // c2 may also have temporary flags or charsets
656             
657             if (ttc<0x20) {
658                 // Spacing code, display space or hold mosaic
659                 if (HoldMosaics) {
660                     c2.SetChar(HoldMosaicChar);
661                     c2.SetCharset(HoldMosaicCharset);
662                 } else {
663                     c2.SetChar(' ');
664                 }
665             } else {
666                 // Character code               
667                 c2.SetChar(ttc);
668                 if (GraphicCharset) {
669                     if (ttc&0x20) {
670                         // real graphics code, remember for HoldMosaics
671                         HoldMosaicChar=ttc;
672                         HoldMosaicCharset=c.GetCharset();
673                     } else {
674                         // invalid code, pass-through to G0
675                         c2.SetCharset(SecondCharset?SecondG0:FirstG0);
676                     }   
677                 }
678             }
679             
680             // Handle double-height and double-width extremes
681             if (y>=23) {
682                 if (Size==sizeDoubleHeight) Size=sizeNormal;
683                 if (Size==sizeDoubleSize) Size=sizeDoubleWidth;
684             }
685             if (x>=38) {
686                 if (Size==sizeDoubleWidth) Size=sizeNormal;
687                 if (Size==sizeDoubleSize) Size=sizeDoubleHeight;
688             }
689             
690             // Now set character code
691             
692             if (NoNextChar) {
693                 // Suppress this char due to double width last char
694                 NoNextChar=false;
695             } else {
696                 switch (Size) {
697                 case sizeNormal:
698                     // Normal sized
699                     setChar(x,y,c2);
700                     if (EmptyNextLine && y<23) {
701                         // Clean up next line
702                         setChar(x,y+1,c2.ToChar(' ').ToCharset(FirstG0));
703                     }
704                     break;
705                 case sizeDoubleWidth:
706                     // Double width
707                     setChar(x,y,c2.ToDblWidth(dblw_Left));
708                     setChar(x+1,y,c2.ToDblWidth(dblw_Right));
709                     if (EmptyNextLine && y<23) {
710                         // Clean up next line
711                         setChar(x  ,y+1,c2.ToChar(' ').ToCharset(FirstG0));
712                         setChar(x+1,y+1,c2.ToChar(' ').ToCharset(FirstG0));
713                     }
714                     NoNextChar=true;
715                     break;
716                 case sizeDoubleHeight:
717                     // Double height
718                     setChar(x,y,c2.ToDblHeight(dblh_Top));
719                     setChar(x,y+1,c2.ToDblHeight(dblh_Bottom));
720                     break;
721                 case sizeDoubleSize:
722                     // Double Size
723                     setChar(x  ,  y,c2.ToDblHeight(dblh_Top   ).ToDblWidth(dblw_Left ));
724                     setChar(x+1,  y,c2.ToDblHeight(dblh_Top   ).ToDblWidth(dblw_Right));
725                     setChar(x  ,y+1,c2.ToDblHeight(dblh_Bottom).ToDblWidth(dblw_Left ));
726                     setChar(x+1,y+1,c2.ToDblHeight(dblh_Bottom).ToDblWidth(dblw_Right));
727                     NoNextChar=true;
728                     break;
729                 }
730             }
731                 
732             // Handle all 'Set-After' spacing codes
733              if (ttc>=0x00 &&  ttc<=0x07) { // Set FG color
734                 if (GraphicCharset) {
735                     // Actual switch from graphics charset
736                     HoldMosaicChar=' ';
737                     HoldMosaicCharset=FirstG0;
738                 }
739                 c.SetFGColor((enumTeletextColor)ttc);
740                 c.SetCharset(SecondCharset?SecondG0:FirstG0);
741                 GraphicCharset=false;
742                 c.SetConceal(false);
743              } else if (ttc==0x08) {
744                 c.SetBlink(true);
745              } else if (ttc==0x0A) {
746                 c.SetBoxedOut(true);
747              } else if (ttc==0x0B) {
748              // Start Box
749                 c.SetBoxedOut(false);
750              } else if (ttc==0x0D) {
751                 if (Size!=sizeDoubleHeight) {
752                     Size=sizeDoubleHeight;
753                     HoldMosaicChar=' ';
754                     HoldMosaicCharset=FirstG0;
755                 }                   
756              } else if (ttc==0x0E) {
757                 if (Size!=sizeDoubleWidth) {
758                     Size=sizeDoubleWidth;
759                     HoldMosaicChar=' ';
760                     HoldMosaicCharset=FirstG0;
761                 }                   
762              } else if (ttc==0x0E) {
763                 if (Size!=sizeDoubleSize) {
764                     Size=sizeDoubleSize;
765                     HoldMosaicChar=' ';
766                     HoldMosaicCharset=FirstG0;
767                 }                   
768              } else  if (ttc>=0x10 && ttc<=0x17) { 
769                 if (!GraphicCharset) {
770                     // Actual switch to graphics charset
771                     HoldMosaicChar=' ';
772                     HoldMosaicCharset=FirstG0;
773                 }
774                 c.SetFGColor((enumTeletextColor)(ttc-0x10));
775                 c.SetCharset(SeparateGraphics?CHARSET_GRAPHICS_G1_SEP:CHARSET_GRAPHICS_G1);
776                 GraphicCharset=true;
777                 c.SetConceal(false);
778              } else if (ttc==0x1B) {
779                 SecondCharset=!SecondCharset;
780                 if (!GraphicCharset) c.SetCharset(SecondCharset?SecondG0:FirstG0);
781              } else if (ttc==0x1F) {
782                 HoldMosaics=false;
783              }
784             
785         } // end for x
786         if (renderfirstlineonly) break;
787     } // end for y
788     
789     for (x=0;x<40;x++) {
790         // Clean out last line
791         cTeletextChar c;
792         c.SetFGColor(ttcWhite);
793         c.SetBGColor(ttcBlack);
794         c.SetCharset(FirstG0);
795         c.SetChar(' ');
796         if (flags&0x60) {
797             c.SetBoxedOut(true);    
798         }
799         setChar(x,24,c);
800     } 
801     for (y=0;y<25;y++) {
802         for (x=0;x<40;x++) {
803             if (isDirty(x,y)) {
804                 dirty=true;
805                 break;
806             }
807             if (dirty) break;
808         }
809         if (dirty) break;
810     }
811      
812     if (dirty && txtview!=NULL ) {
813         
814         if ( !renderfirstlineonly) {
815             Message* m= new Message();
816             m->message = Message::TELETEXTUPDATE;
817             m->to = txtview;
818             m->from = this;
819             m->parameter = 0;
820             Command::getInstance()->postMessageFromOuterSpace(m);
821         } else if (firstlineupdate==10) {
822             Message* m= new Message();
823             m->message = Message::TELETEXTUPDATEFIRSTLINE;
824             m->to = txtview;
825             m->from = this;
826             m->parameter = 0;
827             Command::getInstance()->postMessageFromOuterSpace(m);
828             firstlineupdate=0;
829         } else firstlineupdate++;
830         
831        
832     }
833         
834 }