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