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