2015-03-11 10:00:08 +00:00
|
|
|
/* GStreamer
|
|
|
|
* Copyright (C) 2013 CableLabs, Louisville, CO 80027
|
|
|
|
* Copyright (C) 2015 Samsung Electronics Co., Ltd.
|
|
|
|
* @Author: Chengjun Wang <cjun.wang@samsung.com>
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Library General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Library General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Library General Public
|
|
|
|
* License along with this library; if not, write to the
|
|
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
|
|
* Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
#include <gst/gst.h>
|
|
|
|
#include <pango/pangocairo.h>
|
|
|
|
#include <gstcea708decoder.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#define GST_CAT_DEFAULT gst_cea708_decoder_debug
|
|
|
|
GST_DEBUG_CATEGORY (gst_cea708_decoder_debug);
|
|
|
|
|
|
|
|
void
|
|
|
|
gst_cea708_decoder_init_debug (void)
|
|
|
|
{
|
|
|
|
GST_DEBUG_CATEGORY_INIT (gst_cea708_decoder_debug, "cc708decoder", 0,
|
|
|
|
"CEA708 Closed Caption Decoder");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 708 colors are defined by 2 bits each for R,G,&B for a total of 64 color combinations */
|
|
|
|
static const gchar *color_names[] = {
|
|
|
|
"black",
|
|
|
|
"white",
|
|
|
|
"red",
|
|
|
|
"green",
|
|
|
|
"blue",
|
|
|
|
"yellow",
|
|
|
|
"magenta",
|
|
|
|
"cyan",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
static const gchar *font_names[] = {
|
|
|
|
"serif",
|
|
|
|
"courier",
|
|
|
|
"times new roman",
|
|
|
|
"helvetica",
|
|
|
|
"Arial",
|
|
|
|
"Dom Casual",
|
|
|
|
"Coronet",
|
|
|
|
"Gothic",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
static const gchar *pen_size_names[] = {
|
|
|
|
"30", /*"small" */
|
|
|
|
"36", /*"medium" */
|
|
|
|
"42", /*"large" */
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
/* G2 table defined in EIA/CEA-708 Spec */
|
|
|
|
static const gunichar g2_table[CC_MAX_CODE_SET_SIZE] = {
|
|
|
|
' ', 0xA0, 0, 0, 0, 0x2026, 0, 0,
|
|
|
|
0, 0, 0x160, 0, 0x152, 0, 0, 0,
|
|
|
|
0x2588, 0x2018, 0x2019, 0x201c, 0x201d, 0xB7, 0, 0,
|
|
|
|
0, 0x2122, 0x161, 0, 0x153, 0x2120, 0, 0x178,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0x215b, 0x215c,
|
|
|
|
0x215d, 0x215e, 0x2502, 0x2510, 0x2514, 0x2500, 0x2518, 0x250c,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void gst_cea708dec_print_command_name (Cea708Dec * decoder, guint8 c);
|
|
|
|
static void gst_cea708dec_render_pangocairo (cea708Window * window);
|
|
|
|
static void
|
|
|
|
gst_cea708dec_adjust_values_with_fontdesc (cea708Window * window,
|
|
|
|
PangoFontDescription * desc);
|
|
|
|
static gint
|
|
|
|
gst_cea708dec_text_list_add (GSList ** text_list,
|
|
|
|
gint len, const gchar * format, ...);
|
|
|
|
static const PangoAlignment gst_cea708dec_get_align_mode (guint8 justify_mode);
|
|
|
|
static const gchar *gst_cea708dec_get_color_name (guint8 color);
|
|
|
|
static guint8 gst_cea708dec_map_minimum_color (guint8 color);
|
|
|
|
static void
|
|
|
|
gst_cea708dec_set_pen_color (Cea708Dec * decoder,
|
|
|
|
guint8 * dtvcc_buffer, int index);
|
|
|
|
static void
|
|
|
|
gst_cea708dec_set_window_attributes (Cea708Dec * decoder,
|
|
|
|
guint8 * dtvcc_buffer, int index);
|
|
|
|
static void
|
|
|
|
gst_cea708dec_set_pen_style (Cea708Dec * decoder, guint8 pen_style_id);
|
|
|
|
static void
|
|
|
|
gst_cea708dec_set_window_style (Cea708Dec * decoder, guint8 style_id);
|
|
|
|
static void
|
|
|
|
gst_cea708dec_define_window (Cea708Dec * decoder,
|
|
|
|
guint8 * dtvcc_buffer, int index);
|
|
|
|
static inline void
|
|
|
|
pango_span_markup_init (cea708PangoSpanControl * span_control);
|
|
|
|
static inline void
|
|
|
|
pango_span_markup_start (cea708PangoSpanControl * span_control,
|
|
|
|
gchar * line_buffer, guint16 * index);
|
|
|
|
static inline void
|
|
|
|
pango_span_markup_txt (cea708PangoSpanControl * span_control,
|
|
|
|
gchar * line_buffer, guint16 * index);
|
|
|
|
static inline void
|
|
|
|
pango_span_markup_end (cea708PangoSpanControl * span_control,
|
|
|
|
gchar * line_buffer, guint16 * index);
|
|
|
|
static void
|
|
|
|
gst_cea708dec_show_pango_window (Cea708Dec * decoder, guint window_id);
|
|
|
|
static void
|
|
|
|
gst_cea708dec_clear_window_text (Cea708Dec * decoder, guint window_id);
|
|
|
|
static void
|
|
|
|
gst_cea708dec_scroll_window_up (Cea708Dec * decoder, guint window_id);
|
|
|
|
static void gst_cea708dec_init_window (Cea708Dec * decoder, guint window_id);
|
|
|
|
static void
|
|
|
|
gst_cea708dec_set_pen_attributes (Cea708Dec * decoder,
|
|
|
|
guint8 * dtvcc_buffer, int index);
|
|
|
|
static void
|
|
|
|
gst_cea708dec_for_each_window (Cea708Dec * decoder,
|
|
|
|
guint8 window_list, VisibilityControl visibility_control,
|
|
|
|
const gchar * log_message, void (*function) (Cea708Dec * decoder,
|
|
|
|
guint window_id));
|
|
|
|
static void
|
|
|
|
gst_cea708dec_process_command (Cea708Dec * decoder,
|
|
|
|
guint8 * dtvcc_buffer, int index);
|
|
|
|
static void get_cea708dec_bufcat (gpointer data, gpointer whole_buf);
|
|
|
|
static gboolean
|
|
|
|
gst_cea708dec_render_text (Cea708Dec * decoder, GSList ** text_list,
|
|
|
|
gint length, guint window_id);
|
|
|
|
static void gst_cea708dec_window_add_char (Cea708Dec * decoder, gunichar c);
|
|
|
|
static void
|
|
|
|
gst_cea708dec_process_c2 (Cea708Dec * decoder, guint8 * dtvcc_buffer,
|
|
|
|
int index);
|
|
|
|
static void
|
|
|
|
gst_cea708dec_process_g2 (Cea708Dec * decoder, guint8 * dtvcc_buffer,
|
|
|
|
int index);
|
|
|
|
static void
|
|
|
|
gst_cea708dec_process_c3 (Cea708Dec * decoder, guint8 * dtvcc_buffer,
|
|
|
|
int index);
|
|
|
|
static void
|
|
|
|
gst_cea708dec_process_g3 (Cea708Dec * decoder, guint8 * dtvcc_buffer,
|
|
|
|
int index);
|
|
|
|
static void
|
|
|
|
gst_cea708dec_process_dtvcc_byte (Cea708Dec * decoder,
|
|
|
|
guint8 * dtvcc_buffer, int index);
|
|
|
|
|
|
|
|
/* For debug, print name of 708 command */
|
|
|
|
Cea708Dec *
|
|
|
|
gst_cea708dec_create (PangoContext * pango_context)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
Cea708Dec *decoder = g_malloc (sizeof (Cea708Dec));;
|
|
|
|
memset (decoder, 0, sizeof (Cea708Dec));
|
|
|
|
|
|
|
|
/* Initialize 708 variables */
|
|
|
|
for (i = 0; i < MAX_708_WINDOWS; i++) {
|
|
|
|
decoder->cc_windows[i] = g_malloc (sizeof (cea708Window));
|
|
|
|
gst_cea708dec_init_window (decoder, i);
|
|
|
|
}
|
|
|
|
decoder->desired_service = 1;
|
|
|
|
decoder->use_ARGB = FALSE;
|
|
|
|
decoder->pango_context = pango_context;
|
|
|
|
return decoder;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gst_cea708dec_set_service_number (Cea708Dec * decoder, gint8 desired_service)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
gint8 previous_desired_service;
|
|
|
|
previous_desired_service = decoder->desired_service;
|
|
|
|
decoder->desired_service = desired_service;
|
|
|
|
/* If there has been a change in the desired service number, then clear
|
|
|
|
* the windows for the new service. */
|
|
|
|
if (decoder->desired_service != previous_desired_service) {
|
|
|
|
for (i = 0; i < MAX_708_WINDOWS; i++) {
|
|
|
|
gst_cea708dec_init_window (decoder, i);
|
|
|
|
}
|
|
|
|
decoder->current_window = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
gst_cea708dec_process_dtvcc_packet (Cea708Dec * decoder, guint8 * dtvcc_buffer,
|
|
|
|
gsize dtvcc_size)
|
|
|
|
{
|
|
|
|
guint i;
|
|
|
|
gboolean need_render = FALSE;
|
|
|
|
cea708Window *window = NULL;
|
|
|
|
guint window_id;
|
|
|
|
|
|
|
|
/* Service block header (see CEA-708 6.2.1) */
|
|
|
|
guint8 block_size;
|
|
|
|
guint8 service_number;
|
|
|
|
|
|
|
|
guint parse_index = 0;
|
2018-05-28 13:55:57 +00:00
|
|
|
#ifndef GST_DISABLE_GST_DEBUG
|
2015-03-11 10:00:08 +00:00
|
|
|
guint8 sequence_number = (dtvcc_buffer[parse_index] & 0xC0) >> 6;
|
|
|
|
guint8 pkt_size = DTVCC_PKT_SIZE (dtvcc_buffer[parse_index] & 0x3F);
|
2018-05-28 13:55:57 +00:00
|
|
|
#endif
|
|
|
|
|
2015-03-11 10:00:08 +00:00
|
|
|
parse_index += 1;
|
|
|
|
|
|
|
|
block_size = dtvcc_buffer[parse_index] & 0x1F;
|
|
|
|
service_number = (dtvcc_buffer[parse_index] & 0xE0) >> 5;
|
|
|
|
parse_index += 1;
|
|
|
|
|
|
|
|
if (service_number == 7) {
|
|
|
|
/* Get extended service number */
|
|
|
|
service_number = dtvcc_buffer[parse_index] & 0x3F;
|
|
|
|
parse_index += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
GST_LOG ("full_size:%" G_GSIZE_FORMAT
|
|
|
|
" size=%d seq=%d block_size=%d service_num=%d", dtvcc_size, pkt_size,
|
|
|
|
sequence_number, block_size, service_number);
|
|
|
|
|
|
|
|
|
|
|
|
/* Process desired_service cc data */
|
|
|
|
if (decoder->desired_service == service_number) {
|
|
|
|
for (i = 0; i < block_size; i++) {
|
|
|
|
/* The Dtvcc buffer contains a stream of commands, command parameters,
|
|
|
|
* and characters which are the actual captions. Process commands and
|
|
|
|
* store captions in simulated 708 windows: */
|
|
|
|
gst_cea708dec_process_dtvcc_byte (decoder, dtvcc_buffer, parse_index + i);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (window_id = 0; window_id < 8; window_id++) {
|
|
|
|
window = decoder->cc_windows[window_id];
|
|
|
|
GST_LOG ("window #%02d deleted:%d visible:%d updated:%d", window_id,
|
|
|
|
window->deleted, window->visible, window->updated);
|
|
|
|
if (!window->updated) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
need_render = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return need_render;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_cea708dec_process_dtvcc_byte (Cea708Dec * decoder,
|
|
|
|
guint8 * dtvcc_buffer, int index)
|
|
|
|
{
|
|
|
|
guint8 c = dtvcc_buffer[index];
|
|
|
|
|
|
|
|
if (decoder->output_ignore) {
|
|
|
|
/* Ignore characters/parameters after a command. */
|
|
|
|
/* GST_TRACE ("[%d] ignore %X", decoder->output_ignore, c); */
|
|
|
|
decoder->output_ignore--;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
GST_DEBUG ("processing 0x%02X", c);
|
|
|
|
|
|
|
|
if (c >= 0x00 && c <= 0x1F) { /* C0 */
|
|
|
|
if (c == 0x03) { /* ETX */
|
|
|
|
gst_cea708dec_process_command (decoder, dtvcc_buffer, index);
|
|
|
|
} else if (c == 0x00 || c == 0x08 || c == 0x0C || c == 0x0D || c == 0x0E) {
|
|
|
|
gst_cea708dec_window_add_char (decoder, c);
|
|
|
|
} else if (c == 0x10) { /* EXT1 */
|
|
|
|
guint8 next_c = dtvcc_buffer[index + 1];
|
|
|
|
if (next_c >= 0x00 && next_c <= 0x1F) { /* C2 */
|
|
|
|
gst_cea708dec_process_c2 (decoder, dtvcc_buffer, index + 1);
|
|
|
|
} else if (next_c >= 0x20 && next_c <= 0x7F) { /* G2 */
|
|
|
|
gst_cea708dec_process_g2 (decoder, dtvcc_buffer, index + 1);
|
|
|
|
} else if (next_c >= 0x80 && next_c <= 0x9F) { /* C3 */
|
|
|
|
gst_cea708dec_process_c3 (decoder, dtvcc_buffer, index + 1);
|
|
|
|
} else if (next_c >= 0xA0 && next_c <= 0xFF) { /* G3 */
|
|
|
|
gst_cea708dec_process_g3 (decoder, dtvcc_buffer, index + 1);
|
|
|
|
}
|
|
|
|
} else if (c > 0x10 && c < 0x18) {
|
|
|
|
decoder->output_ignore = 1;
|
|
|
|
GST_INFO ("do not support 0x11-0x17");
|
|
|
|
} else if (c >= 0x18) { /* P16 */
|
|
|
|
/*P16 do not support now */
|
|
|
|
decoder->output_ignore = 2;
|
|
|
|
GST_INFO ("do not support 0x18-0x1F");
|
|
|
|
}
|
|
|
|
} else if ((c >= 0x20) && (c <= 0x7F)) { /* G0 */
|
|
|
|
if (c == 0x7F) {
|
|
|
|
gst_cea708dec_window_add_char (decoder, CC_SPECIAL_CODE_MUSIC_NOTE);
|
|
|
|
} else {
|
|
|
|
gst_cea708dec_window_add_char (decoder, c);
|
|
|
|
}
|
|
|
|
} else if ((c >= 0x80) && (c <= 0x9F)) { /* C1 */
|
|
|
|
gst_cea708dec_process_command (decoder, dtvcc_buffer, index);
|
|
|
|
} else if ((c >= 0xA0) && (c <= 0xFF)) { /* G1 */
|
|
|
|
gst_cea708dec_window_add_char (decoder, c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* For debug, print name of 708 command */
|
|
|
|
static void
|
|
|
|
gst_cea708dec_print_command_name (Cea708Dec * decoder, guint8 c)
|
|
|
|
{
|
|
|
|
gchar buffer[32];
|
|
|
|
const gchar *command = NULL;
|
|
|
|
|
|
|
|
switch (c) {
|
|
|
|
case CC_COMMAND_ETX:
|
|
|
|
command = (const gchar *) "End of text";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CC_COMMAND_CW0:
|
|
|
|
case CC_COMMAND_CW1:
|
|
|
|
case CC_COMMAND_CW2:
|
|
|
|
case CC_COMMAND_CW3:
|
|
|
|
case CC_COMMAND_CW4:
|
|
|
|
case CC_COMMAND_CW5:
|
|
|
|
case CC_COMMAND_CW6:
|
|
|
|
case CC_COMMAND_CW7:
|
|
|
|
/* Set current window, no parameters */
|
|
|
|
g_snprintf (buffer, sizeof (buffer), "Set current window %d", c & 0x3);
|
|
|
|
command = buffer;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CC_COMMAND_CLW:
|
|
|
|
command = (const gchar *) "Clear windows";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CC_COMMAND_DSW:
|
|
|
|
command = (const gchar *) "Display windows";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CC_COMMAND_HDW:
|
|
|
|
command = (const gchar *) "Hide windows";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CC_COMMAND_TGW:
|
|
|
|
command = (const gchar *) "Toggle windows";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CC_COMMAND_DLW:
|
|
|
|
command = (const gchar *) "Delete windows";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CC_COMMAND_DLY:
|
|
|
|
command = (const gchar *) "Delay";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CC_COMMAND_DLC:
|
|
|
|
command = (const gchar *) "Delay cancel";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CC_COMMAND_RST:
|
|
|
|
command = (const gchar *) "Reset";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CC_COMMAND_SPA:
|
|
|
|
command = (const gchar *) "Set pen attributes";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CC_COMMAND_SPC:
|
|
|
|
command = (const gchar *) "Set pen color";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CC_COMMAND_SPL:
|
|
|
|
command = (const gchar *) "Set pen location";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CC_COMMAND_SWA:
|
|
|
|
command = (const gchar *) "Set window attributes";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CC_COMMAND_DF0:
|
|
|
|
case CC_COMMAND_DF1:
|
|
|
|
case CC_COMMAND_DF2:
|
|
|
|
case CC_COMMAND_DF3:
|
|
|
|
case CC_COMMAND_DF4:
|
|
|
|
case CC_COMMAND_DF5:
|
|
|
|
case CC_COMMAND_DF6:
|
|
|
|
case CC_COMMAND_DF7:
|
|
|
|
g_snprintf (buffer, sizeof (buffer), "define window %d", c & 0x3);
|
|
|
|
command = buffer;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
if ((c > 0x80) && (c < 0x9F))
|
|
|
|
command = (const gchar *) "Unknown";
|
|
|
|
break;
|
|
|
|
} /* switch */
|
|
|
|
|
|
|
|
if (NULL != command) {
|
|
|
|
GST_LOG ("Process 708 command (%02X): %s", c, command);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_cea708dec_render_pangocairo (cea708Window * window)
|
|
|
|
{
|
|
|
|
cairo_t *crt;
|
|
|
|
cairo_surface_t *surf;
|
|
|
|
cairo_t *shadow;
|
|
|
|
cairo_surface_t *surf_shadow;
|
|
|
|
PangoRectangle ink_rec, logical_rec;
|
|
|
|
gint width, height;
|
|
|
|
|
|
|
|
pango_layout_get_pixel_extents (window->layout, &ink_rec, &logical_rec);
|
|
|
|
|
|
|
|
width = logical_rec.width + window->shadow_offset;
|
|
|
|
height = logical_rec.height + logical_rec.y + window->shadow_offset;
|
|
|
|
|
|
|
|
surf_shadow = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
|
|
|
|
shadow = cairo_create (surf_shadow);
|
|
|
|
|
|
|
|
/* clear shadow surface */
|
|
|
|
cairo_set_operator (shadow, CAIRO_OPERATOR_CLEAR);
|
|
|
|
cairo_paint (shadow);
|
|
|
|
cairo_set_operator (shadow, CAIRO_OPERATOR_OVER);
|
|
|
|
|
|
|
|
/* draw shadow text */
|
|
|
|
cairo_save (shadow);
|
|
|
|
cairo_set_source_rgba (shadow, 0.0, 0.0, 0.0, 0.5);
|
|
|
|
cairo_translate (shadow, window->shadow_offset, window->shadow_offset);
|
|
|
|
pango_cairo_show_layout (shadow, window->layout);
|
|
|
|
cairo_restore (shadow);
|
|
|
|
|
|
|
|
/* draw outline text */
|
|
|
|
cairo_save (shadow);
|
|
|
|
cairo_set_source_rgb (shadow, 0.0, 0.0, 0.0);
|
|
|
|
cairo_set_line_width (shadow, window->outline_offset);
|
|
|
|
pango_cairo_layout_path (shadow, window->layout);
|
|
|
|
cairo_stroke (shadow);
|
|
|
|
cairo_restore (shadow);
|
|
|
|
|
|
|
|
cairo_destroy (shadow);
|
|
|
|
|
|
|
|
window->text_image = g_realloc (window->text_image, 4 * width * height);
|
|
|
|
|
|
|
|
surf = cairo_image_surface_create_for_data (window->text_image,
|
|
|
|
CAIRO_FORMAT_ARGB32, width, height, width * 4);
|
|
|
|
crt = cairo_create (surf);
|
|
|
|
cairo_set_operator (crt, CAIRO_OPERATOR_CLEAR);
|
|
|
|
cairo_paint (crt);
|
|
|
|
cairo_set_operator (crt, CAIRO_OPERATOR_OVER);
|
|
|
|
|
|
|
|
/* set default color */
|
|
|
|
cairo_set_source_rgb (crt, 1.0, 1.0, 1.0);
|
|
|
|
|
|
|
|
cairo_save (crt);
|
|
|
|
/* draw text */
|
|
|
|
pango_cairo_show_layout (crt, window->layout);
|
|
|
|
cairo_restore (crt);
|
|
|
|
|
|
|
|
/* composite shadow with offset */
|
|
|
|
cairo_set_operator (crt, CAIRO_OPERATOR_DEST_OVER);
|
|
|
|
cairo_set_source_surface (crt, surf_shadow, 0.0, 0.0);
|
|
|
|
cairo_paint (crt);
|
|
|
|
|
|
|
|
cairo_destroy (crt);
|
|
|
|
cairo_surface_destroy (surf_shadow);
|
|
|
|
cairo_surface_destroy (surf);
|
|
|
|
window->image_width = width;
|
|
|
|
window->image_height = height;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_cea708dec_adjust_values_with_fontdesc (cea708Window * window,
|
|
|
|
PangoFontDescription * desc)
|
|
|
|
{
|
|
|
|
gint font_size = pango_font_description_get_size (desc) / PANGO_SCALE;
|
|
|
|
|
|
|
|
window->shadow_offset = (double) (font_size) / 13.0;
|
|
|
|
window->outline_offset = (double) (font_size) / 15.0;
|
|
|
|
if (window->outline_offset < MINIMUM_OUTLINE_OFFSET)
|
|
|
|
window->outline_offset = MINIMUM_OUTLINE_OFFSET;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gint
|
|
|
|
gst_cea708dec_text_list_add (GSList ** text_list,
|
|
|
|
gint len, const gchar * format, ...)
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
gchar *str;
|
|
|
|
|
|
|
|
va_start (args, format);
|
|
|
|
|
|
|
|
str = g_malloc0 (len);
|
|
|
|
len = g_vsnprintf (str, len, format, args);
|
|
|
|
*text_list = g_slist_append (*text_list, str);
|
|
|
|
GST_LOG ("added %p str[%d]: %s", str, len, str);
|
|
|
|
|
|
|
|
va_end (args);
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const PangoAlignment
|
|
|
|
gst_cea708dec_get_align_mode (guint8 justify_mode)
|
|
|
|
{
|
|
|
|
guint align_mode = PANGO_ALIGN_LEFT;
|
|
|
|
|
|
|
|
switch (justify_mode) {
|
|
|
|
case JUSTIFY_LEFT:
|
|
|
|
align_mode = PANGO_ALIGN_LEFT;
|
|
|
|
break;
|
|
|
|
case JUSTIFY_RIGHT:
|
|
|
|
align_mode = PANGO_ALIGN_RIGHT;
|
|
|
|
break;
|
|
|
|
case JUSTIFY_CENTER:
|
|
|
|
align_mode = PANGO_ALIGN_CENTER;
|
|
|
|
break;
|
|
|
|
case JUSTIFY_FULL:
|
|
|
|
default:
|
|
|
|
align_mode = PANGO_ALIGN_LEFT;
|
|
|
|
}
|
|
|
|
return align_mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const gchar *
|
|
|
|
gst_cea708dec_get_color_name (guint8 color)
|
|
|
|
{
|
|
|
|
guint index = 0;
|
|
|
|
|
|
|
|
switch (color) {
|
|
|
|
case CEA708_COLOR_BLACK:
|
|
|
|
index = COLOR_TYPE_BLACK;
|
|
|
|
break;
|
|
|
|
case CEA708_COLOR_WHITE:
|
|
|
|
index = COLOR_TYPE_WHITE;
|
|
|
|
break;
|
|
|
|
case CEA708_COLOR_RED:
|
|
|
|
index = COLOR_TYPE_RED;
|
|
|
|
break;
|
|
|
|
case CEA708_COLOR_GREEN:
|
|
|
|
index = COLOR_TYPE_GREEN;
|
|
|
|
break;
|
|
|
|
case CEA708_COLOR_BLUE:
|
|
|
|
index = COLOR_TYPE_BLUE;
|
|
|
|
break;
|
|
|
|
case CEA708_COLOR_YELLOW:
|
|
|
|
index = COLOR_TYPE_YELLOW;
|
|
|
|
break;
|
|
|
|
case CEA708_COLOR_MAGENTA:
|
|
|
|
index = COLOR_TYPE_MAGENTA;
|
|
|
|
break;
|
|
|
|
case CEA708_COLOR_CYAN:
|
|
|
|
index = COLOR_TYPE_CYAN;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return color_names[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
static guint8
|
|
|
|
gst_cea708dec_map_minimum_color (guint8 color)
|
|
|
|
{
|
|
|
|
/*According to spec minimum color list define */
|
|
|
|
/*check R */
|
|
|
|
switch ((color & 0x30) >> 4) {
|
|
|
|
case 1:
|
|
|
|
color &= 0xF;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
color &= 0x2F;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/*check G */
|
|
|
|
switch ((color & 0xC) >> 2) {
|
|
|
|
case 1:
|
|
|
|
color &= 0x33;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
color &= 0x3B;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/*check B */
|
|
|
|
switch (color & 0x3) {
|
|
|
|
case 1:
|
|
|
|
color &= 0x3C;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
color &= 0x3E;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return color;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_cea708dec_set_pen_color (Cea708Dec * decoder,
|
|
|
|
guint8 * dtvcc_buffer, int index)
|
|
|
|
{
|
|
|
|
cea708Window *window = decoder->cc_windows[decoder->current_window];
|
|
|
|
|
|
|
|
/* format
|
|
|
|
fo1 fo0 fr1 fr0 fg1 fg0 fb1 fb0
|
|
|
|
bo1 bo0 br1 br0 bg1 bg0 bb1 bb0
|
|
|
|
0 0 er1 er0 eg1 eg0 eb1 eb0 */
|
|
|
|
window->pen_color.fg_color =
|
|
|
|
gst_cea708dec_map_minimum_color (dtvcc_buffer[index] & 0x3F);
|
|
|
|
window->pen_color.fg_opacity = (dtvcc_buffer[index] & 0xC0) >> 6;
|
|
|
|
window->pen_color.bg_color =
|
|
|
|
gst_cea708dec_map_minimum_color (dtvcc_buffer[index + 1] & 0x3F);
|
|
|
|
window->pen_color.bg_opacity = (dtvcc_buffer[index + 1] & 0xC0) >> 6;
|
|
|
|
window->pen_color.edge_color =
|
|
|
|
gst_cea708dec_map_minimum_color (dtvcc_buffer[index + 2] & 0x3F);
|
|
|
|
GST_LOG ("pen_color fg=0x%x fg_op=0x%x bg=0x%x bg_op=0x%x edge=0x%x",
|
|
|
|
window->pen_color.fg_color, window->pen_color.fg_opacity,
|
|
|
|
window->pen_color.bg_color, window->pen_color.bg_opacity,
|
|
|
|
window->pen_color.edge_color);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_cea708dec_set_window_attributes (Cea708Dec * decoder,
|
|
|
|
guint8 * dtvcc_buffer, int index)
|
|
|
|
{
|
|
|
|
cea708Window *window = decoder->cc_windows[decoder->current_window];
|
|
|
|
|
|
|
|
/* format
|
|
|
|
fo1 fo0 fr1 fr0 fg1 fg0 fb1 fb0
|
|
|
|
bt1 bt0 br1 br0 bg1 bg0 bb1 bb0
|
|
|
|
bt2 ww pd1 pd0 sd1 sd0 j1 j0
|
|
|
|
es3 es2 es1 es0 ed1 ed0 de1 de0 */
|
|
|
|
window->fill_color =
|
|
|
|
gst_cea708dec_map_minimum_color (dtvcc_buffer[index] & 0x3F);
|
|
|
|
window->fill_opacity = (dtvcc_buffer[index] & 0xC0) >> 6;
|
|
|
|
window->border_color =
|
|
|
|
gst_cea708dec_map_minimum_color (dtvcc_buffer[index + 1] & 0x3F);
|
|
|
|
window->border_type =
|
|
|
|
((dtvcc_buffer[index + 1] & 0xC0) >> 6) | ((dtvcc_buffer[index +
|
|
|
|
2] & 0x80) >> 5);
|
|
|
|
window->word_wrap = (dtvcc_buffer[index + 2] & 0x40) ? TRUE : FALSE;
|
|
|
|
window->justify_mode = dtvcc_buffer[index + 2] & 0x3;
|
|
|
|
window->scroll_direction = (dtvcc_buffer[index + 2] & 0xC) >> 2;
|
|
|
|
window->print_direction = (dtvcc_buffer[index + 2] & 0x30) >> 2;
|
|
|
|
window->display_effect = (dtvcc_buffer[index + 3] & 0x3);
|
|
|
|
window->effect_direction = (dtvcc_buffer[index + 3] & 0xC);
|
|
|
|
window->effect_speed = (dtvcc_buffer[index + 3] & 0xF0) >> 4;
|
|
|
|
|
|
|
|
GST_LOG ("Print direction = %d", window->print_direction);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_cea708dec_set_pen_style (Cea708Dec * decoder, guint8 pen_style_id)
|
|
|
|
{
|
|
|
|
cea708Window *window = decoder->cc_windows[decoder->current_window];
|
|
|
|
|
|
|
|
window->pen_attributes.pen_size = PEN_SIZE_STANDARD;
|
|
|
|
window->pen_attributes.font_style = FONT_STYLE_DEFAULT;
|
|
|
|
window->pen_attributes.offset = PEN_OFFSET_NORMAL;
|
|
|
|
window->pen_attributes.italics = FALSE;
|
|
|
|
window->pen_attributes.underline = FALSE;
|
|
|
|
window->pen_attributes.edge_type = EDGE_TYPE_NONE;
|
|
|
|
window->pen_color.fg_color = CEA708_COLOR_WHITE;
|
|
|
|
window->pen_color.fg_opacity = SOLID;
|
|
|
|
window->pen_color.bg_color = CEA708_COLOR_BLACK;
|
|
|
|
window->pen_color.bg_opacity = SOLID;
|
|
|
|
window->pen_color.edge_color = CEA708_COLOR_BLACK;
|
|
|
|
|
|
|
|
/* CEA-708 predefined pen style ids */
|
|
|
|
switch (pen_style_id) {
|
|
|
|
default:
|
|
|
|
case PEN_STYLE_DEFAULT:
|
|
|
|
window->pen_attributes.font_style = FONT_STYLE_DEFAULT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PEN_STYLE_MONO_SERIF:
|
|
|
|
window->pen_attributes.font_style = FONT_STYLE_MONO_SERIF;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PEN_STYLE_PROP_SERIF:
|
|
|
|
window->pen_attributes.font_style = FONT_STYLE_PROP_SERIF;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PEN_STYLE_MONO_SANS:
|
|
|
|
window->pen_attributes.font_style = FONT_STYLE_MONO_SANS;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PEN_STYLE_PROP_SANS:
|
|
|
|
window->pen_attributes.font_style = FONT_STYLE_PROP_SANS;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PEN_STYLE_MONO_SANS_TRANSPARENT:
|
|
|
|
window->pen_attributes.font_style = FONT_STYLE_MONO_SANS;
|
|
|
|
window->pen_color.bg_opacity = TRANSPARENT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PEN_STYLE_PROP_SANS_TRANSPARENT:
|
|
|
|
window->pen_attributes.font_style = FONT_STYLE_PROP_SANS;
|
|
|
|
window->pen_color.bg_opacity = TRANSPARENT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_cea708dec_set_window_style (Cea708Dec * decoder, guint8 style_id)
|
|
|
|
{
|
|
|
|
cea708Window *window = decoder->cc_windows[decoder->current_window];
|
|
|
|
|
|
|
|
/* set the 'normal' styles first, then deviate in special cases below... */
|
|
|
|
window->justify_mode = JUSTIFY_LEFT;
|
|
|
|
window->print_direction = PRINT_DIR_LEFT_TO_RIGHT;
|
|
|
|
window->scroll_direction = SCROLL_DIR_BOTTOM_TO_TOP;
|
|
|
|
window->word_wrap = FALSE;
|
|
|
|
window->effect_direction = EFFECT_DIR_LEFT_TO_RIGHT;
|
|
|
|
window->display_effect = DISPLAY_EFFECT_SNAP;
|
|
|
|
window->effect_speed = 0;
|
|
|
|
window->fill_color = CEA708_COLOR_BLACK;
|
|
|
|
window->fill_opacity = SOLID;
|
|
|
|
|
|
|
|
/* CEA-708 predefined window style ids */
|
|
|
|
switch (style_id) {
|
|
|
|
default:
|
|
|
|
case WIN_STYLE_NORMAL:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WIN_STYLE_TRANSPARENT:
|
|
|
|
window->fill_opacity = TRANSPARENT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WIN_STYLE_NORMAL_CENTERED:
|
|
|
|
window->justify_mode = JUSTIFY_CENTER;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WIN_STYLE_NORMAL_WORD_WRAP:
|
|
|
|
window->word_wrap = TRUE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WIN_STYLE_TRANSPARENT_WORD_WRAP:
|
|
|
|
window->fill_opacity = TRANSPARENT;
|
|
|
|
window->word_wrap = TRUE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WIN_STYLE_TRANSPARENT_CENTERED:
|
|
|
|
window->fill_opacity = TRANSPARENT;
|
|
|
|
window->justify_mode = JUSTIFY_CENTER;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WIN_STYLE_ROTATED:
|
|
|
|
window->print_direction = PRINT_DIR_TOP_TO_BOTTOM;
|
|
|
|
window->scroll_direction = SCROLL_DIR_RIGHT_TO_LEFT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Define window - window size, window style, pen style, anchor position, etc */
|
|
|
|
static void
|
|
|
|
gst_cea708dec_define_window (Cea708Dec * decoder,
|
|
|
|
guint8 * dtvcc_buffer, int index)
|
|
|
|
{
|
|
|
|
cea708Window *window = decoder->cc_windows[decoder->current_window];
|
|
|
|
guint8 priority = 0;
|
|
|
|
guint8 anchor_point = 0;
|
|
|
|
guint8 relative_position = 0;
|
|
|
|
guint8 anchor_vertical = 0;
|
|
|
|
guint8 anchor_horizontal = 0;
|
|
|
|
guint8 row_count = 0;
|
|
|
|
guint8 column_count = 0;
|
|
|
|
guint8 row_lock = 0;
|
|
|
|
guint8 column_lock = 0;
|
|
|
|
gboolean visible = FALSE;
|
|
|
|
guint8 style_id = 0;
|
|
|
|
guint8 pen_style_id = 0;
|
2018-05-28 13:55:57 +00:00
|
|
|
#ifndef GST_DISABLE_GST_DEBUG
|
2015-03-11 10:00:08 +00:00
|
|
|
guint v_anchor = 0;
|
|
|
|
guint h_anchor = 0;
|
2018-05-28 13:55:57 +00:00
|
|
|
#endif
|
2015-03-11 10:00:08 +00:00
|
|
|
|
|
|
|
GST_LOG ("current_window=%d", decoder->current_window);
|
|
|
|
GST_LOG ("dtvcc_buffer %02x %02x %02x %02x %02x %02x",
|
|
|
|
dtvcc_buffer[index + 0], dtvcc_buffer[index + 1],
|
|
|
|
dtvcc_buffer[index + 2], dtvcc_buffer[index + 3],
|
|
|
|
dtvcc_buffer[index + 4], dtvcc_buffer[index + 5]);
|
|
|
|
|
|
|
|
/* Initialize window structure */
|
|
|
|
if (NULL != window) {
|
|
|
|
if (window->deleted) {
|
|
|
|
/* Spec says on window create (but not re-definition) the pen position
|
|
|
|
* must be reset to 0
|
|
|
|
* TODO: also set all text positions to the fill color */
|
|
|
|
window->deleted = FALSE;
|
|
|
|
window->pen_row = 0;
|
|
|
|
window->pen_col = 0;
|
|
|
|
}
|
|
|
|
/* format of parameters:
|
|
|
|
0 0 v rl cl p2 p1 p0
|
|
|
|
rp av7 av6 av5 av4 av3 av1 av0
|
|
|
|
ah7 ah6 ah5 ah4 ah3 ah2 ah1 ah0
|
|
|
|
ap3 ap2 ap1 ap0 rc3 rc2 rc1 rc0
|
|
|
|
0 0 cc5 cc4 cc3 cc2 cc1 cc0
|
|
|
|
0 0 ws2 ws1 ws0 ps2 ps1 ps0 */
|
|
|
|
|
|
|
|
/* parameter byte 0 */
|
|
|
|
priority = dtvcc_buffer[index] & 0x7;
|
|
|
|
column_lock = (dtvcc_buffer[index] & 0x8) ? TRUE : FALSE;
|
|
|
|
row_lock = (dtvcc_buffer[index] & 0x10) ? TRUE : FALSE;
|
|
|
|
visible = (dtvcc_buffer[index] & 0x20) ? TRUE : FALSE;
|
|
|
|
|
|
|
|
/* parameter byte 1 */
|
|
|
|
relative_position = (dtvcc_buffer[index + 1] & 0x80) ? TRUE : FALSE;
|
|
|
|
anchor_vertical = dtvcc_buffer[index + 1] & 0x7F;
|
|
|
|
|
|
|
|
/* parameter byte 2 */
|
|
|
|
anchor_horizontal = dtvcc_buffer[index + 2];
|
|
|
|
|
|
|
|
/* parameter byte 3 */
|
|
|
|
anchor_point = (dtvcc_buffer[index + 3] & 0xF0) >> 4;
|
|
|
|
row_count = (dtvcc_buffer[index + 3] & 0xF) + 1;
|
|
|
|
|
|
|
|
/* parameter byte 4 */
|
|
|
|
column_count = (dtvcc_buffer[index + 4] & 0x3F) + 1;
|
|
|
|
|
|
|
|
/* parameter byte 5 */
|
|
|
|
style_id = (dtvcc_buffer[index + 5] & 0x38) >> 3;
|
|
|
|
pen_style_id = dtvcc_buffer[index + 5] & 0x7;
|
|
|
|
|
|
|
|
window->screen_vertical = anchor_vertical;
|
|
|
|
window->screen_horizontal = anchor_horizontal;
|
|
|
|
|
|
|
|
if (relative_position == FALSE) {
|
|
|
|
/* If position is in absolute coords, convert to percent */
|
|
|
|
if (decoder->width == 0 || decoder->height == 0) {
|
|
|
|
window->screen_vertical /= 100;
|
|
|
|
window->screen_horizontal /= 100;
|
|
|
|
} else if ((decoder->width * 9) % (decoder->height * 16) == 0) {
|
|
|
|
window->screen_vertical /= SCREEN_HEIGHT_16_9;
|
|
|
|
window->screen_horizontal /= SCREEN_WIDTH_16_9;
|
|
|
|
} else if ((decoder->width * 3) % (decoder->height * 4) == 0) {
|
|
|
|
window->screen_vertical /= SCREEN_HEIGHT_4_3;
|
|
|
|
window->screen_horizontal /= SCREEN_WIDTH_4_3;
|
|
|
|
} else {
|
|
|
|
window->screen_vertical /= 100;
|
|
|
|
window->screen_horizontal /= 100;
|
|
|
|
}
|
|
|
|
window->screen_vertical *= 100;
|
|
|
|
window->screen_horizontal *= 100;
|
|
|
|
}
|
|
|
|
|
|
|
|
window->priority = priority;
|
|
|
|
window->anchor_point = anchor_point;
|
|
|
|
window->relative_position = relative_position;
|
|
|
|
window->anchor_vertical = anchor_vertical;
|
|
|
|
window->anchor_horizontal = anchor_horizontal;
|
|
|
|
window->row_count = row_count;
|
|
|
|
window->column_count = column_count;
|
|
|
|
window->row_lock = row_lock;
|
|
|
|
window->column_lock = column_lock;
|
|
|
|
window->visible = visible;
|
|
|
|
|
|
|
|
/* Make sure row/col limits are not too large */
|
|
|
|
if (window->row_count > WINDOW_MAX_ROWS) {
|
|
|
|
GST_WARNING ("window row count %d is too large", window->row_count);
|
|
|
|
window->row_count = WINDOW_MAX_ROWS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (window->column_count > WINDOW_MAX_COLS) {
|
|
|
|
GST_WARNING ("window column count %d is too large", window->column_count);
|
|
|
|
window->column_count = WINDOW_MAX_COLS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (style_id != 0) {
|
|
|
|
window->style_id = style_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pen_style_id != 0) {
|
|
|
|
window->pen_style_id = pen_style_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
gst_cea708dec_set_window_style (decoder, window->style_id);
|
|
|
|
gst_cea708dec_set_pen_style (decoder, window->pen_style_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
GST_LOG ("priority=%d anchor=%d relative_pos=%d anchor_v=%d anchor_h=%d",
|
|
|
|
window->priority,
|
|
|
|
window->anchor_point,
|
|
|
|
window->relative_position,
|
|
|
|
window->anchor_vertical, window->anchor_horizontal);
|
|
|
|
|
|
|
|
GST_LOG ("row_count=%d col_count=%d row_lock=%d col_lock=%d visible=%d",
|
|
|
|
window->row_count,
|
|
|
|
window->column_count,
|
|
|
|
window->row_lock, window->column_lock, window->visible);
|
|
|
|
|
|
|
|
GST_LOG ("style_id=%d pen_style_id=%d screenH=%f screenV=%f v_offset=%d "
|
|
|
|
"h_offset=%d v_anchor=%d h_anchor=%d",
|
|
|
|
window->style_id,
|
|
|
|
window->pen_style_id,
|
|
|
|
window->screen_horizontal,
|
|
|
|
window->screen_vertical,
|
|
|
|
window->v_offset, window->h_offset, v_anchor, h_anchor);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
pango_span_markup_init (cea708PangoSpanControl * span_control)
|
|
|
|
{
|
|
|
|
memset (span_control, 0, sizeof (cea708PangoSpanControl));
|
|
|
|
span_control->size = PEN_SIZE_STANDARD;
|
|
|
|
span_control->fg_color = CEA708_COLOR_WHITE;
|
|
|
|
span_control->bg_color = CEA708_COLOR_INVALID;
|
|
|
|
span_control->size = PEN_SIZE_STANDARD;
|
|
|
|
span_control->font_style = FONT_STYLE_DEFAULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
pango_span_markup_start (cea708PangoSpanControl * span_control,
|
|
|
|
gchar * line_buffer, guint16 * index)
|
|
|
|
{
|
|
|
|
GST_LOG ("span_control start_flag:%d end_flag:%d txt_flag:%d",
|
|
|
|
span_control->span_start_flag, span_control->span_end_flag,
|
|
|
|
span_control->span_txt_flag);
|
|
|
|
if (!span_control->span_start_flag) {
|
|
|
|
g_strlcat (line_buffer, CEA708_PANGO_SPAN_MARKUP_START, LINEBUFFER_SIZE);
|
|
|
|
*index += strlen (CEA708_PANGO_SPAN_MARKUP_START);
|
|
|
|
span_control->span_start_flag = TRUE;
|
|
|
|
span_control->span_end_flag = FALSE;
|
|
|
|
} else {
|
|
|
|
GST_WARNING ("warning span start !!!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
pango_span_markup_txt (cea708PangoSpanControl * span_control,
|
|
|
|
gchar * line_buffer, guint16 * index)
|
|
|
|
{
|
|
|
|
GST_LOG ("span_control start_flag:%d end_flag:%d txt_flag:%d",
|
|
|
|
span_control->span_start_flag, span_control->span_end_flag,
|
|
|
|
span_control->span_txt_flag);
|
|
|
|
if (span_control->span_start_flag && !span_control->span_txt_flag) {
|
|
|
|
line_buffer[*index] = '>';
|
|
|
|
*index = *index + 1;
|
|
|
|
span_control->span_txt_flag = TRUE;
|
|
|
|
} else {
|
|
|
|
GST_WARNING ("warning span txt !!!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
pango_span_markup_end (cea708PangoSpanControl * span_control,
|
|
|
|
gchar * line_buffer, guint16 * index)
|
|
|
|
{
|
|
|
|
GST_LOG ("span_control start_flag:%d end_flag:%d txt_flag:%d",
|
|
|
|
span_control->span_start_flag, span_control->span_end_flag,
|
|
|
|
span_control->span_txt_flag);
|
|
|
|
if (span_control->span_start_flag && !span_control->span_end_flag) {
|
|
|
|
g_strlcat (line_buffer, CEA708_PANGO_SPAN_MARKUP_END, LINEBUFFER_SIZE);
|
|
|
|
*index = *index + strlen (CEA708_PANGO_SPAN_MARKUP_END);
|
|
|
|
span_control->span_start_flag = FALSE;
|
|
|
|
span_control->span_txt_flag = FALSE;
|
|
|
|
span_control->span_end_flag = TRUE;
|
|
|
|
} else {
|
|
|
|
GST_WARNING ("line_buffer=%s", line_buffer);
|
|
|
|
GST_WARNING ("warning span end !!!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* FIXME: Convert to GString ! */
|
|
|
|
static void
|
|
|
|
gst_cea708dec_show_pango_window (Cea708Dec * decoder, guint window_id)
|
|
|
|
{
|
|
|
|
cea708Window *window = decoder->cc_windows[window_id];
|
|
|
|
guint16 row, col;
|
|
|
|
gboolean display = FALSE; /* = TRUE when text lines should be written */
|
|
|
|
gchar line_buffer[LINEBUFFER_SIZE];
|
|
|
|
gchar outchar_utf8[CC_UTF8_MAX_LENGTH + 1] = { 0 };
|
|
|
|
guint8 utf8_char_length;
|
|
|
|
guint16 i, j;
|
|
|
|
guint16 right_index; /* within a single line of window text, the
|
|
|
|
* index of the rightmost non-blank character */
|
|
|
|
guint16 index;
|
|
|
|
guint len = 0;
|
|
|
|
|
|
|
|
cea708PangoSpanControl span_control;
|
|
|
|
const gchar *fg_color = NULL;
|
|
|
|
const gchar *bg_color = NULL;
|
|
|
|
const gchar *pen_size = NULL;
|
|
|
|
const gchar *font = NULL;
|
|
|
|
|
|
|
|
GST_DEBUG ("window #%02d (visible:%d)", window_id, window->visible);
|
|
|
|
|
|
|
|
window->updated = TRUE;
|
|
|
|
|
|
|
|
if (!window->visible) {
|
|
|
|
GST_DEBUG ("Window is not visible, skipping rendering");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (row = 0; row < window->row_count; row++) {
|
|
|
|
for (col = 0; col < window->column_count; col++) {
|
|
|
|
GST_LOG ("window->text[%d][%d].c '%c'", row, col,
|
|
|
|
window->text[row][col].c);
|
|
|
|
if (window->text[row][col].c != ' ') {
|
|
|
|
/* If there is a non-blank line, then display from there */
|
|
|
|
display = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!display) {
|
|
|
|
GST_DEBUG ("No visible text, skiping rendering");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (row = 0; row < window->row_count; row++) {
|
|
|
|
for (col = 0; col < window->column_count; col++) {
|
|
|
|
if (window->text[row][col].c != ' ') {
|
|
|
|
|
|
|
|
memset (line_buffer, '\0', LINEBUFFER_SIZE);
|
|
|
|
pango_span_markup_init (&span_control);
|
|
|
|
/* Find the rightmost non-blank character on this line: */
|
|
|
|
for (i = right_index = WINDOW_MAX_COLS - 1; i >= col; i--) {
|
|
|
|
if (window->text[row][i].c != ' ') {
|
|
|
|
right_index = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy all of the characters in this row, from the current position
|
|
|
|
* to the right edge */
|
|
|
|
for (i = 0, index = 0;
|
|
|
|
(i <= right_index) && (index < LINEBUFFER_SIZE - 15); i++) {
|
|
|
|
cea708char *current = &window->text[row][i];
|
|
|
|
GST_LOG ("Adding row=%d i=%d c=%c %d", row,
|
|
|
|
i, current->c, current->c);
|
|
|
|
|
|
|
|
do {
|
|
|
|
GST_MEMDUMP ("line_buffer", (guint8 *) line_buffer, index);
|
|
|
|
GST_INFO
|
|
|
|
("text[%d][%d] '%c' underline:%d , italics:%d , font_style:%d , pen_size : %d",
|
|
|
|
row, i, current->c,
|
|
|
|
current->pen_attributes.underline,
|
|
|
|
current->pen_attributes.italics,
|
|
|
|
current->pen_attributes.font_style,
|
|
|
|
current->pen_attributes.pen_size);
|
|
|
|
GST_INFO ("text[%d][%d] '%c' pen_color fg:0x%02X bg:0x%02x", row, i,
|
|
|
|
current->c, current->pen_color.fg_color,
|
|
|
|
current->pen_color.bg_color);
|
|
|
|
GST_INFO
|
|
|
|
("span_control: span_next_flag = %d, underline = %d, italics = %d, font_style = %d, size = %d, fg_color = 0x%02X, bg_color = 0x%02X",
|
|
|
|
span_control.span_next_flag, span_control.underline,
|
|
|
|
span_control.italics, span_control.font_style,
|
|
|
|
span_control.size, span_control.fg_color,
|
|
|
|
span_control.bg_color);
|
|
|
|
|
|
|
|
if ((current->pen_attributes.underline != span_control.underline)
|
|
|
|
|| (current->pen_attributes.italics != span_control.italics)
|
|
|
|
|| (current->pen_attributes.font_style !=
|
|
|
|
span_control.font_style)
|
|
|
|
|| (current->pen_attributes.pen_size != span_control.size)
|
|
|
|
|| (current->pen_color.fg_color != span_control.fg_color)
|
|
|
|
|| (current->pen_color.bg_color != span_control.bg_color)
|
|
|
|
) {
|
|
|
|
GST_LOG ("Markup changed");
|
|
|
|
|
|
|
|
/* check end span to next span start */
|
|
|
|
if (!span_control.span_next_flag) {
|
|
|
|
pango_span_markup_end (&span_control, line_buffer, &index);
|
|
|
|
if (span_control.span_end_flag) {
|
|
|
|
pango_span_markup_init (&span_control);
|
|
|
|
span_control.span_next_flag = TRUE;
|
|
|
|
GST_INFO ("continue check next span !!!");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pango_span_markup_start (&span_control, line_buffer, &index);
|
|
|
|
|
|
|
|
/* Check for transitions to/from underline: */
|
|
|
|
if (current->pen_attributes.underline) {
|
|
|
|
g_strlcat (line_buffer,
|
|
|
|
CEA708_PANGO_SPAN_ATTRIBUTES_UNDERLINE_SINGLE,
|
|
|
|
sizeof (line_buffer));
|
|
|
|
index += strlen (CEA708_PANGO_SPAN_ATTRIBUTES_UNDERLINE_SINGLE);
|
|
|
|
span_control.underline = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check for transitions to/from italics: */
|
|
|
|
if (current->pen_attributes.italics) {
|
|
|
|
g_strlcat (line_buffer,
|
|
|
|
CEA708_PANGO_SPAN_ATTRIBUTES_STYLE_ITALIC,
|
|
|
|
sizeof (line_buffer));
|
|
|
|
index += strlen (CEA708_PANGO_SPAN_ATTRIBUTES_STYLE_ITALIC);
|
|
|
|
span_control.italics = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* FIXME : Something is totally wrong with the way fonts
|
|
|
|
* are being handled. Shouldn't the font description (if
|
|
|
|
* specified by the user) be written for everything ? */
|
|
|
|
if (!decoder->default_font_desc) {
|
|
|
|
font = font_names[current->pen_attributes.font_style];
|
|
|
|
|
|
|
|
if (font) {
|
|
|
|
g_strlcat (line_buffer, CEA708_PANGO_SPAN_ATTRIBUTES_FONT,
|
|
|
|
sizeof (line_buffer));
|
|
|
|
index += strlen (CEA708_PANGO_SPAN_ATTRIBUTES_FONT);
|
|
|
|
line_buffer[index++] = 0x27; /* ' */
|
|
|
|
g_strlcat (line_buffer, font, sizeof (line_buffer));
|
|
|
|
index += strlen (font);
|
|
|
|
span_control.font_style = current->pen_attributes.font_style;
|
|
|
|
|
|
|
|
/* Check for transitions to/from pen size */
|
|
|
|
pen_size = pen_size_names[current->pen_attributes.pen_size];
|
|
|
|
|
|
|
|
line_buffer[index++] = ' ';
|
|
|
|
g_strlcat (line_buffer, pen_size, sizeof (line_buffer));
|
|
|
|
index += strlen (pen_size);
|
|
|
|
line_buffer[index++] = 0x27; /* ' */
|
|
|
|
span_control.size = current->pen_attributes.pen_size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Regardless of the above, we want to remember the latest changes */
|
|
|
|
span_control.font_style = current->pen_attributes.font_style;
|
|
|
|
span_control.size = current->pen_attributes.pen_size;
|
|
|
|
;
|
|
|
|
/* Check for transitions to/from foreground color */
|
|
|
|
fg_color =
|
|
|
|
gst_cea708dec_get_color_name (current->pen_color.fg_color);
|
|
|
|
if (fg_color && current->pen_color.bg_opacity != TRANSPARENT) {
|
|
|
|
g_strlcat (line_buffer, CEA708_PANGO_SPAN_ATTRIBUTES_FOREGROUND,
|
|
|
|
sizeof (line_buffer));
|
|
|
|
index += strlen (CEA708_PANGO_SPAN_ATTRIBUTES_FOREGROUND);
|
|
|
|
line_buffer[index++] = 0x27; /* ' */
|
|
|
|
g_strlcat (line_buffer, fg_color, sizeof (line_buffer));
|
|
|
|
index += strlen (fg_color);
|
|
|
|
line_buffer[index++] = 0x27; /* ' */
|
|
|
|
span_control.fg_color = current->pen_color.fg_color;
|
|
|
|
GST_DEBUG ("span_control.fg_color updated to 0x%02x",
|
|
|
|
span_control.fg_color);
|
|
|
|
} else
|
|
|
|
GST_DEBUG
|
|
|
|
("span_control.fg_color was NOT updated (still 0x%02x)",
|
|
|
|
span_control.fg_color);
|
|
|
|
|
|
|
|
/* Check for transitions to/from background color */
|
|
|
|
bg_color =
|
|
|
|
gst_cea708dec_get_color_name (current->pen_color.bg_color);
|
|
|
|
if (bg_color && current->pen_color.bg_opacity != TRANSPARENT) {
|
|
|
|
g_strlcat (line_buffer, CEA708_PANGO_SPAN_ATTRIBUTES_BACKGROUND,
|
|
|
|
sizeof (line_buffer));
|
|
|
|
index += strlen (CEA708_PANGO_SPAN_ATTRIBUTES_BACKGROUND);
|
|
|
|
line_buffer[index++] = 0x27; /* ' */
|
|
|
|
g_strlcat (line_buffer, bg_color, sizeof (line_buffer));
|
|
|
|
index += strlen (bg_color);
|
|
|
|
line_buffer[index++] = 0x27; /* ' */
|
|
|
|
span_control.bg_color = current->pen_color.bg_color;
|
|
|
|
GST_DEBUG ("span_control.bg_color updated to 0x%02x",
|
|
|
|
span_control.bg_color);
|
|
|
|
} else
|
|
|
|
GST_DEBUG
|
|
|
|
("span_control.bg_color was NOT updated (still 0x%02x)",
|
|
|
|
span_control.bg_color);
|
|
|
|
|
|
|
|
|
|
|
|
/*span text start */
|
|
|
|
pango_span_markup_txt (&span_control, line_buffer, &index);
|
|
|
|
GST_INFO ("span_next_flag = %d", span_control.span_next_flag);
|
|
|
|
}
|
|
|
|
span_control.span_next_flag = FALSE;
|
|
|
|
} while (span_control.span_next_flag);
|
|
|
|
|
|
|
|
|
|
|
|
/* Finally write the character */
|
|
|
|
|
|
|
|
j = 0;
|
|
|
|
|
|
|
|
|
|
|
|
switch (current->c) {
|
|
|
|
case '&':
|
|
|
|
g_snprintf (&(line_buffer[index]),
|
|
|
|
sizeof (line_buffer) - index - 1, "&");
|
|
|
|
index += 5;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '<':
|
|
|
|
g_snprintf (&(line_buffer[index]),
|
|
|
|
sizeof (line_buffer) - index - 1, "<");
|
|
|
|
index += 4;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '>':
|
|
|
|
g_snprintf (&(line_buffer[index]),
|
|
|
|
sizeof (line_buffer) - index - 1, ">");
|
|
|
|
index += 4;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '\'':
|
|
|
|
g_snprintf (&(line_buffer[index]),
|
|
|
|
sizeof (line_buffer) - index - 1, "'");
|
|
|
|
index += 6;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '"':
|
|
|
|
g_snprintf (&(line_buffer[index]),
|
|
|
|
sizeof (line_buffer) - index - 1, """);
|
|
|
|
index += 6;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* FIXME : Use g_string_append_unichar() when switched to GString */
|
|
|
|
utf8_char_length = g_unichar_to_utf8 (current->c, outchar_utf8);
|
|
|
|
while (utf8_char_length > 0) {
|
|
|
|
line_buffer[index++] = outchar_utf8[j++];
|
|
|
|
utf8_char_length--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* pango markup span mode ends */
|
|
|
|
if (span_control.underline || span_control.italics
|
|
|
|
|| (span_control.font_style != FONT_STYLE_DEFAULT)
|
|
|
|
|| (span_control.size != PEN_SIZE_STANDARD)
|
|
|
|
|| (span_control.fg_color != CEA708_COLOR_WHITE)
|
|
|
|
|| (span_control.bg_color != CEA708_COLOR_INVALID)
|
|
|
|
) {
|
|
|
|
pango_span_markup_end (&span_control, line_buffer, &index);
|
|
|
|
pango_span_markup_init (&span_control);
|
|
|
|
}
|
|
|
|
|
|
|
|
GST_LOG ("adding row[%d]: %s\nlength:%d", row, line_buffer, index);
|
|
|
|
|
|
|
|
if (row != window->row_count - 1) {
|
|
|
|
line_buffer[index++] = '\n';
|
|
|
|
}
|
|
|
|
|
|
|
|
len +=
|
|
|
|
gst_cea708dec_text_list_add (&decoder->text_list, index + 1, "%s",
|
|
|
|
line_buffer);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (col == window->column_count && row != window->row_count - 1) {
|
|
|
|
len +=
|
|
|
|
gst_cea708dec_text_list_add (&decoder->text_list, strlen ("\n") + 1,
|
|
|
|
"\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len == 0) {
|
|
|
|
GST_LOG ("window %d had no text", window_id);
|
|
|
|
} else {
|
|
|
|
/* send text to output pad */
|
|
|
|
gst_cea708dec_render_text (decoder, &decoder->text_list, len, window_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_cea708dec_clear_window_text (Cea708Dec * decoder, guint window_id)
|
|
|
|
{
|
|
|
|
cea708Window *window = decoder->cc_windows[window_id];
|
|
|
|
guint row, col;
|
|
|
|
|
|
|
|
for (row = 0; row < WINDOW_MAX_ROWS; row++) {
|
|
|
|
for (col = 0; col < WINDOW_MAX_COLS; col++) {
|
|
|
|
window->text[row][col].c = ' ';
|
|
|
|
window->text[row][col].justify_mode = window->justify_mode;
|
|
|
|
window->text[row][col].pen_attributes = window->pen_attributes;
|
|
|
|
window->text[row][col].pen_color = window->pen_color;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_cea708dec_scroll_window_up (Cea708Dec * decoder, guint window_id)
|
|
|
|
{
|
|
|
|
cea708Window *window = decoder->cc_windows[window_id];
|
|
|
|
guint row, col;
|
|
|
|
|
|
|
|
/* This function should be called to scroll the window up if bottom to
|
|
|
|
* top scrolling is enabled and a carraige-return is encountered, or
|
|
|
|
* word-wrapping */
|
|
|
|
GST_LOG_OBJECT (decoder, "called for window: %d", window_id);
|
|
|
|
|
|
|
|
/* start at row 1 to copy into row 0 (scrolling up) */
|
|
|
|
for (row = 1; row < WINDOW_MAX_ROWS; row++) {
|
|
|
|
for (col = 0; col < WINDOW_MAX_COLS; col++) {
|
|
|
|
window->text[row - 1][col] = window->text[row][col];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear the bottom row: */
|
|
|
|
row = WINDOW_MAX_ROWS - 1;
|
|
|
|
for (col = 0; col < WINDOW_MAX_COLS; col++) {
|
|
|
|
window->text[row][col].c = ' ';
|
|
|
|
window->text[row][col].justify_mode = window->justify_mode;
|
|
|
|
window->text[row][col].pen_attributes = window->pen_attributes;
|
|
|
|
window->text[row][col].pen_color = window->pen_color;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_cea708dec_init_window (Cea708Dec * decoder, guint window_id)
|
|
|
|
{
|
|
|
|
cea708Window *window = decoder->cc_windows[window_id];
|
|
|
|
|
|
|
|
if (window_id >= MAX_708_WINDOWS) {
|
|
|
|
GST_ERROR ("window_id outside of range %d", window_id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
window->priority = 0;
|
|
|
|
window->anchor_point = 0;
|
|
|
|
window->relative_position = 0;
|
|
|
|
window->anchor_vertical = 0;
|
|
|
|
window->anchor_horizontal = 0;
|
|
|
|
window->screen_vertical = 0;
|
|
|
|
window->screen_horizontal = 0;
|
|
|
|
|
|
|
|
window->row_count = WINDOW_MAX_ROWS;
|
|
|
|
window->column_count = WINDOW_MAX_COLS;
|
|
|
|
window->row_lock = 0;
|
|
|
|
window->column_lock = 0;
|
|
|
|
window->visible = FALSE;
|
|
|
|
window->style_id = 0;
|
|
|
|
window->pen_style_id = 0;
|
|
|
|
window->deleted = TRUE;
|
|
|
|
window->pen_color.fg_color = CEA708_COLOR_WHITE;
|
|
|
|
window->pen_color.fg_opacity = SOLID;
|
|
|
|
window->pen_color.bg_color = CEA708_COLOR_BLACK;
|
|
|
|
window->pen_color.bg_opacity = SOLID;
|
|
|
|
window->pen_color.edge_color = CEA708_COLOR_BLACK;
|
|
|
|
|
|
|
|
window->pen_attributes.pen_size = PEN_SIZE_STANDARD;
|
|
|
|
window->pen_attributes.font_style = FONT_STYLE_DEFAULT;
|
|
|
|
window->pen_attributes.offset = PEN_OFFSET_NORMAL;
|
|
|
|
window->pen_attributes.italics = FALSE;
|
|
|
|
window->pen_attributes.text_tag = TAG_DIALOG;
|
|
|
|
window->pen_attributes.underline = FALSE;
|
|
|
|
window->pen_attributes.edge_type = EDGE_TYPE_NONE;
|
|
|
|
|
|
|
|
/* Init pen position */
|
|
|
|
window->pen_row = 0;
|
|
|
|
window->pen_col = 0;
|
|
|
|
|
|
|
|
/* Initialize text array to all spaces. When sending window text, only
|
|
|
|
* send if there are non-blank rows */
|
|
|
|
gst_cea708dec_clear_window_text (decoder, window_id);
|
|
|
|
|
|
|
|
/* window attributes */
|
|
|
|
window->justify_mode = JUSTIFY_LEFT;
|
|
|
|
window->print_direction = PRINT_DIR_LEFT_TO_RIGHT;
|
|
|
|
window->scroll_direction = SCROLL_DIR_BOTTOM_TO_TOP;
|
|
|
|
window->word_wrap = FALSE;
|
|
|
|
window->display_effect = DISPLAY_EFFECT_SNAP;
|
|
|
|
window->effect_direction = EFFECT_DIR_LEFT_TO_RIGHT;
|
|
|
|
window->effect_speed = 0;
|
|
|
|
window->fill_color = CEA708_COLOR_BLACK;
|
|
|
|
window->fill_opacity = TRANSPARENT;
|
|
|
|
window->border_type = BORDER_TYPE_NONE;
|
|
|
|
window->border_color = CEA708_COLOR_BLACK;
|
|
|
|
|
|
|
|
window->v_offset = 0;
|
|
|
|
window->h_offset = 0;
|
|
|
|
window->layout = NULL;
|
|
|
|
window->shadow_offset = 0;
|
|
|
|
window->outline_offset = 0;
|
|
|
|
window->image_width = 0;
|
|
|
|
window->image_height = 0;
|
|
|
|
window->text_image = NULL;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_cea708dec_set_pen_attributes (Cea708Dec * decoder,
|
|
|
|
guint8 * dtvcc_buffer, int index)
|
|
|
|
{
|
|
|
|
cea708Window *window = decoder->cc_windows[decoder->current_window];
|
|
|
|
|
|
|
|
/* format
|
|
|
|
tt3 tt2 tt1 tt0 o1 o0 s1 s0
|
|
|
|
i u et2 et1 et0 fs2 fs1 fs0 */
|
|
|
|
window->pen_attributes.pen_size = dtvcc_buffer[index] & 0x3;
|
|
|
|
window->pen_attributes.font_style = dtvcc_buffer[index + 1] & 0x3;
|
|
|
|
window->pen_attributes.text_tag = (dtvcc_buffer[index] & 0xF0) >> 4;
|
|
|
|
window->pen_attributes.offset = (dtvcc_buffer[index] & 0xC0) >> 2;
|
|
|
|
window->pen_attributes.italics =
|
|
|
|
((dtvcc_buffer[index + 1] & 0x80) >> 7) ? TRUE : FALSE;
|
|
|
|
window->pen_attributes.underline =
|
|
|
|
((dtvcc_buffer[index + 1] & 0x40) >> 6) ? TRUE : FALSE;
|
|
|
|
window->pen_attributes.edge_type = (dtvcc_buffer[index + 1] & 0x38) >> 3;
|
|
|
|
|
|
|
|
GST_LOG ("pen_size=%d font=%d text_tag=%d offset=%d",
|
|
|
|
window->pen_attributes.pen_size,
|
|
|
|
window->pen_attributes.font_style,
|
|
|
|
window->pen_attributes.text_tag, window->pen_attributes.offset);
|
|
|
|
|
|
|
|
GST_LOG ("italics=%d underline=%d edge_type=%d",
|
|
|
|
window->pen_attributes.italics,
|
|
|
|
window->pen_attributes.underline, window->pen_attributes.edge_type);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_cea708dec_for_each_window (Cea708Dec * decoder,
|
|
|
|
guint8 window_list, VisibilityControl visibility_control,
|
|
|
|
const gchar * log_message, void (*function) (Cea708Dec * decoder,
|
|
|
|
guint window_id))
|
|
|
|
{
|
|
|
|
guint i;
|
|
|
|
|
|
|
|
GST_LOG ("window_list: %02x", window_list);
|
|
|
|
|
|
|
|
for (i = 0; i < MAX_708_WINDOWS; i++) {
|
|
|
|
if (WINDOW_IN_LIST_IS_ACTIVE (window_list)) {
|
|
|
|
GST_LOG ("%s[%d]:%d %s v_offset=%d h_offset=%d",
|
|
|
|
log_message, i, WINDOW_IN_LIST_IS_ACTIVE (window_list),
|
|
|
|
(decoder->cc_windows[i]->visible) ? "visible" : "hidden",
|
|
|
|
decoder->cc_windows[i]->v_offset, decoder->cc_windows[i]->h_offset);
|
|
|
|
switch (visibility_control) {
|
|
|
|
default:
|
|
|
|
case NO_CHANGE:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SWITCH_TO_HIDE:
|
|
|
|
decoder->cc_windows[i]->visible = FALSE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SWITCH_TO_SHOW:
|
|
|
|
decoder->cc_windows[i]->visible = TRUE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TOGGLE:
|
|
|
|
decoder->cc_windows[i]->visible = !decoder->cc_windows[i]->visible;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NULL != function) {
|
|
|
|
function (decoder, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
window_list >>= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_cea708dec_process_command (Cea708Dec * decoder,
|
|
|
|
guint8 * dtvcc_buffer, int index)
|
|
|
|
{
|
|
|
|
cea708Window *window = decoder->cc_windows[decoder->current_window];
|
|
|
|
guint8 c = dtvcc_buffer[index];
|
|
|
|
guint8 window_list = dtvcc_buffer[index + 1]; /* always the first arg (if any) */
|
|
|
|
|
|
|
|
/* Process command codes */
|
|
|
|
gst_cea708dec_print_command_name (decoder, c);
|
|
|
|
switch (c) {
|
|
|
|
case CC_COMMAND_ETX: /* End of text */
|
|
|
|
window->visible = TRUE;
|
|
|
|
gst_cea708dec_show_pango_window (decoder, decoder->current_window);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case CC_COMMAND_CW0: /* Set current window */
|
|
|
|
case CC_COMMAND_CW1:
|
|
|
|
case CC_COMMAND_CW2:
|
|
|
|
case CC_COMMAND_CW3:
|
|
|
|
case CC_COMMAND_CW4:
|
|
|
|
case CC_COMMAND_CW5:
|
|
|
|
case CC_COMMAND_CW6:
|
|
|
|
case CC_COMMAND_CW7:
|
|
|
|
decoder->current_window = c & 0x03;
|
|
|
|
GST_LOG ("Current window=%d", decoder->current_window);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case CC_COMMAND_CLW: /* Clear windows */
|
|
|
|
decoder->output_ignore = 1; /* 1 byte parameter = windowmap */
|
|
|
|
|
|
|
|
/* Clear window data */
|
|
|
|
gst_cea708dec_for_each_window (decoder, window_list, NO_CHANGE,
|
|
|
|
"clear_window", gst_cea708dec_clear_window_text);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case CC_COMMAND_DSW: /* Display windows */
|
|
|
|
decoder->output_ignore = 1; /* 1 byte parameter = windowmap */
|
|
|
|
|
|
|
|
/* Show window */
|
|
|
|
gst_cea708dec_for_each_window (decoder, window_list, NO_CHANGE,
|
|
|
|
"display_window", gst_cea708dec_show_pango_window);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case CC_COMMAND_HDW: /* Hide windows */
|
|
|
|
decoder->output_ignore = 1; /* 1 byte parameter = windowmap */
|
|
|
|
|
|
|
|
/* Hide window */
|
|
|
|
gst_cea708dec_for_each_window (decoder, window_list, SWITCH_TO_HIDE,
|
|
|
|
"hide_window", NULL);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case CC_COMMAND_TGW: /* Toggle windows */
|
|
|
|
decoder->output_ignore = 1; /* 1 byte parameter = windowmap */
|
|
|
|
|
|
|
|
/* Toggle windows - hide displayed windows, display hidden windows */
|
|
|
|
gst_cea708dec_for_each_window (decoder, window_list, TOGGLE,
|
|
|
|
"toggle_window", gst_cea708dec_show_pango_window);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case CC_COMMAND_DLW: /* Delete windows */
|
|
|
|
decoder->output_ignore = 1; /* 1 byte parameter = windowmap */
|
|
|
|
|
|
|
|
/* Delete window */
|
|
|
|
gst_cea708dec_for_each_window (decoder, window_list, NO_CHANGE,
|
|
|
|
"delete_window", gst_cea708dec_init_window);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case CC_COMMAND_DLY: /* Delay */
|
|
|
|
decoder->output_ignore = 1; /* 1 byte parameter = delay in 1/10 sec */
|
|
|
|
/* TODO: - process this command. */
|
|
|
|
return;
|
|
|
|
|
|
|
|
case CC_COMMAND_DLC: /* Delay cancel */
|
|
|
|
/* TODO: - process this command. */
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Reset */
|
|
|
|
case CC_COMMAND_RST:
|
|
|
|
/* Reset - cancel any delay, delete all windows */
|
|
|
|
window_list = 0xFF; /* all windows... */
|
|
|
|
|
|
|
|
/* Delete window */
|
|
|
|
gst_cea708dec_for_each_window (decoder, window_list, NO_CHANGE,
|
|
|
|
"reset_window", gst_cea708dec_init_window);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case CC_COMMAND_SPA: /* Set pen attributes */
|
|
|
|
decoder->output_ignore = 2; /* 2 byte parameter = pen attributes */
|
|
|
|
gst_cea708dec_set_pen_attributes (decoder, dtvcc_buffer, index + 1);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case CC_COMMAND_SPC: /* Set pen color */
|
|
|
|
decoder->output_ignore = 3; /* 3 byte parameter = color & opacity */
|
|
|
|
gst_cea708dec_set_pen_color (decoder, dtvcc_buffer, index + 1);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case CC_COMMAND_SPL: /* Set pen location */
|
|
|
|
/* Set pen location - row, column address within the current window */
|
|
|
|
decoder->output_ignore = 2; /* 2 byte parameter = row, col */
|
|
|
|
window->pen_row = dtvcc_buffer[index + 1] & 0xF;
|
|
|
|
window->pen_col = dtvcc_buffer[index + 2] & 0x3F;
|
|
|
|
GST_LOG ("Pen location: row=%d col=%d", window->pen_row, window->pen_col);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case CC_COMMAND_SWA: /* Set window attributes */
|
|
|
|
/* Set window attributes - color, word wrap, border, scroll effect, etc */
|
|
|
|
decoder->output_ignore = 4; /* 4 byte parameter = window attributes */
|
|
|
|
gst_cea708dec_set_window_attributes (decoder, dtvcc_buffer, index + 1);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case CC_COMMAND_DF0: /* Define window */
|
|
|
|
case CC_COMMAND_DF1:
|
|
|
|
case CC_COMMAND_DF2:
|
|
|
|
case CC_COMMAND_DF3:
|
|
|
|
case CC_COMMAND_DF4:
|
|
|
|
case CC_COMMAND_DF5:
|
|
|
|
case CC_COMMAND_DF6:
|
|
|
|
case CC_COMMAND_DF7:
|
|
|
|
{
|
|
|
|
window_list = 0xFF; /* all windows... */
|
|
|
|
|
|
|
|
/* set window - size, style, pen style, anchor position, etc. */
|
|
|
|
decoder->output_ignore = 6; /* 6 byte parameter = window definition */
|
|
|
|
decoder->current_window = c & 0x7;
|
|
|
|
gst_cea708dec_define_window (decoder, dtvcc_buffer, index + 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
get_cea708dec_bufcat (gpointer data, gpointer whole_buf)
|
|
|
|
{
|
|
|
|
gchar *buf = whole_buf;
|
|
|
|
strcat ((gchar *) buf, data);
|
|
|
|
g_free (data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
gst_cea708dec_render_text (Cea708Dec * decoder, GSList ** text_list,
|
|
|
|
gint length, guint window_id)
|
|
|
|
{
|
|
|
|
gchar *out_str = NULL;
|
|
|
|
PangoAlignment align_mode;
|
|
|
|
PangoFontDescription *desc;
|
|
|
|
gchar *font_desc;
|
|
|
|
cea708Window *window = decoder->cc_windows[window_id];
|
|
|
|
|
|
|
|
if (length > 0) {
|
|
|
|
out_str = g_malloc0 (length + 1);
|
|
|
|
memset (out_str, 0, length + 1);
|
|
|
|
|
|
|
|
g_slist_foreach (*text_list, get_cea708dec_bufcat, out_str);
|
|
|
|
GST_LOG ("rendering '%s'", out_str);
|
|
|
|
g_slist_free (*text_list);
|
|
|
|
window->layout = pango_layout_new (decoder->pango_context);
|
|
|
|
align_mode = gst_cea708dec_get_align_mode (window->justify_mode);
|
|
|
|
pango_layout_set_alignment (window->layout, (PangoAlignment) align_mode);
|
|
|
|
pango_layout_set_markup (window->layout, out_str, length);
|
|
|
|
if (!decoder->default_font_desc)
|
|
|
|
font_desc = g_strdup_printf ("%s %s", font_names[0], pen_size_names[1]);
|
|
|
|
else
|
|
|
|
font_desc = g_strdup (decoder->default_font_desc);
|
|
|
|
desc = pango_font_description_from_string (font_desc);
|
|
|
|
if (desc) {
|
|
|
|
GST_INFO ("font description set: %s", font_desc);
|
|
|
|
pango_layout_set_font_description (window->layout, desc);
|
|
|
|
gst_cea708dec_adjust_values_with_fontdesc (window, desc);
|
|
|
|
pango_font_description_free (desc);
|
|
|
|
gst_cea708dec_render_pangocairo (window);
|
|
|
|
} else {
|
|
|
|
GST_ERROR ("font description parse failed: %s", font_desc);
|
|
|
|
}
|
|
|
|
g_free (font_desc);
|
|
|
|
g_free (out_str);
|
|
|
|
/* data freed in slist loop!
|
|
|
|
*g_slist_free_full (*text_list, g_free); */
|
|
|
|
*text_list = NULL;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_cea708dec_window_add_char (Cea708Dec * decoder, gunichar c)
|
|
|
|
{
|
|
|
|
cea708Window *window = decoder->cc_windows[decoder->current_window];
|
|
|
|
guint16 pen_row;
|
|
|
|
guint16 pen_col;
|
|
|
|
|
|
|
|
/* Add one character to the current window, using current pen location.
|
|
|
|
* Wrap pen location if necessary */
|
|
|
|
if (c == 0) /* NULL */
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (c == 0x0E) { /* HCR,moves the pen location to the beginning of the current line and deletes its contents */
|
|
|
|
for (pen_col = window->pen_col; pen_col >= 0; pen_col--) {
|
|
|
|
window->text[window->pen_row][pen_col].c = ' ';
|
|
|
|
}
|
|
|
|
window->pen_col = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (c == 0x08) { /* BS */
|
|
|
|
switch (window->print_direction) {
|
|
|
|
case PRINT_DIR_LEFT_TO_RIGHT:
|
|
|
|
if (window->pen_col) {
|
|
|
|
window->pen_col--;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRINT_DIR_RIGHT_TO_LEFT:
|
|
|
|
window->pen_col++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRINT_DIR_TOP_TO_BOTTOM:
|
|
|
|
if (window->pen_row) {
|
|
|
|
window->pen_row--;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRINT_DIR_BOTTOM_TO_TOP:
|
|
|
|
window->pen_row++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
pen_row = window->pen_row;
|
|
|
|
pen_col = window->pen_col;
|
|
|
|
window->text[pen_row][pen_col].c = ' ';
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (c == 0x0C) { /* FF clears the screen and moves the pen location to (0,0) */
|
|
|
|
window->pen_row = 0;
|
|
|
|
window->pen_col = 0;
|
|
|
|
gst_cea708dec_clear_window_text (decoder, decoder->current_window);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (c == 0x0D) {
|
|
|
|
GST_DEBUG
|
|
|
|
("carriage return, window->word_wrap=%d,window->scroll_direction=%d",
|
|
|
|
window->word_wrap, window->scroll_direction);
|
|
|
|
window->pen_col = 0;
|
|
|
|
window->pen_row++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (window->pen_col >= window->column_count) {
|
|
|
|
window->pen_col = 0;
|
|
|
|
window->pen_row++;
|
|
|
|
}
|
|
|
|
/* Wrap row position if too large */
|
|
|
|
if (window->pen_row >= window->row_count) {
|
|
|
|
if (window->scroll_direction == SCROLL_DIR_BOTTOM_TO_TOP) {
|
|
|
|
gst_cea708dec_scroll_window_up (decoder, decoder->current_window);
|
|
|
|
}
|
|
|
|
window->pen_row = window->row_count - 1;
|
|
|
|
GST_WARNING ("pen row exceed window row count,scroll up");
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((c != '\r') && (c != '\n')) {
|
|
|
|
pen_row = window->pen_row;
|
|
|
|
pen_col = window->pen_col;
|
|
|
|
|
|
|
|
GST_LOG ("[text x=%d y=%d fgcolor=%d win=%d vis=%d] '%c' 0x%02X", pen_col,
|
|
|
|
pen_row, window->pen_color.fg_color, decoder->current_window,
|
|
|
|
window->visible, c, c);
|
|
|
|
|
|
|
|
/* Each cell in the window should get the current pen color and
|
|
|
|
* attributes as it is written */
|
|
|
|
window->text[pen_row][pen_col].c = c;
|
|
|
|
window->text[pen_row][pen_col].justify_mode = window->justify_mode;
|
|
|
|
window->text[pen_row][pen_col].pen_color = window->pen_color;
|
|
|
|
window->text[pen_row][pen_col].pen_attributes = window->pen_attributes;
|
|
|
|
|
|
|
|
switch (window->print_direction) {
|
|
|
|
case PRINT_DIR_LEFT_TO_RIGHT:
|
|
|
|
window->pen_col++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRINT_DIR_RIGHT_TO_LEFT:
|
|
|
|
if (window->pen_col) {
|
|
|
|
window->pen_col--;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRINT_DIR_TOP_TO_BOTTOM:
|
|
|
|
window->pen_row++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRINT_DIR_BOTTOM_TO_TOP:
|
|
|
|
if (window->pen_row) {
|
|
|
|
window->pen_row--;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
} /* switch (print_direction) */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_cea708dec_process_c2 (Cea708Dec * decoder, guint8 * dtvcc_buffer, int index)
|
|
|
|
{
|
|
|
|
guint8 c = dtvcc_buffer[index];
|
|
|
|
if (c >= 0x00 && c <= 0x07) {
|
|
|
|
decoder->output_ignore = 1;
|
|
|
|
} else if (c >= 0x08 && c <= 0x0F) {
|
|
|
|
decoder->output_ignore = 2;
|
|
|
|
} else if (c >= 0x10 && c <= 0x17) {
|
|
|
|
decoder->output_ignore = 3;
|
|
|
|
} else if (c >= 0x18 && c <= 0x1F) {
|
|
|
|
decoder->output_ignore = 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_cea708dec_process_g2 (Cea708Dec * decoder, guint8 * dtvcc_buffer, int index)
|
|
|
|
{
|
|
|
|
guint8 c = dtvcc_buffer[index];
|
|
|
|
gst_cea708dec_window_add_char (decoder, g2_table[c - 0x20]);
|
|
|
|
decoder->output_ignore = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_cea708dec_process_c3 (Cea708Dec * decoder, guint8 * dtvcc_buffer, int index)
|
|
|
|
{
|
|
|
|
guint8 c = dtvcc_buffer[index];
|
|
|
|
int command_length = 0;
|
|
|
|
if (c >= 0x80 && c <= 0x87) {
|
|
|
|
decoder->output_ignore = 5;
|
|
|
|
} else if (c >= 0x88 && c <= 0x8F) {
|
|
|
|
decoder->output_ignore = 6;
|
|
|
|
} else if (c >= 0x90 && c <= 0x9F) {
|
|
|
|
command_length = dtvcc_buffer[index + 1] & 0x3F;
|
|
|
|
decoder->output_ignore = command_length + 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gst_cea708dec_process_g3 (Cea708Dec * decoder, guint8 * dtvcc_buffer, int index)
|
|
|
|
{
|
|
|
|
gst_cea708dec_window_add_char (decoder, 0x5F);
|
|
|
|
decoder->output_ignore = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gst_cea708dec_set_video_width_height (Cea708Dec * decoder, gint width,
|
|
|
|
gint height)
|
|
|
|
{
|
|
|
|
decoder->width = width;
|
|
|
|
decoder->height = height;
|
|
|
|
}
|