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