/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 | 40 | { |
55 | 40 | h->i_pkt_sequence = -1; |
56 | 40 | h->i_total_data = h->i_data = 0; |
57 | 40 | } |
58 | | |
59 | | void CEA708_DTVCC_Demuxer_Release( cea708_demux_t *h ) |
60 | 40 | { |
61 | 40 | free( h ); |
62 | 40 | } |
63 | | |
64 | | cea708_demux_t * CEA708_DTVCC_Demuxer_New( void *priv, service_data_hdlr_t hdlr ) |
65 | 40 | { |
66 | 40 | cea708_demux_t *h = malloc( sizeof(cea708_demux_t) ); |
67 | 40 | if( h ) |
68 | 40 | { |
69 | 40 | h->priv = priv; |
70 | 40 | h->p_callback = hdlr; |
71 | 40 | CEA708_DTVCC_Demuxer_Flush( h ); |
72 | 40 | } |
73 | 40 | return h; |
74 | 40 | } |
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 | 16 | { |
79 | 18 | while( i_data >= 2 ) |
80 | 9 | { |
81 | 9 | uint8_t i_sid = p_data[0] >> 5; |
82 | 9 | const uint8_t i_block_size = p_data[0] & 0x1F; |
83 | | |
84 | 9 | if( i_block_size == 0 || i_block_size > i_data - 1 ) |
85 | 6 | { |
86 | 6 | return; |
87 | 6 | } |
88 | 3 | else if( i_sid == 0x07 ) |
89 | 1 | { |
90 | 1 | i_sid = p_data[1] & 0x3F; |
91 | 1 | if( i_sid < 0x07 ) |
92 | 1 | return; |
93 | 0 | p_data += 1; i_data -= 1; |
94 | 0 | } |
95 | 2 | p_data += 1; i_data -= 1; |
96 | | |
97 | 2 | h->p_callback( h->priv, i_sid, i_start, p_data, i_block_size ); |
98 | | |
99 | 2 | p_data += i_block_size; |
100 | 2 | i_data -= i_block_size; |
101 | 2 | } |
102 | 16 | } |
103 | | |
104 | | void CEA708_DTVCC_Demuxer_Push( cea708_demux_t *h, vlc_tick_t i_start, const uint8_t data[3] ) |
105 | 464 | { |
106 | 464 | if( (data[0] & 0x03) == 3 ) /* Header packet */ |
107 | 122 | { |
108 | 122 | const int8_t i_pkt_sequence = data[1] >> 6; |
109 | | |
110 | | /* pkt loss/discontinuity, trash buffer */ |
111 | 122 | if( i_pkt_sequence > 0 && ((h->i_pkt_sequence + 1) % 4) != i_pkt_sequence ) |
112 | 75 | { |
113 | 75 | h->i_data = h->i_total_data = 0; |
114 | 75 | h->i_pkt_sequence = i_pkt_sequence; |
115 | 75 | return; |
116 | 75 | } |
117 | | |
118 | 47 | uint8_t pktsize = data[1] & 63; |
119 | 47 | if( pktsize == 0 ) |
120 | 10 | pktsize = 127; |
121 | 37 | else |
122 | 37 | pktsize = pktsize * 2 - 1; |
123 | | |
124 | 47 | h->i_pkt_sequence = i_pkt_sequence; |
125 | 47 | h->i_total_data = pktsize; |
126 | 47 | h->i_data = 0; |
127 | 47 | h->i_time = i_start; |
128 | 47 | h->data[h->i_data++] = data[2]; |
129 | 47 | } |
130 | 342 | else if( h->i_total_data > 0 ) /* Not synced to pkt header yet */ |
131 | 128 | { |
132 | 128 | h->data[h->i_data++] = data[1]; |
133 | 128 | h->data[h->i_data++] = data[2]; |
134 | 128 | } |
135 | | |
136 | | /* pkts assembly finished, we have a service block */ |
137 | 389 | if( h->i_data > 0 && h->i_data >= h->i_total_data ) |
138 | 16 | { |
139 | 16 | if( h->i_data == h->i_total_data ) /* Only if correct */ |
140 | 16 | CEA708_DTVCC_Demux_ServiceBlocks( h, h->i_time, h->data, h->i_data ); |
141 | 16 | h->i_total_data = h->i_data = 0; |
142 | 16 | } |
143 | 389 | } |
144 | | |
145 | | /***************************************************************************** |
146 | | * Service Data Decoding |
147 | | *****************************************************************************/ |
148 | | |
149 | 0 | #define CEA708_SERVICE_INPUT_BUFFER 128 |
150 | | |
151 | 1.08k | #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 | 1.28k | #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 | 80 | { |
220 | 80 | ib->capacity = 0; |
221 | 80 | ib->start = 0; |
222 | 80 | } |
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 | 960 | { |
515 | 960 | memset( p_w, 0, sizeof(*p_w) ); |
516 | 960 | p_w->style = cea708_default_window_styles[0]; |
517 | 960 | p_w->pen = cea708_default_pen_styles[0]; |
518 | 960 | p_w->i_firstrow = CEA708_WINDOW_MAX_ROWS; |
519 | 960 | p_w->b_row_lock = true; |
520 | 960 | p_w->b_column_lock = true; |
521 | 960 | } |
522 | | |
523 | | static void CEA708_Window_ClearText( cea708_window_t *p_w ) |
524 | 320 | { |
525 | 320 | 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 | 320 | p_w->i_lastrow = 0; |
531 | 320 | p_w->i_firstrow = CEA708_WINDOW_MAX_ROWS; |
532 | 320 | } |
533 | | |
534 | | static void CEA708_Window_Reset( cea708_window_t *p_w ) |
535 | 320 | { |
536 | 320 | CEA708_Window_ClearText( p_w ); |
537 | 320 | CEA708_Window_Init( p_w ); |
538 | 320 | } |
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[i]; |
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[i]; |
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) |
609 | 0 | continue; |
610 | 0 | if( row->lastcol == i_max ) |
611 | 0 | { |
612 | 0 | if( row->firstcol >= row->lastcol ) |
613 | 0 | { |
614 | 0 | cea708_text_row_Delete( row ); |
615 | 0 | p_w->rows[i] = NULL; |
616 | 0 | if( i == p_w->i_firstrow ) |
617 | 0 | p_w->i_firstrow++; |
618 | 0 | else if( i == p_w->i_lastrow ) |
619 | 0 | p_w->i_lastrow--; |
620 | 0 | } |
621 | 0 | else |
622 | 0 | { |
623 | | /* Drop rightmost column */ |
624 | 0 | row->lastcol--; |
625 | 0 | } |
626 | | |
627 | 0 | } |
628 | 0 | } |
629 | 0 | } |
630 | 0 | break; |
631 | 0 | case CEA708_WA_DIRECTION_RTL: /* Deletes all most left col */ |
632 | 0 | { |
633 | 0 | uint8_t i_min = CEA708_Window_MinCol( p_w ); |
634 | 0 | for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ ) |
635 | 0 | { |
636 | 0 | cea708_text_row_t *row = p_w->rows[i]; |
637 | 0 | if (!row) |
638 | 0 | continue; |
639 | 0 | if( row->firstcol == i_min ) |
640 | 0 | { |
641 | 0 | if( row->firstcol >= row->lastcol ) |
642 | 0 | { |
643 | 0 | cea708_text_row_Delete( row ); |
644 | 0 | p_w->rows[i] = NULL; |
645 | 0 | if( i == p_w->i_firstrow ) |
646 | 0 | p_w->i_firstrow++; |
647 | 0 | else if( i == p_w->i_lastrow ) |
648 | 0 | p_w->i_lastrow--; |
649 | 0 | } |
650 | 0 | else |
651 | 0 | { |
652 | | /* Drop leftmost column */ |
653 | 0 | row->firstcol++; |
654 | 0 | } |
655 | | |
656 | 0 | } |
657 | 0 | } |
658 | 0 | } |
659 | 0 | break; |
660 | 0 | case CEA708_WA_DIRECTION_TB: /* Deletes LAST row */ |
661 | 0 | if( CEA708_Window_RowCount( p_w ) > 0 ) |
662 | 0 | { |
663 | 0 | cea708_text_row_Delete( p_w->rows[p_w->i_lastrow] ); |
664 | 0 | p_w->rows[p_w->i_lastrow--] = NULL; |
665 | 0 | } |
666 | 0 | break; |
667 | 0 | case CEA708_WA_DIRECTION_BT: /* Deletes First row */ |
668 | 0 | if( CEA708_Window_RowCount( p_w ) > 0 ) |
669 | 0 | { |
670 | 0 | cea708_text_row_Delete( p_w->rows[p_w->i_firstrow] ); |
671 | 0 | p_w->rows[p_w->i_firstrow++] = NULL; |
672 | 0 | } |
673 | 0 | break; |
674 | 0 | } |
675 | 0 | } |
676 | | |
677 | | static void CEA708_Window_Scroll( cea708_window_t *p_w ) |
678 | 0 | { |
679 | 0 | if( CEA708_Window_RowCount( p_w ) == 0 ) |
680 | 0 | return; |
681 | | |
682 | 0 | switch( p_w->style.scroll_direction ) |
683 | 0 | { |
684 | 0 | case CEA708_WA_DIRECTION_LTR: |
685 | | /* Move RIGHT */ |
686 | 0 | if( CEA708_Window_MaxCol( p_w ) == CEA708_WINDOW_MAX_COLS - 1 ) |
687 | 0 | CEA708_Window_Truncate( p_w, CEA708_WA_DIRECTION_LTR ); |
688 | 0 | for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ ) |
689 | 0 | { |
690 | 0 | cea708_text_row_t *row = p_w->rows[i]; |
691 | 0 | if( !row ) |
692 | 0 | continue; |
693 | 0 | if( row->lastcol < row->firstcol ) /* should not happen */ |
694 | 0 | continue; |
695 | | |
696 | 0 | size_t start = (size_t) row->firstcol * 4U; |
697 | 0 | size_t count = (size_t) (row->lastcol - row->firstcol + 1) * 4U; |
698 | 0 | memmove( &row->characters[start + 4U], &row->characters[start], |
699 | 0 | count ); |
700 | 0 | memmove( &row->styles[row->firstcol + 1], &row->styles[row->firstcol], |
701 | 0 | (row->lastcol - row->firstcol + 1) * sizeof(cea708_pen_style_t) ); |
702 | 0 | row->firstcol++; |
703 | 0 | row->lastcol++; |
704 | 0 | } |
705 | 0 | break; |
706 | 0 | case CEA708_WA_DIRECTION_RTL: |
707 | | /* Move LEFT */ |
708 | 0 | if( CEA708_Window_MinCol( p_w ) == 0 ) |
709 | 0 | CEA708_Window_Truncate( p_w, CEA708_WA_DIRECTION_RTL ); |
710 | 0 | for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ ) |
711 | 0 | { |
712 | 0 | cea708_text_row_t *row = p_w->rows[i]; |
713 | 0 | if( !row ) |
714 | 0 | continue; |
715 | 0 | if( row->lastcol < row->firstcol ) /* should not happen */ |
716 | 0 | continue; |
717 | 0 | if( row->firstcol > 0 ) |
718 | 0 | { |
719 | 0 | size_t start = (size_t) row->firstcol * 4U; |
720 | 0 | size_t count = (size_t) (row->lastcol - row->firstcol + 1) * 4U; |
721 | 0 | memmove( &row->characters[start -4U], &row->characters[start], |
722 | 0 | count ); |
723 | 0 | memmove( &row->styles[row->firstcol - 1], &row->styles[row->firstcol], |
724 | 0 | (row->lastcol - row->firstcol + 1) * sizeof(cea708_pen_style_t) ); |
725 | 0 | row->firstcol--; |
726 | 0 | row->lastcol--; |
727 | 0 | } |
728 | 0 | } |
729 | 0 | break; |
730 | 0 | case CEA708_WA_DIRECTION_TB: |
731 | | /* Move DOWN */ |
732 | 0 | if( p_w->i_lastrow == CEA708_WINDOW_MAX_ROWS - 1 ) |
733 | 0 | CEA708_Window_Truncate( p_w, CEA708_WA_DIRECTION_TB ); |
734 | 0 | for( int i=p_w->i_lastrow; i >= p_w->i_firstrow; i-- ) |
735 | 0 | p_w->rows[i+1] = p_w->rows[i]; |
736 | 0 | p_w->rows[p_w->i_firstrow] = NULL; |
737 | 0 | p_w->i_firstrow++; |
738 | 0 | p_w->i_lastrow++; |
739 | 0 | break; |
740 | 0 | case CEA708_WA_DIRECTION_BT: |
741 | | /* Move UP */ |
742 | 0 | if( p_w->i_firstrow == 0 ) |
743 | 0 | CEA708_Window_Truncate( p_w, CEA708_WA_DIRECTION_BT ); |
744 | 0 | for( int i=p_w->i_firstrow; i <= p_w->i_lastrow; i++ ) |
745 | 0 | p_w->rows[i-1] = p_w->rows[i]; |
746 | 0 | p_w->rows[p_w->i_lastrow] = NULL; |
747 | 0 | p_w->i_firstrow--; |
748 | 0 | p_w->i_lastrow--; |
749 | 0 | break; |
750 | 0 | } |
751 | 0 | } |
752 | | |
753 | | static void CEA708_Window_CarriageReturn( cea708_window_t *p_w ) |
754 | 0 | { |
755 | 0 | switch( p_w->style.scroll_direction ) |
756 | 0 | { |
757 | 0 | case CEA708_WA_DIRECTION_LTR: |
758 | 0 | if( p_w->col > 0 && |
759 | 0 | CEA708_Window_ColCount( p_w ) < p_w->i_col_count ) |
760 | 0 | p_w->col--; |
761 | 0 | else |
762 | 0 | CEA708_Window_Scroll( p_w ); |
763 | 0 | p_w->row = (p_w->style.print_direction == CEA708_WA_DIRECTION_TB) ? |
764 | 0 | 0 : CEA708_WINDOW_MAX_ROWS - 1; |
765 | 0 | break; |
766 | 0 | case CEA708_WA_DIRECTION_RTL: |
767 | 0 | if( p_w->col + 1 < CEA708_WINDOW_MAX_COLS && |
768 | 0 | CEA708_Window_ColCount( p_w ) < p_w->i_col_count ) |
769 | 0 | p_w->col++; |
770 | 0 | else |
771 | 0 | CEA708_Window_Scroll( p_w ); |
772 | 0 | p_w->row = (p_w->style.print_direction == CEA708_WA_DIRECTION_TB) ? |
773 | 0 | 0 : CEA708_WINDOW_MAX_ROWS - 1; |
774 | 0 | break; |
775 | 0 | case CEA708_WA_DIRECTION_TB: |
776 | 0 | if( p_w->row > 0 && |
777 | 0 | CEA708_Window_RowCount( p_w ) < p_w->i_row_count ) |
778 | 0 | p_w->row--; |
779 | 0 | else |
780 | 0 | CEA708_Window_Scroll( p_w ); |
781 | 0 | p_w->col = (p_w->style.print_direction == CEA708_WA_DIRECTION_LTR) ? |
782 | 0 | 0 : CEA708_WINDOW_MAX_COLS - 1; |
783 | 0 | break; |
784 | 0 | case CEA708_WA_DIRECTION_BT: |
785 | 0 | if( p_w->row + 1 < p_w->i_row_count ) |
786 | 0 | p_w->row++; |
787 | 0 | else |
788 | 0 | CEA708_Window_Scroll( p_w ); |
789 | 0 | p_w->col = (p_w->style.print_direction == CEA708_WA_DIRECTION_LTR) ? |
790 | 0 | 0 : CEA708_WINDOW_MAX_COLS - 1; |
791 | 0 | break; |
792 | 0 | } |
793 | 0 | } |
794 | | |
795 | | static void CEA708_Window_Forward( cea708_window_t *p_w ) |
796 | 0 | { |
797 | 0 | switch( p_w->style.print_direction ) |
798 | 0 | { |
799 | 0 | case CEA708_WA_DIRECTION_LTR: |
800 | 0 | if( p_w->col + 1 < CEA708_WINDOW_MAX_COLS ) |
801 | 0 | p_w->col++; |
802 | 0 | else |
803 | 0 | CEA708_Window_CarriageReturn( p_w ); |
804 | 0 | break; |
805 | 0 | case CEA708_WA_DIRECTION_RTL: |
806 | 0 | if( p_w->col > 0 ) |
807 | 0 | p_w->col--; |
808 | 0 | else |
809 | 0 | CEA708_Window_CarriageReturn( p_w ); |
810 | 0 | break; |
811 | 0 | case CEA708_WA_DIRECTION_TB: |
812 | 0 | if( p_w->row + 1 < CEA708_WINDOW_MAX_ROWS ) |
813 | 0 | p_w->row++; |
814 | 0 | else |
815 | 0 | CEA708_Window_CarriageReturn( p_w ); |
816 | 0 | break; |
817 | 0 | case CEA708_WA_DIRECTION_BT: |
818 | 0 | if( p_w->row > 0 ) |
819 | 0 | p_w->row--; |
820 | 0 | else |
821 | 0 | CEA708_Window_CarriageReturn( p_w ); |
822 | 0 | break; |
823 | 0 | } |
824 | 0 | } |
825 | | |
826 | | static void CEA708_Window_Backward( cea708_window_t *p_w ) |
827 | 0 | { |
828 | 0 | static const int reverse[] = |
829 | 0 | { |
830 | 0 | [CEA708_WA_DIRECTION_LTR] = CEA708_WA_DIRECTION_RTL, |
831 | 0 | [CEA708_WA_DIRECTION_RTL] = CEA708_WA_DIRECTION_LTR, |
832 | 0 | [CEA708_WA_DIRECTION_TB] = CEA708_WA_DIRECTION_BT, |
833 | 0 | [CEA708_WA_DIRECTION_BT] = CEA708_WA_DIRECTION_TB, |
834 | 0 | }; |
835 | 0 | int save = p_w->style.print_direction; |
836 | 0 | p_w->style.print_direction = reverse[p_w->style.print_direction]; |
837 | 0 | CEA708_Window_Forward( p_w ); |
838 | 0 | p_w->style.print_direction = save; |
839 | 0 | } |
840 | | |
841 | | static void CEA708_Window_Write( const uint8_t c[4], cea708_window_t *p_w ) |
842 | 0 | { |
843 | 0 | if( !p_w->b_defined ) |
844 | 0 | return; |
845 | | |
846 | | |
847 | 0 | if( unlikely( p_w->row >= CEA708_WINDOW_MAX_ROWS || |
848 | 0 | p_w->col >= CEA708_WINDOW_MAX_COLS ) ) |
849 | 0 | { |
850 | 0 | assert( p_w->row < CEA708_WINDOW_MAX_ROWS ); |
851 | 0 | assert( p_w->col < CEA708_WINDOW_MAX_COLS ); |
852 | 0 | return; |
853 | 0 | } |
854 | | |
855 | 0 | cea708_text_row_t *p_row = p_w->rows[p_w->row]; |
856 | 0 | if( !p_row ) |
857 | 0 | { |
858 | 0 | p_w->rows[p_w->row] = p_row = cea708_text_row_New(); |
859 | 0 | if( !p_row ) |
860 | 0 | return; |
861 | 0 | if( p_w->row < p_w->i_firstrow ) |
862 | 0 | p_w->i_firstrow = p_w->row; |
863 | 0 | if( p_w->row > p_w->i_lastrow ) |
864 | 0 | p_w->i_lastrow = p_w->row; |
865 | 0 | } |
866 | | |
867 | 0 | memcpy( &p_row->characters[p_w->col * 4U], c, 4 ); |
868 | 0 | p_row->styles[p_w->col] = p_w->pen; |
869 | 0 | if( p_w->col < p_row->firstcol ) |
870 | 0 | p_row->firstcol = p_w->col; |
871 | 0 | if( p_w->col > p_row->lastcol ) |
872 | 0 | p_row->lastcol = p_w->col; |
873 | |
|
874 | 0 | CEA708_Window_Forward( p_w ); |
875 | |
|
876 | 0 | Debug(printf("\033[0;33m%s\033[0m", c)); |
877 | 0 | } |
878 | | |
879 | | static uint32_t CEA708ColorConvert( uint8_t c ) |
880 | 0 | { |
881 | 0 | const uint32_t value[4] = {0x00,0x3F,0xF0,0xFF}; |
882 | 0 | c = c & 0x3F; |
883 | 0 | return (value[(c >> 4) & 0x03] << 16) | |
884 | 0 | (value[(c >> 2) & 0x03] << 8) | |
885 | 0 | value[c & 0x03]; |
886 | 0 | } |
887 | | |
888 | | static uint8_t CEA708AlphaConvert( uint8_t c ) |
889 | 0 | { |
890 | 0 | if( c == CEA708_OPACITY_TRANSLUCENT ) |
891 | 0 | return STYLE_ALPHA_OPAQUE / 2; |
892 | 0 | else if( c == CEA708_OPACITY_TRANSPARENT ) |
893 | 0 | return STYLE_ALPHA_TRANSPARENT; |
894 | 0 | else |
895 | 0 | return STYLE_ALPHA_OPAQUE; |
896 | 0 | } |
897 | | |
898 | | static void CEA708PenStyleToSegment( const cea708_pen_style_t *ps, text_style_t *s ) |
899 | 0 | { |
900 | 0 | if( ps->background.opacity != CEA708_OPACITY_TRANSPARENT ) |
901 | 0 | { |
902 | 0 | s->i_background_alpha = CEA708AlphaConvert( ps->background.opacity ); |
903 | 0 | s->i_style_flags |= STYLE_BACKGROUND; |
904 | 0 | s->i_background_color = CEA708ColorConvert( ps->background.color ); |
905 | 0 | s->i_features |= STYLE_HAS_BACKGROUND_COLOR|STYLE_HAS_BACKGROUND_ALPHA; |
906 | 0 | if( ps->background.opacity == CEA708_OPACITY_FLASH ) |
907 | 0 | s->i_style_flags |= STYLE_BLINK_BACKGROUND; |
908 | 0 | } |
909 | 0 | s->i_font_color = CEA708ColorConvert( ps->foreground.color ); |
910 | 0 | s->i_font_alpha = CEA708AlphaConvert( ps->foreground.opacity ); |
911 | 0 | s->i_features |= STYLE_HAS_FONT_ALPHA|STYLE_HAS_FONT_COLOR; |
912 | 0 | if( ps->foreground.opacity == CEA708_OPACITY_FLASH ) |
913 | 0 | s->i_style_flags |= STYLE_BLINK_FOREGROUND; |
914 | |
|
915 | 0 | if( ps->b_italics ) |
916 | 0 | s->i_style_flags |= STYLE_ITALIC; |
917 | 0 | if( ps->b_underline ) |
918 | 0 | s->i_style_flags |= STYLE_UNDERLINE; |
919 | |
|
920 | 0 | switch( ps->font ) |
921 | 0 | { |
922 | 0 | default: |
923 | 0 | case CEA708_FONT_UNDEFINED: |
924 | 0 | case CEA708_FONT_MONOSPACED: |
925 | 0 | case CEA708_FONT_MONO_SANS_SERIF: |
926 | 0 | s->i_style_flags |= STYLE_MONOSPACED; |
927 | 0 | break; |
928 | 0 | case CEA708_FONT_PROP: |
929 | 0 | case CEA708_FONT_PROP_SANS_SERIF: |
930 | 0 | case CEA708_FONT_CASUAL: |
931 | 0 | case CEA708_FONT_CURSIVE: |
932 | 0 | case CEA708_FONT_SMALL_CAPS: |
933 | 0 | break; |
934 | 0 | } |
935 | | |
936 | 0 | switch( ps->size ) |
937 | 0 | { |
938 | 0 | case CEA708_PEN_SIZE_SMALL: |
939 | 0 | s->f_font_relsize = CEA708_FONTRELSIZE_SMALL; |
940 | 0 | break; |
941 | 0 | case CEA708_PEN_SIZE_LARGE: |
942 | 0 | s->f_font_relsize = CEA708_FONTRELSIZE_LARGE; |
943 | 0 | break; |
944 | 0 | default: |
945 | 0 | s->f_font_relsize = CEA708_FONTRELSIZE_STANDARD; |
946 | 0 | break; |
947 | 0 | } |
948 | 0 | } |
949 | | |
950 | | static text_segment_t * CEA708CharsToSegment( const cea708_text_row_t *p_row, |
951 | | uint8_t i_start, uint8_t i_end, |
952 | | bool b_newline ) |
953 | 0 | { |
954 | 0 | text_segment_t *p_segment = text_segment_New( NULL ); |
955 | 0 | if( !p_segment ) |
956 | 0 | return NULL; |
957 | | |
958 | 0 | p_segment->style = text_style_Create( STYLE_NO_DEFAULTS ); |
959 | 0 | if( p_segment->style ) |
960 | 0 | CEA708PenStyleToSegment( &p_row->styles[i_start], p_segment->style ); |
961 | |
|
962 | 0 | p_segment->psz_text = malloc( 1U + !!b_newline + (i_end - i_start + 1) * 4U ); |
963 | 0 | if( !p_segment->psz_text ) |
964 | 0 | { |
965 | 0 | text_segment_Delete( p_segment ); |
966 | 0 | return NULL; |
967 | 0 | } |
968 | | |
969 | 0 | size_t offsetw = 0; |
970 | 0 | for( uint8_t i=i_start; i<=i_end; i++ ) |
971 | 0 | { |
972 | 0 | for( size_t j=0; j<4; j++ ) |
973 | 0 | { |
974 | 0 | if( p_row->characters[i * 4 + j] != 0 ) |
975 | 0 | p_segment->psz_text[offsetw++] = p_row->characters[i * 4 + j]; |
976 | 0 | else if( j == 0 ) |
977 | 0 | p_segment->psz_text[offsetw++] = ' '; |
978 | 0 | else |
979 | 0 | break; |
980 | 0 | } |
981 | 0 | } |
982 | |
|
983 | 0 | if( b_newline ) |
984 | 0 | p_segment->psz_text[offsetw++] = '\n'; |
985 | 0 | p_segment->psz_text[offsetw] = '\0'; |
986 | |
|
987 | 0 | return p_segment; |
988 | 0 | } |
989 | | |
990 | | static text_segment_t * CEA708RowToSegments( const cea708_text_row_t *p_row, |
991 | | bool b_addnewline ) |
992 | 0 | { |
993 | 0 | text_segment_t *p_segments = NULL; |
994 | 0 | text_segment_t **pp_last = &p_segments; |
995 | |
|
996 | 0 | uint8_t i_start = p_row->firstcol; |
997 | 0 | for( uint8_t i=i_start; i<=p_row->lastcol; i++ ) |
998 | 0 | { |
999 | 0 | if( i == p_row->lastcol || |
1000 | 0 | memcmp( &p_row->styles[i], &p_row->styles[i+1], sizeof(cea708_pen_style_t) ) ) |
1001 | 0 | { |
1002 | 0 | *pp_last = CEA708CharsToSegment( p_row, i_start, i, |
1003 | 0 | b_addnewline && (i == p_row->lastcol) ); |
1004 | 0 | if( *pp_last ) |
1005 | 0 | pp_last = &((*pp_last)->p_next); |
1006 | 0 | i_start = i+1; |
1007 | 0 | } |
1008 | 0 | } |
1009 | |
|
1010 | 0 | return p_segments; |
1011 | 0 | } |
1012 | | |
1013 | | static void CEA708SpuConvert( const cea708_window_t *p_w, |
1014 | | substext_updater_region_t *p_region ) |
1015 | 0 | { |
1016 | 0 | if( !p_w->b_visible || CEA708_Window_RowCount( p_w ) == 0 ) |
1017 | 0 | return; |
1018 | | |
1019 | 0 | if( p_region == NULL && !(p_region = SubpictureUpdaterSysRegionNew()) ) |
1020 | 0 | return; |
1021 | | |
1022 | 0 | int first, last; |
1023 | |
|
1024 | 0 | if (p_w->style.scroll_direction == CEA708_WA_DIRECTION_BT) { |
1025 | | /* BT is a bit of a special case since we need to grab the last N |
1026 | | rows between first and last, rather than the first... */ |
1027 | 0 | last = p_w->i_lastrow; |
1028 | 0 | if (p_w->i_lastrow - p_w->i_row_count < p_w->i_firstrow) |
1029 | 0 | first = p_w->i_firstrow; |
1030 | 0 | else |
1031 | 0 | first = p_w->i_lastrow - p_w->i_row_count + 1; |
1032 | |
|
1033 | 0 | } else { |
1034 | 0 | first = p_w->i_firstrow; |
1035 | 0 | if (p_w->i_firstrow + p_w->i_row_count > p_w->i_lastrow) |
1036 | 0 | last = p_w->i_lastrow; |
1037 | 0 | else |
1038 | 0 | last = p_w->i_firstrow + p_w->i_row_count - 1; |
1039 | 0 | } |
1040 | |
|
1041 | 0 | text_segment_t **pp_last = &p_region->p_segments; |
1042 | 0 | for( uint8_t i=first; i<=last; i++ ) |
1043 | 0 | { |
1044 | 0 | if( !p_w->rows[i] ) |
1045 | 0 | continue; |
1046 | | |
1047 | 0 | *pp_last = CEA708RowToSegments( p_w->rows[i], i < p_w->i_lastrow ); |
1048 | 0 | if( *pp_last ) |
1049 | 0 | pp_last = &((*pp_last)->p_next); |
1050 | 0 | } |
1051 | |
|
1052 | 0 | if( p_w->b_relative ) |
1053 | 0 | { |
1054 | | /* FIXME: take into account left/right anchors */ |
1055 | 0 | p_region->origin.x = p_w->i_anchor_offset_h / 100.0; |
1056 | |
|
1057 | 0 | switch (p_w->anchor_point) { |
1058 | 0 | case CEA708_ANCHOR_TOP_LEFT: |
1059 | 0 | case CEA708_ANCHOR_TOP_CENTER: |
1060 | 0 | case CEA708_ANCHOR_TOP_RIGHT: |
1061 | 0 | p_region->origin.y = p_w->i_anchor_offset_v / 100.0; |
1062 | 0 | break; |
1063 | 0 | case CEA708_ANCHOR_BOTTOM_LEFT: |
1064 | 0 | case CEA708_ANCHOR_BOTTOM_CENTER: |
1065 | 0 | case CEA708_ANCHOR_BOTTOM_RIGHT: |
1066 | 0 | p_region->origin.y = 1.0 - (p_w->i_anchor_offset_v / 100.0); |
1067 | 0 | break; |
1068 | 0 | default: |
1069 | | /* FIXME: for CENTER vertical justified, just position as top */ |
1070 | 0 | p_region->origin.y = p_w->i_anchor_offset_v / 100.0; |
1071 | 0 | break; |
1072 | 0 | } |
1073 | 0 | } |
1074 | 0 | else |
1075 | 0 | { |
1076 | 0 | p_region->origin.x = (float)p_w->i_anchor_offset_h / CEA708_SCREEN_COLS_169; |
1077 | 0 | p_region->origin.y = (float)p_w->i_anchor_offset_v / |
1078 | 0 | (CEA708_SCREEN_ROWS * CEA708_FONT_TO_LINE_HEIGHT_RATIO); |
1079 | 0 | } |
1080 | 0 | p_region->flags |= UPDT_REGION_ORIGIN_X_IS_RATIO|UPDT_REGION_ORIGIN_Y_IS_RATIO; |
1081 | 0 | p_region->b_absolute = false; p_region->b_in_window = false; |
1082 | |
|
1083 | 0 | if( p_w->i_firstrow <= p_w->i_lastrow ) |
1084 | 0 | { |
1085 | 0 | p_region->origin.y += p_w->i_firstrow * CEA708_ROW_HEIGHT_STANDARD; |
1086 | | /*const uint8_t i_min = CEA708_Window_MinCol( p_w ); |
1087 | | if( i_min < CEA708_WINDOW_MAX_COLS ) |
1088 | | p_region->origin.x += (float) i_min / CEA708_WINDOW_MAX_COLS;*/ |
1089 | 0 | } |
1090 | |
|
1091 | 0 | if( p_w->anchor_point <= CEA708_ANCHOR_BOTTOM_RIGHT ) |
1092 | 0 | { |
1093 | 0 | static const int vlc_subpicture_aligns[] = |
1094 | 0 | { |
1095 | 0 | [CEA708_ANCHOR_TOP_LEFT] = SUBPICTURE_ALIGN_TOP|SUBPICTURE_ALIGN_LEFT, |
1096 | 0 | [CEA708_ANCHOR_TOP_CENTER] = SUBPICTURE_ALIGN_TOP, |
1097 | 0 | [CEA708_ANCHOR_TOP_RIGHT] = SUBPICTURE_ALIGN_TOP|SUBPICTURE_ALIGN_RIGHT, |
1098 | 0 | [CEA708_ANCHOR_CENTER_LEFT] = SUBPICTURE_ALIGN_LEFT, |
1099 | 0 | [CEA708_ANCHOR_CENTER_CENTER] = 0, |
1100 | 0 | [CEA708_ANCHOR_CENTER_RIGHT] = SUBPICTURE_ALIGN_RIGHT, |
1101 | 0 | [CEA708_ANCHOR_BOTTOM_LEFT] = SUBPICTURE_ALIGN_BOTTOM|SUBPICTURE_ALIGN_LEFT, |
1102 | 0 | [CEA708_ANCHOR_BOTTOM_CENTER] = SUBPICTURE_ALIGN_BOTTOM, |
1103 | 0 | [CEA708_ANCHOR_BOTTOM_RIGHT] = SUBPICTURE_ALIGN_BOTTOM|SUBPICTURE_ALIGN_RIGHT, |
1104 | 0 | }; |
1105 | 0 | p_region->align = vlc_subpicture_aligns[p_w->anchor_point]; |
1106 | 0 | } |
1107 | 0 | p_region->inner_align = SUBPICTURE_ALIGN_BOTTOM|SUBPICTURE_ALIGN_LEFT; |
1108 | 0 | } |
1109 | | |
1110 | | static subpicture_t *CEA708_BuildSubtitle( cea708_t *p_cea708 ) |
1111 | 0 | { |
1112 | 0 | subpicture_t *p_spu = decoder_NewSubpictureText( p_cea708->p_dec ); |
1113 | 0 | if( !p_spu ) |
1114 | 0 | return NULL; |
1115 | | |
1116 | 0 | subtext_updater_sys_t *p_spu_sys = p_spu->updater.sys; |
1117 | 0 | substext_updater_region_t *p_region = &p_spu_sys->region; |
1118 | |
|
1119 | 0 | p_spu_sys->margin_ratio = CEA708_SCREEN_SAFE_MARGIN_RATIO; |
1120 | |
|
1121 | 0 | bool first = true; |
1122 | |
|
1123 | 0 | for(size_t i=0; i<CEA708_WINDOWS_COUNT; i++) |
1124 | 0 | { |
1125 | 0 | cea708_window_t *p_w = &p_cea708->window[i]; |
1126 | 0 | if( p_w->b_defined && p_w->b_visible && CEA708_Window_RowCount( p_w ) ) |
1127 | 0 | { |
1128 | 0 | if( !first ) |
1129 | 0 | { |
1130 | 0 | substext_updater_region_t *p_newregion = |
1131 | 0 | SubpictureUpdaterSysRegionNew(); |
1132 | 0 | if( p_newregion == NULL ) |
1133 | 0 | break; |
1134 | 0 | SubpictureUpdaterSysRegionAdd( p_region, p_newregion ); |
1135 | 0 | p_region = p_newregion; |
1136 | 0 | } |
1137 | 0 | first = false; |
1138 | | |
1139 | | /* Fill region */ |
1140 | 0 | CEA708SpuConvert( p_w, p_region ); |
1141 | 0 | } |
1142 | 0 | } |
1143 | |
|
1144 | 0 | p_spu->i_start = p_cea708->i_clock; |
1145 | 0 | p_spu->i_stop = p_cea708->i_clock + VLC_TICK_FROM_SEC(10); /* 10s max */ |
1146 | |
|
1147 | 0 | p_spu->b_ephemer = true; |
1148 | 0 | p_spu->b_subtitle = true; |
1149 | |
|
1150 | 0 | return p_spu; |
1151 | 0 | } |
1152 | | |
1153 | | static void CEA708_Decoder_Init( cea708_t *p_cea708 ) |
1154 | 80 | { |
1155 | 80 | cea708_input_buffer_init( &p_cea708->input_buffer ); |
1156 | 720 | for(size_t i=0; i<CEA708_WINDOWS_COUNT; i++) |
1157 | 640 | CEA708_Window_Init( &p_cea708->window[i] ); |
1158 | 80 | p_cea708->p_cw = &p_cea708->window[0]; |
1159 | 80 | p_cea708->suspended_deadline = VLC_TICK_INVALID; |
1160 | 80 | p_cea708->b_text_waiting = false; |
1161 | 80 | p_cea708->i_clock = 0; |
1162 | 80 | } |
1163 | | |
1164 | | static void CEA708_Decoder_Reset( cea708_t *p_cea708 ) |
1165 | 40 | { |
1166 | 360 | for(size_t i=0; i<CEA708_WINDOWS_COUNT; i++) |
1167 | 320 | CEA708_Window_Reset( &p_cea708->window[i] ); |
1168 | 40 | CEA708_Decoder_Init( p_cea708 ); |
1169 | 40 | } |
1170 | | |
1171 | | void CEA708_Decoder_Flush( cea708_t *p_cea708 ) |
1172 | 0 | { |
1173 | 0 | CEA708_Decoder_Reset( p_cea708 ); |
1174 | 0 | } |
1175 | | |
1176 | | void CEA708_Decoder_Release( cea708_t *p_cea708 ) |
1177 | 40 | { |
1178 | 40 | CEA708_Decoder_Reset( p_cea708 ); |
1179 | 40 | free( p_cea708 ); |
1180 | 40 | } |
1181 | | |
1182 | | cea708_t * CEA708_Decoder_New( decoder_t *p_dec ) |
1183 | 40 | { |
1184 | 40 | cea708_t *p_cea708 = malloc( sizeof(cea708_t) ); |
1185 | 40 | if( p_cea708 ) |
1186 | 40 | { |
1187 | 40 | CEA708_Decoder_Init( p_cea708 ); |
1188 | 40 | p_cea708->p_dec = p_dec; |
1189 | 40 | } |
1190 | 40 | return p_cea708; |
1191 | 40 | } |
1192 | | |
1193 | 0 | #define POP_COMMAND() (void) cea708_input_buffer_get( ib ) |
1194 | 0 | #define POP_ARGS(n) for(size_t pops=0; pops<(size_t)n;pops++) POP_COMMAND() |
1195 | 0 | #define REQUIRE_ARGS(n) if(cea708_input_buffer_size( ib ) < n + 1)\ |
1196 | 0 | return CEA708_STATUS_STARVING |
1197 | 0 | #define REQUIRE_ARGS_AND_POP_COMMAND(n) REQUIRE_ARGS(n); else POP_COMMAND() |
1198 | | |
1199 | | static void CEA708_Output( cea708_t *p_cea708 ) |
1200 | 0 | { |
1201 | 0 | Debug(printf("@%ld ms\n", MS_FROM_VLC_TICK(p_cea708->i_clock))); |
1202 | 0 | subpicture_t *p_spu = CEA708_BuildSubtitle( p_cea708 ); |
1203 | 0 | if( p_spu ) |
1204 | 0 | decoder_QueueSub( p_cea708->p_dec, p_spu ); |
1205 | 0 | } |
1206 | | |
1207 | | static int CEA708_Decode_C0( uint8_t code, cea708_t *p_cea708 ) |
1208 | 0 | { |
1209 | 0 | uint8_t v, i; |
1210 | 0 | uint16_t u16; |
1211 | 0 | cea708_input_buffer_t *ib = &p_cea708->input_buffer; |
1212 | 0 | int i_ret = CEA708_STATUS_OK; |
1213 | |
|
1214 | 0 | switch( code ) |
1215 | 0 | { |
1216 | 0 | case CEA708_C0_NUL: |
1217 | 0 | POP_COMMAND(); |
1218 | 0 | break; |
1219 | 0 | case CEA708_C0_ETX: |
1220 | 0 | POP_COMMAND(); |
1221 | 0 | if( p_cea708->b_text_waiting ) |
1222 | 0 | { |
1223 | 0 | i_ret |= CEA708_STATUS_OUTPUT; |
1224 | 0 | p_cea708->b_text_waiting = false; |
1225 | 0 | } |
1226 | 0 | break; |
1227 | 0 | case CEA708_C0_BS: |
1228 | 0 | POP_COMMAND(); |
1229 | 0 | if( !p_cea708->p_cw->b_defined ) |
1230 | 0 | break; |
1231 | 0 | CEA708_Window_Backward( p_cea708->p_cw ); |
1232 | 0 | p_cea708->b_text_waiting = true; |
1233 | 0 | break; |
1234 | 0 | case CEA708_C0_FF: |
1235 | 0 | POP_COMMAND(); |
1236 | 0 | if( !p_cea708->p_cw->b_defined ) |
1237 | 0 | break; |
1238 | 0 | CEA708_Window_ClearText( p_cea708->p_cw ); |
1239 | 0 | p_cea708->p_cw->col = 0; |
1240 | 0 | p_cea708->p_cw->row = 0; |
1241 | 0 | p_cea708->b_text_waiting = true; |
1242 | 0 | break; |
1243 | 0 | case CEA708_C0_CR: |
1244 | 0 | POP_COMMAND(); |
1245 | 0 | if( !p_cea708->p_cw->b_defined ) |
1246 | 0 | break; |
1247 | 0 | if( p_cea708->p_cw->style.print_direction <= CEA708_WA_DIRECTION_RTL ) |
1248 | 0 | { |
1249 | 0 | CEA708_Window_CarriageReturn( p_cea708->p_cw ); |
1250 | 0 | if( p_cea708->p_cw->b_visible ) |
1251 | 0 | i_ret |= CEA708_STATUS_OUTPUT; |
1252 | 0 | } |
1253 | 0 | break; |
1254 | 0 | case CEA708_C0_HCR: |
1255 | 0 | POP_COMMAND(); |
1256 | 0 | if( !p_cea708->p_cw->b_defined ) |
1257 | 0 | break; |
1258 | 0 | if( p_cea708->p_cw->style.print_direction > CEA708_WA_DIRECTION_RTL ) |
1259 | 0 | { |
1260 | 0 | CEA708_Window_CarriageReturn( p_cea708->p_cw ); |
1261 | 0 | if( p_cea708->p_cw->b_visible ) |
1262 | 0 | i_ret |= CEA708_STATUS_OUTPUT; |
1263 | 0 | } |
1264 | 0 | break; |
1265 | 0 | case CEA708_C0_EXT1: /* Special extended table case */ |
1266 | 0 | if( cea708_input_buffer_size( ib ) >= 2 ) |
1267 | 0 | { |
1268 | 0 | v = cea708_input_buffer_peek( ib, 1 ); |
1269 | | /* C2 extended code set */ |
1270 | 0 | if( v < 0x20 ) |
1271 | 0 | { |
1272 | 0 | if( v > 0x17 ) |
1273 | 0 | i = 3; |
1274 | 0 | else if( v > 0x0f ) |
1275 | 0 | i = 2; |
1276 | 0 | else if( v > 0x07 ) |
1277 | 0 | i = 1; |
1278 | 0 | else |
1279 | 0 | i = 0; |
1280 | 0 | if( cea708_input_buffer_size( ib ) < 2 + i ) |
1281 | 0 | return CEA708_STATUS_STARVING; |
1282 | 0 | POP_COMMAND(); |
1283 | 0 | POP_ARGS(1 + i); |
1284 | 0 | } |
1285 | | /* C3 extended code set */ |
1286 | 0 | else if( v > 0x7f && v < 0xa0 ) |
1287 | 0 | { |
1288 | 0 | if( v > 0x87 ) |
1289 | 0 | i = 5; |
1290 | 0 | else |
1291 | 0 | i = 4; |
1292 | 0 | if( cea708_input_buffer_size( ib ) < 2 + i ) |
1293 | 0 | return CEA708_STATUS_STARVING; |
1294 | 0 | POP_COMMAND(); |
1295 | 0 | POP_ARGS(1 + i); |
1296 | 0 | } |
1297 | 0 | else |
1298 | 0 | { |
1299 | 0 | POP_COMMAND(); |
1300 | 0 | v = cea708_input_buffer_get( ib ); |
1301 | 0 | if( p_cea708->p_cw->b_defined ) |
1302 | 0 | i_ret |= CEA708_Decode_G2G3( v, p_cea708 ); |
1303 | 0 | } |
1304 | 0 | } |
1305 | 0 | else return CEA708_STATUS_STARVING; |
1306 | 0 | break; |
1307 | 0 | case CEA708_C0_P16: |
1308 | 0 | REQUIRE_ARGS_AND_POP_COMMAND(2); |
1309 | 0 | u16 = cea708_input_buffer_get( ib ) << 8; |
1310 | 0 | u16 |= cea708_input_buffer_get( ib ); |
1311 | 0 | i_ret |= CEA708_Decode_P16( u16, p_cea708 ); |
1312 | 0 | Debug(printf("[P16 %x]", u16)); |
1313 | 0 | break; |
1314 | 0 | default: |
1315 | 0 | POP_COMMAND(); |
1316 | 0 | Debug(printf("[UNK %2.2x]", code)); |
1317 | 0 | break; |
1318 | 0 | } |
1319 | 0 | Debug(printf("[C0 %x]", code)); |
1320 | 0 | return i_ret; |
1321 | 0 | } |
1322 | | |
1323 | | static int CEA708_Decode_G0( uint8_t code, cea708_t *p_cea708 ) |
1324 | 0 | { |
1325 | 0 | cea708_input_buffer_t *ib = &p_cea708->input_buffer; |
1326 | 0 | POP_COMMAND(); |
1327 | 0 | int i_ret = CEA708_STATUS_OK; |
1328 | |
|
1329 | 0 | if( !p_cea708->p_cw->b_defined ) |
1330 | 0 | return i_ret; |
1331 | | |
1332 | 0 | uint8_t utf8[4] = {code,0x00,0x00,0x00}; |
1333 | |
|
1334 | 0 | if(code == 0x7F) // Music note |
1335 | 0 | { |
1336 | 0 | utf8[0] = 0xe2; |
1337 | 0 | utf8[1] = 0x99; |
1338 | 0 | utf8[2] = 0xaa; |
1339 | 0 | } |
1340 | |
|
1341 | 0 | CEA708_Window_Write( utf8, p_cea708->p_cw ); |
1342 | |
|
1343 | 0 | if( code == 0x20 && |
1344 | 0 | p_cea708->b_text_waiting && |
1345 | 0 | CEA708_Window_BreaksSpace( p_cea708->p_cw ) ) |
1346 | 0 | { |
1347 | 0 | i_ret |= CEA708_STATUS_OUTPUT; |
1348 | 0 | } |
1349 | | |
1350 | |
|
1351 | 0 | p_cea708->b_text_waiting |= p_cea708->p_cw->b_visible; |
1352 | |
|
1353 | 0 | return i_ret; |
1354 | 0 | } |
1355 | | |
1356 | | static int CEA708_Decode_C1( uint8_t code, cea708_t *p_cea708 ) |
1357 | 0 | { |
1358 | 0 | uint8_t v, i; |
1359 | 0 | cea708_input_buffer_t *ib = &p_cea708->input_buffer; |
1360 | 0 | int i_ret = CEA708_STATUS_OK; |
1361 | |
|
1362 | 0 | if( p_cea708->b_text_waiting ) |
1363 | 0 | { |
1364 | 0 | i_ret |= CEA708_STATUS_OUTPUT; |
1365 | 0 | p_cea708->b_text_waiting = false; |
1366 | 0 | } |
1367 | |
|
1368 | 0 | switch( code ) |
1369 | 0 | { |
1370 | 0 | case CEA708_C1_CLW: |
1371 | 0 | REQUIRE_ARGS_AND_POP_COMMAND(1); |
1372 | 0 | Debug(printf("[CLW")); |
1373 | 0 | for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ ) |
1374 | 0 | if( v & 1 ) |
1375 | 0 | { |
1376 | 0 | if( p_cea708->window[i].b_defined && |
1377 | 0 | p_cea708->window[i].b_visible ) |
1378 | 0 | i_ret |= CEA708_STATUS_OUTPUT; |
1379 | 0 | CEA708_Window_ClearText( &p_cea708->window[i] ); |
1380 | 0 | Debug(printf("%d", i)); |
1381 | 0 | } |
1382 | 0 | Debug(printf("]")); |
1383 | 0 | break; |
1384 | 0 | case CEA708_C1_DSW: |
1385 | 0 | REQUIRE_ARGS_AND_POP_COMMAND(1); |
1386 | 0 | Debug(printf("[DSW")); |
1387 | 0 | for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ ) |
1388 | 0 | if( v & 1 ) |
1389 | 0 | { |
1390 | 0 | if( p_cea708->window[i].b_defined ) |
1391 | 0 | { |
1392 | 0 | if( !p_cea708->window[i].b_visible ) |
1393 | 0 | i_ret |= CEA708_STATUS_OUTPUT; |
1394 | 0 | p_cea708->window[i].b_visible = true; |
1395 | 0 | } |
1396 | 0 | Debug(printf("%d", i)); |
1397 | 0 | } |
1398 | 0 | Debug(printf("]")); |
1399 | 0 | break; |
1400 | 0 | case CEA708_C1_HDW: |
1401 | 0 | REQUIRE_ARGS_AND_POP_COMMAND(1); |
1402 | 0 | Debug(printf("[HDW")); |
1403 | 0 | for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ ) |
1404 | 0 | if( v & 1 ) |
1405 | 0 | { |
1406 | 0 | if( p_cea708->window[i].b_defined ) |
1407 | 0 | { |
1408 | 0 | if( p_cea708->window[i].b_visible ) |
1409 | 0 | i_ret |= CEA708_STATUS_OUTPUT; |
1410 | 0 | p_cea708->window[i].b_visible = false; |
1411 | 0 | } |
1412 | 0 | Debug(printf("%d", i)); |
1413 | 0 | } |
1414 | 0 | Debug(printf("]")); |
1415 | 0 | break; |
1416 | 0 | case CEA708_C1_TGW: |
1417 | 0 | REQUIRE_ARGS_AND_POP_COMMAND(1); |
1418 | 0 | Debug(printf("[TGW")); |
1419 | 0 | for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ ) |
1420 | 0 | if( v & 1 ) |
1421 | 0 | { |
1422 | 0 | if( p_cea708->window[i].b_defined ) |
1423 | 0 | { |
1424 | 0 | i_ret |= CEA708_STATUS_OUTPUT; |
1425 | 0 | p_cea708->window[i].b_visible = !p_cea708->window[i].b_visible; |
1426 | 0 | } |
1427 | 0 | Debug(printf("%d", i)); |
1428 | 0 | } |
1429 | 0 | Debug(printf("]")); |
1430 | 0 | break; |
1431 | 0 | case CEA708_C1_DLW: |
1432 | 0 | REQUIRE_ARGS_AND_POP_COMMAND(1); |
1433 | 0 | Debug(printf("[DLW")); |
1434 | 0 | for( i = 0, v = cea708_input_buffer_get( ib ); v; v = v >> 1, i++ ) |
1435 | 0 | if( v & 1 ) |
1436 | 0 | { |
1437 | 0 | if( p_cea708->window[i].b_defined ) |
1438 | 0 | { |
1439 | 0 | if( p_cea708->window[i].b_visible ) |
1440 | 0 | i_ret |= CEA708_STATUS_OUTPUT; |
1441 | 0 | CEA708_Window_Reset( &p_cea708->window[i] ); |
1442 | 0 | } |
1443 | 0 | Debug(printf("%d", i)); |
1444 | 0 | } |
1445 | 0 | Debug(printf("]")); |
1446 | 0 | break; |
1447 | 0 | case CEA708_C1_DLY: |
1448 | 0 | REQUIRE_ARGS_AND_POP_COMMAND(1); |
1449 | 0 | p_cea708->suspended_deadline = p_cea708->i_clock + |
1450 | 0 | VLC_TICK_FROM_MS( cea708_input_buffer_get( ib ) * 100 ); |
1451 | 0 | Debug(printf("[DLY]")); |
1452 | 0 | break; |
1453 | 0 | case CEA708_C1_DLC: |
1454 | 0 | POP_COMMAND(); |
1455 | 0 | p_cea708->suspended_deadline = VLC_TICK_INVALID; |
1456 | 0 | Debug(printf("[DLC]")); |
1457 | 0 | break; |
1458 | 0 | case CEA708_C1_RST: |
1459 | 0 | POP_COMMAND(); |
1460 | 0 | i_ret |= CEA708_STATUS_OUTPUT; |
1461 | | /* FIXME */ |
1462 | 0 | break; |
1463 | 0 | case CEA708_C1_SPA: |
1464 | 0 | REQUIRE_ARGS_AND_POP_COMMAND(2); |
1465 | 0 | if( !p_cea708->p_cw->b_defined ) |
1466 | 0 | { |
1467 | 0 | POP_ARGS(2); |
1468 | 0 | break; |
1469 | 0 | } |
1470 | 0 | v = cea708_input_buffer_get( ib ); |
1471 | 0 | p_cea708->p_cw->pen.text_tag = v >> 4; |
1472 | 0 | p_cea708->p_cw->pen.offset = (v >> 2) & 0x03; |
1473 | 0 | p_cea708->p_cw->pen.size = v & 0x03; |
1474 | 0 | v = cea708_input_buffer_get( ib ); |
1475 | 0 | p_cea708->p_cw->pen.b_italics = v & 0x80; |
1476 | 0 | p_cea708->p_cw->pen.b_underline = v & 0x40; |
1477 | 0 | p_cea708->p_cw->pen.edge_type = (v >> 3) & 0x07; |
1478 | 0 | p_cea708->p_cw->pen.font = v & 0x07; |
1479 | 0 | Debug(printf("[SPA]")); |
1480 | 0 | break; |
1481 | 0 | case CEA708_C1_SPC: |
1482 | 0 | REQUIRE_ARGS_AND_POP_COMMAND(3); |
1483 | 0 | if( !p_cea708->p_cw->b_defined ) |
1484 | 0 | { |
1485 | 0 | POP_ARGS(3); |
1486 | 0 | break; |
1487 | 0 | } |
1488 | 0 | v = cea708_input_buffer_get( ib ); |
1489 | 0 | p_cea708->p_cw->pen.foreground.opacity = v >> 6; |
1490 | 0 | p_cea708->p_cw->pen.foreground.color = v & 0x3F; |
1491 | 0 | v = cea708_input_buffer_get( ib ); |
1492 | 0 | p_cea708->p_cw->pen.background.opacity = v >> 6; |
1493 | 0 | p_cea708->p_cw->pen.background.color = v & 0x3F; |
1494 | 0 | v = cea708_input_buffer_get( ib ); |
1495 | 0 | p_cea708->p_cw->pen.edge_color = v & 0x3F; |
1496 | 0 | Debug(printf("[SPC]")); |
1497 | 0 | break; |
1498 | 0 | case CEA708_C1_SPL: |
1499 | 0 | REQUIRE_ARGS_AND_POP_COMMAND(2); |
1500 | 0 | if( !p_cea708->p_cw->b_defined ) |
1501 | 0 | { |
1502 | 0 | POP_ARGS(2); |
1503 | 0 | break; |
1504 | 0 | } |
1505 | 0 | v = cea708_input_buffer_get( ib ); |
1506 | 0 | p_cea708->p_cw->row = (v & 0x0F) % CEA708_WINDOW_MAX_ROWS; |
1507 | 0 | v = cea708_input_buffer_get( ib ); |
1508 | 0 | p_cea708->p_cw->col = (v & 0x3F) % CEA708_WINDOW_MAX_COLS; |
1509 | 0 | Debug(printf("[SPL r%d c%d]", p_cea708->p_cw->row, p_cea708->p_cw->col)); |
1510 | 0 | break; |
1511 | 0 | case CEA708_C1_SWA: |
1512 | 0 | REQUIRE_ARGS_AND_POP_COMMAND(4); |
1513 | 0 | if( !p_cea708->p_cw->b_defined ) |
1514 | 0 | { |
1515 | 0 | POP_ARGS(4); |
1516 | 0 | break; |
1517 | 0 | } |
1518 | 0 | v = cea708_input_buffer_get( ib ); |
1519 | 0 | p_cea708->p_cw->style.fill_opacity = v >> 6; |
1520 | 0 | p_cea708->p_cw->style.fill_color_color = v & 0x3F; |
1521 | 0 | v = cea708_input_buffer_get( ib ); |
1522 | 0 | p_cea708->p_cw->style.border_color_color = v & 0x3F; |
1523 | 0 | p_cea708->p_cw->style.border_type = v >> 6; |
1524 | 0 | v = cea708_input_buffer_get( ib ); |
1525 | 0 | p_cea708->p_cw->style.border_type |= ((v & 0x80) >> 5); |
1526 | 0 | p_cea708->p_cw->style.b_word_wrap = v & 0x40; |
1527 | 0 | p_cea708->p_cw->style.print_direction = (v >> 4) & 0x03; |
1528 | 0 | p_cea708->p_cw->style.scroll_direction = (v >> 2) & 0x03; |
1529 | 0 | p_cea708->p_cw->style.justify = v & 0x03; |
1530 | 0 | v = cea708_input_buffer_get( ib ); |
1531 | 0 | p_cea708->p_cw->style.effect_speed = v >> 4; |
1532 | 0 | p_cea708->p_cw->style.effect_direction = (v >> 2) & 0x03; |
1533 | 0 | p_cea708->p_cw->style.display_effect = v & 0x03; |
1534 | 0 | Debug(printf("[SWA]")); |
1535 | 0 | break; |
1536 | | |
1537 | 0 | default: |
1538 | 0 | if( code >= CEA708_C1_CW0 && code <= CEA708_C1_CW7 ) |
1539 | 0 | { |
1540 | 0 | POP_COMMAND(); |
1541 | 0 | Debug(printf("[CW%d]", code - CEA708_C1_CW0)); |
1542 | 0 | if( p_cea708->window[code - CEA708_C1_CW0].b_defined ) |
1543 | 0 | p_cea708->p_cw = &p_cea708->window[code - CEA708_C1_CW0]; |
1544 | 0 | } |
1545 | 0 | else if( code >= CEA708_C1_DF0 && code <= CEA708_C1_DF7 ) |
1546 | 0 | { |
1547 | 0 | REQUIRE_ARGS_AND_POP_COMMAND(6); |
1548 | 0 | Debug(printf("[DF%d]", code - CEA708_C1_DF0)); |
1549 | | /* also sets current window */ |
1550 | 0 | p_cea708->p_cw = &p_cea708->window[code - CEA708_C1_DF0]; |
1551 | 0 | v = cea708_input_buffer_get( ib ); |
1552 | 0 | if( p_cea708->p_cw->b_defined && |
1553 | 0 | !p_cea708->p_cw->b_visible != !(v & 0x20) ) |
1554 | 0 | i_ret |= CEA708_STATUS_OUTPUT; |
1555 | 0 | p_cea708->p_cw->b_visible = v & 0x20; |
1556 | 0 | p_cea708->p_cw->b_row_lock = v & 0x10; |
1557 | 0 | p_cea708->p_cw->b_column_lock = v & 0x08; |
1558 | 0 | p_cea708->p_cw->i_priority = v & 0x07; |
1559 | 0 | v = cea708_input_buffer_get( ib ); |
1560 | 0 | p_cea708->p_cw->b_relative = v & 0x80; |
1561 | 0 | p_cea708->p_cw->i_anchor_offset_v = v & 0x7F; |
1562 | 0 | v = cea708_input_buffer_get( ib ); |
1563 | 0 | p_cea708->p_cw->i_anchor_offset_h = v; |
1564 | 0 | v = cea708_input_buffer_get( ib ); |
1565 | 0 | p_cea708->p_cw->anchor_point = v >> 4; |
1566 | 0 | p_cea708->p_cw->i_row_count = (v & 0x0F) + 1; |
1567 | 0 | v = cea708_input_buffer_get( ib ); |
1568 | 0 | p_cea708->p_cw->i_col_count = v & 0x3F; |
1569 | 0 | v = cea708_input_buffer_get( ib ); |
1570 | | /* zero values style set on init, avoid dealing with updt case */ |
1571 | 0 | i = (v >> 3) & 0x07; /* Window style id */ |
1572 | 0 | if( i > 0 ) |
1573 | 0 | p_cea708->p_cw->style = cea708_default_window_styles[i-1]; |
1574 | 0 | else if( !p_cea708->p_cw->b_defined ) /* Set to style #1 or ignore */ |
1575 | 0 | p_cea708->p_cw->style = cea708_default_window_styles[0]; |
1576 | 0 | i = v & 0x07; /* Pen style id */ |
1577 | 0 | if( i > 0 ) |
1578 | 0 | p_cea708->p_cw->pen = cea708_default_pen_styles[i-1]; |
1579 | 0 | else if( !p_cea708->p_cw->b_defined ) /* Set to style #1 or ignore */ |
1580 | 0 | p_cea708->p_cw->pen = cea708_default_pen_styles[0]; |
1581 | 0 | p_cea708->p_cw->b_defined = true; |
1582 | 0 | } |
1583 | 0 | else |
1584 | 0 | { |
1585 | 0 | Debug(printf("{%2.2x}", code)); |
1586 | 0 | POP_COMMAND(); |
1587 | 0 | } |
1588 | 0 | } |
1589 | | |
1590 | 0 | return i_ret; |
1591 | 0 | } |
1592 | | |
1593 | | static int CEA708_Decode_G1( uint8_t code, cea708_t *p_cea708 ) |
1594 | 0 | { |
1595 | 0 | cea708_input_buffer_t *ib = &p_cea708->input_buffer; |
1596 | 0 | POP_COMMAND(); |
1597 | |
|
1598 | 0 | if( !p_cea708->p_cw->b_defined ) |
1599 | 0 | return CEA708_STATUS_OK; |
1600 | | |
1601 | 0 | uint8_t utf8[4] = {0xc0 | (code & 0xc0) >> 6, |
1602 | 0 | 0x80 | (code & 0x3f), |
1603 | 0 | 0, 0}; |
1604 | |
|
1605 | 0 | CEA708_Window_Write( utf8, p_cea708->p_cw ); |
1606 | 0 | p_cea708->b_text_waiting |= p_cea708->p_cw->b_visible; |
1607 | |
|
1608 | 0 | return CEA708_STATUS_OK; |
1609 | 0 | } |
1610 | | |
1611 | | static int CEA708_Decode_G2G3( uint8_t code, cea708_t *p_cea708 ) |
1612 | 0 | { |
1613 | 0 | if( !p_cea708->p_cw->b_defined ) |
1614 | 0 | return CEA708_STATUS_OK; |
1615 | | |
1616 | 0 | uint8_t out[4] = { '?', 0, 0, 0 }; |
1617 | 0 | static const struct { |
1618 | 0 | uint8_t c; |
1619 | 0 | uint8_t utf8[4]; |
1620 | 0 | } code2utf8[] = { |
1621 | | /* G2 */ |
1622 | 0 | { 0x20, { 0x20 } },// transparent space [*** will need special handling] |
1623 | 0 | { 0x21, { 0x20 } },// non breaking transparent space [*** will need special handling] |
1624 | 0 | { 0x25, { 0xe2,0x80,0xa6 } },// HORIZONTAL ELLIPSIS |
1625 | 0 | { 0x2a, { 0xc5,0xa0 } },// LATIN CAPITAL LETTER S WITH CARON |
1626 | 0 | { 0x2c, { 0xc5,0x92 } },// LATIN CAPITAL LIGATURE OE |
1627 | 0 | { 0x30, { 0xe2,0x96,0x88 } },// FULL BLOCK |
1628 | 0 | { 0x31, { 0xe2,0x80,0x98 } },// LEFT SINGLE QUOTATION MARK |
1629 | 0 | { 0x32, { 0xe2,0x80,0x99 } },// RIGHT SINGLE QUOTATION MARK |
1630 | 0 | { 0x33, { 0xe2,0x80,0x9c } },// LEFT DOUBLE QUOTATION MARK |
1631 | 0 | { 0x34, { 0xe2,0x80,0x9d } },// RIGHT DOUBLE QUOTATION MARK |
1632 | 0 | { 0x35, { 0xe2,0x80,0xa2 } },// BULLET |
1633 | 0 | { 0x39, { 0xe2,0x84,0xa2 } },// Trademark symbol (TM) |
1634 | 0 | { 0x3a, { 0xc5,0xa1 } },// LATIN SMALL LETTER S WITH CARON |
1635 | 0 | { 0x3c, { 0xc5,0x93 } },// LATIN SMALL LIGATURE OE |
1636 | 0 | { 0x3d, { 0xe2,0x84,0xa0 } },// SERVICE MARK |
1637 | 0 | { 0x3f, { 0xc5,0xb8 } },// LATIN CAPITAL LETTER Y WITH DIAERESIS |
1638 | 0 | { 0x76, { 0xe2,0x85,0x9b } },// VULGAR FRACTION ONE EIGHTH |
1639 | 0 | { 0x77, { 0xe2,0x85,0x9c } },// VULGAR FRACTION THREE EIGHTHS |
1640 | 0 | { 0x78, { 0xe2,0x85,0x9d } },// VULGAR FRACTION FIVE EIGHTHS |
1641 | 0 | { 0x79, { 0xe2,0x85,0x9e } },// VULGAR FRACTION SEVEN EIGHTHS |
1642 | 0 | { 0x7a, { 0xe2,0x94,0x82 } },// BOX DRAWINGS LIGHT VERTICAL |
1643 | 0 | { 0x7b, { 0xe2,0x94,0x90 } },// BOX DRAWINGS LIGHT DOWN AND LEFT |
1644 | 0 | { 0x7c, { 0xe2,0x94,0x94 } },// BOX DRAWINGS LIGHT UP AND RIGHT |
1645 | 0 | { 0x7d, { 0xe2,0x94,0x80 } },// BOX DRAWINGS LIGHT HORIZONTAL |
1646 | 0 | { 0x7e, { 0xe2,0x94,0x98 } },// BOX DRAWINGS LIGHT UP AND LEFT |
1647 | 0 | { 0x7f, { 0xe2,0x94,0x8c } },// BOX DRAWINGS LIGHT DOWN AND RIGHT |
1648 | | /* G3 */ |
1649 | 0 | { 0xa0, { 0xf0,0x9f,0x85,0xb2 } },// CC (replaced with NEGATIVE SQUARED LATIN CAPITAL LETTER C) |
1650 | 0 | }; |
1651 | |
|
1652 | 0 | for( size_t i = 0; i < ARRAY_SIZE(code2utf8) ; i++ ) |
1653 | 0 | { |
1654 | 0 | if( code2utf8[i].c == code ) |
1655 | 0 | { |
1656 | 0 | memcpy( out, code2utf8[i].utf8, 4 ); |
1657 | 0 | if(out[0] < 0xf0) |
1658 | 0 | { |
1659 | 0 | if(out[0] < 0x80) |
1660 | 0 | out[1] = 0; |
1661 | 0 | else if(out[0] < 0xe0) |
1662 | 0 | out[2] = 0; |
1663 | 0 | else |
1664 | 0 | out[3] = 0; |
1665 | 0 | } |
1666 | 0 | break; |
1667 | 0 | } |
1668 | 0 | } |
1669 | |
|
1670 | 0 | CEA708_Window_Write( out, p_cea708->p_cw ); |
1671 | |
|
1672 | 0 | p_cea708->b_text_waiting |= p_cea708->p_cw->b_visible; |
1673 | |
|
1674 | 0 | return CEA708_STATUS_OK; |
1675 | 0 | } |
1676 | | |
1677 | | static int CEA708_Decode_P16( uint16_t ucs2, cea708_t *p_cea708 ) |
1678 | 0 | { |
1679 | 0 | if( !p_cea708->p_cw->b_defined ) |
1680 | 0 | return CEA708_STATUS_OK; |
1681 | | |
1682 | 0 | uint8_t out[4] = { '?', 0, 0, 0 }; |
1683 | | |
1684 | | /* adapted from codepoint conversion from strings.h */ |
1685 | 0 | if( ucs2 <= 0x7F ) |
1686 | 0 | { |
1687 | 0 | out[0] = ucs2; |
1688 | 0 | } |
1689 | 0 | else if( ucs2 <= 0x7FF ) |
1690 | 0 | { |
1691 | 0 | out[0] = 0xC0 | (ucs2 >> 6); |
1692 | 0 | out[1] = 0x80 | (ucs2 & 0x3F); |
1693 | 0 | } |
1694 | 0 | else |
1695 | 0 | { |
1696 | 0 | out[0] = 0xE0 | (ucs2 >> 12); |
1697 | 0 | out[1] = 0x80 | ((ucs2 >> 6) & 0x3F); |
1698 | 0 | out[2] = 0x80 | (ucs2 & 0x3F); |
1699 | 0 | } |
1700 | |
|
1701 | 0 | CEA708_Window_Write( out, p_cea708->p_cw ); |
1702 | |
|
1703 | 0 | p_cea708->b_text_waiting |= p_cea708->p_cw->b_visible; |
1704 | |
|
1705 | 0 | return CEA708_STATUS_OK; |
1706 | 0 | } |
1707 | | |
1708 | | static void CEA708_Decode_ServiceBuffer( cea708_t *h ) |
1709 | 0 | { |
1710 | 0 | for( ;; ) |
1711 | 0 | { |
1712 | 0 | const uint8_t i_in = cea708_input_buffer_size( &h->input_buffer ); |
1713 | 0 | if( i_in == 0 ) |
1714 | 0 | break; |
1715 | | |
1716 | 0 | int i_ret; |
1717 | 0 | uint8_t c = cea708_input_buffer_peek( &h->input_buffer, 0 ); |
1718 | |
|
1719 | 0 | if( c < 0x20 ) |
1720 | 0 | i_ret = CEA708_Decode_C0( c, h ); |
1721 | 0 | else if( c <= 0x7F ) |
1722 | 0 | i_ret = CEA708_Decode_G0( c, h ); |
1723 | 0 | else if( c <= 0x9F ) |
1724 | 0 | i_ret = CEA708_Decode_C1( c, h ); |
1725 | 0 | else |
1726 | 0 | i_ret = CEA708_Decode_G1( c, h ); |
1727 | |
|
1728 | 0 | if( i_ret & CEA708_STATUS_OUTPUT ) |
1729 | 0 | CEA708_Output( h ); |
1730 | |
|
1731 | 0 | if( i_ret & CEA708_STATUS_STARVING ) |
1732 | 0 | break; |
1733 | | |
1734 | | /* Update internal clock */ |
1735 | 0 | const uint8_t i_consumed = i_in - cea708_input_buffer_size( &h->input_buffer ); |
1736 | 0 | if( i_consumed ) |
1737 | 0 | h->i_clock += vlc_tick_from_samples(1, 9600) * i_consumed; |
1738 | 0 | } |
1739 | 0 | } |
1740 | | |
1741 | | void CEA708_Decoder_Push( cea708_t *h, vlc_tick_t i_time, |
1742 | | const uint8_t *p_data, size_t i_data ) |
1743 | 0 | { |
1744 | | /* Set new buffer start time */ |
1745 | 0 | h->i_clock = i_time; |
1746 | |
|
1747 | 0 | for( size_t i=0; i<i_data; ) |
1748 | 0 | { |
1749 | | /* Never push more than buffer */ |
1750 | 0 | size_t i_push = cea708_input_buffer_remain(&h->input_buffer); |
1751 | 0 | if( (i_data - i) < i_push ) |
1752 | 0 | i_push = (i_data - i); |
1753 | 0 | else |
1754 | 0 | h->suspended_deadline = VLC_TICK_INVALID; /* Full buffer cancels pause */ |
1755 | |
|
1756 | 0 | for( size_t j=0; j<i_push; j++ ) |
1757 | 0 | { |
1758 | 0 | uint8_t byte = p_data[i+j]; |
1759 | 0 | cea708_input_buffer_add( &h->input_buffer, byte ); |
1760 | 0 | } |
1761 | |
|
1762 | 0 | if( h->suspended_deadline != VLC_TICK_INVALID ) |
1763 | 0 | { |
1764 | | /* Decoding is paused */ |
1765 | 0 | if ( h->suspended_deadline > h->i_clock ) |
1766 | 0 | { |
1767 | | /* Increase internal clock */ |
1768 | 0 | if( i_push ) |
1769 | 0 | h->i_clock += vlc_tick_from_samples(1, 1200) * i_push; |
1770 | 0 | continue; |
1771 | 0 | } |
1772 | 0 | h->suspended_deadline = VLC_TICK_INVALID; |
1773 | 0 | } |
1774 | | |
1775 | | /* Decode Buffer */ |
1776 | 0 | CEA708_Decode_ServiceBuffer( h ); |
1777 | |
|
1778 | 0 | i += i_push; |
1779 | 0 | } |
1780 | 0 | } |