From f28df30a8b8fb840c9173c8d727d152bde99f149 Mon Sep 17 00:00:00 2001 From: Chris Bass Date: Wed, 22 Mar 2017 09:21:09 +0000 Subject: [PATCH] ttmlrender: apply correct line height to blocks with multiple text sizes In TTML, the height of every line in a block is determined by lineHeight and fontSize style attributes, and should be the same for each line in that block, regardless of whether different sized text appears on different lines. Currently, a single PangoLayout is used to lay out all the text in a block; however, pango will vary the line height in a layout depending on the size of text used in each line, which is not compliant with TTML. This patch makes ttmlrender lay out the lines in a block itself, rather than using a PangoLayout to do the work. The code still uses a PangoLayout to render the text of each element, but the overall layout of the text in a block is now controlled by ttmlrender itself. By doing this, ttmlrender is able to ensure that the height of each line in a block is correct. https://bugzilla.gnome.org/show_bug.cgi?id=780402 --- ext/ttml/gstttmlrender.c | 1150 +++++++++++++++++++++++++------------- ext/ttml/gstttmlrender.h | 11 - 2 files changed, 747 insertions(+), 414 deletions(-) diff --git a/ext/ttml/gstttmlrender.c b/ext/ttml/gstttmlrender.c index 96e31fb49e..ba18a1b8e8 100644 --- a/ext/ttml/gstttmlrender.c +++ b/ext/ttml/gstttmlrender.c @@ -101,6 +101,13 @@ typedef enum } GstTtmlDirection; +typedef struct +{ + guint line_height; + guint baseline_offset; +} BlockMetrics; + + typedef struct { guint height; @@ -156,9 +163,13 @@ static GstTtmlRenderRenderedImage *gst_ttml_render_rendered_image_copy (GstTtmlRenderRenderedImage * image); static void gst_ttml_render_rendered_image_free (GstTtmlRenderRenderedImage * image); +static GstTtmlRenderRenderedImage *gst_ttml_render_rendered_image_combine + (GstTtmlRenderRenderedImage * image1, GstTtmlRenderRenderedImage * image2); static GstTtmlRenderRenderedImage *gst_ttml_render_stitch_images (GPtrArray * images, GstTtmlDirection direction); +static gboolean gst_ttml_render_color_is_transparent (GstSubtitleColor * color); + GType gst_ttml_render_get_type (void) { @@ -1272,11 +1283,35 @@ typedef struct static void gst_ttml_render_unified_element_free (UnifiedElement * unified_element) { + if (!unified_element) + return; + + gst_subtitle_element_unref (unified_element->element); g_free (unified_element->text); g_slice_free (UnifiedElement, unified_element); } +static UnifiedElement * +gst_ttml_render_unified_element_copy (const UnifiedElement * unified_element) +{ + UnifiedElement *ret; + + if (!unified_element) + return NULL; + + ret = g_slice_new0 (UnifiedElement); + ret->element = gst_subtitle_element_ref (unified_element->element); + ret->pango_font_size = unified_element->pango_font_size; + ret->pango_font_metrics.height = unified_element->pango_font_metrics.height; + ret->pango_font_metrics.baseline = + unified_element->pango_font_metrics.baseline; + ret->text = g_strdup (unified_element->text); + + return ret; +} + + typedef struct { GPtrArray *unified_elements; @@ -1309,39 +1344,35 @@ gst_ttml_render_unified_block_get_element (const UnifiedBlock * block, } -static void -gst_ttml_render_handle_whitespace (UnifiedBlock * block) +static UnifiedBlock * +gst_ttml_render_unified_block_copy (const UnifiedBlock * block) { - UnifiedElement *last = NULL; - UnifiedElement *cur = gst_ttml_render_unified_block_get_element (block, 0); - UnifiedElement *next = gst_ttml_render_unified_block_get_element (block, 1); - guint i; + UnifiedBlock *ret; + gint i; - for (i = 2; cur; ++i) { - if (cur->element->suppress_whitespace) { - if (!last || (g_strcmp0 (last->text, "\n") == 0)) { - /* Strip leading whitespace. */ - if (cur->text[0] == 0x20) { - gchar *tmp = cur->text; - GST_CAT_LOG (ttmlrender_debug, "Stripping leading whitespace."); - cur->text = g_strdup (cur->text + 1); - g_free (tmp); - } - } - if (!next || (g_strcmp0 (next->text, "\n") == 0)) { - /* Strip trailing whitespace. */ - if (cur->text[strlen (cur->text) - 1] == 0x20) { - gchar *tmp = cur->text; - GST_CAT_LOG (ttmlrender_debug, "Stripping trailing whitespace."); - cur->text = g_strndup (cur->text, strlen (cur->text) - 1); - g_free (tmp); - } - } - } - last = cur; - cur = next; - next = gst_ttml_render_unified_block_get_element (block, i); + if (!block) + return NULL; + + ret = g_slice_new0 (UnifiedBlock); + ret->joined_text = g_strdup (block->joined_text); + ret->style_set = gst_subtitle_style_set_ref (block->style_set); + ret->unified_elements = g_ptr_array_new_with_free_func ((GDestroyNotify) + gst_ttml_render_unified_element_free); + + for (i = 0; i < block->unified_elements->len; ++i) { + UnifiedElement *ue = gst_ttml_render_unified_block_get_element (block, i); + UnifiedElement *ue_copy = gst_ttml_render_unified_element_copy (ue); + g_ptr_array_add (ret->unified_elements, ue_copy); } + + return ret; +} + + +static guint +gst_ttml_render_unified_block_element_count (const UnifiedBlock * block) +{ + return block->unified_elements->len; } @@ -1475,132 +1506,432 @@ gst_ttml_render_unify_block (GstTtmlRender * render, } -/* From the elements within @block, generate a string of the subtitle text - * marked-up using pango-markup. Also, store the ranges of characters belonging - * to the text of each element in @char_ranges. */ -static gchar * -gst_ttml_render_generate_marked_up_string (GstTtmlRender * render, - const GstSubtitleBlock * block, GstBuffer * text_buf, - GPtrArray * char_ranges) +/* + * Returns index of nearest breakpoint before @index in @block's text. If no + * breakpoints are found, returns -1. + */ +static gint +gst_ttml_render_get_nearest_breakpoint (const UnifiedBlock * block, guint index) { - gchar *escaped_text, *joined_text, *old_text, *font_family, *font_size, - *fgcolor; - const gchar *font_style, *font_weight, *underline; - guint total_text_length = 0U; - guint element_count = gst_subtitle_block_get_element_count (block); - UnifiedBlock *unified_block; + const gchar *end = block->joined_text + index - 1; + + while ((end = g_utf8_find_prev_char (block->joined_text, end))) { + gchar buf[6] = { 0 }; + gunichar u = g_utf8_get_char (end); + gint nbytes = g_unichar_to_utf8 (u, buf); + + if (nbytes == 1 && (buf[0] == 0x20 || buf[0] == 0x9 || buf[0] == 0xD)) + return end - block->joined_text; + } + + return -1; +} + + +/* Return the pango markup representation of all the elements in @block. */ +static gchar * +gst_ttml_render_generate_block_markup (const UnifiedBlock * block) +{ + gchar *joined_text, *old_text; + guint element_count = gst_ttml_render_unified_block_element_count (block); guint i; joined_text = g_strdup (""); - unified_block = gst_ttml_render_unify_block (render, block, text_buf); - gst_ttml_render_handle_whitespace (unified_block); for (i = 0; i < element_count; ++i) { - CharRange *range = g_slice_new0 (CharRange); - UnifiedElement *unified_element = - gst_ttml_render_unified_block_get_element (unified_block, i); - - escaped_text = g_markup_escape_text (unified_element->text, -1); - GST_CAT_DEBUG (ttmlrender_debug, "Escaped text is: \"%s\"", escaped_text); - range->first_index = total_text_length; - - fgcolor = - gst_ttml_render_color_to_string (unified_element->element-> - style_set->color); - font_size = - g_strdup_printf ("%u", - (guint) (round (unified_element->element->style_set->font_size * - render->height))); - font_family = - gst_ttml_render_resolve_generic_fontname (unified_element-> - element->style_set->font_family); - if (!font_family) - font_family = g_strdup (unified_element->element->style_set->font_family); - font_style = - (unified_element->element->style_set->font_style == - GST_SUBTITLE_FONT_STYLE_NORMAL) ? "normal" : "italic"; - font_weight = - (unified_element->element->style_set->font_weight == - GST_SUBTITLE_FONT_WEIGHT_NORMAL) ? "normal" : "bold"; - underline = - (unified_element->element->style_set->text_decoration == - GST_SUBTITLE_TEXT_DECORATION_UNDERLINE) ? "single" : "none"; + UnifiedElement *ue = gst_ttml_render_unified_block_get_element (block, i); + gchar *element_markup = + gst_ttml_render_generate_pango_markup (ue->element->style_set, + ue->pango_font_size, ue->text); old_text = joined_text; - joined_text = g_strconcat (joined_text, - "", escaped_text, "", NULL); + joined_text = g_strconcat (joined_text, element_markup, NULL); GST_CAT_DEBUG (ttmlrender_debug, "Joined text is now: %s", joined_text); - total_text_length += strlen (unified_element->text); - range->last_index = total_text_length - 1; - GST_CAT_DEBUG (ttmlrender_debug, - "First character index: %u; last character " "index: %u", - range->first_index, range->last_index); - g_ptr_array_insert (char_ranges, i, range); - + g_free (element_markup); g_free (old_text); - g_free (escaped_text); - g_free (fgcolor); - g_free (font_family); - g_free (font_size); } - gst_ttml_render_unified_block_free (unified_block); return joined_text; } -/* Render the text in a pango-markup string. */ -static GstTtmlRenderRenderedText * -gst_ttml_render_draw_text (GstTtmlRender * render, const gchar * text, - guint max_width, PangoAlignment alignment, guint line_height, - guint max_font_size, gboolean wrap) +/* + * Returns a set of character ranges, which correspond to the ranges of + * characters from @block that should be rendered on each generated line area. + * Essentially, this function determines line breaking and wrapping. + */ +static GPtrArray * +gst_ttml_render_get_line_char_ranges (GstTtmlRender * render, + const UnifiedBlock * block, guint width, gboolean wrap) { - GstTtmlRenderRenderedText *ret; + gint start_index = 0; + GPtrArray *line_ranges = g_ptr_array_new_with_free_func ((GDestroyNotify) + gst_ttml_render_char_range_free); + PangoRectangle ink_rect; + gchar *markup; + gint i; + + /* Handle hard breaks in block text. */ + while (start_index < strlen (block->joined_text)) { + CharRange *range = g_slice_new0 (CharRange); + gchar *c = block->joined_text + start_index; + while (*c != '\0' && *c != '\n') + ++c; + range->first_index = start_index; + range->last_index = (c - block->joined_text) - 1; + g_ptr_array_add (line_ranges, range); + start_index = range->last_index + 2; + } + + if (!wrap) + return line_ranges; + + GST_CAT_LOG (ttmlrender_debug, + "After handling breaks, we have the following ranges:"); + for (i = 0; i < line_ranges->len; ++i) { + CharRange *range = g_ptr_array_index (line_ranges, i); + GST_CAT_LOG (ttmlrender_debug, "ranges[%d] first:%u last:%u", i, + range->first_index, range->last_index); + } + + markup = gst_ttml_render_generate_block_markup (block); + pango_layout_set_markup (render->layout, markup, strlen (markup)); + pango_layout_set_width (render->layout, -1); + + pango_layout_get_pixel_extents (render->layout, &ink_rect, NULL); + GST_CAT_LOG (ttmlrender_debug, "Layout extents - x:%d y:%d w:%d h:%d", + ink_rect.x, ink_rect.y, ink_rect.width, ink_rect.height); + + /* For each range, wrap if it extends beyond allowed width. */ + for (i = 0; i < line_ranges->len; ++i) { + CharRange *range, *new_range; + gint max_line_extent; + gint end_index = 0; + gint trailing; + PangoRectangle rect; + gboolean within_line; + + do { + range = g_ptr_array_index (line_ranges, i); + GST_CAT_LOG (ttmlrender_debug, + "Seeing if we need to wrap range[%d] - start:%u end:%u", i, + range->first_index, range->last_index); + + pango_layout_index_to_pos (render->layout, range->first_index, &rect); + GST_CAT_LOG (ttmlrender_debug, "First char at x:%d y:%d", rect.x, + rect.y); + + max_line_extent = rect.x + (PANGO_SCALE * width); + GST_CAT_LOG (ttmlrender_debug, "max_line_extent: %d", + PANGO_PIXELS (max_line_extent)); + + within_line = + pango_layout_xy_to_index (render->layout, max_line_extent, rect.y, + &end_index, &trailing); + + GST_CAT_LOG (ttmlrender_debug, "Index nearest to breakpoint: %d", + end_index); + + if (within_line) { + end_index = gst_ttml_render_get_nearest_breakpoint (block, end_index); + + if (end_index > range->first_index) { + new_range = g_slice_new0 (CharRange); + new_range->first_index = end_index + 1; + new_range->last_index = range->last_index; + GST_CAT_LOG (ttmlrender_debug, + "Wrapping line %d; added new range - start:%u end:%u", i, + new_range->first_index, new_range->last_index); + + range->last_index = end_index; + GST_CAT_LOG (ttmlrender_debug, + "Modified last_index of existing range; range is now start:%u " + "end:%u", range->first_index, range->last_index); + + g_ptr_array_insert (line_ranges, ++i, new_range); + } else { + GST_CAT_DEBUG (ttmlrender_debug, + "Couldn't find a suitable breakpoint"); + within_line = FALSE; + } + } + } while (within_line); + } + + g_free (markup); + return line_ranges; +} + + +/* + * Returns the index of the element in @block containing the character at index + * @char_index in @block's text. If @offset is not NULL, sets it to the + * character offset of @char_index within the element where it is found. + */ +static gint +gst_ttml_render_get_element_index (const UnifiedBlock * block, + const gint char_index, gint * offset) +{ + gint count = 0; + gint i; + + if ((char_index < 0) || (char_index >= strlen (block->joined_text))) + return -1; + + for (i = 0; i < gst_ttml_render_unified_block_element_count (block); ++i) { + UnifiedElement *ue = gst_ttml_render_unified_block_get_element (block, i); + if ((char_index >= count) && (char_index < (count + strlen (ue->text)))) { + if (offset) + *offset = char_index - count; + break; + } + count += strlen (ue->text); + } + + return i; +} + + +static guint +gst_ttml_render_strip_leading_spaces (gchar ** string) +{ + gchar *c = *string; + + while (c) { + gchar buf[6] = { 0 }; + gunichar u = g_utf8_get_char (c); + gint nbytes = g_unichar_to_utf8 (u, buf); + + if ((nbytes == 1) && (buf[0] == 0x20)) + c = g_utf8_find_next_char (c, c + strlen (*string)); + else + break; + } + + if (!c) { + GST_CAT_DEBUG (ttmlrender_debug, + "All characters would be removed from string."); + return 0; + } else if (c > *string) { + gchar *tmp = *string; + *string = g_strdup (c); + GST_CAT_DEBUG (ttmlrender_debug, "Replacing text \"%s\" with \"%s\"", tmp, + *string); + g_free (tmp); + } + + return strlen (*string); +} + + +static guint +gst_ttml_render_strip_trailing_spaces (gchar ** string) +{ + gchar *c = *string + strlen (*string) - 1; + gint nbytes; + + while (c) { + gchar buf[6] = { 0 }; + gunichar u = g_utf8_get_char (c); + nbytes = g_unichar_to_utf8 (u, buf); + + if ((nbytes == 1) && (buf[0] == 0x20)) + c = g_utf8_find_prev_char (*string, c); + else + break; + } + + if (!c) { + GST_CAT_DEBUG (ttmlrender_debug, + "All characters would be removed from string."); + return 0; + } else { + gchar *tmp = *string; + *string = g_strndup (*string, (c - *string) + nbytes); + GST_CAT_DEBUG (ttmlrender_debug, "Replacing text \"%s\" with \"%s\"", tmp, + *string); + g_free (tmp); + } + + return strlen (*string); +} + + +/* + * Treating each block in @blocks as a separate line area, conditionally strips + * space characters from the beginning and end of each line. This function + * implements the suppress-at-line-break="auto" and + * white-space-treatment="ignore-if-surrounding-linefeed" behaviours (specified + * by TTML section 7.2.3) for elements at the start and end of lines that have + * xml:space="default" applied to them. If stripping whitespace from a block + * removes all elements of that block, the block will be removed from @blocks. + * Returns the number of remaining blocks. + */ +static guint +gst_ttml_render_handle_whitespace (GPtrArray * blocks) +{ + gint i; + + for (i = 0; i < blocks->len; ++i) { + UnifiedBlock *ub = g_ptr_array_index (blocks, i); + UnifiedElement *ue; + guint remaining_chars = 0; + + /* Remove leading spaces from line area. */ + while ((gst_ttml_render_unified_block_element_count (ub) > 0) + && (remaining_chars == 0)) { + ue = gst_ttml_render_unified_block_get_element (ub, 0); + if (!ue->element->suppress_whitespace) + break; + remaining_chars = gst_ttml_render_strip_leading_spaces (&ue->text); + + if (remaining_chars == 0) { + g_ptr_array_remove_index (ub->unified_elements, 0); + GST_CAT_DEBUG (ttmlrender_debug, "Removed first element from block"); + } + } + + remaining_chars = 0; + + /* Remove trailing spaces from line area. */ + while ((gst_ttml_render_unified_block_element_count (ub) > 0) + && (remaining_chars == 0)) { + ue = gst_ttml_render_unified_block_get_element (ub, + gst_ttml_render_unified_block_element_count (ub) - 1); + if (!ue->element->suppress_whitespace) + break; + remaining_chars = gst_ttml_render_strip_trailing_spaces (&ue->text); + + if (remaining_chars == 0) { + g_ptr_array_remove_index (ub->unified_elements, + gst_ttml_render_unified_block_element_count (ub) - 1); + GST_CAT_DEBUG (ttmlrender_debug, "Removed last element from block"); + } + } + + if (gst_ttml_render_unified_block_element_count (ub) == 0) + g_ptr_array_remove_index (blocks, i--); + } + + return blocks->len; +} + + +/* + * Splits a single UnifiedBlock, @block, into an array of separate + * UnifiedBlocks, according to the character ranges given in @char_ranges. + * Each resulting UnifiedBlock will contain only the elements to which belong + * the characters in its corresponding character range; the text of the first + * and last element in the block will be clipped of any characters before and + * after, respectively, the first and last characters in the corresponding + * range. + */ +static GPtrArray * +gst_ttml_render_split_block (UnifiedBlock * block, GPtrArray * char_ranges) +{ + GPtrArray *ret = g_ptr_array_new_with_free_func ((GDestroyNotify) + gst_ttml_render_unified_block_free); + gint i; + + for (i = 0; i < char_ranges->len; ++i) { + gint index, first_offset, last_offset; + CharRange *range = g_ptr_array_index (char_ranges, i); + UnifiedBlock *clone = gst_ttml_render_unified_block_copy (block); + UnifiedElement *ue; + gchar *tmp; + + GST_CAT_LOG (ttmlrender_debug, "range start:%u end:%u", range->first_index, + range->last_index); + index = + gst_ttml_render_get_element_index (clone, range->last_index, + &last_offset); + GST_CAT_LOG (ttmlrender_debug, "Last char in range is in element %d", + index); + + /* Remove elements that are after the one that contains the range end. */ + GST_CAT_LOG (ttmlrender_debug, "There are %d elements in cloned block.", + gst_ttml_render_unified_block_element_count (clone)); + while (gst_ttml_render_unified_block_element_count (clone) > (index + 1)) { + GST_CAT_LOG (ttmlrender_debug, "Removing last element in cloned block."); + g_ptr_array_remove_index (clone->unified_elements, index + 1); + } + + index = + gst_ttml_render_get_element_index (clone, range->first_index, + &first_offset); + GST_CAT_LOG (ttmlrender_debug, "First char in range is in element %d", + index); + + /* Remove elements that are before the one that contains the range start. */ + while (index > 0) { + GST_CAT_LOG (ttmlrender_debug, "Removing first element in cloned block"); + g_ptr_array_remove_index (clone->unified_elements, 0); + --index; + } + + /* Remove characters from first element that are before the range start. */ + ue = gst_ttml_render_unified_block_get_element (clone, 0); + if (first_offset > 0) { + tmp = ue->text; + ue->text = g_strdup (ue->text + first_offset); + GST_CAT_DEBUG (ttmlrender_debug, + "First element text has been clipped to \"%s\"", ue->text); + g_free (tmp); + + if (gst_ttml_render_unified_block_element_count (clone) == 1) + last_offset -= first_offset; + } + + /* Remove characters from last element that are after the range end. */ + ue = gst_ttml_render_unified_block_get_element (clone, + gst_ttml_render_unified_block_element_count (clone) - 1); + if (last_offset < (strlen (ue->text) - 1)) { + tmp = ue->text; + ue->text = g_strndup (ue->text, last_offset + 1); + GST_CAT_DEBUG (ttmlrender_debug, + "Last element text has been clipped to \"%s\"", ue->text); + g_free (tmp); + } + + if (gst_ttml_render_unified_block_element_count (clone) > 0) + g_ptr_array_add (ret, clone); + } + + if (ret->len == 0) { + GST_CAT_DEBUG (ttmlrender_debug, "No elements remain in clone."); + g_ptr_array_unref (ret); + ret = NULL; + } + return ret; +} + + +/* Render the text in a pango-markup string. */ +static GstTtmlRenderRenderedImage * +gst_ttml_render_draw_text (GstTtmlRender * render, const gchar * text, + guint line_height, guint baseline_offset) +{ + GstTtmlRenderRenderedImage *ret; cairo_surface_t *surface, *cropped_surface; cairo_t *cairo_state, *cropped_state; GstMapInfo map; PangoRectangle logical_rect, ink_rect; - gint spacing = 0; guint buf_width, buf_height; gint stride; - PangoLayoutLine *line; - PangoRectangle line_extents; gint bounding_box_x1, bounding_box_x2, bounding_box_y1, bounding_box_y2; + gint baseline; - ret = g_slice_new0 (GstTtmlRenderRenderedText); - ret->text_image = gst_ttml_render_rendered_image_new_empty (); + ret = gst_ttml_render_rendered_image_new_empty (); pango_layout_set_markup (render->layout, text, strlen (text)); - GST_CAT_DEBUG (ttmlrender_debug, "Layout text: %s", + GST_CAT_DEBUG (ttmlrender_debug, "Layout text: \"%s\"", pango_layout_get_text (render->layout)); - if (wrap) { - pango_layout_set_width (render->layout, max_width * PANGO_SCALE); - pango_layout_set_wrap (render->layout, PANGO_WRAP_WORD_CHAR); - } else { - pango_layout_set_width (render->layout, -1); - } - - pango_layout_set_alignment (render->layout, alignment); - line = pango_layout_get_line_readonly (render->layout, 0); - pango_layout_line_get_pixel_extents (line, NULL, &line_extents); - - GST_CAT_LOG (ttmlrender_debug, "Requested line_height: %u", line_height); - spacing = line_height - line_extents.height; - pango_layout_set_spacing (render->layout, PANGO_SCALE * spacing); - GST_CAT_LOG (ttmlrender_debug, "Line spacing set to %d", - pango_layout_get_spacing (render->layout) / PANGO_SCALE); + pango_layout_set_width (render->layout, -1); pango_layout_get_pixel_extents (render->layout, &ink_rect, &logical_rect); - GST_CAT_DEBUG (ttmlrender_debug, "logical_rect.x: %d logical_rect.y: %d " - "logical_rect.width: %d logical_rect.height: %d", logical_rect.x, - logical_rect.y, logical_rect.width, logical_rect.height); + + baseline = PANGO_PIXELS (pango_layout_get_baseline (render->layout)); bounding_box_x1 = MIN (logical_rect.x, ink_rect.x); bounding_box_x2 = MAX (logical_rect.x + logical_rect.width, @@ -1609,40 +1940,33 @@ gst_ttml_render_draw_text (GstTtmlRender * render, const gchar * text, bounding_box_y2 = MAX (logical_rect.y + logical_rect.height, ink_rect.y + ink_rect.height); - surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, bounding_box_x2, - bounding_box_y2); + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + (bounding_box_x2 - bounding_box_x1), (bounding_box_y2 - bounding_box_y1)); cairo_state = cairo_create (surface); cairo_set_operator (cairo_state, CAIRO_OPERATOR_CLEAR); cairo_paint (cairo_state); cairo_set_operator (cairo_state, CAIRO_OPERATOR_OVER); - /* Render layout. */ cairo_save (cairo_state); pango_cairo_show_layout (cairo_state, render->layout); cairo_restore (cairo_state); buf_width = bounding_box_x2 - bounding_box_x1; - buf_height = (bounding_box_y2 - bounding_box_y1) + spacing; + buf_height = ink_rect.height; GST_CAT_DEBUG (ttmlrender_debug, "Output buffer width: %u height: %u", buf_width, buf_height); - /* Depending on whether the text is wrapped and its alignment, the image - * created by rendering a PangoLayout will contain more than just the - * rendered text: it may also contain blankspace around the rendered text. - * The following code crops blankspace from around the rendered text, - * returning only the rendered text itself in a GstBuffer. */ - ret->text_image->image = - gst_buffer_new_allocate (NULL, 4 * buf_width * buf_height, NULL); - gst_buffer_memset (ret->text_image->image, 0, 0U, 4 * buf_width * buf_height); - gst_buffer_map (ret->text_image->image, &map, GST_MAP_READWRITE); + ret->image = gst_buffer_new_allocate (NULL, 4 * buf_width * buf_height, NULL); + gst_buffer_memset (ret->image, 0, 0U, 4 * buf_width * buf_height); + gst_buffer_map (ret->image, &map, GST_MAP_READWRITE); stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, buf_width); cropped_surface = cairo_image_surface_create_for_data (map.data, CAIRO_FORMAT_ARGB32, - buf_width, buf_height, stride); + (bounding_box_x2 - bounding_box_x1), ink_rect.height, stride); cropped_state = cairo_create (cropped_surface); cairo_set_source_surface (cropped_state, surface, -bounding_box_x1, - -(bounding_box_y1 - spacing / 2.0)); + -ink_rect.y); cairo_rectangle (cropped_state, 0, 0, buf_width, buf_height); cairo_fill (cropped_state); @@ -1650,12 +1974,145 @@ gst_ttml_render_draw_text (GstTtmlRender * render, const gchar * text, cairo_surface_destroy (surface); cairo_destroy (cropped_state); cairo_surface_destroy (cropped_surface); - gst_buffer_unmap (ret->text_image->image, &map); + gst_buffer_unmap (ret->image, &map); - ret->text_image->width = buf_width; - ret->text_image->height = buf_height; - ret->horiz_offset = bounding_box_x1; + ret->width = buf_width; + ret->height = buf_height; + ret->x = 0; + ret->y = MAX (0, baseline_offset - (baseline - ink_rect.y)); + return ret; +} + +static GstTtmlRenderRenderedImage * +gst_ttml_render_render_block_elements (GstTtmlRender * render, + UnifiedBlock * block, BlockMetrics block_metrics) +{ + GPtrArray *inline_images = g_ptr_array_new_with_free_func ( + (GDestroyNotify) gst_ttml_render_rendered_image_free); + GstTtmlRenderRenderedImage *ret = NULL; + guint line_padding = + (guint) ceil (block->style_set->line_padding * render->width); + gint i; + + for (i = 0; i < gst_ttml_render_unified_block_element_count (block); ++i) { + UnifiedElement *ue = gst_ttml_render_unified_block_get_element (block, i); + gchar *markup; + GstTtmlRenderRenderedImage *text_image, *bg_image, *combined_image; + guint bg_offset, bg_width, bg_height; + GstBuffer *background; + + markup = gst_ttml_render_generate_pango_markup (ue->element->style_set, + ue->pango_font_size, ue->text); + text_image = gst_ttml_render_draw_text (render, markup, + block_metrics.line_height, block_metrics.baseline_offset); + g_free (markup); + + bg_offset = 0; + bg_height = block_metrics.line_height; + bg_width = text_image->width; + + if (line_padding > 0) { + if (i == 0) { + text_image->x += line_padding; + bg_width += line_padding; + } + if (i == (gst_ttml_render_unified_block_element_count (block) - 1)) + bg_width += line_padding; + } + + background = gst_ttml_render_draw_rectangle (bg_width, bg_height, + ue->element->style_set->background_color); + bg_image = gst_ttml_render_rendered_image_new (background, 0, + bg_offset, bg_width, bg_height); + combined_image = gst_ttml_render_rendered_image_combine (bg_image, + text_image); + gst_ttml_render_rendered_image_free (bg_image); + gst_ttml_render_rendered_image_free (text_image); + g_ptr_array_add (inline_images, combined_image); + } + + ret = gst_ttml_render_stitch_images (inline_images, + GST_TTML_DIRECTION_INLINE); + GST_CAT_DEBUG (ttmlrender_debug, + "Stitched line image - x:%d y:%d w:%u h:%u", + ret->x, ret->y, ret->width, ret->height); + g_ptr_array_unref (inline_images); + return ret; +} + + +/* + * Align the images in @lines according to the multi_row_align and text_align + * settings in @style_set. + */ +static void +gst_ttml_render_align_line_areas (GPtrArray * lines, + const GstSubtitleStyleSet * style_set) +{ + guint longest_line_width = 0; + gint i; + + for (i = 0; i < lines->len; ++i) { + GstTtmlRenderRenderedImage *line = g_ptr_array_index (lines, i); + if (line->width > longest_line_width) + longest_line_width = line->width; + } + + for (i = 0; i < lines->len; ++i) { + GstTtmlRenderRenderedImage *line = g_ptr_array_index (lines, i); + + switch (style_set->multi_row_align) { + case GST_SUBTITLE_MULTI_ROW_ALIGN_CENTER: + line->x += (gint) round ((longest_line_width - line->width) / 2.0); + break; + case GST_SUBTITLE_MULTI_ROW_ALIGN_END: + line->x += (longest_line_width - line->width); + break; + case GST_SUBTITLE_MULTI_ROW_ALIGN_AUTO: + switch (style_set->text_align) { + case GST_SUBTITLE_TEXT_ALIGN_CENTER: + line->x += (gint) round ((longest_line_width - line->width) / 2.0); + break; + case GST_SUBTITLE_TEXT_ALIGN_END: + case GST_SUBTITLE_TEXT_ALIGN_RIGHT: + line->x += (longest_line_width - line->width); + break; + default: + break; + } + break; + default: + break; + } + } +} + + +/* + * Renders each UnifiedBlock in @blocks, and sets the positions of the + * resulting images according to the line height in @metrics and the alignment + * settings in @style_set. + */ +static GPtrArray * +gst_ttml_render_layout_blocks (GstTtmlRender * render, GPtrArray * blocks, + BlockMetrics metrics, const GstSubtitleStyleSet * style_set) +{ + GPtrArray *ret = g_ptr_array_new_with_free_func ((GDestroyNotify) + gst_ttml_render_rendered_image_free); + gint i; + + for (i = 0; i < blocks->len; ++i) { + UnifiedBlock *block = g_ptr_array_index (blocks, i); + + GstTtmlRenderRenderedImage *line = + gst_ttml_render_render_block_elements (render, block, + metrics); + line->y += (i * metrics.line_height); + g_ptr_array_add (ret, line); + } + + gst_ttml_render_align_line_areas (ret, style_set); return ret; } @@ -1677,21 +2134,76 @@ gst_ttml_render_elements_are_wrapped (GPtrArray * elements) } -/* Return the maximum font size used in an array of elements. */ -static gdouble -gst_ttml_render_get_max_font_size (GPtrArray * elements) +/* + * Return the descender (in pixels) shared by the greatest number of glyphs in + * @block. + */ +static guint +gst_ttml_render_get_most_frequent_descender (GstTtmlRender * render, + UnifiedBlock * block) { - GstSubtitleElement *element; - guint i; - gdouble max_size = 0.0; + GHashTable *count_table = g_hash_table_new (g_direct_hash, g_direct_equal); + GHashTableIter iter; + gpointer key, value; + guint max_count = 0; + guint ret = 0; + gint i; - for (i = 0; i < elements->len; ++i) { - element = g_ptr_array_index (elements, i); - if (element->style_set->font_size > max_size) - max_size = element->style_set->font_size; + for (i = 0; i < gst_ttml_render_unified_block_element_count (block); ++i) { + UnifiedElement *ue = gst_ttml_render_unified_block_get_element (block, i); + guint descender = + ue->pango_font_metrics.height - ue->pango_font_metrics.baseline; + guint count; + + if (g_hash_table_contains (count_table, GUINT_TO_POINTER (descender))) { + count = GPOINTER_TO_UINT (g_hash_table_lookup (count_table, + GUINT_TO_POINTER (descender))); + GST_CAT_LOG (ttmlrender_debug, + "Table already contains %u glyphs with descender %u; increasing " + "that count to %ld", count, descender, + count + g_utf8_strlen (ue->text, -1)); + count += g_utf8_strlen (ue->text, -1); + } else { + count = g_utf8_strlen (ue->text, -1); + GST_CAT_LOG (ttmlrender_debug, + "No glyphs with descender %u; adding entry to table with count of %u", + descender, count); + } + + g_hash_table_insert (count_table, + GUINT_TO_POINTER (descender), GUINT_TO_POINTER (count)); } - return max_size; + g_hash_table_iter_init (&iter, count_table); + while (g_hash_table_iter_next (&iter, &key, &value)) { + guint descender = GPOINTER_TO_UINT (key); + guint count = GPOINTER_TO_UINT (value); + + if (count > max_count) { + max_count = count; + ret = descender; + } + } + + g_hash_table_unref (count_table); + return ret; +} + + +static BlockMetrics +gst_ttml_render_get_block_metrics (GstTtmlRender * render, UnifiedBlock * block) +{ + BlockMetrics ret; + guint descender = gst_ttml_render_get_most_frequent_descender (render, block); + guint font_size = (guint) ceil (block->style_set->font_size * render->height); + + ret.line_height = (guint) ceil (font_size * block->style_set->line_height); + ret.baseline_offset = (guint) ((font_size + ret.line_height) / 2.0) + - descender; + GST_CAT_DEBUG (ttmlrender_debug, + "Got most frequent descender value of %u pixels.", descender); + + return ret; } @@ -1756,6 +2268,8 @@ gst_ttml_render_rendered_image_combine (GstTtmlRenderRenderedImage * image1, cairo_surface_t *sfc1, *sfc2, *sfc_dest; cairo_t *state_dest; + if (!image1 && !image2) + return NULL; if (image1 && !image2) return gst_ttml_render_rendered_image_copy (image1); if (image2 && !image1) @@ -1899,160 +2413,6 @@ gst_ttml_render_color_is_transparent (GstSubtitleColor * color) } -/* Render the background rectangles to be placed behind each element. */ -static GstTtmlRenderRenderedImage * -gst_ttml_render_render_element_backgrounds (const GstSubtitleBlock * block, - GPtrArray * char_ranges, PangoLayout * layout, guint origin_x, - guint origin_y, guint line_height, guint line_padding, guint horiz_offset) -{ - gint first_line, last_line, cur_line; - guint padding; - PangoLayoutLine *line; - PangoRectangle first_char_pos, last_char_pos, line_extents; - CharRange *range; - const GstSubtitleElement *element; - guint rect_width; - GstBuffer *rectangle; - guint first_char_start, last_char_end; - guint i; - GstTtmlRenderRenderedImage *ret = NULL; - - for (i = 0; i < char_ranges->len; ++i) { - range = g_ptr_array_index (char_ranges, i); - element = gst_subtitle_block_get_element (block, i); - - GST_CAT_LOG (ttmlrender_debug, "First char index: %u Last char index: %u", - range->first_index, range->last_index); - pango_layout_index_to_pos (layout, range->first_index, &first_char_pos); - pango_layout_index_to_pos (layout, range->last_index, &last_char_pos); - pango_layout_index_to_line_x (layout, range->first_index, 1, - &first_line, NULL); - pango_layout_index_to_line_x (layout, range->last_index, 0, - &last_line, NULL); - - first_char_start = PANGO_PIXELS (first_char_pos.x) - horiz_offset; - last_char_end = PANGO_PIXELS (last_char_pos.x + last_char_pos.width) - - horiz_offset; - - GST_CAT_LOG (ttmlrender_debug, "First char start: %u Last char end: %u", - first_char_start, last_char_end); - GST_CAT_LOG (ttmlrender_debug, "First line: %u Last line: %u", first_line, - last_line); - - for (cur_line = first_line; cur_line <= last_line; ++cur_line) { - guint line_start, line_end; - guint area_start, area_end; - gint first_char_index; - PangoRectangle line_pos; - padding = 0; - - line = pango_layout_get_line (layout, cur_line); - pango_layout_line_get_pixel_extents (line, NULL, &line_extents); - - pango_layout_line_x_to_index (line, 0, &first_char_index, NULL); - pango_layout_index_to_pos (layout, first_char_index, &line_pos); - GST_CAT_LOG (ttmlrender_debug, "First char index:%d position_X:%d " - "position_Y:%d", first_char_index, PANGO_PIXELS (line_pos.x), - PANGO_PIXELS (line_pos.y)); - - line_start = PANGO_PIXELS (line_pos.x) - horiz_offset; - line_end = (PANGO_PIXELS (line_pos.x) + line_extents.width) - - horiz_offset; - - GST_CAT_LOG (ttmlrender_debug, "line_extents.x:%d line_extents.y:%d " - "line_extents.width:%d line_extents.height:%d", line_extents.x, - line_extents.y, line_extents.width, line_extents.height); - GST_CAT_LOG (ttmlrender_debug, "cur_line:%u line start:%u line end:%u " - "first_char_start: %u last_char_end: %u", cur_line, line_start, - line_end, first_char_start, last_char_end); - - if ((cur_line == first_line) && (first_char_start != line_start)) { - area_start = first_char_start + line_padding; - GST_CAT_LOG (ttmlrender_debug, - "First line, but there is preceding text in line."); - } else { - GST_CAT_LOG (ttmlrender_debug, - "Area contains first text on the line; adding padding..."); - ++padding; - area_start = line_start; - } - - if ((cur_line == last_line) && (last_char_end != line_end)) { - GST_CAT_LOG (ttmlrender_debug, - "Last line, but there is following text in line."); - area_end = last_char_end + line_padding; - } else { - GST_CAT_LOG (ttmlrender_debug, - "Area contains last text on the line; adding padding..."); - ++padding; - area_end = line_end + (2 * line_padding); - } - - rect_width = (area_end - area_start); - - if (rect_width > 0) { /*
s will result in zero-width rectangle */ - GstTtmlRenderRenderedImage *image, *tmp; - rectangle = gst_ttml_render_draw_rectangle (rect_width, line_height, - element->style_set->background_color); - image = gst_ttml_render_rendered_image_new (rectangle, - origin_x + area_start, - origin_y + (cur_line * line_height), rect_width, line_height); - tmp = ret; - ret = gst_ttml_render_rendered_image_combine (ret, image); - if (tmp) - gst_ttml_render_rendered_image_free (tmp); - gst_ttml_render_rendered_image_free (image); - } - } - } - - return ret; -} - - -static PangoAlignment -gst_ttml_render_get_alignment (GstSubtitleStyleSet * style_set) -{ - PangoAlignment align = PANGO_ALIGN_LEFT; - - switch (style_set->multi_row_align) { - case GST_SUBTITLE_MULTI_ROW_ALIGN_START: - align = PANGO_ALIGN_LEFT; - break; - case GST_SUBTITLE_MULTI_ROW_ALIGN_CENTER: - align = PANGO_ALIGN_CENTER; - break; - case GST_SUBTITLE_MULTI_ROW_ALIGN_END: - align = PANGO_ALIGN_RIGHT; - break; - case GST_SUBTITLE_MULTI_ROW_ALIGN_AUTO: - switch (style_set->text_align) { - case GST_SUBTITLE_TEXT_ALIGN_START: - case GST_SUBTITLE_TEXT_ALIGN_LEFT: - align = PANGO_ALIGN_LEFT; - break; - case GST_SUBTITLE_TEXT_ALIGN_CENTER: - align = PANGO_ALIGN_CENTER; - break; - case GST_SUBTITLE_TEXT_ALIGN_END: - case GST_SUBTITLE_TEXT_ALIGN_RIGHT: - align = PANGO_ALIGN_RIGHT; - break; - default: - GST_CAT_ERROR (ttmlrender_debug, "Illegal textAlign value (%d)", - style_set->text_align); - break; - } - break; - default: - GST_CAT_ERROR (ttmlrender_debug, "Illegal multiRowAlign value (%d)", - style_set->multi_row_align); - break; - } - return align; -} - - /* * Overlays a set of rendered images to return a single image. Order is * significant: later entries in @images are rendered on top of earlier @@ -2114,100 +2474,54 @@ gst_ttml_render_stitch_images (GPtrArray * images, GstTtmlDirection direction) } -static void -gst_ttml_render_rendered_text_free (GstTtmlRenderRenderedText * text) -{ - if (text->text_image) - gst_ttml_render_rendered_image_free (text->text_image); - g_slice_free (GstTtmlRenderRenderedText, text); -} - - static GstTtmlRenderRenderedImage * gst_ttml_render_render_text_block (GstTtmlRender * render, const GstSubtitleBlock * block, GstBuffer * text_buf, guint width, gboolean overflow) { - GPtrArray *char_ranges = g_ptr_array_new_with_free_func ((GDestroyNotify) - gst_ttml_render_char_range_free); - gchar *marked_up_string; - PangoAlignment alignment; - guint max_font_size; - guint line_height; + UnifiedBlock *unified_block; + BlockMetrics metrics; + gboolean wrap; guint line_padding; - gint text_offset = 0; - GstTtmlRenderRenderedText *rendered_text; - GstTtmlRenderRenderedImage *backgrounds = NULL; - GstTtmlRenderRenderedImage *ret; + GPtrArray *ranges; + GPtrArray *split_blocks; + GPtrArray *images; + GstTtmlRenderRenderedImage *rendered_block = NULL; + gint i; - /* Join text from elements to form a single marked-up string. */ - marked_up_string = gst_ttml_render_generate_marked_up_string (render, block, - text_buf, char_ranges); + unified_block = gst_ttml_render_unify_block (render, block, text_buf); + metrics = gst_ttml_render_get_block_metrics (render, unified_block); + wrap = gst_ttml_render_elements_are_wrapped (block->elements); - max_font_size = (guint) (gst_ttml_render_get_max_font_size (block->elements) - * render->height); - GST_CAT_DEBUG (ttmlrender_debug, "Max font size: %u", max_font_size); - line_height = (guint) round (block->style_set->line_height * max_font_size); + line_padding = (guint) ceil (block->style_set->line_padding * render->width); + ranges = gst_ttml_render_get_line_char_ranges (render, unified_block, width - + (2 * line_padding), wrap); - line_padding = (guint) (block->style_set->line_padding * render->width); - alignment = gst_ttml_render_get_alignment (block->style_set); - - /* Render text to buffer. */ - rendered_text = gst_ttml_render_draw_text (render, marked_up_string, - (width - (2 * line_padding)), alignment, line_height, max_font_size, - gst_ttml_render_elements_are_wrapped (block->elements)); - - switch (block->style_set->text_align) { - case GST_SUBTITLE_TEXT_ALIGN_START: - case GST_SUBTITLE_TEXT_ALIGN_LEFT: - text_offset = line_padding; - break; - case GST_SUBTITLE_TEXT_ALIGN_CENTER: - text_offset = ((gint) width - rendered_text->text_image->width); - text_offset /= 2; - break; - case GST_SUBTITLE_TEXT_ALIGN_END: - case GST_SUBTITLE_TEXT_ALIGN_RIGHT: - text_offset = (gint) width - - (rendered_text->text_image->width + line_padding); - break; + for (i = 0; i < ranges->len; ++i) { + CharRange *range = g_ptr_array_index (ranges, i); + GST_CAT_LOG (ttmlrender_debug, "ranges[%d] first:%u last:%u", i, + range->first_index, range->last_index); } - rendered_text->text_image->x = text_offset; + split_blocks = gst_ttml_render_split_block (unified_block, ranges); + if (split_blocks) { + guint blocks_remining = gst_ttml_render_handle_whitespace (split_blocks); + GST_CAT_DEBUG (ttmlrender_debug, + "There are %u blocks remaining after whitespace handling.", + blocks_remining); - /* Render background rectangles, if any. */ - backgrounds = gst_ttml_render_render_element_backgrounds (block, char_ranges, - render->layout, text_offset - line_padding, 0, - (guint) round (block->style_set->line_height * max_font_size), - line_padding, rendered_text->horiz_offset); - - /* Render block background, if non-transparent. */ - if (!gst_ttml_render_color_is_transparent (&block->style_set-> - background_color)) { - GstTtmlRenderRenderedImage *block_background; - GstTtmlRenderRenderedImage *tmp = backgrounds; - - GstBuffer *block_bg_image = gst_ttml_render_draw_rectangle (width, - backgrounds->height, block->style_set->background_color); - block_background = gst_ttml_render_rendered_image_new (block_bg_image, 0, - 0, width, backgrounds->height); - backgrounds = gst_ttml_render_rendered_image_combine (block_background, - backgrounds); - gst_ttml_render_rendered_image_free (tmp); - gst_ttml_render_rendered_image_free (block_background); + if (blocks_remining > 0) { + images = gst_ttml_render_layout_blocks (render, split_blocks, metrics, + unified_block->style_set); + rendered_block = gst_ttml_render_overlay_images (images); + g_ptr_array_unref (images); + } + g_ptr_array_unref (split_blocks); } - /* Combine text and background images. */ - ret = gst_ttml_render_rendered_image_combine (backgrounds, - rendered_text->text_image); - gst_ttml_render_rendered_image_free (backgrounds); - gst_ttml_render_rendered_text_free (rendered_text); - - g_free (marked_up_string); - g_ptr_array_unref (char_ranges); - GST_CAT_DEBUG (ttmlrender_debug, "block width: %u block height: %u", - ret->width, ret->height); - return ret; + g_ptr_array_unref (ranges); + gst_ttml_render_unified_block_free (unified_block); + return rendered_block; } @@ -2241,7 +2555,6 @@ gst_ttml_render_render_text_region (GstTtmlRender * render, g_ptr_array_new_with_free_func ( (GDestroyNotify) gst_ttml_render_rendered_image_free); GstTtmlRenderRenderedImage *region_image = NULL; - GstTtmlRenderRenderedImage *blocks_image; GstVideoOverlayComposition *ret = NULL; guint i; @@ -2291,11 +2604,51 @@ gst_ttml_render_render_text_region (GstTtmlRender * render, rendered_block = gst_ttml_render_render_text_block (render, block, text_buf, window_width, TRUE); + if (!rendered_block) + continue; + + GST_CAT_LOG (ttmlrender_debug, "rendered_block - x:%d y:%d w:%u h:%u", + rendered_block->x, rendered_block->y, rendered_block->width, + rendered_block->height); + + switch (block->style_set->text_align) { + case GST_SUBTITLE_TEXT_ALIGN_CENTER: + rendered_block->x + += (gint) round ((window_width - rendered_block->width) / 2.0); + break; + + case GST_SUBTITLE_TEXT_ALIGN_RIGHT: + case GST_SUBTITLE_TEXT_ALIGN_END: + rendered_block->x += (window_width - rendered_block->width); + break; + + default: + break; + } + + if (!gst_ttml_render_color_is_transparent (&block->style_set-> + background_color)) { + /* Draw block background rectangle and render block image over it */ + GstTtmlRenderRenderedImage *tmp = rendered_block; + GstBuffer *block_bg_buf; + GstTtmlRenderRenderedImage *block_bg_image; + + block_bg_buf = gst_ttml_render_draw_rectangle (window_width, + rendered_block->height, block->style_set->background_color); + block_bg_image = gst_ttml_render_rendered_image_new (block_bg_buf, 0, + rendered_block->y, window_width, rendered_block->height); + rendered_block = gst_ttml_render_rendered_image_combine (block_bg_image, + rendered_block); + gst_ttml_render_rendered_image_free (tmp); + gst_ttml_render_rendered_image_free (block_bg_image); + } + + rendered_block->y = 0; g_ptr_array_add (rendered_blocks, rendered_block); } if (rendered_blocks->len > 0) { - GstTtmlRenderRenderedImage *tmp; + GstTtmlRenderRenderedImage *blocks_image, *tmp; blocks_image = gst_ttml_render_stitch_images (rendered_blocks, GST_TTML_DIRECTION_BLOCK); @@ -2325,22 +2678,13 @@ gst_ttml_render_render_text_region (GstTtmlRender * render, } tmp = region_image; - if (region_image || blocks_image) { - region_image = - gst_ttml_render_rendered_image_combine (region_image, blocks_image); - } else { - GST_CAT_DEBUG (ttmlrender_debug, "Nothing to render"); - return NULL; - } - - if (tmp) - gst_ttml_render_rendered_image_free (tmp); + region_image = + gst_ttml_render_rendered_image_combine (region_image, blocks_image); + gst_ttml_render_rendered_image_free (tmp); gst_ttml_render_rendered_image_free (blocks_image); } if (region_image) { - GST_CAT_DEBUG (ttmlrender_debug, "Height of rendered region: %u", - region_image->height); ret = gst_ttml_render_compose_overlay (region_image); gst_ttml_render_rendered_image_free (region_image); } diff --git a/ext/ttml/gstttmlrender.h b/ext/ttml/gstttmlrender.h index 480c7484a9..7550db1fe2 100644 --- a/ext/ttml/gstttmlrender.h +++ b/ext/ttml/gstttmlrender.h @@ -49,7 +49,6 @@ G_BEGIN_DECLS typedef struct _GstTtmlRender GstTtmlRender; typedef struct _GstTtmlRenderClass GstTtmlRenderClass; typedef struct _GstTtmlRenderRenderedImage GstTtmlRenderRenderedImage; -typedef struct _GstTtmlRenderRenderedText GstTtmlRenderRenderedText; struct _GstTtmlRenderRenderedImage { GstBuffer *image; @@ -59,16 +58,6 @@ struct _GstTtmlRenderRenderedImage { guint height; }; -struct _GstTtmlRenderRenderedText { - GstTtmlRenderRenderedImage *text_image; - - /* The coordinates in @layout will be offset horizontally with respect to the - * position of those characters in @text_image. Store that offset here so - * that the information in @layout can be used to locate the position and - * extent of text areas in @text_image. */ - guint horiz_offset; -}; - struct _GstTtmlRender { GstElement element;