[video] Ask the user for confirmation when turning a version into an extra or vice...
[xbmc.git] / xbmc / video / Teletext.cpp
blobaa64ec1c9662ce92a2266fcff436b18760bc3bb1
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::Changed()
452 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
453 if (IsSubtitlePage(m_txtCache->Page))
455 m_updateTexture = true;
456 return true;
459 /* Update on every changed second */
460 if (m_txtCache->TimeString[7] != prevTimeSec)
462 prevTimeSec = m_txtCache->TimeString[7];
463 m_updateTexture = true;
464 return true;
466 return false;
469 bool CTeletextDecoder::HandleAction(const CAction &action)
471 if (m_txtCache == NULL)
473 CLog::Log(LOGERROR, "CTeletextDecoder::HandleAction called without teletext cache");
474 return false;
477 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
479 if (action.GetID() == ACTION_MOVE_UP)
481 if (m_RenderInfo.PageCatching)
482 CatchNextPage(-1, -1);
483 else
484 GetNextPageOne(true);
485 return true;
487 else if (action.GetID() == ACTION_MOVE_DOWN)
489 if (m_RenderInfo.PageCatching)
490 CatchNextPage(1, 1);
491 else
492 GetNextPageOne(false);
493 return true;
495 else if (action.GetID() == ACTION_MOVE_RIGHT)
497 if (m_RenderInfo.PageCatching)
498 CatchNextPage(0, 1);
499 else if (m_RenderInfo.Boxed)
501 m_RenderInfo.SubtitleDelay++;
502 // display SubtitleDelay
503 m_RenderInfo.PosY = 0;
504 char ns[10];
505 SetPosX(1);
506 snprintf(ns, sizeof(ns), "+%d ", m_RenderInfo.SubtitleDelay);
507 RenderCharFB(ns[0], &Text_AtrTable[ATR_WB]);
508 RenderCharFB(ns[1], &Text_AtrTable[ATR_WB]);
509 RenderCharFB(ns[2], &Text_AtrTable[ATR_WB]);
510 RenderCharFB(ns[4], &Text_AtrTable[ATR_WB]);
512 else
514 GetNextSubPage(1);
516 return true;
518 else if (action.GetID() == ACTION_MOVE_LEFT)
520 if (m_RenderInfo.PageCatching)
521 CatchNextPage(0, -1);
522 else if (m_RenderInfo.Boxed)
524 m_RenderInfo.SubtitleDelay--;
526 // display subtitledelay
527 m_RenderInfo.PosY = 0;
528 char ns[10];
529 SetPosX(1);
530 snprintf(ns, sizeof(ns), "+%d ", m_RenderInfo.SubtitleDelay);
531 RenderCharFB(ns[0], &Text_AtrTable[ATR_WB]);
532 RenderCharFB(ns[1], &Text_AtrTable[ATR_WB]);
533 RenderCharFB(ns[2], &Text_AtrTable[ATR_WB]);
534 RenderCharFB(ns[4], &Text_AtrTable[ATR_WB]);
536 else
538 GetNextSubPage(-1);
540 return true;
542 else if (action.GetID() >= REMOTE_0 && action.GetID() <= REMOTE_9)
544 PageInput(action.GetID() - REMOTE_0);
545 return true;
547 else if (action.GetID() == KEY_UNICODE)
548 { // input from the keyboard
549 if (action.GetUnicode() >= 48 && action.GetUnicode() < 58)
551 PageInput(action.GetUnicode() - 48);
552 return true;
554 return false;
556 else if (action.GetID() == ACTION_PAGE_UP)
558 SwitchZoomMode();
559 return true;
561 else if (action.GetID() == ACTION_PAGE_DOWN)
563 SwitchTranspMode();
564 return true;
566 else if (action.GetID() == ACTION_SELECT_ITEM)
568 if (m_txtCache->SubPageTable[m_txtCache->Page] == 0xFF)
569 return false;
571 if (!m_RenderInfo.PageCatching)
572 StartPageCatching();
573 else
574 StopPageCatching();
576 return true;
579 if (m_RenderInfo.PageCatching)
581 m_txtCache->PageUpdate = true;
582 m_RenderInfo.PageCatching = false;
583 return true;
586 if (action.GetID() == ACTION_SHOW_INFO)
588 SwitchHintMode();
589 return true;
591 else if (action.GetID() == ACTION_TELETEXT_RED)
593 ColorKey(m_RenderInfo.Prev_100);
594 return true;
596 else if (action.GetID() == ACTION_TELETEXT_GREEN)
598 ColorKey(m_RenderInfo.Prev_10);
599 return true;
601 else if (action.GetID() == ACTION_TELETEXT_YELLOW)
603 ColorKey(m_RenderInfo.Next_10);
604 return true;
606 else if (action.GetID() == ACTION_TELETEXT_BLUE)
608 ColorKey(m_RenderInfo.Next_100);
609 return true;
612 return false;
615 bool CTeletextDecoder::InitDecoder()
617 int error;
619 auto& components = CServiceBroker::GetAppComponents();
620 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
621 m_txtCache = appPlayer->GetTeletextCache();
622 if (m_txtCache == nullptr)
624 CLog::Log(LOGERROR, "{}: called without teletext cache", __FUNCTION__);
625 return false;
628 /* init fontlibrary */
629 if ((error = FT_Init_FreeType(&m_Library)))
631 CLog::Log(LOGERROR, "{}: <FT_Init_FreeType: {:#2X}>", __FUNCTION__, error);
632 m_Library = NULL;
633 return false;
636 if ((error = FTC_Manager_New(m_Library, 7, 2, 0, &MyFaceRequester, NULL, &m_Manager)))
638 FT_Done_FreeType(m_Library);
639 m_Library = NULL;
640 m_Manager = NULL;
641 CLog::Log(LOGERROR, "{}: <FTC_Manager_New: {:#2X}>", __FUNCTION__, error);
642 return false;
645 if ((error = FTC_SBitCache_New(m_Manager, &m_Cache)))
647 FTC_Manager_Done(m_Manager);
648 FT_Done_FreeType(m_Library);
649 m_Manager = NULL;
650 m_Library = NULL;
651 CLog::Log(LOGERROR, "{}: <FTC_SBit_Cache_New: {:#2X}>", __FUNCTION__, error);
652 return false;
655 /* calculate font dimensions */
656 m_RenderInfo.Width = (int)(CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth()*CServiceBroker::GetWinSystem()->GetGfxContext().GetGUIScaleX());
657 m_RenderInfo.Height = (int)(CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight()*CServiceBroker::GetWinSystem()->GetGfxContext().GetGUIScaleY());
658 m_RenderInfo.FontHeight = m_RenderInfo.Height / 25;
659 m_RenderInfo.FontWidth_Normal = m_RenderInfo.Width / (m_RenderInfo.Show39 ? 39 : 40);
660 SetFontWidth(m_RenderInfo.FontWidth_Normal);
661 for (int i = 0; i <= 10; i++)
662 m_RenderInfo.axdrcs[i+12+1] = (m_RenderInfo.FontHeight * i + 6) / 10;
664 /* center screen */
665 m_TypeTTF.face_id = (FTC_FaceID) const_cast<char*>(m_teletextFont.c_str());
666 m_TypeTTF.height = (FT_UShort) m_RenderInfo.FontHeight;
667 m_TypeTTF.flags = FT_LOAD_MONOCHROME;
668 if (FTC_Manager_LookupFace(m_Manager, m_TypeTTF.face_id, &m_Face))
670 m_TypeTTF.face_id = (FTC_FaceID) const_cast<char*>(m_teletextFont.c_str());
671 if ((error = FTC_Manager_LookupFace(m_Manager, m_TypeTTF.face_id, &m_Face)))
673 CLog::Log(LOGERROR, "{}: <FTC_Manager_Lookup_Face failed with Errorcode {:#2X}>",
674 __FUNCTION__, error);
675 FTC_Manager_Done(m_Manager);
676 FT_Done_FreeType(m_Library);
677 m_Manager = NULL;
678 m_Library = NULL;
679 return false;
682 m_Ascender = m_RenderInfo.FontHeight * m_Face->ascender / m_Face->units_per_EM;
684 /* set variable screeninfo for double buffering */
685 m_YOffset = 0;
686 m_TextureBuffer = new UTILS::COLOR::Color[4 * m_RenderInfo.Height * m_RenderInfo.Width];
688 ClearFB(GetColorRGB(TXT_ColorTransp));
689 ClearBB(GetColorRGB(TXT_ColorTransp)); /* initialize backbuffer */
690 /* set new colormap */
691 SetColors(DefaultColors, 0, TXT_Color_SIZECOLTABLE);
693 for (int i = 0; i < 40 * 25; i++)
695 m_RenderInfo.PageChar[i] = ' ';
696 m_RenderInfo.PageAtrb[i].fg = TXT_ColorTransp;
697 m_RenderInfo.PageAtrb[i].bg = TXT_ColorTransp;
698 m_RenderInfo.PageAtrb[i].charset = C_G0P;
699 m_RenderInfo.PageAtrb[i].doubleh = 0;
700 m_RenderInfo.PageAtrb[i].doublew = 0;
701 m_RenderInfo.PageAtrb[i].IgnoreAtBlackBgSubst = 0;
704 m_RenderInfo.TranspMode = false;
705 m_LastPage = 0x100;
707 return true;
710 void CTeletextDecoder::EndDecoder()
712 /* clear SubtitleCache */
713 for (TextSubtitleCache_t*& subtitleCache : m_RenderInfo.SubtitleCache)
715 if (subtitleCache != NULL)
717 delete subtitleCache;
718 subtitleCache = NULL;
722 if (m_TextureBuffer)
724 delete[] m_TextureBuffer;
725 m_TextureBuffer = NULL;
728 /* close freetype */
729 if (m_Manager)
731 FTC_Node_Unref(m_anode, m_Manager);
732 FTC_Manager_Done(m_Manager);
734 if (m_Library)
736 FT_Done_FreeType(m_Library);
739 m_Manager = NULL;
740 m_Library = NULL;
742 if (!m_txtCache)
744 CLog::Log(LOGINFO, "{}: called without cache", __FUNCTION__);
746 else
748 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
749 m_txtCache->PageUpdate = true;
750 CLog::Log(LOGDEBUG, "Teletext: Rendering ended");
754 void CTeletextDecoder::PageInput(int Number)
756 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
758 m_updateTexture = true;
760 /* clear m_TempPage */
761 if (m_RenderInfo.InputCounter == 2)
762 m_TempPage = 0;
764 /* check for 0 & 9 on first position */
765 if (Number == 0 && m_RenderInfo.InputCounter == 2)
767 /* set page */
768 m_TempPage = m_LastPage; /* 0 toggles to last page as in program switching */
769 m_RenderInfo.InputCounter = -1;
771 else if (Number == 9 && m_RenderInfo.InputCounter == 2)
773 return;
776 /* show pageinput */
777 if (m_RenderInfo.ZoomMode == 2)
779 m_RenderInfo.ZoomMode = 1;
780 CopyBB2FB();
783 m_RenderInfo.PosY = 0;
785 switch (m_RenderInfo.InputCounter)
787 case 2:
788 SetPosX(1);
789 RenderCharFB(Number | '0', &Text_AtrTable[ATR_WB]);
790 RenderCharFB('-', &Text_AtrTable[ATR_WB]);
791 RenderCharFB('-', &Text_AtrTable[ATR_WB]);
792 break;
794 case 1:
795 SetPosX(2);
796 RenderCharFB(Number | '0', &Text_AtrTable[ATR_WB]);
797 break;
799 case 0:
800 SetPosX(3);
801 RenderCharFB(Number | '0', &Text_AtrTable[ATR_WB]);
802 break;
805 /* generate pagenumber */
806 m_TempPage |= Number << (m_RenderInfo.InputCounter*4);
808 m_RenderInfo.InputCounter--;
810 if (m_RenderInfo.InputCounter < 0)
812 /* disable SubPage zapping */
813 m_txtCache->ZapSubpageManual = false;
815 /* reset input */
816 m_RenderInfo.InputCounter = 2;
818 /* set new page */
819 m_LastPage = m_txtCache->Page;
821 m_txtCache->Page = m_TempPage;
822 m_RenderInfo.HintMode = false;
824 /* check cache */
825 int subp = m_txtCache->SubPageTable[m_txtCache->Page];
826 if (subp != 0xFF)
828 m_txtCache->SubPage = subp;
829 m_txtCache->PageUpdate = true;
831 else
833 m_txtCache->SubPage = 0;
834 // RenderMessage(PageNotFound);
839 void CTeletextDecoder::GetNextPageOne(bool up)
841 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
843 /* disable subpage zapping */
844 m_txtCache->ZapSubpageManual = false;
846 /* abort pageinput */
847 m_RenderInfo.InputCounter = 2;
849 /* find next cached page */
850 m_LastPage = m_txtCache->Page;
852 int subp;
853 do {
854 if (up)
855 CDVDTeletextTools::NextDec(&m_txtCache->Page);
856 else
857 CDVDTeletextTools::PrevDec(&m_txtCache->Page);
858 subp = m_txtCache->SubPageTable[m_txtCache->Page];
859 } while (subp == 0xFF && m_txtCache->Page != m_LastPage);
861 /* update Page */
862 if (m_txtCache->Page != m_LastPage)
864 if (m_RenderInfo.ZoomMode == 2)
865 m_RenderInfo.ZoomMode = 1;
867 m_txtCache->SubPage = subp;
868 m_RenderInfo.HintMode = false;
869 m_txtCache->PageUpdate = true;
873 void CTeletextDecoder::GetNextSubPage(int offset)
875 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
877 /* abort pageinput */
878 m_RenderInfo.InputCounter = 2;
880 for (int loop = m_txtCache->SubPage + offset; loop != m_txtCache->SubPage; loop += offset)
882 if (loop < 0)
883 loop = 0x79;
884 else if (loop > 0x79)
885 loop = 0;
886 if (loop == m_txtCache->SubPage)
887 break;
889 if (m_txtCache->astCachetable[m_txtCache->Page][loop])
891 /* enable manual SubPage zapping */
892 m_txtCache->ZapSubpageManual = true;
894 /* update page */
895 if (m_RenderInfo.ZoomMode == 2) /* if zoomed to lower half */
896 m_RenderInfo.ZoomMode = 1; /* activate upper half */
898 m_txtCache->SubPage = loop;
899 m_RenderInfo.HintMode = false;
900 m_txtCache->PageUpdate = true;
902 return;
907 void CTeletextDecoder::SwitchZoomMode()
909 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
911 if (m_txtCache->SubPageTable[m_txtCache->Page] != 0xFF)
913 /* toggle mode */
914 m_RenderInfo.ZoomMode++;
916 if (m_RenderInfo.ZoomMode == 3)
917 m_RenderInfo.ZoomMode = 0;
919 /* update page */
920 m_txtCache->PageUpdate = true;
924 void CTeletextDecoder::SwitchTranspMode()
926 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
928 /* toggle mode */
929 if (!m_RenderInfo.TranspMode)
930 m_RenderInfo.TranspMode = true;
931 else
932 m_RenderInfo.TranspMode = false; /* backward to immediately switch to TV-screen */
934 /* set mode */
935 if (!m_RenderInfo.TranspMode) /* normal text-only */
937 ClearBB(m_txtCache->FullScrColor);
938 m_txtCache->PageUpdate = true;
940 else /* semi-transparent BG with FG text */
942 ClearBB(TXT_ColorTransp);
943 m_txtCache->PageUpdate = true;
947 void CTeletextDecoder::SwitchHintMode()
949 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
951 /* toggle mode */
952 m_RenderInfo.HintMode ^= true;
954 if (!m_RenderInfo.HintMode) /* toggle evaluation of level 2.5 information by explicitly switching off HintMode */
956 m_RenderInfo.Showl25 ^= true;
958 /* update page */
959 m_txtCache->PageUpdate = true;
962 void CTeletextDecoder::ColorKey(int target)
964 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
966 if (!target)
967 return;
969 if (m_RenderInfo.ZoomMode == 2)
970 m_RenderInfo.ZoomMode = 1;
972 m_LastPage = m_txtCache->Page;
973 m_txtCache->Page = target;
974 m_txtCache->SubPage = m_txtCache->SubPageTable[m_txtCache->Page];
975 m_RenderInfo.InputCounter = 2;
976 m_RenderInfo.HintMode = false;
977 m_txtCache->PageUpdate = true;
980 void CTeletextDecoder::StartPageCatching()
982 m_RenderInfo.PageCatching = true;
984 /* abort pageinput */
985 m_RenderInfo.InputCounter = 2;
987 /* show info line */
988 m_RenderInfo.ZoomMode = 0;
989 m_RenderInfo.PosX = 0;
990 m_RenderInfo.PosY = 24*m_RenderInfo.FontHeight;
992 /* check for pagenumber(s) */
993 m_CatchRow = 1;
994 m_CatchCol = 0;
995 m_CatchedPage = 0;
996 m_PCOldRow = 0;
997 m_PCOldCol = 0; /* no inverted page number to restore yet */
998 CatchNextPage(0, 1);
1000 if (!m_CatchedPage)
1002 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1004 m_RenderInfo.PageCatching = false;
1005 m_txtCache->PageUpdate = true;
1006 return;
1010 void CTeletextDecoder::StopPageCatching()
1012 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1014 /* set new page */
1015 if (m_RenderInfo.ZoomMode == 2)
1016 m_RenderInfo.ZoomMode = 1;
1018 m_LastPage = m_txtCache->Page;
1019 m_txtCache->Page = m_CatchedPage;
1020 m_RenderInfo.HintMode = false;
1021 m_txtCache->PageUpdate = true;
1022 m_RenderInfo.PageCatching = false;
1024 int subp = m_txtCache->SubPageTable[m_txtCache->Page];
1025 if (subp != 0xFF)
1026 m_txtCache->SubPage = subp;
1027 else
1028 m_txtCache->SubPage = 0;
1031 void CTeletextDecoder::CatchNextPage(int firstlineinc, int inc)
1033 int tmp_page, allowwrap = 1; /* allow first wrap around */
1035 /* catch next page */
1036 for(;;)
1038 unsigned char *p = &(m_RenderInfo.PageChar[m_CatchRow*40 + m_CatchCol]);
1039 TextPageAttr_t a = m_RenderInfo.PageAtrb[m_CatchRow*40 + m_CatchCol];
1041 if (!(a.charset == C_G1C || a.charset == C_G1S) && /* no mosaic */
1042 (a.fg != a.bg) && /* not hidden */
1043 (*p >= '1' && *p <= '8' && /* valid page number */
1044 *(p+1) >= '0' && *(p+1) <= '9' &&
1045 *(p+2) >= '0' && *(p+2) <= '9') &&
1046 (m_CatchRow == 0 || (*(p-1) < '0' || *(p-1) > '9')) && /* non-numeric char before and behind */
1047 (m_CatchRow == 37 || (*(p+3) < '0' || *(p+3) > '9')))
1049 tmp_page = ((*p - '0')<<8) | ((*(p+1) - '0')<<4) | (*(p+2) - '0');
1051 #if 0
1052 if (tmp_page != m_CatchedPage) /* confusing to skip identical page numbers - I want to reach what I aim to */
1053 #endif
1055 m_CatchedPage = tmp_page;
1056 RenderCatchedPage();
1057 m_CatchCol += inc; /* FIXME: limit */
1058 return;
1062 if (firstlineinc > 0)
1064 m_CatchRow++;
1065 m_CatchCol = 0;
1066 firstlineinc = 0;
1068 else if (firstlineinc < 0)
1070 m_CatchRow--;
1071 m_CatchCol = 37;
1072 firstlineinc = 0;
1074 else
1075 m_CatchCol += inc;
1077 if (m_CatchCol > 37)
1079 m_CatchRow++;
1080 m_CatchCol = 0;
1082 else if (m_CatchCol < 0)
1084 m_CatchRow--;
1085 m_CatchCol = 37;
1088 if (m_CatchRow > 23)
1090 if (allowwrap)
1092 allowwrap = 0;
1093 m_CatchRow = 1;
1094 m_CatchCol = 0;
1096 else
1098 return;
1101 else if (m_CatchRow < 1)
1103 if (allowwrap)
1105 allowwrap = 0;
1106 m_CatchRow = 23;
1107 m_CatchCol =37;
1109 else
1111 return;
1117 void CTeletextDecoder::RenderCatchedPage()
1119 int zoom = 0;
1120 m_updateTexture = true;
1122 /* handle zoom */
1123 if (m_RenderInfo.ZoomMode)
1124 zoom = 1<<10;
1126 if (m_PCOldRow || m_PCOldCol) /* not at first call */
1128 /* restore pagenumber */
1129 SetPosX(m_PCOldCol);
1131 if (m_RenderInfo.ZoomMode == 2)
1132 m_RenderInfo.PosY = (m_PCOldRow-12)*m_RenderInfo.FontHeight*((zoom>>10)+1);
1133 else
1134 m_RenderInfo.PosY = m_PCOldRow*m_RenderInfo.FontHeight*((zoom>>10)+1);
1136 RenderCharFB(m_RenderInfo.PageChar[m_PCOldRow*40 + m_PCOldCol ], &m_RenderInfo.PageAtrb[m_PCOldRow*40 + m_PCOldCol ]);
1137 RenderCharFB(m_RenderInfo.PageChar[m_PCOldRow*40 + m_PCOldCol + 1], &m_RenderInfo.PageAtrb[m_PCOldRow*40 + m_PCOldCol + 1]);
1138 RenderCharFB(m_RenderInfo.PageChar[m_PCOldRow*40 + m_PCOldCol + 2], &m_RenderInfo.PageAtrb[m_PCOldRow*40 + m_PCOldCol + 2]);
1141 m_PCOldRow = m_CatchRow;
1142 m_PCOldCol = m_CatchCol;
1144 /* mark pagenumber */
1145 if (m_RenderInfo.ZoomMode == 1 && m_CatchRow > 11)
1147 m_RenderInfo.ZoomMode = 2;
1148 CopyBB2FB();
1150 else if (m_RenderInfo.ZoomMode == 2 && m_CatchRow < 12)
1152 m_RenderInfo.ZoomMode = 1;
1153 CopyBB2FB();
1155 SetPosX(m_CatchCol);
1157 if (m_RenderInfo.ZoomMode == 2)
1158 m_RenderInfo.PosY = (m_CatchRow-12)*m_RenderInfo.FontHeight*((zoom>>10)+1);
1159 else
1160 m_RenderInfo.PosY = m_CatchRow*m_RenderInfo.FontHeight*((zoom>>10)+1);
1162 TextPageAttr_t a0 = m_RenderInfo.PageAtrb[m_CatchRow*40 + m_CatchCol ];
1163 TextPageAttr_t a1 = m_RenderInfo.PageAtrb[m_CatchRow*40 + m_CatchCol + 1];
1164 TextPageAttr_t a2 = m_RenderInfo.PageAtrb[m_CatchRow*40 + m_CatchCol + 2];
1165 int t;
1167 /* exchange colors */
1168 t = a0.fg; a0.fg = a0.bg; a0.bg = t;
1169 t = a1.fg; a1.fg = a1.bg; a1.bg = t;
1170 t = a2.fg; a2.fg = a2.bg; a2.bg = t;
1172 RenderCharFB(m_RenderInfo.PageChar[m_CatchRow*40 + m_CatchCol ], &a0);
1173 RenderCharFB(m_RenderInfo.PageChar[m_CatchRow*40 + m_CatchCol + 1], &a1);
1174 RenderCharFB(m_RenderInfo.PageChar[m_CatchRow*40 + m_CatchCol + 2], &a2);
1177 void CTeletextDecoder::RenderPage()
1179 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1181 int StartRow = 0;
1182 int national_subset_bak = m_txtCache->NationalSubset;
1184 if (m_txtCache->PageUpdate)
1185 m_updateTexture = true;
1187 /* update page or timestring */
1188 if (m_txtCache->PageUpdate && m_txtCache->PageReceiving != m_txtCache->Page && m_RenderInfo.InputCounter == 2)
1190 /* reset update flag */
1191 m_txtCache->PageUpdate = false;
1192 if (m_RenderInfo.Boxed && m_RenderInfo.SubtitleDelay)
1194 TextSubtitleCache_t* c = NULL;
1195 int j = -1;
1196 for (int i = 0; i < SUBTITLE_CACHESIZE; i++)
1198 if (j == -1 && !m_RenderInfo.SubtitleCache[i])
1199 j = i;
1200 if (m_RenderInfo.SubtitleCache[i] && !m_RenderInfo.SubtitleCache[i]->Valid)
1202 c = m_RenderInfo.SubtitleCache[i];
1203 break;
1206 if (c == NULL)
1208 if (j == -1) // no more space in SubtitleCache
1209 return;
1211 c = new TextSubtitleCache_t;
1212 if (c == NULL)
1213 return;
1215 *c = {};
1216 m_RenderInfo.SubtitleCache[j] = c;
1218 c->Valid = true;
1219 c->Timestamp = std::chrono::steady_clock::now();
1221 if (m_txtCache->SubPageTable[m_txtCache->Page] != 0xFF)
1223 TextPageinfo_t * p = DecodePage(m_RenderInfo.Showl25, c->PageChar, c->PageAtrb, m_RenderInfo.HintMode, m_RenderInfo.ShowFlof);
1224 if (p)
1226 m_RenderInfo.Boxed = p->boxed;
1229 m_RenderInfo.DelayStarted = true;
1230 return;
1232 m_RenderInfo.DelayStarted = false;
1233 /* decode page */
1234 if (m_txtCache->SubPageTable[m_txtCache->Page] != 0xFF)
1236 TextPageinfo_t * p = DecodePage(m_RenderInfo.Showl25, m_RenderInfo.PageChar, m_RenderInfo.PageAtrb, m_RenderInfo.HintMode, m_RenderInfo.ShowFlof);
1237 if (p)
1239 m_RenderInfo.PageInfo = p;
1240 m_RenderInfo.Boxed = p->boxed;
1242 if (m_RenderInfo.Boxed || m_RenderInfo.TranspMode)
1243 FillBorder(GetColorRGB(TXT_ColorTransp));
1244 else
1245 FillBorder(GetColorRGB((enumTeletextColor)m_txtCache->FullScrColor));
1247 if (m_txtCache->ColorTable) /* as late as possible to shorten the time the old page is displayed with the new colors */
1248 SetColors(m_txtCache->ColorTable, 16, 16); /* set colors for CLUTs 2+3 */
1250 else
1251 StartRow = 1;
1253 DoRenderPage(StartRow, national_subset_bak);
1255 else
1257 if (m_RenderInfo.DelayStarted)
1259 auto now = std::chrono::steady_clock::now();
1260 for (TextSubtitleCache_t* const subtitleCache : m_RenderInfo.SubtitleCache)
1262 if (subtitleCache && subtitleCache->Valid &&
1263 std::chrono::duration_cast<std::chrono::seconds>(now - subtitleCache->Timestamp)
1264 .count() >= m_RenderInfo.SubtitleDelay)
1266 memcpy(m_RenderInfo.PageChar, subtitleCache->PageChar, 40 * 25);
1267 memcpy(m_RenderInfo.PageAtrb, subtitleCache->PageAtrb, 40 * 25 * sizeof(TextPageAttr_t));
1268 DoRenderPage(StartRow, national_subset_bak);
1269 subtitleCache->Valid = false;
1270 return;
1274 if (m_RenderInfo.ZoomMode != 2)
1276 m_RenderInfo.PosY = 0;
1277 if (m_txtCache->SubPageTable[m_txtCache->Page] == 0xff)
1279 m_RenderInfo.PageAtrb[32].fg = TXT_ColorYellow;
1280 m_RenderInfo.PageAtrb[32].bg = TXT_ColorMenu1;
1281 int showpage = m_txtCache->PageReceiving;
1282 int showsubpage;
1284 // Verify that showpage is positive before any access to the array
1285 if (showpage >= 0 && (showsubpage = m_txtCache->SubPageTable[showpage]) != 0xff)
1287 TextCachedPage_t *pCachedPage;
1288 pCachedPage = m_txtCache->astCachetable[showpage][showsubpage];
1289 if (pCachedPage && IsDec(showpage))
1291 m_RenderInfo.PosX = 0;
1292 if (m_RenderInfo.InputCounter == 2)
1294 if (m_txtCache->BTTok && !m_txtCache->BasicTop[m_txtCache->Page]) /* page non-existent according to TOP (continue search anyway) */
1296 m_RenderInfo.PageAtrb[0].fg = TXT_ColorWhite;
1297 m_RenderInfo.PageAtrb[0].bg = TXT_ColorRed;
1299 else
1301 m_RenderInfo.PageAtrb[0].fg = TXT_ColorYellow;
1302 m_RenderInfo.PageAtrb[0].bg = TXT_ColorMenu1;
1304 CDVDTeletextTools::Hex2Str((char*)m_RenderInfo.PageChar+3, m_txtCache->Page);
1306 int col;
1307 for (col = m_RenderInfo.nofirst; col < 7; col++) // selected page
1309 RenderCharFB(m_RenderInfo.PageChar[col], &m_RenderInfo.PageAtrb[0]);
1311 RenderCharFB(m_RenderInfo.PageChar[col], &m_RenderInfo.PageAtrb[32]);
1313 else
1314 SetPosX(8);
1316 memcpy(&m_RenderInfo.PageChar[8], pCachedPage->p0, 24); /* header line without timestring */
1317 for (unsigned char i : pCachedPage->p0)
1319 RenderCharFB(i, &m_RenderInfo.PageAtrb[32]);
1322 /* Update on every Header number change */
1323 if (pCachedPage->p0[2] != prevHeaderPage)
1325 prevHeaderPage = pCachedPage->p0[2];
1326 m_updateTexture = true;
1332 /* update timestring */
1333 SetPosX(32);
1334 for (int i = 0; i < 8; i++)
1336 if (!m_RenderInfo.PageAtrb[32+i].flashing)
1337 RenderCharFB(m_txtCache->TimeString[i], &m_RenderInfo.PageAtrb[32]);
1338 else
1340 SetPosX(33+i);
1344 DoFlashing(StartRow);
1345 m_txtCache->NationalSubset = national_subset_bak;
1349 bool CTeletextDecoder::IsSubtitlePage(int pageNumber) const
1351 if (!m_txtCache)
1352 return false;
1354 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1356 for (const auto subPage : m_txtCache->SubtitlePages)
1358 if (subPage.page == pageNumber)
1359 return true;
1362 return false;
1365 void CTeletextDecoder::DoFlashing(int startrow)
1367 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1369 TextCachedPage_t* textCachepage =
1370 m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPage];
1372 // Verify that the page is not deleted by the other thread: CDVDTeletextData::ResetTeletextCache()
1373 if (!textCachepage || m_RenderInfo.PageInfo != &textCachepage->pageinfo)
1374 m_RenderInfo.PageInfo = nullptr;
1376 /* get national subset */
1377 if (m_txtCache->NationalSubset <= NAT_MAX_FROM_HEADER && /* not for GR/RU as long as line28 is not evaluated */
1378 m_RenderInfo.PageInfo && m_RenderInfo.PageInfo->nationalvalid) /* individual subset according to page header */
1380 m_txtCache->NationalSubset = CountryConversionTable[m_RenderInfo.PageInfo->national];
1383 /* Flashing */
1384 TextPageAttr_t flashattr;
1385 char flashchar;
1386 std::chrono::milliseconds flashphase = std::chrono::duration_cast<std::chrono::milliseconds>(
1387 std::chrono::steady_clock::now().time_since_epoch()) %
1388 1000;
1390 int srow = startrow;
1391 int erow = 24;
1392 int factor=1;
1394 switch (m_RenderInfo.ZoomMode)
1396 case 1: erow = 12; factor=2;break;
1397 case 2: srow = 12; factor=2;break;
1400 m_RenderInfo.PosY = startrow*m_RenderInfo.FontHeight*factor;
1401 for (int row = srow; row < erow; row++)
1403 int index = row * 40;
1404 int dhset = 0;
1405 int incflash = 3;
1406 int decflash = 2;
1408 m_RenderInfo.PosX = 0;
1409 for (int col = m_RenderInfo.nofirst; col < 40; col++)
1411 if (m_RenderInfo.PageAtrb[index + col].flashing && m_RenderInfo.PageChar[index + col] > 0x20 && m_RenderInfo.PageChar[index + col] != 0xff )
1413 SetPosX(col);
1414 flashchar = m_RenderInfo.PageChar[index + col];
1415 bool doflash = false;
1416 memcpy(&flashattr, &m_RenderInfo.PageAtrb[index + col], sizeof(TextPageAttr_t));
1417 switch (flashattr.flashing &0x1c) // Flash Rate
1419 case 0x00 : // 1 Hz
1420 if (flashphase > 500ms)
1421 doflash = true;
1422 break;
1423 case 0x04 : // 2 Hz Phase 1
1424 if (flashphase < 250ms)
1425 doflash = true;
1426 break;
1427 case 0x08 : // 2 Hz Phase 2
1428 if (flashphase >= 250ms && flashphase < 500ms)
1429 doflash = true;
1430 break;
1431 case 0x0c : // 2 Hz Phase 3
1432 if (flashphase >= 500ms && flashphase < 750ms)
1433 doflash = true;
1434 break;
1435 case 0x10 : // incremental flash
1436 incflash++;
1437 if (incflash>3) incflash = 1;
1438 switch (incflash)
1440 case 1:
1441 if (flashphase < 250ms)
1442 doflash = true;
1443 break;
1444 case 2:
1445 if (flashphase >= 250ms && flashphase < 500ms)
1446 doflash = true;
1447 break;
1448 case 3:
1449 if (flashphase >= 500ms && flashphase < 750ms)
1450 doflash = true;
1451 break;
1453 break;
1454 case 0x14 : // decremental flash
1455 decflash--;
1456 if (decflash<1) decflash = 3;
1457 switch (decflash)
1459 case 1:
1460 if (flashphase < 250ms)
1461 doflash = true;
1462 break;
1463 case 2:
1464 if (flashphase >= 250ms && flashphase < 500ms)
1465 doflash = true;
1466 break;
1467 case 3:
1468 if (flashphase >= 500ms && flashphase < 750ms)
1469 doflash = true;
1470 break;
1472 break;
1476 switch (flashattr.flashing &0x03) // Flash Mode
1478 case 0x01 : // normal Flashing
1479 if (doflash) flashattr.fg = flashattr.bg;
1480 break;
1481 case 0x02 : // inverted Flashing
1482 doflash = !doflash;
1483 if (doflash) flashattr.fg = flashattr.bg;
1484 break;
1485 case 0x03 : // color Flashing
1486 if (doflash) flashattr.fg = flashattr.fg + (flashattr.fg > 7 ? (-8) : 8);
1487 break;
1490 RenderCharFB(flashchar, &flashattr);
1491 if (flashattr.doublew) col++;
1492 if (flashattr.doubleh) dhset = 1;
1494 m_updateTexture = true;
1497 if (dhset)
1499 row++;
1500 m_RenderInfo.PosY += m_RenderInfo.FontHeight*factor;
1502 m_RenderInfo.PosY += m_RenderInfo.FontHeight*factor;
1506 void CTeletextDecoder::DoRenderPage(int startrow, int national_subset_bak)
1508 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1510 /* display first column? */
1511 m_RenderInfo.nofirst = m_RenderInfo.Show39;
1512 for (int row = 1; row < 24; row++)
1514 int Byte = m_RenderInfo.PageChar[row*40];
1515 if (Byte != ' ' && Byte != 0x00 && Byte != 0xFF && m_RenderInfo.PageAtrb[row*40].fg != m_RenderInfo.PageAtrb[row*40].bg)
1517 m_RenderInfo.nofirst = 0;
1518 break;
1521 m_RenderInfo.FontWidth_Normal = m_RenderInfo.Width / (m_RenderInfo.nofirst ? 39 : 40);
1522 SetFontWidth(m_RenderInfo.FontWidth_Normal);
1524 if (m_RenderInfo.TranspMode || m_RenderInfo.Boxed)
1526 FillBorder(GetColorRGB(TXT_ColorTransp));//ClearBB(transp);
1527 m_RenderInfo.ClearBBColor = TXT_ColorTransp;
1530 /* get national subset */
1531 if (m_txtCache->NationalSubset <= NAT_MAX_FROM_HEADER && /* not for GR/RU as long as line28 is not evaluated */
1532 m_RenderInfo.PageInfo && m_RenderInfo.PageInfo->nationalvalid) /* individual subset according to page header */
1534 m_txtCache->NationalSubset = CountryConversionTable[m_RenderInfo.PageInfo->national];
1536 /* render page */
1537 if (m_RenderInfo.PageInfo && (m_RenderInfo.PageInfo->function == FUNC_GDRCS || m_RenderInfo.PageInfo->function == FUNC_DRCS)) /* character definitions */
1539 #define DRCSROWS 8
1540 #define DRCSCOLS (48/DRCSROWS)
1541 #define DRCSZOOMX 3
1542 #define DRCSZOOMY 5
1543 #define DRCSXSPC (12*DRCSZOOMX + 2)
1544 #define DRCSYSPC (10*DRCSZOOMY + 2)
1546 unsigned char ax[] = { /* array[0..12] of x-offsets, array[0..10] of y-offsets for each pixel */
1547 DRCSZOOMX * 0,
1548 DRCSZOOMX * 1,
1549 DRCSZOOMX * 2,
1550 DRCSZOOMX * 3,
1551 DRCSZOOMX * 4,
1552 DRCSZOOMX * 5,
1553 DRCSZOOMX * 6,
1554 DRCSZOOMX * 7,
1555 DRCSZOOMX * 8,
1556 DRCSZOOMX * 9,
1557 DRCSZOOMX * 10,
1558 DRCSZOOMX * 11,
1559 DRCSZOOMX * 12,
1560 DRCSZOOMY * 0,
1561 DRCSZOOMY * 1,
1562 DRCSZOOMY * 2,
1563 DRCSZOOMY * 3,
1564 DRCSZOOMY * 4,
1565 DRCSZOOMY * 5,
1566 DRCSZOOMY * 6,
1567 DRCSZOOMY * 7,
1568 DRCSZOOMY * 8,
1569 DRCSZOOMY * 9,
1570 DRCSZOOMY * 10
1573 ClearBB(TXT_ColorBlack);
1574 for (int col = 0; col < 24*40; col++)
1575 m_RenderInfo.PageAtrb[col] = Text_AtrTable[ATR_WB];
1577 for (int row = 0; row < DRCSROWS; row++)
1579 for (int col = 0; col < DRCSCOLS; col++)
1581 RenderDRCS(m_RenderInfo.Width,
1582 m_RenderInfo.PageChar + 20 * (DRCSCOLS * row + col + 2),
1583 m_TextureBuffer
1584 + (m_RenderInfo.FontHeight + DRCSYSPC * row + m_RenderInfo.Height) * m_RenderInfo.Width
1585 + DRCSXSPC * col,
1586 ax, GetColorRGB(TXT_ColorWhite), GetColorRGB(TXT_ColorBlack));
1589 memset(m_RenderInfo.PageChar + 40, 0xff, 24*40); /* don't render any char below row 0 */
1591 m_RenderInfo.PosY = startrow*m_RenderInfo.FontHeight;
1592 for (int row = startrow; row < 24; row++)
1594 int index = row * 40;
1596 m_RenderInfo.PosX = 0;
1597 for (int col = m_RenderInfo.nofirst; col < 40; col++)
1599 RenderCharBB(m_RenderInfo.PageChar[index + col], &m_RenderInfo.PageAtrb[index + col]);
1601 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 */
1602 m_RenderInfo.PageChar[index + col + 40] = 0xff;
1603 if (m_RenderInfo.PageAtrb[index + col].doublew && col < 40-1) /* skip next column if double width */
1605 col++;
1606 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 */
1607 m_RenderInfo.PageChar[index + col + 40] = 0xff;
1610 m_RenderInfo.PosY += m_RenderInfo.FontHeight;
1612 DoFlashing(startrow);
1614 /* update framebuffer */
1615 CopyBB2FB();
1616 m_txtCache->NationalSubset = national_subset_bak;
1619 void CTeletextDecoder::Decode_BTT()
1621 /* basic top table */
1622 int current, b1, b2, b3, b4;
1623 unsigned char btt[23*40];
1625 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1627 if (m_txtCache->SubPageTable[0x1f0] == 0xff || 0 == m_txtCache->astCachetable[0x1f0][m_txtCache->SubPageTable[0x1f0]]) /* not yet received */
1628 return;
1630 auto& components = CServiceBroker::GetAppComponents();
1631 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
1633 appPlayer->LoadPage(0x1f0, m_txtCache->SubPageTable[0x1f0], btt);
1634 if (btt[799] == ' ') /* not completely received or error */
1635 return;
1637 current = 0x100;
1638 for (int i = 0; i < 800; i++)
1640 b1 = btt[i];
1641 if (b1 == ' ')
1642 b1 = 0;
1643 else
1645 b1 = dehamming[b1];
1646 if (b1 == 0xFF) /* hamming error in btt */
1648 btt[799] = ' '; /* mark btt as not received */
1649 return;
1652 m_txtCache->BasicTop[current] = b1;
1653 CDVDTeletextTools::NextDec(&current);
1655 /* page linking table */
1656 m_txtCache->ADIP_PgMax = -1; /* rebuild table of adip pages */
1657 for (int i = 0; i < 10; i++)
1659 b1 = dehamming[btt[800 + 8*i +0]];
1661 if (b1 == 0xE)
1662 continue; /* unused */
1663 else if (b1 == 0xF)
1664 break; /* end */
1666 b4 = dehamming[btt[800 + 8*i +7]];
1668 if (b4 != 2) /* only adip, ignore multipage (1) */
1669 continue;
1671 b2 = dehamming[btt[800 + 8*i +1]];
1672 b3 = dehamming[btt[800 + 8*i +2]];
1674 if (b1 == 0xFF || b2 == 0xFF || b3 == 0xFF)
1676 CLog::Log(LOGERROR, "CTeletextDecoder::Decode_BTT <Biterror in btt/plt index {}>", i);
1677 btt[799] = ' '; /* mark btt as not received */
1678 return;
1681 b1 = b1<<8 | b2<<4 | b3; /* page number */
1682 m_txtCache->ADIP_Pg[++m_txtCache->ADIP_PgMax] = b1;
1685 m_txtCache->BTTok = true;
1688 void CTeletextDecoder::Decode_ADIP() /* additional information table */
1690 int i, p, j, b1, b2, b3, charfound;
1691 unsigned char padip[23*40];
1693 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1695 auto& components = CServiceBroker::GetAppComponents();
1696 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
1698 for (i = 0; i <= m_txtCache->ADIP_PgMax; i++)
1700 p = m_txtCache->ADIP_Pg[i];
1701 if (!p || m_txtCache->SubPageTable[p] == 0xff || 0 == m_txtCache->astCachetable[p][m_txtCache->SubPageTable[p]]) /* not cached (avoid segfault) */
1702 continue;
1704 appPlayer->LoadPage(p, m_txtCache->SubPageTable[p], padip);
1705 for (j = 0; j < 44; j++)
1707 b1 = dehamming[padip[20*j+0]];
1708 if (b1 == 0xE)
1709 continue; /* unused */
1711 if (b1 == 0xF)
1712 break; /* end */
1714 b2 = dehamming[padip[20*j+1]];
1715 b3 = dehamming[padip[20*j+2]];
1717 if (b1 == 0xFF || b2 == 0xFF || b3 == 0xFF)
1719 CLog::Log(LOGERROR,
1720 "CTeletextDecoder::Decode_BTT <Biterror in ait {:03x} {} {:02x} {:02x} {:02x} "
1721 "{:02x} {:02x} {:02x}>",
1722 p, j, padip[20 * j + 0], padip[20 * j + 1], padip[20 * j + 2], b1, b2, b3);
1723 return;
1726 if (b1>8 || b2>9 || b3>9) /* ignore entries with invalid or hex page numbers */
1728 continue;
1731 b1 = b1<<8 | b2<<4 | b3; /* page number */
1732 charfound = 0; /* flag: no printable char found */
1734 for (b2 = 11; b2 >= 0; b2--)
1736 b3 = deparity[padip[20*j + 8 + b2]];
1737 if (b3 < ' ')
1738 b3 = ' ';
1740 if (b3 == ' ' && !charfound)
1741 m_txtCache->ADIPTable[b1][b2] = '\0';
1742 else
1744 m_txtCache->ADIPTable[b1][b2] = b3;
1745 charfound = 1;
1748 } /* next link j */
1750 m_txtCache->ADIP_Pg[i] = 0; /* completely decoded: clear entry */
1751 } /* next adip page i */
1753 while ((m_txtCache->ADIP_PgMax >= 0) && !m_txtCache->ADIP_Pg[m_txtCache->ADIP_PgMax]) /* and shrink table */
1754 m_txtCache->ADIP_PgMax--;
1757 int CTeletextDecoder::TopText_GetNext(int startpage, int up, int findgroup)
1759 int current, nextgrp, nextblk;
1761 int stoppage = (IsDec(startpage) ? startpage : startpage & 0xF00); // avoid endless loop in hexmode
1762 nextgrp = nextblk = 0;
1763 current = startpage;
1765 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1767 do {
1768 if (up)
1769 CDVDTeletextTools::NextDec(&current);
1770 else
1771 CDVDTeletextTools::PrevDec(&current);
1773 if (!m_txtCache->BTTok || m_txtCache->BasicTop[current]) /* only if existent */
1775 if (findgroup)
1777 if (m_txtCache->BasicTop[current] >= 6 && m_txtCache->BasicTop[current] <= 7)
1778 return current;
1779 if (!nextgrp && (current&0x00F) == 0)
1780 nextgrp = current;
1782 if (m_txtCache->BasicTop[current] >= 2 && m_txtCache->BasicTop[current] <= 5) /* always find block */
1783 return current;
1785 if (!nextblk && (current&0x0FF) == 0)
1786 nextblk = current;
1788 } while (current != stoppage);
1790 if (nextgrp)
1791 return nextgrp;
1792 else if (nextblk)
1793 return nextblk;
1794 else
1795 return current;
1798 void CTeletextDecoder::Showlink(int column, int linkpage)
1800 unsigned char line[] = " >??? ";
1801 int oldfontwidth = m_RenderInfo.FontWidth;
1802 int yoffset;
1804 if (m_YOffset)
1805 yoffset = 0;
1806 else
1807 yoffset = m_RenderInfo.Height;
1809 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
1810 int width = m_RenderInfo.Width /4;
1812 m_RenderInfo.PosY = 24*m_RenderInfo.FontHeight;
1814 if (m_RenderInfo.Boxed)
1816 m_RenderInfo.PosX = column*width;
1817 FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY+yoffset, m_RenderInfo.Width, m_RenderInfo.FontHeight, GetColorRGB(TXT_ColorTransp));
1818 return;
1821 if (m_txtCache->ADIPTable[linkpage][0])
1823 m_RenderInfo.PosX = column*width;
1824 int l = strlen(m_txtCache->ADIPTable[linkpage]);
1826 if (l > 9) /* smaller font, if no space for one half space at front and end */
1827 SetFontWidth(oldfontwidth * 10 / (l+1));
1829 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));
1830 m_RenderInfo.PosX += ((width) - (l*m_RenderInfo.FontWidth+l*m_RenderInfo.FontWidth/abx))/2; /* center */
1832 for (char *p = m_txtCache->ADIPTable[linkpage]; *p; p++)
1833 RenderCharBB(*p, &Text_AtrTable[ATR_L250 + column]);
1835 SetFontWidth(oldfontwidth);
1837 else /* display number */
1839 m_RenderInfo.PosX = column*width;
1840 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));
1841 if (linkpage < m_txtCache->Page)
1843 line[6] = '<';
1844 CDVDTeletextTools::Hex2Str((char*)line + 5, linkpage);
1846 else
1847 CDVDTeletextTools::Hex2Str((char*)line + 6, linkpage);
1849 for (unsigned char *p = line; p < line+9; p++)
1850 RenderCharBB(*p, &Text_AtrTable[ATR_L250 + column]);
1854 void CTeletextDecoder::CreateLine25()
1856 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1858 /* btt completely received and not yet decoded */
1859 if (!m_txtCache->BTTok)
1860 Decode_BTT();
1862 if (m_txtCache->ADIP_PgMax >= 0)
1863 Decode_ADIP();
1865 if (!m_RenderInfo.ShowHex && m_RenderInfo.ShowFlof &&
1866 (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
1868 m_RenderInfo.Prev_100 = m_txtCache->FlofPages[m_txtCache->Page][0];
1869 m_RenderInfo.Prev_10 = m_txtCache->FlofPages[m_txtCache->Page][1];
1870 m_RenderInfo.Next_10 = m_txtCache->FlofPages[m_txtCache->Page][2];
1871 m_RenderInfo.Next_100 = m_txtCache->FlofPages[m_txtCache->Page][3];
1873 m_RenderInfo.PosY = 24*m_RenderInfo.FontHeight;
1874 m_RenderInfo.PosX = 0;
1875 for (int i=m_RenderInfo.nofirst; i<40; i++)
1876 RenderCharBB(m_RenderInfo.PageChar[24*40 + i], &m_RenderInfo.PageAtrb[24*40 + i]);
1878 else
1880 /* normal: blk-1, grp+1, grp+2, blk+1 */
1881 /* hex: hex+1, blk-1, grp+1, blk+1 */
1882 if (m_RenderInfo.ShowHex)
1884 /* arguments: startpage, up, findgroup */
1885 m_RenderInfo.Prev_100 = NextHex(m_txtCache->Page);
1886 m_RenderInfo.Prev_10 = TopText_GetNext(m_txtCache->Page, 0, 0);
1887 m_RenderInfo.Next_10 = TopText_GetNext(m_txtCache->Page, 1, 1);
1889 else
1891 m_RenderInfo.Prev_100 = TopText_GetNext(m_txtCache->Page, 0, 0);
1892 m_RenderInfo.Prev_10 = TopText_GetNext(m_txtCache->Page, 1, 1);
1893 m_RenderInfo.Next_10 = TopText_GetNext(m_RenderInfo.Prev_10, 1, 1);
1895 m_RenderInfo.Next_100 = TopText_GetNext(m_RenderInfo.Next_10, 1, 0);
1896 Showlink(0, m_RenderInfo.Prev_100);
1897 Showlink(1, m_RenderInfo.Prev_10);
1898 Showlink(2, m_RenderInfo.Next_10);
1899 Showlink(3, m_RenderInfo.Next_100);
1903 void CTeletextDecoder::RenderCharFB(int Char, TextPageAttr_t *Attribute)
1905 RenderCharIntern(&m_RenderInfo, Char, Attribute, m_RenderInfo.ZoomMode, m_YOffset);
1908 void CTeletextDecoder::RenderCharBB(int Char, TextPageAttr_t *Attribute)
1910 RenderCharIntern(&m_RenderInfo, Char, Attribute, 0, m_RenderInfo.Height-m_YOffset);
1913 void CTeletextDecoder::CopyBB2FB()
1915 UTILS::COLOR::Color *src, *dst, *topsrc;
1916 int screenwidth;
1917 UTILS::COLOR::Color fillcolor;
1919 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
1921 /* line 25 */
1922 if (!m_RenderInfo.PageCatching)
1923 CreateLine25();
1925 /* copy backbuffer to framebuffer */
1926 if (!m_RenderInfo.ZoomMode)
1928 if (m_YOffset)
1929 m_YOffset = 0;
1930 else
1931 m_YOffset = m_RenderInfo.Height;
1933 if (m_RenderInfo.ClearBBColor >= 0)
1935 m_RenderInfo.ClearBBColor = -1;
1937 return;
1940 src = dst = topsrc = m_TextureBuffer + m_RenderInfo.Width;
1942 if (m_YOffset)
1944 dst += m_RenderInfo.Width * m_RenderInfo.Height;
1946 else
1948 src += m_RenderInfo.Width * m_RenderInfo.Height;
1949 topsrc += m_RenderInfo.Width * m_RenderInfo.Height;
1952 if (!m_RenderInfo.PageCatching)
1953 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 */
1955 if (m_RenderInfo.TranspMode)
1956 fillcolor = GetColorRGB(TXT_ColorTransp);
1957 else
1958 fillcolor = GetColorRGB((enumTeletextColor)m_txtCache->FullScrColor);
1960 if (m_RenderInfo.ZoomMode == 2)
1961 src += 12*m_RenderInfo.FontHeight*m_RenderInfo.Width;
1963 screenwidth = m_RenderInfo.Width;
1965 for (int i = 12*m_RenderInfo.FontHeight; i; i--)
1967 SDL_memcpy4(dst, src, screenwidth);
1968 dst += m_RenderInfo.Width;
1969 SDL_memcpy4(dst, src, screenwidth);
1970 dst += m_RenderInfo.Width;
1971 src += m_RenderInfo.Width;
1974 for (int i = m_RenderInfo.Height - 25*m_RenderInfo.FontHeight; i >= 0;i--)
1976 SDL_memset4(dst + m_RenderInfo.Width*(m_RenderInfo.FontHeight+i), fillcolor, screenwidth);
1980 FT_Error CTeletextDecoder::MyFaceRequester(FTC_FaceID face_id, FT_Library library, FT_Pointer request_data, FT_Face *aface)
1982 FT_Error result = FT_New_Face(library, (const char*)face_id, 0, aface);
1984 if (!result)
1985 CLog::Log(LOGINFO, "Teletext font {} loaded", (char*)face_id);
1986 else
1987 CLog::Log(LOGERROR, "Opening of Teletext font {} failed", (char*)face_id);
1989 return result;
1992 void CTeletextDecoder::SetFontWidth(int newWidth)
1994 if (m_RenderInfo.FontWidth != newWidth)
1996 m_RenderInfo.FontWidth = newWidth;
1997 m_TypeTTF.width = (FT_UShort) m_RenderInfo.FontWidth;
1999 for (int i = 0; i <= 12; i++)
2000 m_RenderInfo.axdrcs[i] = (m_RenderInfo.FontWidth * i + 6) / 12;
2004 int CTeletextDecoder::GetCurFontWidth()
2006 int mx = (m_RenderInfo.Width)%(40-m_RenderInfo.nofirst); // # of unused pixels
2007 int abx = (mx == 0 ? m_RenderInfo.Width+1 : (m_RenderInfo.Width)/(mx+1)); // distance between 'inserted' pixels
2008 int nx = abx+1-(m_RenderInfo.PosX % (abx+1)); // # of pixels to next insert
2009 return m_RenderInfo.FontWidth+(((m_RenderInfo.PosX+m_RenderInfo.FontWidth+1) <= m_RenderInfo.Width && nx <= m_RenderInfo.FontWidth+1) ? 1 : 0);
2012 void CTeletextDecoder::SetPosX(int column)
2014 m_RenderInfo.PosX = 0;
2016 for (int i = 0; i < column-m_RenderInfo.nofirst; i++)
2017 m_RenderInfo.PosX += GetCurFontWidth();
2020 void CTeletextDecoder::ClearBB(UTILS::COLOR::Color Color)
2022 SDL_memset4(m_TextureBuffer + (m_RenderInfo.Height-m_YOffset)*m_RenderInfo.Width, Color, m_RenderInfo.Width*m_RenderInfo.Height);
2025 void CTeletextDecoder::ClearFB(UTILS::COLOR::Color Color)
2027 SDL_memset4(m_TextureBuffer + m_RenderInfo.Width*m_YOffset, Color, m_RenderInfo.Width*m_RenderInfo.Height);
2030 void CTeletextDecoder::FillBorder(UTILS::COLOR::Color Color)
2032 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);
2033 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);
2036 void CTeletextDecoder::FillRect(
2037 UTILS::COLOR::Color* buffer, int xres, int x, int y, int w, int h, UTILS::COLOR::Color Color)
2039 if (!buffer) return;
2041 UTILS::COLOR::Color* p = buffer + x + y * xres;
2043 if (w > 0)
2045 for ( ; h > 0 ; h--)
2047 SDL_memset4(p, Color, w);
2048 p += xres;
2053 void CTeletextDecoder::DrawVLine(
2054 UTILS::COLOR::Color* lfb, int xres, int x, int y, int l, UTILS::COLOR::Color color)
2056 if (!lfb) return;
2057 UTILS::COLOR::Color* p = lfb + x + y * xres;
2059 for ( ; l > 0 ; l--)
2061 *p = color;
2062 p += xres;
2066 void CTeletextDecoder::DrawHLine(
2067 UTILS::COLOR::Color* lfb, int xres, int x, int y, int l, UTILS::COLOR::Color color)
2069 if (!lfb) return;
2070 if (l > 0)
2071 SDL_memset4(lfb + x + y * xres, color, l);
2074 void CTeletextDecoder::RenderDRCS(
2075 int xres,
2076 unsigned char* s, /* pointer to char data, parity undecoded */
2077 UTILS::COLOR::Color* d, /* pointer to frame buffer of top left pixel */
2078 unsigned char* ax, /* array[0..12] of x-offsets, array[0..10] of y-offsets for each pixel */
2079 UTILS::COLOR::Color fgcolor,
2080 UTILS::COLOR::Color bgcolor)
2082 if (d == NULL) return;
2084 unsigned char *ay = ax + 13; /* array[0..10] of y-offsets for each pixel */
2086 for (int y = 0; y < 10; y++) /* 10*2 bytes a 6 pixels per char definition */
2088 unsigned char c1 = deparity[*s++];
2089 unsigned char c2 = deparity[*s++];
2090 int h = ay[y+1] - ay[y];
2092 if (!h)
2093 continue;
2094 if (((c1 == ' ') && (*(s-2) != ' ')) || ((c2 == ' ') && (*(s-1) != ' '))) /* parity error: stop decoding FIXME */
2095 return;
2096 for (int bit = 0x20, x = 0;
2097 bit;
2098 bit >>= 1, x++) /* bit mask (MSB left), column counter */
2100 UTILS::COLOR::Color f1 = (c1 & bit) ? fgcolor : bgcolor;
2101 UTILS::COLOR::Color f2 = (c2 & bit) ? fgcolor : bgcolor;
2102 for (int i = 0; i < h; i++)
2104 if (ax[x+1] > ax[x])
2105 SDL_memset4(d + ax[x], f1, ax[x+1] - ax[x]);
2106 if (ax[x+7] > ax[x+6])
2107 SDL_memset4(d + ax[x+6], f2, ax[x+7] - ax[x+6]); /* 2nd byte 6 pixels to the right */
2108 d += xres;
2110 d -= h * xres;
2112 d += h * xres;
2116 void CTeletextDecoder::FillRectMosaicSeparated(UTILS::COLOR::Color* lfb,
2117 int xres,
2118 int x,
2119 int y,
2120 int w,
2121 int h,
2122 UTILS::COLOR::Color fgcolor,
2123 UTILS::COLOR::Color bgcolor,
2124 int set)
2126 if (!lfb) return;
2127 FillRect(lfb,xres,x, y, w, h, bgcolor);
2128 if (set)
2130 FillRect(lfb,xres,x+1, y+1, w-2, h-2, fgcolor);
2134 void CTeletextDecoder::FillTrapez(UTILS::COLOR::Color* lfb,
2135 int xres,
2136 int x0,
2137 int y0,
2138 int l0,
2139 int xoffset1,
2140 int h,
2141 int l1,
2142 UTILS::COLOR::Color color)
2144 UTILS::COLOR::Color* p = lfb + x0 + y0 * xres;
2145 int xoffset, l;
2147 for (int yoffset = 0; yoffset < h; yoffset++)
2149 l = l0 + ((l1-l0) * yoffset + h/2) / h;
2150 xoffset = (xoffset1 * yoffset + h/2) / h;
2151 if (l > 0)
2152 SDL_memset4(p + xoffset, color, l);
2153 p += xres;
2157 void CTeletextDecoder::FlipHorz(UTILS::COLOR::Color* lfb, int xres, int x, int y, int w, int h)
2159 UTILS::COLOR::Color buf[2048];
2160 UTILS::COLOR::Color* p = lfb + x + y * xres;
2161 int w1,h1;
2163 for (h1 = 0 ; h1 < h ; h1++)
2165 SDL_memcpy4(buf,p,w);
2166 for (w1 = 0 ; w1 < w ; w1++)
2168 *(p+w1) = buf[w-(w1+1)];
2170 p += xres;
2174 void CTeletextDecoder::FlipVert(UTILS::COLOR::Color* lfb, int xres, int x, int y, int w, int h)
2176 UTILS::COLOR::Color buf[2048];
2177 UTILS::COLOR::Color *p = lfb + x + y * xres, *p1, *p2;
2178 int h1;
2180 for (h1 = 0 ; h1 < h/2 ; h1++)
2182 p1 = (p+(h1*xres));
2183 p2 = (p+(h-(h1+1))*xres);
2184 SDL_memcpy4(buf, p1, w);
2185 SDL_memcpy4(p1, p2, w);
2186 SDL_memcpy4(p2, buf, w);
2190 int CTeletextDecoder::ShapeCoord(int param, int curfontwidth, int curFontHeight)
2192 switch (param)
2194 case S_W13:
2195 return curfontwidth/3;
2196 case S_W12:
2197 return curfontwidth/2;
2198 case S_W23:
2199 return curfontwidth*2/3;
2200 case S_W11:
2201 return curfontwidth;
2202 case S_WM3:
2203 return curfontwidth-3;
2204 case S_H13:
2205 return curFontHeight/3;
2206 case S_H12:
2207 return curFontHeight/2;
2208 case S_H23:
2209 return curFontHeight*2/3;
2210 case S_H11:
2211 return curFontHeight;
2212 default:
2213 return param;
2217 void CTeletextDecoder::DrawShape(UTILS::COLOR::Color* lfb,
2218 int xres,
2219 int x,
2220 int y,
2221 int shapenumber,
2222 int curfontwidth,
2223 int FontHeight,
2224 int curFontHeight,
2225 UTILS::COLOR::Color fgcolor,
2226 UTILS::COLOR::Color bgcolor,
2227 bool clear)
2229 if (!lfb || shapenumber < 0x20 || shapenumber > 0x7e || (shapenumber == 0x7e && clear))
2230 return;
2232 unsigned char *p = aShapes[shapenumber - 0x20];
2234 if (*p == S_INV)
2236 int t = fgcolor;
2237 fgcolor = bgcolor;
2238 bgcolor = t;
2239 p++;
2242 if (clear)
2243 FillRect(lfb, xres, x, y, curfontwidth, FontHeight, bgcolor);
2245 while (*p != S_END)
2247 switch (*p++)
2249 case S_FHL:
2251 int offset = ShapeCoord(*p++, curfontwidth, curFontHeight);
2252 DrawHLine(lfb, xres, x, y + offset, curfontwidth, fgcolor);
2253 break;
2255 case S_FVL:
2257 int offset = ShapeCoord(*p++, curfontwidth, curFontHeight);
2258 DrawVLine(lfb,xres,x + offset, y, FontHeight, fgcolor);
2259 break;
2261 case S_FLH:
2262 FlipHorz(lfb,xres,x,y,curfontwidth, FontHeight);
2263 break;
2264 case S_FLV:
2265 FlipVert(lfb,xres,x,y,curfontwidth, FontHeight);
2266 break;
2267 case S_BOX:
2269 int xo = ShapeCoord(*p++, curfontwidth, curFontHeight);
2270 int yo = ShapeCoord(*p++, curfontwidth, curFontHeight);
2271 int w = ShapeCoord(*p++, curfontwidth, curFontHeight);
2272 int h = ShapeCoord(*p++, curfontwidth, curFontHeight);
2273 FillRect(lfb,xres,x + xo, y + yo, w, h, fgcolor);
2274 break;
2276 case S_TRA:
2278 int x0 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2279 int y0 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2280 int l0 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2281 int x1 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2282 int y1 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2283 int l1 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2284 FillTrapez(lfb, xres,x + x0, y + y0, l0, x1-x0, y1-y0, l1, fgcolor);
2285 break;
2287 case S_BTR:
2289 int x0 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2290 int y0 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2291 int l0 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2292 int x1 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2293 int y1 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2294 int l1 = ShapeCoord(*p++, curfontwidth, curFontHeight);
2295 FillTrapez(lfb, xres, x + x0, y + y0, l0, x1-x0, y1-y0, l1, bgcolor);
2296 break;
2298 case S_LNK:
2300 DrawShape(lfb,xres,x, y, ShapeCoord(*p, curfontwidth, curFontHeight), curfontwidth, FontHeight, curFontHeight, fgcolor, bgcolor, false);
2301 break;
2303 default:
2304 break;
2309 void CTeletextDecoder::RenderCharIntern(TextRenderInfo_t* RenderInfo, int Char, TextPageAttr_t *Attribute, int zoom, int yoffset)
2311 int Row, Pitch;
2312 int glyph;
2313 UTILS::COLOR::Color bgcolor, fgcolor;
2314 int factor, xfactor;
2315 unsigned char *sbitbuffer;
2317 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
2319 int national_subset_local = m_txtCache->NationalSubset;
2320 int curfontwidth = GetCurFontWidth();
2321 int t = curfontwidth;
2322 m_RenderInfo.PosX += t;
2323 int curfontwidth2 = GetCurFontWidth();
2324 m_RenderInfo.PosX -= t;
2325 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);
2326 if (alphachar <= 0) return;
2328 if (zoom && Attribute->doubleh)
2329 factor = 4;
2330 else if (zoom || Attribute->doubleh)
2331 factor = 2;
2332 else
2333 factor = 1;
2335 fgcolor = GetColorRGB((enumTeletextColor)Attribute->fg);
2336 if (m_RenderInfo.TranspMode && m_RenderInfo.PosY < 24*m_RenderInfo.FontHeight)
2338 bgcolor = GetColorRGB(TXT_ColorTransp);
2340 else
2342 bgcolor = GetColorRGB((enumTeletextColor)Attribute->bg);
2345 if (Attribute->doublew)
2347 curfontwidth += curfontwidth2;
2348 xfactor = 2;
2350 else
2351 xfactor = 1;
2353 // Check if the alphanumeric char has diacritical marks (or results from composing chars) or
2354 // on the other hand it is just a simple alphanumeric char
2355 if (!Attribute->diacrit)
2357 Char = alphachar;
2359 else
2361 if ((national_subset_local == NAT_SC) || (national_subset_local == NAT_RB) ||
2362 (national_subset_local == NAT_UA))
2363 Char = G2table[1][0x20 + Attribute->diacrit];
2364 else if (national_subset_local == NAT_GR)
2365 Char = G2table[2][0x20 + Attribute->diacrit];
2366 else if (national_subset_local == NAT_HB)
2367 Char = G2table[3][0x20 + Attribute->diacrit];
2368 else if (national_subset_local == NAT_AR)
2369 Char = G2table[4][0x20 + Attribute->diacrit];
2370 else
2371 Char = G2table[0][0x20 + Attribute->diacrit];
2373 // use harfbuzz to combine the diacritical mark with the alphanumeric char
2374 // fallback to the alphanumeric char if composition fails
2375 hb_unicode_funcs_t* ufuncs = hb_unicode_funcs_get_default();
2376 hb_codepoint_t composedChar;
2377 const hb_bool_t isComposed = hb_unicode_compose(ufuncs, alphachar, Char, &composedChar);
2378 Char = isComposed ? composedChar : alphachar;
2381 /* render char */
2382 if (!(glyph = FT_Get_Char_Index(m_Face, Char)))
2384 CLog::Log(LOGERROR, "{}: <FT_Get_Char_Index for Char {:x} \"{}\" failed", __FUNCTION__,
2385 alphachar, alphachar);
2387 FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY + yoffset, curfontwidth, factor*m_RenderInfo.FontHeight, bgcolor);
2388 m_RenderInfo.PosX += curfontwidth;
2389 return;
2392 if (FTC_SBitCache_Lookup(m_Cache, &m_TypeTTF, glyph, &m_sBit, &m_anode) != 0)
2394 FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY + yoffset, curfontwidth, m_RenderInfo.FontHeight, bgcolor);
2395 m_RenderInfo.PosX += curfontwidth;
2396 return;
2399 sbitbuffer = m_sBit->buffer;
2401 int backupTTFshiftY = m_RenderInfo.TTFShiftY;
2402 if (national_subset_local == NAT_AR)
2403 m_RenderInfo.TTFShiftY = backupTTFshiftY - 2; // for arabic TTF font should be shifted up slightly
2405 UTILS::COLOR::Color* p;
2406 int f; /* running counter for zoom factor */
2407 int he = m_sBit->height; // sbit->height should not be altered, I guess
2408 Row = factor * (m_Ascender - m_sBit->top + m_RenderInfo.TTFShiftY);
2409 if (Row < 0)
2411 sbitbuffer -= m_sBit->pitch*Row;
2412 he += Row;
2413 Row = 0;
2415 else
2417 FillRect(m_TextureBuffer, m_RenderInfo.Width, m_RenderInfo.PosX, m_RenderInfo.PosY + yoffset, curfontwidth, Row, bgcolor); /* fill upper margin */
2420 if (m_Ascender - m_sBit->top + m_RenderInfo.TTFShiftY + he > m_RenderInfo.FontHeight)
2421 he = m_RenderInfo.FontHeight - m_Ascender + m_sBit->top - m_RenderInfo.TTFShiftY; /* limit char height to defined/calculated FontHeight */
2422 if (he < 0) he = m_RenderInfo.FontHeight;
2424 p = m_TextureBuffer + m_RenderInfo.PosX + (yoffset + m_RenderInfo.PosY + Row) * m_RenderInfo.Width; /* running pointer into framebuffer */
2425 for (Row = he; Row; Row--) /* row counts up, but down may be a little faster :) */
2427 int pixtodo = m_sBit->width;
2428 UTILS::COLOR::Color* pstart = p;
2430 for (int Bit = xfactor * (m_sBit->left + m_RenderInfo.TTFShiftX); Bit > 0; Bit--) /* fill left margin */
2432 for (f = factor-1; f >= 0; f--)
2433 *(p + f*m_RenderInfo.Width) = bgcolor;
2434 p++;
2437 for (Pitch = m_sBit->pitch; Pitch; Pitch--)
2439 for (int Bit = 0x80; Bit; Bit >>= 1)
2441 UTILS::COLOR::Color color;
2443 if (--pixtodo < 0)
2444 break;
2446 if (*sbitbuffer & Bit) /* bit set -> foreground */
2447 color = fgcolor;
2448 else /* bit not set -> background */
2449 color = bgcolor;
2451 for (f = factor-1; f >= 0; f--)
2452 *(p + f*m_RenderInfo.Width) = color;
2453 p++;
2455 if (xfactor > 1) /* double width */
2457 for (f = factor-1; f >= 0; f--)
2458 *(p + f*m_RenderInfo.Width) = color;
2459 p++;
2462 sbitbuffer++;
2464 for (int Bit = (curfontwidth - xfactor*(m_sBit->width + m_sBit->left + m_RenderInfo.TTFShiftX));
2465 Bit > 0; Bit--) /* fill rest of char width */
2467 for (f = factor-1; f >= 0; f--)
2468 *(p + f*m_RenderInfo.Width) = bgcolor;
2469 p++;
2472 p = pstart + factor*m_RenderInfo.Width;
2475 Row = m_Ascender - m_sBit->top + he + m_RenderInfo.TTFShiftY;
2476 FillRect(m_TextureBuffer,
2477 m_RenderInfo.Width,
2478 m_RenderInfo.PosX,
2479 m_RenderInfo.PosY + yoffset + Row * factor,
2480 curfontwidth,
2481 (m_RenderInfo.FontHeight - Row) * factor,
2482 bgcolor); /* fill lower margin */
2484 if (Attribute->underline)
2485 FillRect(m_TextureBuffer,
2486 m_RenderInfo.Width,
2487 m_RenderInfo.PosX,
2488 m_RenderInfo.PosY + yoffset + (m_RenderInfo.FontHeight-2)* factor,
2489 curfontwidth,
2490 2*factor,
2491 fgcolor); /* underline char */
2493 m_RenderInfo.PosX += curfontwidth;
2494 m_RenderInfo.TTFShiftY = backupTTFshiftY; // restore TTFShiftY
2497 int CTeletextDecoder::RenderChar(
2498 UTILS::COLOR::Color* buffer, // pointer to render buffer, min. FontHeight*2*xres
2499 int xres, // length of 1 line in render buffer
2500 int Char, // character to render
2501 int*
2502 pPosX, // left border for rendering relative to *buffer, will be set to right border after rendering
2503 int PosY, // vertical position of char in *buffer
2504 TextPageAttr_t* Attribute, // Attributes of Char
2505 bool zoom, // 1= character will be rendered in double height
2506 int curfontwidth, // rendering width of character
2507 int curfontwidth2, // rendering width of next character (needed for doublewidth)
2508 int FontHeight, // height of character
2509 bool transpmode, // 1= transparent display
2510 unsigned char* axdrcs, // width and height of DRCS-chars
2511 int Ascender) // Ascender of font
2513 UTILS::COLOR::Color bgcolor, fgcolor;
2514 int factor, xfactor;
2516 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
2518 int national_subset_local = m_txtCache->NationalSubset;
2519 int ymosaic[4];
2520 ymosaic[0] = 0; /* y-offsets for 2*3 mosaic */
2521 ymosaic[1] = (FontHeight + 1) / 3;
2522 ymosaic[2] = (FontHeight * 2 + 1) / 3;
2523 ymosaic[3] = FontHeight;
2525 if (Attribute->setX26)
2527 national_subset_local = 0; // no national subset
2530 // G0+G2 set designation
2531 if (Attribute->setG0G2 != 0x3f)
2533 switch (Attribute->setG0G2)
2535 case 0x20 :
2536 national_subset_local = NAT_SC;
2537 break;
2538 case 0x24 :
2539 national_subset_local = NAT_RB;
2540 break;
2541 case 0x25 :
2542 national_subset_local = NAT_UA;
2543 break;
2544 case 0x37:
2545 national_subset_local = NAT_GR;
2546 break;
2547 case 0x55:
2548 national_subset_local = NAT_HB;
2549 break;
2550 case 0x47:
2551 case 0x57:
2552 national_subset_local = NAT_AR;
2553 break;
2554 default:
2555 national_subset_local = CountryConversionTable[Attribute->setG0G2 & 0x07];
2556 break;
2560 if (Attribute->charset == C_G0S) // use secondary charset
2561 national_subset_local = m_txtCache->NationalSubsetSecondary;
2562 if (zoom && Attribute->doubleh)
2563 factor = 4;
2564 else if (zoom || Attribute->doubleh)
2565 factor = 2;
2566 else
2567 factor = 1;
2569 if (Attribute->doublew)
2571 curfontwidth += curfontwidth2;
2572 xfactor = 2;
2574 else
2575 xfactor = 1;
2577 if (Char == 0xFF) /* skip doubleheight chars in lower line */
2579 *pPosX += curfontwidth;
2580 return -1;
2583 /* get colors */
2584 if (Attribute->inverted)
2586 int t = Attribute->fg;
2587 Attribute->fg = Attribute->bg;
2588 Attribute->bg = t;
2590 fgcolor = GetColorRGB((enumTeletextColor)Attribute->fg);
2591 if (transpmode == true && PosY < 24*FontHeight)
2593 bgcolor = GetColorRGB(TXT_ColorTransp);
2595 else
2597 bgcolor = GetColorRGB((enumTeletextColor)Attribute->bg);
2600 /* handle mosaic */
2601 if ((Attribute->charset == C_G1C || Attribute->charset == C_G1S) &&
2602 ((Char&0xA0) == 0x20))
2604 int w1 = (curfontwidth / 2 ) *xfactor;
2605 int w2 = (curfontwidth - w1) *xfactor;
2607 Char = (Char & 0x1f) | ((Char & 0x40) >> 1);
2608 if (Attribute->charset == C_G1S) /* separated mosaic */
2610 for (int y = 0; y < 3; y++)
2612 FillRectMosaicSeparated(buffer, xres,*pPosX, PosY + ymosaic[y]*factor, w1, (ymosaic[y+1] - ymosaic[y])*factor, fgcolor, bgcolor, Char & 0x01);
2613 FillRectMosaicSeparated(buffer, xres,*pPosX + w1, PosY + ymosaic[y]*factor, w2, (ymosaic[y+1] - ymosaic[y])*factor, fgcolor, bgcolor, Char & 0x02);
2614 Char >>= 2;
2617 else
2619 for (int y = 0; y < 3; y++)
2621 FillRect(buffer, xres, *pPosX, PosY + ymosaic[y]*factor, w1, (ymosaic[y+1] - ymosaic[y])*factor, (Char & 0x01) ? fgcolor : bgcolor);
2622 FillRect(buffer, xres, *pPosX + w1, PosY + ymosaic[y]*factor, w2, (ymosaic[y+1] - ymosaic[y])*factor, (Char & 0x02) ? fgcolor : bgcolor);
2623 Char >>= 2;
2627 *pPosX += curfontwidth;
2628 return 0;
2631 if (Attribute->charset == C_G3)
2633 if (Char < 0x20 || Char > 0x7d)
2635 Char = 0x20;
2637 else
2639 if (*aShapes[Char - 0x20] == S_CHR)
2641 unsigned char *p = aShapes[Char - 0x20];
2642 Char = (*(p+1) <<8) + (*(p+2));
2644 else if (*aShapes[Char - 0x20] == S_ADT)
2646 if (buffer)
2648 int x,y,f,c;
2649 UTILS::COLOR::Color* p = buffer + *pPosX + PosY * xres;
2650 for (y=0; y<FontHeight;y++)
2652 for (f=0; f<factor; f++)
2654 for (x=0; x<curfontwidth*xfactor;x++)
2656 c = (y&4 ? (x/3)&1 :((x+3)/3)&1);
2657 *(p+x) = (c ? fgcolor : bgcolor);
2659 p += xres;
2663 *pPosX += curfontwidth;
2664 return 0;
2666 else
2668 DrawShape(buffer, xres,*pPosX, PosY, Char, curfontwidth, FontHeight, factor*FontHeight, fgcolor, bgcolor, true);
2669 *pPosX += curfontwidth;
2670 return 0;
2674 else if (Attribute->charset >= C_OFFSET_DRCS)
2676 TextCachedPage_t *pcache = m_txtCache->astCachetable[(Attribute->charset & 0x10) ? m_txtCache->drcs : m_txtCache->gdrcs][Attribute->charset & 0x0f];
2677 if (pcache)
2679 unsigned char drcs_data[23*40];
2680 auto& components = CServiceBroker::GetAppComponents();
2681 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
2682 appPlayer->LoadPage((Attribute->charset & 0x10) ? m_txtCache->drcs : m_txtCache->gdrcs,
2683 Attribute->charset & 0x0f, drcs_data);
2684 unsigned char *p;
2685 if (Char < 23*2)
2686 p = drcs_data + 20*Char;
2687 else if (pcache->pageinfo.p24)
2688 p = pcache->pageinfo.p24 + 20*(Char - 23*2);
2689 else
2691 FillRect(buffer, xres,*pPosX, PosY, curfontwidth, factor*FontHeight, bgcolor);
2692 *pPosX += curfontwidth;
2693 return 0;
2695 axdrcs[12] = curfontwidth; /* adjust last x-offset according to position, FIXME: double width */
2696 RenderDRCS(xres, p, buffer + *pPosX + PosY * xres, axdrcs, fgcolor, bgcolor);
2698 else
2700 FillRect(buffer,xres,*pPosX, PosY, curfontwidth, factor*FontHeight, bgcolor);
2702 *pPosX += curfontwidth;
2703 return 0;
2705 else if (Attribute->charset == C_G2 && Char >= 0x20 && Char <= 0x7F)
2707 if ((national_subset_local == NAT_SC) || (national_subset_local == NAT_RB) || (national_subset_local == NAT_UA))
2708 Char = G2table[1][Char-0x20];
2709 else if (national_subset_local == NAT_GR)
2710 Char = G2table[2][Char-0x20];
2711 else if (national_subset_local == NAT_AR)
2712 Char = G2table[3][Char-0x20];
2713 else
2714 Char = G2table[0][Char-0x20];
2716 //if (Char == 0x7F)
2718 // FillRect(buffer,xres,*pPosX, PosY, curfontwidth, factor*Ascender, fgcolor);
2719 // FillRect(buffer,xres,*pPosX, PosY + factor*Ascender, curfontwidth, factor*(FontHeight-Ascender), bgcolor);
2720 // *pPosX += curfontwidth;
2721 // return 0;
2724 else if (national_subset_local == NAT_SC && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for serbian/croatian */
2725 Char = G0table[0][Char-0x20];
2726 else if (national_subset_local == NAT_RB && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for russian/bulgarian */
2727 Char = G0table[1][Char-0x20];
2728 else if (national_subset_local == NAT_UA && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for ukrainian */
2729 Char = G0table[2][Char-0x20];
2730 else if (national_subset_local == NAT_GR && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for greek */
2731 Char = G0table[3][Char-0x20];
2732 else if (national_subset_local == NAT_HB && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for hebrew */
2733 Char = G0table[4][Char-0x20];
2734 else if (national_subset_local == NAT_AR && Char >= 0x20 && Char <= 0x7F) /* remap complete areas for arabic */
2735 Char = G0table[5][Char-0x20];
2736 else
2738 /* load char */
2739 switch (Char)
2741 case 0x00:
2742 case 0x20:
2743 FillRect(buffer, xres, *pPosX, PosY, curfontwidth, factor*FontHeight, bgcolor);
2744 *pPosX += curfontwidth;
2745 return -3;
2746 case 0x23:
2747 case 0x24:
2748 Char = nationaltable23[national_subset_local][Char-0x23];
2749 break;
2750 case 0x40:
2751 Char = nationaltable40[national_subset_local];
2752 break;
2753 case 0x5B:
2754 case 0x5C:
2755 case 0x5D:
2756 case 0x5E:
2757 case 0x5F:
2758 case 0x60:
2759 Char = nationaltable5b[national_subset_local][Char-0x5B];
2760 break;
2761 case 0x7B:
2762 case 0x7C:
2763 case 0x7D:
2764 case 0x7E:
2765 Char = nationaltable7b[national_subset_local][Char-0x7B];
2766 break;
2767 case 0x7F:
2768 FillRect(buffer,xres,*pPosX, PosY , curfontwidth, factor*Ascender, fgcolor);
2769 FillRect(buffer,xres,*pPosX, PosY + factor*Ascender, curfontwidth, factor*(FontHeight-Ascender), bgcolor);
2770 *pPosX += curfontwidth;
2771 return 0;
2772 case 0xE0: /* |- */
2773 DrawHLine(buffer,xres,*pPosX, PosY, curfontwidth, fgcolor);
2774 DrawVLine(buffer,xres,*pPosX, PosY +1, FontHeight -1, fgcolor);
2775 FillRect(buffer,xres,*pPosX +1, PosY +1, curfontwidth-1, FontHeight-1, bgcolor);
2776 *pPosX += curfontwidth;
2777 return 0;
2778 case 0xE1: /* - */
2779 DrawHLine(buffer,xres,*pPosX, PosY, curfontwidth, fgcolor);
2780 FillRect(buffer,xres,*pPosX, PosY +1, curfontwidth, FontHeight-1, bgcolor);
2781 *pPosX += curfontwidth;
2782 return 0;
2783 case 0xE2: /* -| */
2784 DrawHLine(buffer,xres,*pPosX, PosY, curfontwidth, fgcolor);
2785 DrawVLine(buffer,xres,*pPosX + curfontwidth -1, PosY +1, FontHeight -1, fgcolor);
2786 FillRect(buffer,xres,*pPosX, PosY +1, curfontwidth-1, FontHeight-1, bgcolor);
2787 *pPosX += curfontwidth;
2788 return 0;
2789 case 0xE3: /* | */
2790 DrawVLine(buffer,xres,*pPosX, PosY, FontHeight, fgcolor);
2791 FillRect(buffer,xres,*pPosX +1, PosY, curfontwidth -1, FontHeight, bgcolor);
2792 *pPosX += curfontwidth;
2793 return 0;
2794 case 0xE4: /* | */
2795 DrawVLine(buffer,xres,*pPosX + curfontwidth -1, PosY, FontHeight, fgcolor);
2796 FillRect(buffer,xres,*pPosX, PosY, curfontwidth -1, FontHeight, bgcolor);
2797 *pPosX += curfontwidth;
2798 return 0;
2799 case 0xE5: /* |_ */
2800 DrawHLine(buffer,xres,*pPosX, PosY + FontHeight -1, curfontwidth, fgcolor);
2801 DrawVLine(buffer,xres,*pPosX, PosY, FontHeight -1, fgcolor);
2802 FillRect(buffer,xres,*pPosX +1, PosY, curfontwidth-1, FontHeight-1, bgcolor);
2803 *pPosX += curfontwidth;
2804 return 0;
2805 case 0xE6: /* _ */
2806 DrawHLine(buffer,xres,*pPosX, PosY + FontHeight -1, curfontwidth, fgcolor);
2807 FillRect(buffer,xres,*pPosX, PosY, curfontwidth, FontHeight-1, bgcolor);
2808 *pPosX += curfontwidth;
2809 return 0;
2810 case 0xE7: /* _| */
2811 DrawHLine(buffer,xres,*pPosX, PosY + FontHeight -1, curfontwidth, fgcolor);
2812 DrawVLine(buffer,xres,*pPosX + curfontwidth -1, PosY, FontHeight -1, fgcolor);
2813 FillRect(buffer,xres,*pPosX, PosY, curfontwidth-1, FontHeight-1, bgcolor);
2814 *pPosX += curfontwidth;
2815 return 0;
2816 case 0xE8: /* Ii */
2817 FillRect(buffer,xres,*pPosX +1, PosY, curfontwidth -1, FontHeight, bgcolor);
2818 for (int Row=0; Row < curfontwidth/2; Row++)
2819 DrawVLine(buffer,xres,*pPosX + Row, PosY + Row, FontHeight - Row, fgcolor);
2820 *pPosX += curfontwidth;
2821 return 0;
2822 case 0xE9: /* II */
2823 FillRect(buffer,xres,*pPosX, PosY, curfontwidth/2, FontHeight, fgcolor);
2824 FillRect(buffer,xres,*pPosX + curfontwidth/2, PosY, (curfontwidth+1)/2, FontHeight, bgcolor);
2825 *pPosX += curfontwidth;
2826 return 0;
2827 case 0xEA: /* ∞ */
2828 FillRect(buffer,xres,*pPosX, PosY, curfontwidth, FontHeight, bgcolor);
2829 FillRect(buffer,xres,*pPosX, PosY, curfontwidth/2, curfontwidth/2, fgcolor);
2830 *pPosX += curfontwidth;
2831 return 0;
2832 case 0xEB: /* ¨ */
2833 FillRect(buffer,xres,*pPosX, PosY +1, curfontwidth, FontHeight -1, bgcolor);
2834 for (int Row=0; Row < curfontwidth/2; Row++)
2835 DrawHLine(buffer,xres,*pPosX + Row, PosY + Row, curfontwidth - Row, fgcolor);
2836 *pPosX += curfontwidth;
2837 return 0;
2838 case 0xEC: /* -- */
2839 FillRect(buffer, xres,*pPosX, PosY, curfontwidth, curfontwidth/2, fgcolor);
2840 FillRect(buffer, xres,*pPosX, PosY + curfontwidth/2, curfontwidth, FontHeight - curfontwidth/2, bgcolor);
2841 *pPosX += curfontwidth;
2842 return 0;
2843 case 0xED:
2844 case 0xEE:
2845 case 0xEF:
2846 case 0xF0:
2847 case 0xF1:
2848 case 0xF2:
2849 case 0xF3:
2850 case 0xF4:
2851 Char = arrowtable[Char - 0xED];
2852 break;
2853 default:
2854 break;
2857 if (Char <= 0x20)
2859 FillRect(buffer, xres, *pPosX, PosY, curfontwidth, factor*FontHeight, bgcolor);
2860 *pPosX += curfontwidth;
2861 return -2;
2863 return Char; // Char is an alphanumeric unicode character
2866 TextPageinfo_t* CTeletextDecoder::DecodePage(bool showl25, // 1=decode Level2.5-graphics
2867 unsigned char* PageChar, // page buffer, min. 25*40
2868 TextPageAttr_t *PageAtrb, // attribute buffer, min 25*40
2869 bool HintMode, // 1=show hidden information
2870 bool showflof) // 1=decode FLOF-line
2872 int col;
2873 int hold, dhset;
2874 int foreground, background, doubleheight, doublewidth, charset, previous_charset, mosaictype, IgnoreAtBlackBgSubst, concealed, flashmode, boxwin;
2875 unsigned char held_mosaic, *p;
2876 TextCachedPage_t *pCachedPage;
2878 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
2880 /* copy page to decode buffer */
2881 if (m_txtCache->SubPageTable[m_txtCache->Page] == 0xff) /* not cached: do nothing */
2882 return NULL;
2884 if (m_txtCache->ZapSubpageManual)
2885 pCachedPage = m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPage];
2886 else
2887 pCachedPage = m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPageTable[m_txtCache->Page]];
2888 if (!pCachedPage) /* not cached: do nothing */
2889 return nullptr;
2891 auto& components = CServiceBroker::GetAppComponents();
2892 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
2893 appPlayer->LoadPage(m_txtCache->Page, m_txtCache->SubPage, &PageChar[40]);
2895 memcpy(&PageChar[8], pCachedPage->p0, 24); /* header line without TimeString */
2897 TextPageinfo_t* PageInfo = &(pCachedPage->pageinfo);
2898 if (PageInfo->p24)
2899 memcpy(&PageChar[24*40], PageInfo->p24, 40); /* line 25 for FLOF */
2901 /* copy TimeString */
2902 memcpy(&PageChar[32], &m_txtCache->TimeString, 8);
2904 bool boxed;
2905 /* check for newsflash & subtitle */
2906 if (PageInfo->boxed && IsDec(m_txtCache->Page))
2907 boxed = true;
2908 else
2909 boxed = false;
2912 /* modify header */
2913 if (boxed)
2915 memset(PageChar, ' ', 40);
2917 else
2919 memset(PageChar, ' ', 8);
2920 CDVDTeletextTools::Hex2Str((char*)PageChar+3, m_txtCache->Page);
2921 if (m_txtCache->SubPage)
2923 *(PageChar+4) ='/';
2924 *(PageChar+5) ='0';
2925 CDVDTeletextTools::Hex2Str((char*)PageChar+6, m_txtCache->SubPage);
2929 if (!IsDec(m_txtCache->Page))
2931 TextPageAttr_t atr = { TXT_ColorWhite , TXT_ColorBlack , C_G0P, 0, 0, 0 ,0, 0, 0, 0, 0, 0, 0, 0x3f};
2932 if (PageInfo->function == FUNC_MOT) /* magazine organization table */
2934 for (col = 0; col < 24*40; col++)
2935 PageAtrb[col] = atr;
2936 for (col = 40; col < 24*40; col++)
2937 PageChar[col] = number2char(PageChar[col]);
2938 return PageInfo; /* don't interpret irregular pages */
2940 else if (PageInfo->function == FUNC_GPOP || PageInfo->function == FUNC_POP) /* object definitions */
2942 for (int col = 0; col < 24*40; col++)
2943 PageAtrb[col] = atr;
2945 p = PageChar + 40;
2946 for (int row = 1; row < 12; row++)
2948 *p++ = number2char(row); /* first column: number (0-9, A-..) */
2949 for (int col = 1; col < 40; col += 3)
2951 int d = CDVDTeletextTools::deh24(p);
2952 if (d < 0)
2954 memcpy(p, "???", 3);
2955 p += 3;
2957 else
2959 *p++ = number2char((d >> 6) & 0x1f); /* mode */
2960 *p++ = number2char(d & 0x3f); /* address */
2961 *p++ = number2char((d >> 11) & 0x7f); /* data */
2965 return PageInfo; /* don't interpret irregular pages */
2967 else if (PageInfo->function == FUNC_GDRCS || PageInfo->function == FUNC_DRCS) /* character definitions */
2969 return PageInfo; /* don't interpret irregular pages */
2971 else
2973 int h, parityerror = 0;
2975 for (int i = 0; i < 8; i++)
2976 PageAtrb[i] = atr;
2978 /* decode parity/hamming */
2979 for (unsigned int i = 40; i < TELETEXT_PAGE_SIZE; i++)
2981 PageAtrb[i] = atr;
2982 p = PageChar + i;
2983 h = dehamming[*p];
2984 if (parityerror && h != 0xFF) /* if no regular page (after any parity error) */
2985 CDVDTeletextTools::Hex2Str((char*)p, h); /* first try dehamming */
2986 else
2988 if (*p == ' ' || deparity[*p] != ' ') /* correct parity */
2989 *p &= 127;
2990 else
2992 parityerror = 1;
2993 if (h != 0xFF) /* first parity error: try dehamming */
2994 CDVDTeletextTools::Hex2Str((char*)p, h);
2995 else
2996 *p = ' ';
3000 if (parityerror)
3002 return PageInfo; /* don't interpret irregular pages */
3006 int mosaic_pending,esc_pending;
3007 /* decode */
3008 for (int row = 0; row < ((showflof && PageInfo->p24) ? 25 : 24); row++)
3010 /* start-of-row default conditions */
3011 foreground = TXT_ColorWhite;
3012 background = TXT_ColorBlack;
3013 doubleheight = 0;
3014 doublewidth = 0;
3015 charset = previous_charset = C_G0P; // remember charset for switching back after mosaic charset was used
3016 mosaictype = 0;
3017 concealed = 0;
3018 flashmode = 0;
3019 hold = 0;
3020 boxwin = 0;
3021 held_mosaic = ' ';
3022 dhset = 0;
3023 IgnoreAtBlackBgSubst = 0;
3024 mosaic_pending = esc_pending = 0; // we need to render at least one mosaic char if 'esc' is received immediately after mosaic charset switch on
3026 if (boxed && memchr(&PageChar[row*40], start_box, 40) == 0)
3028 foreground = TXT_ColorTransp;
3029 background = TXT_ColorTransp;
3032 for (int col = 0; col < 40; col++)
3034 int index = row*40 + col;
3036 PageAtrb[index].fg = foreground;
3037 PageAtrb[index].bg = background;
3038 PageAtrb[index].charset = charset;
3039 PageAtrb[index].doubleh = doubleheight;
3040 PageAtrb[index].doublew = (col < 39 ? doublewidth : 0);
3041 PageAtrb[index].IgnoreAtBlackBgSubst = IgnoreAtBlackBgSubst;
3042 PageAtrb[index].concealed = concealed;
3043 PageAtrb[index].flashing = flashmode;
3044 PageAtrb[index].boxwin = boxwin;
3045 PageAtrb[index].inverted = 0; // only relevant for Level 2.5
3046 PageAtrb[index].underline = 0; // only relevant for Level 2.5
3047 PageAtrb[index].diacrit = 0; // only relevant for Level 2.5
3048 PageAtrb[index].setX26 = 0; // only relevant for Level 2.5
3049 PageAtrb[index].setG0G2 = 0x3f; // only relevant for Level 2.5
3051 if (PageChar[index] < ' ')
3053 if (esc_pending) { // mosaic char has been rendered and we can switch charsets
3054 charset = previous_charset;
3055 if (charset == C_G0P)
3056 charset = previous_charset = C_G0S;
3057 else if (charset == C_G0S)
3058 charset = previous_charset = C_G0P;
3059 esc_pending = 0;
3061 switch (PageChar[index])
3063 case alpha_black:
3064 case alpha_red:
3065 case alpha_green:
3066 case alpha_yellow:
3067 case alpha_blue:
3068 case alpha_magenta:
3069 case alpha_cyan:
3070 case alpha_white:
3071 concealed = 0;
3072 foreground = PageChar[index] - alpha_black + TXT_ColorBlack;
3073 if (col == 0 && PageChar[index] == alpha_white)
3074 PageAtrb[index].fg = TXT_ColorBlack; // indicate level 1 color change on column 0; (hack)
3075 if ((charset!=C_G0P) && (charset!=C_G0S)) // we need to change charset to state it was before mosaic
3076 charset = previous_charset;
3077 break;
3079 case flash:
3080 flashmode = 1;
3081 break;
3083 case steady:
3084 flashmode = 0;
3085 PageAtrb[index].flashing = 0;
3086 break;
3088 case end_box:
3089 boxwin = 0;
3090 IgnoreAtBlackBgSubst = 0;
3091 break;
3093 case start_box:
3094 if (!boxwin)
3095 boxwin = 1;
3096 break;
3098 case normal_size:
3099 doubleheight = 0;
3100 doublewidth = 0;
3101 PageAtrb[index].doubleh = doubleheight;
3102 PageAtrb[index].doublew = doublewidth;
3103 break;
3105 case double_height:
3106 if (row < 23)
3108 doubleheight = 1;
3109 dhset = 1;
3111 doublewidth = 0;
3113 break;
3115 case double_width:
3116 if (col < 39)
3117 doublewidth = 1;
3118 doubleheight = 0;
3119 break;
3121 case double_size:
3122 if (row < 23)
3124 doubleheight = 1;
3125 dhset = 1;
3127 if (col < 39)
3128 doublewidth = 1;
3129 break;
3131 case mosaic_black:
3132 case mosaic_red:
3133 case mosaic_green:
3134 case mosaic_yellow:
3135 case mosaic_blue:
3136 case mosaic_magenta:
3137 case mosaic_cyan:
3138 case mosaic_white:
3139 concealed = 0;
3140 foreground = PageChar[index] - mosaic_black + TXT_ColorBlack;
3141 if ((charset==C_G0P) || (charset==C_G0S))
3142 previous_charset=charset;
3143 charset = mosaictype ? C_G1S : C_G1C;
3144 mosaic_pending = 1;
3145 break;
3147 case conceal:
3148 PageAtrb[index].concealed = 1;
3149 concealed = 1;
3150 if (!HintMode)
3152 foreground = background;
3153 PageAtrb[index].fg = foreground;
3155 break;
3157 case contiguous_mosaic:
3158 mosaictype = 0;
3159 if (charset == C_G1S)
3161 charset = C_G1C;
3162 PageAtrb[index].charset = charset;
3164 break;
3166 case separated_mosaic:
3167 mosaictype = 1;
3168 if (charset == C_G1C)
3170 charset = C_G1S;
3171 PageAtrb[index].charset = charset;
3173 break;
3175 case esc:
3176 if (!mosaic_pending) { // if mosaic is pending we need to wait before mosaic arrives
3177 if ((charset != C_G0P) && (charset != C_G0S)) // we need to switch to charset which was active before mosaic
3178 charset = previous_charset;
3179 if (charset == C_G0P)
3180 charset = previous_charset = C_G0S;
3181 else if (charset == C_G0S)
3182 charset = previous_charset = C_G0P;
3183 } else esc_pending = 1;
3184 break;
3186 case black_background:
3187 background = TXT_ColorBlack;
3188 IgnoreAtBlackBgSubst = 0;
3189 PageAtrb[index].bg = background;
3190 PageAtrb[index].IgnoreAtBlackBgSubst = IgnoreAtBlackBgSubst;
3191 break;
3193 case new_background:
3194 background = foreground;
3195 if (background == TXT_ColorBlack)
3196 IgnoreAtBlackBgSubst = 1;
3197 else
3198 IgnoreAtBlackBgSubst = 0;
3199 PageAtrb[index].bg = background;
3200 PageAtrb[index].IgnoreAtBlackBgSubst = IgnoreAtBlackBgSubst;
3201 break;
3203 case hold_mosaic:
3204 hold = 1;
3205 break;
3207 case release_mosaic:
3208 hold = 2;
3209 break;
3212 /* handle spacing attributes */
3213 if (hold && (PageAtrb[index].charset == C_G1C || PageAtrb[index].charset == C_G1S))
3214 PageChar[index] = held_mosaic;
3215 else
3216 PageChar[index] = ' ';
3218 if (hold == 2)
3219 hold = 0;
3221 else /* char >= ' ' */
3223 mosaic_pending = 0; // charset will be switched next if esc_pending
3224 /* set new held-mosaic char */
3225 if ((charset == C_G1C || charset == C_G1S) &&
3226 ((PageChar[index]&0xA0) == 0x20))
3227 held_mosaic = PageChar[index];
3228 if (PageAtrb[index].doubleh)
3229 PageChar[index + 40] = 0xFF;
3232 if (!(charset == C_G1C || charset == C_G1S))
3233 held_mosaic = ' '; /* forget if outside mosaic */
3235 } /* for col */
3237 /* skip row if doubleheight */
3238 if (row < 23 && dhset)
3240 for (int col = 0; col < 40; col++)
3242 int index = row*40 + col;
3243 PageAtrb[index+40].bg = PageAtrb[index].bg;
3244 PageAtrb[index+40].fg = TXT_ColorWhite;
3245 if (!PageAtrb[index].doubleh)
3246 PageChar[index+40] = ' ';
3247 PageAtrb[index+40].flashing = 0;
3248 PageAtrb[index+40].charset = C_G0P;
3249 PageAtrb[index+40].doubleh = 0;
3250 PageAtrb[index+40].doublew = 0;
3251 PageAtrb[index+40].IgnoreAtBlackBgSubst = 0;
3252 PageAtrb[index+40].concealed = 0;
3253 PageAtrb[index+40].flashing = 0;
3254 PageAtrb[index+40].boxwin = PageAtrb[index].boxwin;
3256 row++;
3258 } /* for row */
3259 m_txtCache->FullScrColor = TXT_ColorBlack;
3261 if (showl25)
3262 Eval_l25(PageChar, PageAtrb, HintMode);
3264 /* handle Black Background Color Substitution and transparency (CLUT1#0) */
3266 int o = 0;
3267 char bitmask ;
3269 for (unsigned char row : m_txtCache->FullRowColor)
3271 for (int c = 0; c < 40; c++)
3273 bitmask = (PageAtrb[o].bg == 0x08 ? 0x08 : 0x00) | (row == 0x08 ? 0x04 : 0x00) | (PageAtrb[o].boxwin <<1) | (int)boxed;
3274 switch (bitmask)
3276 case 0x08:
3277 case 0x0b:
3278 if (row == 0x08)
3279 PageAtrb[o].bg = m_txtCache->FullScrColor;
3280 else
3281 PageAtrb[o].bg = row;
3282 break;
3283 case 0x01:
3284 case 0x05:
3285 case 0x09:
3286 case 0x0a:
3287 case 0x0c:
3288 case 0x0d:
3289 case 0x0e:
3290 case 0x0f:
3291 PageAtrb[o].bg = TXT_ColorTransp;
3292 break;
3294 bitmask = (PageAtrb[o].fg == 0x08 ? 0x08 : 0x00) | (row == 0x08 ? 0x04 : 0x00) | (PageAtrb[o].boxwin <<1) | (int)boxed;
3295 switch (bitmask)
3297 case 0x08:
3298 case 0x0b:
3299 if (row == 0x08)
3300 PageAtrb[o].fg = m_txtCache->FullScrColor;
3301 else
3302 PageAtrb[o].fg = row;
3303 break;
3304 case 0x01:
3305 case 0x05:
3306 case 0x09:
3307 case 0x0a:
3308 case 0x0c:
3309 case 0x0d:
3310 case 0x0e:
3311 case 0x0f:
3312 PageAtrb[o].fg = TXT_ColorTransp;
3313 break;
3315 o++;
3319 return PageInfo;
3322 void CTeletextDecoder::Eval_l25(unsigned char* PageChar, TextPageAttr_t *PageAtrb, bool HintMode)
3324 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
3326 memset(m_txtCache->FullRowColor, 0, sizeof(m_txtCache->FullRowColor));
3327 m_txtCache->FullScrColor = TXT_ColorBlack;
3328 m_txtCache->ColorTable = NULL;
3330 if (!m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPage])
3331 return;
3333 /* normal page */
3334 if (IsDec(m_txtCache->Page))
3336 unsigned char APx0, APy0, APx, APy;
3337 TextPageinfo_t *pi = &(m_txtCache->astCachetable[m_txtCache->Page][m_txtCache->SubPage]->pageinfo);
3338 TextCachedPage_t *pmot = m_txtCache->astCachetable[(m_txtCache->Page & 0xf00) | 0xfe][0];
3339 int p26Received = 0;
3340 int BlackBgSubst = 0;
3341 int ColorTableRemapping = 0;
3343 m_txtCache->pop = m_txtCache->gpop = m_txtCache->drcs = m_txtCache->gdrcs = 0;
3345 if (pi->ext)
3347 TextExtData_t *e = pi->ext;
3349 if (e->p26[0])
3350 p26Received = 1;
3352 if (e->p27)
3354 Textp27_t *p27 = e->p27;
3355 if (p27[0].l25)
3356 m_txtCache->gpop = p27[0].page;
3357 if (p27[1].l25)
3358 m_txtCache->pop = p27[1].page;
3359 if (p27[2].l25)
3360 m_txtCache->gdrcs = p27[2].page;
3361 if (p27[3].l25)
3362 m_txtCache->drcs = p27[3].page;
3365 if (e->p28Received)
3367 m_txtCache->ColorTable = e->bgr;
3368 BlackBgSubst = e->BlackBgSubst;
3369 ColorTableRemapping = e->ColorTableRemapping;
3370 memset(m_txtCache->FullRowColor, e->DefRowColor, sizeof(m_txtCache->FullRowColor));
3371 m_txtCache->FullScrColor = e->DefScreenColor;
3372 m_txtCache->NationalSubset = SetNational(e->DefaultCharset);
3373 m_txtCache->NationalSubsetSecondary = SetNational(e->SecondCharset);
3374 } /* e->p28Received */
3377 if (!m_txtCache->ColorTable && m_txtCache->astP29[m_txtCache->Page >> 8])
3379 TextExtData_t *e = m_txtCache->astP29[m_txtCache->Page >> 8];
3380 m_txtCache->ColorTable = e->bgr;
3381 BlackBgSubst = e->BlackBgSubst;
3382 ColorTableRemapping = e->ColorTableRemapping;
3383 memset(m_txtCache->FullRowColor, e->DefRowColor, sizeof(m_txtCache->FullRowColor));
3384 m_txtCache->FullScrColor = e->DefScreenColor;
3385 m_txtCache->NationalSubset = SetNational(e->DefaultCharset);
3386 m_txtCache->NationalSubsetSecondary = SetNational(e->SecondCharset);
3389 if (ColorTableRemapping)
3391 for (int i = 0; i < 25*40; i++)
3393 PageAtrb[i].fg += MapTblFG[ColorTableRemapping - 1];
3394 if (!BlackBgSubst || PageAtrb[i].bg != TXT_ColorBlack || PageAtrb[i].IgnoreAtBlackBgSubst)
3395 PageAtrb[i].bg += MapTblBG[ColorTableRemapping - 1];
3399 /* determine ?pop/?drcs from MOT */
3400 if (pmot)
3402 unsigned char pmot_data[23*40];
3403 auto& components = CServiceBroker::GetAppComponents();
3404 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
3405 appPlayer->LoadPage((m_txtCache->Page & 0xf00) | 0xfe, 0, pmot_data);
3407 unsigned char *p = pmot_data; /* start of link data */
3408 int o = 2 * (((m_txtCache->Page & 0xf0) >> 4) * 10 + (m_txtCache->Page & 0x0f)); /* offset of links for current page */
3409 int opop = p[o] & 0x07; /* index of POP link */
3410 int odrcs = p[o+1] & 0x07; /* index of DRCS link */
3411 unsigned char obj[3*4*4]; /* types* objects * (triplet,packet,subp,high) */
3412 unsigned char type,ct, tstart = 4*4;
3413 memset(obj,0,sizeof(obj));
3415 if (p[o] & 0x08) /* GPOP data used */
3417 if (!m_txtCache->gpop || !(p[18*40] & 0x08)) /* no p27 data or higher prio of MOT link */
3419 m_txtCache->gpop = ((p[18*40] << 8) | (p[18*40+1] << 4) | p[18*40+2]) & 0x7ff;
3420 if ((m_txtCache->gpop & 0xff) == 0xff)
3421 m_txtCache->gpop = 0;
3422 else
3424 if (m_txtCache->gpop < 0x100)
3425 m_txtCache->gpop += 0x800;
3426 if (!p26Received)
3428 ct = 2;
3429 while (ct)
3431 ct--;
3432 type = (p[18*40+5] >> 2*ct) & 0x03;
3434 if (type == 0) continue;
3435 obj[(type-1)*(tstart)+ct*4 ] = 3 * ((p[18*40+7+ct*2] >> 1) & 0x03) + type; //triplet
3436 obj[(type-1)*(tstart)+ct*4+1] = ((p[18*40+7+ct*2] & 0x08) >> 3) + 1 ; //packet
3437 obj[(type-1)*(tstart)+ct*4+2] = p[18*40+6+ct*2] & 0x0f ; //subp
3438 obj[(type-1)*(tstart)+ct*4+3] = p[18*40+7+ct*2] & 0x01 ; //high
3444 if (opop) /* POP data used */
3446 opop = 18*40 + 10*opop; /* offset to POP link */
3447 if (!m_txtCache->pop || !(p[opop] & 0x08)) /* no p27 data or higher prio of MOT link */
3449 m_txtCache->pop = ((p[opop] << 8) | (p[opop+1] << 4) | p[opop+2]) & 0x7ff;
3450 if ((m_txtCache->pop & 0xff) == 0xff)
3451 m_txtCache->pop = 0;
3452 else
3454 if (m_txtCache->pop < 0x100)
3455 m_txtCache->pop += 0x800;
3456 if (!p26Received)
3458 ct = 2;
3459 while (ct)
3461 ct--;
3462 type = (p[opop+5] >> 2*ct) & 0x03;
3464 if (type == 0) continue;
3465 obj[(type-1)*(tstart)+(ct+2)*4 ] = 3 * ((p[opop+7+ct*2] >> 1) & 0x03) + type; //triplet
3466 obj[(type-1)*(tstart)+(ct+2)*4+1] = ((p[opop+7+ct*2] & 0x08) >> 3) + 1 ; //packet
3467 obj[(type-1)*(tstart)+(ct+2)*4+2] = p[opop+6+ct*2] ; //subp
3468 obj[(type-1)*(tstart)+(ct+2)*4+3] = p[opop+7+ct*2] & 0x01 ; //high
3474 // eval default objects in correct order
3475 for (int i = 0; i < 12; i++)
3477 if (obj[i*4] != 0)
3479 APx0 = APy0 = APx = APy = m_txtCache->tAPx = m_txtCache->tAPy = 0;
3480 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);
3484 if (p[o+1] & 0x08) /* GDRCS data used */
3486 if (!m_txtCache->gdrcs || !(p[20*40] & 0x08)) /* no p27 data or higher prio of MOT link */
3488 m_txtCache->gdrcs = ((p[20*40] << 8) | (p[20*40+1] << 4) | p[20*40+2]) & 0x7ff;
3489 if ((m_txtCache->gdrcs & 0xff) == 0xff)
3490 m_txtCache->gdrcs = 0;
3491 else if (m_txtCache->gdrcs < 0x100)
3492 m_txtCache->gdrcs += 0x800;
3495 if (odrcs) /* DRCS data used */
3497 odrcs = 20*40 + 4*odrcs; /* offset to DRCS link */
3498 if (!m_txtCache->drcs || !(p[odrcs] & 0x08)) /* no p27 data or higher prio of MOT link */
3500 m_txtCache->drcs = ((p[odrcs] << 8) | (p[odrcs+1] << 4) | p[odrcs+2]) & 0x7ff;
3501 if ((m_txtCache->drcs & 0xff) == 0xff)
3502 m_txtCache->drcs = 0;
3503 else if (m_txtCache->drcs < 0x100)
3504 m_txtCache->drcs += 0x800;
3507 if (m_txtCache->astCachetable[m_txtCache->gpop][0])
3508 m_txtCache->astCachetable[m_txtCache->gpop][0]->pageinfo.function = FUNC_GPOP;
3509 if (m_txtCache->astCachetable[m_txtCache->pop][0])
3510 m_txtCache->astCachetable[m_txtCache->pop][0]->pageinfo.function = FUNC_POP;
3511 if (m_txtCache->astCachetable[m_txtCache->gdrcs][0])
3512 m_txtCache->astCachetable[m_txtCache->gdrcs][0]->pageinfo.function = FUNC_GDRCS;
3513 if (m_txtCache->astCachetable[m_txtCache->drcs][0])
3514 m_txtCache->astCachetable[m_txtCache->drcs][0]->pageinfo.function = FUNC_DRCS;
3515 } /* if mot */
3517 /* evaluate local extension data from p26 */
3518 if (p26Received)
3520 APx0 = APy0 = APx = APy = m_txtCache->tAPx = m_txtCache->tAPy = 0;
3521 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 */
3525 int o = 0;
3526 for (unsigned char row : m_txtCache->FullRowColor)
3528 for (int c = 0; c < 40; c++)
3530 if (BlackBgSubst && PageAtrb[o].bg == TXT_ColorBlack && !(PageAtrb[o].IgnoreAtBlackBgSubst))
3532 if (row == 0x08)
3533 PageAtrb[o].bg = m_txtCache->FullScrColor;
3534 else
3535 PageAtrb[o].bg = row;
3537 o++;
3542 if (!HintMode)
3544 for (int i = 0; i < 25*40; i++)
3546 if (PageAtrb[i].concealed) PageAtrb[i].fg = PageAtrb[i].bg;
3549 } /* is_dec(page) */
3552 /* dump interpreted object data to stdout */
3553 /* in: 18 bit object data */
3554 /* out: termination info, >0 if end of object */
3555 void CTeletextDecoder::Eval_Object(int iONr, TextCachedPage_t *pstCachedPage,
3556 unsigned char *pAPx, unsigned char *pAPy,
3557 unsigned char *pAPx0, unsigned char *pAPy0,
3558 tObjType ObjType, unsigned char* pagedata, unsigned char* PageChar, TextPageAttr_t* PageAtrb)
3560 int iOData;
3561 int iONr1 = iONr + 1; /* don't terminate after first triplet */
3562 unsigned char drcssubp=0, gdrcssubp=0;
3563 signed char endcol = -1; /* last column to which to extend attribute changes */
3564 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 */
3568 iOData = iTripletNumber2Data(iONr, pstCachedPage, pagedata); /* get triplet data, next triplet */
3569 if (iOData < 0) /* invalid number, not cached, or hamming error: terminate */
3570 break;
3572 if (endcol < 0)
3574 if (ObjType == OBJ_ACTIVE)
3576 endcol = 40;
3578 else if (ObjType == OBJ_ADAPTIVE) /* search end of line */
3580 for (int i = iONr; i <= 506; i++)
3582 int iTempOData = iTripletNumber2Data(i, pstCachedPage, pagedata); /* get triplet data, next triplet */
3583 int iAddress = (iTempOData ) & 0x3f;
3584 int iMode = (iTempOData >> 6) & 0x1f;
3585 //int iData = (iTempOData >> 11) & 0x7f;
3586 if (iTempOData < 0 || /* invalid number, not cached, or hamming error: terminate */
3587 (iAddress >= 40 /* new row: row address and */
3588 && (iMode == 0x01 || /* Full Row Color or */
3589 iMode == 0x04 || /* Set Active Position */
3590 (iMode >= 0x15 && iMode <= 0x17) || /* Object Definition */
3591 iMode == 0x17))) /* Object Termination */
3592 break;
3593 if (iAddress < 40 && iMode != 0x06)
3594 endcol = iAddress;
3598 iONr++;
3600 while (0 == Eval_Triplet(iOData, pstCachedPage, pAPx, pAPy, pAPx0, pAPy0, &drcssubp, &gdrcssubp, &endcol, &attrPassive, pagedata, PageChar, PageAtrb) || iONr1 == iONr); /* repeat until termination reached */
3603 void CTeletextDecoder::Eval_NumberedObject(int p, int s, int packet, int triplet, int high,
3604 unsigned char *pAPx, unsigned char *pAPy,
3605 unsigned char *pAPx0, unsigned char *pAPy0, unsigned char* PageChar, TextPageAttr_t* PageAtrb)
3607 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
3609 if (!packet || 0 == m_txtCache->astCachetable[p][s])
3610 return;
3612 unsigned char pagedata[23*40];
3613 auto& components = CServiceBroker::GetAppComponents();
3614 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
3615 appPlayer->LoadPage(p, s, pagedata);
3617 int idata = CDVDTeletextTools::deh24(pagedata + 40*(packet-1) + 1 + 3*triplet);
3618 int iONr;
3620 if (idata < 0) /* hamming error: ignore triplet */
3621 return;
3622 if (high)
3623 iONr = idata >> 9; /* triplet number of odd object data */
3624 else
3625 iONr = idata & 0x1ff; /* triplet number of even object data */
3626 if (iONr <= 506)
3628 Eval_Object(iONr, m_txtCache->astCachetable[p][s], pAPx, pAPy, pAPx0, pAPy0, (tObjType)(triplet % 3),pagedata, PageChar, PageAtrb);
3632 int CTeletextDecoder::Eval_Triplet(int iOData, TextCachedPage_t *pstCachedPage,
3633 unsigned char *pAPx, unsigned char *pAPy,
3634 unsigned char *pAPx0, unsigned char *pAPy0,
3635 unsigned char *drcssubp, unsigned char *gdrcssubp,
3636 signed char *endcol, TextPageAttr_t *attrPassive, unsigned char* pagedata, unsigned char* PageChar, TextPageAttr_t* PageAtrb)
3638 int iAddress = (iOData ) & 0x3f;
3639 int iMode = (iOData >> 6) & 0x1f;
3640 int iData = (iOData >> 11) & 0x7f;
3642 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
3644 if (iAddress < 40) /* column addresses */
3646 int offset; /* offset to PageChar and PageAtrb */
3648 if (iMode != 0x06)
3649 *pAPx = iAddress; /* new Active Column */
3650 offset = (*pAPy0 + *pAPy) * 40 + *pAPx0 + *pAPx; /* offset to PageChar and PageAtrb */
3652 switch (iMode)
3654 case 0x00:
3655 if (0 == (iData>>5))
3657 int newcolor = iData & 0x1f;
3658 if (*endcol < 0) /* passive object */
3659 attrPassive->fg = newcolor;
3660 else if (*endcol == 40) /* active object */
3662 TextPageAttr_t *p = &PageAtrb[offset];
3663 int oldcolor = (p)->fg; /* current color (set-after) */
3664 int c = *pAPx0 + *pAPx; /* current column absolute */
3667 p->fg = newcolor;
3668 p++;
3669 c++;
3670 } while (c < 40 && p->fg == oldcolor); /* stop at change by level 1 page */
3672 else /* adaptive object */
3674 TextPageAttr_t *p = &PageAtrb[offset];
3675 int c = *pAPx; /* current column relative to object origin */
3678 p->fg = newcolor;
3679 p++;
3680 c++;
3681 } while (c <= *endcol);
3684 break;
3685 case 0x01:
3686 if (iData >= 0x20)
3688 PageChar[offset] = iData;
3689 if (*endcol < 0) /* passive object */
3691 attrPassive->charset = C_G1C; /* FIXME: separated? */
3692 PageAtrb[offset] = *attrPassive;
3694 else if (PageAtrb[offset].charset != C_G1S)
3695 PageAtrb[offset].charset = C_G1C; /* FIXME: separated? */
3697 break;
3698 case 0x02:
3699 case 0x0b:
3700 PageChar[offset] = iData;
3701 if (*endcol < 0) /* passive object */
3703 attrPassive->charset = C_G3;
3704 PageAtrb[offset] = *attrPassive;
3706 else
3707 PageAtrb[offset].charset = C_G3;
3708 break;
3709 case 0x03:
3710 if (0 == (iData>>5))
3712 int newcolor = iData & 0x1f;
3713 if (*endcol < 0) /* passive object */
3714 attrPassive->bg = newcolor;
3715 else if (*endcol == 40) /* active object */
3717 TextPageAttr_t *p = &PageAtrb[offset];
3718 int oldcolor = (p)->bg; /* current color (set-after) */
3719 int c = *pAPx0 + *pAPx; /* current column absolute */
3722 p->bg = newcolor;
3723 if (newcolor == TXT_ColorBlack)
3724 p->IgnoreAtBlackBgSubst = 1;
3725 p++;
3726 c++;
3727 } while (c < 40 && p->bg == oldcolor); /* stop at change by level 1 page */
3729 else /* adaptive object */
3731 TextPageAttr_t *p = &PageAtrb[offset];
3732 int c = *pAPx; /* current column relative to object origin */
3735 p->bg = newcolor;
3736 if (newcolor == TXT_ColorBlack)
3737 p->IgnoreAtBlackBgSubst = 1;
3738 p++;
3739 c++;
3740 } while (c <= *endcol);
3743 break;
3744 case 0x06:
3745 /* ignore */
3746 break;
3747 case 0x07:
3748 if ((iData & 0x60) != 0) break; // reserved data field
3749 if (*endcol < 0) /* passive object */
3751 attrPassive->flashing=iData & 0x1f;
3752 PageAtrb[offset] = *attrPassive;
3754 else
3755 PageAtrb[offset].flashing=iData & 0x1f;
3756 break;
3757 case 0x08:
3758 if (*endcol < 0) /* passive object */
3760 attrPassive->setG0G2=iData & 0x3f;
3761 PageAtrb[offset] = *attrPassive;
3763 else
3764 PageAtrb[offset].setG0G2=iData & 0x3f;
3765 break;
3766 case 0x09:
3767 PageChar[offset] = iData;
3768 if (*endcol < 0) /* passive object */
3770 attrPassive->charset = C_G0P; /* FIXME: secondary? */
3771 attrPassive->setX26 = 1;
3772 PageAtrb[offset] = *attrPassive;
3774 else
3776 PageAtrb[offset].charset = C_G0P; /* FIXME: secondary? */
3777 PageAtrb[offset].setX26 = 1;
3779 break;
3780 // case 0x0b: (see 0x02)
3781 case 0x0c:
3783 int conc = (iData & 0x04);
3784 int inv = (iData & 0x10);
3785 int dw = (iData & 0x40 ?1:0);
3786 int dh = (iData & 0x01 ?1:0);
3787 int sep = (iData & 0x20);
3788 int bw = (iData & 0x02 ?1:0);
3789 if (*endcol < 0) /* passive object */
3791 if (conc)
3793 attrPassive->concealed = 1;
3794 attrPassive->fg = attrPassive->bg;
3796 attrPassive->inverted = (inv ? 1- attrPassive->inverted : 0);
3797 attrPassive->doubleh = dh;
3798 attrPassive->doublew = dw;
3799 attrPassive->boxwin = bw;
3800 if (bw) attrPassive->IgnoreAtBlackBgSubst = 0;
3801 if (sep)
3803 if (attrPassive->charset == C_G1C)
3804 attrPassive->charset = C_G1S;
3805 else
3806 attrPassive->underline = 1;
3808 else
3810 if (attrPassive->charset == C_G1S)
3811 attrPassive->charset = C_G1C;
3812 else
3813 attrPassive->underline = 0;
3816 else
3819 int c = *pAPx0 + (*endcol == 40 ? *pAPx : 0); /* current column */
3820 TextPageAttr_t *p = &PageAtrb[offset];
3823 p->inverted = (inv ? 1- p->inverted : 0);
3824 if (conc)
3826 p->concealed = 1;
3827 p->fg = p->bg;
3829 if (sep)
3831 if (p->charset == C_G1C)
3832 p->charset = C_G1S;
3833 else
3834 p->underline = 1;
3836 else
3838 if (p->charset == C_G1S)
3839 p->charset = C_G1C;
3840 else
3841 p->underline = 0;
3843 p->doublew = dw;
3844 p->doubleh = dh;
3845 p->boxwin = bw;
3846 if (bw) p->IgnoreAtBlackBgSubst = 0;
3847 p++;
3848 c++;
3849 } while (c < *endcol);
3851 break;
3853 case 0x0d:
3854 PageChar[offset] = iData & 0x3f;
3855 if (*endcol < 0) /* passive object */
3857 attrPassive->charset = C_OFFSET_DRCS + ((iData & 0x40) ? (0x10 + *drcssubp) : *gdrcssubp);
3858 PageAtrb[offset] = *attrPassive;
3860 else
3861 PageAtrb[offset].charset = C_OFFSET_DRCS + ((iData & 0x40) ? (0x10 + *drcssubp) : *gdrcssubp);
3862 break;
3863 case 0x0f:
3864 PageChar[offset] = iData;
3865 if (*endcol < 0) /* passive object */
3867 attrPassive->charset = C_G2;
3868 PageAtrb[offset] = *attrPassive;
3870 else
3871 PageAtrb[offset].charset = C_G2;
3872 break;
3873 default:
3874 if (iMode == 0x10 && iData == 0x2a)
3875 iData = '@';
3876 if (iMode >= 0x10)
3878 PageChar[offset] = iData;
3879 if (*endcol < 0) /* passive object */
3881 attrPassive->charset = C_G0P;
3882 attrPassive->diacrit = iMode & 0x0f;
3883 attrPassive->setX26 = 1;
3884 PageAtrb[offset] = *attrPassive;
3886 else
3888 PageAtrb[offset].charset = C_G0P;
3889 PageAtrb[offset].diacrit = iMode & 0x0f;
3890 PageAtrb[offset].setX26 = 1;
3893 break; /* unsupported or not yet implemented mode: ignore */
3894 } /* switch (iMode) */
3896 else /* ================= (iAddress >= 40): row addresses ====================== */
3898 switch (iMode)
3900 case 0x00:
3901 if (0 == (iData>>5))
3903 m_txtCache->FullScrColor = iData & 0x1f;
3905 break;
3906 case 0x01:
3907 if (*endcol == 40) /* active object */
3909 *pAPy = RowAddress2Row(iAddress); /* new Active Row */
3911 int color = iData & 0x1f;
3912 int row = *pAPy0 + *pAPy;
3913 int maxrow;
3915 if (row <= 24 && 0 == (iData>>5))
3916 maxrow = row;
3917 else if (3 == (iData>>5))
3918 maxrow = 24;
3919 else
3920 maxrow = -1;
3921 for (; row <= maxrow; row++)
3922 m_txtCache->FullRowColor[row] = color;
3923 *endcol = -1;
3925 break;
3926 case 0x04:
3927 *pAPy = RowAddress2Row(iAddress); /* new Active Row */
3928 if (iData < 40)
3929 *pAPx = iData; /* new Active Column */
3930 *endcol = -1; /* FIXME: check if row changed? */
3931 break;
3932 case 0x07:
3933 if (iAddress == 0x3f)
3935 *pAPx = *pAPy = 0; /* new Active Position 0,0 */
3936 if (*endcol == 40) /* active object */
3938 int color = iData & 0x1f;
3939 int row = *pAPy0; // + *pAPy;
3940 int maxrow;
3942 if (row <= 24 && 0 == (iData>>5))
3943 maxrow = row;
3944 else if (3 == (iData>>5))
3945 maxrow = 24;
3946 else
3947 maxrow = -1;
3948 for (; row <= maxrow; row++)
3949 m_txtCache->FullRowColor[row] = color;
3951 *endcol = -1;
3953 break;
3954 case 0x08:
3955 case 0x09:
3956 case 0x0a:
3957 case 0x0b:
3958 case 0x0c:
3959 case 0x0d:
3960 case 0x0e:
3961 case 0x0f:
3962 /* ignore */
3963 break;
3964 case 0x10:
3965 m_txtCache->tAPy = iAddress - 40;
3966 m_txtCache->tAPx = iData;
3967 break;
3968 case 0x11:
3969 case 0x12:
3970 case 0x13:
3971 if (iAddress & 0x10) /* POP or GPOP */
3973 unsigned char APx = 0, APy = 0;
3974 unsigned char APx0 = *pAPx0 + *pAPx + m_txtCache->tAPx, APy0 = *pAPy0 + *pAPy + m_txtCache->tAPy;
3975 int triplet = 3 * ((iData >> 5) & 0x03) + (iMode & 0x03);
3976 int packet = (iAddress & 0x03) + 1;
3977 int subp = iData & 0x0f;
3978 int high = (iData >> 4) & 0x01;
3981 if (APx0 < 40) /* not in side panel */
3983 Eval_NumberedObject((iAddress & 0x08) ? m_txtCache->gpop : m_txtCache->pop, subp, packet, triplet, high, &APx, &APy, &APx0, &APy0, PageChar,PageAtrb);
3986 else if (iAddress & 0x08) /* local: eval invoked object */
3988 unsigned char APx = 0, APy = 0;
3989 unsigned char APx0 = *pAPx0 + *pAPx + m_txtCache->tAPx, APy0 = *pAPy0 + *pAPy + m_txtCache->tAPy;
3990 int descode = ((iAddress & 0x01) << 3) | (iData >> 4);
3991 int triplet = iData & 0x0f;
3993 if (APx0 < 40) /* not in side panel */
3995 Eval_Object(13 * 23 + 13 * descode + triplet, pstCachedPage, &APx, &APy, &APx0, &APy0, (tObjType)(triplet % 3), pagedata, PageChar, PageAtrb);
3998 break;
3999 case 0x15:
4000 case 0x16:
4001 case 0x17:
4002 if (0 == (iAddress & 0x08)) /* Object Definition illegal or only level 3.5 */
4003 break; /* ignore */
4005 m_txtCache->tAPx = m_txtCache->tAPy = 0;
4006 *endcol = -1;
4007 return 0xFF; /* termination by object definition */
4008 break;
4009 case 0x18:
4010 if (0 == (iData & 0x10)) /* DRCS Mode reserved or only level 3.5 */
4011 break; /* ignore */
4013 if (iData & 0x40)
4014 *drcssubp = iData & 0x0f;
4015 else
4016 *gdrcssubp = iData & 0x0f;
4017 break;
4018 case 0x1f:
4019 m_txtCache->tAPx = m_txtCache->tAPy = 0;
4020 *endcol = -1;
4021 return 0x80 | iData; /* explicit termination */
4022 break;
4023 default:
4024 break; /* unsupported or not yet implemented mode: ignore */
4025 } /* switch (iMode) */
4026 } /* (iAddress >= 40): row addresses */
4028 if (iAddress < 40 || iMode != 0x10) /* leave temp. AP-Offset unchanged only immediately after definition */
4029 m_txtCache->tAPx = m_txtCache->tAPy = 0;
4031 return 0; /* normal exit, no termination */
4034 /* get object data */
4035 /* in: absolute triplet number (0..506, start at packet 3 byte 1) */
4036 /* in: pointer to cache struct of page data */
4037 /* out: 18 bit triplet data, <0 if invalid number, not cached, or hamming error */
4038 int CTeletextDecoder::iTripletNumber2Data(int iONr, TextCachedPage_t *pstCachedPage, unsigned char* pagedata)
4040 if (iONr > 506 || 0 == pstCachedPage)
4041 return -1;
4043 unsigned char *p;
4044 int packet = (iONr / 13) + 3;
4045 int packetoffset = 3 * (iONr % 13);
4047 if (packet <= 23)
4048 p = pagedata + 40*(packet-1) + packetoffset + 1;
4049 else if (packet <= 25)
4051 if (0 == pstCachedPage->pageinfo.p24)
4052 return -1;
4053 p = pstCachedPage->pageinfo.p24 + 40*(packet-24) + packetoffset + 1;
4055 else
4057 int descode = packet - 26;
4058 if (0 == pstCachedPage->pageinfo.ext)
4059 return -1;
4060 if (0 == pstCachedPage->pageinfo.ext->p26[descode])
4061 return -1;
4062 p = pstCachedPage->pageinfo.ext->p26[descode] + packetoffset; /* first byte (=designation code) is not cached */
4064 return CDVDTeletextTools::deh24(p);
4067 int CTeletextDecoder::SetNational(unsigned char sec)
4069 std::unique_lock<CCriticalSection> lock(m_txtCache->m_critSection);
4071 switch (sec)
4073 case 0x08:
4074 return NAT_PL; //polish
4075 case 0x16:
4076 case 0x36:
4077 return NAT_TR; //turkish
4078 case 0x1d:
4079 return NAT_SR; //serbian, croatian, slovenian
4080 case 0x20:
4081 return NAT_SC; // serbian, croatian
4082 case 0x24:
4083 return NAT_RB; // russian, bulgarian
4084 case 0x25:
4085 return NAT_UA; // ukrainian
4086 case 0x22:
4087 return NAT_ET; // estonian
4088 case 0x23:
4089 return NAT_LV; // latvian, lithuanian
4090 case 0x37:
4091 return NAT_GR; // greek
4092 case 0x55:
4093 return NAT_HB; // hebrew
4094 case 0x47:
4095 case 0x57:
4096 return NAT_AR; // arabic
4098 return CountryConversionTable[sec & 0x07];
4101 int CTeletextDecoder::NextHex(int i) /* return next existing non-decimal page number */
4103 int startpage = i;
4104 if (startpage < 0x100)
4105 startpage = 0x100;
4109 i++;
4110 if (i > 0x8FF)
4111 i = 0x100;
4112 if (i == startpage)
4113 break;
4114 } while ((m_txtCache->SubPageTable[i] == 0xFF) || IsDec(i));
4115 return i;
4118 void CTeletextDecoder::SetColors(const unsigned short *pcolormap, int offset, int number)
4120 int j = offset; /* index in global color table */
4122 for (int i = 0; i < number; i++)
4124 int r = ((pcolormap[i] >> 8) & 0xf) << 4;
4125 int g = ((pcolormap[i] >> 4) & 0xf) << 4;
4126 int b = ((pcolormap[i]) & 0xf) << 4;
4128 if (m_RenderInfo.rd0[j] != r)
4130 m_RenderInfo.rd0[j] = r;
4132 if (m_RenderInfo.gn0[j] != g)
4134 m_RenderInfo.gn0[j] = g;
4136 if (m_RenderInfo.bl0[j] != b)
4138 m_RenderInfo.bl0[j] = b;
4140 j++;
4144 UTILS::COLOR::Color CTeletextDecoder::GetColorRGB(enumTeletextColor ttc)
4146 switch (ttc)
4148 case TXT_ColorBlack: return 0xFF000000;
4149 case TXT_ColorRed: return 0xFFFC1414;
4150 case TXT_ColorGreen: return 0xFF24FC24;
4151 case TXT_ColorYellow: return 0xFFFCC024;
4152 case TXT_ColorBlue: return 0xFF0000FC;
4153 case TXT_ColorMagenta: return 0xFFB000FC;
4154 case TXT_ColorCyan: return 0xFF00FCFC;
4155 case TXT_ColorWhite: return 0xFFFCFCFC;
4156 case TXT_ColorTransp: return 0x00000000;
4157 default: break;
4160 /* Get colors for CLUTs 2+3 */
4161 int index = (int)ttc;
4162 UTILS::COLOR::Color color = (m_RenderInfo.tr0[index] << 24) | (m_RenderInfo.bl0[index] << 16) |
4163 (m_RenderInfo.gn0[index] << 8) | m_RenderInfo.rd0[index];
4164 return color;