gstreamer/ext/ttml/subtitle.c
Chris Bass 0288ee24e9 ttml: correctly implement lineHeight behaviour
The specified behaviour in TTML when lineHeight is "normal" is different
from the behaviour when a percentage is given. In the former case, the
line height is a percentage (the TTML spec recommends 125%) of the largest
font size that is applied to the spans within the block; in the latter
case, the line height is the given percentage of the font size that is
applied to the block itself.

The code doesn't correctly implement this behaviour; this patch fixes
that.

https://bugzilla.gnome.org/show_bug.cgi?id=780402
2017-04-09 10:42:08 +03:00

318 lines
9.3 KiB
C

/* GStreamer
* Copyright (C) <2015> British Broadcasting Corporation
* Author: Chris Bass <dash@rd.bbc.co.uk>
*
* 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.
*/
/**
* SECTION:gstsubtitle
* @short_description: Library for describing sets of static subtitles.
*
* This library enables the description of static text scenes made up of a
* number of regions, which may contain a number of block and inline text
* elements. It is derived from the concepts and features defined in the Timed
* Text Markup Language 1 (TTML1), Second Edition
* (http://www.w3.org/TR/ttaf1-dfxp), and the EBU-TT-D profile of TTML1
* (https://tech.ebu.ch/files/live/sites/tech/files/shared/tech/tech3380.pdf).
*/
#include "subtitle.h"
/**
* gst_subtitle_style_set_free:
* @style_set: A #GstSubtitleStyleSet.
*
* Free @style_set and its associated memory.
*/
static void
_gst_subtitle_style_set_free (GstSubtitleStyleSet * style_set)
{
g_return_if_fail (style_set != NULL);
g_free (style_set->font_family);
g_slice_free (GstSubtitleStyleSet, style_set);
}
GST_DEFINE_MINI_OBJECT_TYPE (GstSubtitleStyleSet, gst_subtitle_style_set);
/**
* gst_subtitle_style_set_new:
*
* Create a new #GstSubtitleStyleSet with default values for all properties.
*
* Returns: (transfer full): A newly-allocated #GstSubtitleStyleSet.
*/
GstSubtitleStyleSet *
gst_subtitle_style_set_new (void)
{
GstSubtitleStyleSet *ret = g_slice_new0 (GstSubtitleStyleSet);
GstSubtitleColor white = { 255, 255, 255, 255 };
GstSubtitleColor transparent = { 0, 0, 0, 0 };
gst_mini_object_init (GST_MINI_OBJECT_CAST (ret), 0,
gst_subtitle_style_set_get_type (), NULL, NULL,
(GstMiniObjectFreeFunction) _gst_subtitle_style_set_free);
ret->font_family = g_strdup ("default");
ret->font_size = 1.0;
ret->line_height = -1;
ret->color = white;
ret->background_color = transparent;
ret->line_padding = 0.0;
ret->origin_x = ret->origin_y = 0.0;
ret->extent_w = ret->extent_h = 0.0;
ret->padding_start = ret->padding_end
= ret->padding_before = ret->padding_after = 0.0;
return ret;
}
static void
_gst_subtitle_element_free (GstSubtitleElement * element)
{
g_return_if_fail (element != NULL);
gst_subtitle_style_set_unref (element->style_set);
g_slice_free (GstSubtitleElement, element);
}
GST_DEFINE_MINI_OBJECT_TYPE (GstSubtitleElement, gst_subtitle_element);
/**
* gst_subtitle_element_new:
* @style_set: (transfer full): A #GstSubtitleStyleSet that defines the styling
* and layout associated with this inline text element.
* @text_index: The index within a #GstBuffer of the #GstMemory that contains
* the text of this inline text element.
*
* Allocates a new #GstSubtitleElement.
*
* Returns: (transfer full): A newly-allocated #GstSubtitleElement. Unref
* with gst_subtitle_element_unref() when no longer needed.
*/
GstSubtitleElement *
gst_subtitle_element_new (GstSubtitleStyleSet * style_set,
guint text_index, gboolean suppress_whitespace)
{
GstSubtitleElement *element;
g_return_val_if_fail (style_set != NULL, NULL);
element = g_slice_new0 (GstSubtitleElement);
gst_mini_object_init (GST_MINI_OBJECT_CAST (element), 0,
gst_subtitle_element_get_type (), NULL, NULL,
(GstMiniObjectFreeFunction) _gst_subtitle_element_free);
element->style_set = style_set;
element->text_index = text_index;
element->suppress_whitespace = suppress_whitespace;
return element;
}
static void
_gst_subtitle_block_free (GstSubtitleBlock * block)
{
g_return_if_fail (block != NULL);
gst_subtitle_style_set_unref (block->style_set);
g_ptr_array_unref (block->elements);
g_slice_free (GstSubtitleBlock, block);
}
GST_DEFINE_MINI_OBJECT_TYPE (GstSubtitleBlock, gst_subtitle_block);
/**
* gst_subtitle_block_new:
* @style_set: (transfer full): A #GstSubtitleStyleSet that defines the styling
* and layout associated with this block of text elements.
*
* Allocates a new #GstSubtitleBlock.
*
* Returns: (transfer full): A newly-allocated #GstSubtitleBlock. Unref
* with gst_subtitle_block_unref() when no longer needed.
*/
GstSubtitleBlock *
gst_subtitle_block_new (GstSubtitleStyleSet * style_set)
{
GstSubtitleBlock *block;
g_return_val_if_fail (style_set != NULL, NULL);
block = g_slice_new0 (GstSubtitleBlock);
gst_mini_object_init (GST_MINI_OBJECT_CAST (block), 0,
gst_subtitle_block_get_type (), NULL, NULL,
(GstMiniObjectFreeFunction) _gst_subtitle_block_free);
block->style_set = style_set;
block->elements = g_ptr_array_new_with_free_func (
(GDestroyNotify) gst_subtitle_element_unref);
return block;
}
/**
* gst_subtitle_block_add_element:
* @block: A #GstSubtitleBlock.
* @element: (transfer full): A #GstSubtitleElement to add.
*
* Adds a #GstSubtitleElement to @block.
*/
void
gst_subtitle_block_add_element (GstSubtitleBlock * block,
GstSubtitleElement * element)
{
g_return_if_fail (block != NULL);
g_return_if_fail (element != NULL);
g_ptr_array_add (block->elements, element);
}
/**
* gst_subtitle_block_get_element_count:
* @block: A #GstSubtitleBlock.
*
* Returns: The number of #GstSubtitleElements in @block.
*/
guint
gst_subtitle_block_get_element_count (const GstSubtitleBlock * block)
{
g_return_val_if_fail (block != NULL, 0);
return block->elements->len;
}
/**
* gst_subtitle_block_get_element:
* @block: A #GstSubtitleBlock.
* @index: Index of the element to get.
*
* Gets the #GstSubtitleElement at @index in the array of elements held by
* @block.
*
* Returns: (transfer none): The #GstSubtitleElement at @index in the array of
* elements held by @block, or %NULL if @index is out-of-bounds. The
* function does not return a reference; the caller should obtain a reference
* using gst_subtitle_element_ref(), if needed.
*/
GstSubtitleElement *
gst_subtitle_block_get_element (const GstSubtitleBlock * block, guint index)
{
g_return_val_if_fail (block != NULL, NULL);
if (index >= block->elements->len)
return NULL;
else
return g_ptr_array_index (block->elements, index);
}
static void
_gst_subtitle_region_free (GstSubtitleRegion * region)
{
g_return_if_fail (region != NULL);
gst_subtitle_style_set_unref (region->style_set);
g_ptr_array_unref (region->blocks);
g_slice_free (GstSubtitleRegion, region);
}
GST_DEFINE_MINI_OBJECT_TYPE (GstSubtitleRegion, gst_subtitle_region);
/**
* gst_subtitle_region_new:
* @style_set: (transfer full): A #GstSubtitleStyleSet that defines the styling
* and layout associated with this region.
*
* Allocates a new #GstSubtitleRegion.
*
* Returns: (transfer full): A newly-allocated #GstSubtitleRegion. Unref
* with gst_subtitle_region_unref() when no longer needed.
*/
GstSubtitleRegion *
gst_subtitle_region_new (GstSubtitleStyleSet * style_set)
{
GstSubtitleRegion *region;
g_return_val_if_fail (style_set != NULL, NULL);
region = g_slice_new0 (GstSubtitleRegion);
gst_mini_object_init (GST_MINI_OBJECT_CAST (region), 0,
gst_subtitle_region_get_type (), NULL, NULL,
(GstMiniObjectFreeFunction) _gst_subtitle_region_free);
region->style_set = style_set;
region->blocks = g_ptr_array_new_with_free_func (
(GDestroyNotify) gst_subtitle_block_unref);
return region;
}
/**
* gst_subtitle_region_add_block:
* @region: A #GstSubtitleRegion.
* @block: (transfer full): A #GstSubtitleBlock which should be added
* to @region's array of blocks.
*
* Adds a #GstSubtitleBlock to the end of the array of blocks held by @region.
* @region will take ownership of @block, and will unref it when @region
* is freed.
*/
void
gst_subtitle_region_add_block (GstSubtitleRegion * region,
GstSubtitleBlock * block)
{
g_return_if_fail (region != NULL);
g_return_if_fail (block != NULL);
g_ptr_array_add (region->blocks, block);
}
/**
* gst_subtitle_region_get_block_count:
* @region: A #GstSubtitleRegion.
*
* Returns: The number of blocks in @region.
*/
guint
gst_subtitle_region_get_block_count (const GstSubtitleRegion * region)
{
g_return_val_if_fail (region != NULL, 0);
return region->blocks->len;
}
/**
* gst_subtitle_region_get_block:
* @region: A #GstSubtitleRegion.
* @index: Index of the block to get.
*
* Gets the block at @index in the array of blocks held by @region.
*
* Returns: (transfer none): The #GstSubtitleBlock at @index in the array of
* blocks held by @region, or %NULL if @index is out-of-bounds. The
* function does not return a reference; the caller should obtain a reference
* using gst_subtitle_block_ref(), if needed.
*/
const GstSubtitleBlock *
gst_subtitle_region_get_block (const GstSubtitleRegion * region, guint index)
{
g_return_val_if_fail (region != NULL, NULL);
if (index >= region->blocks->len)
return NULL;
else
return g_ptr_array_index (region->blocks, index);
}