[PVR][Estuary] Timer settings dialog: Show client name in timer type selection dialog...
[xbmc.git] / xbmc / video / Teletext.cpp
blob3cbff168564609c91f8d4568781b20d86859866f
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/Key.h"
22 #include "utils/log.h"
23 #include "windowing/GraphicContext.h"
25 #include <harfbuzz/hb-ft.h>
27 using namespace std::chrono_literals;
29 static inline void SDL_memset4(uint32_t* dst, uint32_t val, size_t len)
31 for (; len > 0; --len)
32 *dst++ = val;
34 #define SDL_memcpy4(dst, src, len) memcpy(dst, src, (len) << 2)
36 static const char *TeletextFont = "special://xbmc/media/Fonts/teletext.ttf";
38 /* spacing attributes */
39 #define alpha_black 0x00
40 #define alpha_red 0x01
41 #define alpha_green 0x02
42 #define alpha_yellow 0x03
43 #define alpha_blue 0x04
44 #define alpha_magenta 0x05
45 #define alpha_cyan 0x06
46 #define alpha_white 0x07
47 #define flash 0x08
48 #define steady 0x09
49 #define end_box 0x0A
50 #define start_box 0x0B
51 #define normal_size 0x0C
52 #define double_height 0x0D
53 #define double_width 0x0E
54 #define double_size 0x0F
55 #define mosaic_black 0x10
56 #define mosaic_red 0x11
57 #define mosaic_green 0x12
58 #define mosaic_yellow 0x13
59 #define mosaic_blue 0x14
60 #define mosaic_magenta 0x15
61 #define mosaic_cyan 0x16
62 #define mosaic_white 0x17
63 #define conceal 0x18
64 #define contiguous_mosaic 0x19
65 #define separated_mosaic 0x1A
66 #define esc 0x1B
67 #define black_background 0x1C
68 #define new_background 0x1D
69 #define hold_mosaic 0x1E
70 #define release_mosaic 0x1F
72 #define RowAddress2Row(row) ((row == 40) ? 24 : (row - 40))
74 // G2 Set as defined in ETS 300 706
75 const unsigned short int G2table[5][6*16] =
77 // Latin G2 Supplementary Set
78 { 0x0020, 0x00A1, 0x00A2, 0x00A3, 0x0024, 0x00A5, 0x0023, 0x00A7, 0x00A4, 0x2018, 0x201C, 0x00AB, 0x2190, 0x2191, 0x2192, 0x2193,
79 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00D7, 0x00B5, 0x00B6, 0x00B7, 0x00F7, 0x2019, 0x201D, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
80 0x0020, 0x0300, 0x0301, 0x02C6, 0x0303, 0x02C9, 0x02D8, 0x02D9, 0x00A8, 0x002E, 0x02DA, 0x00B8, 0x005F, 0x02DD, 0x02DB, 0x02C7,
81 0x2014, 0x00B9, 0x00AE, 0x00A9, 0x2122, 0x266A, 0x20AC, 0x2030, 0x03B1, 0x0020, 0x0020, 0x0020, 0x215B, 0x215C, 0x215D, 0x215E,
82 0x2126, 0x00C6, 0x00D0, 0x00AA, 0x0126, 0x0020, 0x0132, 0x013F, 0x0141, 0x00D8, 0x0152, 0x00BA, 0x00DE, 0x0166, 0x014A, 0x0149,
83 0x0138, 0x00E6, 0x0111, 0x00F0, 0x0127, 0x0131, 0x0133, 0x0140, 0x0142, 0x00F8, 0x0153, 0x00DF, 0x00FE, 0x0167, 0x014B, 0x25A0},
84 // Cyrillic G2 Supplementary Set
85 { 0x0020, 0x00A1, 0x00A2, 0x00A3, 0x0024, 0x00A5, 0x0020, 0x00A7, 0x0020, 0x2018, 0x201C, 0x00AB, 0x2190, 0x2191, 0x2192, 0x2193,
86 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00D7, 0x00B5, 0x00B6, 0x00B7, 0x00F7, 0x2019, 0x201D, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
87 0x0020, 0x0300, 0x0301, 0x02C6, 0x02DC, 0x02C9, 0x02D8, 0x02D9, 0x00A8, 0x002E, 0x02DA, 0x00B8, 0x005F, 0x02DD, 0x02DB, 0x02C7,
88 0x2014, 0x00B9, 0x00AE, 0x00A9, 0x2122, 0x266A, 0x20AC, 0x2030, 0x03B1, 0x0141, 0x0142, 0x00DF, 0x215B, 0x215C, 0x215D, 0x215E,
89 0x0044, 0x0045, 0x0046, 0x0047, 0x0049, 0x004A, 0x004B, 0x004C, 0x004E, 0x0051, 0x0052, 0x0053, 0x0055, 0x0056, 0x0057, 0x005A,
90 0x0064, 0x0065, 0x0066, 0x0067, 0x0069, 0x006A, 0x006B, 0x006C, 0x006E, 0x0071, 0x0072, 0x0073, 0x0075, 0x0076, 0x0077, 0x007A},
91 // Greek G2 Supplementary Set
92 { 0x0020, 0x0061, 0x0062, 0x00A3, 0x0065, 0x0068, 0x0069, 0x00A7, 0x003A, 0x2018, 0x201C, 0x006B, 0x2190, 0x2191, 0x2192, 0x2193,
93 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00D7, 0x006D, 0x006E, 0x0070, 0x00F7, 0x2019, 0x201D, 0x0074, 0x00BC, 0x00BD, 0x00BE, 0x0078,
94 0x0020, 0x0300, 0x0301, 0x02C6, 0x02DC, 0x02C9, 0x02D8, 0x02D9, 0x00A8, 0x002E, 0x02DA, 0x00B8, 0x005F, 0x02DD, 0x02DB, 0x02C7,
95 0x003F, 0x00B9, 0x00AE, 0x00A9, 0x2122, 0x266A, 0x20AC, 0x2030, 0x03B1, 0x038A, 0x038E, 0x038F, 0x215B, 0x215C, 0x215D, 0x215E,
96 0x0043, 0x0044, 0x0046, 0x0047, 0x004A, 0x004C, 0x0051, 0x0052, 0x0053, 0x0055, 0x0056, 0x0057, 0x0059, 0x005A, 0x0386, 0x0389,
97 0x0063, 0x0064, 0x0066, 0x0067, 0x006A, 0x006C, 0x0071, 0x0072, 0x0073, 0x0075, 0x0076, 0x0077, 0x0079, 0x007A, 0x0388, 0x25A0},
98 // Arabic G2 Set
99 { 0x0020, 0x0639, 0xFEC9, 0xFE83, 0xFE85, 0xFE87, 0xFE8B, 0xFE89, 0xFB7C, 0xFB7D, 0xFB7A, 0xFB58, 0xFB59, 0xFB56, 0xFB6D, 0xFB8E,
100 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669, 0xFECE, 0xFECD, 0xFEFC, 0xFEEC, 0xFEEA, 0xFEE9,
101 0x00E0, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
102 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00EB, 0x00EA, 0x00F9, 0x00EE, 0xFECA,
103 0x00E9, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
104 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00E2, 0x00F4, 0x00FB, 0x00E7, 0x25A0}
107 //const (avoid warnings :<)
108 TextPageAttr_t Text_AtrTable[] =
110 { TXT_ColorWhite , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_WB */
111 { TXT_ColorWhite , TXT_ColorBlack , C_G0P, 0, 0, 1 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_PassiveDefault */
112 { TXT_ColorWhite , TXT_ColorRed , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_L250 */
113 { TXT_ColorBlack , TXT_ColorGreen , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_L251 */
114 { TXT_ColorBlack , TXT_ColorYellow, C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_L252 */
115 { TXT_ColorWhite , TXT_ColorBlue , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_L253 */
116 { TXT_ColorMagenta, TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_TOPMENU0 */
117 { TXT_ColorGreen , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_TOPMENU1 */
118 { TXT_ColorYellow , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_TOPMENU2 */
119 { TXT_ColorCyan , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_TOPMENU3 */
120 { TXT_ColorMenu2 , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSG0 */
121 { TXT_ColorYellow , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSG1 */
122 { TXT_ColorMenu2 , TXT_ColorTransp, C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSG2 */
123 { TXT_ColorWhite , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSG3 */
124 { TXT_ColorMenu2 , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSGDRM0 */
125 { TXT_ColorYellow , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSGDRM1 */
126 { TXT_ColorMenu2 , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSGDRM2 */
127 { TXT_ColorWhite , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MSGDRM3 */
128 { TXT_ColorMenu1 , TXT_ColorBlue , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENUHIL0 5a Z */
129 { TXT_ColorWhite , TXT_ColorBlue , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENUHIL1 58 X */
130 { TXT_ColorMenu2 , TXT_ColorTransp, C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENUHIL2 9b õ */
131 { TXT_ColorMenu2 , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU0 ab ´ */
132 { TXT_ColorYellow , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU1 a4 § */
133 { TXT_ColorMenu2 , TXT_ColorTransp, C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU2 9b õ */
134 { TXT_ColorMenu2 , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU3 cb À */
135 { TXT_ColorCyan , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU4 c7 « */
136 { TXT_ColorWhite , TXT_ColorMenu3 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU5 c8 » */
137 { TXT_ColorWhite , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_MENU6 a8 ® */
138 { TXT_ColorYellow , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f}, /* ATR_CATCHMENU0 a4 § */
139 { TXT_ColorWhite , TXT_ColorMenu1 , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f} /* ATR_CATCHMENU1 a8 ® */
142 /* shapes */
143 enum
145 S_END = 0,
146 S_FHL, /* full horizontal line: y-offset */
147 S_FVL, /* full vertical line: x-offset */
148 S_BOX, /* rectangle: x-offset, y-offset, width, height */
149 S_TRA, /* trapez: x0, y0, l0, x1, y1, l1 */
150 S_BTR, /* trapez in bgcolor: x0, y0, l0, x1, y1, l1 */
151 S_INV, /* invert */
152 S_LNK, /* call other shape: shapenumber */
153 S_CHR, /* Character from freetype hibyte, lowbyte */
154 S_ADT, /* Character 2F alternating raster */
155 S_FLH, /* flip horizontal */
156 S_FLV /* flip vertical */
159 /* shape coordinates */
160 enum
162 S_W13 = 5, /* width*1/3 */
163 S_W12, /* width*1/2 */
164 S_W23, /* width*2/3 */
165 S_W11, /* width */
166 S_WM3, /* width-3 */
167 S_H13, /* height*1/3 */
168 S_H12, /* height*1/2 */
169 S_H23, /* height*2/3 */
170 S_H11, /* height */
171 S_NrShCoord
174 /* G3 characters */
175 unsigned char aG3_20[] = { S_TRA, 0, S_H23, 1, 0, S_H11, S_W12, S_END };
176 unsigned char aG3_21[] = { S_TRA, 0, S_H23, 1, 0, S_H11, S_W11, S_END };
177 unsigned char aG3_22[] = { S_TRA, 0, S_H12, 1, 0, S_H11, S_W12, S_END };
178 unsigned char aG3_23[] = { S_TRA, 0, S_H12, 1, 0, S_H11, S_W11, S_END };
179 unsigned char aG3_24[] = { S_TRA, 0, 0, 1, 0, S_H11, S_W12, S_END };
180 unsigned char aG3_25[] = { S_TRA, 0, 0, 1, 0, S_H11, S_W11, S_END };
181 unsigned char aG3_26[] = { S_INV, S_LNK, 0x66, S_END };
182 unsigned char aG3_27[] = { S_INV, S_LNK, 0x67, S_END };
183 unsigned char aG3_28[] = { S_INV, S_LNK, 0x68, S_END };
184 unsigned char aG3_29[] = { S_INV, S_LNK, 0x69, S_END };
185 unsigned char aG3_2a[] = { S_INV, S_LNK, 0x6a, S_END };
186 unsigned char aG3_2b[] = { S_INV, S_LNK, 0x6b, S_END };
187 unsigned char aG3_2c[] = { S_INV, S_LNK, 0x6c, S_END };
188 unsigned char aG3_2d[] = { S_INV, S_LNK, 0x6d, S_END };
189 unsigned char aG3_2e[] = { S_BOX, 2, 0, 3, S_H11, S_END };
190 unsigned char aG3_2f[] = { S_ADT };
191 unsigned char aG3_30[] = { S_LNK, 0x20, S_FLH, S_END };
192 unsigned char aG3_31[] = { S_LNK, 0x21, S_FLH, S_END };
193 unsigned char aG3_32[] = { S_LNK, 0x22, S_FLH, S_END };
194 unsigned char aG3_33[] = { S_LNK, 0x23, S_FLH, S_END };
195 unsigned char aG3_34[] = { S_LNK, 0x24, S_FLH, S_END };
196 unsigned char aG3_35[] = { S_LNK, 0x25, S_FLH, S_END };
197 unsigned char aG3_36[] = { S_INV, S_LNK, 0x76, S_END };
198 unsigned char aG3_37[] = { S_INV, S_LNK, 0x77, S_END };
199 unsigned char aG3_38[] = { S_INV, S_LNK, 0x78, S_END };
200 unsigned char aG3_39[] = { S_INV, S_LNK, 0x79, S_END };
201 unsigned char aG3_3a[] = { S_INV, S_LNK, 0x7a, S_END };
202 unsigned char aG3_3b[] = { S_INV, S_LNK, 0x7b, S_END };
203 unsigned char aG3_3c[] = { S_INV, S_LNK, 0x7c, S_END };
204 unsigned char aG3_3d[] = { S_INV, S_LNK, 0x7d, S_END };
205 unsigned char aG3_3e[] = { S_LNK, 0x2e, S_FLH, S_END };
206 unsigned char aG3_3f[] = { S_BOX, 0, 0, S_W11, S_H11, S_END };
207 unsigned char aG3_40[] = { S_BOX, 0, S_H13, S_W11, S_H13, S_LNK, 0x7e, S_END };
208 unsigned char aG3_41[] = { S_BOX, 0, S_H13, S_W11, S_H13, S_LNK, 0x7e, S_FLV, S_END };
209 unsigned char aG3_42[] = { S_LNK, 0x50, S_BOX, S_W12, S_H13, S_W12, S_H13, S_END };
210 unsigned char aG3_43[] = { S_LNK, 0x50, S_BOX, 0, S_H13, S_W12, S_H13, S_END };
211 unsigned char aG3_44[] = { S_LNK, 0x48, S_FLV, S_LNK, 0x48, S_END };
212 unsigned char aG3_45[] = { S_LNK, 0x44, S_FLH, S_END };
213 unsigned char aG3_46[] = { S_LNK, 0x47, S_FLV, S_END };
214 unsigned char aG3_47[] = { S_LNK, 0x48, S_FLH, S_LNK, 0x48, S_END };
215 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 };
216 unsigned char aG3_49[] = { S_LNK, 0x48, S_FLH, S_END };
217 unsigned char aG3_4a[] = { S_LNK, 0x48, S_FLV, S_END };
218 unsigned char aG3_4b[] = { S_LNK, 0x48, S_FLH, S_FLV, S_END };
219 unsigned char aG3_4c[] = { S_LNK, 0x50, S_BOX, 0, S_H13, S_W11, S_H13, S_END };
220 unsigned char aG3_4d[] = { S_CHR, 0x25, 0xE6 };
221 unsigned char aG3_4e[] = { S_CHR, 0x25, 0xCF };
222 unsigned char aG3_4f[] = { S_CHR, 0x25, 0xCB };
223 unsigned char aG3_50[] = { S_BOX, S_W12, 0, 2, S_H11, S_FLH, S_BOX, S_W12, 0, 2, S_H11,S_END };
224 unsigned char aG3_51[] = { S_BOX, 0, S_H12, S_W11, 2, S_FLV, S_BOX, 0, S_H12, S_W11, 2,S_END };
225 unsigned char aG3_52[] = { S_LNK, 0x55, S_FLH, S_FLV, S_END };
226 unsigned char aG3_53[] = { S_LNK, 0x55, S_FLV, S_END };
227 unsigned char aG3_54[] = { S_LNK, 0x55, S_FLH, S_END };
228 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 };
229 unsigned char aG3_56[] = { S_LNK, 0x57, S_FLH, S_END};
230 unsigned char aG3_57[] = { S_LNK, 0x55, S_LNK, 0x50 , S_END};
231 unsigned char aG3_58[] = { S_LNK, 0x59, S_FLV, S_END};
232 unsigned char aG3_59[] = { S_LNK, 0x7e, S_LNK, 0x51 , S_END};
233 unsigned char aG3_5a[] = { S_LNK, 0x50, S_LNK, 0x51 , S_END};
234 unsigned char aG3_5b[] = { S_CHR, 0x21, 0x92};
235 unsigned char aG3_5c[] = { S_CHR, 0x21, 0x90};
236 unsigned char aG3_5d[] = { S_CHR, 0x21, 0x91};
237 unsigned char aG3_5e[] = { S_CHR, 0x21, 0x93};
238 unsigned char aG3_5f[] = { S_CHR, 0x00, 0x20};
239 unsigned char aG3_60[] = { S_INV, S_LNK, 0x20, S_END };
240 unsigned char aG3_61[] = { S_INV, S_LNK, 0x21, S_END };
241 unsigned char aG3_62[] = { S_INV, S_LNK, 0x22, S_END };
242 unsigned char aG3_63[] = { S_INV, S_LNK, 0x23, S_END };
243 unsigned char aG3_64[] = { S_INV, S_LNK, 0x24, S_END };
244 unsigned char aG3_65[] = { S_INV, S_LNK, 0x25, S_END };
245 unsigned char aG3_66[] = { S_LNK, 0x20, S_FLV, S_END };
246 unsigned char aG3_67[] = { S_LNK, 0x21, S_FLV, S_END };
247 unsigned char aG3_68[] = { S_LNK, 0x22, S_FLV, S_END };
248 unsigned char aG3_69[] = { S_LNK, 0x23, S_FLV, S_END };
249 unsigned char aG3_6a[] = { S_LNK, 0x24, S_FLV, S_END };
250 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 };
251 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 };
252 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 };
253 unsigned char aG3_6e[] = { S_CHR, 0x00, 0x20};
254 unsigned char aG3_6f[] = { S_CHR, 0x00, 0x20};
255 unsigned char aG3_70[] = { S_INV, S_LNK, 0x30, S_END };
256 unsigned char aG3_71[] = { S_INV, S_LNK, 0x31, S_END };
257 unsigned char aG3_72[] = { S_INV, S_LNK, 0x32, S_END };
258 unsigned char aG3_73[] = { S_INV, S_LNK, 0x33, S_END };
259 unsigned char aG3_74[] = { S_INV, S_LNK, 0x34, S_END };
260 unsigned char aG3_75[] = { S_INV, S_LNK, 0x35, S_END };
261 unsigned char aG3_76[] = { S_LNK, 0x66, S_FLH, S_END };
262 unsigned char aG3_77[] = { S_LNK, 0x67, S_FLH, S_END };
263 unsigned char aG3_78[] = { S_LNK, 0x68, S_FLH, S_END };
264 unsigned char aG3_79[] = { S_LNK, 0x69, S_FLH, S_END };
265 unsigned char aG3_7a[] = { S_LNK, 0x6a, S_FLH, S_END };
266 unsigned char aG3_7b[] = { S_LNK, 0x6b, S_FLH, S_END };
267 unsigned char aG3_7c[] = { S_LNK, 0x6c, S_FLH, S_END };
268 unsigned char aG3_7d[] = { S_LNK, 0x6d, S_FLV, S_END };
269 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)
271 unsigned char *aShapes[] =
273 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,
274 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,
275 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,
276 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,
277 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,
278 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
281 // G0 Table as defined in ETS 300 706
282 // cyrillic G0 Charset (0 = Serbian/Croatian, 1 = Russian/Bulgarian, 2 = Ukrainian)
283 const unsigned short int G0table[6][6*16] =
285 // Cyrillic G0 Set - Option 1 - Serbian/Croatian
286 { ' ', '!', '\"', '#', '$', '%', '&', '\'', '(' , ')' , '*', '+', ',', '-', '.', '/',
287 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
288 0x0427, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0408, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E,
289 0x041F, 0x040C, 0x0420, 0x0421, 0x0422, 0x0423, 0x0412, 0x0403, 0x0409, 0x040A, 0x0417, 0x040B, 0x0416, 0x0402, 0x0428, 0x040F,
290 0x0447, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0458, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E,
291 0x043F, 0x045C, 0x0440, 0x0441, 0x0442, 0x0443, 0x0432, 0x0453, 0x0459, 0x045A, 0x0437, 0x045B, 0x0436, 0x0452, 0x0448, 0x25A0},
292 // Cyrillic G0 Set - Option 2 - Russian/Bulgarian
293 { ' ', '!', '\"', '#', '$', '%', 0x044B, '\'', '(' , ')' , '*', '+', ',', '-', '.', '/',
294 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
295 0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E,
296 0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, 0x042C, 0x042A, 0x0417, 0x0428, 0x042D, 0x0429, 0x0427, 0x042B,
297 0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E,
298 0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, 0x044C, 0x044A, 0x0437, 0x0448, 0x044D, 0x0449, 0x0447, 0x25A0},
299 // Cyrillic G0 Set - Option 3 - Ukrainian
300 { ' ', '!', '\"', '#', '$', '%', 0x0457, '\'', '(' , ')' , '*', '+', ',', '-', '.', '/',
301 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
302 0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E,
303 0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, 0x042C, 0x0406, 0x0417, 0x0428, 0x0404, 0x0429, 0x0427, 0x0407,
304 0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E,
305 0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, 0x044C, 0x0456, 0x0437, 0x0448, 0x0454, 0x0449, 0x0447, 0x25A0},
306 // Greek G0 Set
307 { ' ', '!', '\"', '#', '$', '%', '&', '\'', '(' , ')' , '*', '+', ',', '-', '.', '/',
308 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', 0x00AB, '=', 0x00BB, '?',
309 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F,
310 0x03A0, 0x03A1, 0x0384, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF,
311 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
312 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x25A0},
313 // Hebrew G0 Set
314 { ' ', '!', 0x05F2, 0x00A3, '$', '%', '&', '\'', '(' , ')' , '*', '+', ',', '-', '.', '/',
315 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
316 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
317 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 0x2190, 0x00BD, 0x2192, 0x2191, '#',
318 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
319 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x20AA, 0x2551, 0x00BE, 0x00F7, 0x25A0},
320 // Arabic G0 Set - Thanks to Habib2006(fannansat)
321 { ' ', '!', 0x05F2, 0x00A3, '$', 0x066A, 0xFEF0, 0xFEF2, 0xFD3F, 0xFD3E, '*', '+', ',', '-', '.', '/',
322 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', 0x061B, '>', '=', '<', 0x061F,
323 0xFE94, 0x0621, 0xFE92, 0x0628, 0xFE98, 0x062A, 0xFE8E, 0xFE8D, 0xFE91, 0xFE93, 0xFE97, 0xFE9B, 0xFE9F, 0xFEA3, 0xFEA7, 0xFEA9,
324 0x0630, 0xFEAD, 0xFEAF, 0xFEB3, 0xFEB7, 0xFEBB, 0xFEBF, 0xFEC1, 0xFEC5, 0xFECB, 0xFECF, 0xFE9C, 0xFEA0, 0xFEA4, 0xFEA8, 0x0023,
325 0x0640, 0xFED3, 0xFED7, 0xFEDB, 0xFEDF, 0xFEE3, 0xFEE7, 0xFEEB, 0xFEED, 0xFEEF, 0xFEF3, 0xFE99, 0xFE9D, 0xFEA1, 0xFEA5, 0xFEF4,
326 0xFEF0, 0xFECC, 0xFED0, 0xFED4, 0xFED1, 0xFED8, 0xFED5, 0xFED9, 0xFEE0, 0xFEDD, 0xFEE4, 0xFEE1, 0xFEE8, 0xFEE5, 0xFEFB, 0x25A0}
329 const unsigned short int nationaltable23[14][2] =
331 { '#', 0x00A4 }, /* 0 */
332 { '#', 0x016F }, /* 1 CS/SK */
333 { 0x00A3, '$' }, /* 2 EN */
334 { '#', 0x00F5 }, /* 3 ET */
335 { 0x00E9, 0x0457 }, /* 4 FR */
336 { '#', '$' }, /* 5 DE */
337 { 0x00A3, '$' }, /* 6 IT */
338 { '#', '$' }, /* 7 LV/LT */
339 { '#', 0x0144 }, /* 8 PL */
340 { 0x00E7, '$' }, /* 9 PT/ES */
341 { '#', 0x00A4 }, /* A RO */
342 { '#', 0x00CB }, /* B SR/HR/SL */
343 { '#', 0x00A4 }, /* C SV/FI/HU */
344 { 0x20A4, 0x011F }, /* D TR */
346 const unsigned short int nationaltable40[14] =
348 '@', /* 0 */
349 0x010D, /* 1 CS/SK */
350 '@', /* 2 EN */
351 0x0161, /* 3 ET */
352 0x00E0, /* 4 FR */
353 0x00A7, /* 5 DE */
354 0x00E9, /* 6 IT */
355 0x0161, /* 7 LV/LT */
356 0x0105, /* 8 PL */
357 0x00A1, /* 9 PT/ES */
358 0x0162, /* A RO */
359 0x010C, /* B SR/HR/SL */
360 0x00C9, /* C SV/FI/HU */
361 0x0130, /* D TR */
363 const unsigned short int nationaltable5b[14][6] =
365 { '[', '\\', ']', '^', '_', '`' }, /* 0 */
366 { 0x0165, 0x017E, 0x00FD, 0x00ED, 0x0159, 0x00E9 }, /* 1 CS/SK */
367 { 0x2190, 0x00BD, 0x2192, 0x2191, '#', 0x00AD }, /* 2 EN */
368 { 0x00C4, 0x00D6, 0x017D, 0x00DC, 0x00D5, 0x0161 }, /* 3 ET */
369 { 0x0451, 0x00EA, 0x00F9, 0x00EE, '#', 0x00E8 }, /* 4 FR */
370 { 0x00C4, 0x00D6, 0x00DC, '^', '_', 0x00B0 }, /* 5 DE */
371 { 0x00B0, 0x00E7, 0x2192, 0x2191, '#', 0x00F9 }, /* 6 IT */
372 { 0x0117, 0x0119, 0x017D, 0x010D, 0x016B, 0x0161 }, /* 7 LV/LT */
373 { 0x017B, 0x015A, 0x0141, 0x0107, 0x00F3, 0x0119 }, /* 8 PL */
374 { 0x00E1, 0x00E9, 0x00ED, 0x00F3, 0x00FA, 0x00BF }, /* 9 PT/ES */
375 { 0x00C2, 0x015E, 0x01CD, 0x01CF, 0x0131, 0x0163 }, /* A RO */
376 { 0x0106, 0x017D, 0x00D0, 0x0160, 0x0451, 0x010D }, /* B SR/HR/SL */
377 { 0x00C4, 0x00D6, 0x00C5, 0x00DC, '_', 0x00E9 }, /* C SV/FI/HU */
378 { 0x015E, 0x00D6, 0x00C7, 0x00DC, 0x011E, 0x0131 }, /* D TR */
380 const unsigned short int nationaltable7b[14][4] =
382 { '{', '|', '}', '~' }, /* 0 */
383 { 0x00E1, 0x011B, 0x00FA, 0x0161 }, /* 1 CS/SK */
384 { 0x00BC, 0x2551, 0x00BE, 0x00F7 }, /* 2 EN */
385 { 0x00E4, 0x00F6, 0x017E, 0x00FC }, /* 3 ET */
386 { 0x00E2, 0x00F4, 0x00FB, 0x00E7 }, /* 4 FR */
387 { 0x00E4, 0x00F6, 0x00FC, 0x00DF }, /* 5 DE */
388 { 0x00E0, 0x00F3, 0x00E8, 0x00EC }, /* 6 IT */
389 { 0x0105, 0x0173, 0x017E, 0x012F }, /* 7 LV/LT */
390 { 0x017C, 0x015B, 0x0142, 0x017A }, /* 8 PL */
391 { 0x00FC, 0x00F1, 0x00E8, 0x00E0 }, /* 9 PT/ES */
392 { 0x00E2, 0x015F, 0x01CE, 0x00EE }, /* A RO */
393 { 0x0107, 0x017E, 0x0111, 0x0161 }, /* B SR/HR/SL */
394 { 0x00E4, 0x00F6, 0x00E5, 0x00FC }, /* C SV/FI/HU */
395 { 0x015F, 0x00F6, 0x00E7, 0x00FC }, /* D TR */
397 const unsigned short int arrowtable[] =
399 8592, 8594, 8593, 8595, 'O', 'K', 8592, 8592
402 CTeletextDecoder::CTeletextDecoder()
404 memset(&m_RenderInfo, 0, sizeof(TextRenderInfo_t));
406 m_teletextFont = CSpecialProtocol::TranslatePath(TeletextFont);
407 m_TextureBuffer = NULL;
408 m_txtCache = NULL;
409 m_Manager = NULL;
410 m_Library = NULL;
411 m_RenderInfo.ShowFlof = true;
412 m_RenderInfo.Show39 = false;
413 m_RenderInfo.Showl25 = true;
414 m_RenderInfo.Prev_100 = 0x100;
415 m_RenderInfo.Prev_10 = 0x100;
416 m_RenderInfo.Next_100 = 0x100;
417 m_RenderInfo.Next_10 = 0x100;
418 m_RenderInfo.InputCounter = 2;
420 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 };
421 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 };
422 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 };
423 unsigned short tr0[] = {0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
424 0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
425 0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
426 0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
427 0x0000 , 0x0000 , 0x0A0A , 0xFFFF, 0x3030 };
429 memcpy(m_RenderInfo.rd0,rd0,TXT_Color_SIZECOLTABLE*sizeof(unsigned short));
430 memcpy(m_RenderInfo.gn0,gn0,TXT_Color_SIZECOLTABLE*sizeof(unsigned short));
431 memcpy(m_RenderInfo.bl0,bl0,TXT_Color_SIZECOLTABLE*sizeof(unsigned short));
432 memcpy(m_RenderInfo.tr0,tr0,TXT_Color_SIZECOLTABLE*sizeof(unsigned short));
434 m_LastPage = 0;
435 m_TempPage = 0;
436 m_Ascender = 0;
437 m_PCOldCol = 0;
438 m_PCOldRow = 0;
439 m_CatchedPage = 0;
440 m_CatchCol = 0;
441 m_CatchRow = 0;
442 prevTimeSec = 0;
443 prevHeaderPage = 0;
444 m_updateTexture = false;
445 m_YOffset = 0;
448 CTeletextDecoder::~CTeletextDecoder() = default;
450 bool CTeletextDecoder::HandleAction(const CAction &action)
452 if (m_txtCache == NULL)
454 CLog::Log(LOGERROR, "CTeletextDecoder::HandleAction called without teletext cache");
455 return false;
458 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
460 if (action.GetID() == ACTION_MOVE_UP)
462 if (m_RenderInfo.PageCatching)
463 CatchNextPage(-1, -1);
464 else
465 GetNextPageOne(true);
466 return true;
468 else if (action.GetID() == ACTION_MOVE_DOWN)
470 if (m_RenderInfo.PageCatching)
471 CatchNextPage(1, 1);
472 else
473 GetNextPageOne(false);
474 return true;
476 else if (action.GetID() == ACTION_MOVE_RIGHT)
478 if (m_RenderInfo.PageCatching)
479 CatchNextPage(0, 1);
480 else if (m_RenderInfo.Boxed)
482 m_RenderInfo.SubtitleDelay++;
483 // display SubtitleDelay
484 m_RenderInfo.PosY = 0;
485 char ns[10];
486 SetPosX(1);
487 sprintf(ns,"+%d ", m_RenderInfo.SubtitleDelay);
488 RenderCharFB(ns[0], &Text_AtrTable[ATR_WB]);
489 RenderCharFB(ns[1], &Text_AtrTable[ATR_WB]);
490 RenderCharFB(ns[2], &Text_AtrTable[ATR_WB]);
491 RenderCharFB(ns[4], &Text_AtrTable[ATR_WB]);
493 else
495 GetNextSubPage(1);
497 return true;
499 else if (action.GetID() == ACTION_MOVE_LEFT)
501 if (m_RenderInfo.PageCatching)
502 CatchNextPage(0, -1);
503 else if (m_RenderInfo.Boxed)
505 m_RenderInfo.SubtitleDelay--;
507 // display subtitledelay
508 m_RenderInfo.PosY = 0;
509 char ns[10];
510 SetPosX(1);
511 sprintf(ns,"+%d ", m_RenderInfo.SubtitleDelay);
512 RenderCharFB(ns[0], &Text_AtrTable[ATR_WB]);
513 RenderCharFB(ns[1], &Text_AtrTable[ATR_WB]);
514 RenderCharFB(ns[2], &Text_AtrTable[ATR_WB]);
515 RenderCharFB(ns[4], &Text_AtrTable[ATR_WB]);
517 else
519 GetNextSubPage(-1);
521 return true;
523 else if (action.GetID() >= REMOTE_0 && action.GetID() <= REMOTE_9)
525 PageInput(action.GetID() - REMOTE_0);
526 return true;
528 else if (action.GetID() == KEY_UNICODE)
529 { // input from the keyboard
530 if (action.GetUnicode() >= 48 && action.GetUnicode() < 58)
532 PageInput(action.GetUnicode() - 48);
533 return true;
535 return false;
537 else if (action.GetID() == ACTION_PAGE_UP)
539 SwitchZoomMode();
540 return true;
542 else if (action.GetID() == ACTION_PAGE_DOWN)
544 SwitchTranspMode();
545 return true;
547 else if (action.GetID() == ACTION_SELECT_ITEM)
549 if (m_txtCache->SubPageTable[m_txtCache->Page] == 0xFF)
550 return false;
552 if (!m_RenderInfo.PageCatching)
553 StartPageCatching();
554 else
555 StopPageCatching();
557 return true;
560 if (m_RenderInfo.PageCatching)
562 m_txtCache->PageUpdate = true;
563 m_RenderInfo.PageCatching = false;
564 return true;
567 if (action.GetID() == ACTION_SHOW_INFO)
569 SwitchHintMode();
570 return true;
572 else if (action.GetID() == ACTION_TELETEXT_RED)
574 ColorKey(m_RenderInfo.Prev_100);
575 return true;
577 else if (action.GetID() == ACTION_TELETEXT_GREEN)
579 ColorKey(m_RenderInfo.Prev_10);
580 return true;
582 else if (action.GetID() == ACTION_TELETEXT_YELLOW)
584 ColorKey(m_RenderInfo.Next_10);
585 return true;
587 else if (action.GetID() == ACTION_TELETEXT_BLUE)
589 ColorKey(m_RenderInfo.Next_100);
590 return true;
593 return false;
596 bool CTeletextDecoder::InitDecoder()
598 int error;
600 auto& components = CServiceBroker::GetAppComponents();
601 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
602 m_txtCache = appPlayer->GetTeletextCache();
603 if (m_txtCache == nullptr)
605 CLog::Log(LOGERROR, "{}: called without teletext cache", __FUNCTION__);
606 return false;
609 /* init fontlibrary */
610 if ((error = FT_Init_FreeType(&m_Library)))
612 CLog::Log(LOGERROR, "{}: <FT_Init_FreeType: {:#2X}>", __FUNCTION__, error);
613 m_Library = NULL;
614 return false;
617 if ((error = FTC_Manager_New(m_Library, 7, 2, 0, &MyFaceRequester, NULL, &m_Manager)))
619 FT_Done_FreeType(m_Library);
620 m_Library = NULL;
621 m_Manager = NULL;
622 CLog::Log(LOGERROR, "{}: <FTC_Manager_New: {:#2X}>", __FUNCTION__, error);
623 return false;
626 if ((error = FTC_SBitCache_New(m_Manager, &m_Cache)))
628 FTC_Manager_Done(m_Manager);
629 FT_Done_FreeType(m_Library);
630 m_Manager = NULL;
631 m_Library = NULL;
632 CLog::Log(LOGERROR, "{}: <FTC_SBit_Cache_New: {:#2X}>", __FUNCTION__, error);
633 return false;
636 /* calculate font dimensions */
637 m_RenderInfo.Width = (int)(CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth()*CServiceBroker::GetWinSystem()->GetGfxContext().GetGUIScaleX());
638 m_RenderInfo.Height = (int)(CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight()*CServiceBroker::GetWinSystem()->GetGfxContext().GetGUIScaleY());
639 m_RenderInfo.FontHeight = m_RenderInfo.Height / 25;
640 m_RenderInfo.FontWidth_Normal = m_RenderInfo.Width / (m_RenderInfo.Show39 ? 39 : 40);
641 SetFontWidth(m_RenderInfo.FontWidth_Normal);
642 for (int i = 0; i <= 10; i++)
643 m_RenderInfo.axdrcs[i+12+1] = (m_RenderInfo.FontHeight * i + 6) / 10;
645 /* center screen */
646 m_TypeTTF.face_id = (FTC_FaceID) const_cast<char*>(m_teletextFont.c_str());
647 m_TypeTTF.height = (FT_UShort) m_RenderInfo.FontHeight;
648 m_TypeTTF.flags = FT_LOAD_MONOCHROME;
649 if (FTC_Manager_LookupFace(m_Manager, m_TypeTTF.face_id, &m_Face))
651 m_TypeTTF.face_id = (FTC_FaceID) const_cast<char*>(m_teletextFont.c_str());
652 if ((error = FTC_Manager_LookupFace(m_Manager, m_TypeTTF.face_id, &m_Face)))
654 CLog::Log(LOGERROR, "{}: <FTC_Manager_Lookup_Face failed with Errorcode {:#2X}>",
655 __FUNCTION__, error);
656 FTC_Manager_Done(m_Manager);
657 FT_Done_FreeType(m_Library);
658 m_Manager = NULL;
659 m_Library = NULL;
660 return false;
663 m_Ascender = m_RenderInfo.FontHeight * m_Face->ascender / m_Face->units_per_EM;
665 /* set variable screeninfo for double buffering */
666 m_YOffset = 0;
667 m_TextureBuffer = new UTILS::COLOR::Color[4 * m_RenderInfo.Height * m_RenderInfo.Width];
669 ClearFB(GetColorRGB(TXT_ColorTransp));
670 ClearBB(GetColorRGB(TXT_ColorTransp)); /* initialize backbuffer */
671 /* set new colormap */
672 SetColors(DefaultColors, 0, TXT_Color_SIZECOLTABLE);
674 for (int i = 0; i < 40 * 25; i++)
676 m_RenderInfo.PageChar[i] = ' ';
677 m_RenderInfo.PageAtrb[i].fg = TXT_ColorTransp;
678 m_RenderInfo.PageAtrb[i].bg = TXT_ColorTransp;
679 m_RenderInfo.PageAtrb[i].charset = C_G0P;
680 m_RenderInfo.PageAtrb[i].doubleh = 0;
681 m_RenderInfo.PageAtrb[i].doublew = 0;
682 m_RenderInfo.PageAtrb[i].IgnoreAtBlackBgSubst = 0;
685 m_RenderInfo.TranspMode = false;
686 m_LastPage = 0x100;
688 return true;
691 void CTeletextDecoder::EndDecoder()
693 /* clear SubtitleCache */
694 for (TextSubtitleCache_t*& subtitleCache : m_RenderInfo.SubtitleCache)
696 if (subtitleCache != NULL)
698 delete subtitleCache;
699 subtitleCache = NULL;
703 if (m_TextureBuffer)
705 delete[] m_TextureBuffer;
706 m_TextureBuffer = NULL;
709 /* close freetype */
710 if (m_Manager)
712 FTC_Node_Unref(m_anode, m_Manager);
713 FTC_Manager_Done(m_Manager);
715 if (m_Library)
717 FT_Done_FreeType(m_Library);
720 m_Manager = NULL;
721 m_Library = NULL;
723 if (!m_txtCache)
725 CLog::Log(LOGINFO, "{}: called without cache", __FUNCTION__);
727 else
729 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
730 m_txtCache->PageUpdate = true;
731 CLog::Log(LOGDEBUG, "Teletext: Rendering ended");
735 void CTeletextDecoder::PageInput(int Number)
737 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
739 m_updateTexture = true;
741 /* clear m_TempPage */
742 if (m_RenderInfo.InputCounter == 2)
743 m_TempPage = 0;
745 /* check for 0 & 9 on first position */
746 if (Number == 0 && m_RenderInfo.InputCounter == 2)
748 /* set page */
749 m_TempPage = m_LastPage; /* 0 toggles to last page as in program switching */
750 m_RenderInfo.InputCounter = -1;
752 else if (Number == 9 && m_RenderInfo.InputCounter == 2)
754 return;
757 /* show pageinput */
758 if (m_RenderInfo.ZoomMode == 2)
760 m_RenderInfo.ZoomMode = 1;
761 CopyBB2FB();
764 m_RenderInfo.PosY = 0;
766 switch (m_RenderInfo.InputCounter)
768 case 2:
769 SetPosX(1);
770 RenderCharFB(Number | '0', &Text_AtrTable[ATR_WB]);
771 RenderCharFB('-', &Text_AtrTable[ATR_WB]);
772 RenderCharFB('-', &Text_AtrTable[ATR_WB]);
773 break;
775 case 1:
776 SetPosX(2);
777 RenderCharFB(Number | '0', &Text_AtrTable[ATR_WB]);
778 break;
780 case 0:
781 SetPosX(3);
782 RenderCharFB(Number | '0', &Text_AtrTable[ATR_WB]);
783 break;
786 /* generate pagenumber */
787 m_TempPage |= Number << (m_RenderInfo.InputCounter*4);
789 m_RenderInfo.InputCounter--;
791 if (m_RenderInfo.InputCounter < 0)
793 /* disable SubPage zapping */
794 m_txtCache->ZapSubpageManual = false;
796 /* reset input */
797 m_RenderInfo.InputCounter = 2;
799 /* set new page */
800 m_LastPage = m_txtCache->Page;
802 m_txtCache->Page = m_TempPage;
803 m_RenderInfo.HintMode = false;
805 /* check cache */
806 int subp = m_txtCache->SubPageTable[m_txtCache->Page];
807 if (subp != 0xFF)
809 m_txtCache->SubPage = subp;
810 m_txtCache->PageUpdate = true;
812 else
814 m_txtCache->SubPage = 0;
815 // RenderMessage(PageNotFound);
820 void CTeletextDecoder::GetNextPageOne(bool up)
822 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
824 /* disable subpage zapping */
825 m_txtCache->ZapSubpageManual = false;
827 /* abort pageinput */
828 m_RenderInfo.InputCounter = 2;
830 /* find next cached page */
831 m_LastPage = m_txtCache->Page;
833 int subp;
834 do {
835 if (up)
836 CDVDTeletextTools::NextDec(&m_txtCache->Page);
837 else
838 CDVDTeletextTools::PrevDec(&m_txtCache->Page);
839 subp = m_txtCache->SubPageTable[m_txtCache->Page];
840 } while (subp == 0xFF && m_txtCache->Page != m_LastPage);
842 /* update Page */
843 if (m_txtCache->Page != m_LastPage)
845 if (m_RenderInfo.ZoomMode == 2)
846 m_RenderInfo.ZoomMode = 1;
848 m_txtCache->SubPage = subp;
849 m_RenderInfo.HintMode = false;
850 m_txtCache->PageUpdate = true;
854 void CTeletextDecoder::GetNextSubPage(int offset)
856 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
858 /* abort pageinput */
859 m_RenderInfo.InputCounter = 2;
861 for (int loop = m_txtCache->SubPage + offset; loop != m_txtCache->SubPage; loop += offset)
863 if (loop < 0)
864 loop = 0x79;
865 else if (loop > 0x79)
866 loop = 0;
867 if (loop == m_txtCache->SubPage)
868 break;
870 if (m_txtCache->astCachetable[m_txtCache->Page][loop])
872 /* enable manual SubPage zapping */
873 m_txtCache->ZapSubpageManual = true;
875 /* update page */
876 if (m_RenderInfo.ZoomMode == 2) /* if zoomed to lower half */
877 m_RenderInfo.ZoomMode = 1; /* activate upper half */
879 m_txtCache->SubPage = loop;
880 m_RenderInfo.HintMode = false;
881 m_txtCache->PageUpdate = true;
883 return;
888 void CTeletextDecoder::SwitchZoomMode()
890 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
892 if (m_txtCache->SubPageTable[m_txtCache->Page] != 0xFF)
894 /* toggle mode */
895 m_RenderInfo.ZoomMode++;
897 if (m_RenderInfo.ZoomMode == 3)
898 m_RenderInfo.ZoomMode = 0;
900 /* update page */
901 m_txtCache->PageUpdate = true;
905 void CTeletextDecoder::SwitchTranspMode()
907 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
909 /* toggle mode */
910 if (!m_RenderInfo.TranspMode)
911 m_RenderInfo.TranspMode = true;
912 else
913 m_RenderInfo.TranspMode = false; /* backward to immediately switch to TV-screen */
915 /* set mode */
916 if (!m_RenderInfo.TranspMode) /* normal text-only */
918 ClearBB(m_txtCache->FullScrColor);
919 m_txtCache->PageUpdate = true;
921 else /* semi-transparent BG with FG text */
923 ClearBB(TXT_ColorTransp);
924 m_txtCache->PageUpdate = true;
928 void CTeletextDecoder::SwitchHintMode()
930 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
932 /* toggle mode */
933 m_RenderInfo.HintMode ^= true;
935 if (!m_RenderInfo.HintMode) /* toggle evaluation of level 2.5 information by explicitly switching off HintMode */
937 m_RenderInfo.Showl25 ^= true;
939 /* update page */
940 m_txtCache->PageUpdate = true;
943 void CTeletextDecoder::ColorKey(int target)
945 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
947 if (!target)
948 return;
950 if (m_RenderInfo.ZoomMode == 2)
951 m_RenderInfo.ZoomMode = 1;
953 m_LastPage = m_txtCache->Page;
954 m_txtCache->Page = target;
955 m_txtCache->SubPage = m_txtCache->SubPageTable[m_txtCache->Page];
956 m_RenderInfo.InputCounter = 2;
957 m_RenderInfo.HintMode = false;
958 m_txtCache->PageUpdate = true;
961 void CTeletextDecoder::StartPageCatching()
963 m_RenderInfo.PageCatching = true;
965 /* abort pageinput */
966 m_RenderInfo.InputCounter = 2;
968 /* show info line */
969 m_RenderInfo.ZoomMode = 0;
970 m_RenderInfo.PosX = 0;
971 m_RenderInfo.PosY = 24*m_RenderInfo.FontHeight;
973 /* check for pagenumber(s) */
974 m_CatchRow = 1;
975 m_CatchCol = 0;
976 m_CatchedPage = 0;
977 m_PCOldRow = 0;
978 m_PCOldCol = 0; /* no inverted page number to restore yet */
979 CatchNextPage(0, 1);
981 if (!m_CatchedPage)
983 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
985 m_RenderInfo.PageCatching = false;
986 m_txtCache->PageUpdate = true;
987 return;
991 void CTeletextDecoder::StopPageCatching()
993 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
995 /* set new page */
996 if (m_RenderInfo.ZoomMode == 2)
997 m_RenderInfo.ZoomMode = 1;
999 m_LastPage = m_txtCache->Page;
1000 m_txtCache->Page = m_CatchedPage;
1001 m_RenderInfo.HintMode = false;
1002 m_txtCache->PageUpdate = true;
1003 m_RenderInfo.PageCatching = false;
1005 int subp = m_txtCache->SubPageTable[m_txtCache->Page];
1006 if (subp != 0xFF)
1007 m_txtCache->SubPage = subp;
1008 else
1009 m_txtCache->SubPage = 0;
1012 void CTeletextDecoder::CatchNextPage(int firstlineinc, int inc)
1014 int tmp_page, allowwrap = 1; /* allow first wrap around */
1016 /* catch next page */
1017 for(;;)
1019 unsigned char *p = &(m_RenderInfo.PageChar[m_CatchRow*40 + m_CatchCol]);
1020 TextPageAttr_t a = m_RenderInfo.PageAtrb[m_CatchRow*40 + m_CatchCol];
1022 if (!(a.charset == C_G1C || a.charset == C_G1S) && /* no mosaic */
1023 (a.fg != a.bg) && /* not hidden */
1024 (*p >= '1' && *p <= '8' && /* valid page number */
1025 *(p+1) >= '0' && *(p+1) <= '9' &&
1026 *(p+2) >= '0' && *(p+2) <= '9') &&
1027 (m_CatchRow == 0 || (*(p-1) < '0' || *(p-1) > '9')) && /* non-numeric char before and behind */
1028 (m_CatchRow == 37 || (*(p+3) < '0' || *(p+3) > '9')))
1030 tmp_page = ((*p - '0')<<8) | ((*(p+1) - '0')<<4) | (*(p+2) - '0');
1032 #if 0
1033 if (tmp_page != m_CatchedPage) /* confusing to skip identical page numbers - I want to reach what I aim to */
1034 #endif
1036 m_CatchedPage = tmp_page;
1037 RenderCatchedPage();
1038 m_CatchCol += inc; /* FIXME: limit */
1039 return;
1043 if (firstlineinc > 0)
1045 m_CatchRow++;
1046 m_CatchCol = 0;
1047 firstlineinc = 0;
1049 else if (firstlineinc < 0)
1051 m_CatchRow--;
1052 m_CatchCol = 37;
1053 firstlineinc = 0;
1055 else
1056 m_CatchCol += inc;
1058 if (m_CatchCol > 37)
1060 m_CatchRow++;
1061 m_CatchCol = 0;
1063 else if (m_CatchCol < 0)
1065 m_CatchRow--;
1066 m_CatchCol = 37;
1069 if (m_CatchRow > 23)
1071 if (allowwrap)
1073 allowwrap = 0;
1074 m_CatchRow = 1;
1075 m_CatchCol = 0;
1077 else
1079 return;
1082 else if (m_CatchRow < 1)
1084 if (allowwrap)
1086 allowwrap = 0;
1087 m_CatchRow = 23;
1088 m_CatchCol =37;
1090 else
1092 return;
1098 void CTeletextDecoder::RenderCatchedPage()
1100 int zoom = 0;
1101 m_updateTexture = true;
1103 /* handle zoom */
1104 if (m_RenderInfo.ZoomMode)
1105 zoom = 1<<10;
1107 if (m_PCOldRow || m_PCOldCol) /* not at first call */
1109 /* restore pagenumber */
1110 SetPosX(m_PCOldCol);
1112 if (m_RenderInfo.ZoomMode == 2)
1113 m_RenderInfo.PosY = (m_PCOldRow-12)*m_RenderInfo.FontHeight*((zoom>>10)+1);
1114 else
1115 m_RenderInfo.PosY = m_PCOldRow*m_RenderInfo.FontHeight*((zoom>>10)+1);
1117 RenderCharFB(m_RenderInfo.PageChar[m_PCOldRow*40 + m_PCOldCol ], &m_RenderInfo.PageAtrb[m_PCOldRow*40 + m_PCOldCol ]);
1118 RenderCharFB(m_RenderInfo.PageChar[m_PCOldRow*40 + m_PCOldCol + 1], &m_RenderInfo.PageAtrb[m_PCOldRow*40 + m_PCOldCol + 1]);
1119 RenderCharFB(m_RenderInfo.PageChar[m_PCOldRow*40 + m_PCOldCol + 2], &m_RenderInfo.PageAtrb[m_PCOldRow*40 + m_PCOldCol + 2]);
1122 m_PCOldRow = m_CatchRow;
1123 m_PCOldCol = m_CatchCol;
1125 /* mark pagenumber */
1126 if (m_RenderInfo.ZoomMode == 1 && m_CatchRow > 11)
1128 m_RenderInfo.ZoomMode = 2;
1129 CopyBB2FB();
1131 else if (m_RenderInfo.ZoomMode == 2 && m_CatchRow < 12)
1133 m_RenderInfo.ZoomMode = 1;
1134 CopyBB2FB();
1136 SetPosX(m_CatchCol);
1138 if (m_RenderInfo.ZoomMode == 2)
1139 m_RenderInfo.PosY = (m_CatchRow-12)*m_RenderInfo.FontHeight*((zoom>>10)+1);
1140 else
1141 m_RenderInfo.PosY = m_CatchRow*m_RenderInfo.FontHeight*((zoom>>10)+1);
1143 TextPageAttr_t a0 = m_RenderInfo.PageAtrb[m_CatchRow*40 + m_CatchCol ];
1144 TextPageAttr_t a1 = m_RenderInfo.PageAtrb[m_CatchRow*40 + m_CatchCol + 1];
1145 TextPageAttr_t a2 = m_RenderInfo.PageAtrb[m_CatchRow*40 + m_CatchCol + 2];
1146 int t;
1148 /* exchange colors */
1149 t = a0.fg; a0.fg = a0.bg; a0.bg = t;
1150 t = a1.fg; a1.fg = a1.bg; a1.bg = t;
1151 t = a2.fg; a2.fg = a2.bg; a2.bg = t;
1153 RenderCharFB(m_RenderInfo.PageChar[m_CatchRow*40 + m_CatchCol ], &a0);
1154 RenderCharFB(m_RenderInfo.PageChar[m_CatchRow*40 + m_CatchCol + 1], &a1);
1155 RenderCharFB(m_RenderInfo.PageChar[m_CatchRow*40 + m_CatchCol + 2], &a2);
1158 void CTeletextDecoder::RenderPage()
1160 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1162 int StartRow = 0;
1163 int national_subset_bak = m_txtCache->NationalSubset;
1165 if (m_txtCache->PageUpdate)
1166 m_updateTexture = true;
1168 /* update page or timestring */
1169 if (m_txtCache->PageUpdate && m_txtCache->PageReceiving != m_txtCache->Page && m_RenderInfo.InputCounter == 2)
1171 /* reset update flag */
1172 m_txtCache->PageUpdate = false;
1173 if (m_RenderInfo.Boxed && m_RenderInfo.SubtitleDelay)
1175 TextSubtitleCache_t* c = NULL;
1176 int j = -1;
1177 for (int i = 0; i < SUBTITLE_CACHESIZE; i++)
1179 if (j == -1 && !m_RenderInfo.SubtitleCache[i])
1180 j = i;
1181 if (m_RenderInfo.SubtitleCache[i] && !m_RenderInfo.SubtitleCache[i]->Valid)
1183 c = m_RenderInfo.SubtitleCache[i];
1184 break;
1187 if (c == NULL)
1189 if (j == -1) // no more space in SubtitleCache
1190 return;
1192 c = new TextSubtitleCache_t;
1193 if (c == NULL)
1194 return;
1196 *c = {};
1197 m_RenderInfo.SubtitleCache[j] = c;
1199 c->Valid = true;
1200 c->Timestamp = std::chrono::steady_clock::now();
1202 if (m_txtCache->SubPageTable[m_txtCache->Page] != 0xFF)
1204 TextPageinfo_t * p = DecodePage(m_RenderInfo.Showl25, c->PageChar, c->PageAtrb, m_RenderInfo.HintMode, m_RenderInfo.ShowFlof);
1205 if (p)
1207 m_RenderInfo.Boxed = p->boxed;
1210 m_RenderInfo.DelayStarted = true;
1211 return;
1213 m_RenderInfo.DelayStarted = false;
1214 /* decode page */
1215 if (m_txtCache->SubPageTable[m_txtCache->Page] != 0xFF)
1217 TextPageinfo_t * p = DecodePage(m_RenderInfo.Showl25, m_RenderInfo.PageChar, m_RenderInfo.PageAtrb, m_RenderInfo.HintMode, m_RenderInfo.ShowFlof);
1218 if (p)
1220 m_RenderInfo.PageInfo = p;
1221 m_RenderInfo.Boxed = p->boxed;
1223 if (m_RenderInfo.Boxed || m_RenderInfo.TranspMode)
1224 FillBorder(GetColorRGB(TXT_ColorTransp));
1225 else
1226 FillBorder(GetColorRGB((enumTeletextColor)m_txtCache->FullScrColor));
1228 if (m_txtCache->ColorTable) /* as late as possible to shorten the time the old page is displayed with the new colors */
1229 SetColors(m_txtCache->ColorTable, 16, 16); /* set colors for CLUTs 2+3 */
1231 else
1232 StartRow = 1;
1234 DoRenderPage(StartRow, national_subset_bak);
1236 else
1238 if (m_RenderInfo.DelayStarted)
1240 auto now = std::chrono::steady_clock::now();
1241 for (TextSubtitleCache_t* const subtitleCache : m_RenderInfo.SubtitleCache)
1243 if (subtitleCache && subtitleCache->Valid &&
1244 std::chrono::duration_cast<std::chrono::seconds>(now - subtitleCache->Timestamp)
1245 .count() >= m_RenderInfo.SubtitleDelay)
1247 memcpy(m_RenderInfo.PageChar, subtitleCache->PageChar, 40 * 25);
1248 memcpy(m_RenderInfo.PageAtrb, subtitleCache->PageAtrb, 40 * 25 * sizeof(TextPageAttr_t));
1249 DoRenderPage(StartRow, national_subset_bak);
1250 subtitleCache->Valid = false;
1251 return;
1255 if (m_RenderInfo.ZoomMode != 2)
1257 m_RenderInfo.PosY = 0;
1258 if (m_txtCache->SubPageTable[m_txtCache->Page] == 0xff)
1260 m_RenderInfo.PageAtrb[32].fg = TXT_ColorYellow;
1261 m_RenderInfo.PageAtrb[32].bg = TXT_ColorMenu1;
1262 int showpage = m_txtCache->PageReceiving;
1263 int showsubpage;
1265 // Verify that showpage is positive before any access to the array
1266 if (showpage >= 0 && (showsubpage = m_txtCache->SubPageTable[showpage]) != 0xff)
1268 TextCachedPage_t *pCachedPage;
1269 pCachedPage = m_txtCache->astCachetable[showpage][showsubpage];
1270 if (pCachedPage && IsDec(showpage))
1272 m_RenderInfo.PosX = 0;
1273 if (m_RenderInfo.InputCounter == 2)
1275 if (m_txtCache->BTTok && !m_txtCache->BasicTop[m_txtCache->Page]) /* page non-existent according to TOP (continue search anyway) */
1277 m_RenderInfo.PageAtrb[0].fg = TXT_ColorWhite;
1278 m_RenderInfo.PageAtrb[0].bg = TXT_ColorRed;
1280 else
1282 m_RenderInfo.PageAtrb[0].fg = TXT_ColorYellow;
1283 m_RenderInfo.PageAtrb[0].bg = TXT_ColorMenu1;
1285 CDVDTeletextTools::Hex2Str((char*)m_RenderInfo.PageChar+3, m_txtCache->Page);
1287 int col;
1288 for (col = m_RenderInfo.nofirst; col < 7; col++) // selected page
1290 RenderCharFB(m_RenderInfo.PageChar[col], &m_RenderInfo.PageAtrb[0]);
1292 RenderCharFB(m_RenderInfo.PageChar[col], &m_RenderInfo.PageAtrb[32]);
1294 else
1295 SetPosX(8);
1297 memcpy(&m_RenderInfo.PageChar[8], pCachedPage->p0, 24); /* header line without timestring */
1298 for (unsigned char i : pCachedPage->p0)
1300 RenderCharFB(i, &m_RenderInfo.PageAtrb[32]);
1303 /* Update on every Header number change */
1304 if (pCachedPage->p0[2] != prevHeaderPage)
1306 prevHeaderPage = pCachedPage->p0[2];
1307 m_updateTexture = true;
1313 /* update timestring */
1314 SetPosX(32);
1315 for (int i = 0; i < 8; i++)
1317 if (!m_RenderInfo.PageAtrb[32+i].flashing)
1318 RenderCharFB(m_txtCache->TimeString[i], &m_RenderInfo.PageAtrb[32]);
1319 else
1321 SetPosX(33+i);
1325 if (!IsSubtitlePage(m_txtCache->Page))
1327 /* Update on every changed second */
1328 if (m_txtCache->TimeString[7] != prevTimeSec)
1330 prevTimeSec = m_txtCache->TimeString[7];
1331 m_updateTexture = true;
1334 else
1336 m_updateTexture = true;
1339 DoFlashing(StartRow);
1340 m_txtCache->NationalSubset = national_subset_bak;
1344 bool CTeletextDecoder::IsSubtitlePage(int pageNumber) const
1346 if (!m_txtCache)
1347 return false;
1349 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1351 for (const auto subPage : m_txtCache->SubtitlePages)
1353 if (subPage.page == pageNumber)
1354 return true;
1357 return false;
1360 void CTeletextDecoder::DoFlashing(int startrow)
1362 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1364 TextCachedPage_t* textCachepage =
1365 m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPage];
1367 // Verify that the page is not deleted by the other thread: CDVDTeletextData::ResetTeletextCache()
1368 if (!textCachepage || m_RenderInfo.PageInfo != &textCachepage->pageinfo)
1369 m_RenderInfo.PageInfo = nullptr;
1371 /* get national subset */
1372 if (m_txtCache->NationalSubset <= NAT_MAX_FROM_HEADER && /* not for GR/RU as long as line28 is not evaluated */
1373 m_RenderInfo.PageInfo && m_RenderInfo.PageInfo->nationalvalid) /* individual subset according to page header */
1375 m_txtCache->NationalSubset = CountryConversionTable[m_RenderInfo.PageInfo->national];
1378 /* Flashing */
1379 TextPageAttr_t flashattr;
1380 char flashchar;
1381 std::chrono::milliseconds flashphase = std::chrono::duration_cast<std::chrono::milliseconds>(
1382 std::chrono::steady_clock::now().time_since_epoch()) %
1383 1000;
1385 int srow = startrow;
1386 int erow = 24;
1387 int factor=1;
1389 switch (m_RenderInfo.ZoomMode)
1391 case 1: erow = 12; factor=2;break;
1392 case 2: srow = 12; factor=2;break;
1395 m_RenderInfo.PosY = startrow*m_RenderInfo.FontHeight*factor;
1396 for (int row = srow; row < erow; row++)
1398 int index = row * 40;
1399 int dhset = 0;
1400 int incflash = 3;
1401 int decflash = 2;
1403 m_RenderInfo.PosX = 0;
1404 for (int col = m_RenderInfo.nofirst; col < 40; col++)
1406 if (m_RenderInfo.PageAtrb[index + col].flashing && m_RenderInfo.PageChar[index + col] > 0x20 && m_RenderInfo.PageChar[index + col] != 0xff )
1408 SetPosX(col);
1409 flashchar = m_RenderInfo.PageChar[index + col];
1410 bool doflash = false;
1411 memcpy(&flashattr, &m_RenderInfo.PageAtrb[index + col], sizeof(TextPageAttr_t));
1412 switch (flashattr.flashing &0x1c) // Flash Rate
1414 case 0x00 : // 1 Hz
1415 if (flashphase > 500ms)
1416 doflash = true;
1417 break;
1418 case 0x04 : // 2 Hz Phase 1
1419 if (flashphase < 250ms)
1420 doflash = true;
1421 break;
1422 case 0x08 : // 2 Hz Phase 2
1423 if (flashphase >= 250ms && flashphase < 500ms)
1424 doflash = true;
1425 break;
1426 case 0x0c : // 2 Hz Phase 3
1427 if (flashphase >= 500ms && flashphase < 750ms)
1428 doflash = true;
1429 break;
1430 case 0x10 : // incremental flash
1431 incflash++;
1432 if (incflash>3) incflash = 1;
1433 switch (incflash)
1435 case 1:
1436 if (flashphase < 250ms)
1437 doflash = true;
1438 break;
1439 case 2:
1440 if (flashphase >= 250ms && flashphase < 500ms)
1441 doflash = true;
1442 break;
1443 case 3:
1444 if (flashphase >= 500ms && flashphase < 750ms)
1445 doflash = true;
1446 break;
1448 break;
1449 case 0x14 : // decremental flash
1450 decflash--;
1451 if (decflash<1) decflash = 3;
1452 switch (decflash)
1454 case 1:
1455 if (flashphase < 250ms)
1456 doflash = true;
1457 break;
1458 case 2:
1459 if (flashphase >= 250ms && flashphase < 500ms)
1460 doflash = true;
1461 break;
1462 case 3:
1463 if (flashphase >= 500ms && flashphase < 750ms)
1464 doflash = true;
1465 break;
1467 break;
1471 switch (flashattr.flashing &0x03) // Flash Mode
1473 case 0x01 : // normal Flashing
1474 if (doflash) flashattr.fg = flashattr.bg;
1475 break;
1476 case 0x02 : // inverted Flashing
1477 doflash = !doflash;
1478 if (doflash) flashattr.fg = flashattr.bg;
1479 break;
1480 case 0x03 : // color Flashing
1481 if (doflash) flashattr.fg = flashattr.fg + (flashattr.fg > 7 ? (-8) : 8);
1482 break;
1485 RenderCharFB(flashchar, &flashattr);
1486 if (flashattr.doublew) col++;
1487 if (flashattr.doubleh) dhset = 1;
1489 m_updateTexture = true;
1492 if (dhset)
1494 row++;
1495 m_RenderInfo.PosY += m_RenderInfo.FontHeight*factor;
1497 m_RenderInfo.PosY += m_RenderInfo.FontHeight*factor;
1501 void CTeletextDecoder::DoRenderPage(int startrow, int national_subset_bak)
1503 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1505 /* display first column? */
1506 m_RenderInfo.nofirst = m_RenderInfo.Show39;
1507 for (int row = 1; row < 24; row++)
1509 int Byte = m_RenderInfo.PageChar[row*40];
1510 if (Byte != ' ' && Byte != 0x00 && Byte != 0xFF && m_RenderInfo.PageAtrb[row*40].fg != m_RenderInfo.PageAtrb[row*40].bg)
1512 m_RenderInfo.nofirst = 0;
1513 break;
1516 m_RenderInfo.FontWidth_Normal = m_RenderInfo.Width / (m_RenderInfo.nofirst ? 39 : 40);
1517 SetFontWidth(m_RenderInfo.FontWidth_Normal);
1519 if (m_RenderInfo.TranspMode || m_RenderInfo.Boxed)
1521 FillBorder(GetColorRGB(TXT_ColorTransp));//ClearBB(transp);
1522 m_RenderInfo.ClearBBColor = TXT_ColorTransp;
1525 /* get national subset */
1526 if (m_txtCache->NationalSubset <= NAT_MAX_FROM_HEADER && /* not for GR/RU as long as line28 is not evaluated */
1527 m_RenderInfo.PageInfo && m_RenderInfo.PageInfo->nationalvalid) /* individual subset according to page header */
1529 m_txtCache->NationalSubset = CountryConversionTable[m_RenderInfo.PageInfo->national];
1531 /* render page */
1532 if (m_RenderInfo.PageInfo && (m_RenderInfo.PageInfo->function == FUNC_GDRCS || m_RenderInfo.PageInfo->function == FUNC_DRCS)) /* character definitions */
1534 #define DRCSROWS 8
1535 #define DRCSCOLS (48/DRCSROWS)
1536 #define DRCSZOOMX 3
1537 #define DRCSZOOMY 5
1538 #define DRCSXSPC (12*DRCSZOOMX + 2)
1539 #define DRCSYSPC (10*DRCSZOOMY + 2)
1541 unsigned char ax[] = { /* array[0..12] of x-offsets, array[0..10] of y-offsets for each pixel */
1542 DRCSZOOMX * 0,
1543 DRCSZOOMX * 1,
1544 DRCSZOOMX * 2,
1545 DRCSZOOMX * 3,
1546 DRCSZOOMX * 4,
1547 DRCSZOOMX * 5,
1548 DRCSZOOMX * 6,
1549 DRCSZOOMX * 7,
1550 DRCSZOOMX * 8,
1551 DRCSZOOMX * 9,
1552 DRCSZOOMX * 10,
1553 DRCSZOOMX * 11,
1554 DRCSZOOMX * 12,
1555 DRCSZOOMY * 0,
1556 DRCSZOOMY * 1,
1557 DRCSZOOMY * 2,
1558 DRCSZOOMY * 3,
1559 DRCSZOOMY * 4,
1560 DRCSZOOMY * 5,
1561 DRCSZOOMY * 6,
1562 DRCSZOOMY * 7,
1563 DRCSZOOMY * 8,
1564 DRCSZOOMY * 9,
1565 DRCSZOOMY * 10
1568 ClearBB(TXT_ColorBlack);
1569 for (int col = 0; col < 24*40; col++)
1570 m_RenderInfo.PageAtrb[col] = Text_AtrTable[ATR_WB];
1572 for (int row = 0; row < DRCSROWS; row++)
1574 for (int col = 0; col < DRCSCOLS; col++)
1576 RenderDRCS(m_RenderInfo.Width,
1577 m_RenderInfo.PageChar + 20 * (DRCSCOLS * row + col + 2),
1578 m_TextureBuffer
1579 + (m_RenderInfo.FontHeight + DRCSYSPC * row + m_RenderInfo.Height) * m_RenderInfo.Width
1580 + DRCSXSPC * col,
1581 ax, GetColorRGB(TXT_ColorWhite), GetColorRGB(TXT_ColorBlack));
1584 memset(m_RenderInfo.PageChar + 40, 0xff, 24*40); /* don't render any char below row 0 */
1586 m_RenderInfo.PosY = startrow*m_RenderInfo.FontHeight;
1587 for (int row = startrow; row < 24; row++)
1589 int index = row * 40;
1591 m_RenderInfo.PosX = 0;
1592 for (int col = m_RenderInfo.nofirst; col < 40; col++)
1594 RenderCharBB(m_RenderInfo.PageChar[index + col], &m_RenderInfo.PageAtrb[index + col]);
1596 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 */
1597 m_RenderInfo.PageChar[index + col + 40] = 0xff;
1598 if (m_RenderInfo.PageAtrb[index + col].doublew && col < 40-1) /* skip next column if double width */
1600 col++;
1601 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 */
1602 m_RenderInfo.PageChar[index + col + 40] = 0xff;
1605 m_RenderInfo.PosY += m_RenderInfo.FontHeight;
1607 DoFlashing(startrow);
1609 /* update framebuffer */
1610 CopyBB2FB();
1611 m_txtCache->NationalSubset = national_subset_bak;
1614 void CTeletextDecoder::Decode_BTT()
1616 /* basic top table */
1617 int current, b1, b2, b3, b4;
1618 unsigned char btt[23*40];
1620 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1622 if (m_txtCache->SubPageTable[0x1f0] == 0xff || 0 == m_txtCache->astCachetable[0x1f0][m_txtCache->SubPageTable[0x1f0]]) /* not yet received */
1623 return;
1625 auto& components = CServiceBroker::GetAppComponents();
1626 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
1628 appPlayer->LoadPage(0x1f0, m_txtCache->SubPageTable[0x1f0], btt);
1629 if (btt[799] == ' ') /* not completely received or error */
1630 return;
1632 current = 0x100;
1633 for (int i = 0; i < 800; i++)
1635 b1 = btt[i];
1636 if (b1 == ' ')
1637 b1 = 0;
1638 else
1640 b1 = dehamming[b1];
1641 if (b1 == 0xFF) /* hamming error in btt */
1643 btt[799] = ' '; /* mark btt as not received */
1644 return;
1647 m_txtCache->BasicTop[current] = b1;
1648 CDVDTeletextTools::NextDec(&current);
1650 /* page linking table */
1651 m_txtCache->ADIP_PgMax = -1; /* rebuild table of adip pages */
1652 for (int i = 0; i < 10; i++)
1654 b1 = dehamming[btt[800 + 8*i +0]];
1656 if (b1 == 0xE)
1657 continue; /* unused */
1658 else if (b1 == 0xF)
1659 break; /* end */
1661 b4 = dehamming[btt[800 + 8*i +7]];
1663 if (b4 != 2) /* only adip, ignore multipage (1) */
1664 continue;
1666 b2 = dehamming[btt[800 + 8*i +1]];
1667 b3 = dehamming[btt[800 + 8*i +2]];
1669 if (b1 == 0xFF || b2 == 0xFF || b3 == 0xFF)
1671 CLog::Log(LOGERROR, "CTeletextDecoder::Decode_BTT <Biterror in btt/plt index {}>", i);
1672 btt[799] = ' '; /* mark btt as not received */
1673 return;
1676 b1 = b1<<8 | b2<<4 | b3; /* page number */
1677 m_txtCache->ADIP_Pg[++m_txtCache->ADIP_PgMax] = b1;
1680 m_txtCache->BTTok = true;
1683 void CTeletextDecoder::Decode_ADIP() /* additional information table */
1685 int i, p, j, b1, b2, b3, charfound;
1686 unsigned char padip[23*40];
1688 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1690 auto& components = CServiceBroker::GetAppComponents();
1691 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
1693 for (i = 0; i <= m_txtCache->ADIP_PgMax; i++)
1695 p = m_txtCache->ADIP_Pg[i];
1696 if (!p || m_txtCache->SubPageTable[p] == 0xff || 0 == m_txtCache->astCachetable[p][m_txtCache->SubPageTable[p]]) /* not cached (avoid segfault) */
1697 continue;
1699 appPlayer->LoadPage(p, m_txtCache->SubPageTable[p], padip);
1700 for (j = 0; j < 44; j++)
1702 b1 = dehamming[padip[20*j+0]];
1703 if (b1 == 0xE)
1704 continue; /* unused */
1706 if (b1 == 0xF)
1707 break; /* end */
1709 b2 = dehamming[padip[20*j+1]];
1710 b3 = dehamming[padip[20*j+2]];
1712 if (b1 == 0xFF || b2 == 0xFF || b3 == 0xFF)
1714 CLog::Log(LOGERROR,
1715 "CTeletextDecoder::Decode_BTT <Biterror in ait {:03x} {} {:02x} {:02x} {:02x} "
1716 "{:02x} {:02x} {:02x}>",
1717 p, j, padip[20 * j + 0], padip[20 * j + 1], padip[20 * j + 2], b1, b2, b3);
1718 return;
1721 if (b1>8 || b2>9 || b3>9) /* ignore entries with invalid or hex page numbers */
1723 continue;
1726 b1 = b1<<8 | b2<<4 | b3; /* page number */
1727 charfound = 0; /* flag: no printable char found */
1729 for (b2 = 11; b2 >= 0; b2--)
1731 b3 = deparity[padip[20*j + 8 + b2]];
1732 if (b3 < ' ')
1733 b3 = ' ';
1735 if (b3 == ' ' && !charfound)
1736 m_txtCache->ADIPTable[b1][b2] = '\0';
1737 else
1739 m_txtCache->ADIPTable[b1][b2] = b3;
1740 charfound = 1;
1743 } /* next link j */
1745 m_txtCache->ADIP_Pg[i] = 0; /* completely decoded: clear entry */
1746 } /* next adip page i */
1748 while ((m_txtCache->ADIP_PgMax >= 0) && !m_txtCache->ADIP_Pg[m_txtCache->ADIP_PgMax]) /* and shrink table */
1749 m_txtCache->ADIP_PgMax--;
1752 int CTeletextDecoder::TopText_GetNext(int startpage, int up, int findgroup)
1754 int current, nextgrp, nextblk;
1756 int stoppage = (IsDec(startpage) ? startpage : startpage & 0xF00); // avoid endless loop in hexmode
1757 nextgrp = nextblk = 0;
1758 current = startpage;
1760 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1762 do {
1763 if (up)
1764 CDVDTeletextTools::NextDec(&current);
1765 else
1766 CDVDTeletextTools::PrevDec(&current);
1768 if (!m_txtCache->BTTok || m_txtCache->BasicTop[current]) /* only if existent */
1770 if (findgroup)
1772 if (m_txtCache->BasicTop[current] >= 6 && m_txtCache->BasicTop[current] <= 7)
1773 return current;
1774 if (!nextgrp && (current&0x00F) == 0)
1775 nextgrp = current;
1777 if (m_txtCache->BasicTop[current] >= 2 && m_txtCache->BasicTop[current] <= 5) /* always find block */
1778 return current;
1780 if (!nextblk && (current&0x0FF) == 0)
1781 nextblk = current;
1783 } while (current != stoppage);
1785 if (nextgrp)
1786 return nextgrp;
1787 else if (nextblk)
1788 return nextblk;
1789 else
1790 return current;
1793 void CTeletextDecoder::Showlink(int column, int linkpage)
1795 unsigned char line[] = " >??? ";
1796 int oldfontwidth = m_RenderInfo.FontWidth;
1797 int yoffset;
1799 if (m_YOffset)
1800 yoffset = 0;
1801 else
1802 yoffset = m_RenderInfo.Height;
1804 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
1805 int width = m_RenderInfo.Width /4;
1807 m_RenderInfo.PosY = 24*m_RenderInfo.FontHeight;
1809 if (m_RenderInfo.Boxed)
1811 m_RenderInfo.PosX = column*width;
1812 FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY+yoffset, m_RenderInfo.Width, m_RenderInfo.FontHeight, GetColorRGB(TXT_ColorTransp));
1813 return;
1816 if (m_txtCache->ADIPTable[linkpage][0])
1818 m_RenderInfo.PosX = column*width;
1819 int l = strlen(m_txtCache->ADIPTable[linkpage]);
1821 if (l > 9) /* smaller font, if no space for one half space at front and end */
1822 SetFontWidth(oldfontwidth * 10 / (l+1));
1824 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));
1825 m_RenderInfo.PosX += ((width) - (l*m_RenderInfo.FontWidth+l*m_RenderInfo.FontWidth/abx))/2; /* center */
1827 for (char *p = m_txtCache->ADIPTable[linkpage]; *p; p++)
1828 RenderCharBB(*p, &Text_AtrTable[ATR_L250 + column]);
1830 SetFontWidth(oldfontwidth);
1832 else /* display number */
1834 m_RenderInfo.PosX = column*width;
1835 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));
1836 if (linkpage < m_txtCache->Page)
1838 line[6] = '<';
1839 CDVDTeletextTools::Hex2Str((char*)line + 5, linkpage);
1841 else
1842 CDVDTeletextTools::Hex2Str((char*)line + 6, linkpage);
1844 for (unsigned char *p = line; p < line+9; p++)
1845 RenderCharBB(*p, &Text_AtrTable[ATR_L250 + column]);
1849 void CTeletextDecoder::CreateLine25()
1851 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1853 /* btt completely received and not yet decoded */
1854 if (!m_txtCache->BTTok)
1855 Decode_BTT();
1857 if (m_txtCache->ADIP_PgMax >= 0)
1858 Decode_ADIP();
1860 if (!m_RenderInfo.ShowHex && m_RenderInfo.ShowFlof &&
1861 (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
1863 m_RenderInfo.Prev_100 = m_txtCache->FlofPages[m_txtCache->Page][0];
1864 m_RenderInfo.Prev_10 = m_txtCache->FlofPages[m_txtCache->Page][1];
1865 m_RenderInfo.Next_10 = m_txtCache->FlofPages[m_txtCache->Page][2];
1866 m_RenderInfo.Next_100 = m_txtCache->FlofPages[m_txtCache->Page][3];
1868 m_RenderInfo.PosY = 24*m_RenderInfo.FontHeight;
1869 m_RenderInfo.PosX = 0;
1870 for (int i=m_RenderInfo.nofirst; i<40; i++)
1871 RenderCharBB(m_RenderInfo.PageChar[24*40 + i], &m_RenderInfo.PageAtrb[24*40 + i]);
1873 else
1875 /* normal: blk-1, grp+1, grp+2, blk+1 */
1876 /* hex: hex+1, blk-1, grp+1, blk+1 */
1877 if (m_RenderInfo.ShowHex)
1879 /* arguments: startpage, up, findgroup */
1880 m_RenderInfo.Prev_100 = NextHex(m_txtCache->Page);
1881 m_RenderInfo.Prev_10 = TopText_GetNext(m_txtCache->Page, 0, 0);
1882 m_RenderInfo.Next_10 = TopText_GetNext(m_txtCache->Page, 1, 1);
1884 else
1886 m_RenderInfo.Prev_100 = TopText_GetNext(m_txtCache->Page, 0, 0);
1887 m_RenderInfo.Prev_10 = TopText_GetNext(m_txtCache->Page, 1, 1);
1888 m_RenderInfo.Next_10 = TopText_GetNext(m_RenderInfo.Prev_10, 1, 1);
1890 m_RenderInfo.Next_100 = TopText_GetNext(m_RenderInfo.Next_10, 1, 0);
1891 Showlink(0, m_RenderInfo.Prev_100);
1892 Showlink(1, m_RenderInfo.Prev_10);
1893 Showlink(2, m_RenderInfo.Next_10);
1894 Showlink(3, m_RenderInfo.Next_100);
1898 void CTeletextDecoder::RenderCharFB(int Char, TextPageAttr_t *Attribute)
1900 RenderCharIntern(&m_RenderInfo, Char, Attribute, m_RenderInfo.ZoomMode, m_YOffset);
1903 void CTeletextDecoder::RenderCharBB(int Char, TextPageAttr_t *Attribute)
1905 RenderCharIntern(&m_RenderInfo, Char, Attribute, 0, m_RenderInfo.Height-m_YOffset);
1908 void CTeletextDecoder::CopyBB2FB()
1910 UTILS::COLOR::Color *src, *dst, *topsrc;
1911 int screenwidth;
1912 UTILS::COLOR::Color fillcolor;
1914 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1916 /* line 25 */
1917 if (!m_RenderInfo.PageCatching)
1918 CreateLine25();
1920 /* copy backbuffer to framebuffer */
1921 if (!m_RenderInfo.ZoomMode)
1923 if (m_YOffset)
1924 m_YOffset = 0;
1925 else
1926 m_YOffset = m_RenderInfo.Height;
1928 if (m_RenderInfo.ClearBBColor >= 0)
1930 m_RenderInfo.ClearBBColor = -1;
1932 return;
1935 src = dst = topsrc = m_TextureBuffer + m_RenderInfo.Width;
1937 if (m_YOffset)
1939 dst += m_RenderInfo.Width * m_RenderInfo.Height;
1941 else
1943 src += m_RenderInfo.Width * m_RenderInfo.Height;
1944 topsrc += m_RenderInfo.Width * m_RenderInfo.Height;
1947 if (!m_RenderInfo.PageCatching)
1948 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 */
1950 if (m_RenderInfo.TranspMode)
1951 fillcolor = GetColorRGB(TXT_ColorTransp);
1952 else
1953 fillcolor = GetColorRGB((enumTeletextColor)m_txtCache->FullScrColor);
1955 if (m_RenderInfo.ZoomMode == 2)
1956 src += 12*m_RenderInfo.FontHeight*m_RenderInfo.Width;
1958 screenwidth = m_RenderInfo.Width;
1960 for (int i = 12*m_RenderInfo.FontHeight; i; i--)
1962 SDL_memcpy4(dst, src, screenwidth);
1963 dst += m_RenderInfo.Width;
1964 SDL_memcpy4(dst, src, screenwidth);
1965 dst += m_RenderInfo.Width;
1966 src += m_RenderInfo.Width;
1969 for (int i = m_RenderInfo.Height - 25*m_RenderInfo.FontHeight; i >= 0;i--)
1971 SDL_memset4(dst + m_RenderInfo.Width*(m_RenderInfo.FontHeight+i), fillcolor, screenwidth);
1975 FT_Error CTeletextDecoder::MyFaceRequester(FTC_FaceID face_id, FT_Library library, FT_Pointer request_data, FT_Face *aface)
1977 FT_Error result = FT_New_Face(library, (const char*)face_id, 0, aface);
1979 if (!result)
1980 CLog::Log(LOGINFO, "Teletext font {} loaded", (char*)face_id);
1981 else
1982 CLog::Log(LOGERROR, "Opening of Teletext font {} failed", (char*)face_id);
1984 return result;
1987 void CTeletextDecoder::SetFontWidth(int newWidth)
1989 if (m_RenderInfo.FontWidth != newWidth)
1991 m_RenderInfo.FontWidth = newWidth;
1992 m_TypeTTF.width = (FT_UShort) m_RenderInfo.FontWidth;
1994 for (int i = 0; i <= 12; i++)
1995 m_RenderInfo.axdrcs[i] = (m_RenderInfo.FontWidth * i + 6) / 12;
1999 int CTeletextDecoder::GetCurFontWidth()
2001 int mx = (m_RenderInfo.Width)%(40-m_RenderInfo.nofirst); // # of unused pixels
2002 int abx = (mx == 0 ? m_RenderInfo.Width+1 : (m_RenderInfo.Width)/(mx+1)); // distance between 'inserted' pixels
2003 int nx = abx+1-(m_RenderInfo.PosX % (abx+1)); // # of pixels to next insert
2004 return m_RenderInfo.FontWidth+(((m_RenderInfo.PosX+m_RenderInfo.FontWidth+1) <= m_RenderInfo.Width && nx <= m_RenderInfo.FontWidth+1) ? 1 : 0);
2007 void CTeletextDecoder::SetPosX(int column)
2009 m_RenderInfo.PosX = 0;
2011 for (int i = 0; i < column-m_RenderInfo.nofirst; i++)
2012 m_RenderInfo.PosX += GetCurFontWidth();
2015 void CTeletextDecoder::ClearBB(UTILS::COLOR::Color Color)
2017 SDL_memset4(m_TextureBuffer + (m_RenderInfo.Height-m_YOffset)*m_RenderInfo.Width, Color, m_RenderInfo.Width*m_RenderInfo.Height);
2020 void CTeletextDecoder::ClearFB(UTILS::COLOR::Color Color)
2022 SDL_memset4(m_TextureBuffer + m_RenderInfo.Width*m_YOffset, Color, m_RenderInfo.Width*m_RenderInfo.Height);
2025 void CTeletextDecoder::FillBorder(UTILS::COLOR::Color Color)
2027 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);
2028 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);
2031 void CTeletextDecoder::FillRect(
2032 UTILS::COLOR::Color* buffer, int xres, int x, int y, int w, int h, UTILS::COLOR::Color Color)
2034 if (!buffer) return;
2036 UTILS::COLOR::Color* p = buffer + x + y * xres;
2038 if (w > 0)
2040 for ( ; h > 0 ; h--)
2042 SDL_memset4(p, Color, w);
2043 p += xres;
2048 void CTeletextDecoder::DrawVLine(
2049 UTILS::COLOR::Color* lfb, int xres, int x, int y, int l, UTILS::COLOR::Color color)
2051 if (!lfb) return;
2052 UTILS::COLOR::Color* p = lfb + x + y * xres;
2054 for ( ; l > 0 ; l--)
2056 *p = color;
2057 p += xres;
2061 void CTeletextDecoder::DrawHLine(
2062 UTILS::COLOR::Color* lfb, int xres, int x, int y, int l, UTILS::COLOR::Color color)
2064 if (!lfb) return;
2065 if (l > 0)
2066 SDL_memset4(lfb + x + y * xres, color, l);
2069 void CTeletextDecoder::RenderDRCS(
2070 int xres,
2071 unsigned char* s, /* pointer to char data, parity undecoded */
2072 UTILS::COLOR::Color* d, /* pointer to frame buffer of top left pixel */
2073 unsigned char* ax, /* array[0..12] of x-offsets, array[0..10] of y-offsets for each pixel */
2074 UTILS::COLOR::Color fgcolor,
2075 UTILS::COLOR::Color bgcolor)
2077 if (d == NULL) return;
2079 unsigned char *ay = ax + 13; /* array[0..10] of y-offsets for each pixel */
2081 for (int y = 0; y < 10; y++) /* 10*2 bytes a 6 pixels per char definition */
2083 unsigned char c1 = deparity[*s++];
2084 unsigned char c2 = deparity[*s++];
2085 int h = ay[y+1] - ay[y];
2087 if (!h)
2088 continue;
2089 if (((c1 == ' ') && (*(s-2) != ' ')) || ((c2 == ' ') && (*(s-1) != ' '))) /* parity error: stop decoding FIXME */
2090 return;
2091 for (int bit = 0x20, x = 0;
2092 bit;
2093 bit >>= 1, x++) /* bit mask (MSB left), column counter */
2095 UTILS::COLOR::Color f1 = (c1 & bit) ? fgcolor : bgcolor;
2096 UTILS::COLOR::Color f2 = (c2 & bit) ? fgcolor : bgcolor;
2097 for (int i = 0; i < h; i++)
2099 if (ax[x+1] > ax[x])
2100 SDL_memset4(d + ax[x], f1, ax[x+1] - ax[x]);
2101 if (ax[x+7] > ax[x+6])
2102 SDL_memset4(d + ax[x+6], f2, ax[x+7] - ax[x+6]); /* 2nd byte 6 pixels to the right */
2103 d += xres;
2105 d -= h * xres;
2107 d += h * xres;
2111 void CTeletextDecoder::FillRectMosaicSeparated(UTILS::COLOR::Color* lfb,
2112 int xres,
2113 int x,
2114 int y,
2115 int w,
2116 int h,
2117 UTILS::COLOR::Color fgcolor,
2118 UTILS::COLOR::Color bgcolor,
2119 int set)
2121 if (!lfb) return;
2122 FillRect(lfb,xres,x, y, w, h, bgcolor);
2123 if (set)
2125 FillRect(lfb,xres,x+1, y+1, w-2, h-2, fgcolor);
2129 void CTeletextDecoder::FillTrapez(UTILS::COLOR::Color* lfb,
2130 int xres,
2131 int x0,
2132 int y0,
2133 int l0,
2134 int xoffset1,
2135 int h,
2136 int l1,
2137 UTILS::COLOR::Color color)
2139 UTILS::COLOR::Color* p = lfb + x0 + y0 * xres;
2140 int xoffset, l;
2142 for (int yoffset = 0; yoffset < h; yoffset++)
2144 l = l0 + ((l1-l0) * yoffset + h/2) / h;
2145 xoffset = (xoffset1 * yoffset + h/2) / h;
2146 if (l > 0)
2147 SDL_memset4(p + xoffset, color, l);
2148 p += xres;
2152 void CTeletextDecoder::FlipHorz(UTILS::COLOR::Color* lfb, int xres, int x, int y, int w, int h)
2154 UTILS::COLOR::Color buf[2048];
2155 UTILS::COLOR::Color* p = lfb + x + y * xres;
2156 int w1,h1;
2158 for (h1 = 0 ; h1 < h ; h1++)
2160 SDL_memcpy4(buf,p,w);
2161 for (w1 = 0 ; w1 < w ; w1++)
2163 *(p+w1) = buf[w-(w1+1)];
2165 p += xres;
2169 void CTeletextDecoder::FlipVert(UTILS::COLOR::Color* lfb, int xres, int x, int y, int w, int h)
2171 UTILS::COLOR::Color buf[2048];
2172 UTILS::COLOR::Color *p = lfb + x + y * xres, *p1, *p2;
2173 int h1;
2175 for (h1 = 0 ; h1 < h/2 ; h1++)
2177 p1 = (p+(h1*xres));
2178 p2 = (p+(h-(h1+1))*xres);
2179 SDL_memcpy4(buf, p1, w);
2180 SDL_memcpy4(p1, p2, w);
2181 SDL_memcpy4(p2, buf, w);
2185 int CTeletextDecoder::ShapeCoord(int param, int curfontwidth, int curFontHeight)
2187 switch (param)
2189 case S_W13:
2190 return curfontwidth/3;
2191 case S_W12:
2192 return curfontwidth/2;
2193 case S_W23:
2194 return curfontwidth*2/3;
2195 case S_W11:
2196 return curfontwidth;
2197 case S_WM3:
2198 return curfontwidth-3;
2199 case S_H13:
2200 return curFontHeight/3;
2201 case S_H12:
2202 return curFontHeight/2;
2203 case S_H23:
2204 return curFontHeight*2/3;
2205 case S_H11:
2206 return curFontHeight;
2207 default:
2208 return param;
2212 void CTeletextDecoder::DrawShape(UTILS::COLOR::Color* lfb,
2213 int xres,
2214 int x,
2215 int y,
2216 int shapenumber,
2217 int curfontwidth,
2218 int FontHeight,
2219 int curFontHeight,
2220 UTILS::COLOR::Color fgcolor,
2221 UTILS::COLOR::Color bgcolor,
2222 bool clear)
2224 if (!lfb || shapenumber < 0x20 || shapenumber > 0x7e || (shapenumber == 0x7e && clear))
2225 return;
2227 unsigned char *p = aShapes[shapenumber - 0x20];
2229 if (*p == S_INV)
2231 int t = fgcolor;
2232 fgcolor = bgcolor;
2233 bgcolor = t;
2234 p++;
2237 if (clear)
2238 FillRect(lfb, xres, x, y, curfontwidth, FontHeight, bgcolor);
2240 while (*p != S_END)
2242 switch (*p++)
2244 case S_FHL:
2246 int offset = ShapeCoord(*p++, curfontwidth, curFontHeight);
2247 DrawHLine(lfb, xres, x, y + offset, curfontwidth, fgcolor);
2248 break;
2250 case S_FVL:
2252 int offset = ShapeCoord(*p++, curfontwidth, curFontHeight);
2253 DrawVLine(lfb,xres,x + offset, y, FontHeight, fgcolor);
2254 break;
2256 case S_FLH:
2257 FlipHorz(lfb,xres,x,y,curfontwidth, FontHeight);
2258 break;
2259 case S_FLV:
2260 FlipVert(lfb,xres,x,y,curfontwidth, FontHeight);
2261 break;
2262 case S_BOX:
2264 int xo = ShapeCoord(*p++, curfontwidth, curFontHeight);
2265 int yo = ShapeCoord(*p++, curfontwidth, curFontHeight);
2266 int w = ShapeCoord(*p++, curfontwidth, curFontHeight);
2267 int h = ShapeCoord(*p++, curfontwidth, curFontHeight);
2268 FillRect(lfb,xres,x + xo, y + yo, w, h, fgcolor);
2269 break;
2271 case S_TRA:
2273 int x0 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2274 int y0 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2275 int l0 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2276 int x1 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2277 int y1 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2278 int l1 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2279 FillTrapez(lfb, xres,x + x0, y + y0, l0, x1-x0, y1-y0, l1, fgcolor);
2280 break;
2282 case S_BTR:
2284 int x0 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2285 int y0 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2286 int l0 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2287 int x1 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2288 int y1 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2289 int l1 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2290 FillTrapez(lfb, xres, x + x0, y + y0, l0, x1-x0, y1-y0, l1, bgcolor);
2291 break;
2293 case S_LNK:
2295 DrawShape(lfb,xres,x, y, ShapeCoord(*p, curfontwidth, curFontHeight), curfontwidth, FontHeight, curFontHeight, fgcolor, bgcolor, false);
2296 break;
2298 default:
2299 break;
2304 void CTeletextDecoder::RenderCharIntern(TextRenderInfo_t* RenderInfo, int Char, TextPageAttr_t *Attribute, int zoom, int yoffset)
2306 int Row, Pitch;
2307 int glyph;
2308 UTILS::COLOR::Color bgcolor, fgcolor;
2309 int factor, xfactor;
2310 unsigned char *sbitbuffer;
2312 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
2314 int national_subset_local = m_txtCache->NationalSubset;
2315 int curfontwidth = GetCurFontWidth();
2316 int t = curfontwidth;
2317 m_RenderInfo.PosX += t;
2318 int curfontwidth2 = GetCurFontWidth();
2319 m_RenderInfo.PosX -= t;
2320 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);
2321 if (alphachar <= 0) return;
2323 if (zoom && Attribute->doubleh)
2324 factor = 4;
2325 else if (zoom || Attribute->doubleh)
2326 factor = 2;
2327 else
2328 factor = 1;
2330 fgcolor = GetColorRGB((enumTeletextColor)Attribute->fg);
2331 if (m_RenderInfo.TranspMode && m_RenderInfo.PosY < 24*m_RenderInfo.FontHeight)
2333 bgcolor = GetColorRGB(TXT_ColorTransp);
2335 else
2337 bgcolor = GetColorRGB((enumTeletextColor)Attribute->bg);
2340 if (Attribute->doublew)
2342 curfontwidth += curfontwidth2;
2343 xfactor = 2;
2345 else
2346 xfactor = 1;
2348 // Check if the alphanumeric char has diacritical marks (or results from composing chars) or
2349 // on the other hand it is just a simple alphanumeric char
2350 if (!Attribute->diacrit)
2352 Char = alphachar;
2354 else
2356 if ((national_subset_local == NAT_SC) || (national_subset_local == NAT_RB) ||
2357 (national_subset_local == NAT_UA))
2358 Char = G2table[1][0x20 + Attribute->diacrit];
2359 else if (national_subset_local == NAT_GR)
2360 Char = G2table[2][0x20 + Attribute->diacrit];
2361 else if (national_subset_local == NAT_HB)
2362 Char = G2table[3][0x20 + Attribute->diacrit];
2363 else if (national_subset_local == NAT_AR)
2364 Char = G2table[4][0x20 + Attribute->diacrit];
2365 else
2366 Char = G2table[0][0x20 + Attribute->diacrit];
2368 // use harfbuzz to combine the diacritical mark with the alphanumeric char
2369 // fallback to the alphanumeric char if composition fails
2370 hb_unicode_funcs_t* ufuncs = hb_unicode_funcs_get_default();
2371 hb_codepoint_t composedChar;
2372 const hb_bool_t isComposed = hb_unicode_compose(ufuncs, alphachar, Char, &composedChar);
2373 Char = isComposed ? composedChar : alphachar;
2376 /* render char */
2377 if (!(glyph = FT_Get_Char_Index(m_Face, Char)))
2379 CLog::Log(LOGERROR, "{}: <FT_Get_Char_Index for Char {:x} \"{}\" failed", __FUNCTION__,
2380 alphachar, alphachar);
2382 FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY + yoffset, curfontwidth, factor*m_RenderInfo.FontHeight, bgcolor);
2383 m_RenderInfo.PosX += curfontwidth;
2384 return;
2387 if (FTC_SBitCache_Lookup(m_Cache, &m_TypeTTF, glyph, &m_sBit, &m_anode) != 0)
2389 FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY + yoffset, curfontwidth, m_RenderInfo.FontHeight, bgcolor);
2390 m_RenderInfo.PosX += curfontwidth;
2391 return;
2394 sbitbuffer = m_sBit->buffer;
2396 int backupTTFshiftY = m_RenderInfo.TTFShiftY;
2397 if (national_subset_local == NAT_AR)
2398 m_RenderInfo.TTFShiftY = backupTTFshiftY - 2; // for arabic TTF font should be shifted up slightly
2400 UTILS::COLOR::Color* p;
2401 int f; /* running counter for zoom factor */
2402 int he = m_sBit->height; // sbit->height should not be altered, I guess
2403 Row = factor * (m_Ascender - m_sBit->top + m_RenderInfo.TTFShiftY);
2404 if (Row < 0)
2406 sbitbuffer -= m_sBit->pitch*Row;
2407 he += Row;
2408 Row = 0;
2410 else
2412 FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY + yoffset, curfontwidth, Row, bgcolor); /* fill upper margin */
2415 if (m_Ascender - m_sBit->top + m_RenderInfo.TTFShiftY + he > m_RenderInfo.FontHeight)
2416 he = m_RenderInfo.FontHeight - m_Ascender + m_sBit->top - m_RenderInfo.TTFShiftY; /* limit char height to defined/calculated FontHeight */
2417 if (he < 0) he = m_RenderInfo.FontHeight;
2419 p = m_TextureBuffer + m_RenderInfo.PosX + (yoffset + m_RenderInfo.PosY + Row) * m_RenderInfo.Width; /* running pointer into framebuffer */
2420 for (Row = he; Row; Row--) /* row counts up, but down may be a little faster :) */
2422 int pixtodo = m_sBit->width;
2423 UTILS::COLOR::Color* pstart = p;
2425 for (int Bit = xfactor * (m_sBit->left + m_RenderInfo.TTFShiftX); Bit > 0; Bit--) /* fill left margin */
2427 for (f = factor-1; f >= 0; f--)
2428 *(p + f*m_RenderInfo.Width) = bgcolor;
2429 p++;
2432 for (Pitch = m_sBit->pitch; Pitch; Pitch--)
2434 for (int Bit = 0x80; Bit; Bit >>= 1)
2436 UTILS::COLOR::Color color;
2438 if (--pixtodo < 0)
2439 break;
2441 if (*sbitbuffer & Bit) /* bit set -> foreground */
2442 color = fgcolor;
2443 else /* bit not set -> background */
2444 color = bgcolor;
2446 for (f = factor-1; f >= 0; f--)
2447 *(p + f*m_RenderInfo.Width) = color;
2448 p++;
2450 if (xfactor > 1) /* double width */
2452 for (f = factor-1; f >= 0; f--)
2453 *(p + f*m_RenderInfo.Width) = color;
2454 p++;
2457 sbitbuffer++;
2459 for (int Bit = (curfontwidth - xfactor*(m_sBit->width + m_sBit->left + m_RenderInfo.TTFShiftX));
2460 Bit > 0; Bit--) /* fill rest of char width */
2462 for (f = factor-1; f >= 0; f--)
2463 *(p + f*m_RenderInfo.Width) = bgcolor;
2464 p++;
2467 p = pstart + factor*m_RenderInfo.Width;
2470 Row = m_Ascender - m_sBit->top + he + m_RenderInfo.TTFShiftY;
2471 FillRect(m_TextureBuffer,
2472 m_RenderInfo.Width,
2473 m_RenderInfo.PosX,
2474 m_RenderInfo.PosY + yoffset + Row * factor,
2475 curfontwidth,
2476 (m_RenderInfo.FontHeight - Row) * factor,
2477 bgcolor); /* fill lower margin */
2479 if (Attribute->underline)
2480 FillRect(m_TextureBuffer,
2481 m_RenderInfo.Width,
2482 m_RenderInfo.PosX,
2483 m_RenderInfo.PosY + yoffset + (m_RenderInfo.FontHeight-2)* factor,
2484 curfontwidth,
2485 2*factor,
2486 fgcolor); /* underline char */
2488 m_RenderInfo.PosX += curfontwidth;
2489 m_RenderInfo.TTFShiftY = backupTTFshiftY; // restore TTFShiftY
2492 int CTeletextDecoder::RenderChar(
2493 UTILS::COLOR::Color* buffer, // pointer to render buffer, min. FontHeight*2*xres
2494 int xres, // length of 1 line in render buffer
2495 int Char, // character to render
2496 int*
2497 pPosX, // left border for rendering relative to *buffer, will be set to right border after rendering
2498 int PosY, // vertical position of char in *buffer
2499 TextPageAttr_t* Attribute, // Attributes of Char
2500 bool zoom, // 1= character will be rendered in double height
2501 int curfontwidth, // rendering width of character
2502 int curfontwidth2, // rendering width of next character (needed for doublewidth)
2503 int FontHeight, // height of character
2504 bool transpmode, // 1= transparent display
2505 unsigned char* axdrcs, // width and height of DRCS-chars
2506 int Ascender) // Ascender of font
2508 UTILS::COLOR::Color bgcolor, fgcolor;
2509 int factor, xfactor;
2511 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
2513 int national_subset_local = m_txtCache->NationalSubset;
2514 int ymosaic[4];
2515 ymosaic[0] = 0; /* y-offsets for 2*3 mosaic */
2516 ymosaic[1] = (FontHeight + 1) / 3;
2517 ymosaic[2] = (FontHeight * 2 + 1) / 3;
2518 ymosaic[3] = FontHeight;
2520 if (Attribute->setX26)
2522 national_subset_local = 0; // no national subset
2525 // G0+G2 set designation
2526 if (Attribute->setG0G2 != 0x3f)
2528 switch (Attribute->setG0G2)
2530 case 0x20 :
2531 national_subset_local = NAT_SC;
2532 break;
2533 case 0x24 :
2534 national_subset_local = NAT_RB;
2535 break;
2536 case 0x25 :
2537 national_subset_local = NAT_UA;
2538 break;
2539 case 0x37:
2540 national_subset_local = NAT_GR;
2541 break;
2542 case 0x55:
2543 national_subset_local = NAT_HB;
2544 break;
2545 case 0x47:
2546 case 0x57:
2547 national_subset_local = NAT_AR;
2548 break;
2549 default:
2550 national_subset_local = CountryConversionTable[Attribute->setG0G2 & 0x07];
2551 break;
2555 if (Attribute->charset == C_G0S) // use secondary charset
2556 national_subset_local = m_txtCache->NationalSubsetSecondary;
2557 if (zoom && Attribute->doubleh)
2558 factor = 4;
2559 else if (zoom || Attribute->doubleh)
2560 factor = 2;
2561 else
2562 factor = 1;
2564 if (Attribute->doublew)
2566 curfontwidth += curfontwidth2;
2567 xfactor = 2;
2569 else
2570 xfactor = 1;
2572 if (Char == 0xFF) /* skip doubleheight chars in lower line */
2574 *pPosX += curfontwidth;
2575 return -1;
2578 /* get colors */
2579 if (Attribute->inverted)
2581 int t = Attribute->fg;
2582 Attribute->fg = Attribute->bg;
2583 Attribute->bg = t;
2585 fgcolor = GetColorRGB((enumTeletextColor)Attribute->fg);
2586 if (transpmode == true && PosY < 24*FontHeight)
2588 bgcolor = GetColorRGB(TXT_ColorTransp);
2590 else
2592 bgcolor = GetColorRGB((enumTeletextColor)Attribute->bg);
2595 /* handle mosaic */
2596 if ((Attribute->charset == C_G1C || Attribute->charset == C_G1S) &&
2597 ((Char&0xA0) == 0x20))
2599 int w1 = (curfontwidth / 2 ) *xfactor;
2600 int w2 = (curfontwidth - w1) *xfactor;
2602 Char = (Char & 0x1f) | ((Char & 0x40) >> 1);
2603 if (Attribute->charset == C_G1S) /* separated mosaic */
2605 for (int y = 0; y < 3; y++)
2607 FillRectMosaicSeparated(buffer, xres,*pPosX, PosY + ymosaic[y]*factor, w1, (ymosaic[y+1] - ymosaic[y])*factor, fgcolor, bgcolor, Char & 0x01);
2608 FillRectMosaicSeparated(buffer, xres,*pPosX + w1, PosY + ymosaic[y]*factor, w2, (ymosaic[y+1] - ymosaic[y])*factor, fgcolor, bgcolor, Char & 0x02);
2609 Char >>= 2;
2612 else
2614 for (int y = 0; y < 3; y++)
2616 FillRect(buffer, xres, *pPosX, PosY + ymosaic[y]*factor, w1, (ymosaic[y+1] - ymosaic[y])*factor, (Char & 0x01) ? fgcolor : bgcolor);
2617 FillRect(buffer, xres, *pPosX + w1, PosY + ymosaic[y]*factor, w2, (ymosaic[y+1] - ymosaic[y])*factor, (Char & 0x02) ? fgcolor : bgcolor);
2618 Char >>= 2;
2622 *pPosX += curfontwidth;
2623 return 0;
2626 if (Attribute->charset == C_G3)
2628 if (Char < 0x20 || Char > 0x7d)
2630 Char = 0x20;
2632 else
2634 if (*aShapes[Char - 0x20] == S_CHR)
2636 unsigned char *p = aShapes[Char - 0x20];
2637 Char = (*(p+1) <<8) + (*(p+2));
2639 else if (*aShapes[Char - 0x20] == S_ADT)
2641 if (buffer)
2643 int x,y,f,c;
2644 UTILS::COLOR::Color* p = buffer + *pPosX + PosY * xres;
2645 for (y=0; y<FontHeight;y++)
2647 for (f=0; f<factor; f++)
2649 for (x=0; x<curfontwidth*xfactor;x++)
2651 c = (y&4 ? (x/3)&1 :((x+3)/3)&1);
2652 *(p+x) = (c ? fgcolor : bgcolor);
2654 p += xres;
2658 *pPosX += curfontwidth;
2659 return 0;
2661 else
2663 DrawShape(buffer, xres,*pPosX, PosY, Char, curfontwidth, FontHeight, factor*FontHeight, fgcolor, bgcolor, true);
2664 *pPosX += curfontwidth;
2665 return 0;
2669 else if (Attribute->charset >= C_OFFSET_DRCS)
2671 TextCachedPage_t *pcache = m_txtCache->astCachetable[(Attribute->charset & 0x10) ? m_txtCache->drcs : m_txtCache->gdrcs][Attribute->charset & 0x0f];
2672 if (pcache)
2674 unsigned char drcs_data[23*40];
2675 auto& components = CServiceBroker::GetAppComponents();
2676 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
2677 appPlayer->LoadPage((Attribute->charset & 0x10) ? m_txtCache->drcs : m_txtCache->gdrcs,
2678 Attribute->charset & 0x0f, drcs_data);
2679 unsigned char *p;
2680 if (Char < 23*2)
2681 p = drcs_data + 20*Char;
2682 else if (pcache->pageinfo.p24)
2683 p = pcache->pageinfo.p24 + 20*(Char - 23*2);
2684 else
2686 FillRect(buffer, xres,*pPosX, PosY, curfontwidth, factor*FontHeight, bgcolor);
2687 *pPosX += curfontwidth;
2688 return 0;
2690 axdrcs[12] = curfontwidth; /* adjust last x-offset according to position, FIXME: double width */
2691 RenderDRCS(xres, p, buffer + *pPosX + PosY * xres, axdrcs, fgcolor, bgcolor);
2693 else
2695 FillRect(buffer,xres,*pPosX, PosY, curfontwidth, factor*FontHeight, bgcolor);
2697 *pPosX += curfontwidth;
2698 return 0;
2700 else if (Attribute->charset == C_G2 && Char >= 0x20 && Char <= 0x7F)
2702 if ((national_subset_local == NAT_SC) || (national_subset_local == NAT_RB) || (national_subset_local == NAT_UA))
2703 Char = G2table[1][Char-0x20];
2704 else if (national_subset_local == NAT_GR)
2705 Char = G2table[2][Char-0x20];
2706 else if (national_subset_local == NAT_AR)
2707 Char = G2table[3][Char-0x20];
2708 else
2709 Char = G2table[0][Char-0x20];
2711 //if (Char == 0x7F)
2713 // FillRect(buffer,xres,*pPosX, PosY, curfontwidth, factor*Ascender, fgcolor);
2714 // FillRect(buffer,xres,*pPosX, PosY + factor*Ascender, curfontwidth, factor*(FontHeight-Ascender), bgcolor);
2715 // *pPosX += curfontwidth;
2716 // return 0;
2719 else if (national_subset_local == NAT_SC && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for serbian/croatian */
2720 Char = G0table[0][Char-0x20];
2721 else if (national_subset_local == NAT_RB && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for russian/bulgarian */
2722 Char = G0table[1][Char-0x20];
2723 else if (national_subset_local == NAT_UA && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for ukrainian */
2724 Char = G0table[2][Char-0x20];
2725 else if (national_subset_local == NAT_GR && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for greek */
2726 Char = G0table[3][Char-0x20];
2727 else if (national_subset_local == NAT_HB && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for hebrew */
2728 Char = G0table[4][Char-0x20];
2729 else if (national_subset_local == NAT_AR && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for arabic */
2730 Char = G0table[5][Char-0x20];
2731 else
2733 /* load char */
2734 switch (Char)
2736 case 0x00:
2737 case 0x20:
2738 FillRect(buffer, xres, *pPosX, PosY, curfontwidth, factor*FontHeight, bgcolor);
2739 *pPosX += curfontwidth;
2740 return -3;
2741 case 0x23:
2742 case 0x24:
2743 Char = nationaltable23[national_subset_local][Char-0x23];
2744 break;
2745 case 0x40:
2746 Char = nationaltable40[national_subset_local];
2747 break;
2748 case 0x5B:
2749 case 0x5C:
2750 case 0x5D:
2751 case 0x5E:
2752 case 0x5F:
2753 case 0x60:
2754 Char = nationaltable5b[national_subset_local][Char-0x5B];
2755 break;
2756 case 0x7B:
2757 case 0x7C:
2758 case 0x7D:
2759 case 0x7E:
2760 Char = nationaltable7b[national_subset_local][Char-0x7B];
2761 break;
2762 case 0x7F:
2763 FillRect(buffer,xres,*pPosX, PosY , curfontwidth, factor*Ascender, fgcolor);
2764 FillRect(buffer,xres,*pPosX, PosY + factor*Ascender, curfontwidth, factor*(FontHeight-Ascender), bgcolor);
2765 *pPosX += curfontwidth;
2766 return 0;
2767 case 0xE0: /* |- */
2768 DrawHLine(buffer,xres,*pPosX, PosY, curfontwidth, fgcolor);
2769 DrawVLine(buffer,xres,*pPosX, PosY +1, FontHeight -1, fgcolor);
2770 FillRect(buffer,xres,*pPosX +1, PosY +1, curfontwidth-1, FontHeight-1, bgcolor);
2771 *pPosX += curfontwidth;
2772 return 0;
2773 case 0xE1: /* - */
2774 DrawHLine(buffer,xres,*pPosX, PosY, curfontwidth, fgcolor);
2775 FillRect(buffer,xres,*pPosX, PosY +1, curfontwidth, FontHeight-1, bgcolor);
2776 *pPosX += curfontwidth;
2777 return 0;
2778 case 0xE2: /* -| */
2779 DrawHLine(buffer,xres,*pPosX, PosY, curfontwidth, fgcolor);
2780 DrawVLine(buffer,xres,*pPosX + curfontwidth -1, PosY +1, FontHeight -1, fgcolor);
2781 FillRect(buffer,xres,*pPosX, PosY +1, curfontwidth-1, FontHeight-1, bgcolor);
2782 *pPosX += curfontwidth;
2783 return 0;
2784 case 0xE3: /* | */
2785 DrawVLine(buffer,xres,*pPosX, PosY, FontHeight, fgcolor);
2786 FillRect(buffer,xres,*pPosX +1, PosY, curfontwidth -1, FontHeight, bgcolor);
2787 *pPosX += curfontwidth;
2788 return 0;
2789 case 0xE4: /* | */
2790 DrawVLine(buffer,xres,*pPosX + curfontwidth -1, PosY, FontHeight, fgcolor);
2791 FillRect(buffer,xres,*pPosX, PosY, curfontwidth -1, FontHeight, bgcolor);
2792 *pPosX += curfontwidth;
2793 return 0;
2794 case 0xE5: /* |_ */
2795 DrawHLine(buffer,xres,*pPosX, PosY + FontHeight -1, curfontwidth, fgcolor);
2796 DrawVLine(buffer,xres,*pPosX, PosY, FontHeight -1, fgcolor);
2797 FillRect(buffer,xres,*pPosX +1, PosY, curfontwidth-1, FontHeight-1, bgcolor);
2798 *pPosX += curfontwidth;
2799 return 0;
2800 case 0xE6: /* _ */
2801 DrawHLine(buffer,xres,*pPosX, PosY + FontHeight -1, curfontwidth, fgcolor);
2802 FillRect(buffer,xres,*pPosX, PosY, curfontwidth, FontHeight-1, bgcolor);
2803 *pPosX += curfontwidth;
2804 return 0;
2805 case 0xE7: /* _| */
2806 DrawHLine(buffer,xres,*pPosX, PosY + FontHeight -1, curfontwidth, fgcolor);
2807 DrawVLine(buffer,xres,*pPosX + curfontwidth -1, PosY, FontHeight -1, fgcolor);
2808 FillRect(buffer,xres,*pPosX, PosY, curfontwidth-1, FontHeight-1, bgcolor);
2809 *pPosX += curfontwidth;
2810 return 0;
2811 case 0xE8: /* Ii */
2812 FillRect(buffer,xres,*pPosX +1, PosY, curfontwidth -1, FontHeight, bgcolor);
2813 for (int Row=0; Row < curfontwidth/2; Row++)
2814 DrawVLine(buffer,xres,*pPosX + Row, PosY + Row, FontHeight - Row, fgcolor);
2815 *pPosX += curfontwidth;
2816 return 0;
2817 case 0xE9: /* II */
2818 FillRect(buffer,xres,*pPosX, PosY, curfontwidth/2, FontHeight, fgcolor);
2819 FillRect(buffer,xres,*pPosX + curfontwidth/2, PosY, (curfontwidth+1)/2, FontHeight, bgcolor);
2820 *pPosX += curfontwidth;
2821 return 0;
2822 case 0xEA: /* ∞ */
2823 FillRect(buffer,xres,*pPosX, PosY, curfontwidth, FontHeight, bgcolor);
2824 FillRect(buffer,xres,*pPosX, PosY, curfontwidth/2, curfontwidth/2, fgcolor);
2825 *pPosX += curfontwidth;
2826 return 0;
2827 case 0xEB: /* ¨ */
2828 FillRect(buffer,xres,*pPosX, PosY +1, curfontwidth, FontHeight -1, bgcolor);
2829 for (int Row=0; Row < curfontwidth/2; Row++)
2830 DrawHLine(buffer,xres,*pPosX + Row, PosY + Row, curfontwidth - Row, fgcolor);
2831 *pPosX += curfontwidth;
2832 return 0;
2833 case 0xEC: /* -- */
2834 FillRect(buffer, xres,*pPosX, PosY, curfontwidth, curfontwidth/2, fgcolor);
2835 FillRect(buffer, xres,*pPosX, PosY + curfontwidth/2, curfontwidth, FontHeight - curfontwidth/2, bgcolor);
2836 *pPosX += curfontwidth;
2837 return 0;
2838 case 0xED:
2839 case 0xEE:
2840 case 0xEF:
2841 case 0xF0:
2842 case 0xF1:
2843 case 0xF2:
2844 case 0xF3:
2845 case 0xF4:
2846 Char = arrowtable[Char - 0xED];
2847 break;
2848 default:
2849 break;
2852 if (Char <= 0x20)
2854 FillRect(buffer, xres, *pPosX, PosY, curfontwidth, factor*FontHeight, bgcolor);
2855 *pPosX += curfontwidth;
2856 return -2;
2858 return Char; // Char is an alphanumeric unicode character
2861 TextPageinfo_t* CTeletextDecoder::DecodePage(bool showl25, // 1=decode Level2.5-graphics
2862 unsigned char* PageChar, // page buffer, min. 25*40
2863 TextPageAttr_t *PageAtrb, // attribute buffer, min 25*40
2864 bool HintMode, // 1=show hidden information
2865 bool showflof) // 1=decode FLOF-line
2867 int col;
2868 int hold, dhset;
2869 int foreground, background, doubleheight, doublewidth, charset, previous_charset, mosaictype, IgnoreAtBlackBgSubst, concealed, flashmode, boxwin;
2870 unsigned char held_mosaic, *p;
2871 TextCachedPage_t *pCachedPage;
2873 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
2875 /* copy page to decode buffer */
2876 if (m_txtCache->SubPageTable[m_txtCache->Page] == 0xff) /* not cached: do nothing */
2877 return NULL;
2879 if (m_txtCache->ZapSubpageManual)
2880 pCachedPage = m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPage];
2881 else
2882 pCachedPage = m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPageTable[m_txtCache->Page]];
2883 if (!pCachedPage) /* not cached: do nothing */
2884 return nullptr;
2886 auto& components = CServiceBroker::GetAppComponents();
2887 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
2888 appPlayer->LoadPage(m_txtCache->Page, m_txtCache->SubPage, &PageChar[40]);
2890 memcpy(&PageChar[8], pCachedPage->p0, 24); /* header line without TimeString */
2892 TextPageinfo_t* PageInfo = &(pCachedPage->pageinfo);
2893 if (PageInfo->p24)
2894 memcpy(&PageChar[24*40], PageInfo->p24, 40); /* line 25 for FLOF */
2896 /* copy TimeString */
2897 memcpy(&PageChar[32], &m_txtCache->TimeString, 8);
2899 bool boxed;
2900 /* check for newsflash & subtitle */
2901 if (PageInfo->boxed && IsDec(m_txtCache->Page))
2902 boxed = true;
2903 else
2904 boxed = false;
2907 /* modify header */
2908 if (boxed)
2910 memset(PageChar, ' ', 40);
2912 else
2914 memset(PageChar, ' ', 8);
2915 CDVDTeletextTools::Hex2Str((char*)PageChar+3, m_txtCache->Page);
2916 if (m_txtCache->SubPage)
2918 *(PageChar+4) ='/';
2919 *(PageChar+5) ='0';
2920 CDVDTeletextTools::Hex2Str((char*)PageChar+6, m_txtCache->SubPage);
2924 if (!IsDec(m_txtCache->Page))
2926 TextPageAttr_t atr = { TXT_ColorWhite , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f};
2927 if (PageInfo->function == FUNC_MOT) /* magazine organization table */
2929 for (col = 0; col < 24*40; col++)
2930 PageAtrb[col] = atr;
2931 for (col = 40; col < 24*40; col++)
2932 PageChar[col] = number2char(PageChar[col]);
2933 return PageInfo; /* don't interpret irregular pages */
2935 else if (PageInfo->function == FUNC_GPOP || PageInfo->function == FUNC_POP) /* object definitions */
2937 for (int col = 0; col < 24*40; col++)
2938 PageAtrb[col] = atr;
2940 p = PageChar + 40;
2941 for (int row = 1; row < 12; row++)
2943 *p++ = number2char(row); /* first column: number (0-9, A-..) */
2944 for (int col = 1; col < 40; col += 3)
2946 int d = CDVDTeletextTools::deh24(p);
2947 if (d < 0)
2949 memcpy(p, "???", 3);
2950 p += 3;
2952 else
2954 *p++ = number2char((d >> 6) & 0x1f); /* mode */
2955 *p++ = number2char(d & 0x3f); /* address */
2956 *p++ = number2char((d >> 11) & 0x7f); /* data */
2960 return PageInfo; /* don't interpret irregular pages */
2962 else if (PageInfo->function == FUNC_GDRCS || PageInfo->function == FUNC_DRCS) /* character definitions */
2964 return PageInfo; /* don't interpret irregular pages */
2966 else
2968 int h, parityerror = 0;
2970 for (int i = 0; i < 8; i++)
2971 PageAtrb[i] = atr;
2973 /* decode parity/hamming */
2974 for (unsigned int i = 40; i < TELETEXT_PAGE_SIZE; i++)
2976 PageAtrb[i] = atr;
2977 p = PageChar + i;
2978 h = dehamming[*p];
2979 if (parityerror && h != 0xFF) /* if no regular page (after any parity error) */
2980 CDVDTeletextTools::Hex2Str((char*)p, h); /* first try dehamming */
2981 else
2983 if (*p == ' ' || deparity[*p] != ' ') /* correct parity */
2984 *p &= 127;
2985 else
2987 parityerror = 1;
2988 if (h != 0xFF) /* first parity error: try dehamming */
2989 CDVDTeletextTools::Hex2Str((char*)p, h);
2990 else
2991 *p = ' ';
2995 if (parityerror)
2997 return PageInfo; /* don't interpret irregular pages */
3001 int mosaic_pending,esc_pending;
3002 /* decode */
3003 for (int row = 0; row < ((showflof && PageInfo->p24) ? 25 : 24); row++)
3005 /* start-of-row default conditions */
3006 foreground = TXT_ColorWhite;
3007 background = TXT_ColorBlack;
3008 doubleheight = 0;
3009 doublewidth = 0;
3010 charset = previous_charset = C_G0P; // remember charset for switching back after mosaic charset was used
3011 mosaictype = 0;
3012 concealed = 0;
3013 flashmode = 0;
3014 hold = 0;
3015 boxwin = 0;
3016 held_mosaic = ' ';
3017 dhset = 0;
3018 IgnoreAtBlackBgSubst = 0;
3019 mosaic_pending = esc_pending = 0; // we need to render at least one mosaic char if 'esc' is received immediately after mosaic charset switch on
3021 if (boxed && memchr(&PageChar[row*40], start_box, 40) == 0)
3023 foreground = TXT_ColorTransp;
3024 background = TXT_ColorTransp;
3027 for (int col = 0; col < 40; col++)
3029 int index = row*40 + col;
3031 PageAtrb[index].fg = foreground;
3032 PageAtrb[index].bg = background;
3033 PageAtrb[index].charset = charset;
3034 PageAtrb[index].doubleh = doubleheight;
3035 PageAtrb[index].doublew = (col < 39 ? doublewidth : 0);
3036 PageAtrb[index].IgnoreAtBlackBgSubst = IgnoreAtBlackBgSubst;
3037 PageAtrb[index].concealed = concealed;
3038 PageAtrb[index].flashing = flashmode;
3039 PageAtrb[index].boxwin = boxwin;
3040 PageAtrb[index].inverted = 0; // only relevant for Level 2.5
3041 PageAtrb[index].underline = 0; // only relevant for Level 2.5
3042 PageAtrb[index].diacrit = 0; // only relevant for Level 2.5
3043 PageAtrb[index].setX26 = 0; // only relevant for Level 2.5
3044 PageAtrb[index].setG0G2 = 0x3f; // only relevant for Level 2.5
3046 if (PageChar[index] < ' ')
3048 if (esc_pending) { // mosaic char has been rendered and we can switch charsets
3049 charset = previous_charset;
3050 if (charset == C_G0P)
3051 charset = previous_charset = C_G0S;
3052 else if (charset == C_G0S)
3053 charset = previous_charset = C_G0P;
3054 esc_pending = 0;
3056 switch (PageChar[index])
3058 case alpha_black:
3059 case alpha_red:
3060 case alpha_green:
3061 case alpha_yellow:
3062 case alpha_blue:
3063 case alpha_magenta:
3064 case alpha_cyan:
3065 case alpha_white:
3066 concealed = 0;
3067 foreground = PageChar[index] - alpha_black + TXT_ColorBlack;
3068 if (col == 0 && PageChar[index] == alpha_white)
3069 PageAtrb[index].fg = TXT_ColorBlack; // indicate level 1 color change on column 0; (hack)
3070 if ((charset!=C_G0P) && (charset!=C_G0S)) // we need to change charset to state it was before mosaic
3071 charset = previous_charset;
3072 break;
3074 case flash:
3075 flashmode = 1;
3076 break;
3078 case steady:
3079 flashmode = 0;
3080 PageAtrb[index].flashing = 0;
3081 break;
3083 case end_box:
3084 boxwin = 0;
3085 IgnoreAtBlackBgSubst = 0;
3086 break;
3088 case start_box:
3089 if (!boxwin)
3090 boxwin = 1;
3091 break;
3093 case normal_size:
3094 doubleheight = 0;
3095 doublewidth = 0;
3096 PageAtrb[index].doubleh = doubleheight;
3097 PageAtrb[index].doublew = doublewidth;
3098 break;
3100 case double_height:
3101 if (row < 23)
3103 doubleheight = 1;
3104 dhset = 1;
3106 doublewidth = 0;
3108 break;
3110 case double_width:
3111 if (col < 39)
3112 doublewidth = 1;
3113 doubleheight = 0;
3114 break;
3116 case double_size:
3117 if (row < 23)
3119 doubleheight = 1;
3120 dhset = 1;
3122 if (col < 39)
3123 doublewidth = 1;
3124 break;
3126 case mosaic_black:
3127 case mosaic_red:
3128 case mosaic_green:
3129 case mosaic_yellow:
3130 case mosaic_blue:
3131 case mosaic_magenta:
3132 case mosaic_cyan:
3133 case mosaic_white:
3134 concealed = 0;
3135 foreground = PageChar[index] - mosaic_black + TXT_ColorBlack;
3136 if ((charset==C_G0P) || (charset==C_G0S))
3137 previous_charset=charset;
3138 charset = mosaictype ? C_G1S : C_G1C;
3139 mosaic_pending = 1;
3140 break;
3142 case conceal:
3143 PageAtrb[index].concealed = 1;
3144 concealed = 1;
3145 if (!HintMode)
3147 foreground = background;
3148 PageAtrb[index].fg = foreground;
3150 break;
3152 case contiguous_mosaic:
3153 mosaictype = 0;
3154 if (charset == C_G1S)
3156 charset = C_G1C;
3157 PageAtrb[index].charset = charset;
3159 break;
3161 case separated_mosaic:
3162 mosaictype = 1;
3163 if (charset == C_G1C)
3165 charset = C_G1S;
3166 PageAtrb[index].charset = charset;
3168 break;
3170 case esc:
3171 if (!mosaic_pending) { // if mosaic is pending we need to wait before mosaic arrives
3172 if ((charset != C_G0P) && (charset != C_G0S)) // we need to switch to charset which was active before mosaic
3173 charset = previous_charset;
3174 if (charset == C_G0P)
3175 charset = previous_charset = C_G0S;
3176 else if (charset == C_G0S)
3177 charset = previous_charset = C_G0P;
3178 } else esc_pending = 1;
3179 break;
3181 case black_background:
3182 background = TXT_ColorBlack;
3183 IgnoreAtBlackBgSubst = 0;
3184 PageAtrb[index].bg = background;
3185 PageAtrb[index].IgnoreAtBlackBgSubst = IgnoreAtBlackBgSubst;
3186 break;
3188 case new_background:
3189 background = foreground;
3190 if (background == TXT_ColorBlack)
3191 IgnoreAtBlackBgSubst = 1;
3192 else
3193 IgnoreAtBlackBgSubst = 0;
3194 PageAtrb[index].bg = background;
3195 PageAtrb[index].IgnoreAtBlackBgSubst = IgnoreAtBlackBgSubst;
3196 break;
3198 case hold_mosaic:
3199 hold = 1;
3200 break;
3202 case release_mosaic:
3203 hold = 2;
3204 break;
3207 /* handle spacing attributes */
3208 if (hold && (PageAtrb[index].charset == C_G1C || PageAtrb[index].charset == C_G1S))
3209 PageChar[index] = held_mosaic;
3210 else
3211 PageChar[index] = ' ';
3213 if (hold == 2)
3214 hold = 0;
3216 else /* char >= ' ' */
3218 mosaic_pending = 0; // charset will be switched next if esc_pending
3219 /* set new held-mosaic char */
3220 if ((charset == C_G1C || charset == C_G1S) &&
3221 ((PageChar[index]&0xA0) == 0x20))
3222 held_mosaic = PageChar[index];
3223 if (PageAtrb[index].doubleh)
3224 PageChar[index + 40] = 0xFF;
3227 if (!(charset == C_G1C || charset == C_G1S))
3228 held_mosaic = ' '; /* forget if outside mosaic */
3230 } /* for col */
3232 /* skip row if doubleheight */
3233 if (row < 23 && dhset)
3235 for (int col = 0; col < 40; col++)
3237 int index = row*40 + col;
3238 PageAtrb[index+40].bg = PageAtrb[index].bg;
3239 PageAtrb[index+40].fg = TXT_ColorWhite;
3240 if (!PageAtrb[index].doubleh)
3241 PageChar[index+40] = ' ';
3242 PageAtrb[index+40].flashing = 0;
3243 PageAtrb[index+40].charset = C_G0P;
3244 PageAtrb[index+40].doubleh = 0;
3245 PageAtrb[index+40].doublew = 0;
3246 PageAtrb[index+40].IgnoreAtBlackBgSubst = 0;
3247 PageAtrb[index+40].concealed = 0;
3248 PageAtrb[index+40].flashing = 0;
3249 PageAtrb[index+40].boxwin = PageAtrb[index].boxwin;
3251 row++;
3253 } /* for row */
3254 m_txtCache->FullScrColor = TXT_ColorBlack;
3256 if (showl25)
3257 Eval_l25(PageChar, PageAtrb, HintMode);
3259 /* handle Black Background Color Substitution and transparency (CLUT1#0) */
3261 int o = 0;
3262 char bitmask ;
3264 for (unsigned char row : m_txtCache->FullRowColor)
3266 for (int c = 0; c < 40; c++)
3268 bitmask = (PageAtrb[o].bg == 0x08 ? 0x08 : 0x00) | (row == 0x08 ? 0x04 : 0x00) | (PageAtrb[o].boxwin <<1) | (int)boxed;
3269 switch (bitmask)
3271 case 0x08:
3272 case 0x0b:
3273 if (row == 0x08)
3274 PageAtrb[o].bg = m_txtCache->FullScrColor;
3275 else
3276 PageAtrb[o].bg = row;
3277 break;
3278 case 0x01:
3279 case 0x05:
3280 case 0x09:
3281 case 0x0a:
3282 case 0x0c:
3283 case 0x0d:
3284 case 0x0e:
3285 case 0x0f:
3286 PageAtrb[o].bg = TXT_ColorTransp;
3287 break;
3289 bitmask = (PageAtrb[o].fg == 0x08 ? 0x08 : 0x00) | (row == 0x08 ? 0x04 : 0x00) | (PageAtrb[o].boxwin <<1) | (int)boxed;
3290 switch (bitmask)
3292 case 0x08:
3293 case 0x0b:
3294 if (row == 0x08)
3295 PageAtrb[o].fg = m_txtCache->FullScrColor;
3296 else
3297 PageAtrb[o].fg = row;
3298 break;
3299 case 0x01:
3300 case 0x05:
3301 case 0x09:
3302 case 0x0a:
3303 case 0x0c:
3304 case 0x0d:
3305 case 0x0e:
3306 case 0x0f:
3307 PageAtrb[o].fg = TXT_ColorTransp;
3308 break;
3310 o++;
3314 return PageInfo;
3317 void CTeletextDecoder::Eval_l25(unsigned char* PageChar, TextPageAttr_t *PageAtrb, bool HintMode)
3319 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
3321 memset(m_txtCache->FullRowColor, 0, sizeof(m_txtCache->FullRowColor));
3322 m_txtCache->FullScrColor = TXT_ColorBlack;
3323 m_txtCache->ColorTable = NULL;
3325 if (!m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPage])
3326 return;
3328 /* normal page */
3329 if (IsDec(m_txtCache->Page))
3331 unsigned char APx0, APy0, APx, APy;
3332 TextPageinfo_t *pi = &(m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPage]->pageinfo);
3333 TextCachedPage_t *pmot = m_txtCache->astCachetable[(m_txtCache->Page & 0xf00) | 0xfe][0];
3334 int p26Received = 0;
3335 int BlackBgSubst = 0;
3336 int ColorTableRemapping = 0;
3338 m_txtCache->pop = m_txtCache->gpop = m_txtCache->drcs = m_txtCache->gdrcs = 0;
3340 if (pi->ext)
3342 TextExtData_t *e = pi->ext;
3344 if (e->p26[0])
3345 p26Received = 1;
3347 if (e->p27)
3349 Textp27_t *p27 = e->p27;
3350 if (p27[0].l25)
3351 m_txtCache->gpop = p27[0].page;
3352 if (p27[1].l25)
3353 m_txtCache->pop = p27[1].page;
3354 if (p27[2].l25)
3355 m_txtCache->gdrcs = p27[2].page;
3356 if (p27[3].l25)
3357 m_txtCache->drcs = p27[3].page;
3360 if (e->p28Received)
3362 m_txtCache->ColorTable = e->bgr;
3363 BlackBgSubst = e->BlackBgSubst;
3364 ColorTableRemapping = e->ColorTableRemapping;
3365 memset(m_txtCache->FullRowColor, e->DefRowColor, sizeof(m_txtCache->FullRowColor));
3366 m_txtCache->FullScrColor = e->DefScreenColor;
3367 m_txtCache->NationalSubset = SetNational(e->DefaultCharset);
3368 m_txtCache->NationalSubsetSecondary = SetNational(e->SecondCharset);
3369 } /* e->p28Received */
3372 if (!m_txtCache->ColorTable && m_txtCache->astP29[m_txtCache->Page >> 8])
3374 TextExtData_t *e = m_txtCache->astP29[m_txtCache->Page >> 8];
3375 m_txtCache->ColorTable = e->bgr;
3376 BlackBgSubst = e->BlackBgSubst;
3377 ColorTableRemapping = e->ColorTableRemapping;
3378 memset(m_txtCache->FullRowColor, e->DefRowColor, sizeof(m_txtCache->FullRowColor));
3379 m_txtCache->FullScrColor = e->DefScreenColor;
3380 m_txtCache->NationalSubset = SetNational(e->DefaultCharset);
3381 m_txtCache->NationalSubsetSecondary = SetNational(e->SecondCharset);
3384 if (ColorTableRemapping)
3386 for (int i = 0; i < 25*40; i++)
3388 PageAtrb[i].fg += MapTblFG[ColorTableRemapping - 1];
3389 if (!BlackBgSubst || PageAtrb[i].bg != TXT_ColorBlack || PageAtrb[i].IgnoreAtBlackBgSubst)
3390 PageAtrb[i].bg += MapTblBG[ColorTableRemapping - 1];
3394 /* determine ?pop/?drcs from MOT */
3395 if (pmot)
3397 unsigned char pmot_data[23*40];
3398 auto& components = CServiceBroker::GetAppComponents();
3399 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
3400 appPlayer->LoadPage((m_txtCache->Page & 0xf00) | 0xfe, 0, pmot_data);
3402 unsigned char *p = pmot_data; /* start of link data */
3403 int o = 2 * (((m_txtCache->Page & 0xf0) >> 4) * 10 + (m_txtCache->Page & 0x0f)); /* offset of links for current page */
3404 int opop = p[o] & 0x07; /* index of POP link */
3405 int odrcs = p[o+1] & 0x07; /* index of DRCS link */
3406 unsigned char obj[3*4*4]; /* types* objects * (triplet,packet,subp,high) */
3407 unsigned char type,ct, tstart = 4*4;
3408 memset(obj,0,sizeof(obj));
3410 if (p[o] & 0x08) /* GPOP data used */
3412 if (!m_txtCache->gpop || !(p[18*40] & 0x08)) /* no p27 data or higher prio of MOT link */
3414 m_txtCache->gpop = ((p[18*40] << 8) | (p[18*40+1] << 4) | p[18*40+2]) & 0x7ff;
3415 if ((m_txtCache->gpop & 0xff) == 0xff)
3416 m_txtCache->gpop = 0;
3417 else
3419 if (m_txtCache->gpop < 0x100)
3420 m_txtCache->gpop += 0x800;
3421 if (!p26Received)
3423 ct = 2;
3424 while (ct)
3426 ct--;
3427 type = (p[18*40+5] >> 2*ct) & 0x03;
3429 if (type == 0) continue;
3430 obj[(type-1)*(tstart)+ct*4 ] = 3 * ((p[18*40+7+ct*2] >> 1) & 0x03) + type; //triplet
3431 obj[(type-1)*(tstart)+ct*4+1] = ((p[18*40+7+ct*2] & 0x08) >> 3) + 1 ; //packet
3432 obj[(type-1)*(tstart)+ct*4+2] = p[18*40+6+ct*2] & 0x0f ; //subp
3433 obj[(type-1)*(tstart)+ct*4+3] = p[18*40+7+ct*2] & 0x01 ; //high
3439 if (opop) /* POP data used */
3441 opop = 18*40 + 10*opop; /* offset to POP link */
3442 if (!m_txtCache->pop || !(p[opop] & 0x08)) /* no p27 data or higher prio of MOT link */
3444 m_txtCache->pop = ((p[opop] << 8) | (p[opop+1] << 4) | p[opop+2]) & 0x7ff;
3445 if ((m_txtCache->pop & 0xff) == 0xff)
3446 m_txtCache->pop = 0;
3447 else
3449 if (m_txtCache->pop < 0x100)
3450 m_txtCache->pop += 0x800;
3451 if (!p26Received)
3453 ct = 2;
3454 while (ct)
3456 ct--;
3457 type = (p[opop+5] >> 2*ct) & 0x03;
3459 if (type == 0) continue;
3460 obj[(type-1)*(tstart)+(ct+2)*4 ] = 3 * ((p[opop+7+ct*2] >> 1) & 0x03) + type; //triplet
3461 obj[(type-1)*(tstart)+(ct+2)*4+1] = ((p[opop+7+ct*2] & 0x08) >> 3) + 1 ; //packet
3462 obj[(type-1)*(tstart)+(ct+2)*4+2] = p[opop+6+ct*2] ; //subp
3463 obj[(type-1)*(tstart)+(ct+2)*4+3] = p[opop+7+ct*2] & 0x01 ; //high
3469 // eval default objects in correct order
3470 for (int i = 0; i < 12; i++)
3472 if (obj[i*4] != 0)
3474 APx0 = APy0 = APx = APy = m_txtCache->tAPx = m_txtCache->tAPy = 0;
3475 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);
3479 if (p[o+1] & 0x08) /* GDRCS data used */
3481 if (!m_txtCache->gdrcs || !(p[20*40] & 0x08)) /* no p27 data or higher prio of MOT link */
3483 m_txtCache->gdrcs = ((p[20*40] << 8) | (p[20*40+1] << 4) | p[20*40+2]) & 0x7ff;
3484 if ((m_txtCache->gdrcs & 0xff) == 0xff)
3485 m_txtCache->gdrcs = 0;
3486 else if (m_txtCache->gdrcs < 0x100)
3487 m_txtCache->gdrcs += 0x800;
3490 if (odrcs) /* DRCS data used */
3492 odrcs = 20*40 + 4*odrcs; /* offset to DRCS link */
3493 if (!m_txtCache->drcs || !(p[odrcs] & 0x08)) /* no p27 data or higher prio of MOT link */
3495 m_txtCache->drcs = ((p[odrcs] << 8) | (p[odrcs+1] << 4) | p[odrcs+2]) & 0x7ff;
3496 if ((m_txtCache->drcs & 0xff) == 0xff)
3497 m_txtCache->drcs = 0;
3498 else if (m_txtCache->drcs < 0x100)
3499 m_txtCache->drcs += 0x800;
3502 if (m_txtCache->astCachetable[m_txtCache->gpop][0])
3503 m_txtCache->astCachetable[m_txtCache->gpop][0]->pageinfo.function = FUNC_GPOP;
3504 if (m_txtCache->astCachetable[m_txtCache->pop][0])
3505 m_txtCache->astCachetable[m_txtCache->pop][0]->pageinfo.function = FUNC_POP;
3506 if (m_txtCache->astCachetable[m_txtCache->gdrcs][0])
3507 m_txtCache->astCachetable[m_txtCache->gdrcs][0]->pageinfo.function = FUNC_GDRCS;
3508 if (m_txtCache->astCachetable[m_txtCache->drcs][0])
3509 m_txtCache->astCachetable[m_txtCache->drcs][0]->pageinfo.function = FUNC_DRCS;
3510 } /* if mot */
3512 /* evaluate local extension data from p26 */
3513 if (p26Received)
3515 APx0 = APy0 = APx = APy = m_txtCache->tAPx = m_txtCache->tAPy = 0;
3516 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 */
3520 int o = 0;
3521 for (unsigned char row : m_txtCache->FullRowColor)
3523 for (int c = 0; c < 40; c++)
3525 if (BlackBgSubst && PageAtrb[o].bg == TXT_ColorBlack && !(PageAtrb[o].IgnoreAtBlackBgSubst))
3527 if (row == 0x08)
3528 PageAtrb[o].bg = m_txtCache->FullScrColor;
3529 else
3530 PageAtrb[o].bg = row;
3532 o++;
3537 if (!HintMode)
3539 for (int i = 0; i < 25*40; i++)
3541 if (PageAtrb[i].concealed) PageAtrb[i].fg = PageAtrb[i].bg;
3544 } /* is_dec(page) */
3547 /* dump interpreted object data to stdout */
3548 /* in: 18 bit object data */
3549 /* out: termination info, >0 if end of object */
3550 void CTeletextDecoder::Eval_Object(int iONr, TextCachedPage_t *pstCachedPage,
3551 unsigned char *pAPx, unsigned char *pAPy,
3552 unsigned char *pAPx0, unsigned char *pAPy0,
3553 tObjType ObjType, unsigned char* pagedata, unsigned char* PageChar, TextPageAttr_t* PageAtrb)
3555 int iOData;
3556 int iONr1 = iONr + 1; /* don't terminate after first triplet */
3557 unsigned char drcssubp=0, gdrcssubp=0;
3558 signed char endcol = -1; /* last column to which to extend attribute changes */
3559 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 */
3563 iOData = iTripletNumber2Data(iONr, pstCachedPage, pagedata); /* get triplet data, next triplet */
3564 if (iOData < 0) /* invalid number, not cached, or hamming error: terminate */
3565 break;
3567 if (endcol < 0)
3569 if (ObjType == OBJ_ACTIVE)
3571 endcol = 40;
3573 else if (ObjType == OBJ_ADAPTIVE) /* search end of line */
3575 for (int i = iONr; i <= 506; i++)
3577 int iTempOData = iTripletNumber2Data(i, pstCachedPage, pagedata); /* get triplet data, next triplet */
3578 int iAddress = (iTempOData ) & 0x3f;
3579 int iMode = (iTempOData >> 6) & 0x1f;
3580 //int iData = (iTempOData >> 11) & 0x7f;
3581 if (iTempOData < 0 || /* invalid number, not cached, or hamming error: terminate */
3582 (iAddress >= 40 /* new row: row address and */
3583 && (iMode == 0x01 || /* Full Row Color or */
3584 iMode == 0x04 || /* Set Active Position */
3585 (iMode >= 0x15 && iMode <= 0x17) || /* Object Definition */
3586 iMode == 0x17))) /* Object Termination */
3587 break;
3588 if (iAddress < 40 && iMode != 0x06)
3589 endcol = iAddress;
3593 iONr++;
3595 while (0 == Eval_Triplet(iOData, pstCachedPage, pAPx, pAPy, pAPx0, pAPy0, &drcssubp, &gdrcssubp, &endcol, &attrPassive, pagedata, PageChar, PageAtrb) || iONr1 == iONr); /* repeat until termination reached */
3598 void CTeletextDecoder::Eval_NumberedObject(int p, int s, int packet, int triplet, int high,
3599 unsigned char *pAPx, unsigned char *pAPy,
3600 unsigned char *pAPx0, unsigned char *pAPy0, unsigned char* PageChar, TextPageAttr_t* PageAtrb)
3602 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
3604 if (!packet || 0 == m_txtCache->astCachetable[p][s])
3605 return;
3607 unsigned char pagedata[23*40];
3608 auto& components = CServiceBroker::GetAppComponents();
3609 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
3610 appPlayer->LoadPage(p, s, pagedata);
3612 int idata = CDVDTeletextTools::deh24(pagedata + 40*(packet-1) + 1 + 3*triplet);
3613 int iONr;
3615 if (idata < 0) /* hamming error: ignore triplet */
3616 return;
3617 if (high)
3618 iONr = idata >> 9; /* triplet number of odd object data */
3619 else
3620 iONr = idata & 0x1ff; /* triplet number of even object data */
3621 if (iONr <= 506)
3623 Eval_Object(iONr, m_txtCache->astCachetable[p][s], pAPx, pAPy, pAPx0, pAPy0, (tObjType)(triplet % 3),pagedata, PageChar, PageAtrb);
3627 int CTeletextDecoder::Eval_Triplet(int iOData, TextCachedPage_t *pstCachedPage,
3628 unsigned char *pAPx, unsigned char *pAPy,
3629 unsigned char *pAPx0, unsigned char *pAPy0,
3630 unsigned char *drcssubp, unsigned char *gdrcssubp,
3631 signed char *endcol, TextPageAttr_t *attrPassive, unsigned char* pagedata, unsigned char* PageChar, TextPageAttr_t* PageAtrb)
3633 int iAddress = (iOData ) & 0x3f;
3634 int iMode = (iOData >> 6) & 0x1f;
3635 int iData = (iOData >> 11) & 0x7f;
3637 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
3639 if (iAddress < 40) /* column addresses */
3641 int offset; /* offset to PageChar and PageAtrb */
3643 if (iMode != 0x06)
3644 *pAPx = iAddress; /* new Active Column */
3645 offset = (*pAPy0 + *pAPy) * 40 + *pAPx0 + *pAPx; /* offset to PageChar and PageAtrb */
3647 switch (iMode)
3649 case 0x00:
3650 if (0 == (iData>>5))
3652 int newcolor = iData & 0x1f;
3653 if (*endcol < 0) /* passive object */
3654 attrPassive->fg = newcolor;
3655 else if (*endcol == 40) /* active object */
3657 TextPageAttr_t *p = &PageAtrb[offset];
3658 int oldcolor = (p)->fg; /* current color (set-after) */
3659 int c = *pAPx0 + *pAPx; /* current column absolute */
3662 p->fg = newcolor;
3663 p++;
3664 c++;
3665 } while (c < 40 && p->fg == oldcolor); /* stop at change by level 1 page */
3667 else /* adaptive object */
3669 TextPageAttr_t *p = &PageAtrb[offset];
3670 int c = *pAPx; /* current column relative to object origin */
3673 p->fg = newcolor;
3674 p++;
3675 c++;
3676 } while (c <= *endcol);
3679 break;
3680 case 0x01:
3681 if (iData >= 0x20)
3683 PageChar[offset] = iData;
3684 if (*endcol < 0) /* passive object */
3686 attrPassive->charset = C_G1C; /* FIXME: separated? */
3687 PageAtrb[offset] = *attrPassive;
3689 else if (PageAtrb[offset].charset != C_G1S)
3690 PageAtrb[offset].charset = C_G1C; /* FIXME: separated? */
3692 break;
3693 case 0x02:
3694 case 0x0b:
3695 PageChar[offset] = iData;
3696 if (*endcol < 0) /* passive object */
3698 attrPassive->charset = C_G3;
3699 PageAtrb[offset] = *attrPassive;
3701 else
3702 PageAtrb[offset].charset = C_G3;
3703 break;
3704 case 0x03:
3705 if (0 == (iData>>5))
3707 int newcolor = iData & 0x1f;
3708 if (*endcol < 0) /* passive object */
3709 attrPassive->bg = newcolor;
3710 else if (*endcol == 40) /* active object */
3712 TextPageAttr_t *p = &PageAtrb[offset];
3713 int oldcolor = (p)->bg; /* current color (set-after) */
3714 int c = *pAPx0 + *pAPx; /* current column absolute */
3717 p->bg = newcolor;
3718 if (newcolor == TXT_ColorBlack)
3719 p->IgnoreAtBlackBgSubst = 1;
3720 p++;
3721 c++;
3722 } while (c < 40 && p->bg == oldcolor); /* stop at change by level 1 page */
3724 else /* adaptive object */
3726 TextPageAttr_t *p = &PageAtrb[offset];
3727 int c = *pAPx; /* current column relative to object origin */
3730 p->bg = newcolor;
3731 if (newcolor == TXT_ColorBlack)
3732 p->IgnoreAtBlackBgSubst = 1;
3733 p++;
3734 c++;
3735 } while (c <= *endcol);
3738 break;
3739 case 0x06:
3740 /* ignore */
3741 break;
3742 case 0x07:
3743 if ((iData & 0x60) != 0) break; // reserved data field
3744 if (*endcol < 0) /* passive object */
3746 attrPassive->flashing=iData & 0x1f;
3747 PageAtrb[offset] = *attrPassive;
3749 else
3750 PageAtrb[offset].flashing=iData & 0x1f;
3751 break;
3752 case 0x08:
3753 if (*endcol < 0) /* passive object */
3755 attrPassive->setG0G2=iData & 0x3f;
3756 PageAtrb[offset] = *attrPassive;
3758 else
3759 PageAtrb[offset].setG0G2=iData & 0x3f;
3760 break;
3761 case 0x09:
3762 PageChar[offset] = iData;
3763 if (*endcol < 0) /* passive object */
3765 attrPassive->charset = C_G0P; /* FIXME: secondary? */
3766 attrPassive->setX26 = 1;
3767 PageAtrb[offset] = *attrPassive;
3769 else
3771 PageAtrb[offset].charset = C_G0P; /* FIXME: secondary? */
3772 PageAtrb[offset].setX26 = 1;
3774 break;
3775 // case 0x0b: (see 0x02)
3776 case 0x0c:
3778 int conc = (iData & 0x04);
3779 int inv = (iData & 0x10);
3780 int dw = (iData & 0x40 ?1:0);
3781 int dh = (iData & 0x01 ?1:0);
3782 int sep = (iData & 0x20);
3783 int bw = (iData & 0x02 ?1:0);
3784 if (*endcol < 0) /* passive object */
3786 if (conc)
3788 attrPassive->concealed = 1;
3789 attrPassive->fg = attrPassive->bg;
3791 attrPassive->inverted = (inv ? 1- attrPassive->inverted : 0);
3792 attrPassive->doubleh = dh;
3793 attrPassive->doublew = dw;
3794 attrPassive->boxwin = bw;
3795 if (bw) attrPassive->IgnoreAtBlackBgSubst = 0;
3796 if (sep)
3798 if (attrPassive->charset == C_G1C)
3799 attrPassive->charset = C_G1S;
3800 else
3801 attrPassive->underline = 1;
3803 else
3805 if (attrPassive->charset == C_G1S)
3806 attrPassive->charset = C_G1C;
3807 else
3808 attrPassive->underline = 0;
3811 else
3814 int c = *pAPx0 + (*endcol == 40 ? *pAPx : 0); /* current column */
3815 int c1 = offset;
3816 TextPageAttr_t *p = &PageAtrb[offset];
3819 p->inverted = (inv ? 1- p->inverted : 0);
3820 if (conc)
3822 p->concealed = 1;
3823 p->fg = p->bg;
3825 if (sep)
3827 if (p->charset == C_G1C)
3828 p->charset = C_G1S;
3829 else
3830 p->underline = 1;
3832 else
3834 if (p->charset == C_G1S)
3835 p->charset = C_G1C;
3836 else
3837 p->underline = 0;
3839 p->doublew = dw;
3840 p->doubleh = dh;
3841 p->boxwin = bw;
3842 if (bw) p->IgnoreAtBlackBgSubst = 0;
3843 p++;
3844 c++;
3845 c1++;
3846 } while (c < *endcol);
3848 break;
3850 case 0x0d:
3851 PageChar[offset] = iData & 0x3f;
3852 if (*endcol < 0) /* passive object */
3854 attrPassive->charset = C_OFFSET_DRCS + ((iData & 0x40) ? (0x10 + *drcssubp) : *gdrcssubp);
3855 PageAtrb[offset] = *attrPassive;
3857 else
3858 PageAtrb[offset].charset = C_OFFSET_DRCS + ((iData & 0x40) ? (0x10 + *drcssubp) : *gdrcssubp);
3859 break;
3860 case 0x0f:
3861 PageChar[offset] = iData;
3862 if (*endcol < 0) /* passive object */
3864 attrPassive->charset = C_G2;
3865 PageAtrb[offset] = *attrPassive;
3867 else
3868 PageAtrb[offset].charset = C_G2;
3869 break;
3870 default:
3871 if (iMode == 0x10 && iData == 0x2a)
3872 iData = '@';
3873 if (iMode >= 0x10)
3875 PageChar[offset] = iData;
3876 if (*endcol < 0) /* passive object */
3878 attrPassive->charset = C_G0P;
3879 attrPassive->diacrit = iMode & 0x0f;
3880 attrPassive->setX26 = 1;
3881 PageAtrb[offset] = *attrPassive;
3883 else
3885 PageAtrb[offset].charset = C_G0P;
3886 PageAtrb[offset].diacrit = iMode & 0x0f;
3887 PageAtrb[offset].setX26 = 1;
3890 break; /* unsupported or not yet implemented mode: ignore */
3891 } /* switch (iMode) */
3893 else /* ================= (iAddress >= 40): row addresses ====================== */
3895 switch (iMode)
3897 case 0x00:
3898 if (0 == (iData>>5))
3900 m_txtCache->FullScrColor = iData & 0x1f;
3902 break;
3903 case 0x01:
3904 if (*endcol == 40) /* active object */
3906 *pAPy = RowAddress2Row(iAddress); /* new Active Row */
3908 int color = iData & 0x1f;
3909 int row = *pAPy0 + *pAPy;
3910 int maxrow;
3912 if (row <= 24 && 0 == (iData>>5))
3913 maxrow = row;
3914 else if (3 == (iData>>5))
3915 maxrow = 24;
3916 else
3917 maxrow = -1;
3918 for (; row <= maxrow; row++)
3919 m_txtCache->FullRowColor[row] = color;
3920 *endcol = -1;
3922 break;
3923 case 0x04:
3924 *pAPy = RowAddress2Row(iAddress); /* new Active Row */
3925 if (iData < 40)
3926 *pAPx = iData; /* new Active Column */
3927 *endcol = -1; /* FIXME: check if row changed? */
3928 break;
3929 case 0x07:
3930 if (iAddress == 0x3f)
3932 *pAPx = *pAPy = 0; /* new Active Position 0,0 */
3933 if (*endcol == 40) /* active object */
3935 int color = iData & 0x1f;
3936 int row = *pAPy0; // + *pAPy;
3937 int maxrow;
3939 if (row <= 24 && 0 == (iData>>5))
3940 maxrow = row;
3941 else if (3 == (iData>>5))
3942 maxrow = 24;
3943 else
3944 maxrow = -1;
3945 for (; row <= maxrow; row++)
3946 m_txtCache->FullRowColor[row] = color;
3948 *endcol = -1;
3950 break;
3951 case 0x08:
3952 case 0x09:
3953 case 0x0a:
3954 case 0x0b:
3955 case 0x0c:
3956 case 0x0d:
3957 case 0x0e:
3958 case 0x0f:
3959 /* ignore */
3960 break;
3961 case 0x10:
3962 m_txtCache->tAPy = iAddress - 40;
3963 m_txtCache->tAPx = iData;
3964 break;
3965 case 0x11:
3966 case 0x12:
3967 case 0x13:
3968 if (iAddress & 0x10) /* POP or GPOP */
3970 unsigned char APx = 0, APy = 0;
3971 unsigned char APx0 = *pAPx0 + *pAPx + m_txtCache->tAPx, APy0 = *pAPy0 + *pAPy + m_txtCache->tAPy;
3972 int triplet = 3 * ((iData >> 5) & 0x03) + (iMode & 0x03);
3973 int packet = (iAddress & 0x03) + 1;
3974 int subp = iData & 0x0f;
3975 int high = (iData >> 4) & 0x01;
3978 if (APx0 < 40) /* not in side panel */
3980 Eval_NumberedObject((iAddress & 0x08) ? m_txtCache->gpop : m_txtCache->pop, subp, packet, triplet, high, &APx, &APy, &APx0, &APy0, PageChar,PageAtrb);
3983 else if (iAddress & 0x08) /* local: eval invoked object */
3985 unsigned char APx = 0, APy = 0;
3986 unsigned char APx0 = *pAPx0 + *pAPx + m_txtCache->tAPx, APy0 = *pAPy0 + *pAPy + m_txtCache->tAPy;
3987 int descode = ((iAddress & 0x01) << 3) | (iData >> 4);
3988 int triplet = iData & 0x0f;
3990 if (APx0 < 40) /* not in side panel */
3992 Eval_Object(13 * 23 + 13 * descode + triplet, pstCachedPage, &APx, &APy, &APx0, &APy0, (tObjType)(triplet % 3), pagedata, PageChar, PageAtrb);
3995 break;
3996 case 0x15:
3997 case 0x16:
3998 case 0x17:
3999 if (0 == (iAddress & 0x08)) /* Object Definition illegal or only level 3.5 */
4000 break; /* ignore */
4002 m_txtCache->tAPx = m_txtCache->tAPy = 0;
4003 *endcol = -1;
4004 return 0xFF; /* termination by object definition */
4005 break;
4006 case 0x18:
4007 if (0 == (iData & 0x10)) /* DRCS Mode reserved or only level 3.5 */
4008 break; /* ignore */
4010 if (iData & 0x40)
4011 *drcssubp = iData & 0x0f;
4012 else
4013 *gdrcssubp = iData & 0x0f;
4014 break;
4015 case 0x1f:
4016 m_txtCache->tAPx = m_txtCache->tAPy = 0;
4017 *endcol = -1;
4018 return 0x80 | iData; /* explicit termination */
4019 break;
4020 default:
4021 break; /* unsupported or not yet implemented mode: ignore */
4022 } /* switch (iMode) */
4023 } /* (iAddress >= 40): row addresses */
4025 if (iAddress < 40 || iMode != 0x10) /* leave temp. AP-Offset unchanged only immediately after definition */
4026 m_txtCache->tAPx = m_txtCache->tAPy = 0;
4028 return 0; /* normal exit, no termination */
4031 /* get object data */
4032 /* in: absolute triplet number (0..506, start at packet 3 byte 1) */
4033 /* in: pointer to cache struct of page data */
4034 /* out: 18 bit triplet data, <0 if invalid number, not cached, or hamming error */
4035 int CTeletextDecoder::iTripletNumber2Data(int iONr, TextCachedPage_t *pstCachedPage, unsigned char* pagedata)
4037 if (iONr > 506 || 0 == pstCachedPage)
4038 return -1;
4040 unsigned char *p;
4041 int packet = (iONr / 13) + 3;
4042 int packetoffset = 3 * (iONr % 13);
4044 if (packet <= 23)
4045 p = pagedata + 40*(packet-1) + packetoffset + 1;
4046 else if (packet <= 25)
4048 if (0 == pstCachedPage->pageinfo.p24)
4049 return -1;
4050 p = pstCachedPage->pageinfo.p24 + 40*(packet-24) + packetoffset + 1;
4052 else
4054 int descode = packet - 26;
4055 if (0 == pstCachedPage->pageinfo.ext)
4056 return -1;
4057 if (0 == pstCachedPage->pageinfo.ext->p26[descode])
4058 return -1;
4059 p = pstCachedPage->pageinfo.ext->p26[descode] + packetoffset; /* first byte (=designation code) is not cached */
4061 return CDVDTeletextTools::deh24(p);
4064 int CTeletextDecoder::SetNational(unsigned char sec)
4066 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
4068 switch (sec)
4070 case 0x08:
4071 return NAT_PL; //polish
4072 case 0x16:
4073 case 0x36:
4074 return NAT_TR; //turkish
4075 case 0x1d:
4076 return NAT_SR; //serbian, croatian, slovenian
4077 case 0x20:
4078 return NAT_SC; // serbian, croatian
4079 case 0x24:
4080 return NAT_RB; // russian, bulgarian
4081 case 0x25:
4082 return NAT_UA; // ukrainian
4083 case 0x22:
4084 return NAT_ET; // estonian
4085 case 0x23:
4086 return NAT_LV; // latvian, lithuanian
4087 case 0x37:
4088 return NAT_GR; // greek
4089 case 0x55:
4090 return NAT_HB; // hebrew
4091 case 0x47:
4092 case 0x57:
4093 return NAT_AR; // arabic
4095 return CountryConversionTable[sec & 0x07];
4098 int CTeletextDecoder::NextHex(int i) /* return next existing non-decimal page number */
4100 int startpage = i;
4101 if (startpage < 0x100)
4102 startpage = 0x100;
4106 i++;
4107 if (i > 0x8FF)
4108 i = 0x100;
4109 if (i == startpage)
4110 break;
4111 } while ((m_txtCache->SubPageTable[i] == 0xFF) || IsDec(i));
4112 return i;
4115 void CTeletextDecoder::SetColors(const unsigned short *pcolormap, int offset, int number)
4117 int j = offset; /* index in global color table */
4119 for (int i = 0; i < number; i++)
4121 int r = ((pcolormap[i] >> 8) & 0xf) << 4;
4122 int g = ((pcolormap[i] >> 4) & 0xf) << 4;
4123 int b = ((pcolormap[i]) & 0xf) << 4;
4125 if (m_RenderInfo.rd0[j] != r)
4127 m_RenderInfo.rd0[j] = r;
4129 if (m_RenderInfo.gn0[j] != g)
4131 m_RenderInfo.gn0[j] = g;
4133 if (m_RenderInfo.bl0[j] != b)
4135 m_RenderInfo.bl0[j] = b;
4137 j++;
4141 UTILS::COLOR::Color CTeletextDecoder::GetColorRGB(enumTeletextColor ttc)
4143 switch (ttc)
4145 case TXT_ColorBlack: return 0xFF000000;
4146 case TXT_ColorRed: return 0xFFFC1414;
4147 case TXT_ColorGreen: return 0xFF24FC24;
4148 case TXT_ColorYellow: return 0xFFFCC024;
4149 case TXT_ColorBlue: return 0xFF0000FC;
4150 case TXT_ColorMagenta: return 0xFFB000FC;
4151 case TXT_ColorCyan: return 0xFF00FCFC;
4152 case TXT_ColorWhite: return 0xFFFCFCFC;
4153 case TXT_ColorTransp: return 0x00000000;
4154 default: break;
4157 /* Get colors for CLUTs 2+3 */
4158 int index = (int)ttc;
4159 UTILS::COLOR::Color color = (m_RenderInfo.tr0[index] << 24) | (m_RenderInfo.bl0[index] << 16) |
4160 (m_RenderInfo.gn0[index] << 8) | m_RenderInfo.rd0[index];
4161 return color;