Merge pull request #26032 from sundermann/android-make-packaging
[xbmc.git] / xbmc / video / Teletext.cpp
blob471218d5d6f7a940db476392353409d4f945556e
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 /*
10 * Most Codeparts are taken from the TuxBox Teletext plugin which is based
11 * upon videotext-0.6.19991029 and written by Thomas Loewe (LazyT),
12 * Roland Meier and DBLuelle. See http://www.tuxtxt.net/ for more information.
13 * Many thanks to the TuxBox Teletext Team for this great work.
16 #include "Teletext.h"
18 #include "application/ApplicationComponents.h"
19 #include "application/ApplicationPlayer.h"
20 #include "filesystem/SpecialProtocol.h"
21 #include "input/actions/Action.h"
22 #include "input/actions/ActionIDs.h"
23 #include "input/keyboard/KeyIDs.h"
24 #include "utils/log.h"
25 #include "windowing/GraphicContext.h"
27 #include <harfbuzz/hb-ft.h>
29 using namespace std::chrono_literals;
30 using KODI::UTILS::COLOR::Color;
32 static inline void SDL_memset4(uint32_t* dst, uint32_t val, size_t len)
34 for (; len > 0; --len)
35 *dst++ = val;
37 #define SDL_memcpy4(dst, src, len) memcpy(dst, src, (len) << 2)
39 static const char *TeletextFont = "special://xbmc/media/Fonts/teletext.ttf";
41 /* spacing attributes */
42 #define alpha_black 0x00
43 #define alpha_red 0x01
44 #define alpha_green 0x02
45 #define alpha_yellow 0x03
46 #define alpha_blue 0x04
47 #define alpha_magenta 0x05
48 #define alpha_cyan 0x06
49 #define alpha_white 0x07
50 #define flash 0x08
51 #define steady 0x09
52 #define end_box 0x0A
53 #define start_box 0x0B
54 #define normal_size 0x0C
55 #define double_height 0x0D
56 #define double_width 0x0E
57 #define double_size 0x0F
58 #define mosaic_black 0x10
59 #define mosaic_red 0x11
60 #define mosaic_green 0x12
61 #define mosaic_yellow 0x13
62 #define mosaic_blue 0x14
63 #define mosaic_magenta 0x15
64 #define mosaic_cyan 0x16
65 #define mosaic_white 0x17
66 #define conceal 0x18
67 #define contiguous_mosaic 0x19
68 #define separated_mosaic 0x1A
69 #define esc 0x1B
70 #define black_background 0x1C
71 #define new_background 0x1D
72 #define hold_mosaic 0x1E
73 #define release_mosaic 0x1F
75 #define RowAddress2Row(row) ((row == 40) ? 24 : (row - 40))
77 // G2 Set as defined in ETS 300 706
78 const unsigned short int G2table[5][6*16] =
80 // Latin G2 Supplementary Set
81 { 0x0020, 0x00A1, 0x00A2, 0x00A3, 0x0024, 0x00A5, 0x0023, 0x00A7, 0x00A4, 0x2018, 0x201C, 0x00AB, 0x2190, 0x2191, 0x2192, 0x2193,
82 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00D7, 0x00B5, 0x00B6, 0x00B7, 0x00F7, 0x2019, 0x201D, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
83 0x0020, 0x0300, 0x0301, 0x02C6, 0x0303, 0x02C9, 0x02D8, 0x02D9, 0x00A8, 0x002E, 0x02DA, 0x00B8, 0x005F, 0x02DD, 0x02DB, 0x02C7,
84 0x2014, 0x00B9, 0x00AE, 0x00A9, 0x2122, 0x266A, 0x20AC, 0x2030, 0x03B1, 0x0020, 0x0020, 0x0020, 0x215B, 0x215C, 0x215D, 0x215E,
85 0x2126, 0x00C6, 0x00D0, 0x00AA, 0x0126, 0x0020, 0x0132, 0x013F, 0x0141, 0x00D8, 0x0152, 0x00BA, 0x00DE, 0x0166, 0x014A, 0x0149,
86 0x0138, 0x00E6, 0x0111, 0x00F0, 0x0127, 0x0131, 0x0133, 0x0140, 0x0142, 0x00F8, 0x0153, 0x00DF, 0x00FE, 0x0167, 0x014B, 0x25A0},
87 // Cyrillic G2 Supplementary Set
88 { 0x0020, 0x00A1, 0x00A2, 0x00A3, 0x0024, 0x00A5, 0x0020, 0x00A7, 0x0020, 0x2018, 0x201C, 0x00AB, 0x2190, 0x2191, 0x2192, 0x2193,
89 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00D7, 0x00B5, 0x00B6, 0x00B7, 0x00F7, 0x2019, 0x201D, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
90 0x0020, 0x0300, 0x0301, 0x02C6, 0x02DC, 0x02C9, 0x02D8, 0x02D9, 0x00A8, 0x002E, 0x02DA, 0x00B8, 0x005F, 0x02DD, 0x02DB, 0x02C7,
91 0x2014, 0x00B9, 0x00AE, 0x00A9, 0x2122, 0x266A, 0x20AC, 0x2030, 0x03B1, 0x0141, 0x0142, 0x00DF, 0x215B, 0x215C, 0x215D, 0x215E,
92 0x0044, 0x0045, 0x0046, 0x0047, 0x0049, 0x004A, 0x004B, 0x004C, 0x004E, 0x0051, 0x0052, 0x0053, 0x0055, 0x0056, 0x0057, 0x005A,
93 0x0064, 0x0065, 0x0066, 0x0067, 0x0069, 0x006A, 0x006B, 0x006C, 0x006E, 0x0071, 0x0072, 0x0073, 0x0075, 0x0076, 0x0077, 0x007A},
94 // Greek G2 Supplementary Set
95 { 0x0020, 0x0061, 0x0062, 0x00A3, 0x0065, 0x0068, 0x0069, 0x00A7, 0x003A, 0x2018, 0x201C, 0x006B, 0x2190, 0x2191, 0x2192, 0x2193,
96 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00D7, 0x006D, 0x006E, 0x0070, 0x00F7, 0x2019, 0x201D, 0x0074, 0x00BC, 0x00BD, 0x00BE, 0x0078,
97 0x0020, 0x0300, 0x0301, 0x02C6, 0x02DC, 0x02C9, 0x02D8, 0x02D9, 0x00A8, 0x002E, 0x02DA, 0x00B8, 0x005F, 0x02DD, 0x02DB, 0x02C7,
98 0x003F, 0x00B9, 0x00AE, 0x00A9, 0x2122, 0x266A, 0x20AC, 0x2030, 0x03B1, 0x038A, 0x038E, 0x038F, 0x215B, 0x215C, 0x215D, 0x215E,
99 0x0043, 0x0044, 0x0046, 0x0047, 0x004A, 0x004C, 0x0051, 0x0052, 0x0053, 0x0055, 0x0056, 0x0057, 0x0059, 0x005A, 0x0386, 0x0389,
100 0x0063, 0x0064, 0x0066, 0x0067, 0x006A, 0x006C, 0x0071, 0x0072, 0x0073, 0x0075, 0x0076, 0x0077, 0x0079, 0x007A, 0x0388, 0x25A0},
101 // Arabic G2 Set
102 { 0x0020, 0x0639, 0xFEC9, 0xFE83, 0xFE85, 0xFE87, 0xFE8B, 0xFE89, 0xFB7C, 0xFB7D, 0xFB7A, 0xFB58, 0xFB59, 0xFB56, 0xFB6D, 0xFB8E,
103 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669, 0xFECE, 0xFECD, 0xFEFC, 0xFEEC, 0xFEEA, 0xFEE9,
104 0x00E0, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
105 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00EB, 0x00EA, 0x00F9, 0x00EE, 0xFECA,
106 0x00E9, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
107 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00E2, 0x00F4, 0x00FB, 0x00E7, 0x25A0}
110 //const (avoid warnings :<)
111 TextPageAttr_t Text_AtrTable[] =
113 { TXT_ColorWhite , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_WB */
114 { TXT_ColorWhite , TXT_ColorBlack , C_G0P, 0, 0, 1 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_PassiveDefault */
115 { TXT_ColorWhite , TXT_ColorRed , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_L250 */
116 { TXT_ColorBlack , TXT_ColorGreen , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_L251 */
117 { TXT_ColorBlack , TXT_ColorYellow, C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_L252 */
118 { TXT_ColorWhite , TXT_ColorBlue , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_L253 */
119 { TXT_ColorMagenta, TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_TOPMENU0 */
120 { TXT_ColorGreen , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_TOPMENU1 */
121 { TXT_ColorYellow , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_TOPMENU2 */
122 { TXT_ColorCyan , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_TOPMENU3 */
123 { TXT_ColorMenu2 , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSG0 */
124 { TXT_ColorYellow , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSG1 */
125 { TXT_ColorMenu2 , TXT_ColorTransp, C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSG2 */
126 { TXT_ColorWhite , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSG3 */
127 { TXT_ColorMenu2 , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSGDRM0 */
128 { TXT_ColorYellow , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSGDRM1 */
129 { TXT_ColorMenu2 , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSGDRM2 */
130 { TXT_ColorWhite , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSGDRM3 */
131 { TXT_ColorMenu1 , TXT_ColorBlue , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENUHIL0 5a Z */
132 { TXT_ColorWhite , TXT_ColorBlue , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENUHIL1 58 X */
133 { TXT_ColorMenu2 , TXT_ColorTransp, C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENUHIL2 9b õ */
134 { TXT_ColorMenu2 , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU0 ab ´ */
135 { TXT_ColorYellow , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU1 a4 § */
136 { TXT_ColorMenu2 , TXT_ColorTransp, C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU2 9b õ */
137 { TXT_ColorMenu2 , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU3 cb À */
138 { TXT_ColorCyan , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU4 c7 « */
139 { TXT_ColorWhite , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU5 c8 » */
140 { TXT_ColorWhite , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU6 a8 ® */
141 { TXT_ColorYellow , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_CATCHMENU0 a4 § */
142 { TXT_ColorWhite , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f} /* ATR_CATCHMENU1 a8 ® */
145 /* shapes */
146 enum
148 S_END = 0,
149 S_FHL, /* full horizontal line: y-offset */
150 S_FVL, /* full vertical line: x-offset */
151 S_BOX, /* rectangle: x-offset, y-offset, width, height */
152 S_TRA, /* trapez: x0, y0, l0, x1, y1, l1 */
153 S_BTR, /* trapez in bgcolor: x0, y0, l0, x1, y1, l1 */
154 S_INV, /* invert */
155 S_LNK, /* call other shape: shapenumber */
156 S_CHR, /* Character from freetype hibyte, lowbyte */
157 S_ADT, /* Character 2F alternating raster */
158 S_FLH, /* flip horizontal */
159 S_FLV /* flip vertical */
162 /* shape coordinates */
163 enum
165 S_W13 = 5, /* width*1/3 */
166 S_W12, /* width*1/2 */
167 S_W23, /* width*2/3 */
168 S_W11, /* width */
169 S_WM3, /* width-3 */
170 S_H13, /* height*1/3 */
171 S_H12, /* height*1/2 */
172 S_H23, /* height*2/3 */
173 S_H11, /* height */
174 S_NrShCoord
177 /* G3 characters */
178 unsigned char aG3_20[] = { S_TRA, 0, S_H23, 1, 0, S_H11, S_W12, S_END };
179 unsigned char aG3_21[] = { S_TRA, 0, S_H23, 1, 0, S_H11, S_W11, S_END };
180 unsigned char aG3_22[] = { S_TRA, 0, S_H12, 1, 0, S_H11, S_W12, S_END };
181 unsigned char aG3_23[] = { S_TRA, 0, S_H12, 1, 0, S_H11, S_W11, S_END };
182 unsigned char aG3_24[] = { S_TRA, 0, 0, 1, 0, S_H11, S_W12, S_END };
183 unsigned char aG3_25[] = { S_TRA, 0, 0, 1, 0, S_H11, S_W11, S_END };
184 unsigned char aG3_26[] = { S_INV, S_LNK, 0x66, S_END };
185 unsigned char aG3_27[] = { S_INV, S_LNK, 0x67, S_END };
186 unsigned char aG3_28[] = { S_INV, S_LNK, 0x68, S_END };
187 unsigned char aG3_29[] = { S_INV, S_LNK, 0x69, S_END };
188 unsigned char aG3_2a[] = { S_INV, S_LNK, 0x6a, S_END };
189 unsigned char aG3_2b[] = { S_INV, S_LNK, 0x6b, S_END };
190 unsigned char aG3_2c[] = { S_INV, S_LNK, 0x6c, S_END };
191 unsigned char aG3_2d[] = { S_INV, S_LNK, 0x6d, S_END };
192 unsigned char aG3_2e[] = { S_BOX, 2, 0, 3, S_H11, S_END };
193 unsigned char aG3_2f[] = { S_ADT };
194 unsigned char aG3_30[] = { S_LNK, 0x20, S_FLH, S_END };
195 unsigned char aG3_31[] = { S_LNK, 0x21, S_FLH, S_END };
196 unsigned char aG3_32[] = { S_LNK, 0x22, S_FLH, S_END };
197 unsigned char aG3_33[] = { S_LNK, 0x23, S_FLH, S_END };
198 unsigned char aG3_34[] = { S_LNK, 0x24, S_FLH, S_END };
199 unsigned char aG3_35[] = { S_LNK, 0x25, S_FLH, S_END };
200 unsigned char aG3_36[] = { S_INV, S_LNK, 0x76, S_END };
201 unsigned char aG3_37[] = { S_INV, S_LNK, 0x77, S_END };
202 unsigned char aG3_38[] = { S_INV, S_LNK, 0x78, S_END };
203 unsigned char aG3_39[] = { S_INV, S_LNK, 0x79, S_END };
204 unsigned char aG3_3a[] = { S_INV, S_LNK, 0x7a, S_END };
205 unsigned char aG3_3b[] = { S_INV, S_LNK, 0x7b, S_END };
206 unsigned char aG3_3c[] = { S_INV, S_LNK, 0x7c, S_END };
207 unsigned char aG3_3d[] = { S_INV, S_LNK, 0x7d, S_END };
208 unsigned char aG3_3e[] = { S_LNK, 0x2e, S_FLH, S_END };
209 unsigned char aG3_3f[] = { S_BOX, 0, 0, S_W11, S_H11, S_END };
210 unsigned char aG3_40[] = { S_BOX, 0, S_H13, S_W11, S_H13, S_LNK, 0x7e, S_END };
211 unsigned char aG3_41[] = { S_BOX, 0, S_H13, S_W11, S_H13, S_LNK, 0x7e, S_FLV, S_END };
212 unsigned char aG3_42[] = { S_LNK, 0x50, S_BOX, S_W12, S_H13, S_W12, S_H13, S_END };
213 unsigned char aG3_43[] = { S_LNK, 0x50, S_BOX, 0, S_H13, S_W12, S_H13, S_END };
214 unsigned char aG3_44[] = { S_LNK, 0x48, S_FLV, S_LNK, 0x48, S_END };
215 unsigned char aG3_45[] = { S_LNK, 0x44, S_FLH, S_END };
216 unsigned char aG3_46[] = { S_LNK, 0x47, S_FLV, S_END };
217 unsigned char aG3_47[] = { S_LNK, 0x48, S_FLH, S_LNK, 0x48, S_END };
218 unsigned char aG3_48[] = { S_TRA, 0, 0, S_W23, 0, S_H23, 0, S_BTR, 0, 0, S_W13, 0, S_H13, 0, S_END };
219 unsigned char aG3_49[] = { S_LNK, 0x48, S_FLH, S_END };
220 unsigned char aG3_4a[] = { S_LNK, 0x48, S_FLV, S_END };
221 unsigned char aG3_4b[] = { S_LNK, 0x48, S_FLH, S_FLV, S_END };
222 unsigned char aG3_4c[] = { S_LNK, 0x50, S_BOX, 0, S_H13, S_W11, S_H13, S_END };
223 unsigned char aG3_4d[] = { S_CHR, 0x25, 0xE6 };
224 unsigned char aG3_4e[] = { S_CHR, 0x25, 0xCF };
225 unsigned char aG3_4f[] = { S_CHR, 0x25, 0xCB };
226 unsigned char aG3_50[] = { S_BOX, S_W12, 0, 2, S_H11, S_FLH, S_BOX, S_W12, 0, 2, S_H11,S_END };
227 unsigned char aG3_51[] = { S_BOX, 0, S_H12, S_W11, 2, S_FLV, S_BOX, 0, S_H12, S_W11, 2,S_END };
228 unsigned char aG3_52[] = { S_LNK, 0x55, S_FLH, S_FLV, S_END };
229 unsigned char aG3_53[] = { S_LNK, 0x55, S_FLV, S_END };
230 unsigned char aG3_54[] = { S_LNK, 0x55, S_FLH, S_END };
231 unsigned char aG3_55[] = { S_LNK, 0x7e, S_FLV, S_BOX, 0, S_H12, S_W12, 2, S_FLV, S_BOX, 0, S_H12, S_W12, 2, S_END };
232 unsigned char aG3_56[] = { S_LNK, 0x57, S_FLH, S_END};
233 unsigned char aG3_57[] = { S_LNK, 0x55, S_LNK, 0x50 , S_END};
234 unsigned char aG3_58[] = { S_LNK, 0x59, S_FLV, S_END};
235 unsigned char aG3_59[] = { S_LNK, 0x7e, S_LNK, 0x51 , S_END};
236 unsigned char aG3_5a[] = { S_LNK, 0x50, S_LNK, 0x51 , S_END};
237 unsigned char aG3_5b[] = { S_CHR, 0x21, 0x92};
238 unsigned char aG3_5c[] = { S_CHR, 0x21, 0x90};
239 unsigned char aG3_5d[] = { S_CHR, 0x21, 0x91};
240 unsigned char aG3_5e[] = { S_CHR, 0x21, 0x93};
241 unsigned char aG3_5f[] = { S_CHR, 0x00, 0x20};
242 unsigned char aG3_60[] = { S_INV, S_LNK, 0x20, S_END };
243 unsigned char aG3_61[] = { S_INV, S_LNK, 0x21, S_END };
244 unsigned char aG3_62[] = { S_INV, S_LNK, 0x22, S_END };
245 unsigned char aG3_63[] = { S_INV, S_LNK, 0x23, S_END };
246 unsigned char aG3_64[] = { S_INV, S_LNK, 0x24, S_END };
247 unsigned char aG3_65[] = { S_INV, S_LNK, 0x25, S_END };
248 unsigned char aG3_66[] = { S_LNK, 0x20, S_FLV, S_END };
249 unsigned char aG3_67[] = { S_LNK, 0x21, S_FLV, S_END };
250 unsigned char aG3_68[] = { S_LNK, 0x22, S_FLV, S_END };
251 unsigned char aG3_69[] = { S_LNK, 0x23, S_FLV, S_END };
252 unsigned char aG3_6a[] = { S_LNK, 0x24, S_FLV, S_END };
253 unsigned char aG3_6b[] = { S_BOX, 0, 0, S_W11, S_H13, S_TRA, 0, S_H13, S_W11, 0, S_H23, 1, S_END };
254 unsigned char aG3_6c[] = { S_TRA, 0, 0, 1, 0, S_H12, S_W12, S_FLV, S_TRA, 0, 0, 1, 0, S_H12, S_W12, S_BOX, 0, S_H12, S_W12,1, S_END };
255 unsigned char aG3_6d[] = { S_TRA, 0, 0, S_W12, S_W12, S_H12, 0, S_FLH, S_TRA, 0, 0, S_W12, S_W12, S_H12, 0, S_END };
256 unsigned char aG3_6e[] = { S_CHR, 0x00, 0x20};
257 unsigned char aG3_6f[] = { S_CHR, 0x00, 0x20};
258 unsigned char aG3_70[] = { S_INV, S_LNK, 0x30, S_END };
259 unsigned char aG3_71[] = { S_INV, S_LNK, 0x31, S_END };
260 unsigned char aG3_72[] = { S_INV, S_LNK, 0x32, S_END };
261 unsigned char aG3_73[] = { S_INV, S_LNK, 0x33, S_END };
262 unsigned char aG3_74[] = { S_INV, S_LNK, 0x34, S_END };
263 unsigned char aG3_75[] = { S_INV, S_LNK, 0x35, S_END };
264 unsigned char aG3_76[] = { S_LNK, 0x66, S_FLH, S_END };
265 unsigned char aG3_77[] = { S_LNK, 0x67, S_FLH, S_END };
266 unsigned char aG3_78[] = { S_LNK, 0x68, S_FLH, S_END };
267 unsigned char aG3_79[] = { S_LNK, 0x69, S_FLH, S_END };
268 unsigned char aG3_7a[] = { S_LNK, 0x6a, S_FLH, S_END };
269 unsigned char aG3_7b[] = { S_LNK, 0x6b, S_FLH, S_END };
270 unsigned char aG3_7c[] = { S_LNK, 0x6c, S_FLH, S_END };
271 unsigned char aG3_7d[] = { S_LNK, 0x6d, S_FLV, S_END };
272 unsigned char aG3_7e[] = { S_BOX, S_W12, 0, 2, S_H12, S_FLH, S_BOX, S_W12, 0, 2, S_H12, S_END };// help char, not printed directly (only by S_LNK)
274 unsigned char *aShapes[] =
276 aG3_20, aG3_21, aG3_22, aG3_23, aG3_24, aG3_25, aG3_26, aG3_27, aG3_28, aG3_29, aG3_2a, aG3_2b, aG3_2c, aG3_2d, aG3_2e, aG3_2f,
277 aG3_30, aG3_31, aG3_32, aG3_33, aG3_34, aG3_35, aG3_36, aG3_37, aG3_38, aG3_39, aG3_3a, aG3_3b, aG3_3c, aG3_3d, aG3_3e, aG3_3f,
278 aG3_40, aG3_41, aG3_42, aG3_43, aG3_44, aG3_45, aG3_46, aG3_47, aG3_48, aG3_49, aG3_4a, aG3_4b, aG3_4c, aG3_4d, aG3_4e, aG3_4f,
279 aG3_50, aG3_51, aG3_52, aG3_53, aG3_54, aG3_55, aG3_56, aG3_57, aG3_58, aG3_59, aG3_5a, aG3_5b, aG3_5c, aG3_5d, aG3_5e, aG3_5f,
280 aG3_60, aG3_61, aG3_62, aG3_63, aG3_64, aG3_65, aG3_66, aG3_67, aG3_68, aG3_69, aG3_6a, aG3_6b, aG3_6c, aG3_6d, aG3_6e, aG3_6f,
281 aG3_70, aG3_71, aG3_72, aG3_73, aG3_74, aG3_75, aG3_76, aG3_77, aG3_78, aG3_79, aG3_7a, aG3_7b, aG3_7c, aG3_7d, aG3_7e
284 // G0 Table as defined in ETS 300 706
285 // cyrillic G0 Charset (0 = Serbian/Croatian, 1 = Russian/Bulgarian, 2 = Ukrainian)
286 const unsigned short int G0table[6][6*16] =
288 // Cyrillic G0 Set - Option 1 - Serbian/Croatian
289 { ' ', '!', '\"', '#', '$', '%', '&', '\'', '(' , ')' , '*', '+', ',', '-', '.', '/',
290 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
291 0x0427, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0408, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E,
292 0x041F, 0x040C, 0x0420, 0x0421, 0x0422, 0x0423, 0x0412, 0x0403, 0x0409, 0x040A, 0x0417, 0x040B, 0x0416, 0x0402, 0x0428, 0x040F,
293 0x0447, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0458, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E,
294 0x043F, 0x045C, 0x0440, 0x0441, 0x0442, 0x0443, 0x0432, 0x0453, 0x0459, 0x045A, 0x0437, 0x045B, 0x0436, 0x0452, 0x0448, 0x25A0},
295 // Cyrillic G0 Set - Option 2 - Russian/Bulgarian
296 { ' ', '!', '\"', '#', '$', '%', 0x044B, '\'', '(' , ')' , '*', '+', ',', '-', '.', '/',
297 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
298 0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E,
299 0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, 0x042C, 0x042A, 0x0417, 0x0428, 0x042D, 0x0429, 0x0427, 0x042B,
300 0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E,
301 0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, 0x044C, 0x044A, 0x0437, 0x0448, 0x044D, 0x0449, 0x0447, 0x25A0},
302 // Cyrillic G0 Set - Option 3 - Ukrainian
303 { ' ', '!', '\"', '#', '$', '%', 0x0457, '\'', '(' , ')' , '*', '+', ',', '-', '.', '/',
304 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
305 0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E,
306 0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, 0x042C, 0x0406, 0x0417, 0x0428, 0x0404, 0x0429, 0x0427, 0x0407,
307 0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E,
308 0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, 0x044C, 0x0456, 0x0437, 0x0448, 0x0454, 0x0449, 0x0447, 0x25A0},
309 // Greek G0 Set
310 { ' ', '!', '\"', '#', '$', '%', '&', '\'', '(' , ')' , '*', '+', ',', '-', '.', '/',
311 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', 0x00AB, '=', 0x00BB, '?',
312 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F,
313 0x03A0, 0x03A1, 0x0384, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF,
314 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
315 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x25A0},
316 // Hebrew G0 Set
317 { ' ', '!', 0x05F2, 0x00A3, '$', '%', '&', '\'', '(' , ')' , '*', '+', ',', '-', '.', '/',
318 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
319 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
320 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 0x2190, 0x00BD, 0x2192, 0x2191, '#',
321 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
322 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x20AA, 0x2551, 0x00BE, 0x00F7, 0x25A0},
323 // Arabic G0 Set - Thanks to Habib2006(fannansat)
324 { ' ', '!', 0x05F2, 0x00A3, '$', 0x066A, 0xFEF0, 0xFEF2, 0xFD3F, 0xFD3E, '*', '+', ',', '-', '.', '/',
325 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', 0x061B, '>', '=', '<', 0x061F,
326 0xFE94, 0x0621, 0xFE92, 0x0628, 0xFE98, 0x062A, 0xFE8E, 0xFE8D, 0xFE91, 0xFE93, 0xFE97, 0xFE9B, 0xFE9F, 0xFEA3, 0xFEA7, 0xFEA9,
327 0x0630, 0xFEAD, 0xFEAF, 0xFEB3, 0xFEB7, 0xFEBB, 0xFEBF, 0xFEC1, 0xFEC5, 0xFECB, 0xFECF, 0xFE9C, 0xFEA0, 0xFEA4, 0xFEA8, 0x0023,
328 0x0640, 0xFED3, 0xFED7, 0xFEDB, 0xFEDF, 0xFEE3, 0xFEE7, 0xFEEB, 0xFEED, 0xFEEF, 0xFEF3, 0xFE99, 0xFE9D, 0xFEA1, 0xFEA5, 0xFEF4,
329 0xFEF0, 0xFECC, 0xFED0, 0xFED4, 0xFED1, 0xFED8, 0xFED5, 0xFED9, 0xFEE0, 0xFEDD, 0xFEE4, 0xFEE1, 0xFEE8, 0xFEE5, 0xFEFB, 0x25A0}
332 const unsigned short int nationaltable23[14][2] =
334 { '#', 0x00A4 }, /* 0 */
335 { '#', 0x016F }, /* 1 CS/SK */
336 { 0x00A3, '$' }, /* 2 EN */
337 { '#', 0x00F5 }, /* 3 ET */
338 { 0x00E9, 0x0457 }, /* 4 FR */
339 { '#', '$' }, /* 5 DE */
340 { 0x00A3, '$' }, /* 6 IT */
341 { '#', '$' }, /* 7 LV/LT */
342 { '#', 0x0144 }, /* 8 PL */
343 { 0x00E7, '$' }, /* 9 PT/ES */
344 { '#', 0x00A4 }, /* A RO */
345 { '#', 0x00CB }, /* B SR/HR/SL */
346 { '#', 0x00A4 }, /* C SV/FI/HU */
347 { 0x20A4, 0x011F }, /* D TR */
349 const unsigned short int nationaltable40[14] =
351 '@', /* 0 */
352 0x010D, /* 1 CS/SK */
353 '@', /* 2 EN */
354 0x0161, /* 3 ET */
355 0x00E0, /* 4 FR */
356 0x00A7, /* 5 DE */
357 0x00E9, /* 6 IT */
358 0x0161, /* 7 LV/LT */
359 0x0105, /* 8 PL */
360 0x00A1, /* 9 PT/ES */
361 0x0162, /* A RO */
362 0x010C, /* B SR/HR/SL */
363 0x00C9, /* C SV/FI/HU */
364 0x0130, /* D TR */
366 const unsigned short int nationaltable5b[14][6] =
368 { '[', '\\', ']', '^', '_', '`' }, /* 0 */
369 { 0x0165, 0x017E, 0x00FD, 0x00ED, 0x0159, 0x00E9 }, /* 1 CS/SK */
370 { 0x2190, 0x00BD, 0x2192, 0x2191, '#', 0x00AD }, /* 2 EN */
371 { 0x00C4, 0x00D6, 0x017D, 0x00DC, 0x00D5, 0x0161 }, /* 3 ET */
372 { 0x0451, 0x00EA, 0x00F9, 0x00EE, '#', 0x00E8 }, /* 4 FR */
373 { 0x00C4, 0x00D6, 0x00DC, '^', '_', 0x00B0 }, /* 5 DE */
374 { 0x00B0, 0x00E7, 0x2192, 0x2191, '#', 0x00F9 }, /* 6 IT */
375 { 0x0117, 0x0119, 0x017D, 0x010D, 0x016B, 0x0161 }, /* 7 LV/LT */
376 { 0x017B, 0x015A, 0x0141, 0x0107, 0x00F3, 0x0119 }, /* 8 PL */
377 { 0x00E1, 0x00E9, 0x00ED, 0x00F3, 0x00FA, 0x00BF }, /* 9 PT/ES */
378 { 0x00C2, 0x015E, 0x01CD, 0x01CF, 0x0131, 0x0163 }, /* A RO */
379 { 0x0106, 0x017D, 0x00D0, 0x0160, 0x0451, 0x010D }, /* B SR/HR/SL */
380 { 0x00C4, 0x00D6, 0x00C5, 0x00DC, '_', 0x00E9 }, /* C SV/FI/HU */
381 { 0x015E, 0x00D6, 0x00C7, 0x00DC, 0x011E, 0x0131 }, /* D TR */
383 const unsigned short int nationaltable7b[14][4] =
385 { '{', '|', '}', '~' }, /* 0 */
386 { 0x00E1, 0x011B, 0x00FA, 0x0161 }, /* 1 CS/SK */
387 { 0x00BC, 0x2551, 0x00BE, 0x00F7 }, /* 2 EN */
388 { 0x00E4, 0x00F6, 0x017E, 0x00FC }, /* 3 ET */
389 { 0x00E2, 0x00F4, 0x00FB, 0x00E7 }, /* 4 FR */
390 { 0x00E4, 0x00F6, 0x00FC, 0x00DF }, /* 5 DE */
391 { 0x00E0, 0x00F3, 0x00E8, 0x00EC }, /* 6 IT */
392 { 0x0105, 0x0173, 0x017E, 0x012F }, /* 7 LV/LT */
393 { 0x017C, 0x015B, 0x0142, 0x017A }, /* 8 PL */
394 { 0x00FC, 0x00F1, 0x00E8, 0x00E0 }, /* 9 PT/ES */
395 { 0x00E2, 0x015F, 0x01CE, 0x00EE }, /* A RO */
396 { 0x0107, 0x017E, 0x0111, 0x0161 }, /* B SR/HR/SL */
397 { 0x00E4, 0x00F6, 0x00E5, 0x00FC }, /* C SV/FI/HU */
398 { 0x015F, 0x00F6, 0x00E7, 0x00FC }, /* D TR */
400 const unsigned short int arrowtable[] =
402 8592, 8594, 8593, 8595, 'O', 'K', 8592, 8592
405 CTeletextDecoder::CTeletextDecoder()
407 memset(&m_RenderInfo, 0, sizeof(TextRenderInfo_t));
409 m_teletextFont = CSpecialProtocol::TranslatePath(TeletextFont);
410 m_TextureBuffer = NULL;
411 m_txtCache = NULL;
412 m_Manager = NULL;
413 m_Library = NULL;
414 m_RenderInfo.ShowFlof = true;
415 m_RenderInfo.Show39 = false;
416 m_RenderInfo.Showl25 = true;
417 m_RenderInfo.Prev_100 = 0x100;
418 m_RenderInfo.Prev_10 = 0x100;
419 m_RenderInfo.Next_100 = 0x100;
420 m_RenderInfo.Next_10 = 0x100;
421 m_RenderInfo.InputCounter = 2;
423 unsigned short rd0[] = {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0x00<<8, 0x00<<8, 0x00<<8, 0, 0 };
424 unsigned short gn0[] = {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0x20<<8, 0x10<<8, 0x20<<8, 0, 0 };
425 unsigned short bl0[] = {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0x40<<8, 0x20<<8, 0x40<<8, 0, 0 };
426 unsigned short tr0[] = {0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
427 0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
428 0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
429 0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
430 0x0000 , 0x0000 , 0x0A0A , 0xFFFF, 0x3030 };
432 memcpy(m_RenderInfo.rd0,rd0,TXT_Color_SIZECOLTABLE*sizeof(unsigned short));
433 memcpy(m_RenderInfo.gn0,gn0,TXT_Color_SIZECOLTABLE*sizeof(unsigned short));
434 memcpy(m_RenderInfo.bl0,bl0,TXT_Color_SIZECOLTABLE*sizeof(unsigned short));
435 memcpy(m_RenderInfo.tr0,tr0,TXT_Color_SIZECOLTABLE*sizeof(unsigned short));
437 m_LastPage = 0;
438 m_TempPage = 0;
439 m_Ascender = 0;
440 m_PCOldCol = 0;
441 m_PCOldRow = 0;
442 m_CatchedPage = 0;
443 m_CatchCol = 0;
444 m_CatchRow = 0;
445 prevTimeSec = 0;
446 prevHeaderPage = 0;
447 m_updateTexture = false;
448 m_YOffset = 0;
451 CTeletextDecoder::~CTeletextDecoder() = default;
453 bool CTeletextDecoder::Changed()
455 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
456 if (IsSubtitlePage(m_txtCache->Page))
458 m_updateTexture = true;
459 return true;
462 /* Update on every changed second */
463 if (m_txtCache->TimeString[7] != prevTimeSec)
465 prevTimeSec = m_txtCache->TimeString[7];
466 m_updateTexture = true;
467 return true;
469 return false;
472 bool CTeletextDecoder::HandleAction(const CAction &action)
474 if (m_txtCache == NULL)
476 CLog::Log(LOGERROR, "CTeletextDecoder::HandleAction called without teletext cache");
477 return false;
480 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
482 if (action.GetID() == ACTION_MOVE_UP)
484 if (m_RenderInfo.PageCatching)
485 CatchNextPage(-1, -1);
486 else
487 GetNextPageOne(true);
488 return true;
490 else if (action.GetID() == ACTION_MOVE_DOWN)
492 if (m_RenderInfo.PageCatching)
493 CatchNextPage(1, 1);
494 else
495 GetNextPageOne(false);
496 return true;
498 else if (action.GetID() == ACTION_MOVE_RIGHT)
500 if (m_RenderInfo.PageCatching)
501 CatchNextPage(0, 1);
502 else if (m_RenderInfo.Boxed)
504 m_RenderInfo.SubtitleDelay++;
505 // display SubtitleDelay
506 m_RenderInfo.PosY = 0;
507 char ns[10];
508 SetPosX(1);
509 snprintf(ns, sizeof(ns), "+%d ", m_RenderInfo.SubtitleDelay);
510 RenderCharFB(ns[0], &Text_AtrTable[ATR_WB]);
511 RenderCharFB(ns[1], &Text_AtrTable[ATR_WB]);
512 RenderCharFB(ns[2], &Text_AtrTable[ATR_WB]);
513 RenderCharFB(ns[4], &Text_AtrTable[ATR_WB]);
515 else
517 GetNextSubPage(1);
519 return true;
521 else if (action.GetID() == ACTION_MOVE_LEFT)
523 if (m_RenderInfo.PageCatching)
524 CatchNextPage(0, -1);
525 else if (m_RenderInfo.Boxed)
527 m_RenderInfo.SubtitleDelay--;
529 // display subtitledelay
530 m_RenderInfo.PosY = 0;
531 char ns[10];
532 SetPosX(1);
533 snprintf(ns, sizeof(ns), "+%d ", m_RenderInfo.SubtitleDelay);
534 RenderCharFB(ns[0], &Text_AtrTable[ATR_WB]);
535 RenderCharFB(ns[1], &Text_AtrTable[ATR_WB]);
536 RenderCharFB(ns[2], &Text_AtrTable[ATR_WB]);
537 RenderCharFB(ns[4], &Text_AtrTable[ATR_WB]);
539 else
541 GetNextSubPage(-1);
543 return true;
545 else if (action.GetID() >= REMOTE_0 && action.GetID() <= REMOTE_9)
547 PageInput(action.GetID() - REMOTE_0);
548 return true;
550 else if (action.GetID() == KEY_UNICODE)
551 { // input from the keyboard
552 if (action.GetUnicode() >= 48 && action.GetUnicode() < 58)
554 PageInput(action.GetUnicode() - 48);
555 return true;
557 return false;
559 else if (action.GetID() == ACTION_PAGE_UP)
561 SwitchZoomMode();
562 return true;
564 else if (action.GetID() == ACTION_PAGE_DOWN)
566 SwitchTranspMode();
567 return true;
569 else if (action.GetID() == ACTION_SELECT_ITEM)
571 if (m_txtCache->SubPageTable[m_txtCache->Page] == 0xFF)
572 return false;
574 if (!m_RenderInfo.PageCatching)
575 StartPageCatching();
576 else
577 StopPageCatching();
579 return true;
582 if (m_RenderInfo.PageCatching)
584 m_txtCache->PageUpdate = true;
585 m_RenderInfo.PageCatching = false;
586 return true;
589 if (action.GetID() == ACTION_SHOW_INFO)
591 SwitchHintMode();
592 return true;
594 else if (action.GetID() == ACTION_TELETEXT_RED)
596 ColorKey(m_RenderInfo.Prev_100);
597 return true;
599 else if (action.GetID() == ACTION_TELETEXT_GREEN)
601 ColorKey(m_RenderInfo.Prev_10);
602 return true;
604 else if (action.GetID() == ACTION_TELETEXT_YELLOW)
606 ColorKey(m_RenderInfo.Next_10);
607 return true;
609 else if (action.GetID() == ACTION_TELETEXT_BLUE)
611 ColorKey(m_RenderInfo.Next_100);
612 return true;
615 return false;
618 bool CTeletextDecoder::InitDecoder()
620 int error;
622 auto& components = CServiceBroker::GetAppComponents();
623 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
624 m_txtCache = appPlayer->GetTeletextCache();
625 if (m_txtCache == nullptr)
627 CLog::Log(LOGERROR, "{}: called without teletext cache", __FUNCTION__);
628 return false;
631 /* init fontlibrary */
632 if ((error = FT_Init_FreeType(&m_Library)))
634 CLog::Log(LOGERROR, "{}: <FT_Init_FreeType: {:#2X}>", __FUNCTION__, error);
635 m_Library = NULL;
636 return false;
639 if ((error = FTC_Manager_New(m_Library, 7, 2, 0, &MyFaceRequester, NULL, &m_Manager)))
641 FT_Done_FreeType(m_Library);
642 m_Library = NULL;
643 m_Manager = NULL;
644 CLog::Log(LOGERROR, "{}: <FTC_Manager_New: {:#2X}>", __FUNCTION__, error);
645 return false;
648 if ((error = FTC_SBitCache_New(m_Manager, &m_Cache)))
650 FTC_Manager_Done(m_Manager);
651 FT_Done_FreeType(m_Library);
652 m_Manager = NULL;
653 m_Library = NULL;
654 CLog::Log(LOGERROR, "{}: <FTC_SBit_Cache_New: {:#2X}>", __FUNCTION__, error);
655 return false;
658 /* calculate font dimensions */
659 m_RenderInfo.Width = (int)(CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth()*CServiceBroker::GetWinSystem()->GetGfxContext().GetGUIScaleX());
660 m_RenderInfo.Height = (int)(CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight()*CServiceBroker::GetWinSystem()->GetGfxContext().GetGUIScaleY());
661 m_RenderInfo.FontHeight = m_RenderInfo.Height / 25;
662 m_RenderInfo.FontWidth_Normal = m_RenderInfo.Width / (m_RenderInfo.Show39 ? 39 : 40);
663 SetFontWidth(m_RenderInfo.FontWidth_Normal);
664 for (int i = 0; i <= 10; i++)
665 m_RenderInfo.axdrcs[i+12+1] = (m_RenderInfo.FontHeight * i + 6) / 10;
667 /* center screen */
668 m_TypeTTF.face_id = (FTC_FaceID) const_cast<char*>(m_teletextFont.c_str());
669 m_TypeTTF.height = (FT_UShort) m_RenderInfo.FontHeight;
670 m_TypeTTF.flags = FT_LOAD_MONOCHROME;
671 if (FTC_Manager_LookupFace(m_Manager, m_TypeTTF.face_id, &m_Face))
673 m_TypeTTF.face_id = (FTC_FaceID) const_cast<char*>(m_teletextFont.c_str());
674 if ((error = FTC_Manager_LookupFace(m_Manager, m_TypeTTF.face_id, &m_Face)))
676 CLog::Log(LOGERROR, "{}: <FTC_Manager_Lookup_Face failed with Errorcode {:#2X}>",
677 __FUNCTION__, error);
678 FTC_Manager_Done(m_Manager);
679 FT_Done_FreeType(m_Library);
680 m_Manager = NULL;
681 m_Library = NULL;
682 return false;
685 m_Ascender = m_RenderInfo.FontHeight * m_Face->ascender / m_Face->units_per_EM;
687 /* set variable screeninfo for double buffering */
688 m_YOffset = 0;
689 m_TextureBuffer = new Color[4 * m_RenderInfo.Height * m_RenderInfo.Width];
691 ClearFB(GetColorRGB(TXT_ColorTransp));
692 ClearBB(GetColorRGB(TXT_ColorTransp)); /* initialize backbuffer */
693 /* set new colormap */
694 SetColors(DefaultColors, 0, TXT_Color_SIZECOLTABLE);
696 for (int i = 0; i < 40 * 25; i++)
698 m_RenderInfo.PageChar[i] = ' ';
699 m_RenderInfo.PageAtrb[i].fg = TXT_ColorTransp;
700 m_RenderInfo.PageAtrb[i].bg = TXT_ColorTransp;
701 m_RenderInfo.PageAtrb[i].charset = C_G0P;
702 m_RenderInfo.PageAtrb[i].doubleh = 0;
703 m_RenderInfo.PageAtrb[i].doublew = 0;
704 m_RenderInfo.PageAtrb[i].IgnoreAtBlackBgSubst = 0;
707 m_RenderInfo.TranspMode = false;
708 m_LastPage = 0x100;
710 return true;
713 void CTeletextDecoder::EndDecoder()
715 /* clear SubtitleCache */
716 for (TextSubtitleCache_t*& subtitleCache : m_RenderInfo.SubtitleCache)
718 if (subtitleCache != NULL)
720 delete subtitleCache;
721 subtitleCache = NULL;
725 if (m_TextureBuffer)
727 delete[] m_TextureBuffer;
728 m_TextureBuffer = NULL;
731 /* close freetype */
732 if (m_Manager)
734 FTC_Node_Unref(m_anode, m_Manager);
735 FTC_Manager_Done(m_Manager);
737 if (m_Library)
739 FT_Done_FreeType(m_Library);
742 m_Manager = NULL;
743 m_Library = NULL;
745 if (!m_txtCache)
747 CLog::Log(LOGINFO, "{}: called without cache", __FUNCTION__);
749 else
751 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
752 m_txtCache->PageUpdate = true;
753 CLog::Log(LOGDEBUG, "Teletext: Rendering ended");
757 void CTeletextDecoder::PageInput(int Number)
759 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
761 m_updateTexture = true;
763 /* clear m_TempPage */
764 if (m_RenderInfo.InputCounter == 2)
765 m_TempPage = 0;
767 /* check for 0 & 9 on first position */
768 if (Number == 0 && m_RenderInfo.InputCounter == 2)
770 /* set page */
771 m_TempPage = m_LastPage; /* 0 toggles to last page as in program switching */
772 m_RenderInfo.InputCounter = -1;
774 else if (Number == 9 && m_RenderInfo.InputCounter == 2)
776 return;
779 /* show pageinput */
780 if (m_RenderInfo.ZoomMode == 2)
782 m_RenderInfo.ZoomMode = 1;
783 CopyBB2FB();
786 m_RenderInfo.PosY = 0;
788 switch (m_RenderInfo.InputCounter)
790 case 2:
791 SetPosX(1);
792 RenderCharFB(Number | '0', &Text_AtrTable[ATR_WB]);
793 RenderCharFB('-', &Text_AtrTable[ATR_WB]);
794 RenderCharFB('-', &Text_AtrTable[ATR_WB]);
795 break;
797 case 1:
798 SetPosX(2);
799 RenderCharFB(Number | '0', &Text_AtrTable[ATR_WB]);
800 break;
802 case 0:
803 SetPosX(3);
804 RenderCharFB(Number | '0', &Text_AtrTable[ATR_WB]);
805 break;
808 /* generate pagenumber */
809 m_TempPage |= Number << (m_RenderInfo.InputCounter*4);
811 m_RenderInfo.InputCounter--;
813 if (m_RenderInfo.InputCounter < 0)
815 /* disable SubPage zapping */
816 m_txtCache->ZapSubpageManual = false;
818 /* reset input */
819 m_RenderInfo.InputCounter = 2;
821 /* set new page */
822 m_LastPage = m_txtCache->Page;
824 m_txtCache->Page = m_TempPage;
825 m_RenderInfo.HintMode = false;
827 /* check cache */
828 int subp = m_txtCache->SubPageTable[m_txtCache->Page];
829 if (subp != 0xFF)
831 m_txtCache->SubPage = subp;
832 m_txtCache->PageUpdate = true;
834 else
836 m_txtCache->SubPage = 0;
837 // RenderMessage(PageNotFound);
842 void CTeletextDecoder::GetNextPageOne(bool up)
844 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
846 /* disable subpage zapping */
847 m_txtCache->ZapSubpageManual = false;
849 /* abort pageinput */
850 m_RenderInfo.InputCounter = 2;
852 /* find next cached page */
853 m_LastPage = m_txtCache->Page;
855 int subp;
856 do {
857 if (up)
858 CDVDTeletextTools::NextDec(&m_txtCache->Page);
859 else
860 CDVDTeletextTools::PrevDec(&m_txtCache->Page);
861 subp = m_txtCache->SubPageTable[m_txtCache->Page];
862 } while (subp == 0xFF && m_txtCache->Page != m_LastPage);
864 /* update Page */
865 if (m_txtCache->Page != m_LastPage)
867 if (m_RenderInfo.ZoomMode == 2)
868 m_RenderInfo.ZoomMode = 1;
870 m_txtCache->SubPage = subp;
871 m_RenderInfo.HintMode = false;
872 m_txtCache->PageUpdate = true;
876 void CTeletextDecoder::GetNextSubPage(int offset)
878 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
880 /* abort pageinput */
881 m_RenderInfo.InputCounter = 2;
883 for (int loop = m_txtCache->SubPage + offset; loop != m_txtCache->SubPage; loop += offset)
885 if (loop < 0)
886 loop = 0x79;
887 else if (loop > 0x79)
888 loop = 0;
889 if (loop == m_txtCache->SubPage)
890 break;
892 if (m_txtCache->astCachetable[m_txtCache->Page][loop])
894 /* enable manual SubPage zapping */
895 m_txtCache->ZapSubpageManual = true;
897 /* update page */
898 if (m_RenderInfo.ZoomMode == 2) /* if zoomed to lower half */
899 m_RenderInfo.ZoomMode = 1; /* activate upper half */
901 m_txtCache->SubPage = loop;
902 m_RenderInfo.HintMode = false;
903 m_txtCache->PageUpdate = true;
905 return;
910 void CTeletextDecoder::SwitchZoomMode()
912 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
914 if (m_txtCache->SubPageTable[m_txtCache->Page] != 0xFF)
916 /* toggle mode */
917 m_RenderInfo.ZoomMode++;
919 if (m_RenderInfo.ZoomMode == 3)
920 m_RenderInfo.ZoomMode = 0;
922 /* update page */
923 m_txtCache->PageUpdate = true;
927 void CTeletextDecoder::SwitchTranspMode()
929 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
931 /* toggle mode */
932 if (!m_RenderInfo.TranspMode)
933 m_RenderInfo.TranspMode = true;
934 else
935 m_RenderInfo.TranspMode = false; /* backward to immediately switch to TV-screen */
937 /* set mode */
938 if (!m_RenderInfo.TranspMode) /* normal text-only */
940 ClearBB(m_txtCache->FullScrColor);
941 m_txtCache->PageUpdate = true;
943 else /* semi-transparent BG with FG text */
945 ClearBB(TXT_ColorTransp);
946 m_txtCache->PageUpdate = true;
950 void CTeletextDecoder::SwitchHintMode()
952 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
954 /* toggle mode */
955 m_RenderInfo.HintMode ^= true;
957 if (!m_RenderInfo.HintMode) /* toggle evaluation of level 2.5 information by explicitly switching off HintMode */
959 m_RenderInfo.Showl25 ^= true;
961 /* update page */
962 m_txtCache->PageUpdate = true;
965 void CTeletextDecoder::ColorKey(int target)
967 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
969 if (!target)
970 return;
972 if (m_RenderInfo.ZoomMode == 2)
973 m_RenderInfo.ZoomMode = 1;
975 m_LastPage = m_txtCache->Page;
976 m_txtCache->Page = target;
977 m_txtCache->SubPage = m_txtCache->SubPageTable[m_txtCache->Page];
978 m_RenderInfo.InputCounter = 2;
979 m_RenderInfo.HintMode = false;
980 m_txtCache->PageUpdate = true;
983 void CTeletextDecoder::StartPageCatching()
985 m_RenderInfo.PageCatching = true;
987 /* abort pageinput */
988 m_RenderInfo.InputCounter = 2;
990 /* show info line */
991 m_RenderInfo.ZoomMode = 0;
992 m_RenderInfo.PosX = 0;
993 m_RenderInfo.PosY = 24*m_RenderInfo.FontHeight;
995 /* check for pagenumber(s) */
996 m_CatchRow = 1;
997 m_CatchCol = 0;
998 m_CatchedPage = 0;
999 m_PCOldRow = 0;
1000 m_PCOldCol = 0; /* no inverted page number to restore yet */
1001 CatchNextPage(0, 1);
1003 if (!m_CatchedPage)
1005 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1007 m_RenderInfo.PageCatching = false;
1008 m_txtCache->PageUpdate = true;
1009 return;
1013 void CTeletextDecoder::StopPageCatching()
1015 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1017 /* set new page */
1018 if (m_RenderInfo.ZoomMode == 2)
1019 m_RenderInfo.ZoomMode = 1;
1021 m_LastPage = m_txtCache->Page;
1022 m_txtCache->Page = m_CatchedPage;
1023 m_RenderInfo.HintMode = false;
1024 m_txtCache->PageUpdate = true;
1025 m_RenderInfo.PageCatching = false;
1027 int subp = m_txtCache->SubPageTable[m_txtCache->Page];
1028 if (subp != 0xFF)
1029 m_txtCache->SubPage = subp;
1030 else
1031 m_txtCache->SubPage = 0;
1034 void CTeletextDecoder::CatchNextPage(int firstlineinc, int inc)
1036 int tmp_page, allowwrap = 1; /* allow first wrap around */
1038 /* catch next page */
1039 for(;;)
1041 unsigned char *p = &(m_RenderInfo.PageChar[m_CatchRow*40 + m_CatchCol]);
1042 TextPageAttr_t a = m_RenderInfo.PageAtrb[m_CatchRow*40 + m_CatchCol];
1044 if (!(a.charset == C_G1C || a.charset == C_G1S) && /* no mosaic */
1045 (a.fg != a.bg) && /* not hidden */
1046 (*p >= '1' && *p <= '8' && /* valid page number */
1047 *(p+1) >= '0' && *(p+1) <= '9' &&
1048 *(p+2) >= '0' && *(p+2) <= '9') &&
1049 (m_CatchRow == 0 || (*(p-1) < '0' || *(p-1) > '9')) && /* non-numeric char before and behind */
1050 (m_CatchRow == 37 || (*(p+3) < '0' || *(p+3) > '9')))
1052 tmp_page = ((*p - '0')<<8) | ((*(p+1) - '0')<<4) | (*(p+2) - '0');
1054 #if 0
1055 if (tmp_page != m_CatchedPage) /* confusing to skip identical page numbers - I want to reach what I aim to */
1056 #endif
1058 m_CatchedPage = tmp_page;
1059 RenderCatchedPage();
1060 m_CatchCol += inc; /* FIXME: limit */
1061 return;
1065 if (firstlineinc > 0)
1067 m_CatchRow++;
1068 m_CatchCol = 0;
1069 firstlineinc = 0;
1071 else if (firstlineinc < 0)
1073 m_CatchRow--;
1074 m_CatchCol = 37;
1075 firstlineinc = 0;
1077 else
1078 m_CatchCol += inc;
1080 if (m_CatchCol > 37)
1082 m_CatchRow++;
1083 m_CatchCol = 0;
1085 else if (m_CatchCol < 0)
1087 m_CatchRow--;
1088 m_CatchCol = 37;
1091 if (m_CatchRow > 23)
1093 if (allowwrap)
1095 allowwrap = 0;
1096 m_CatchRow = 1;
1097 m_CatchCol = 0;
1099 else
1101 return;
1104 else if (m_CatchRow < 1)
1106 if (allowwrap)
1108 allowwrap = 0;
1109 m_CatchRow = 23;
1110 m_CatchCol =37;
1112 else
1114 return;
1120 void CTeletextDecoder::RenderCatchedPage()
1122 int zoom = 0;
1123 m_updateTexture = true;
1125 /* handle zoom */
1126 if (m_RenderInfo.ZoomMode)
1127 zoom = 1<<10;
1129 if (m_PCOldRow || m_PCOldCol) /* not at first call */
1131 /* restore pagenumber */
1132 SetPosX(m_PCOldCol);
1134 if (m_RenderInfo.ZoomMode == 2)
1135 m_RenderInfo.PosY = (m_PCOldRow-12)*m_RenderInfo.FontHeight*((zoom>>10)+1);
1136 else
1137 m_RenderInfo.PosY = m_PCOldRow*m_RenderInfo.FontHeight*((zoom>>10)+1);
1139 RenderCharFB(m_RenderInfo.PageChar[m_PCOldRow*40 + m_PCOldCol ], &m_RenderInfo.PageAtrb[m_PCOldRow*40 + m_PCOldCol ]);
1140 RenderCharFB(m_RenderInfo.PageChar[m_PCOldRow*40 + m_PCOldCol + 1], &m_RenderInfo.PageAtrb[m_PCOldRow*40 + m_PCOldCol + 1]);
1141 RenderCharFB(m_RenderInfo.PageChar[m_PCOldRow*40 + m_PCOldCol + 2], &m_RenderInfo.PageAtrb[m_PCOldRow*40 + m_PCOldCol + 2]);
1144 m_PCOldRow = m_CatchRow;
1145 m_PCOldCol = m_CatchCol;
1147 /* mark pagenumber */
1148 if (m_RenderInfo.ZoomMode == 1 && m_CatchRow > 11)
1150 m_RenderInfo.ZoomMode = 2;
1151 CopyBB2FB();
1153 else if (m_RenderInfo.ZoomMode == 2 && m_CatchRow < 12)
1155 m_RenderInfo.ZoomMode = 1;
1156 CopyBB2FB();
1158 SetPosX(m_CatchCol);
1160 if (m_RenderInfo.ZoomMode == 2)
1161 m_RenderInfo.PosY = (m_CatchRow-12)*m_RenderInfo.FontHeight*((zoom>>10)+1);
1162 else
1163 m_RenderInfo.PosY = m_CatchRow*m_RenderInfo.FontHeight*((zoom>>10)+1);
1165 TextPageAttr_t a0 = m_RenderInfo.PageAtrb[m_CatchRow*40 + m_CatchCol ];
1166 TextPageAttr_t a1 = m_RenderInfo.PageAtrb[m_CatchRow*40 + m_CatchCol + 1];
1167 TextPageAttr_t a2 = m_RenderInfo.PageAtrb[m_CatchRow*40 + m_CatchCol + 2];
1168 int t;
1170 /* exchange colors */
1171 t = a0.fg; a0.fg = a0.bg; a0.bg = t;
1172 t = a1.fg; a1.fg = a1.bg; a1.bg = t;
1173 t = a2.fg; a2.fg = a2.bg; a2.bg = t;
1175 RenderCharFB(m_RenderInfo.PageChar[m_CatchRow*40 + m_CatchCol ], &a0);
1176 RenderCharFB(m_RenderInfo.PageChar[m_CatchRow*40 + m_CatchCol + 1], &a1);
1177 RenderCharFB(m_RenderInfo.PageChar[m_CatchRow*40 + m_CatchCol + 2], &a2);
1180 void CTeletextDecoder::RenderPage()
1182 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1184 int StartRow = 0;
1185 int national_subset_bak = m_txtCache->NationalSubset;
1187 if (m_txtCache->PageUpdate)
1188 m_updateTexture = true;
1190 /* update page or timestring */
1191 if (m_txtCache->PageUpdate && m_txtCache->PageReceiving != m_txtCache->Page && m_RenderInfo.InputCounter == 2)
1193 /* reset update flag */
1194 m_txtCache->PageUpdate = false;
1195 if (m_RenderInfo.Boxed && m_RenderInfo.SubtitleDelay)
1197 TextSubtitleCache_t* c = NULL;
1198 int j = -1;
1199 for (int i = 0; i < SUBTITLE_CACHESIZE; i++)
1201 if (j == -1 && !m_RenderInfo.SubtitleCache[i])
1202 j = i;
1203 if (m_RenderInfo.SubtitleCache[i] && !m_RenderInfo.SubtitleCache[i]->Valid)
1205 c = m_RenderInfo.SubtitleCache[i];
1206 break;
1209 if (c == NULL)
1211 if (j == -1) // no more space in SubtitleCache
1212 return;
1214 c = new TextSubtitleCache_t;
1215 if (c == NULL)
1216 return;
1218 *c = {};
1219 m_RenderInfo.SubtitleCache[j] = c;
1221 c->Valid = true;
1222 c->Timestamp = std::chrono::steady_clock::now();
1224 if (m_txtCache->SubPageTable[m_txtCache->Page] != 0xFF)
1226 TextPageinfo_t * p = DecodePage(m_RenderInfo.Showl25, c->PageChar, c->PageAtrb, m_RenderInfo.HintMode, m_RenderInfo.ShowFlof);
1227 if (p)
1229 m_RenderInfo.Boxed = p->boxed;
1232 m_RenderInfo.DelayStarted = true;
1233 return;
1235 m_RenderInfo.DelayStarted = false;
1236 /* decode page */
1237 if (m_txtCache->SubPageTable[m_txtCache->Page] != 0xFF)
1239 TextPageinfo_t * p = DecodePage(m_RenderInfo.Showl25, m_RenderInfo.PageChar, m_RenderInfo.PageAtrb, m_RenderInfo.HintMode, m_RenderInfo.ShowFlof);
1240 if (p)
1242 m_RenderInfo.PageInfo = p;
1243 m_RenderInfo.Boxed = p->boxed;
1245 if (m_RenderInfo.Boxed || m_RenderInfo.TranspMode)
1246 FillBorder(GetColorRGB(TXT_ColorTransp));
1247 else
1248 FillBorder(GetColorRGB((enumTeletextColor)m_txtCache->FullScrColor));
1250 if (m_txtCache->ColorTable) /* as late as possible to shorten the time the old page is displayed with the new colors */
1251 SetColors(m_txtCache->ColorTable, 16, 16); /* set colors for CLUTs 2+3 */
1253 else
1254 StartRow = 1;
1256 DoRenderPage(StartRow, national_subset_bak);
1258 else
1260 if (m_RenderInfo.DelayStarted)
1262 auto now = std::chrono::steady_clock::now();
1263 for (TextSubtitleCache_t* const subtitleCache : m_RenderInfo.SubtitleCache)
1265 if (subtitleCache && subtitleCache->Valid &&
1266 std::chrono::duration_cast<std::chrono::seconds>(now - subtitleCache->Timestamp)
1267 .count() >= m_RenderInfo.SubtitleDelay)
1269 memcpy(m_RenderInfo.PageChar, subtitleCache->PageChar, 40 * 25);
1270 memcpy(m_RenderInfo.PageAtrb, subtitleCache->PageAtrb, 40 * 25 * sizeof(TextPageAttr_t));
1271 DoRenderPage(StartRow, national_subset_bak);
1272 subtitleCache->Valid = false;
1273 return;
1277 if (m_RenderInfo.ZoomMode != 2)
1279 m_RenderInfo.PosY = 0;
1280 if (m_txtCache->SubPageTable[m_txtCache->Page] == 0xff)
1282 m_RenderInfo.PageAtrb[32].fg = TXT_ColorYellow;
1283 m_RenderInfo.PageAtrb[32].bg = TXT_ColorMenu1;
1284 int showpage = m_txtCache->PageReceiving;
1285 int showsubpage;
1287 // Verify that showpage is positive before any access to the array
1288 if (showpage >= 0 && (showsubpage = m_txtCache->SubPageTable[showpage]) != 0xff)
1290 TextCachedPage_t *pCachedPage;
1291 pCachedPage = m_txtCache->astCachetable[showpage][showsubpage];
1292 if (pCachedPage && IsDec(showpage))
1294 m_RenderInfo.PosX = 0;
1295 if (m_RenderInfo.InputCounter == 2)
1297 if (m_txtCache->BTTok && !m_txtCache->BasicTop[m_txtCache->Page]) /* page non-existent according to TOP (continue search anyway) */
1299 m_RenderInfo.PageAtrb[0].fg = TXT_ColorWhite;
1300 m_RenderInfo.PageAtrb[0].bg = TXT_ColorRed;
1302 else
1304 m_RenderInfo.PageAtrb[0].fg = TXT_ColorYellow;
1305 m_RenderInfo.PageAtrb[0].bg = TXT_ColorMenu1;
1307 CDVDTeletextTools::Hex2Str((char*)m_RenderInfo.PageChar+3, m_txtCache->Page);
1309 int col;
1310 for (col = m_RenderInfo.nofirst; col < 7; col++) // selected page
1312 RenderCharFB(m_RenderInfo.PageChar[col], &m_RenderInfo.PageAtrb[0]);
1314 RenderCharFB(m_RenderInfo.PageChar[col], &m_RenderInfo.PageAtrb[32]);
1316 else
1317 SetPosX(8);
1319 memcpy(&m_RenderInfo.PageChar[8], pCachedPage->p0, 24); /* header line without timestring */
1320 for (unsigned char i : pCachedPage->p0)
1322 RenderCharFB(i, &m_RenderInfo.PageAtrb[32]);
1325 /* Update on every Header number change */
1326 if (pCachedPage->p0[2] != prevHeaderPage)
1328 prevHeaderPage = pCachedPage->p0[2];
1329 m_updateTexture = true;
1335 /* update timestring */
1336 SetPosX(32);
1337 for (int i = 0; i < 8; i++)
1339 if (!m_RenderInfo.PageAtrb[32+i].flashing)
1340 RenderCharFB(m_txtCache->TimeString[i], &m_RenderInfo.PageAtrb[32]);
1341 else
1343 SetPosX(33+i);
1347 DoFlashing(StartRow);
1348 m_txtCache->NationalSubset = national_subset_bak;
1352 bool CTeletextDecoder::IsSubtitlePage(int pageNumber) const
1354 if (!m_txtCache)
1355 return false;
1357 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1359 for (const auto subPage : m_txtCache->SubtitlePages)
1361 if (subPage.page == pageNumber)
1362 return true;
1365 return false;
1368 void CTeletextDecoder::DoFlashing(int startrow)
1370 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1372 TextCachedPage_t* textCachepage =
1373 m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPage];
1375 // Verify that the page is not deleted by the other thread: CDVDTeletextData::ResetTeletextCache()
1376 if (!textCachepage || m_RenderInfo.PageInfo != &textCachepage->pageinfo)
1377 m_RenderInfo.PageInfo = nullptr;
1379 /* get national subset */
1380 if (m_txtCache->NationalSubset <= NAT_MAX_FROM_HEADER && /* not for GR/RU as long as line28 is not evaluated */
1381 m_RenderInfo.PageInfo && m_RenderInfo.PageInfo->nationalvalid) /* individual subset according to page header */
1383 m_txtCache->NationalSubset = CountryConversionTable[m_RenderInfo.PageInfo->national];
1386 /* Flashing */
1387 TextPageAttr_t flashattr;
1388 char flashchar;
1389 std::chrono::milliseconds flashphase = std::chrono::duration_cast<std::chrono::milliseconds>(
1390 std::chrono::steady_clock::now().time_since_epoch()) %
1391 1000;
1393 int srow = startrow;
1394 int erow = 24;
1395 int factor=1;
1397 switch (m_RenderInfo.ZoomMode)
1399 case 1: erow = 12; factor=2;break;
1400 case 2: srow = 12; factor=2;break;
1403 m_RenderInfo.PosY = startrow*m_RenderInfo.FontHeight*factor;
1404 for (int row = srow; row < erow; row++)
1406 int index = row * 40;
1407 int dhset = 0;
1408 int incflash = 3;
1409 int decflash = 2;
1411 m_RenderInfo.PosX = 0;
1412 for (int col = m_RenderInfo.nofirst; col < 40; col++)
1414 if (m_RenderInfo.PageAtrb[index + col].flashing && m_RenderInfo.PageChar[index + col] > 0x20 && m_RenderInfo.PageChar[index + col] != 0xff )
1416 SetPosX(col);
1417 flashchar = m_RenderInfo.PageChar[index + col];
1418 bool doflash = false;
1419 memcpy(&flashattr, &m_RenderInfo.PageAtrb[index + col], sizeof(TextPageAttr_t));
1420 switch (flashattr.flashing &0x1c) // Flash Rate
1422 case 0x00 : // 1 Hz
1423 if (flashphase > 500ms)
1424 doflash = true;
1425 break;
1426 case 0x04 : // 2 Hz Phase 1
1427 if (flashphase < 250ms)
1428 doflash = true;
1429 break;
1430 case 0x08 : // 2 Hz Phase 2
1431 if (flashphase >= 250ms && flashphase < 500ms)
1432 doflash = true;
1433 break;
1434 case 0x0c : // 2 Hz Phase 3
1435 if (flashphase >= 500ms && flashphase < 750ms)
1436 doflash = true;
1437 break;
1438 case 0x10 : // incremental flash
1439 incflash++;
1440 if (incflash>3) incflash = 1;
1441 switch (incflash)
1443 case 1:
1444 if (flashphase < 250ms)
1445 doflash = true;
1446 break;
1447 case 2:
1448 if (flashphase >= 250ms && flashphase < 500ms)
1449 doflash = true;
1450 break;
1451 case 3:
1452 if (flashphase >= 500ms && flashphase < 750ms)
1453 doflash = true;
1454 break;
1456 break;
1457 case 0x14 : // decremental flash
1458 decflash--;
1459 if (decflash<1) decflash = 3;
1460 switch (decflash)
1462 case 1:
1463 if (flashphase < 250ms)
1464 doflash = true;
1465 break;
1466 case 2:
1467 if (flashphase >= 250ms && flashphase < 500ms)
1468 doflash = true;
1469 break;
1470 case 3:
1471 if (flashphase >= 500ms && flashphase < 750ms)
1472 doflash = true;
1473 break;
1475 break;
1479 switch (flashattr.flashing &0x03) // Flash Mode
1481 case 0x01 : // normal Flashing
1482 if (doflash) flashattr.fg = flashattr.bg;
1483 break;
1484 case 0x02 : // inverted Flashing
1485 doflash = !doflash;
1486 if (doflash) flashattr.fg = flashattr.bg;
1487 break;
1488 case 0x03 : // color Flashing
1489 if (doflash) flashattr.fg = flashattr.fg + (flashattr.fg > 7 ? (-8) : 8);
1490 break;
1493 RenderCharFB(flashchar, &flashattr);
1494 if (flashattr.doublew) col++;
1495 if (flashattr.doubleh) dhset = 1;
1497 m_updateTexture = true;
1500 if (dhset)
1502 row++;
1503 m_RenderInfo.PosY += m_RenderInfo.FontHeight*factor;
1505 m_RenderInfo.PosY += m_RenderInfo.FontHeight*factor;
1509 void CTeletextDecoder::DoRenderPage(int startrow, int national_subset_bak)
1511 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1513 /* display first column? */
1514 m_RenderInfo.nofirst = m_RenderInfo.Show39;
1515 for (int row = 1; row < 24; row++)
1517 int Byte = m_RenderInfo.PageChar[row*40];
1518 if (Byte != ' ' && Byte != 0x00 && Byte != 0xFF && m_RenderInfo.PageAtrb[row*40].fg != m_RenderInfo.PageAtrb[row*40].bg)
1520 m_RenderInfo.nofirst = 0;
1521 break;
1524 m_RenderInfo.FontWidth_Normal = m_RenderInfo.Width / (m_RenderInfo.nofirst ? 39 : 40);
1525 SetFontWidth(m_RenderInfo.FontWidth_Normal);
1527 if (m_RenderInfo.TranspMode || m_RenderInfo.Boxed)
1529 FillBorder(GetColorRGB(TXT_ColorTransp));//ClearBB(transp);
1530 m_RenderInfo.ClearBBColor = TXT_ColorTransp;
1533 /* get national subset */
1534 if (m_txtCache->NationalSubset <= NAT_MAX_FROM_HEADER && /* not for GR/RU as long as line28 is not evaluated */
1535 m_RenderInfo.PageInfo && m_RenderInfo.PageInfo->nationalvalid) /* individual subset according to page header */
1537 m_txtCache->NationalSubset = CountryConversionTable[m_RenderInfo.PageInfo->national];
1539 /* render page */
1540 if (m_RenderInfo.PageInfo && (m_RenderInfo.PageInfo->function == FUNC_GDRCS || m_RenderInfo.PageInfo->function == FUNC_DRCS)) /* character definitions */
1542 #define DRCSROWS 8
1543 #define DRCSCOLS (48/DRCSROWS)
1544 #define DRCSZOOMX 3
1545 #define DRCSZOOMY 5
1546 #define DRCSXSPC (12*DRCSZOOMX + 2)
1547 #define DRCSYSPC (10*DRCSZOOMY + 2)
1549 unsigned char ax[] = { /* array[0..12] of x-offsets, array[0..10] of y-offsets for each pixel */
1550 DRCSZOOMX * 0,
1551 DRCSZOOMX * 1,
1552 DRCSZOOMX * 2,
1553 DRCSZOOMX * 3,
1554 DRCSZOOMX * 4,
1555 DRCSZOOMX * 5,
1556 DRCSZOOMX * 6,
1557 DRCSZOOMX * 7,
1558 DRCSZOOMX * 8,
1559 DRCSZOOMX * 9,
1560 DRCSZOOMX * 10,
1561 DRCSZOOMX * 11,
1562 DRCSZOOMX * 12,
1563 DRCSZOOMY * 0,
1564 DRCSZOOMY * 1,
1565 DRCSZOOMY * 2,
1566 DRCSZOOMY * 3,
1567 DRCSZOOMY * 4,
1568 DRCSZOOMY * 5,
1569 DRCSZOOMY * 6,
1570 DRCSZOOMY * 7,
1571 DRCSZOOMY * 8,
1572 DRCSZOOMY * 9,
1573 DRCSZOOMY * 10
1576 ClearBB(TXT_ColorBlack);
1577 for (int col = 0; col < 24*40; col++)
1578 m_RenderInfo.PageAtrb[col] = Text_AtrTable[ATR_WB];
1580 for (int row = 0; row < DRCSROWS; row++)
1582 for (int col = 0; col < DRCSCOLS; col++)
1584 RenderDRCS(m_RenderInfo.Width,
1585 m_RenderInfo.PageChar + 20 * (DRCSCOLS * row + col + 2),
1586 m_TextureBuffer
1587 + (m_RenderInfo.FontHeight + DRCSYSPC * row + m_RenderInfo.Height) * m_RenderInfo.Width
1588 + DRCSXSPC * col,
1589 ax, GetColorRGB(TXT_ColorWhite), GetColorRGB(TXT_ColorBlack));
1592 memset(m_RenderInfo.PageChar + 40, 0xff, 24*40); /* don't render any char below row 0 */
1594 m_RenderInfo.PosY = startrow*m_RenderInfo.FontHeight;
1595 for (int row = startrow; row < 24; row++)
1597 int index = row * 40;
1599 m_RenderInfo.PosX = 0;
1600 for (int col = m_RenderInfo.nofirst; col < 40; col++)
1602 RenderCharBB(m_RenderInfo.PageChar[index + col], &m_RenderInfo.PageAtrb[index + col]);
1604 if (m_RenderInfo.PageAtrb[index + col].doubleh && m_RenderInfo.PageChar[index + col] != 0xff && row < 24-1) /* disable lower char in case of doubleh setting in l25 objects */
1605 m_RenderInfo.PageChar[index + col + 40] = 0xff;
1606 if (m_RenderInfo.PageAtrb[index + col].doublew && col < 40-1) /* skip next column if double width */
1608 col++;
1609 if (m_RenderInfo.PageAtrb[index + col - 1].doubleh && m_RenderInfo.PageChar[index + col] != 0xff && row < 24-1) /* disable lower char in case of doubleh setting in l25 objects */
1610 m_RenderInfo.PageChar[index + col + 40] = 0xff;
1613 m_RenderInfo.PosY += m_RenderInfo.FontHeight;
1615 DoFlashing(startrow);
1617 /* update framebuffer */
1618 CopyBB2FB();
1619 m_txtCache->NationalSubset = national_subset_bak;
1622 void CTeletextDecoder::Decode_BTT()
1624 /* basic top table */
1625 int current, b1, b2, b3, b4;
1626 unsigned char btt[23*40];
1628 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1630 if (m_txtCache->SubPageTable[0x1f0] == 0xff || 0 == m_txtCache->astCachetable[0x1f0][m_txtCache->SubPageTable[0x1f0]]) /* not yet received */
1631 return;
1633 auto& components = CServiceBroker::GetAppComponents();
1634 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
1636 appPlayer->LoadPage(0x1f0, m_txtCache->SubPageTable[0x1f0], btt);
1637 if (btt[799] == ' ') /* not completely received or error */
1638 return;
1640 current = 0x100;
1641 for (int i = 0; i < 800; i++)
1643 b1 = btt[i];
1644 if (b1 == ' ')
1645 b1 = 0;
1646 else
1648 b1 = dehamming[b1];
1649 if (b1 == 0xFF) /* hamming error in btt */
1651 btt[799] = ' '; /* mark btt as not received */
1652 return;
1655 m_txtCache->BasicTop[current] = b1;
1656 CDVDTeletextTools::NextDec(&current);
1658 /* page linking table */
1659 m_txtCache->ADIP_PgMax = -1; /* rebuild table of adip pages */
1660 for (int i = 0; i < 10; i++)
1662 b1 = dehamming[btt[800 + 8*i +0]];
1664 if (b1 == 0xE)
1665 continue; /* unused */
1666 else if (b1 == 0xF)
1667 break; /* end */
1669 b4 = dehamming[btt[800 + 8*i +7]];
1671 if (b4 != 2) /* only adip, ignore multipage (1) */
1672 continue;
1674 b2 = dehamming[btt[800 + 8*i +1]];
1675 b3 = dehamming[btt[800 + 8*i +2]];
1677 if (b1 == 0xFF || b2 == 0xFF || b3 == 0xFF)
1679 CLog::Log(LOGERROR, "CTeletextDecoder::Decode_BTT <Biterror in btt/plt index {}>", i);
1680 btt[799] = ' '; /* mark btt as not received */
1681 return;
1684 b1 = b1<<8 | b2<<4 | b3; /* page number */
1685 m_txtCache->ADIP_Pg[++m_txtCache->ADIP_PgMax] = b1;
1688 m_txtCache->BTTok = true;
1691 void CTeletextDecoder::Decode_ADIP() /* additional information table */
1693 int i, p, j, b1, b2, b3, charfound;
1694 unsigned char padip[23*40];
1696 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1698 auto& components = CServiceBroker::GetAppComponents();
1699 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
1701 for (i = 0; i <= m_txtCache->ADIP_PgMax; i++)
1703 p = m_txtCache->ADIP_Pg[i];
1704 if (!p || m_txtCache->SubPageTable[p] == 0xff || 0 == m_txtCache->astCachetable[p][m_txtCache->SubPageTable[p]]) /* not cached (avoid segfault) */
1705 continue;
1707 appPlayer->LoadPage(p, m_txtCache->SubPageTable[p], padip);
1708 for (j = 0; j < 44; j++)
1710 b1 = dehamming[padip[20*j+0]];
1711 if (b1 == 0xE)
1712 continue; /* unused */
1714 if (b1 == 0xF)
1715 break; /* end */
1717 b2 = dehamming[padip[20*j+1]];
1718 b3 = dehamming[padip[20*j+2]];
1720 if (b1 == 0xFF || b2 == 0xFF || b3 == 0xFF)
1722 CLog::Log(LOGERROR,
1723 "CTeletextDecoder::Decode_BTT <Biterror in ait {:03x} {} {:02x} {:02x} {:02x} "
1724 "{:02x} {:02x} {:02x}>",
1725 p, j, padip[20 * j + 0], padip[20 * j + 1], padip[20 * j + 2], b1, b2, b3);
1726 return;
1729 if (b1>8 || b2>9 || b3>9) /* ignore entries with invalid or hex page numbers */
1731 continue;
1734 b1 = b1<<8 | b2<<4 | b3; /* page number */
1735 charfound = 0; /* flag: no printable char found */
1737 for (b2 = 11; b2 >= 0; b2--)
1739 b3 = deparity[padip[20*j + 8 + b2]];
1740 if (b3 < ' ')
1741 b3 = ' ';
1743 if (b3 == ' ' && !charfound)
1744 m_txtCache->ADIPTable[b1][b2] = '\0';
1745 else
1747 m_txtCache->ADIPTable[b1][b2] = b3;
1748 charfound = 1;
1751 } /* next link j */
1753 m_txtCache->ADIP_Pg[i] = 0; /* completely decoded: clear entry */
1754 } /* next adip page i */
1756 while ((m_txtCache->ADIP_PgMax >= 0) && !m_txtCache->ADIP_Pg[m_txtCache->ADIP_PgMax]) /* and shrink table */
1757 m_txtCache->ADIP_PgMax--;
1760 int CTeletextDecoder::TopText_GetNext(int startpage, int up, int findgroup)
1762 int current, nextgrp, nextblk;
1764 int stoppage = (IsDec(startpage) ? startpage : startpage & 0xF00); // avoid endless loop in hexmode
1765 nextgrp = nextblk = 0;
1766 current = startpage;
1768 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1770 do {
1771 if (up)
1772 CDVDTeletextTools::NextDec(&current);
1773 else
1774 CDVDTeletextTools::PrevDec(&current);
1776 if (!m_txtCache->BTTok || m_txtCache->BasicTop[current]) /* only if existent */
1778 if (findgroup)
1780 if (m_txtCache->BasicTop[current] >= 6 && m_txtCache->BasicTop[current] <= 7)
1781 return current;
1782 if (!nextgrp && (current&0x00F) == 0)
1783 nextgrp = current;
1785 if (m_txtCache->BasicTop[current] >= 2 && m_txtCache->BasicTop[current] <= 5) /* always find block */
1786 return current;
1788 if (!nextblk && (current&0x0FF) == 0)
1789 nextblk = current;
1791 } while (current != stoppage);
1793 if (nextgrp)
1794 return nextgrp;
1795 else if (nextblk)
1796 return nextblk;
1797 else
1798 return current;
1801 void CTeletextDecoder::Showlink(int column, int linkpage)
1803 unsigned char line[] = " >??? ";
1804 int oldfontwidth = m_RenderInfo.FontWidth;
1805 int yoffset;
1807 if (m_YOffset)
1808 yoffset = 0;
1809 else
1810 yoffset = m_RenderInfo.Height;
1812 int abx = ((m_RenderInfo.Width)%(40-m_RenderInfo.nofirst) == 0 ? m_RenderInfo.Width+1 : (m_RenderInfo.Width)/(((m_RenderInfo.Width)%(40-m_RenderInfo.nofirst)))+1);// distance between 'inserted' pixels
1813 int width = m_RenderInfo.Width /4;
1815 m_RenderInfo.PosY = 24*m_RenderInfo.FontHeight;
1817 if (m_RenderInfo.Boxed)
1819 m_RenderInfo.PosX = column*width;
1820 FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY+yoffset, m_RenderInfo.Width, m_RenderInfo.FontHeight, GetColorRGB(TXT_ColorTransp));
1821 return;
1824 if (m_txtCache->ADIPTable[linkpage][0])
1826 m_RenderInfo.PosX = column*width;
1827 int l = strlen(m_txtCache->ADIPTable[linkpage]);
1829 if (l > 9) /* smaller font, if no space for one half space at front and end */
1830 SetFontWidth(oldfontwidth * 10 / (l+1));
1832 FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY+yoffset, width+(m_RenderInfo.Width%4), m_RenderInfo.FontHeight, GetColorRGB((enumTeletextColor)Text_AtrTable[ATR_L250 + column].bg));
1833 m_RenderInfo.PosX += ((width) - (l*m_RenderInfo.FontWidth+l*m_RenderInfo.FontWidth/abx))/2; /* center */
1835 for (char *p = m_txtCache->ADIPTable[linkpage]; *p; p++)
1836 RenderCharBB(*p, &Text_AtrTable[ATR_L250 + column]);
1838 SetFontWidth(oldfontwidth);
1840 else /* display number */
1842 m_RenderInfo.PosX = column*width;
1843 FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY+yoffset, m_RenderInfo.Width-m_RenderInfo.PosX, m_RenderInfo.FontHeight, GetColorRGB((enumTeletextColor)Text_AtrTable[ATR_L250 + column].bg));
1844 if (linkpage < m_txtCache->Page)
1846 line[6] = '<';
1847 CDVDTeletextTools::Hex2Str((char*)line + 5, linkpage);
1849 else
1850 CDVDTeletextTools::Hex2Str((char*)line + 6, linkpage);
1852 for (unsigned char *p = line; p < line+9; p++)
1853 RenderCharBB(*p, &Text_AtrTable[ATR_L250 + column]);
1857 void CTeletextDecoder::CreateLine25()
1859 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1861 /* btt completely received and not yet decoded */
1862 if (!m_txtCache->BTTok)
1863 Decode_BTT();
1865 if (m_txtCache->ADIP_PgMax >= 0)
1866 Decode_ADIP();
1868 if (!m_RenderInfo.ShowHex && m_RenderInfo.ShowFlof &&
1869 (m_txtCache->FlofPages[m_txtCache->Page][0] || m_txtCache->FlofPages[m_txtCache->Page][1] || m_txtCache->FlofPages[m_txtCache->Page][2] || m_txtCache->FlofPages[m_txtCache->Page][3])) // FLOF-Navigation present
1871 m_RenderInfo.Prev_100 = m_txtCache->FlofPages[m_txtCache->Page][0];
1872 m_RenderInfo.Prev_10 = m_txtCache->FlofPages[m_txtCache->Page][1];
1873 m_RenderInfo.Next_10 = m_txtCache->FlofPages[m_txtCache->Page][2];
1874 m_RenderInfo.Next_100 = m_txtCache->FlofPages[m_txtCache->Page][3];
1876 m_RenderInfo.PosY = 24*m_RenderInfo.FontHeight;
1877 m_RenderInfo.PosX = 0;
1878 for (int i=m_RenderInfo.nofirst; i<40; i++)
1879 RenderCharBB(m_RenderInfo.PageChar[24*40 + i], &m_RenderInfo.PageAtrb[24*40 + i]);
1881 else
1883 /* normal: blk-1, grp+1, grp+2, blk+1 */
1884 /* hex: hex+1, blk-1, grp+1, blk+1 */
1885 if (m_RenderInfo.ShowHex)
1887 /* arguments: startpage, up, findgroup */
1888 m_RenderInfo.Prev_100 = NextHex(m_txtCache->Page);
1889 m_RenderInfo.Prev_10 = TopText_GetNext(m_txtCache->Page, 0, 0);
1890 m_RenderInfo.Next_10 = TopText_GetNext(m_txtCache->Page, 1, 1);
1892 else
1894 m_RenderInfo.Prev_100 = TopText_GetNext(m_txtCache->Page, 0, 0);
1895 m_RenderInfo.Prev_10 = TopText_GetNext(m_txtCache->Page, 1, 1);
1896 m_RenderInfo.Next_10 = TopText_GetNext(m_RenderInfo.Prev_10, 1, 1);
1898 m_RenderInfo.Next_100 = TopText_GetNext(m_RenderInfo.Next_10, 1, 0);
1899 Showlink(0, m_RenderInfo.Prev_100);
1900 Showlink(1, m_RenderInfo.Prev_10);
1901 Showlink(2, m_RenderInfo.Next_10);
1902 Showlink(3, m_RenderInfo.Next_100);
1906 void CTeletextDecoder::RenderCharFB(int Char, TextPageAttr_t *Attribute)
1908 RenderCharIntern(&m_RenderInfo, Char, Attribute, m_RenderInfo.ZoomMode, m_YOffset);
1911 void CTeletextDecoder::RenderCharBB(int Char, TextPageAttr_t *Attribute)
1913 RenderCharIntern(&m_RenderInfo, Char, Attribute, 0, m_RenderInfo.Height-m_YOffset);
1916 void CTeletextDecoder::CopyBB2FB()
1918 Color *src, *dst, *topsrc;
1919 int screenwidth;
1920 Color fillcolor;
1922 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1924 /* line 25 */
1925 if (!m_RenderInfo.PageCatching)
1926 CreateLine25();
1928 /* copy backbuffer to framebuffer */
1929 if (!m_RenderInfo.ZoomMode)
1931 if (m_YOffset)
1932 m_YOffset = 0;
1933 else
1934 m_YOffset = m_RenderInfo.Height;
1936 if (m_RenderInfo.ClearBBColor >= 0)
1938 m_RenderInfo.ClearBBColor = -1;
1940 return;
1943 src = dst = topsrc = m_TextureBuffer + m_RenderInfo.Width;
1945 if (m_YOffset)
1947 dst += m_RenderInfo.Width * m_RenderInfo.Height;
1949 else
1951 src += m_RenderInfo.Width * m_RenderInfo.Height;
1952 topsrc += m_RenderInfo.Width * m_RenderInfo.Height;
1955 if (!m_RenderInfo.PageCatching)
1956 SDL_memcpy4(dst+(24*m_RenderInfo.FontHeight)*m_RenderInfo.Width, src + (24*m_RenderInfo.FontHeight)*m_RenderInfo.Width, m_RenderInfo.Width*m_RenderInfo.FontHeight); /* copy line25 in normal height */
1958 if (m_RenderInfo.TranspMode)
1959 fillcolor = GetColorRGB(TXT_ColorTransp);
1960 else
1961 fillcolor = GetColorRGB((enumTeletextColor)m_txtCache->FullScrColor);
1963 if (m_RenderInfo.ZoomMode == 2)
1964 src += 12*m_RenderInfo.FontHeight*m_RenderInfo.Width;
1966 screenwidth = m_RenderInfo.Width;
1968 for (int i = 12*m_RenderInfo.FontHeight; i; i--)
1970 SDL_memcpy4(dst, src, screenwidth);
1971 dst += m_RenderInfo.Width;
1972 SDL_memcpy4(dst, src, screenwidth);
1973 dst += m_RenderInfo.Width;
1974 src += m_RenderInfo.Width;
1977 for (int i = m_RenderInfo.Height - 25*m_RenderInfo.FontHeight; i >= 0;i--)
1979 SDL_memset4(dst + m_RenderInfo.Width*(m_RenderInfo.FontHeight+i), fillcolor, screenwidth);
1983 FT_Error CTeletextDecoder::MyFaceRequester(FTC_FaceID face_id, FT_Library library, FT_Pointer request_data, FT_Face *aface)
1985 FT_Error result = FT_New_Face(library, (const char*)face_id, 0, aface);
1987 if (!result)
1988 CLog::Log(LOGINFO, "Teletext font {} loaded", (char*)face_id);
1989 else
1990 CLog::Log(LOGERROR, "Opening of Teletext font {} failed", (char*)face_id);
1992 return result;
1995 void CTeletextDecoder::SetFontWidth(int newWidth)
1997 if (m_RenderInfo.FontWidth != newWidth)
1999 m_RenderInfo.FontWidth = newWidth;
2000 m_TypeTTF.width = (FT_UShort) m_RenderInfo.FontWidth;
2002 for (int i = 0; i <= 12; i++)
2003 m_RenderInfo.axdrcs[i] = (m_RenderInfo.FontWidth * i + 6) / 12;
2007 int CTeletextDecoder::GetCurFontWidth()
2009 int mx = (m_RenderInfo.Width)%(40-m_RenderInfo.nofirst); // # of unused pixels
2010 int abx = (mx == 0 ? m_RenderInfo.Width+1 : (m_RenderInfo.Width)/(mx+1)); // distance between 'inserted' pixels
2011 int nx = abx+1-(m_RenderInfo.PosX % (abx+1)); // # of pixels to next insert
2012 return m_RenderInfo.FontWidth+(((m_RenderInfo.PosX+m_RenderInfo.FontWidth+1) <= m_RenderInfo.Width && nx <= m_RenderInfo.FontWidth+1) ? 1 : 0);
2015 void CTeletextDecoder::SetPosX(int column)
2017 m_RenderInfo.PosX = 0;
2019 for (int i = 0; i < column-m_RenderInfo.nofirst; i++)
2020 m_RenderInfo.PosX += GetCurFontWidth();
2023 void CTeletextDecoder::ClearBB(Color Color)
2025 SDL_memset4(m_TextureBuffer + (m_RenderInfo.Height-m_YOffset)*m_RenderInfo.Width, Color, m_RenderInfo.Width*m_RenderInfo.Height);
2028 void CTeletextDecoder::ClearFB(Color Color)
2030 SDL_memset4(m_TextureBuffer + m_RenderInfo.Width*m_YOffset, Color, m_RenderInfo.Width*m_RenderInfo.Height);
2033 void CTeletextDecoder::FillBorder(Color Color)
2035 FillRect(m_TextureBuffer + (m_RenderInfo.Height-m_YOffset)*m_RenderInfo.Width, m_RenderInfo.Width, 0, 25*m_RenderInfo.FontHeight, m_RenderInfo.Width, m_RenderInfo.Height-(25*m_RenderInfo.FontHeight), Color);
2036 FillRect(m_TextureBuffer + m_RenderInfo.Width*m_YOffset, m_RenderInfo.Width, 0, 25*m_RenderInfo.FontHeight, m_RenderInfo.Width, m_RenderInfo.Height-(25*m_RenderInfo.FontHeight), Color);
2039 void CTeletextDecoder::FillRect(Color* buffer, int xres, int x, int y, int w, int h, Color color)
2041 if (!buffer) return;
2043 Color* p = buffer + x + y * xres;
2045 if (w > 0)
2047 for ( ; h > 0 ; h--)
2049 SDL_memset4(p, color, w);
2050 p += xres;
2055 void CTeletextDecoder::DrawVLine(Color* lfb, int xres, int x, int y, int l, Color color)
2057 if (!lfb) return;
2058 Color* p = lfb + x + y * xres;
2060 for ( ; l > 0 ; l--)
2062 *p = color;
2063 p += xres;
2067 void CTeletextDecoder::DrawHLine(Color* lfb, int xres, int x, int y, int l, Color color)
2069 if (!lfb) return;
2070 if (l > 0)
2071 SDL_memset4(lfb + x + y * xres, color, l);
2074 void CTeletextDecoder::RenderDRCS(
2075 int xres,
2076 unsigned char* s, /* pointer to char data, parity undecoded */
2077 Color* d, /* pointer to frame buffer of top left pixel */
2078 unsigned char* ax, /* array[0..12] of x-offsets, array[0..10] of y-offsets for each pixel */
2079 Color fgcolor,
2080 Color bgcolor)
2082 if (d == NULL) return;
2084 unsigned char *ay = ax + 13; /* array[0..10] of y-offsets for each pixel */
2086 for (int y = 0; y < 10; y++) /* 10*2 bytes a 6 pixels per char definition */
2088 unsigned char c1 = deparity[*s++];
2089 unsigned char c2 = deparity[*s++];
2090 int h = ay[y+1] - ay[y];
2092 if (!h)
2093 continue;
2094 if (((c1 == ' ') && (*(s-2) != ' ')) || ((c2 == ' ') && (*(s-1) != ' '))) /* parity error: stop decoding FIXME */
2095 return;
2096 for (int bit = 0x20, x = 0;
2097 bit;
2098 bit >>= 1, x++) /* bit mask (MSB left), column counter */
2100 Color f1 = (c1 & bit) ? fgcolor : bgcolor;
2101 Color f2 = (c2 & bit) ? fgcolor : bgcolor;
2102 for (int i = 0; i < h; i++)
2104 if (ax[x+1] > ax[x])
2105 SDL_memset4(d + ax[x], f1, ax[x+1] - ax[x]);
2106 if (ax[x+7] > ax[x+6])
2107 SDL_memset4(d + ax[x+6], f2, ax[x+7] - ax[x+6]); /* 2nd byte 6 pixels to the right */
2108 d += xres;
2110 d -= h * xres;
2112 d += h * xres;
2116 void CTeletextDecoder::FillRectMosaicSeparated(
2117 Color* lfb, int xres, int x, int y, int w, int h, Color fgcolor, Color bgcolor, int set)
2119 if (!lfb) return;
2120 FillRect(lfb,xres,x, y, w, h, bgcolor);
2121 if (set)
2123 FillRect(lfb,xres,x+1, y+1, w-2, h-2, fgcolor);
2127 void CTeletextDecoder::FillTrapez(
2128 Color* lfb, int xres, int x0, int y0, int l0, int xoffset1, int h, int l1, Color color)
2130 Color* p = lfb + x0 + y0 * xres;
2131 int xoffset, l;
2133 for (int yoffset = 0; yoffset < h; yoffset++)
2135 l = l0 + ((l1-l0) * yoffset + h/2) / h;
2136 xoffset = (xoffset1 * yoffset + h/2) / h;
2137 if (l > 0)
2138 SDL_memset4(p + xoffset, color, l);
2139 p += xres;
2143 void CTeletextDecoder::FlipHorz(Color* lfb, int xres, int x, int y, int w, int h)
2145 Color buf[2048];
2146 Color* p = lfb + x + y * xres;
2147 int w1,h1;
2149 for (h1 = 0 ; h1 < h ; h1++)
2151 SDL_memcpy4(buf,p,w);
2152 for (w1 = 0 ; w1 < w ; w1++)
2154 *(p+w1) = buf[w-(w1+1)];
2156 p += xres;
2160 void CTeletextDecoder::FlipVert(Color* lfb, int xres, int x, int y, int w, int h)
2162 Color buf[2048];
2163 Color *p = lfb + x + y * xres, *p1, *p2;
2164 int h1;
2166 for (h1 = 0 ; h1 < h/2 ; h1++)
2168 p1 = (p+(h1*xres));
2169 p2 = (p+(h-(h1+1))*xres);
2170 SDL_memcpy4(buf, p1, w);
2171 SDL_memcpy4(p1, p2, w);
2172 SDL_memcpy4(p2, buf, w);
2176 int CTeletextDecoder::ShapeCoord(int param, int curfontwidth, int curFontHeight)
2178 switch (param)
2180 case S_W13:
2181 return curfontwidth/3;
2182 case S_W12:
2183 return curfontwidth/2;
2184 case S_W23:
2185 return curfontwidth*2/3;
2186 case S_W11:
2187 return curfontwidth;
2188 case S_WM3:
2189 return curfontwidth-3;
2190 case S_H13:
2191 return curFontHeight/3;
2192 case S_H12:
2193 return curFontHeight/2;
2194 case S_H23:
2195 return curFontHeight*2/3;
2196 case S_H11:
2197 return curFontHeight;
2198 default:
2199 return param;
2203 void CTeletextDecoder::DrawShape(Color* lfb,
2204 int xres,
2205 int x,
2206 int y,
2207 int shapenumber,
2208 int curfontwidth,
2209 int FontHeight,
2210 int curFontHeight,
2211 Color fgcolor,
2212 Color bgcolor,
2213 bool clear)
2215 if (!lfb || shapenumber < 0x20 || shapenumber > 0x7e || (shapenumber == 0x7e && clear))
2216 return;
2218 unsigned char *p = aShapes[shapenumber - 0x20];
2220 if (*p == S_INV)
2222 int t = fgcolor;
2223 fgcolor = bgcolor;
2224 bgcolor = t;
2225 p++;
2228 if (clear)
2229 FillRect(lfb, xres, x, y, curfontwidth, FontHeight, bgcolor);
2231 while (*p != S_END)
2233 switch (*p++)
2235 case S_FHL:
2237 int offset = ShapeCoord(*p++, curfontwidth, curFontHeight);
2238 DrawHLine(lfb, xres, x, y + offset, curfontwidth, fgcolor);
2239 break;
2241 case S_FVL:
2243 int offset = ShapeCoord(*p++, curfontwidth, curFontHeight);
2244 DrawVLine(lfb,xres,x + offset, y, FontHeight, fgcolor);
2245 break;
2247 case S_FLH:
2248 FlipHorz(lfb,xres,x,y,curfontwidth, FontHeight);
2249 break;
2250 case S_FLV:
2251 FlipVert(lfb,xres,x,y,curfontwidth, FontHeight);
2252 break;
2253 case S_BOX:
2255 int xo = ShapeCoord(*p++, curfontwidth, curFontHeight);
2256 int yo = ShapeCoord(*p++, curfontwidth, curFontHeight);
2257 int w = ShapeCoord(*p++, curfontwidth, curFontHeight);
2258 int h = ShapeCoord(*p++, curfontwidth, curFontHeight);
2259 FillRect(lfb,xres,x + xo, y + yo, w, h, fgcolor);
2260 break;
2262 case S_TRA:
2264 int x0 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2265 int y0 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2266 int l0 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2267 int x1 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2268 int y1 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2269 int l1 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2270 FillTrapez(lfb, xres,x + x0, y + y0, l0, x1-x0, y1-y0, l1, fgcolor);
2271 break;
2273 case S_BTR:
2275 int x0 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2276 int y0 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2277 int l0 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2278 int x1 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2279 int y1 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2280 int l1 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2281 FillTrapez(lfb, xres, x + x0, y + y0, l0, x1-x0, y1-y0, l1, bgcolor);
2282 break;
2284 case S_LNK:
2286 DrawShape(lfb,xres,x, y, ShapeCoord(*p, curfontwidth, curFontHeight), curfontwidth, FontHeight, curFontHeight, fgcolor, bgcolor, false);
2287 break;
2289 default:
2290 break;
2295 void CTeletextDecoder::RenderCharIntern(TextRenderInfo_t* RenderInfo, int Char, TextPageAttr_t *Attribute, int zoom, int yoffset)
2297 int Row, Pitch;
2298 int glyph;
2299 Color bgcolor, fgcolor;
2300 int factor, xfactor;
2301 unsigned char *sbitbuffer;
2303 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
2305 int national_subset_local = m_txtCache->NationalSubset;
2306 int curfontwidth = GetCurFontWidth();
2307 int t = curfontwidth;
2308 m_RenderInfo.PosX += t;
2309 int curfontwidth2 = GetCurFontWidth();
2310 m_RenderInfo.PosX -= t;
2311 int alphachar = RenderChar(m_TextureBuffer+(yoffset)*m_RenderInfo.Width, m_RenderInfo.Width, Char, &m_RenderInfo.PosX, m_RenderInfo.PosY, Attribute, zoom > 0, curfontwidth, curfontwidth2, m_RenderInfo.FontHeight, m_RenderInfo.TranspMode, m_RenderInfo.axdrcs, m_Ascender);
2312 if (alphachar <= 0) return;
2314 if (zoom && Attribute->doubleh)
2315 factor = 4;
2316 else if (zoom || Attribute->doubleh)
2317 factor = 2;
2318 else
2319 factor = 1;
2321 fgcolor = GetColorRGB((enumTeletextColor)Attribute->fg);
2322 if (m_RenderInfo.TranspMode && m_RenderInfo.PosY < 24*m_RenderInfo.FontHeight)
2324 bgcolor = GetColorRGB(TXT_ColorTransp);
2326 else
2328 bgcolor = GetColorRGB((enumTeletextColor)Attribute->bg);
2331 if (Attribute->doublew)
2333 curfontwidth += curfontwidth2;
2334 xfactor = 2;
2336 else
2337 xfactor = 1;
2339 // Check if the alphanumeric char has diacritical marks (or results from composing chars) or
2340 // on the other hand it is just a simple alphanumeric char
2341 if (!Attribute->diacrit)
2343 Char = alphachar;
2345 else
2347 if ((national_subset_local == NAT_SC) || (national_subset_local == NAT_RB) ||
2348 (national_subset_local == NAT_UA))
2349 Char = G2table[1][0x20 + Attribute->diacrit];
2350 else if (national_subset_local == NAT_GR)
2351 Char = G2table[2][0x20 + Attribute->diacrit];
2352 else if (national_subset_local == NAT_HB)
2353 Char = G2table[3][0x20 + Attribute->diacrit];
2354 else if (national_subset_local == NAT_AR)
2355 Char = G2table[4][0x20 + Attribute->diacrit];
2356 else
2357 Char = G2table[0][0x20 + Attribute->diacrit];
2359 // use harfbuzz to combine the diacritical mark with the alphanumeric char
2360 // fallback to the alphanumeric char if composition fails
2361 hb_unicode_funcs_t* ufuncs = hb_unicode_funcs_get_default();
2362 hb_codepoint_t composedChar;
2363 const hb_bool_t isComposed = hb_unicode_compose(ufuncs, alphachar, Char, &composedChar);
2364 Char = isComposed ? composedChar : alphachar;
2367 /* render char */
2368 if (!(glyph = FT_Get_Char_Index(m_Face, Char)))
2370 CLog::Log(LOGERROR, "{}: <FT_Get_Char_Index for Char {:x} \"{}\" failed", __FUNCTION__,
2371 alphachar, alphachar);
2373 FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY + yoffset, curfontwidth, factor*m_RenderInfo.FontHeight, bgcolor);
2374 m_RenderInfo.PosX += curfontwidth;
2375 return;
2378 if (FTC_SBitCache_Lookup(m_Cache, &m_TypeTTF, glyph, &m_sBit, &m_anode) != 0)
2380 FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY + yoffset, curfontwidth, m_RenderInfo.FontHeight, bgcolor);
2381 m_RenderInfo.PosX += curfontwidth;
2382 return;
2385 sbitbuffer = m_sBit->buffer;
2387 int backupTTFshiftY = m_RenderInfo.TTFShiftY;
2388 if (national_subset_local == NAT_AR)
2389 m_RenderInfo.TTFShiftY = backupTTFshiftY - 2; // for arabic TTF font should be shifted up slightly
2391 Color* p;
2392 int f; /* running counter for zoom factor */
2393 int he = m_sBit->height; // sbit->height should not be altered, I guess
2394 Row = factor * (m_Ascender - m_sBit->top + m_RenderInfo.TTFShiftY);
2395 if (Row < 0)
2397 sbitbuffer -= m_sBit->pitch*Row;
2398 he += Row;
2399 Row = 0;
2401 else
2403 FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY + yoffset, curfontwidth, Row, bgcolor); /* fill upper margin */
2406 if (m_Ascender - m_sBit->top + m_RenderInfo.TTFShiftY + he > m_RenderInfo.FontHeight)
2407 he = m_RenderInfo.FontHeight - m_Ascender + m_sBit->top - m_RenderInfo.TTFShiftY; /* limit char height to defined/calculated FontHeight */
2408 if (he < 0) he = m_RenderInfo.FontHeight;
2410 p = m_TextureBuffer + m_RenderInfo.PosX + (yoffset + m_RenderInfo.PosY + Row) * m_RenderInfo.Width; /* running pointer into framebuffer */
2411 for (Row = he; Row; Row--) /* row counts up, but down may be a little faster :) */
2413 int pixtodo = m_sBit->width;
2414 Color* pstart = p;
2416 for (int Bit = xfactor * (m_sBit->left + m_RenderInfo.TTFShiftX); Bit > 0; Bit--) /* fill left margin */
2418 for (f = factor-1; f >= 0; f--)
2419 *(p + f*m_RenderInfo.Width) = bgcolor;
2420 p++;
2423 for (Pitch = m_sBit->pitch; Pitch; Pitch--)
2425 for (int Bit = 0x80; Bit; Bit >>= 1)
2427 Color color;
2429 if (--pixtodo < 0)
2430 break;
2432 if (*sbitbuffer & Bit) /* bit set -> foreground */
2433 color = fgcolor;
2434 else /* bit not set -> background */
2435 color = bgcolor;
2437 for (f = factor-1; f >= 0; f--)
2438 *(p + f*m_RenderInfo.Width) = color;
2439 p++;
2441 if (xfactor > 1) /* double width */
2443 for (f = factor-1; f >= 0; f--)
2444 *(p + f*m_RenderInfo.Width) = color;
2445 p++;
2448 sbitbuffer++;
2450 for (int Bit = (curfontwidth - xfactor*(m_sBit->width + m_sBit->left + m_RenderInfo.TTFShiftX));
2451 Bit > 0; Bit--) /* fill rest of char width */
2453 for (f = factor-1; f >= 0; f--)
2454 *(p + f*m_RenderInfo.Width) = bgcolor;
2455 p++;
2458 p = pstart + factor*m_RenderInfo.Width;
2461 Row = m_Ascender - m_sBit->top + he + m_RenderInfo.TTFShiftY;
2462 FillRect(m_TextureBuffer,
2463 m_RenderInfo.Width,
2464 m_RenderInfo.PosX,
2465 m_RenderInfo.PosY + yoffset + Row * factor,
2466 curfontwidth,
2467 (m_RenderInfo.FontHeight - Row) * factor,
2468 bgcolor); /* fill lower margin */
2470 if (Attribute->underline)
2471 FillRect(m_TextureBuffer,
2472 m_RenderInfo.Width,
2473 m_RenderInfo.PosX,
2474 m_RenderInfo.PosY + yoffset + (m_RenderInfo.FontHeight-2)* factor,
2475 curfontwidth,
2476 2*factor,
2477 fgcolor); /* underline char */
2479 m_RenderInfo.PosX += curfontwidth;
2480 m_RenderInfo.TTFShiftY = backupTTFshiftY; // restore TTFShiftY
2483 int CTeletextDecoder::RenderChar(
2484 Color* buffer, // pointer to render buffer, min. FontHeight*2*xres
2485 int xres, // length of 1 line in render buffer
2486 int Char, // character to render
2487 int*
2488 pPosX, // left border for rendering relative to *buffer, will be set to right border after rendering
2489 int PosY, // vertical position of char in *buffer
2490 TextPageAttr_t* Attribute, // Attributes of Char
2491 bool zoom, // 1= character will be rendered in double height
2492 int curfontwidth, // rendering width of character
2493 int curfontwidth2, // rendering width of next character (needed for doublewidth)
2494 int FontHeight, // height of character
2495 bool transpmode, // 1= transparent display
2496 unsigned char* axdrcs, // width and height of DRCS-chars
2497 int Ascender) // Ascender of font
2499 Color bgcolor, fgcolor;
2500 int factor, xfactor;
2502 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
2504 int national_subset_local = m_txtCache->NationalSubset;
2505 int ymosaic[4];
2506 ymosaic[0] = 0; /* y-offsets for 2*3 mosaic */
2507 ymosaic[1] = (FontHeight + 1) / 3;
2508 ymosaic[2] = (FontHeight * 2 + 1) / 3;
2509 ymosaic[3] = FontHeight;
2511 if (Attribute->setX26)
2513 national_subset_local = 0; // no national subset
2516 // G0+G2 set designation
2517 if (Attribute->setG0G2 != 0x3f)
2519 switch (Attribute->setG0G2)
2521 case 0x20 :
2522 national_subset_local = NAT_SC;
2523 break;
2524 case 0x24 :
2525 national_subset_local = NAT_RB;
2526 break;
2527 case 0x25 :
2528 national_subset_local = NAT_UA;
2529 break;
2530 case 0x37:
2531 national_subset_local = NAT_GR;
2532 break;
2533 case 0x55:
2534 national_subset_local = NAT_HB;
2535 break;
2536 case 0x47:
2537 case 0x57:
2538 national_subset_local = NAT_AR;
2539 break;
2540 default:
2541 national_subset_local = CountryConversionTable[Attribute->setG0G2 & 0x07];
2542 break;
2546 if (Attribute->charset == C_G0S) // use secondary charset
2547 national_subset_local = m_txtCache->NationalSubsetSecondary;
2548 if (zoom && Attribute->doubleh)
2549 factor = 4;
2550 else if (zoom || Attribute->doubleh)
2551 factor = 2;
2552 else
2553 factor = 1;
2555 if (Attribute->doublew)
2557 curfontwidth += curfontwidth2;
2558 xfactor = 2;
2560 else
2561 xfactor = 1;
2563 if (Char == 0xFF) /* skip doubleheight chars in lower line */
2565 *pPosX += curfontwidth;
2566 return -1;
2569 /* get colors */
2570 if (Attribute->inverted)
2572 int t = Attribute->fg;
2573 Attribute->fg = Attribute->bg;
2574 Attribute->bg = t;
2576 fgcolor = GetColorRGB((enumTeletextColor)Attribute->fg);
2577 if (transpmode == true && PosY < 24*FontHeight)
2579 bgcolor = GetColorRGB(TXT_ColorTransp);
2581 else
2583 bgcolor = GetColorRGB((enumTeletextColor)Attribute->bg);
2586 /* handle mosaic */
2587 if ((Attribute->charset == C_G1C || Attribute->charset == C_G1S) &&
2588 ((Char&0xA0) == 0x20))
2590 int w1 = (curfontwidth / 2 ) *xfactor;
2591 int w2 = (curfontwidth - w1) *xfactor;
2593 Char = (Char & 0x1f) | ((Char & 0x40) >> 1);
2594 if (Attribute->charset == C_G1S) /* separated mosaic */
2596 for (int y = 0; y < 3; y++)
2598 FillRectMosaicSeparated(buffer, xres,*pPosX, PosY + ymosaic[y]*factor, w1, (ymosaic[y+1] - ymosaic[y])*factor, fgcolor, bgcolor, Char & 0x01);
2599 FillRectMosaicSeparated(buffer, xres,*pPosX + w1, PosY + ymosaic[y]*factor, w2, (ymosaic[y+1] - ymosaic[y])*factor, fgcolor, bgcolor, Char & 0x02);
2600 Char >>= 2;
2603 else
2605 for (int y = 0; y < 3; y++)
2607 FillRect(buffer, xres, *pPosX, PosY + ymosaic[y]*factor, w1, (ymosaic[y+1] - ymosaic[y])*factor, (Char & 0x01) ? fgcolor : bgcolor);
2608 FillRect(buffer, xres, *pPosX + w1, PosY + ymosaic[y]*factor, w2, (ymosaic[y+1] - ymosaic[y])*factor, (Char & 0x02) ? fgcolor : bgcolor);
2609 Char >>= 2;
2613 *pPosX += curfontwidth;
2614 return 0;
2617 if (Attribute->charset == C_G3)
2619 if (Char < 0x20 || Char > 0x7d)
2621 Char = 0x20;
2623 else
2625 if (*aShapes[Char - 0x20] == S_CHR)
2627 unsigned char *p = aShapes[Char - 0x20];
2628 Char = (*(p+1) <<8) + (*(p+2));
2630 else if (*aShapes[Char - 0x20] == S_ADT)
2632 if (buffer)
2634 int x,y,f,c;
2635 Color* p = buffer + *pPosX + PosY * xres;
2636 for (y=0; y<FontHeight;y++)
2638 for (f=0; f<factor; f++)
2640 for (x=0; x<curfontwidth*xfactor;x++)
2642 c = (y&4 ? (x/3)&1 :((x+3)/3)&1);
2643 *(p+x) = (c ? fgcolor : bgcolor);
2645 p += xres;
2649 *pPosX += curfontwidth;
2650 return 0;
2652 else
2654 DrawShape(buffer, xres,*pPosX, PosY, Char, curfontwidth, FontHeight, factor*FontHeight, fgcolor, bgcolor, true);
2655 *pPosX += curfontwidth;
2656 return 0;
2660 else if (Attribute->charset >= C_OFFSET_DRCS)
2662 TextCachedPage_t *pcache = m_txtCache->astCachetable[(Attribute->charset & 0x10) ? m_txtCache->drcs : m_txtCache->gdrcs][Attribute->charset & 0x0f];
2663 if (pcache)
2665 unsigned char drcs_data[23*40];
2666 auto& components = CServiceBroker::GetAppComponents();
2667 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
2668 appPlayer->LoadPage((Attribute->charset & 0x10) ? m_txtCache->drcs : m_txtCache->gdrcs,
2669 Attribute->charset & 0x0f, drcs_data);
2670 unsigned char *p;
2671 if (Char < 23*2)
2672 p = drcs_data + 20*Char;
2673 else if (pcache->pageinfo.p24)
2674 p = pcache->pageinfo.p24 + 20*(Char - 23*2);
2675 else
2677 FillRect(buffer, xres,*pPosX, PosY, curfontwidth, factor*FontHeight, bgcolor);
2678 *pPosX += curfontwidth;
2679 return 0;
2681 axdrcs[12] = curfontwidth; /* adjust last x-offset according to position, FIXME: double width */
2682 RenderDRCS(xres, p, buffer + *pPosX + PosY * xres, axdrcs, fgcolor, bgcolor);
2684 else
2686 FillRect(buffer,xres,*pPosX, PosY, curfontwidth, factor*FontHeight, bgcolor);
2688 *pPosX += curfontwidth;
2689 return 0;
2691 else if (Attribute->charset == C_G2 && Char >= 0x20 && Char <= 0x7F)
2693 if ((national_subset_local == NAT_SC) || (national_subset_local == NAT_RB) || (national_subset_local == NAT_UA))
2694 Char = G2table[1][Char-0x20];
2695 else if (national_subset_local == NAT_GR)
2696 Char = G2table[2][Char-0x20];
2697 else if (national_subset_local == NAT_AR)
2698 Char = G2table[3][Char-0x20];
2699 else
2700 Char = G2table[0][Char-0x20];
2702 //if (Char == 0x7F)
2704 // FillRect(buffer,xres,*pPosX, PosY, curfontwidth, factor*Ascender, fgcolor);
2705 // FillRect(buffer,xres,*pPosX, PosY + factor*Ascender, curfontwidth, factor*(FontHeight-Ascender), bgcolor);
2706 // *pPosX += curfontwidth;
2707 // return 0;
2710 else if (national_subset_local == NAT_SC && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for serbian/croatian */
2711 Char = G0table[0][Char-0x20];
2712 else if (national_subset_local == NAT_RB && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for russian/bulgarian */
2713 Char = G0table[1][Char-0x20];
2714 else if (national_subset_local == NAT_UA && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for ukrainian */
2715 Char = G0table[2][Char-0x20];
2716 else if (national_subset_local == NAT_GR && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for greek */
2717 Char = G0table[3][Char-0x20];
2718 else if (national_subset_local == NAT_HB && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for hebrew */
2719 Char = G0table[4][Char-0x20];
2720 else if (national_subset_local == NAT_AR && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for arabic */
2721 Char = G0table[5][Char-0x20];
2722 else
2724 /* load char */
2725 switch (Char)
2727 case 0x00:
2728 case 0x20:
2729 FillRect(buffer, xres, *pPosX, PosY, curfontwidth, factor*FontHeight, bgcolor);
2730 *pPosX += curfontwidth;
2731 return -3;
2732 case 0x23:
2733 case 0x24:
2734 Char = nationaltable23[national_subset_local][Char-0x23];
2735 break;
2736 case 0x40:
2737 Char = nationaltable40[national_subset_local];
2738 break;
2739 case 0x5B:
2740 case 0x5C:
2741 case 0x5D:
2742 case 0x5E:
2743 case 0x5F:
2744 case 0x60:
2745 Char = nationaltable5b[national_subset_local][Char-0x5B];
2746 break;
2747 case 0x7B:
2748 case 0x7C:
2749 case 0x7D:
2750 case 0x7E:
2751 Char = nationaltable7b[national_subset_local][Char-0x7B];
2752 break;
2753 case 0x7F:
2754 FillRect(buffer,xres,*pPosX, PosY , curfontwidth, factor*Ascender, fgcolor);
2755 FillRect(buffer,xres,*pPosX, PosY + factor*Ascender, curfontwidth, factor*(FontHeight-Ascender), bgcolor);
2756 *pPosX += curfontwidth;
2757 return 0;
2758 case 0xE0: /* |- */
2759 DrawHLine(buffer,xres,*pPosX, PosY, curfontwidth, fgcolor);
2760 DrawVLine(buffer,xres,*pPosX, PosY +1, FontHeight -1, fgcolor);
2761 FillRect(buffer,xres,*pPosX +1, PosY +1, curfontwidth-1, FontHeight-1, bgcolor);
2762 *pPosX += curfontwidth;
2763 return 0;
2764 case 0xE1: /* - */
2765 DrawHLine(buffer,xres,*pPosX, PosY, curfontwidth, fgcolor);
2766 FillRect(buffer,xres,*pPosX, PosY +1, curfontwidth, FontHeight-1, bgcolor);
2767 *pPosX += curfontwidth;
2768 return 0;
2769 case 0xE2: /* -| */
2770 DrawHLine(buffer,xres,*pPosX, PosY, curfontwidth, fgcolor);
2771 DrawVLine(buffer,xres,*pPosX + curfontwidth -1, PosY +1, FontHeight -1, fgcolor);
2772 FillRect(buffer,xres,*pPosX, PosY +1, curfontwidth-1, FontHeight-1, bgcolor);
2773 *pPosX += curfontwidth;
2774 return 0;
2775 case 0xE3: /* | */
2776 DrawVLine(buffer,xres,*pPosX, PosY, FontHeight, fgcolor);
2777 FillRect(buffer,xres,*pPosX +1, PosY, curfontwidth -1, FontHeight, bgcolor);
2778 *pPosX += curfontwidth;
2779 return 0;
2780 case 0xE4: /* | */
2781 DrawVLine(buffer,xres,*pPosX + curfontwidth -1, PosY, FontHeight, fgcolor);
2782 FillRect(buffer,xres,*pPosX, PosY, curfontwidth -1, FontHeight, bgcolor);
2783 *pPosX += curfontwidth;
2784 return 0;
2785 case 0xE5: /* |_ */
2786 DrawHLine(buffer,xres,*pPosX, PosY + FontHeight -1, curfontwidth, fgcolor);
2787 DrawVLine(buffer,xres,*pPosX, PosY, FontHeight -1, fgcolor);
2788 FillRect(buffer,xres,*pPosX +1, PosY, curfontwidth-1, FontHeight-1, bgcolor);
2789 *pPosX += curfontwidth;
2790 return 0;
2791 case 0xE6: /* _ */
2792 DrawHLine(buffer,xres,*pPosX, PosY + FontHeight -1, curfontwidth, fgcolor);
2793 FillRect(buffer,xres,*pPosX, PosY, curfontwidth, FontHeight-1, bgcolor);
2794 *pPosX += curfontwidth;
2795 return 0;
2796 case 0xE7: /* _| */
2797 DrawHLine(buffer,xres,*pPosX, PosY + FontHeight -1, curfontwidth, fgcolor);
2798 DrawVLine(buffer,xres,*pPosX + curfontwidth -1, PosY, FontHeight -1, fgcolor);
2799 FillRect(buffer,xres,*pPosX, PosY, curfontwidth-1, FontHeight-1, bgcolor);
2800 *pPosX += curfontwidth;
2801 return 0;
2802 case 0xE8: /* Ii */
2803 FillRect(buffer,xres,*pPosX +1, PosY, curfontwidth -1, FontHeight, bgcolor);
2804 for (int Row=0; Row < curfontwidth/2; Row++)
2805 DrawVLine(buffer,xres,*pPosX + Row, PosY + Row, FontHeight - Row, fgcolor);
2806 *pPosX += curfontwidth;
2807 return 0;
2808 case 0xE9: /* II */
2809 FillRect(buffer,xres,*pPosX, PosY, curfontwidth/2, FontHeight, fgcolor);
2810 FillRect(buffer,xres,*pPosX + curfontwidth/2, PosY, (curfontwidth+1)/2, FontHeight, bgcolor);
2811 *pPosX += curfontwidth;
2812 return 0;
2813 case 0xEA: /* ∞ */
2814 FillRect(buffer,xres,*pPosX, PosY, curfontwidth, FontHeight, bgcolor);
2815 FillRect(buffer,xres,*pPosX, PosY, curfontwidth/2, curfontwidth/2, fgcolor);
2816 *pPosX += curfontwidth;
2817 return 0;
2818 case 0xEB: /* ¨ */
2819 FillRect(buffer,xres,*pPosX, PosY +1, curfontwidth, FontHeight -1, bgcolor);
2820 for (int Row=0; Row < curfontwidth/2; Row++)
2821 DrawHLine(buffer,xres,*pPosX + Row, PosY + Row, curfontwidth - Row, fgcolor);
2822 *pPosX += curfontwidth;
2823 return 0;
2824 case 0xEC: /* -- */
2825 FillRect(buffer, xres,*pPosX, PosY, curfontwidth, curfontwidth/2, fgcolor);
2826 FillRect(buffer, xres,*pPosX, PosY + curfontwidth/2, curfontwidth, FontHeight - curfontwidth/2, bgcolor);
2827 *pPosX += curfontwidth;
2828 return 0;
2829 case 0xED:
2830 case 0xEE:
2831 case 0xEF:
2832 case 0xF0:
2833 case 0xF1:
2834 case 0xF2:
2835 case 0xF3:
2836 case 0xF4:
2837 Char = arrowtable[Char - 0xED];
2838 break;
2839 default:
2840 break;
2843 if (Char <= 0x20)
2845 FillRect(buffer, xres, *pPosX, PosY, curfontwidth, factor*FontHeight, bgcolor);
2846 *pPosX += curfontwidth;
2847 return -2;
2849 return Char; // Char is an alphanumeric unicode character
2852 TextPageinfo_t* CTeletextDecoder::DecodePage(bool showl25, // 1=decode Level2.5-graphics
2853 unsigned char* PageChar, // page buffer, min. 25*40
2854 TextPageAttr_t *PageAtrb, // attribute buffer, min 25*40
2855 bool HintMode, // 1=show hidden information
2856 bool showflof) // 1=decode FLOF-line
2858 int col;
2859 int hold, dhset;
2860 int foreground, background, doubleheight, doublewidth, charset, previous_charset, mosaictype, IgnoreAtBlackBgSubst, concealed, flashmode, boxwin;
2861 unsigned char held_mosaic, *p;
2862 TextCachedPage_t *pCachedPage;
2864 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
2866 /* copy page to decode buffer */
2867 if (m_txtCache->SubPageTable[m_txtCache->Page] == 0xff) /* not cached: do nothing */
2868 return NULL;
2870 if (m_txtCache->ZapSubpageManual)
2871 pCachedPage = m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPage];
2872 else
2873 pCachedPage = m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPageTable[m_txtCache->Page]];
2874 if (!pCachedPage) /* not cached: do nothing */
2875 return nullptr;
2877 auto& components = CServiceBroker::GetAppComponents();
2878 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
2879 appPlayer->LoadPage(m_txtCache->Page, m_txtCache->SubPage, &PageChar[40]);
2881 memcpy(&PageChar[8], pCachedPage->p0, 24); /* header line without TimeString */
2883 TextPageinfo_t* PageInfo = &(pCachedPage->pageinfo);
2884 if (PageInfo->p24)
2885 memcpy(&PageChar[24*40], PageInfo->p24, 40); /* line 25 for FLOF */
2887 /* copy TimeString */
2888 memcpy(&PageChar[32], &m_txtCache->TimeString, 8);
2890 bool boxed;
2891 /* check for newsflash & subtitle */
2892 if (PageInfo->boxed && IsDec(m_txtCache->Page))
2893 boxed = true;
2894 else
2895 boxed = false;
2898 /* modify header */
2899 if (boxed)
2901 memset(PageChar, ' ', 40);
2903 else
2905 memset(PageChar, ' ', 8);
2906 CDVDTeletextTools::Hex2Str((char*)PageChar+3, m_txtCache->Page);
2907 if (m_txtCache->SubPage)
2909 *(PageChar+4) ='/';
2910 *(PageChar+5) ='0';
2911 CDVDTeletextTools::Hex2Str((char*)PageChar+6, m_txtCache->SubPage);
2915 if (!IsDec(m_txtCache->Page))
2917 TextPageAttr_t atr = { TXT_ColorWhite , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f};
2918 if (PageInfo->function == FUNC_MOT) /* magazine organization table */
2920 for (col = 0; col < 24*40; col++)
2921 PageAtrb[col] = atr;
2922 for (col = 40; col < 24*40; col++)
2923 PageChar[col] = number2char(PageChar[col]);
2924 return PageInfo; /* don't interpret irregular pages */
2926 else if (PageInfo->function == FUNC_GPOP || PageInfo->function == FUNC_POP) /* object definitions */
2928 for (int col = 0; col < 24*40; col++)
2929 PageAtrb[col] = atr;
2931 p = PageChar + 40;
2932 for (int row = 1; row < 12; row++)
2934 *p++ = number2char(row); /* first column: number (0-9, A-..) */
2935 for (int col = 1; col < 40; col += 3)
2937 int d = CDVDTeletextTools::deh24(p);
2938 if (d < 0)
2940 memcpy(p, "???", 3);
2941 p += 3;
2943 else
2945 *p++ = number2char((d >> 6) & 0x1f); /* mode */
2946 *p++ = number2char(d & 0x3f); /* address */
2947 *p++ = number2char((d >> 11) & 0x7f); /* data */
2951 return PageInfo; /* don't interpret irregular pages */
2953 else if (PageInfo->function == FUNC_GDRCS || PageInfo->function == FUNC_DRCS) /* character definitions */
2955 return PageInfo; /* don't interpret irregular pages */
2957 else
2959 int h, parityerror = 0;
2961 for (int i = 0; i < 8; i++)
2962 PageAtrb[i] = atr;
2964 /* decode parity/hamming */
2965 for (unsigned int i = 40; i < TELETEXT_PAGE_SIZE; i++)
2967 PageAtrb[i] = atr;
2968 p = PageChar + i;
2969 h = dehamming[*p];
2970 if (parityerror && h != 0xFF) /* if no regular page (after any parity error) */
2971 CDVDTeletextTools::Hex2Str((char*)p, h); /* first try dehamming */
2972 else
2974 if (*p == ' ' || deparity[*p] != ' ') /* correct parity */
2975 *p &= 127;
2976 else
2978 parityerror = 1;
2979 if (h != 0xFF) /* first parity error: try dehamming */
2980 CDVDTeletextTools::Hex2Str((char*)p, h);
2981 else
2982 *p = ' ';
2986 if (parityerror)
2988 return PageInfo; /* don't interpret irregular pages */
2992 int mosaic_pending,esc_pending;
2993 /* decode */
2994 for (int row = 0; row < ((showflof && PageInfo->p24) ? 25 : 24); row++)
2996 /* start-of-row default conditions */
2997 foreground = TXT_ColorWhite;
2998 background = TXT_ColorBlack;
2999 doubleheight = 0;
3000 doublewidth = 0;
3001 charset = previous_charset = C_G0P; // remember charset for switching back after mosaic charset was used
3002 mosaictype = 0;
3003 concealed = 0;
3004 flashmode = 0;
3005 hold = 0;
3006 boxwin = 0;
3007 held_mosaic = ' ';
3008 dhset = 0;
3009 IgnoreAtBlackBgSubst = 0;
3010 mosaic_pending = esc_pending = 0; // we need to render at least one mosaic char if 'esc' is received immediately after mosaic charset switch on
3012 if (boxed && memchr(&PageChar[row*40], start_box, 40) == 0)
3014 foreground = TXT_ColorTransp;
3015 background = TXT_ColorTransp;
3018 for (int col = 0; col < 40; col++)
3020 int index = row*40 + col;
3022 PageAtrb[index].fg = foreground;
3023 PageAtrb[index].bg = background;
3024 PageAtrb[index].charset = charset;
3025 PageAtrb[index].doubleh = doubleheight;
3026 PageAtrb[index].doublew = (col < 39 ? doublewidth : 0);
3027 PageAtrb[index].IgnoreAtBlackBgSubst = IgnoreAtBlackBgSubst;
3028 PageAtrb[index].concealed = concealed;
3029 PageAtrb[index].flashing = flashmode;
3030 PageAtrb[index].boxwin = boxwin;
3031 PageAtrb[index].inverted = 0; // only relevant for Level 2.5
3032 PageAtrb[index].underline = 0; // only relevant for Level 2.5
3033 PageAtrb[index].diacrit = 0; // only relevant for Level 2.5
3034 PageAtrb[index].setX26 = 0; // only relevant for Level 2.5
3035 PageAtrb[index].setG0G2 = 0x3f; // only relevant for Level 2.5
3037 if (PageChar[index] < ' ')
3039 if (esc_pending) { // mosaic char has been rendered and we can switch charsets
3040 charset = previous_charset;
3041 if (charset == C_G0P)
3042 charset = previous_charset = C_G0S;
3043 else if (charset == C_G0S)
3044 charset = previous_charset = C_G0P;
3045 esc_pending = 0;
3047 switch (PageChar[index])
3049 case alpha_black:
3050 case alpha_red:
3051 case alpha_green:
3052 case alpha_yellow:
3053 case alpha_blue:
3054 case alpha_magenta:
3055 case alpha_cyan:
3056 case alpha_white:
3057 concealed = 0;
3058 foreground = PageChar[index] - alpha_black + TXT_ColorBlack;
3059 if (col == 0 && PageChar[index] == alpha_white)
3060 PageAtrb[index].fg = TXT_ColorBlack; // indicate level 1 color change on column 0; (hack)
3061 if ((charset!=C_G0P) && (charset!=C_G0S)) // we need to change charset to state it was before mosaic
3062 charset = previous_charset;
3063 break;
3065 case flash:
3066 flashmode = 1;
3067 break;
3069 case steady:
3070 flashmode = 0;
3071 PageAtrb[index].flashing = 0;
3072 break;
3074 case end_box:
3075 boxwin = 0;
3076 IgnoreAtBlackBgSubst = 0;
3077 break;
3079 case start_box:
3080 if (!boxwin)
3081 boxwin = 1;
3082 break;
3084 case normal_size:
3085 doubleheight = 0;
3086 doublewidth = 0;
3087 PageAtrb[index].doubleh = doubleheight;
3088 PageAtrb[index].doublew = doublewidth;
3089 break;
3091 case double_height:
3092 if (row < 23)
3094 doubleheight = 1;
3095 dhset = 1;
3097 doublewidth = 0;
3099 break;
3101 case double_width:
3102 if (col < 39)
3103 doublewidth = 1;
3104 doubleheight = 0;
3105 break;
3107 case double_size:
3108 if (row < 23)
3110 doubleheight = 1;
3111 dhset = 1;
3113 if (col < 39)
3114 doublewidth = 1;
3115 break;
3117 case mosaic_black:
3118 case mosaic_red:
3119 case mosaic_green:
3120 case mosaic_yellow:
3121 case mosaic_blue:
3122 case mosaic_magenta:
3123 case mosaic_cyan:
3124 case mosaic_white:
3125 concealed = 0;
3126 foreground = PageChar[index] - mosaic_black + TXT_ColorBlack;
3127 if ((charset==C_G0P) || (charset==C_G0S))
3128 previous_charset=charset;
3129 charset = mosaictype ? C_G1S : C_G1C;
3130 mosaic_pending = 1;
3131 break;
3133 case conceal:
3134 PageAtrb[index].concealed = 1;
3135 concealed = 1;
3136 if (!HintMode)
3138 foreground = background;
3139 PageAtrb[index].fg = foreground;
3141 break;
3143 case contiguous_mosaic:
3144 mosaictype = 0;
3145 if (charset == C_G1S)
3147 charset = C_G1C;
3148 PageAtrb[index].charset = charset;
3150 break;
3152 case separated_mosaic:
3153 mosaictype = 1;
3154 if (charset == C_G1C)
3156 charset = C_G1S;
3157 PageAtrb[index].charset = charset;
3159 break;
3161 case esc:
3162 if (!mosaic_pending) { // if mosaic is pending we need to wait before mosaic arrives
3163 if ((charset != C_G0P) && (charset != C_G0S)) // we need to switch to charset which was active before mosaic
3164 charset = previous_charset;
3165 if (charset == C_G0P)
3166 charset = previous_charset = C_G0S;
3167 else if (charset == C_G0S)
3168 charset = previous_charset = C_G0P;
3169 } else esc_pending = 1;
3170 break;
3172 case black_background:
3173 background = TXT_ColorBlack;
3174 IgnoreAtBlackBgSubst = 0;
3175 PageAtrb[index].bg = background;
3176 PageAtrb[index].IgnoreAtBlackBgSubst = IgnoreAtBlackBgSubst;
3177 break;
3179 case new_background:
3180 background = foreground;
3181 if (background == TXT_ColorBlack)
3182 IgnoreAtBlackBgSubst = 1;
3183 else
3184 IgnoreAtBlackBgSubst = 0;
3185 PageAtrb[index].bg = background;
3186 PageAtrb[index].IgnoreAtBlackBgSubst = IgnoreAtBlackBgSubst;
3187 break;
3189 case hold_mosaic:
3190 hold = 1;
3191 break;
3193 case release_mosaic:
3194 hold = 2;
3195 break;
3198 /* handle spacing attributes */
3199 if (hold && (PageAtrb[index].charset == C_G1C || PageAtrb[index].charset == C_G1S))
3200 PageChar[index] = held_mosaic;
3201 else
3202 PageChar[index] = ' ';
3204 if (hold == 2)
3205 hold = 0;
3207 else /* char >= ' ' */
3209 mosaic_pending = 0; // charset will be switched next if esc_pending
3210 /* set new held-mosaic char */
3211 if ((charset == C_G1C || charset == C_G1S) &&
3212 ((PageChar[index]&0xA0) == 0x20))
3213 held_mosaic = PageChar[index];
3214 if (PageAtrb[index].doubleh)
3215 PageChar[index + 40] = 0xFF;
3218 if (!(charset == C_G1C || charset == C_G1S))
3219 held_mosaic = ' '; /* forget if outside mosaic */
3221 } /* for col */
3223 /* skip row if doubleheight */
3224 if (row < 23 && dhset)
3226 for (int col = 0; col < 40; col++)
3228 int index = row*40 + col;
3229 PageAtrb[index+40].bg = PageAtrb[index].bg;
3230 PageAtrb[index+40].fg = TXT_ColorWhite;
3231 if (!PageAtrb[index].doubleh)
3232 PageChar[index+40] = ' ';
3233 PageAtrb[index+40].flashing = 0;
3234 PageAtrb[index+40].charset = C_G0P;
3235 PageAtrb[index+40].doubleh = 0;
3236 PageAtrb[index+40].doublew = 0;
3237 PageAtrb[index+40].IgnoreAtBlackBgSubst = 0;
3238 PageAtrb[index+40].concealed = 0;
3239 PageAtrb[index+40].flashing = 0;
3240 PageAtrb[index+40].boxwin = PageAtrb[index].boxwin;
3242 row++;
3244 } /* for row */
3245 m_txtCache->FullScrColor = TXT_ColorBlack;
3247 if (showl25)
3248 Eval_l25(PageChar, PageAtrb, HintMode);
3250 /* handle Black Background Color Substitution and transparency (CLUT1#0) */
3252 int o = 0;
3253 char bitmask ;
3255 for (unsigned char row : m_txtCache->FullRowColor)
3257 for (int c = 0; c < 40; c++)
3259 bitmask = (PageAtrb[o].bg == 0x08 ? 0x08 : 0x00) | (row == 0x08 ? 0x04 : 0x00) | (PageAtrb[o].boxwin <<1) | (int)boxed;
3260 switch (bitmask)
3262 case 0x08:
3263 case 0x0b:
3264 if (row == 0x08)
3265 PageAtrb[o].bg = m_txtCache->FullScrColor;
3266 else
3267 PageAtrb[o].bg = row;
3268 break;
3269 case 0x01:
3270 case 0x05:
3271 case 0x09:
3272 case 0x0a:
3273 case 0x0c:
3274 case 0x0d:
3275 case 0x0e:
3276 case 0x0f:
3277 PageAtrb[o].bg = TXT_ColorTransp;
3278 break;
3280 bitmask = (PageAtrb[o].fg == 0x08 ? 0x08 : 0x00) | (row == 0x08 ? 0x04 : 0x00) | (PageAtrb[o].boxwin <<1) | (int)boxed;
3281 switch (bitmask)
3283 case 0x08:
3284 case 0x0b:
3285 if (row == 0x08)
3286 PageAtrb[o].fg = m_txtCache->FullScrColor;
3287 else
3288 PageAtrb[o].fg = row;
3289 break;
3290 case 0x01:
3291 case 0x05:
3292 case 0x09:
3293 case 0x0a:
3294 case 0x0c:
3295 case 0x0d:
3296 case 0x0e:
3297 case 0x0f:
3298 PageAtrb[o].fg = TXT_ColorTransp;
3299 break;
3301 o++;
3305 return PageInfo;
3308 void CTeletextDecoder::Eval_l25(unsigned char* PageChar, TextPageAttr_t *PageAtrb, bool HintMode)
3310 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
3312 memset(m_txtCache->FullRowColor, 0, sizeof(m_txtCache->FullRowColor));
3313 m_txtCache->FullScrColor = TXT_ColorBlack;
3314 m_txtCache->ColorTable = NULL;
3316 if (!m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPage])
3317 return;
3319 /* normal page */
3320 if (IsDec(m_txtCache->Page))
3322 unsigned char APx0, APy0, APx, APy;
3323 TextPageinfo_t *pi = &(m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPage]->pageinfo);
3324 TextCachedPage_t *pmot = m_txtCache->astCachetable[(m_txtCache->Page & 0xf00) | 0xfe][0];
3325 int p26Received = 0;
3326 int BlackBgSubst = 0;
3327 int ColorTableRemapping = 0;
3329 m_txtCache->pop = m_txtCache->gpop = m_txtCache->drcs = m_txtCache->gdrcs = 0;
3331 if (pi->ext)
3333 TextExtData_t *e = pi->ext;
3335 if (e->p26[0])
3336 p26Received = 1;
3338 if (e->p27)
3340 Textp27_t *p27 = e->p27;
3341 if (p27[0].l25)
3342 m_txtCache->gpop = p27[0].page;
3343 if (p27[1].l25)
3344 m_txtCache->pop = p27[1].page;
3345 if (p27[2].l25)
3346 m_txtCache->gdrcs = p27[2].page;
3347 if (p27[3].l25)
3348 m_txtCache->drcs = p27[3].page;
3351 if (e->p28Received)
3353 m_txtCache->ColorTable = e->bgr;
3354 BlackBgSubst = e->BlackBgSubst;
3355 ColorTableRemapping = e->ColorTableRemapping;
3356 memset(m_txtCache->FullRowColor, e->DefRowColor, sizeof(m_txtCache->FullRowColor));
3357 m_txtCache->FullScrColor = e->DefScreenColor;
3358 m_txtCache->NationalSubset = SetNational(e->DefaultCharset);
3359 m_txtCache->NationalSubsetSecondary = SetNational(e->SecondCharset);
3360 } /* e->p28Received */
3363 if (!m_txtCache->ColorTable && m_txtCache->astP29[m_txtCache->Page >> 8])
3365 TextExtData_t *e = m_txtCache->astP29[m_txtCache->Page >> 8];
3366 m_txtCache->ColorTable = e->bgr;
3367 BlackBgSubst = e->BlackBgSubst;
3368 ColorTableRemapping = e->ColorTableRemapping;
3369 memset(m_txtCache->FullRowColor, e->DefRowColor, sizeof(m_txtCache->FullRowColor));
3370 m_txtCache->FullScrColor = e->DefScreenColor;
3371 m_txtCache->NationalSubset = SetNational(e->DefaultCharset);
3372 m_txtCache->NationalSubsetSecondary = SetNational(e->SecondCharset);
3375 if (ColorTableRemapping)
3377 for (int i = 0; i < 25*40; i++)
3379 PageAtrb[i].fg += MapTblFG[ColorTableRemapping - 1];
3380 if (!BlackBgSubst || PageAtrb[i].bg != TXT_ColorBlack || PageAtrb[i].IgnoreAtBlackBgSubst)
3381 PageAtrb[i].bg += MapTblBG[ColorTableRemapping - 1];
3385 /* determine ?pop/?drcs from MOT */
3386 if (pmot)
3388 unsigned char pmot_data[23*40];
3389 auto& components = CServiceBroker::GetAppComponents();
3390 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
3391 appPlayer->LoadPage((m_txtCache->Page & 0xf00) | 0xfe, 0, pmot_data);
3393 unsigned char *p = pmot_data; /* start of link data */
3394 int o = 2 * (((m_txtCache->Page & 0xf0) >> 4) * 10 + (m_txtCache->Page & 0x0f)); /* offset of links for current page */
3395 int opop = p[o] & 0x07; /* index of POP link */
3396 int odrcs = p[o+1] & 0x07; /* index of DRCS link */
3397 unsigned char obj[3*4*4]; /* types* objects * (triplet,packet,subp,high) */
3398 unsigned char type,ct, tstart = 4*4;
3399 memset(obj,0,sizeof(obj));
3401 if (p[o] & 0x08) /* GPOP data used */
3403 if (!m_txtCache->gpop || !(p[18*40] & 0x08)) /* no p27 data or higher prio of MOT link */
3405 m_txtCache->gpop = ((p[18*40] << 8) | (p[18*40+1] << 4) | p[18*40+2]) & 0x7ff;
3406 if ((m_txtCache->gpop & 0xff) == 0xff)
3407 m_txtCache->gpop = 0;
3408 else
3410 if (m_txtCache->gpop < 0x100)
3411 m_txtCache->gpop += 0x800;
3412 if (!p26Received)
3414 ct = 2;
3415 while (ct)
3417 ct--;
3418 type = (p[18*40+5] >> 2*ct) & 0x03;
3420 if (type == 0) continue;
3421 obj[(type-1)*(tstart)+ct*4 ] = 3 * ((p[18*40+7+ct*2] >> 1) & 0x03) + type; //triplet
3422 obj[(type-1)*(tstart)+ct*4+1] = ((p[18*40+7+ct*2] & 0x08) >> 3) + 1 ; //packet
3423 obj[(type-1)*(tstart)+ct*4+2] = p[18*40+6+ct*2] & 0x0f ; //subp
3424 obj[(type-1)*(tstart)+ct*4+3] = p[18*40+7+ct*2] & 0x01 ; //high
3430 if (opop) /* POP data used */
3432 opop = 18*40 + 10*opop; /* offset to POP link */
3433 if (!m_txtCache->pop || !(p[opop] & 0x08)) /* no p27 data or higher prio of MOT link */
3435 m_txtCache->pop = ((p[opop] << 8) | (p[opop+1] << 4) | p[opop+2]) & 0x7ff;
3436 if ((m_txtCache->pop & 0xff) == 0xff)
3437 m_txtCache->pop = 0;
3438 else
3440 if (m_txtCache->pop < 0x100)
3441 m_txtCache->pop += 0x800;
3442 if (!p26Received)
3444 ct = 2;
3445 while (ct)
3447 ct--;
3448 type = (p[opop+5] >> 2*ct) & 0x03;
3450 if (type == 0) continue;
3451 obj[(type-1)*(tstart)+(ct+2)*4 ] = 3 * ((p[opop+7+ct*2] >> 1) & 0x03) + type; //triplet
3452 obj[(type-1)*(tstart)+(ct+2)*4+1] = ((p[opop+7+ct*2] & 0x08) >> 3) + 1 ; //packet
3453 obj[(type-1)*(tstart)+(ct+2)*4+2] = p[opop+6+ct*2] ; //subp
3454 obj[(type-1)*(tstart)+(ct+2)*4+3] = p[opop+7+ct*2] & 0x01 ; //high
3460 // eval default objects in correct order
3461 for (int i = 0; i < 12; i++)
3463 if (obj[i*4] != 0)
3465 APx0 = APy0 = APx = APy = m_txtCache->tAPx = m_txtCache->tAPy = 0;
3466 Eval_NumberedObject(i % 4 > 1 ? m_txtCache->pop : m_txtCache->gpop, obj[i*4+2], obj[i*4+1], obj[i*4], obj[i*4+3], &APx, &APy, &APx0, &APy0, PageChar, PageAtrb);
3470 if (p[o+1] & 0x08) /* GDRCS data used */
3472 if (!m_txtCache->gdrcs || !(p[20*40] & 0x08)) /* no p27 data or higher prio of MOT link */
3474 m_txtCache->gdrcs = ((p[20*40] << 8) | (p[20*40+1] << 4) | p[20*40+2]) & 0x7ff;
3475 if ((m_txtCache->gdrcs & 0xff) == 0xff)
3476 m_txtCache->gdrcs = 0;
3477 else if (m_txtCache->gdrcs < 0x100)
3478 m_txtCache->gdrcs += 0x800;
3481 if (odrcs) /* DRCS data used */
3483 odrcs = 20*40 + 4*odrcs; /* offset to DRCS link */
3484 if (!m_txtCache->drcs || !(p[odrcs] & 0x08)) /* no p27 data or higher prio of MOT link */
3486 m_txtCache->drcs = ((p[odrcs] << 8) | (p[odrcs+1] << 4) | p[odrcs+2]) & 0x7ff;
3487 if ((m_txtCache->drcs & 0xff) == 0xff)
3488 m_txtCache->drcs = 0;
3489 else if (m_txtCache->drcs < 0x100)
3490 m_txtCache->drcs += 0x800;
3493 if (m_txtCache->astCachetable[m_txtCache->gpop][0])
3494 m_txtCache->astCachetable[m_txtCache->gpop][0]->pageinfo.function = FUNC_GPOP;
3495 if (m_txtCache->astCachetable[m_txtCache->pop][0])
3496 m_txtCache->astCachetable[m_txtCache->pop][0]->pageinfo.function = FUNC_POP;
3497 if (m_txtCache->astCachetable[m_txtCache->gdrcs][0])
3498 m_txtCache->astCachetable[m_txtCache->gdrcs][0]->pageinfo.function = FUNC_GDRCS;
3499 if (m_txtCache->astCachetable[m_txtCache->drcs][0])
3500 m_txtCache->astCachetable[m_txtCache->drcs][0]->pageinfo.function = FUNC_DRCS;
3501 } /* if mot */
3503 /* evaluate local extension data from p26 */
3504 if (p26Received)
3506 APx0 = APy0 = APx = APy = m_txtCache->tAPx = m_txtCache->tAPy = 0;
3507 Eval_Object(13 * (23-2 + 2), m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPage], &APx, &APy, &APx0, &APy0, OBJ_ACTIVE, &PageChar[40], PageChar, PageAtrb); /* 1st triplet p26/0 */
3511 int o = 0;
3512 for (unsigned char row : m_txtCache->FullRowColor)
3514 for (int c = 0; c < 40; c++)
3516 if (BlackBgSubst && PageAtrb[o].bg == TXT_ColorBlack && !(PageAtrb[o].IgnoreAtBlackBgSubst))
3518 if (row == 0x08)
3519 PageAtrb[o].bg = m_txtCache->FullScrColor;
3520 else
3521 PageAtrb[o].bg = row;
3523 o++;
3528 if (!HintMode)
3530 for (int i = 0; i < 25*40; i++)
3532 if (PageAtrb[i].concealed) PageAtrb[i].fg = PageAtrb[i].bg;
3535 } /* is_dec(page) */
3538 /* dump interpreted object data to stdout */
3539 /* in: 18 bit object data */
3540 /* out: termination info, >0 if end of object */
3541 void CTeletextDecoder::Eval_Object(int iONr, TextCachedPage_t *pstCachedPage,
3542 unsigned char *pAPx, unsigned char *pAPy,
3543 unsigned char *pAPx0, unsigned char *pAPy0,
3544 tObjType ObjType, unsigned char* pagedata, unsigned char* PageChar, TextPageAttr_t* PageAtrb)
3546 int iOData;
3547 int iONr1 = iONr + 1; /* don't terminate after first triplet */
3548 unsigned char drcssubp=0, gdrcssubp=0;
3549 signed char endcol = -1; /* last column to which to extend attribute changes */
3550 TextPageAttr_t attrPassive = { TXT_ColorWhite , TXT_ColorBlack , C_G0P, 0, 0, 1 ,0, 0, 0, 0, 0, 0, 0, 0x3f}; /* current attribute for passive objects */
3554 iOData = iTripletNumber2Data(iONr, pstCachedPage, pagedata); /* get triplet data, next triplet */
3555 if (iOData < 0) /* invalid number, not cached, or hamming error: terminate */
3556 break;
3558 if (endcol < 0)
3560 if (ObjType == OBJ_ACTIVE)
3562 endcol = 40;
3564 else if (ObjType == OBJ_ADAPTIVE) /* search end of line */
3566 for (int i = iONr; i <= 506; i++)
3568 int iTempOData = iTripletNumber2Data(i, pstCachedPage, pagedata); /* get triplet data, next triplet */
3569 int iAddress = (iTempOData ) & 0x3f;
3570 int iMode = (iTempOData >> 6) & 0x1f;
3571 //int iData = (iTempOData >> 11) & 0x7f;
3572 if (iTempOData < 0 || /* invalid number, not cached, or hamming error: terminate */
3573 (iAddress >= 40 /* new row: row address and */
3574 && (iMode == 0x01 || /* Full Row Color or */
3575 iMode == 0x04 || /* Set Active Position */
3576 (iMode >= 0x15 && iMode <= 0x17) || /* Object Definition */
3577 iMode == 0x17))) /* Object Termination */
3578 break;
3579 if (iAddress < 40 && iMode != 0x06)
3580 endcol = iAddress;
3584 iONr++;
3586 while (0 == Eval_Triplet(iOData, pstCachedPage, pAPx, pAPy, pAPx0, pAPy0, &drcssubp, &gdrcssubp, &endcol, &attrPassive, pagedata, PageChar, PageAtrb) || iONr1 == iONr); /* repeat until termination reached */
3589 void CTeletextDecoder::Eval_NumberedObject(int p, int s, int packet, int triplet, int high,
3590 unsigned char *pAPx, unsigned char *pAPy,
3591 unsigned char *pAPx0, unsigned char *pAPy0, unsigned char* PageChar, TextPageAttr_t* PageAtrb)
3593 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
3595 if (!packet || 0 == m_txtCache->astCachetable[p][s])
3596 return;
3598 unsigned char pagedata[23*40];
3599 auto& components = CServiceBroker::GetAppComponents();
3600 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
3601 appPlayer->LoadPage(p, s, pagedata);
3603 int idata = CDVDTeletextTools::deh24(pagedata + 40*(packet-1) + 1 + 3*triplet);
3604 int iONr;
3606 if (idata < 0) /* hamming error: ignore triplet */
3607 return;
3608 if (high)
3609 iONr = idata >> 9; /* triplet number of odd object data */
3610 else
3611 iONr = idata & 0x1ff; /* triplet number of even object data */
3612 if (iONr <= 506)
3614 Eval_Object(iONr, m_txtCache->astCachetable[p][s], pAPx, pAPy, pAPx0, pAPy0, (tObjType)(triplet % 3),pagedata, PageChar, PageAtrb);
3618 int CTeletextDecoder::Eval_Triplet(int iOData, TextCachedPage_t *pstCachedPage,
3619 unsigned char *pAPx, unsigned char *pAPy,
3620 unsigned char *pAPx0, unsigned char *pAPy0,
3621 unsigned char *drcssubp, unsigned char *gdrcssubp,
3622 signed char *endcol, TextPageAttr_t *attrPassive, unsigned char* pagedata, unsigned char* PageChar, TextPageAttr_t* PageAtrb)
3624 int iAddress = (iOData ) & 0x3f;
3625 int iMode = (iOData >> 6) & 0x1f;
3626 int iData = (iOData >> 11) & 0x7f;
3628 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
3630 if (iAddress < 40) /* column addresses */
3632 int offset; /* offset to PageChar and PageAtrb */
3634 if (iMode != 0x06)
3635 *pAPx = iAddress; /* new Active Column */
3636 offset = (*pAPy0 + *pAPy) * 40 + *pAPx0 + *pAPx; /* offset to PageChar and PageAtrb */
3638 switch (iMode)
3640 case 0x00:
3641 if (0 == (iData>>5))
3643 int newcolor = iData & 0x1f;
3644 if (*endcol < 0) /* passive object */
3645 attrPassive->fg = newcolor;
3646 else if (*endcol == 40) /* active object */
3648 TextPageAttr_t *p = &PageAtrb[offset];
3649 int oldcolor = (p)->fg; /* current color (set-after) */
3650 int c = *pAPx0 + *pAPx; /* current column absolute */
3653 p->fg = newcolor;
3654 p++;
3655 c++;
3656 } while (c < 40 && p->fg == oldcolor); /* stop at change by level 1 page */
3658 else /* adaptive object */
3660 TextPageAttr_t *p = &PageAtrb[offset];
3661 int c = *pAPx; /* current column relative to object origin */
3664 p->fg = newcolor;
3665 p++;
3666 c++;
3667 } while (c <= *endcol);
3670 break;
3671 case 0x01:
3672 if (iData >= 0x20)
3674 PageChar[offset] = iData;
3675 if (*endcol < 0) /* passive object */
3677 attrPassive->charset = C_G1C; /* FIXME: separated? */
3678 PageAtrb[offset] = *attrPassive;
3680 else if (PageAtrb[offset].charset != C_G1S)
3681 PageAtrb[offset].charset = C_G1C; /* FIXME: separated? */
3683 break;
3684 case 0x02:
3685 case 0x0b:
3686 PageChar[offset] = iData;
3687 if (*endcol < 0) /* passive object */
3689 attrPassive->charset = C_G3;
3690 PageAtrb[offset] = *attrPassive;
3692 else
3693 PageAtrb[offset].charset = C_G3;
3694 break;
3695 case 0x03:
3696 if (0 == (iData>>5))
3698 int newcolor = iData & 0x1f;
3699 if (*endcol < 0) /* passive object */
3700 attrPassive->bg = newcolor;
3701 else if (*endcol == 40) /* active object */
3703 TextPageAttr_t *p = &PageAtrb[offset];
3704 int oldcolor = (p)->bg; /* current color (set-after) */
3705 int c = *pAPx0 + *pAPx; /* current column absolute */
3708 p->bg = newcolor;
3709 if (newcolor == TXT_ColorBlack)
3710 p->IgnoreAtBlackBgSubst = 1;
3711 p++;
3712 c++;
3713 } while (c < 40 && p->bg == oldcolor); /* stop at change by level 1 page */
3715 else /* adaptive object */
3717 TextPageAttr_t *p = &PageAtrb[offset];
3718 int c = *pAPx; /* current column relative to object origin */
3721 p->bg = newcolor;
3722 if (newcolor == TXT_ColorBlack)
3723 p->IgnoreAtBlackBgSubst = 1;
3724 p++;
3725 c++;
3726 } while (c <= *endcol);
3729 break;
3730 case 0x06:
3731 /* ignore */
3732 break;
3733 case 0x07:
3734 if ((iData & 0x60) != 0) break; // reserved data field
3735 if (*endcol < 0) /* passive object */
3737 attrPassive->flashing=iData & 0x1f;
3738 PageAtrb[offset] = *attrPassive;
3740 else
3741 PageAtrb[offset].flashing=iData & 0x1f;
3742 break;
3743 case 0x08:
3744 if (*endcol < 0) /* passive object */
3746 attrPassive->setG0G2=iData & 0x3f;
3747 PageAtrb[offset] = *attrPassive;
3749 else
3750 PageAtrb[offset].setG0G2=iData & 0x3f;
3751 break;
3752 case 0x09:
3753 PageChar[offset] = iData;
3754 if (*endcol < 0) /* passive object */
3756 attrPassive->charset = C_G0P; /* FIXME: secondary? */
3757 attrPassive->setX26 = 1;
3758 PageAtrb[offset] = *attrPassive;
3760 else
3762 PageAtrb[offset].charset = C_G0P; /* FIXME: secondary? */
3763 PageAtrb[offset].setX26 = 1;
3765 break;
3766 // case 0x0b: (see 0x02)
3767 case 0x0c:
3769 int conc = (iData & 0x04);
3770 int inv = (iData & 0x10);
3771 int dw = (iData & 0x40 ?1:0);
3772 int dh = (iData & 0x01 ?1:0);
3773 int sep = (iData & 0x20);
3774 int bw = (iData & 0x02 ?1:0);
3775 if (*endcol < 0) /* passive object */
3777 if (conc)
3779 attrPassive->concealed = 1;
3780 attrPassive->fg = attrPassive->bg;
3782 attrPassive->inverted = (inv ? 1- attrPassive->inverted : 0);
3783 attrPassive->doubleh = dh;
3784 attrPassive->doublew = dw;
3785 attrPassive->boxwin = bw;
3786 if (bw) attrPassive->IgnoreAtBlackBgSubst = 0;
3787 if (sep)
3789 if (attrPassive->charset == C_G1C)
3790 attrPassive->charset = C_G1S;
3791 else
3792 attrPassive->underline = 1;
3794 else
3796 if (attrPassive->charset == C_G1S)
3797 attrPassive->charset = C_G1C;
3798 else
3799 attrPassive->underline = 0;
3802 else
3805 int c = *pAPx0 + (*endcol == 40 ? *pAPx : 0); /* current column */
3806 TextPageAttr_t *p = &PageAtrb[offset];
3809 p->inverted = (inv ? 1- p->inverted : 0);
3810 if (conc)
3812 p->concealed = 1;
3813 p->fg = p->bg;
3815 if (sep)
3817 if (p->charset == C_G1C)
3818 p->charset = C_G1S;
3819 else
3820 p->underline = 1;
3822 else
3824 if (p->charset == C_G1S)
3825 p->charset = C_G1C;
3826 else
3827 p->underline = 0;
3829 p->doublew = dw;
3830 p->doubleh = dh;
3831 p->boxwin = bw;
3832 if (bw) p->IgnoreAtBlackBgSubst = 0;
3833 p++;
3834 c++;
3835 } while (c < *endcol);
3837 break;
3839 case 0x0d:
3840 PageChar[offset] = iData & 0x3f;
3841 if (*endcol < 0) /* passive object */
3843 attrPassive->charset = C_OFFSET_DRCS + ((iData & 0x40) ? (0x10 + *drcssubp) : *gdrcssubp);
3844 PageAtrb[offset] = *attrPassive;
3846 else
3847 PageAtrb[offset].charset = C_OFFSET_DRCS + ((iData & 0x40) ? (0x10 + *drcssubp) : *gdrcssubp);
3848 break;
3849 case 0x0f:
3850 PageChar[offset] = iData;
3851 if (*endcol < 0) /* passive object */
3853 attrPassive->charset = C_G2;
3854 PageAtrb[offset] = *attrPassive;
3856 else
3857 PageAtrb[offset].charset = C_G2;
3858 break;
3859 default:
3860 if (iMode == 0x10 && iData == 0x2a)
3861 iData = '@';
3862 if (iMode >= 0x10)
3864 PageChar[offset] = iData;
3865 if (*endcol < 0) /* passive object */
3867 attrPassive->charset = C_G0P;
3868 attrPassive->diacrit = iMode & 0x0f;
3869 attrPassive->setX26 = 1;
3870 PageAtrb[offset] = *attrPassive;
3872 else
3874 PageAtrb[offset].charset = C_G0P;
3875 PageAtrb[offset].diacrit = iMode & 0x0f;
3876 PageAtrb[offset].setX26 = 1;
3879 break; /* unsupported or not yet implemented mode: ignore */
3880 } /* switch (iMode) */
3882 else /* ================= (iAddress >= 40): row addresses ====================== */
3884 switch (iMode)
3886 case 0x00:
3887 if (0 == (iData>>5))
3889 m_txtCache->FullScrColor = iData & 0x1f;
3891 break;
3892 case 0x01:
3893 if (*endcol == 40) /* active object */
3895 *pAPy = RowAddress2Row(iAddress); /* new Active Row */
3897 int color = iData & 0x1f;
3898 int row = *pAPy0 + *pAPy;
3899 int maxrow;
3901 if (row <= 24 && 0 == (iData>>5))
3902 maxrow = row;
3903 else if (3 == (iData>>5))
3904 maxrow = 24;
3905 else
3906 maxrow = -1;
3907 for (; row <= maxrow; row++)
3908 m_txtCache->FullRowColor[row] = color;
3909 *endcol = -1;
3911 break;
3912 case 0x04:
3913 *pAPy = RowAddress2Row(iAddress); /* new Active Row */
3914 if (iData < 40)
3915 *pAPx = iData; /* new Active Column */
3916 *endcol = -1; /* FIXME: check if row changed? */
3917 break;
3918 case 0x07:
3919 if (iAddress == 0x3f)
3921 *pAPx = *pAPy = 0; /* new Active Position 0,0 */
3922 if (*endcol == 40) /* active object */
3924 int color = iData & 0x1f;
3925 int row = *pAPy0; // + *pAPy;
3926 int maxrow;
3928 if (row <= 24 && 0 == (iData>>5))
3929 maxrow = row;
3930 else if (3 == (iData>>5))
3931 maxrow = 24;
3932 else
3933 maxrow = -1;
3934 for (; row <= maxrow; row++)
3935 m_txtCache->FullRowColor[row] = color;
3937 *endcol = -1;
3939 break;
3940 case 0x08:
3941 case 0x09:
3942 case 0x0a:
3943 case 0x0b:
3944 case 0x0c:
3945 case 0x0d:
3946 case 0x0e:
3947 case 0x0f:
3948 /* ignore */
3949 break;
3950 case 0x10:
3951 m_txtCache->tAPy = iAddress - 40;
3952 m_txtCache->tAPx = iData;
3953 break;
3954 case 0x11:
3955 case 0x12:
3956 case 0x13:
3957 if (iAddress & 0x10) /* POP or GPOP */
3959 unsigned char APx = 0, APy = 0;
3960 unsigned char APx0 = *pAPx0 + *pAPx + m_txtCache->tAPx, APy0 = *pAPy0 + *pAPy + m_txtCache->tAPy;
3961 int triplet = 3 * ((iData >> 5) & 0x03) + (iMode & 0x03);
3962 int packet = (iAddress & 0x03) + 1;
3963 int subp = iData & 0x0f;
3964 int high = (iData >> 4) & 0x01;
3967 if (APx0 < 40) /* not in side panel */
3969 Eval_NumberedObject((iAddress & 0x08) ? m_txtCache->gpop : m_txtCache->pop, subp, packet, triplet, high, &APx, &APy, &APx0, &APy0, PageChar,PageAtrb);
3972 else if (iAddress & 0x08) /* local: eval invoked object */
3974 unsigned char APx = 0, APy = 0;
3975 unsigned char APx0 = *pAPx0 + *pAPx + m_txtCache->tAPx, APy0 = *pAPy0 + *pAPy + m_txtCache->tAPy;
3976 int descode = ((iAddress & 0x01) << 3) | (iData >> 4);
3977 int triplet = iData & 0x0f;
3979 if (APx0 < 40) /* not in side panel */
3981 Eval_Object(13 * 23 + 13 * descode + triplet, pstCachedPage, &APx, &APy, &APx0, &APy0, (tObjType)(triplet % 3), pagedata, PageChar, PageAtrb);
3984 break;
3985 case 0x15:
3986 case 0x16:
3987 case 0x17:
3988 if (0 == (iAddress & 0x08)) /* Object Definition illegal or only level 3.5 */
3989 break; /* ignore */
3991 m_txtCache->tAPx = m_txtCache->tAPy = 0;
3992 *endcol = -1;
3993 return 0xFF; /* termination by object definition */
3994 break;
3995 case 0x18:
3996 if (0 == (iData & 0x10)) /* DRCS Mode reserved or only level 3.5 */
3997 break; /* ignore */
3999 if (iData & 0x40)
4000 *drcssubp = iData & 0x0f;
4001 else
4002 *gdrcssubp = iData & 0x0f;
4003 break;
4004 case 0x1f:
4005 m_txtCache->tAPx = m_txtCache->tAPy = 0;
4006 *endcol = -1;
4007 return 0x80 | iData; /* explicit termination */
4008 break;
4009 default:
4010 break; /* unsupported or not yet implemented mode: ignore */
4011 } /* switch (iMode) */
4012 } /* (iAddress >= 40): row addresses */
4014 if (iAddress < 40 || iMode != 0x10) /* leave temp. AP-Offset unchanged only immediately after definition */
4015 m_txtCache->tAPx = m_txtCache->tAPy = 0;
4017 return 0; /* normal exit, no termination */
4020 /* get object data */
4021 /* in: absolute triplet number (0..506, start at packet 3 byte 1) */
4022 /* in: pointer to cache struct of page data */
4023 /* out: 18 bit triplet data, <0 if invalid number, not cached, or hamming error */
4024 int CTeletextDecoder::iTripletNumber2Data(int iONr, TextCachedPage_t *pstCachedPage, unsigned char* pagedata)
4026 if (iONr > 506 || 0 == pstCachedPage)
4027 return -1;
4029 unsigned char *p;
4030 int packet = (iONr / 13) + 3;
4031 int packetoffset = 3 * (iONr % 13);
4033 if (packet <= 23)
4034 p = pagedata + 40*(packet-1) + packetoffset + 1;
4035 else if (packet <= 25)
4037 if (0 == pstCachedPage->pageinfo.p24)
4038 return -1;
4039 p = pstCachedPage->pageinfo.p24 + 40*(packet-24) + packetoffset + 1;
4041 else
4043 int descode = packet - 26;
4044 if (0 == pstCachedPage->pageinfo.ext)
4045 return -1;
4046 if (0 == pstCachedPage->pageinfo.ext->p26[descode])
4047 return -1;
4048 p = pstCachedPage->pageinfo.ext->p26[descode] + packetoffset; /* first byte (=designation code) is not cached */
4050 return CDVDTeletextTools::deh24(p);
4053 int CTeletextDecoder::SetNational(unsigned char sec)
4055 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
4057 switch (sec)
4059 case 0x08:
4060 return NAT_PL; //polish
4061 case 0x16:
4062 case 0x36:
4063 return NAT_TR; //turkish
4064 case 0x1d:
4065 return NAT_SR; //serbian, croatian, slovenian
4066 case 0x20:
4067 return NAT_SC; // serbian, croatian
4068 case 0x24:
4069 return NAT_RB; // russian, bulgarian
4070 case 0x25:
4071 return NAT_UA; // ukrainian
4072 case 0x22:
4073 return NAT_ET; // estonian
4074 case 0x23:
4075 return NAT_LV; // latvian, lithuanian
4076 case 0x37:
4077 return NAT_GR; // greek
4078 case 0x55:
4079 return NAT_HB; // hebrew
4080 case 0x47:
4081 case 0x57:
4082 return NAT_AR; // arabic
4084 return CountryConversionTable[sec & 0x07];
4087 int CTeletextDecoder::NextHex(int i) /* return next existing non-decimal page number */
4089 int startpage = i;
4090 if (startpage < 0x100)
4091 startpage = 0x100;
4095 i++;
4096 if (i > 0x8FF)
4097 i = 0x100;
4098 if (i == startpage)
4099 break;
4100 } while ((m_txtCache->SubPageTable[i] == 0xFF) || IsDec(i));
4101 return i;
4104 void CTeletextDecoder::SetColors(const unsigned short *pcolormap, int offset, int number)
4106 int j = offset; /* index in global color table */
4108 for (int i = 0; i < number; i++)
4110 int r = ((pcolormap[i] >> 8) & 0xf) << 4;
4111 int g = ((pcolormap[i] >> 4) & 0xf) << 4;
4112 int b = ((pcolormap[i]) & 0xf) << 4;
4114 if (m_RenderInfo.rd0[j] != r)
4116 m_RenderInfo.rd0[j] = r;
4118 if (m_RenderInfo.gn0[j] != g)
4120 m_RenderInfo.gn0[j] = g;
4122 if (m_RenderInfo.bl0[j] != b)
4124 m_RenderInfo.bl0[j] = b;
4126 j++;
4130 Color CTeletextDecoder::GetColorRGB(enumTeletextColor ttc)
4132 switch (ttc)
4134 case TXT_ColorBlack: return 0xFF000000;
4135 case TXT_ColorRed: return 0xFFFC1414;
4136 case TXT_ColorGreen: return 0xFF24FC24;
4137 case TXT_ColorYellow: return 0xFFFCC024;
4138 case TXT_ColorBlue: return 0xFF0000FC;
4139 case TXT_ColorMagenta: return 0xFFB000FC;
4140 case TXT_ColorCyan: return 0xFF00FCFC;
4141 case TXT_ColorWhite: return 0xFFFCFCFC;
4142 case TXT_ColorTransp: return 0x00000000;
4143 default: break;
4146 /* Get colors for CLUTs 2+3 */
4147 int index = (int)ttc;
4148 Color color = (m_RenderInfo.tr0[index] << 24) | (m_RenderInfo.bl0[index] << 16) |
4149 (m_RenderInfo.gn0[index] << 8) | m_RenderInfo.rd0[index];
4150 return color;