/src/vlc/modules/codec/cea708.c
Line | Count | Source |
1 | | /***************************************************************************** |
2 | | * cea708.c : CEA708 subtitles decoder |
3 | | ***************************************************************************** |
4 | | * Copyright © 2017 VideoLabs, VideoLAN and VLC authors |
5 | | * |
6 | | * This program is free software; you can redistribute it and/or modify it |
7 | | * under the terms of the GNU Lesser General Public License as published by |
8 | | * the Free Software Foundation; either version 2.1 of the License, or |
9 | | * (at your option) any later version. |
10 | | * |
11 | | * This program is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Lesser General Public License |
17 | | * along with this program; if not, write to the Free Software Foundation, |
18 | | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
19 | | *****************************************************************************/ |
20 | | #ifdef HAVE_CONFIG_H |
21 | | # include "config.h" |
22 | | #endif |
23 | | |
24 | | #include <vlc_common.h> |
25 | | #include <vlc_codec.h> |
26 | | #include <vlc_subpicture.h> |
27 | | |
28 | | #include "cea708.h" |
29 | | #include "substext.h" |
30 | | |
31 | | #include <assert.h> |
32 | | |
33 | | #if 0 |
34 | | #define Debug(code) code |
35 | | #else |
36 | | #define Debug(code) |
37 | | #endif |
38 | | |
39 | | /***************************************************************************** |
40 | | * Demuxing / Agreggation |
41 | | *****************************************************************************/ |
42 | | struct cea708_demux_t |
43 | | { |
44 | | int8_t i_pkt_sequence; |
45 | | uint8_t i_total_data; |
46 | | uint8_t i_data; |
47 | | uint8_t data[CEA708_DTVCC_MAX_PKT_SIZE]; |
48 | | vlc_tick_t i_time; |
49 | | service_data_hdlr_t p_callback; |
50 | | void *priv; |
51 | | }; |
52 | | |
53 | | void CEA708_DTVCC_Demuxer_Flush( cea708_demux_t *h ) |
54 | 1 | { |
55 | 1 | h->i_pkt_sequence = -1; |
56 | 1 | h->i_total_data = h->i_data = 0; |
57 | 1 | } |
58 | | |
59 | | void CEA708_DTVCC_Demuxer_Release( cea708_demux_t *h ) |
60 | 1 | { |
61 | 1 | free( h ); |
62 | 1 | } |
63 | | |
64 | | cea708_demux_t * CEA708_DTVCC_Demuxer_New( void *priv, service_data_hdlr_t hdlr ) |
65 | 1 | { |
66 | 1 | cea708_demux_t *h = malloc( sizeof(cea708_demux_t) ); |
67 | 1 | if( h ) |
68 | 1 | { |
69 | 1 | h->priv = priv; |
70 | 1 | h->p_callback = hdlr; |
71 | 1 | CEA708_DTVCC_Demuxer_Flush( h ); |
72 | 1 | } |
73 | 1 | return h; |
74 | 1 | } |
75 | | |
76 | | static void CEA708_DTVCC_Demux_ServiceBlocks( cea708_demux_t *h, vlc_tick_t i_start, |
77 | | const uint8_t *p_data, size_t i_data ) |
78 | 0 | { |
79 | 0 | while( i_data >= 2 ) |
80 | 0 | { |
81 | 0 | uint8_t i_sid = p_data[0] >> 5; |
82 | 0 | const uint8_t i_block_size = p_data[0] & 0x1F; |
83 | |
|
84 | 0 | if( i_block_size == 0 || i_block_size > i_data - 1 ) |
85 | 0 | { |
86 | 0 | return; |
87 | 0 | } |
88 | 0 | else if( i_sid == 0x07 ) |
89 | 0 | { |
90 | 0 | i_sid = p_data[1] & 0x3F; |
91 | 0 | if( i_sid < 0x07 ) |
92 | 0 | return; |
93 | 0 | p_data += 1; i_data -= 1; |
94 | 0 | } |
95 | 0 | p_data += 1; i_data -= 1; |
96 | |
|
97 | 0 | h->p_callback( h->priv, i_sid, i_start, p_data, i_block_size ); |
98 | |
|
99 | 0 | p_data += i_block_size; |
100 | 0 | i_data -= i_block_size; |
101 | 0 | } |
102 | 0 | } |
103 | | |
104 | | void CEA708_DTVCC_Demuxer_Push( cea708_demux_t *h, vlc_tick_t i_start, const uint8_t data[3] ) |
105 | 0 | { |
106 | 0 | if( (data[0] & 0x03) == 3 ) /* Header packet */ |
107 | 0 | { |
108 | 0 | const int8_t i_pkt_sequence = data[1] >> 6; |
109 | | |
110 | | /* pkt loss/discontinuity, trash buffer */ |
111 | 0 | if( i_pkt_sequence > 0 && ((h->i_pkt_sequence + 1) % 4) != i_pkt_sequence ) |
112 | 0 | { |
113 | 0 | h->i_data = h->i_total_data = 0; |
114 | 0 | h->i_pkt_sequence = i_pkt_sequence; |
115 | 0 | return; |
116 | 0 | } |
117 | | |
118 | 0 | uint8_t pktsize = data[1] & 63; |
119 | 0 | if( pktsize == 0 ) |
120 | 0 | pktsize = 127; |
121 | 0 | else |
122 | 0 | pktsize = pktsize * 2 - 1; |
123 | |
|
124 | 0 | h->i_pkt_sequence = i_pkt_sequence; |
125 | 0 | h->i_total_data = pktsize; |
126 | 0 | h->i_data = 0; |
127 | 0 | h->i_time = i_start; |
128 | 0 | h->data[h->i_data++] = data[2]; |
129 | 0 | } |
130 | 0 | else if( h->i_total_data > 0 ) /* Not synced to pkt header yet */ |
131 | 0 | { |
132 | 0 | h->data[h->i_data++] = data[1]; |
133 | 0 | h->data[h->i_data++] = data[2]; |
134 | 0 | } |
135 | | |
136 | | /* pkts assembly finished, we have a service block */ |
137 | 0 | if( h->i_data > 0 && h->i_data >= h->i_total_data ) |
138 | 0 | { |
139 | 0 | if( h->i_data == h->i_total_data ) /* Only if correct */ |
140 | 0 | CEA708_DTVCC_Demux_ServiceBlocks( h, h->i_time, h->data, h->i_data ); |
141 | 0 | h->i_total_data = h->i_data = 0; |
142 | 0 | } |
143 | 0 | } |
144 | | |
145 | | /***************************************************************************** |
146 | | * Service Data Decoding |
147 | | *****************************************************************************/ |
148 | | |
149 | 0 | #define CEA708_SERVICE_INPUT_BUFFER 128 |
150 | | |
151 | 27 | #define CEA708_WINDOWS_COUNT 8 |
152 | | #define CEA708_PREDEFINED_STYLES 7 |
153 | | |
154 | 0 | #define CEA708_SCREEN_ROWS 75 |
155 | | #define CEA708_SCREEN_COLS_43 160 |
156 | 0 | #define CEA708_SCREEN_COLS_169 210 |
157 | 0 | #define CEA708_SCREEN_SAFE_MARGIN_RATIO 0.10 |
158 | 0 | #define CEA708_SAFE_AREA_REL (1.0 - CEA708_SCREEN_SAFE_MARGIN_RATIO) |
159 | | |
160 | 0 | #define CEA708_WINDOW_MAX_COLS 42 |
161 | 32 | #define CEA708_WINDOW_MAX_ROWS 15 |
162 | | |
163 | 0 | #define CEA708_ROW_HEIGHT_STANDARD (CEA708_SAFE_AREA_REL / \ |
164 | 0 | CEA708_WINDOW_MAX_ROWS) |
165 | 0 | #define CEA708_FONT_TO_LINE_HEIGHT_RATIO 1.06 |
166 | | |
167 | 0 | #define CEA708_FONTRELSIZE_STANDARD (100.0 * CEA708_ROW_HEIGHT_STANDARD / \ |
168 | 0 | CEA708_FONT_TO_LINE_HEIGHT_RATIO) |
169 | 0 | #define CEA708_FONTRELSIZE_SMALL (CEA708_FONTRELSIZE_STANDARD * 0.7) |
170 | 0 | #define CEA708_FONTRELSIZE_LARGE (CEA708_FONTRELSIZE_STANDARD * 1.3) |
171 | | |
172 | | enum cea708_status_e |
173 | | { |
174 | | CEA708_STATUS_OK = 1 << 0, |
175 | | CEA708_STATUS_STARVING = 1 << 1, |
176 | | CEA708_STATUS_OUTPUT = 1 << 2, |
177 | | }; |
178 | | |
179 | | enum cea708_c0_codes |
180 | | { |
181 | | CEA708_C0_NUL = 0x00, |
182 | | CEA708_C0_ETX = 0x03, |
183 | | CEA708_C0_BS = 0x08, |
184 | | CEA708_C0_FF = 0x0C, |
185 | | CEA708_C0_CR = 0x0D, |
186 | | CEA708_C0_HCR = 0x0E, |
187 | | CEA708_C0_EXT1 = 0x10, |
188 | | CEA708_C0_P16 = 0x18, |
189 | | }; |
190 | | |
191 | | enum cea708_c1_codes |
192 | | { |
193 | | CEA708_C1_CW0 = 0x80, |
194 | | CEA708_C1_CW7 = 0x87, |
195 | | CEA708_C1_CLW, |
196 | | CEA708_C1_DSW, |
197 | | CEA708_C1_HDW, |
198 | | CEA708_C1_TGW, |
199 | | CEA708_C1_DLW, |
200 | | CEA708_C1_DLY, |
201 | | CEA708_C1_DLC, |
202 | | CEA708_C1_RST, |
203 | | CEA708_C1_SPA = 0x90, |
204 | | CEA708_C1_SPC, |
205 | | CEA708_C1_SPL, |
206 | | CEA708_C1_SWA = 0x97, |
207 | | CEA708_C1_DF0, |
208 | | CEA708_C1_DF7 = 0x9F, |
209 | | }; |
210 | | |
211 | | typedef struct |
212 | | { |
213 | | uint8_t ringbuffer[CEA708_SERVICE_INPUT_BUFFER]; |
214 | | uint8_t start; |
215 | | uint8_t capacity; |
216 | | } cea708_input_buffer_t; |
217 | | |
218 | | static void cea708_input_buffer_init(cea708_input_buffer_t *ib) |
219 | 2 | { |
220 | 2 | ib->capacity = 0; |
221 | 2 | ib->start = 0; |
222 | 2 | } |
223 | | |
224 | | static uint8_t cea708_input_buffer_size(const cea708_input_buffer_t *ib) |
225 | 0 | { |
226 | 0 | return ib->capacity; |
227 | 0 | } |
228 | | |
229 | | static uint8_t cea708_input_buffer_remain(const cea708_input_buffer_t *ib) |
230 | 0 | { |
231 | 0 | return CEA708_SERVICE_INPUT_BUFFER - ib->capacity; |
232 | 0 | } |
233 | | |
234 | | static void cea708_input_buffer_add(cea708_input_buffer_t *ib, uint8_t a) |
235 | 0 | { |
236 | 0 | if( cea708_input_buffer_remain(ib) > 0 ) |
237 | 0 | ib->ringbuffer[(ib->start + ib->capacity++) % CEA708_SERVICE_INPUT_BUFFER] = a; |
238 | 0 | } |
239 | | |
240 | | static uint8_t cea708_input_buffer_peek(cea708_input_buffer_t *ib, uint8_t off) |
241 | 0 | { |
242 | 0 | if(off + 1 > ib->capacity) |
243 | 0 | return 0; |
244 | 0 | off = (ib->start + off) % CEA708_SERVICE_INPUT_BUFFER; |
245 | 0 | return ib->ringbuffer[off]; |
246 | 0 | } |
247 | | |
248 | | static uint8_t cea708_input_buffer_get(cea708_input_buffer_t *ib) |
249 | 0 | { |
250 | 0 | uint8_t a = cea708_input_buffer_peek( ib, 0 ); |
251 | 0 | ib->start = (ib->start + 1) % CEA708_SERVICE_INPUT_BUFFER; |
252 | 0 | ib->capacity--; |
253 | 0 | return a; |
254 | 0 | } |
255 | | |
256 | | enum cea708_opacity_e |
257 | | { |
258 | | CEA708_OPACITY_SOLID = 0, |
259 | | CEA708_OPACITY_FLASH, |
260 | | CEA708_OPACITY_TRANSLUCENT, |
261 | | CEA708_OPACITY_TRANSPARENT, |
262 | | }; |
263 | | |
264 | | enum cea708_edge_e |
265 | | { |
266 | | CEA708_EDGE_NONE =0, |
267 | | CEA708_EDGE_RAISED, |
268 | | CEA708_EDGE_DEPRESSED, |
269 | | CEA708_EDGE_UNIFORM, |
270 | | CEA708_EDGE_LEFT_DROP_SHADOW, |
271 | | CEA708_EDGE_RIGHT_DROP_SHADOW, |
272 | | }; |
273 | | |
274 | | typedef struct |
275 | | { |
276 | | enum |
277 | | { |
278 | | CEA708_PEN_SIZE_SMALL = 0, |
279 | | CEA708_PEN_SIZE_STANDARD, |
280 | | CEA708_PEN_SIZE_LARGE, |
281 | | } size; |
282 | | enum |
283 | | { |
284 | | CEA708_FONT_UNDEFINED = 0, |
285 | | CEA708_FONT_MONOSPACED, |
286 | | CEA708_FONT_PROP, |
287 | | CEA708_FONT_MONO_SANS_SERIF, |
288 | | CEA708_FONT_PROP_SANS_SERIF, |
289 | | CEA708_FONT_CASUAL, |
290 | | CEA708_FONT_CURSIVE, |
291 | | CEA708_FONT_SMALL_CAPS, |
292 | | } font; |
293 | | enum |
294 | | { |
295 | | CEA708_TAG_DIALOG = 0, |
296 | | CEA708_TAG_SPEAKER, |
297 | | CEA708_TAG_SYNTHETIC_VOICE, |
298 | | CEA708_TAG_DIALOG_SECONDARY_LANG, |
299 | | CEA708_TAG_VOICEOVER, |
300 | | CEA708_TAG_AUDIBLE_TRANSLATION, |
301 | | CEA708_TAG_SUBTITLE_TRANSLATION, |
302 | | CEA708_TAG_VOICE_QUALITY_DESCRIPTION, |
303 | | CEA708_TAG_SONG_LYRICS, |
304 | | CEA708_TAG_FX_DESCRIPTION, |
305 | | CEA708_TAG_SCORE_DESCRIPTION, |
306 | | CEA708_TAG_EXPLETIVE, |
307 | | CEA708_TAG_NOT_TO_BE_DISPLAYED = 15, |
308 | | } text_tag; |
309 | | enum |
310 | | { |
311 | | CEA708_PEN_OFFSET_SUBSCRIPT = 0, |
312 | | CEA708_PEN_OFFSET_NORMAL, |
313 | | CEA708_PEN_OFFSET_SUPERSCRIPT, |
314 | | } offset; |
315 | | bool b_italics; |
316 | | bool b_underline; |
317 | | struct |
318 | | { |
319 | | uint8_t color; |
320 | | enum cea708_opacity_e opacity; |
321 | | } foreground, background; |
322 | | uint8_t edge_color; |
323 | | enum cea708_edge_e edge_type; |
324 | | } cea708_pen_style_t; |
325 | | |
326 | | typedef struct |
327 | | { |
328 | | cea708_pen_style_t style; |
329 | | uint8_t row; |
330 | | uint8_t col; |
331 | | } cea708_pen_t; |
332 | | |
333 | | typedef struct |
334 | | { |
335 | | enum |
336 | | { |
337 | | CEA708_WA_JUSTIFY_LEFT = 0, |
338 | | CEA708_WA_JUSTIFY_RIGHT, |
339 | | CEA708_WA_JUSTIFY_CENTER, |
340 | | CEA708_WA_JUSTIFY_FULL, |
341 | | } justify; |
342 | | enum |
343 | | { |
344 | | CEA708_WA_DIRECTION_LTR = 0, |
345 | | CEA708_WA_DIRECTION_RTL, |
346 | | CEA708_WA_DIRECTION_TB, |
347 | | CEA708_WA_DIRECTION_BT, |
348 | | } print_direction, scroll_direction, effect_direction; |
349 | | bool b_word_wrap; |
350 | | enum |
351 | | { |
352 | | CEA708_WA_EFFECT_SNAP = 0, |
353 | | CEA708_WA_EFFECT_FADE, |
354 | | CEA708_WA_EFFECT_WIPE, |
355 | | } display_effect; |
356 | | uint8_t effect_speed; |
357 | | uint8_t fill_color_color; |
358 | | enum cea708_opacity_e fill_opacity; |
359 | | enum cea708_edge_e border_type; |
360 | | uint8_t border_color_color; |
361 | | } cea708_window_style_t; |
362 | | |
363 | | typedef struct cea708_text_row_t cea708_text_row_t; |
364 | | |
365 | | struct cea708_text_row_t |
366 | | { |
367 | | uint8_t characters[CEA708_WINDOW_MAX_COLS * 4]; |
368 | | cea708_pen_style_t styles[CEA708_WINDOW_MAX_COLS]; |
369 | | uint8_t firstcol; |
370 | | uint8_t lastcol; |
371 | | }; |
372 | | |
373 | | static void cea708_text_row_Delete( cea708_text_row_t *p_row ) |
374 | 0 | { |
375 | 0 | free( p_row ); |
376 | 0 | } |
377 | | |
378 | | static cea708_text_row_t * cea708_text_row_New( void ) |
379 | 0 | { |
380 | 0 | cea708_text_row_t *p_row = malloc( sizeof(*p_row) ); |
381 | 0 | if( p_row ) |
382 | 0 | { |
383 | 0 | p_row->firstcol = CEA708_WINDOW_MAX_COLS; |
384 | 0 | p_row->lastcol = 0; |
385 | 0 | memset(p_row->characters, 0, 4 * CEA708_WINDOW_MAX_COLS); |
386 | 0 | } |
387 | 0 | return p_row; |
388 | 0 | } |
389 | | |
390 | | typedef struct |
391 | | { |
392 | | cea708_text_row_t * rows[CEA708_WINDOW_MAX_ROWS]; |
393 | | uint8_t i_firstrow; |
394 | | uint8_t i_lastrow; |
395 | | |
396 | | uint8_t i_priority; |
397 | | |
398 | | enum |
399 | | { |
400 | | CEA708_ANCHOR_TOP_LEFT = 0, |
401 | | CEA708_ANCHOR_TOP_CENTER, |
402 | | CEA708_ANCHOR_TOP_RIGHT, |
403 | | CEA708_ANCHOR_CENTER_LEFT, |
404 | | CEA708_ANCHOR_CENTER_CENTER, |
405 | | CEA708_ANCHOR_CENTER_RIGHT, |
406 | | CEA708_ANCHOR_BOTTOM_LEFT, |
407 | | CEA708_ANCHOR_BOTTOM_CENTER, |
408 | | CEA708_ANCHOR_BOTTOM_RIGHT, |
409 | | } anchor_point; |
410 | | uint8_t i_anchor_offset_v; |
411 | | uint8_t i_anchor_offset_h; |
412 | | |
413 | | /* Extras row for window scroll */ |
414 | | uint8_t i_row_count; |
415 | | uint8_t i_col_count; |
416 | | |
417 | | /* flags */ |
418 | | uint8_t b_relative; |
419 | | uint8_t b_row_lock; |
420 | | uint8_t b_column_lock; |
421 | | uint8_t b_visible; |
422 | | |
423 | | cea708_window_style_t style; |
424 | | cea708_pen_style_t pen; |
425 | | |
426 | | uint8_t row; |
427 | | uint8_t col; |
428 | | |
429 | | bool b_defined; |
430 | | |
431 | | } cea708_window_t; |
432 | | |
433 | | struct cea708_t |
434 | | { |
435 | | decoder_t *p_dec; |
436 | | |
437 | | /* Defaults */ |
438 | | cea708_window_t window[CEA708_WINDOWS_COUNT]; |
439 | | cea708_input_buffer_t input_buffer; |
440 | | |
441 | | /* Decoding context */ |
442 | | cea708_window_t *p_cw; /* current window */ |
443 | | vlc_tick_t suspended_deadline; /* not VLC_TICK_INVALID when delay is active */ |
444 | | vlc_tick_t i_clock; |
445 | | bool b_text_waiting; |
446 | | }; |
447 | | |
448 | | static int CEA708_Decode_G0( uint8_t code, cea708_t *p_cea708 ); |
449 | | static int CEA708_Decode_C0( uint8_t code, cea708_t *p_cea708 ); |
450 | | static int CEA708_Decode_G1( uint8_t code, cea708_t *p_cea708 ); |
451 | | static int CEA708_Decode_C1( uint8_t code, cea708_t *p_cea708 ); |
452 | | static int CEA708_Decode_G2G3( uint8_t code, cea708_t *p_cea708 ); |
453 | | static int CEA708_Decode_P16( uint16_t ucs2, cea708_t *p_cea708 ); |
454 | | |
455 | | #define DEFAULT_NTSC_STYLE(font, edge, bgopacity ) \ |
456 | | {\ |
457 | | CEA708_PEN_SIZE_STANDARD,\ |
458 | | font,\ |
459 | | CEA708_TAG_DIALOG,\ |
460 | | CEA708_PEN_OFFSET_NORMAL,\ |
461 | | false,\ |
462 | | false,\ |
463 | | { 0x2A, CEA708_OPACITY_SOLID, },\ |
464 | | { 0x00, bgopacity, },\ |
465 | | 0x00,\ |
466 | | edge,\ |
467 | | } |
468 | | static const cea708_pen_style_t cea708_default_pen_styles[CEA708_PREDEFINED_STYLES] = |
469 | | { |
470 | | DEFAULT_NTSC_STYLE( CEA708_FONT_UNDEFINED, CEA708_EDGE_NONE, CEA708_OPACITY_SOLID ), |
471 | | DEFAULT_NTSC_STYLE( CEA708_FONT_MONOSPACED, CEA708_EDGE_NONE, CEA708_OPACITY_SOLID ), |
472 | | DEFAULT_NTSC_STYLE( CEA708_FONT_PROP, CEA708_EDGE_NONE, CEA708_OPACITY_SOLID ), |
473 | | DEFAULT_NTSC_STYLE( CEA708_FONT_MONO_SANS_SERIF, CEA708_EDGE_NONE, CEA708_OPACITY_SOLID ), |
474 | | DEFAULT_NTSC_STYLE( CEA708_FONT_PROP_SANS_SERIF, CEA708_EDGE_NONE, CEA708_OPACITY_SOLID ), |
475 | | DEFAULT_NTSC_STYLE( CEA708_FONT_MONO_SANS_SERIF, CEA708_EDGE_UNIFORM, CEA708_OPACITY_TRANSPARENT ), |
476 | | DEFAULT_NTSC_STYLE( CEA708_FONT_PROP_SANS_SERIF, CEA708_EDGE_UNIFORM, CEA708_OPACITY_TRANSPARENT ), |
477 | | }; |
478 | | #undef DEFAULT_NTSC_STYLE |
479 | | |
480 | | #define DEFAULT_NTSC_WA_STYLE(just, pd, scroll, wrap, opacity) \ |
481 | | {\ |
482 | | just,\ |
483 | | pd,\ |
484 | | scroll,\ |
485 | | CEA708_WA_DIRECTION_LTR,\ |
486 | | wrap,\ |
487 | | CEA708_WA_EFFECT_SNAP,\ |
488 | | 1,\ |
489 | | 0x00,\ |
490 | | opacity,\ |
491 | | CEA708_EDGE_NONE,\ |
492 | | 0x00,\ |
493 | | } |
494 | | static const cea708_window_style_t cea708_default_window_styles[CEA708_PREDEFINED_STYLES] = |
495 | | { |
496 | | DEFAULT_NTSC_WA_STYLE(CEA708_WA_JUSTIFY_LEFT, CEA708_WA_DIRECTION_LTR, |
497 | | CEA708_WA_DIRECTION_BT, false, CEA708_OPACITY_SOLID), |
498 | | DEFAULT_NTSC_WA_STYLE(CEA708_WA_JUSTIFY_LEFT, CEA708_WA_DIRECTION_LTR, |
499 | | CEA708_WA_DIRECTION_BT, false, CEA708_OPACITY_TRANSPARENT), |
500 | | DEFAULT_NTSC_WA_STYLE(CEA708_WA_JUSTIFY_CENTER, CEA708_WA_DIRECTION_LTR, |
501 | | CEA708_WA_DIRECTION_BT, false, CEA708_OPACITY_SOLID), |
502 | | DEFAULT_NTSC_WA_STYLE(CEA708_WA_JUSTIFY_LEFT, CEA708_WA_DIRECTION_LTR, |
503 | | CEA708_WA_DIRECTION_BT, true, CEA708_OPACITY_SOLID), |
504 | | DEFAULT_NTSC_WA_STYLE(CEA708_WA_JUSTIFY_LEFT, CEA708_WA_DIRECTION_LTR, |
505 | | CEA708_WA_DIRECTION_BT, true, CEA708_OPACITY_TRANSPARENT), |
506 | | DEFAULT_NTSC_WA_STYLE(CEA708_WA_JUSTIFY_CENTER, CEA708_WA_DIRECTION_LTR, |
507 | | CEA708_WA_DIRECTION_BT, true, CEA708_OPACITY_SOLID), |
508 | | DEFAULT_NTSC_WA_STYLE(CEA708_WA_JUSTIFY_LEFT, CEA708_WA_DIRECTION_TB, |
509 | | CEA708_WA_DIRECTION_RTL, false, CEA708_OPACITY_SOLID), |
510 | | }; |
511 | | #undef DEFAULT_NTSC_WA_STYLE |
512 | | |
513 | | static void CEA708_Window_Init( cea708_window_t *p_w ) |
514 | 24 | { |
515 | 24 | memset( p_w, 0, sizeof(*p_w) ); |
516 | 24 | p_w->style = cea708_default_window_styles[0]; |
517 | 24 | p_w->pen = cea708_default_pen_styles[0]; |
518 | 24 | p_w->i_firstrow = CEA708_WINDOW_MAX_ROWS; |
519 | 24 | p_w->b_row_lock = true; |
520 | 24 | p_w->b_column_lock = true; |
521 | 24 | } |
522 | | |
523 | | static void CEA708_Window_ClearText( cea708_window_t *p_w ) |
524 | 8 | { |
525 | 8 | for( uint8_t i=p_w->i_firstrow; i<=p_w->i_lastrow; i++ ) |
526 | 0 | { |
527 | 0 | cea708_text_row_Delete( p_w->rows[i] ); |
528 | 0 | p_w->rows[i] = NULL; |
529 | 0 | } |
530 | 8 | p_w->i_lastrow = 0; |
531 | 8 | p_w->i_firstrow = CEA708_WINDOW_MAX_ROWS; |
532 | 8 | } |
533 | | |
534 | | static void CEA708_Window_Reset( cea708_window_t *p_w ) |
535 | 8 | { |
536 | 8 | CEA708_Window_ClearText( p_w ); |
537 | 8 | CEA708_Window_Init( p_w ); |
538 | 8 | } |
539 | | |
540 | | static bool CEA708_Window_BreaksSpace( const cea708_window_t *p_w ) |
541 | 0 | { |
542 | 0 | #if 1 |
543 | | // FIXME: missing test case |
544 | 0 | (void)p_w; |
545 | 0 | return true; |
546 | | #else |
547 | | if( p_w->style.print_direction == CEA708_WA_DIRECTION_LTR && |
548 | | p_w->style.justify == CEA708_WA_JUSTIFY_LEFT ) |
549 | | return true; |
550 | | |
551 | | if( p_w->style.print_direction == CEA708_WA_DIRECTION_RTL && |
552 | | p_w->style.justify == CEA708_WA_JUSTIFY_RIGHT ) |
553 | | return true; |
554 | | |
555 | | return false; |
556 | | #endif |
557 | 0 | } |
558 | | |
559 | | static uint8_t CEA708_Window_MinCol( const cea708_window_t *p_w ) |
560 | 0 | { |
561 | 0 | uint8_t i_min = CEA708_WINDOW_MAX_COLS; |
562 | 0 | for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ ) |
563 | 0 | { |
564 | 0 | const cea708_text_row_t *p_row = p_w->rows[p_w->row]; |
565 | 0 | if( p_row && p_row->firstcol < i_min ) |
566 | 0 | i_min = p_row->firstcol; |
567 | 0 | } |
568 | 0 | return i_min; |
569 | 0 | } |
570 | | |
571 | | static uint8_t CEA708_Window_MaxCol( const cea708_window_t *p_w ) |
572 | 0 | { |
573 | 0 | uint8_t i_max = 0; |
574 | 0 | for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ ) |
575 | 0 | { |
576 | 0 | const cea708_text_row_t *p_row = p_w->rows[p_w->row]; |
577 | 0 | if( p_row && p_row->lastcol > i_max ) |
578 | 0 | i_max = p_row->lastcol; |
579 | 0 | } |
580 | 0 | return i_max; |
581 | 0 | } |
582 | | |
583 | | static uint8_t CEA708_Window_ColCount( const cea708_window_t *p_w ) |
584 | 0 | { |
585 | 0 | const cea708_text_row_t *p_row = p_w->rows[p_w->row]; |
586 | 0 | if( !p_row || p_row->firstcol > p_row->lastcol ) |
587 | 0 | return 0; |
588 | 0 | return 1 + p_row->lastcol - p_row->firstcol; |
589 | 0 | } |
590 | | |
591 | | static uint8_t CEA708_Window_RowCount( const cea708_window_t *p_w ) |
592 | 0 | { |
593 | 0 | if( p_w->i_firstrow > p_w->i_lastrow ) |
594 | 0 | return 0; |
595 | 0 | return 1 + p_w->i_lastrow - p_w->i_firstrow; |
596 | 0 | } |
597 | | |
598 | | static void CEA708_Window_Truncate( cea708_window_t *p_w, int i_direction ) |
599 | 0 | { |
600 | 0 | switch( i_direction ) |
601 | 0 | { |
602 | 0 | case CEA708_WA_DIRECTION_LTR: /* Deletes all most right col */ |
603 | 0 | { |
604 | 0 | uint8_t i_max = CEA708_Window_MaxCol( p_w ); |
605 | 0 | for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ ) |
606 | 0 | { |
607 | 0 | cea708_text_row_t *row = p_w->rows[i]; |
608 | 0 | if( row->lastcol == i_max ) |
609 | 0 | { |
610 | 0 | if( row->firstcol >= row->lastcol ) |
611 | 0 | { |
612 | 0 | cea708_text_row_Delete( row ); |
613 | 0 | p_w->rows[i] = NULL; |
614 | 0 | if( i == p_w->i_firstrow ) |
615 | 0 | p_w->i_firstrow++; |
616 | 0 | else if( i == p_w->i_lastrow ) |
617 | 0 | p_w->i_lastrow--; |
618 | 0 | } |
619 | 0 | } |
620 | 0 | } |
621 | 0 | } |
622 | 0 | break; |
623 | 0 | case CEA708_WA_DIRECTION_RTL: /* Deletes all most left col */ |
624 | 0 | { |
625 | 0 | uint8_t i_min = CEA708_Window_MinCol( p_w ); |
626 | 0 | for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ ) |
627 | 0 | { |
628 | 0 | cea708_text_row_t *row = p_w->rows[i]; |
629 | 0 | if( row->firstcol == i_min ) |
630 | 0 | { |
631 | 0 | if( row->firstcol >= row->lastcol ) |
632 | 0 | { |
633 | 0 | cea708_text_row_Delete( row ); |
634 | 0 | p_w->rows[i] = NULL; |
635 | 0 | if( i == p_w->i_firstrow ) |
636 | 0 | p_w->i_firstrow++; |
637 | 0 | else if( i == p_w->i_lastrow ) |
638 | 0 | p_w->i_lastrow--; |
639 | 0 | } |
640 | 0 | } |
641 | 0 | } |
642 | 0 | } |
643 | 0 | break; |
644 | 0 | case CEA708_WA_DIRECTION_TB: /* Deletes LAST row */ |
645 | 0 | if( CEA708_Window_RowCount( p_w ) > 0 ) |
646 | 0 | { |
647 | 0 | cea708_text_row_Delete( p_w->rows[p_w->i_lastrow] ); |
648 | 0 | p_w->rows[p_w->i_lastrow--] = NULL; |
649 | 0 | } |
650 | 0 | break; |
651 | 0 | case CEA708_WA_DIRECTION_BT: /* Deletes First row */ |
652 | 0 | if( CEA708_Window_RowCount( p_w ) > 0 ) |
653 | 0 | { |
654 | 0 | cea708_text_row_Delete( p_w->rows[p_w->i_firstrow] ); |
655 | 0 | p_w->rows[p_w->i_firstrow++] = NULL; |
656 | 0 | } |
657 | 0 | break; |
658 | 0 | } |
659 | 0 | } |
660 | | |
661 | | static void CEA708_Window_Scroll( cea708_window_t *p_w ) |
662 | 0 | { |
663 | 0 | if( CEA708_Window_RowCount( p_w ) == 0 ) |
664 | 0 | return; |
665 | | |
666 | 0 | switch( p_w->style.scroll_direction ) |
667 | 0 | { |
668 | 0 | case CEA708_WA_DIRECTION_LTR: |
669 | | /* Move RIGHT */ |
670 | 0 | if( CEA708_Window_MaxCol( p_w ) == CEA708_WINDOW_MAX_ROWS - 1 ) |
671 | 0 | CEA708_Window_Truncate( p_w, CEA708_WA_DIRECTION_LTR ); |
672 | 0 | for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ ) |
673 | 0 | { |
674 | 0 | cea708_text_row_t *row = p_w->rows[i]; |
675 | 0 | if( row->lastcol < row->firstcol ) /* should not happen */ |
676 | 0 | continue; |
677 | 0 | memmove( &row->characters[row->firstcol + 1], &row->characters[row->firstcol], |
678 | 0 | (row->lastcol - row->firstcol + 1) * 4U ); |
679 | 0 | memmove( &row->styles[row->firstcol + 1], &row->styles[row->firstcol], |
680 | 0 | (row->lastcol - row->firstcol + 1) * sizeof(cea708_pen_style_t) ); |
681 | 0 | row->firstcol++; |
682 | 0 | row->lastcol++; |
683 | 0 | } |
684 | 0 | break; |
685 | 0 | case CEA708_WA_DIRECTION_RTL: |
686 | | /* Move LEFT */ |
687 | 0 | if( CEA708_Window_MinCol( p_w ) == 0 ) |
688 | 0 | CEA708_Window_Truncate( p_w, CEA708_WA_DIRECTION_RTL ); |
689 | 0 | for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ ) |
690 | 0 | { |
691 | 0 | cea708_text_row_t *row = p_w->rows[i]; |
692 | 0 | if( row->lastcol < row->firstcol ) /* should not happen */ |
693 | 0 | continue; |
694 | 0 | memmove( &row->characters[row->firstcol - 1], &row->characters[row->firstcol], |
695 | 0 | (row->lastcol - row->firstcol + 1) * 4U ); |
696 | 0 | memmove( &row->styles[row->firstcol - 1], &row->styles[row->firstcol], |
697 | 0 | (row->lastcol - row->firstcol + 1) * sizeof(cea708_pen_style_t) ); |
698 | 0 | row->firstcol--; |
699 | 0 | row->lastcol--; |
700 | 0 | } |
701 | 0 | break; |
702 | 0 | case CEA708_WA_DIRECTION_TB: |
703 | | /* Move DOWN */ |
704 | 0 | if( p_w->i_lastrow == CEA708_WINDOW_MAX_ROWS - 1 ) |
705 | 0 | CEA708_Window_Truncate( p_w, CEA708_WA_DIRECTION_TB ); |
706 | 0 | for( int i=p_w->i_lastrow; i >= p_w->i_firstrow; i-- ) |
707 | 0 | p_w->rows[i+1] = p_w->rows[i]; |
708 | 0 | p_w->rows[p_w->i_firstrow] = NULL; |
709 | 0 | p_w->i_firstrow++; |
710 | 0 | p_w->i_lastrow++; |
711 | 0 | break; |
712 | 0 | case CEA708_WA_DIRECTION_BT: |
713 | | /* Move UP */ |
714 | 0 | if( p_w->i_firstrow == 0 ) |
715 | 0 | CEA708_Window_Truncate( p_w, CEA708_WA_DIRECTION_BT ); |
716 | 0 | for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ ) |
717 | 0 | p_w->rows[i-1] = p_w->rows[i]; |
718 | 0 | p_w->rows[p_w->i_lastrow] = NULL; |
719 | 0 | p_w->i_firstrow--; |
720 | 0 | p_w->i_lastrow--; |
721 | 0 | break; |
722 | 0 | } |
723 | 0 | } |
724 | | |
725 | | static void CEA708_Window_CarriageReturn( cea708_window_t *p_w ) |
726 | 0 | { |
727 | 0 | switch( p_w->style.scroll_direction ) |
728 | 0 | { |
729 | 0 | case CEA708_WA_DIRECTION_LTR: |
730 | 0 | if( p_w->col > 0 && |
731 | 0 | CEA708_Window_ColCount( p_w ) < p_w->i_col_count ) |
732 | 0 | p_w->col--; |
733 | 0 | else |
734 | 0 | CEA708_Window_Scroll( p_w ); |
735 | 0 | p_w->row = (p_w->style.print_direction == CEA708_WA_DIRECTION_TB) ? |
736 | 0 | 0 : CEA708_WINDOW_MAX_ROWS - 1; |
737 | 0 | break; |
738 | 0 | case CEA708_WA_DIRECTION_RTL: |
739 | 0 | if( p_w->col + 1 < CEA708_WINDOW_MAX_COLS && |
740 | 0 | CEA708_Window_ColCount( p_w ) < p_w->i_col_count ) |
741 | 0 | p_w->col++; |
742 | 0 | else |
743 | 0 | CEA708_Window_Scroll( p_w ); |
744 | 0 | p_w->row = (p_w->style.print_direction == CEA708_WA_DIRECTION_TB) ? |
745 | 0 | 0 : CEA708_WINDOW_MAX_ROWS - 1; |
746 | 0 | break; |
747 | 0 | case CEA708_WA_DIRECTION_TB: |
748 | 0 | if( p_w->row > 0 && |
749 | 0 | CEA708_Window_RowCount( p_w ) < p_w->i_row_count ) |
750 | 0 | p_w->row--; |
751 | 0 | else |
752 | 0 | CEA708_Window_Scroll( p_w ); |
753 | 0 | p_w->col = (p_w->style.print_direction == CEA708_WA_DIRECTION_LTR) ? |
754 | 0 | 0 : CEA708_WINDOW_MAX_COLS - 1; |
755 | 0 | break; |
756 | 0 | case CEA708_WA_DIRECTION_BT: |
757 | 0 | if( p_w->row + 1 < p_w->i_row_count ) |
758 | 0 | p_w->row++; |
759 | 0 | else |
760 | 0 | CEA708_Window_Scroll( p_w ); |
761 | 0 | p_w->col = (p_w->style.print_direction == CEA708_WA_DIRECTION_LTR) ? |
762 | 0 | 0 : CEA708_WINDOW_MAX_COLS - 1; |
763 | 0 | break; |
764 | 0 | } |
765 | 0 | } |
766 | | |
767 | | static void CEA708_Window_Forward( cea708_window_t *p_w ) |
768 | 0 | { |
769 | 0 | switch( p_w->style.print_direction ) |
770 | 0 | { |
771 | 0 | case CEA708_WA_DIRECTION_LTR: |
772 | 0 | if( p_w->col + 1 < CEA708_WINDOW_MAX_COLS ) |
773 | 0 | p_w->col++; |
774 | 0 | else |
775 | 0 | CEA708_Window_CarriageReturn( p_w ); |
776 | 0 | break; |
777 | 0 | case CEA708_WA_DIRECTION_RTL: |
778 | 0 | if( p_w->col > 0 ) |
779 | 0 | p_w->col--; |
780 | 0 | else |
781 | 0 | CEA708_Window_CarriageReturn( p_w ); |
782 | 0 | break; |
783 | 0 | case CEA708_WA_DIRECTION_TB: |
784 | 0 | if( p_w->row + 1 < CEA708_WINDOW_MAX_ROWS ) |
785 | 0 | p_w->row++; |
786 | 0 | else |
787 | 0 | CEA708_Window_CarriageReturn( p_w ); |
788 | 0 | break; |
789 | 0 | case CEA708_WA_DIRECTION_BT: |
790 | 0 | if( p_w->row > 0 ) |
791 | 0 | p_w->row--; |
792 | 0 | else |
793 | 0 | CEA708_Window_CarriageReturn( p_w ); |
794 | 0 | break; |
795 | 0 | } |
796 | 0 | } |
797 | | |
798 | | static void CEA708_Window_Backward( cea708_window_t *p_w ) |
799 | 0 | { |
800 | 0 | static const int reverse[] = |
801 | 0 | { |
802 | 0 | [CEA708_WA_DIRECTION_LTR] = CEA708_WA_DIRECTION_RTL, |
803 | 0 | [CEA708_WA_DIRECTION_RTL] = CEA708_WA_DIRECTION_LTR, |
804 | 0 | [CEA708_WA_DIRECTION_TB] = CEA708_WA_DIRECTION_BT, |
805 | 0 | [CEA708_WA_DIRECTION_BT] = CEA708_WA_DIRECTION_TB, |
806 | 0 | }; |
807 | 0 | int save = p_w->style.print_direction; |
808 | 0 | p_w->style.print_direction = reverse[p_w->style.print_direction]; |
809 | 0 | CEA708_Window_Forward( p_w ); |
810 | 0 | p_w->style.print_direction = save; |
811 | 0 | } |
812 | | |
813 | | static void CEA708_Window_Write( const uint8_t c[4], cea708_window_t *p_w ) |
814 | 0 | { |
815 | 0 | if( !p_w->b_defined ) |
816 | 0 | return; |
817 | | |
818 | | |
819 | 0 | if( unlikely( p_w->row >= CEA708_WINDOW_MAX_ROWS || |
820 | 0 | p_w->col >= CEA708_WINDOW_MAX_COLS ) ) |
821 | 0 | { |
822 | 0 | assert( p_w->row < CEA708_WINDOW_MAX_ROWS ); |
823 | 0 | assert( p_w->col < CEA708_WINDOW_MAX_COLS ); |
824 | 0 | return; |
825 | 0 | } |
826 | | |
827 | 0 | cea708_text_row_t *p_row = p_w->rows[p_w->row]; |
828 | 0 | if( !p_row ) |
829 | 0 | { |
830 | 0 | p_w->rows[p_w->row] = p_row = cea708_text_row_New(); |
831 | 0 | if( !p_row ) |
832 | 0 | return; |
833 | 0 | if( p_w->row < p_w->i_firstrow ) |
834 | 0 | p_w->i_firstrow = p_w->row; |
835 | 0 | if( p_w->row > p_w->i_lastrow ) |
836 | 0 | p_w->i_lastrow = p_w->row; |
837 | 0 | } |
838 | | |
839 | 0 | memcpy( &p_row->characters[p_w->col * 4U], c, 4 ); |
840 | 0 | p_row->styles[p_w->col] = p_w->pen; |
841 | 0 | if( p_w->col < p_row->firstcol ) |
842 | 0 | p_row->firstcol = p_w->col; |
843 | 0 | if( p_w->col > p_row->lastcol ) |
844 | 0 | p_row->lastcol = p_w->col; |
845 | |
|
846 | 0 | CEA708_Window_Forward( p_w ); |
847 | |
|
848 | 0 | Debug(printf("\033[0;33m%s\033[0m", c)); |
849 | 0 | } |
850 | | |
851 | | static uint32_t CEA708ColorConvert( uint8_t c ) |
852 | 0 | { |
853 | 0 | const uint32_t value[4] = {0x00,0x3F,0xF0,0xFF}; |
854 | 0 | c = c & 0x3F; |
855 | 0 | return (value[(c >> 4) & 0x03] << 16) | |
856 | 0 | (value[(c >> 2) & 0x03] << 8) | |
857 | 0 | value[c & 0x03]; |
858 | 0 | } |
859 | | |
860 | | static uint8_t CEA708AlphaConvert( uint8_t c ) |
861 | 0 | { |
862 | 0 | if( c == CEA708_OPACITY_TRANSLUCENT ) |
863 | 0 | return STYLE_ALPHA_OPAQUE / 2; |
864 | 0 | else if( c == CEA708_OPACITY_TRANSPARENT ) |
865 | 0 | return STYLE_ALPHA_TRANSPARENT; |
866 | 0 | else |
867 | 0 | return STYLE_ALPHA_OPAQUE; |
868 | 0 | } |
869 | | |
870 | | static void CEA708PenStyleToSegment( const cea708_pen_style_t *ps, text_style_t *s ) |
871 | 0 | { |
872 | 0 | if( ps->background.opacity != CEA708_OPACITY_TRANSPARENT ) |
873 | 0 | { |
874 | 0 | s->i_background_alpha = CEA708AlphaConvert( ps->background.opacity ); |
875 | 0 | s->i_style_flags |= STYLE_BACKGROUND; |
876 | 0 | s->i_background_color = CEA708ColorConvert( ps->background.color ); |
877 | 0 | s->i_features |= STYLE_HAS_BACKGROUND_COLOR|STYLE_HAS_BACKGROUND_ALPHA; |
878 | 0 | if( ps->background.opacity == CEA708_OPACITY_FLASH ) |
879 | 0 | s->i_style_flags |= STYLE_BLINK_BACKGROUND; |
880 | 0 | } |
881 | 0 | s->i_font_color = CEA708ColorConvert( ps->foreground.color ); |
882 | 0 | s->i_font_alpha = CEA708AlphaConvert( ps->foreground.opacity ); |
883 | 0 | s->i_features |= STYLE_HAS_FONT_ALPHA|STYLE_HAS_FONT_COLOR; |
884 | 0 | if( ps->foreground.opacity == CEA708_OPACITY_FLASH ) |
885 | 0 | s->i_style_flags |= STYLE_BLINK_FOREGROUND; |
886 | |
|
887 | 0 | if( ps->b_italics ) |
888 | 0 | s->i_style_flags |= STYLE_ITALIC; |
889 | 0 | if( ps->b_underline ) |
890 | 0 | s->i_style_flags |= STYLE_UNDERLINE; |
891 | |
|
892 | 0 | switch( ps->font ) |
893 | 0 | { |
894 | 0 | default: |
895 | 0 | case CEA708_FONT_UNDEFINED: |
896 | 0 | case CEA708_FONT_MONOSPACED: |
897 | 0 | case CEA708_FONT_MONO_SANS_SERIF: |
898 | 0 | s->i_style_flags |= STYLE_MONOSPACED; |
899 | 0 | break; |
900 | 0 | case CEA708_FONT_PROP: |
901 | 0 | case CEA708_FONT_PROP_SANS_SERIF: |
902 | 0 | case CEA708_FONT_CASUAL: |
903 | 0 | case CEA708_FONT_CURSIVE: |
904 | 0 | case CEA708_FONT_SMALL_CAPS: |
905 | 0 | break; |
906 | 0 | } |
907 | | |
908 | 0 | switch( ps->size ) |
909 | 0 | { |
910 | 0 | case CEA708_PEN_SIZE_SMALL: |
911 | 0 | s->f_font_relsize = CEA708_FONTRELSIZE_SMALL; |
912 | 0 | break; |
913 | 0 | case CEA708_PEN_SIZE_LARGE: |
914 | 0 | s->f_font_relsize = CEA708_FONTRELSIZE_LARGE; |
915 | 0 | break; |
916 | 0 | default: |
917 | 0 | s->f_font_relsize = CEA708_FONTRELSIZE_STANDARD; |
918 | 0 | break; |
919 | 0 | } |
920 | 0 | } |
921 | | |
922 | | static text_segment_t * CEA708CharsToSegment( const cea708_text_row_t *p_row, |
923 | | uint8_t i_start, uint8_t i_end, |
924 | | bool b_newline ) |
925 | 0 | { |
926 | 0 | text_segment_t *p_segment = text_segment_New( NULL ); |
927 | 0 | if( !p_segment ) |
928 | 0 | return NULL; |
929 | | |
930 | 0 | p_segment->style = text_style_Create( STYLE_NO_DEFAULTS ); |
931 | 0 | if( p_segment->style ) |
932 | 0 | CEA708PenStyleToSegment( &p_row->styles[i_start], p_segment->style ); |
933 | |
|
934 | 0 | p_segment->psz_text = malloc( 1U + !!b_newline + (i_end - i_start + 1) * 4U ); |
935 | 0 | if( !p_segment->psz_text ) |
936 | 0 | { |
937 | 0 | text_segment_Delete( p_segment ); |
938 | 0 | return NULL; |
939 | 0 | } |
940 | | |
941 | 0 | size_t offsetw = 0; |
942 | 0 | for( uint8_t i=i_start; i<=i_end; i++ ) |
943 | 0 | { |
944 | 0 | for( size_t j=0; j<4; j++ ) |
945 | 0 | { |
946 | 0 | if( p_row->characters[i * 4 + j] != 0 ) |
947 | 0 | p_segment->psz_text[offsetw++] = p_row->characters[i * 4 + j]; |
948 | 0 | else if( j == 0 ) |
949 | 0 | p_segment->psz_text[offsetw++] = ' '; |
950 | 0 | else |
951 | 0 | break; |
952 | 0 | } |
953 | 0 | } |
954 | |
|
955 | 0 | if( b_newline ) |
956 | 0 | p_segment->psz_text[offsetw++] = '\n'; |
957 | 0 | p_segment->psz_text[offsetw] = '\0'; |
958 | |
|
959 | 0 | return p_segment; |
960 | 0 | } |
961 | | |
962 | | static text_segment_t * CEA708RowToSegments( const cea708_text_row_t *p_row, |
963 | | bool b_addnewline ) |
964 | 0 | { |
965 | 0 | text_segment_t *p_segments = NULL; |
966 | 0 | text_segment_t **pp_last = &p_segments; |
967 | |
|
968 | 0 | uint8_t i_start = p_row->firstcol; |
969 | 0 | for( uint8_t i=i_start; i<=p_row->lastcol; i++ ) |
970 | 0 | { |
971 | 0 | if( i == p_row->lastcol || |
972 | 0 | memcmp( &p_row->styles[i], &p_row->styles[i+1], sizeof(cea708_pen_style_t) ) ) |
973 | 0 | { |
974 | 0 | *pp_last = CEA708CharsToSegment( p_row, i_start, i, |
975 | 0 | b_addnewline && (i == p_row->lastcol) ); |
976 | 0 | if( *pp_last ) |
977 | 0 | pp_last = &((*pp_last)->p_next); |
978 | 0 | i_start = i+1; |
979 | 0 | } |
980 | 0 | } |
981 | |
|
982 | 0 | return p_segments; |
983 | 0 | } |
984 | | |
985 | | static void CEA708SpuConvert( const cea708_window_t *p_w, |
986 | | substext_updater_region_t *p_region ) |
987 | 0 | { |
988 | 0 | if( !p_w->b_visible || CEA708_Window_RowCount( p_w ) == 0 ) |
989 | 0 | return; |
990 | | |
991 | 0 | if( p_region == NULL && !(p_region = SubpictureUpdaterSysRegionNew()) ) |
992 | 0 | return; |
993 | | |
994 | 0 | int first, last; |
995 | |
|
996 | 0 | if (p_w->style.scroll_direction == CEA708_WA_DIRECTION_BT) { |
997 | | /* BT is a bit of a special case since we need to grab the last N |
998 | | rows between first and last, rather than the first... */ |
999 | 0 | last = p_w->i_lastrow; |
1000 | 0 | if (p_w->i_lastrow - p_w->i_row_count < p_w->i_firstrow) |
1001 | 0 | first = p_w->i_firstrow; |
1002 | 0 | else |
1003 | 0 | first = p_w->i_lastrow - p_w->i_row_count + 1; |
1004 | |
|
1005 | 0 | } else { |
1006 | 0 | first = p_w->i_firstrow; |
1007 | 0 | if (p_w->i_firstrow + p_w->i_row_count > p_w->i_lastrow) |
1008 | 0 | last = p_w->i_lastrow; |
1009 | 0 | else |
1010 | 0 | last = p_w->i_firstrow + p_w->i_row_count - 1; |
1011 | 0 | } |
1012 | |
|
1013 | 0 | text_segment_t **pp_last = &p_region->p_segments; |
1014 | 0 | for( uint8_t i=first; i<=last; i++ ) |
1015 | 0 | { |
1016 | 0 | if( !p_w->rows[i] ) |
1017 | 0 | continue; |
1018 | | |
1019 | 0 | *pp_last = CEA708RowToSegments( p_w->rows[i], i < p_w->i_lastrow ); |
1020 | 0 | if( *pp_last ) |
1021 | 0 | pp_last = &((*pp_last)->p_next); |
1022 | 0 | } |
1023 | |
|
1024 | 0 | if( p_w->b_relative ) |
1025 | 0 | { |
1026 | | /* FIXME: take into account left/right anchors */ |
1027 | 0 | p_region->origin.x = p_w->i_anchor_offset_h / 100.0; |
1028 | |
|
1029 | 0 | switch (p_w->anchor_point) { |
1030 | 0 | case CEA708_ANCHOR_TOP_LEFT: |
1031 | 0 | case CEA708_ANCHOR_TOP_CENTER: |
1032 | 0 | case CEA708_ANCHOR_TOP_RIGHT: |
1033 | 0 | p_region->origin.y = p_w->i_anchor_offset_v / 100.0; |
1034 | 0 | break; |
1035 | 0 | case CEA708_ANCHOR_BOTTOM_LEFT: |
1036 | 0 | case CEA708_ANCHOR_BOTTOM_CENTER: |
1037 | 0 | case CEA708_ANCHOR_BOTTOM_RIGHT: |
1038 | 0 | p_region->origin.y = 1.0 - (p_w->i_anchor_offset_v / 100.0); |
1039 | 0 | break; |
1040 | 0 | default: |
1041 | | /* FIXME: for CENTER vertical justified, just position as top */ |
1042 | 0 | p_region->origin.y = p_w->i_anchor_offset_v / 100.0; |
1043 | 0 | break; |
1044 | 0 | } |
1045 | 0 | } |
1046 | 0 | else |
1047 | 0 | { |
1048 | 0 | p_region->origin.x = (float)p_w->i_anchor_offset_h / CEA708_SCREEN_COLS_169; |
1049 | 0 | p_region->origin.y = (float)p_w->i_anchor_offset_v / |
1050 | 0 | (CEA708_SCREEN_ROWS * CEA708_FONT_TO_LINE_HEIGHT_RATIO); |
1051 | 0 | } |
1052 | 0 | p_region->flags |= UPDT_REGION_ORIGIN_X_IS_RATIO|UPDT_REGION_ORIGIN_Y_IS_RATIO; |
1053 | 0 | p_region->b_absolute = false; p_region->b_in_window = false; |
1054 | |
|
1055 | 0 | if( p_w->i_firstrow <= p_w->i_lastrow ) |
1056 | 0 | { |
1057 | 0 | p_region->origin.y += p_w->i_firstrow * CEA708_ROW_HEIGHT_STANDARD; |
1058 | | /*const uint8_t i_min = CEA708_Window_MinCol( p_w ); |
1059 | | if( i_min < CEA708_WINDOW_MAX_COLS ) |
1060 | | p_region->origin.x += (float) i_min / CEA708_WINDOW_MAX_COLS;*/ |
1061 | 0 | } |
1062 | |
|
1063 | 0 | if( p_w->anchor_point <= CEA708_ANCHOR_BOTTOM_RIGHT ) |
1064 | 0 | { |
1065 | 0 | static const int vlc_subpicture_aligns[] = |
1066 | 0 | { |
1067 | 0 | [CEA708_ANCHOR_TOP_LEFT] = SUBPICTURE_ALIGN_TOP|SUBPICTURE_ALIGN_LEFT, |
1068 | 0 | [CEA708_ANCHOR_TOP_CENTER] = SUBPICTURE_ALIGN_TOP, |
1069 | 0 | [CEA708_ANCHOR_TOP_RIGHT] = SUBPICTURE_ALIGN_TOP|SUBPICTURE_ALIGN_RIGHT, |
1070 | 0 | [CEA708_ANCHOR_CENTER_LEFT] = SUBPICTURE_ALIGN_LEFT, |
1071 | 0 | [CEA708_ANCHOR_CENTER_CENTER] = 0, |
1072 | 0 | [CEA708_ANCHOR_CENTER_RIGHT] = SUBPICTURE_ALIGN_RIGHT, |
1073 | 0 | [CEA708_ANCHOR_BOTTOM_LEFT] = SUBPICTURE_ALIGN_BOTTOM|SUBPICTURE_ALIGN_LEFT, |
1074 | 0 | [CEA708_ANCHOR_BOTTOM_CENTER] = SUBPICTURE_ALIGN_BOTTOM, |
1075 | 0 | [CEA708_ANCHOR_BOTTOM_RIGHT] = SUBPICTURE_ALIGN_BOTTOM|SUBPICTURE_ALIGN_RIGHT, |
1076 | 0 | }; |
1077 | 0 | p_region->align = vlc_subpicture_aligns[p_w->anchor_point]; |
1078 | 0 | } |
1079 | 0 | p_region->inner_align = SUBPICTURE_ALIGN_BOTTOM|SUBPICTURE_ALIGN_LEFT; |
1080 | 0 | } |
1081 | | |
1082 | | static subpicture_t *CEA708_BuildSubtitle( cea708_t *p_cea708 ) |
1083 | 0 | { |
1084 | 0 | subpicture_t *p_spu = decoder_NewSubpictureText( p_cea708->p_dec ); |
1085 | 0 | if( !p_spu ) |
1086 | 0 | return NULL; |
1087 | | |
1088 | 0 | subtext_updater_sys_t *p_spu_sys = p_spu->updater.sys; |
1089 | 0 | substext_updater_region_t *p_region = &p_spu_sys->region; |
1090 | |
|
1091 | 0 | p_spu_sys->margin_ratio = CEA708_SCREEN_SAFE_MARGIN_RATIO; |
1092 | |
|
1093 | 0 | bool first = true; |
1094 | |
|
1095 | 0 | for(size_t i=0; i<CEA708_WINDOWS_COUNT; i++) |
1096 | 0 | { |
1097 | 0 | cea708_window_t *p_w = &p_cea708->window[i]; |
1098 | 0 | if( p_w->b_defined && p_w->b_visible && CEA708_Window_RowCount( p_w ) ) |
1099 | 0 | { |
1100 | 0 | if( !first ) |
1101 | 0 | { |
1102 | 0 | substext_updater_region_t *p_newregion = |
1103 | 0 | SubpictureUpdaterSysRegionNew(); |
1104 | 0 | if( p_newregion == NULL ) |
1105 | 0 | break; |
1106 | 0 | SubpictureUpdaterSysRegionAdd( p_region, p_newregion ); |
1107 | 0 | p_region = p_newregion; |
1108 | 0 | } |
1109 | 0 | first = false; |
1110 | | |
1111 | | /* Fill region */ |
1112 | 0 | CEA708SpuConvert( p_w, p_region ); |
1113 | 0 | } |
1114 | 0 | } |
1115 | |
|
1116 | 0 | p_spu->i_start = p_cea708->i_clock; |
1117 | 0 | p_spu->i_stop = p_cea708->i_clock + VLC_TICK_FROM_SEC(10); /* 10s max */ |
1118 | |
|
1119 | 0 | p_spu->b_ephemer = true; |
1120 | 0 | p_spu->b_subtitle = true; |
1121 | |
|
1122 | 0 | return p_spu; |
1123 | 0 | } |
1124 | | |
1125 | | static void CEA708_Decoder_Init( cea708_t *p_cea708 ) |
1126 | 2 | { |
1127 | 2 | cea708_input_buffer_init( &p_cea708->input_buffer ); |
1128 | 18 | for(size_t i=0; i<CEA708_WINDOWS_COUNT; i++) |
1129 | 16 | CEA708_Window_Init( &p_cea708->window[i] ); |
1130 | 2 | p_cea708->p_cw = &p_cea708->window[0]; |
1131 | 2 | p_cea708->suspended_deadline = VLC_TICK_INVALID; |
1132 | 2 | p_cea708->b_text_waiting = false; |
1133 | 2 | p_cea708->i_clock = 0; |
1134 | 2 | } |
1135 | | |
1136 | | static void CEA708_Decoder_Reset( cea708_t *p_cea708 ) |
1137 | 1 | { |
1138 | 9 | for(size_t i=0; i<CEA708_WINDOWS_COUNT; i++) |
1139 | 8 | CEA708_Window_Reset( &p_cea708->window[i] ); |
1140 | 1 | CEA708_Decoder_Init( p_cea708 ); |
1141 | 1 | } |
1142 | | |
1143 | | void CEA708_Decoder_Flush( cea708_t *p_cea708 ) |
1144 | 0 | { |
1145 | 0 | CEA708_Decoder_Reset( p_cea708 ); |
1146 | 0 | } |
1147 | | |
1148 | | void CEA708_Decoder_Release( cea708_t *p_cea708 ) |
1149 | 1 | { |
1150 | 1 | CEA708_Decoder_Reset( p_cea708 ); |
1151 | 1 | free( p_cea708 ); |
1152 | 1 | } |
1153 | | |
1154 | | cea708_t * CEA708_Decoder_New( decoder_t *p_dec ) |
1155 | 1 | { |
1156 | 1 | cea708_t *p_cea708 = malloc( sizeof(cea708_t) ); |
1157 | 1 | if( p_cea708 ) |
1158 | 1 | { |
1159 | 1 | CEA708_Decoder_Init( p_cea708 ); |
1160 | 1 | p_cea708->p_dec = p_dec; |
1161 | 1 | } |
1162 | 1 | return p_cea708; |
1163 | 1 | } |
1164 | | |
1165 | 0 | #define POP_COMMAND() (void) cea708_input_buffer_get( ib ) |
1166 | 0 | #define POP_ARGS(n) for(size_t pops=0; pops<(size_t)n;pops++) POP_COMMAND() |
1167 | 0 | #define REQUIRE_ARGS(n) if(cea708_input_buffer_size( ib ) < n + 1)\ |
1168 | 0 | return CEA708_STATUS_STARVING |
1169 | 0 | #define REQUIRE_ARGS_AND_POP_COMMAND(n) REQUIRE_ARGS(n); else POP_COMMAND() |
1170 | | |
1171 | | static void CEA708_Output( cea708_t *p_cea708 ) |
1172 | 0 | { |
1173 | 0 | Debug(printf("@%ld ms\n", MS_FROM_VLC_TICK(p_cea708->i_clock))); |
1174 | 0 | subpicture_t *p_spu = CEA708_BuildSubtitle( p_cea708 ); |
1175 | 0 | if( p_spu ) |
1176 | 0 | decoder_QueueSub( p_cea708->p_dec, p_spu ); |
1177 | 0 | } |
1178 | | |
1179 | | static int CEA708_Decode_C0( uint8_t code, cea708_t *p_cea708 ) |
1180 | 0 | { |
1181 | 0 | uint8_t v, i; |
1182 | 0 | uint16_t u16; |
1183 | 0 | cea708_input_buffer_t *ib = &p_cea708->input_buffer; |
1184 | 0 | int i_ret = CEA708_STATUS_OK; |
1185 | |
|
1186 | 0 | switch( code ) |
1187 | 0 | { |
1188 | 0 | case CEA708_C0_NUL: |
1189 | 0 | POP_COMMAND(); |
1190 | 0 | break; |
1191 | 0 | case CEA708_C0_ETX: |
1192 | 0 | POP_COMMAND(); |
1193 | 0 | if( p_cea708->b_text_waiting ) |
1194 | 0 | { |
1195 | 0 | i_ret |= CEA708_STATUS_OUTPUT; |
1196 | 0 | p_cea708->b_text_waiting = false; |
1197 | 0 | } |
1198 | 0 | break; |
1199 | 0 | case CEA708_C0_BS: |
1200 | 0 | POP_COMMAND(); |
1201 | 0 | if( !p_cea708->p_cw->b_defined ) |
1202 | 0 | break; |
1203 | 0 | CEA708_Window_Backward( p_cea708->p_cw ); |
1204 | 0 | p_cea708->b_text_waiting = true; |
1205 | 0 | break; |
1206 | 0 | case CEA708_C0_FF: |
1207 | 0 | POP_COMMAND(); |
1208 | 0 | if( !p_cea708->p_cw->b_defined ) |
1209 | 0 | break; |
1210 | 0 | CEA708_Window_ClearText( p_cea708->p_cw ); |
1211 | 0 | p_cea708->p_cw->col = 0; |
1212 | 0 | p_cea708->p_cw->row = 0; |
1213 | 0 | p_cea708->b_text_waiting = true; |
1214 | 0 | break; |
1215 | 0 | case CEA708_C0_CR: |
1216 | 0 | POP_COMMAND(); |
1217 | 0 | if( !p_cea708->p_cw->b_defined ) |
1218 | 0 | break; |
1219 | 0 | if( p_cea708->p_cw->style.print_direction <= CEA708_WA_DIRECTION_RTL ) |
1220 | 0 | { |
1221 | 0 | CEA708_Window_CarriageReturn( p_cea708->p_cw ); |
1222 | 0 | if( p_cea708->p_cw->b_visible ) |
1223 | 0 | i_ret |= CEA708_STATUS_OUTPUT; |
1224 | 0 | } |
1225 | 0 | break; |
1226 | 0 | case CEA708_C0_HCR: |
1227 | 0 | POP_COMMAND(); |
1228 | 0 | if( !p_cea708->p_cw->b_defined ) |
1229 | 0 | break; |
1230 | 0 | if( p_cea708->p_cw->style.print_direction > CEA708_WA_DIRECTION_RTL ) |
1231 | 0 | { |
1232 | 0 | CEA708_Window_CarriageReturn( p_cea708->p_cw ); |
1233 | 0 | if( p_cea708->p_cw->b_visible ) |
1234 | 0 | i_ret |= CEA708_STATUS_OUTPUT; |
1235 | 0 | } |
1236 | 0 | break; |
1237 | 0 | case CEA708_C0_EXT1: /* Special extended table case */ |
1238 | 0 | if( cea708_input_buffer_size( ib ) >= 2 ) |
1239 | 0 | { |
1240 | 0 | v = cea708_input_buffer_peek( ib, 1 ); |
1241 | | /* C2 extended code set */ |
1242 | 0 | if( v < 0x20 ) |
1243 | 0 | { |
1244 | 0 | if( v > 0x17 ) |
1245 | 0 | i = 3; |
1246 | 0 | else if( v > 0x0f ) |
1247 | 0 | i = 2; |
1248 | 0 | else if( v > 0x07 ) |
1249 | 0 | i = 1; |
1250 | 0 | else |
1251 | 0 | i = 0; |
1252 | 0 | if( cea708_input_buffer_size( ib ) < 2 + i ) |
1253 | 0 | return CEA708_STATUS_STARVING; |
1254 | 0 | POP_COMMAND(); |
1255 | 0 | POP_ARGS(1 + i); |
1256 | 0 | } |
1257 | | /* C3 extended code set */ |
1258 | 0 | else if( v > 0x7f && v < 0xa0 ) |
1259 | 0 | { |
1260 | 0 | if( v > 0x87 ) |
1261 | 0 | i = 5; |
1262 | 0 | else |
1263 | 0 | i = 4; |
1264 | 0 | if( cea708_input_buffer_size( ib ) < 2 + i ) |
1265 | 0 | return CEA708_STATUS_STARVING; |
1266 | 0 | POP_COMMAND(); |
1267 | 0 | POP_ARGS(1 + i); |
1268 | 0 | } |
1269 | 0 | else |
1270 | 0 | { |
1271 | 0 | POP_COMMAND(); |
1272 | 0 | v = cea708_input_buffer_get( ib ); |
1273 | 0 | if( p_cea708->p_cw->b_defined ) |
1274 | 0 | i_ret |= CEA708_Decode_G2G3( v, p_cea708 ); |
1275 | 0 | } |
1276 | 0 | } |
1277 | 0 | else return CEA708_STATUS_STARVING; |
1278 | 0 | break; |
1279 | 0 | case CEA708_C0_P16: |
1280 | 0 | REQUIRE_ARGS_AND_POP_COMMAND(2); |
1281 | 0 | u16 = cea708_input_buffer_get( ib ) << 8; |
1282 | 0 | u16 |= cea708_input_buffer_get( ib ); |
1283 | 0 | i_ret |= CEA708_Decode_P16( u16, p_cea708 ); |
1284 | 0 | Debug(printf("[P16 %x]", u16)); |
1285 | 0 | break; |
1286 | 0 | default: |
1287 | 0 | POP_COMMAND(); |
1288 | 0 | Debug(printf("[UNK %2.2x]", code)); |
1289 | 0 | break; |
1290 | 0 | } |
1291 | 0 | Debug(printf("[C0 %x]", code)); |
1292 | 0 | return i_ret; |
1293 | 0 | } |
1294 | | |
1295 | | static int CEA708_Decode_G0( uint8_t code, cea708_t *p_cea708 ) |
1296 | 0 | { |
1297 | 0 | cea708_input_buffer_t *ib = &p_cea708->input_buffer; |
1298 | 0 | POP_COMMAND(); |
1299 | 0 | int i_ret = CEA708_STATUS_OK; |
1300 | |
|
1301 | 0 | if( !p_cea708->p_cw->b_defined ) |
1302 | 0 | return i_ret; |
1303 | | |
1304 | 0 | uint8_t utf8[4] = {code,0x00,0x00,0x00}; |
1305 | |
|
1306 | 0 | if(code == 0x7F) // Music note |
1307 | 0 | { |
1308 | 0 | utf8[0] = 0xe2; |
1309 | 0 | utf8[1] = 0x99; |
1310 | 0 | utf8[2] = 0xaa; |
1311 | 0 | } |
1312 | |
|
1313 | 0 | CEA708_Window_Write( utf8, p_cea708->p_cw ); |
1314 | |
|
1315 | 0 | if( code == 0x20 && |
1316 | 0 | p_cea708->b_text_waiting && |
1317 | 0 | CEA708_Window_BreaksSpace( p_cea708->p_cw ) ) |
1318 | 0 | { |
1319 | 0 | i_ret |= CEA708_STATUS_OUTPUT; |
1320 | 0 | } |
1321 | | |
1322 | |
|
1323 | 0 | p_cea708->b_text_waiting |= p_cea708->p_cw->b_visible; |
1324 | |
|
1325 | 0 | return i_ret; |
1326 | 0 | } |
1327 | | |
1328 | | static int CEA708_Decode_C1( uint8_t code, cea708_t *p_cea708 ) |
1329 | 0 | { |
1330 | 0 | uint8_t v, i; |
1331 | 0 | cea708_input_buffer_t *ib = &p_cea708->input_buffer; |
1332 | 0 | int i_ret = CEA708_STATUS_OK; |
1333 | |
|
1334 | 0 | if( p_cea708->b_text_waiting ) |
1335 | 0 | { |
1336 | 0 | i_ret |= CEA708_STATUS_OUTPUT; |
1337 | 0 | p_cea708->b_text_waiting = false; |
1338 | 0 | } |
1339 | |
|
1340 | 0 | switch( code ) |
1341 | 0 | { |
1342 | 0 | case CEA708_C1_CLW: |
1343 | 0 | REQUIRE_ARGS_AND_POP_COMMAND(1); |
1344 | 0 | Debug(printf("[CLW")); |
1345 | 0 | for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ ) |
1346 | 0 | if( v & 1 ) |
1347 | 0 | { |
1348 | 0 | if( p_cea708->window[i].b_defined && |
1349 | 0 | p_cea708->window[i].b_visible ) |
1350 | 0 | i_ret |= CEA708_STATUS_OUTPUT; |
1351 | 0 | CEA708_Window_ClearText( &p_cea708->window[i] ); |
1352 | 0 | Debug(printf("%d", i)); |
1353 | 0 | } |
1354 | 0 | Debug(printf("]")); |
1355 | 0 | break; |
1356 | 0 | case CEA708_C1_DSW: |
1357 | 0 | REQUIRE_ARGS_AND_POP_COMMAND(1); |
1358 | 0 | Debug(printf("[DSW")); |
1359 | 0 | for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ ) |
1360 | 0 | if( v & 1 ) |
1361 | 0 | { |
1362 | 0 | if( p_cea708->window[i].b_defined ) |
1363 | 0 | { |
1364 | 0 | if( !p_cea708->window[i].b_visible ) |
1365 | 0 | i_ret |= CEA708_STATUS_OUTPUT; |
1366 | 0 | p_cea708->window[i].b_visible = true; |
1367 | 0 | } |
1368 | 0 | Debug(printf("%d", i)); |
1369 | 0 | } |
1370 | 0 | Debug(printf("]")); |
1371 | 0 | break; |
1372 | 0 | case CEA708_C1_HDW: |
1373 | 0 | REQUIRE_ARGS_AND_POP_COMMAND(1); |
1374 | 0 | Debug(printf("[HDW")); |
1375 | 0 | for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ ) |
1376 | 0 | if( v & 1 ) |
1377 | 0 | { |
1378 | 0 | if( p_cea708->window[i].b_defined ) |
1379 | 0 | { |
1380 | 0 | if( p_cea708->window[i].b_visible ) |
1381 | 0 | i_ret |= CEA708_STATUS_OUTPUT; |
1382 | 0 | p_cea708->window[i].b_visible = false; |
1383 | 0 | } |
1384 | 0 | Debug(printf("%d", i)); |
1385 | 0 | } |
1386 | 0 | Debug(printf("]")); |
1387 | 0 | break; |
1388 | 0 | case CEA708_C1_TGW: |
1389 | 0 | REQUIRE_ARGS_AND_POP_COMMAND(1); |
1390 | 0 | Debug(printf("[TGW")); |
1391 | 0 | for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ ) |
1392 | 0 | if( v & 1 ) |
1393 | 0 | { |
1394 | 0 | if( p_cea708->window[i].b_defined ) |
1395 | 0 | { |
1396 | 0 | i_ret |= CEA708_STATUS_OUTPUT; |
1397 | 0 | p_cea708->window[i].b_visible = !p_cea708->window[i].b_visible; |
1398 | 0 | } |
1399 | 0 | Debug(printf("%d", i)); |
1400 | 0 | } |
1401 | 0 | Debug(printf("]")); |
1402 | 0 | break; |
1403 | 0 | case CEA708_C1_DLW: |
1404 | 0 | REQUIRE_ARGS_AND_POP_COMMAND(1); |
1405 | 0 | Debug(printf("[DLW")); |
1406 | 0 | for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ ) |
1407 | 0 | if( v & 1 ) |
1408 | 0 | { |
1409 | 0 | if( p_cea708->window[i].b_defined ) |
1410 | 0 | { |
1411 | 0 | if( p_cea708->window[i].b_visible ) |
1412 | 0 | i_ret |= CEA708_STATUS_OUTPUT; |
1413 | 0 | CEA708_Window_Reset( &p_cea708->window[i] ); |
1414 | 0 | } |
1415 | 0 | Debug(printf("%d", i)); |
1416 | 0 | } |
1417 | 0 | Debug(printf("]")); |
1418 | 0 | break; |
1419 | 0 | case CEA708_C1_DLY: |
1420 | 0 | REQUIRE_ARGS_AND_POP_COMMAND(1); |
1421 | 0 | p_cea708->suspended_deadline = p_cea708->i_clock + |
1422 | 0 | VLC_TICK_FROM_MS( cea708_input_buffer_get( ib ) * 100 ); |
1423 | 0 | Debug(printf("[DLY]")); |
1424 | 0 | break; |
1425 | 0 | case CEA708_C1_DLC: |
1426 | 0 | POP_COMMAND(); |
1427 | 0 | p_cea708->suspended_deadline = VLC_TICK_INVALID; |
1428 | 0 | Debug(printf("[DLC]")); |
1429 | 0 | break; |
1430 | 0 | case CEA708_C1_RST: |
1431 | 0 | POP_COMMAND(); |
1432 | 0 | i_ret |= CEA708_STATUS_OUTPUT; |
1433 | | /* FIXME */ |
1434 | 0 | break; |
1435 | 0 | case CEA708_C1_SPA: |
1436 | 0 | REQUIRE_ARGS_AND_POP_COMMAND(2); |
1437 | 0 | if( !p_cea708->p_cw->b_defined ) |
1438 | 0 | { |
1439 | 0 | POP_ARGS(2); |
1440 | 0 | break; |
1441 | 0 | } |
1442 | 0 | v = cea708_input_buffer_get( ib ); |
1443 | 0 | p_cea708->p_cw->pen.text_tag = v >> 4; |
1444 | 0 | p_cea708->p_cw->pen.offset = (v >> 2) & 0x03; |
1445 | 0 | p_cea708->p_cw->pen.size = v & 0x03; |
1446 | 0 | v = cea708_input_buffer_get( ib ); |
1447 | 0 | p_cea708->p_cw->pen.b_italics = v & 0x80; |
1448 | 0 | p_cea708->p_cw->pen.b_underline = v & 0x40; |
1449 | 0 | p_cea708->p_cw->pen.edge_type = (v >> 3) & 0x07; |
1450 | 0 | p_cea708->p_cw->pen.font = v & 0x07; |
1451 | 0 | Debug(printf("[SPA]")); |
1452 | 0 | break; |
1453 | 0 | case CEA708_C1_SPC: |
1454 | 0 | REQUIRE_ARGS_AND_POP_COMMAND(3); |
1455 | 0 | if( !p_cea708->p_cw->b_defined ) |
1456 | 0 | { |
1457 | 0 | POP_ARGS(3); |
1458 | 0 | break; |
1459 | 0 | } |
1460 | 0 | v = cea708_input_buffer_get( ib ); |
1461 | 0 | p_cea708->p_cw->pen.foreground.opacity = v >> 6; |
1462 | 0 | p_cea708->p_cw->pen.foreground.color = v & 0x3F; |
1463 | 0 | v = cea708_input_buffer_get( ib ); |
1464 | 0 | p_cea708->p_cw->pen.background.opacity = v >> 6; |
1465 | 0 | p_cea708->p_cw->pen.background.color = v & 0x3F; |
1466 | 0 | v = cea708_input_buffer_get( ib ); |
1467 | 0 | p_cea708->p_cw->pen.edge_color = v & 0x3F; |
1468 | 0 | Debug(printf("[SPC]")); |
1469 | 0 | break; |
1470 | 0 | case CEA708_C1_SPL: |
1471 | 0 | REQUIRE_ARGS_AND_POP_COMMAND(2); |
1472 | 0 | if( !p_cea708->p_cw->b_defined ) |
1473 | 0 | { |
1474 | 0 | POP_ARGS(2); |
1475 | 0 | break; |
1476 | 0 | } |
1477 | 0 | v = cea708_input_buffer_get( ib ); |
1478 | 0 | p_cea708->p_cw->row = (v & 0x0F) % CEA708_WINDOW_MAX_ROWS; |
1479 | 0 | v = cea708_input_buffer_get( ib ); |
1480 | 0 | p_cea708->p_cw->col = (v & 0x3F) % CEA708_WINDOW_MAX_COLS; |
1481 | 0 | Debug(printf("[SPL r%d c%d]", p_cea708->p_cw->row, p_cea708->p_cw->col)); |
1482 | 0 | break; |
1483 | 0 | case CEA708_C1_SWA: |
1484 | 0 | REQUIRE_ARGS_AND_POP_COMMAND(4); |
1485 | 0 | if( !p_cea708->p_cw->b_defined ) |
1486 | 0 | { |
1487 | 0 | POP_ARGS(4); |
1488 | 0 | break; |
1489 | 0 | } |
1490 | 0 | v = cea708_input_buffer_get( ib ); |
1491 | 0 | p_cea708->p_cw->style.fill_opacity = v >> 6; |
1492 | 0 | p_cea708->p_cw->style.fill_color_color = v & 0x3F; |
1493 | 0 | v = cea708_input_buffer_get( ib ); |
1494 | 0 | p_cea708->p_cw->style.border_color_color = v & 0x3F; |
1495 | 0 | p_cea708->p_cw->style.border_type = v >> 6; |
1496 | 0 | v = cea708_input_buffer_get( ib ); |
1497 | 0 | p_cea708->p_cw->style.border_type |= ((v & 0x80) >> 5); |
1498 | 0 | p_cea708->p_cw->style.b_word_wrap = v & 0x40; |
1499 | 0 | p_cea708->p_cw->style.print_direction = (v >> 4) & 0x03; |
1500 | 0 | p_cea708->p_cw->style.scroll_direction = (v >> 2) & 0x03; |
1501 | 0 | p_cea708->p_cw->style.justify = v & 0x03; |
1502 | 0 | v = cea708_input_buffer_get( ib ); |
1503 | 0 | p_cea708->p_cw->style.effect_speed = v >> 4; |
1504 | 0 | p_cea708->p_cw->style.effect_direction = (v >> 2) & 0x03; |
1505 | 0 | p_cea708->p_cw->style.display_effect = v & 0x03; |
1506 | 0 | Debug(printf("[SWA]")); |
1507 | 0 | break; |
1508 | | |
1509 | 0 | default: |
1510 | 0 | if( code >= CEA708_C1_CW0 && code <= CEA708_C1_CW7 ) |
1511 | 0 | { |
1512 | 0 | POP_COMMAND(); |
1513 | 0 | Debug(printf("[CW%d]", code - CEA708_C1_CW0)); |
1514 | 0 | if( p_cea708->window[code - CEA708_C1_CW0].b_defined ) |
1515 | 0 | p_cea708->p_cw = &p_cea708->window[code - CEA708_C1_CW0]; |
1516 | 0 | } |
1517 | 0 | else if( code >= CEA708_C1_DF0 && code <= CEA708_C1_DF7 ) |
1518 | 0 | { |
1519 | 0 | REQUIRE_ARGS_AND_POP_COMMAND(6); |
1520 | 0 | Debug(printf("[DF%d]", code - CEA708_C1_DF0)); |
1521 | | /* also sets current window */ |
1522 | 0 | p_cea708->p_cw = &p_cea708->window[code - CEA708_C1_DF0]; |
1523 | 0 | v = cea708_input_buffer_get( ib ); |
1524 | 0 | if( p_cea708->p_cw->b_defined && |
1525 | 0 | !p_cea708->p_cw->b_visible != !(v & 0x20) ) |
1526 | 0 | i_ret |= CEA708_STATUS_OUTPUT; |
1527 | 0 | p_cea708->p_cw->b_visible = v & 0x20; |
1528 | 0 | p_cea708->p_cw->b_row_lock = v & 0x10; |
1529 | 0 | p_cea708->p_cw->b_column_lock = v & 0x08; |
1530 | 0 | p_cea708->p_cw->i_priority = v & 0x07; |
1531 | 0 | v = cea708_input_buffer_get( ib ); |
1532 | 0 | p_cea708->p_cw->b_relative = v & 0x80; |
1533 | 0 | p_cea708->p_cw->i_anchor_offset_v = v & 0x7F; |
1534 | 0 | v = cea708_input_buffer_get( ib ); |
1535 | 0 | p_cea708->p_cw->i_anchor_offset_h = v; |
1536 | 0 | v = cea708_input_buffer_get( ib ); |
1537 | 0 | p_cea708->p_cw->anchor_point = v >> 4; |
1538 | 0 | p_cea708->p_cw->i_row_count = (v & 0x0F) + 1; |
1539 | 0 | v = cea708_input_buffer_get( ib ); |
1540 | 0 | p_cea708->p_cw->i_col_count = v & 0x3F; |
1541 | 0 | v = cea708_input_buffer_get( ib ); |
1542 | | /* zero values style set on init, avoid dealing with updt case */ |
1543 | 0 | i = (v >> 3) & 0x07; /* Window style id */ |
1544 | 0 | if( i > 0 ) |
1545 | 0 | p_cea708->p_cw->style = cea708_default_window_styles[i-1]; |
1546 | 0 | else if( !p_cea708->p_cw->b_defined ) /* Set to style #1 or ignore */ |
1547 | 0 | p_cea708->p_cw->style = cea708_default_window_styles[0]; |
1548 | 0 | i = v & 0x07; /* Pen style id */ |
1549 | 0 | if( i > 0 ) |
1550 | 0 | p_cea708->p_cw->pen = cea708_default_pen_styles[i-1]; |
1551 | 0 | else if( !p_cea708->p_cw->b_defined ) /* Set to style #1 or ignore */ |
1552 | 0 | p_cea708->p_cw->pen = cea708_default_pen_styles[0]; |
1553 | 0 | p_cea708->p_cw->b_defined = true; |
1554 | 0 | } |
1555 | 0 | else |
1556 | 0 | { |
1557 | 0 | Debug(printf("{%2.2x}", code)); |
1558 | 0 | POP_COMMAND(); |
1559 | 0 | } |
1560 | 0 | } |
1561 | | |
1562 | 0 | return i_ret; |
1563 | 0 | } |
1564 | | |
1565 | | static int CEA708_Decode_G1( uint8_t code, cea708_t *p_cea708 ) |
1566 | 0 | { |
1567 | 0 | cea708_input_buffer_t *ib = &p_cea708->input_buffer; |
1568 | 0 | POP_COMMAND(); |
1569 | |
|
1570 | 0 | if( !p_cea708->p_cw->b_defined ) |
1571 | 0 | return CEA708_STATUS_OK; |
1572 | | |
1573 | 0 | uint8_t utf8[4] = {0xc0 | (code & 0xc0) >> 6, |
1574 | 0 | 0x80 | (code & 0x3f), |
1575 | 0 | 0, 0}; |
1576 | |
|
1577 | 0 | CEA708_Window_Write( utf8, p_cea708->p_cw ); |
1578 | 0 | p_cea708->b_text_waiting |= p_cea708->p_cw->b_visible; |
1579 | |
|
1580 | 0 | return CEA708_STATUS_OK; |
1581 | 0 | } |
1582 | | |
1583 | | static int CEA708_Decode_G2G3( uint8_t code, cea708_t *p_cea708 ) |
1584 | 0 | { |
1585 | 0 | if( !p_cea708->p_cw->b_defined ) |
1586 | 0 | return CEA708_STATUS_OK; |
1587 | | |
1588 | 0 | uint8_t out[4] = { '?', 0, 0, 0 }; |
1589 | 0 | static const struct { |
1590 | 0 | uint8_t c; |
1591 | 0 | uint8_t utf8[4]; |
1592 | 0 | } code2utf8[] = { |
1593 | | /* G2 */ |
1594 | 0 | { 0x20, { 0x20 } },// transparent space [*** will need special handling] |
1595 | 0 | { 0x21, { 0x20 } },// non breaking transparent space [*** will need special handling] |
1596 | 0 | { 0x25, { 0xe2,0x80,0xa6 } },// HORIZONTAL ELLIPSIS |
1597 | 0 | { 0x2a, { 0xc5,0xa0 } },// LATIN CAPITAL LETTER S WITH CARON |
1598 | 0 | { 0x2c, { 0xc5,0x92 } },// LATIN CAPITAL LIGATURE OE |
1599 | 0 | { 0x30, { 0xe2,0x96,0x88 } },// FULL BLOCK |
1600 | 0 | { 0x31, { 0xe2,0x80,0x98 } },// LEFT SINGLE QUOTATION MARK |
1601 | 0 | { 0x32, { 0xe2,0x80,0x99 } },// RIGHT SINGLE QUOTATION MARK |
1602 | 0 | { 0x33, { 0xe2,0x80,0x9c } },// LEFT DOUBLE QUOTATION MARK |
1603 | 0 | { 0x34, { 0xe2,0x80,0x9d } },// RIGHT DOUBLE QUOTATION MARK |
1604 | 0 | { 0x35, { 0xe2,0x80,0xa2 } },// BULLET |
1605 | 0 | { 0x39, { 0xe2,0x84,0xa2 } },// Trademark symbol (TM) |
1606 | 0 | { 0x3a, { 0xc5,0xa1 } },// LATIN SMALL LETTER S WITH CARON |
1607 | 0 | { 0x3c, { 0xc5,0x93 } },// LATIN SMALL LIGATURE OE |
1608 | 0 | { 0x3d, { 0xe2,0x84,0xa0 } },// SERVICE MARK |
1609 | 0 | { 0x3f, { 0xc5,0xb8 } },// LATIN CAPITAL LETTER Y WITH DIAERESIS |
1610 | 0 | { 0x76, { 0xe2,0x85,0x9b } },// VULGAR FRACTION ONE EIGHTH |
1611 | 0 | { 0x77, { 0xe2,0x85,0x9c } },// VULGAR FRACTION THREE EIGHTHS |
1612 | 0 | { 0x78, { 0xe2,0x85,0x9d } },// VULGAR FRACTION FIVE EIGHTHS |
1613 | 0 | { 0x79, { 0xe2,0x85,0x9e } },// VULGAR FRACTION SEVEN EIGHTHS |
1614 | 0 | { 0x7a, { 0xe2,0x94,0x82 } },// BOX DRAWINGS LIGHT VERTICAL |
1615 | 0 | { 0x7b, { 0xe2,0x94,0x90 } },// BOX DRAWINGS LIGHT DOWN AND LEFT |
1616 | 0 | { 0x7c, { 0xe2,0x94,0x94 } },// BOX DRAWINGS LIGHT UP AND RIGHT |
1617 | 0 | { 0x7d, { 0xe2,0x94,0x80 } },// BOX DRAWINGS LIGHT HORIZONTAL |
1618 | 0 | { 0x7e, { 0xe2,0x94,0x98 } },// BOX DRAWINGS LIGHT UP AND LEFT |
1619 | 0 | { 0x7f, { 0xe2,0x94,0x8c } },// BOX DRAWINGS LIGHT DOWN AND RIGHT |
1620 | | /* G3 */ |
1621 | 0 | { 0xa0, { 0xf0,0x9f,0x85,0xb2 } },// CC (replaced with NEGATIVE SQUARED LATIN CAPITAL LETTER C) |
1622 | 0 | }; |
1623 | |
|
1624 | 0 | for( size_t i = 0; i < ARRAY_SIZE(code2utf8) ; i++ ) |
1625 | 0 | { |
1626 | 0 | if( code2utf8[i].c == code ) |
1627 | 0 | { |
1628 | 0 | memcpy( out, code2utf8[i].utf8, 4 ); |
1629 | 0 | if(out[0] < 0xf0) |
1630 | 0 | { |
1631 | 0 | if(out[0] < 0x80) |
1632 | 0 | out[1] = 0; |
1633 | 0 | else if(out[0] < 0xe0) |
1634 | 0 | out[2] = 0; |
1635 | 0 | else |
1636 | 0 | out[3] = 0; |
1637 | 0 | } |
1638 | 0 | break; |
1639 | 0 | } |
1640 | 0 | } |
1641 | |
|
1642 | 0 | CEA708_Window_Write( out, p_cea708->p_cw ); |
1643 | |
|
1644 | 0 | p_cea708->b_text_waiting |= p_cea708->p_cw->b_visible; |
1645 | |
|
1646 | 0 | return CEA708_STATUS_OK; |
1647 | 0 | } |
1648 | | |
1649 | | static int CEA708_Decode_P16( uint16_t ucs2, cea708_t *p_cea708 ) |
1650 | 0 | { |
1651 | 0 | if( !p_cea708->p_cw->b_defined ) |
1652 | 0 | return CEA708_STATUS_OK; |
1653 | | |
1654 | 0 | uint8_t out[4] = { '?', 0, 0, 0 }; |
1655 | | |
1656 | | /* adapted from codepoint conversion from strings.h */ |
1657 | 0 | if( ucs2 <= 0x7F ) |
1658 | 0 | { |
1659 | 0 | out[0] = ucs2; |
1660 | 0 | } |
1661 | 0 | else if( ucs2 <= 0x7FF ) |
1662 | 0 | { |
1663 | 0 | out[0] = 0xC0 | (ucs2 >> 6); |
1664 | 0 | out[1] = 0x80 | (ucs2 & 0x3F); |
1665 | 0 | } |
1666 | 0 | else |
1667 | 0 | { |
1668 | 0 | out[0] = 0xE0 | (ucs2 >> 12); |
1669 | 0 | out[1] = 0x80 | ((ucs2 >> 6) & 0x3F); |
1670 | 0 | out[2] = 0x80 | (ucs2 & 0x3F); |
1671 | 0 | } |
1672 | |
|
1673 | 0 | CEA708_Window_Write( out, p_cea708->p_cw ); |
1674 | |
|
1675 | 0 | p_cea708->b_text_waiting |= p_cea708->p_cw->b_visible; |
1676 | |
|
1677 | 0 | return CEA708_STATUS_OK; |
1678 | 0 | } |
1679 | | |
1680 | | static void CEA708_Decode_ServiceBuffer( cea708_t *h ) |
1681 | 0 | { |
1682 | 0 | for( ;; ) |
1683 | 0 | { |
1684 | 0 | const uint8_t i_in = cea708_input_buffer_size( &h->input_buffer ); |
1685 | 0 | if( i_in == 0 ) |
1686 | 0 | break; |
1687 | | |
1688 | 0 | int i_ret; |
1689 | 0 | uint8_t c = cea708_input_buffer_peek( &h->input_buffer, 0 ); |
1690 | |
|
1691 | 0 | if( c < 0x20 ) |
1692 | 0 | i_ret = CEA708_Decode_C0( c, h ); |
1693 | 0 | else if( c <= 0x7F ) |
1694 | 0 | i_ret = CEA708_Decode_G0( c, h ); |
1695 | 0 | else if( c <= 0x9F ) |
1696 | 0 | i_ret = CEA708_Decode_C1( c, h ); |
1697 | 0 | else |
1698 | 0 | i_ret = CEA708_Decode_G1( c, h ); |
1699 | |
|
1700 | 0 | if( i_ret & CEA708_STATUS_OUTPUT ) |
1701 | 0 | CEA708_Output( h ); |
1702 | |
|
1703 | 0 | if( i_ret & CEA708_STATUS_STARVING ) |
1704 | 0 | break; |
1705 | | |
1706 | | /* Update internal clock */ |
1707 | 0 | const uint8_t i_consumed = i_in - cea708_input_buffer_size( &h->input_buffer ); |
1708 | 0 | if( i_consumed ) |
1709 | 0 | h->i_clock += vlc_tick_from_samples(1, 9600) * i_consumed; |
1710 | 0 | } |
1711 | 0 | } |
1712 | | |
1713 | | void CEA708_Decoder_Push( cea708_t *h, vlc_tick_t i_time, |
1714 | | const uint8_t *p_data, size_t i_data ) |
1715 | 0 | { |
1716 | | /* Set new buffer start time */ |
1717 | 0 | h->i_clock = i_time; |
1718 | |
|
1719 | 0 | for( size_t i=0; i<i_data; ) |
1720 | 0 | { |
1721 | | /* Never push more than buffer */ |
1722 | 0 | size_t i_push = cea708_input_buffer_remain(&h->input_buffer); |
1723 | 0 | if( (i_data - i) < i_push ) |
1724 | 0 | i_push = (i_data - i); |
1725 | 0 | else |
1726 | 0 | h->suspended_deadline = VLC_TICK_INVALID; /* Full buffer cancels pause */ |
1727 | |
|
1728 | 0 | for( size_t j=0; j<i_push; j++ ) |
1729 | 0 | { |
1730 | 0 | uint8_t byte = p_data[i+j]; |
1731 | 0 | cea708_input_buffer_add( &h->input_buffer, byte ); |
1732 | 0 | } |
1733 | |
|
1734 | 0 | if( h->suspended_deadline != VLC_TICK_INVALID ) |
1735 | 0 | { |
1736 | | /* Decoding is paused */ |
1737 | 0 | if ( h->suspended_deadline > h->i_clock ) |
1738 | 0 | { |
1739 | | /* Increase internal clock */ |
1740 | 0 | if( i_push ) |
1741 | 0 | h->i_clock += vlc_tick_from_samples(1, 1200) * i_push; |
1742 | 0 | continue; |
1743 | 0 | } |
1744 | 0 | h->suspended_deadline = VLC_TICK_INVALID; |
1745 | 0 | } |
1746 | | |
1747 | | /* Decode Buffer */ |
1748 | 0 | CEA708_Decode_ServiceBuffer( h ); |
1749 | |
|
1750 | 0 | i += i_push; |
1751 | 0 | } |
1752 | 0 | } |