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