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