[vlc-devel] [PATCH 7/9] freetype: new file: text_layout.c
Salah-Eddin Shaban
salshaaban at gmail.com
Sat Apr 18 19:56:19 CEST 2015
New functions:
NewParagraph()
FreeParagraph()
AnalyzeParagraph()
AddRun()
ItemizeParagraph()
---
modules/text_renderer/text_layout.c | 465 ++++++++++++++++++++++++++++++++++++
1 file changed, 465 insertions(+)
create mode 100644 modules/text_renderer/text_layout.c
diff --git a/modules/text_renderer/text_layout.c b/modules/text_renderer/text_layout.c
new file mode 100644
index 0000000..4487e6d
--- /dev/null
+++ b/modules/text_renderer/text_layout.c
@@ -0,0 +1,465 @@
+/*****************************************************************************
+ * text_layout.c : Text shaping and layout
+ *****************************************************************************
+ * Copyright (C) 2015 VLC authors and VideoLAN
+ * $Id$
+ *
+ * Authors: Salah-Eddin Shaban <salshaaban at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#include <vlc_common.h>
+#include <vlc_filter.h>
+#include <vlc_text_style.h>
+
+/* Freetype */
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_GLYPH_H
+#include FT_STROKER_H
+#include FT_SYNTHESIS_H
+
+/* RTL */
+#if defined(HAVE_FRIBIDI)
+# include <fribidi/fribidi.h>
+#endif
+
+/* Complex Scripts */
+#if defined(HAVE_HARFBUZZ)
+# include <hb.h>
+# include <hb-ft.h>
+#endif
+
+#include "text_renderer.h"
+#include "text_layout.h"
+#include "freetype.h"
+
+/*
+ * Within a paragraph, run_desc_t represents a run of characters
+ * having the same font face, size, and style, Unicode script
+ * and text direction
+ */
+typedef struct run_desc_t
+{
+ int i_start_offset;
+ int i_end_offset;
+ FT_Face p_face;
+ text_style_t *p_style;
+
+#ifdef HAVE_HARFBUZZ
+ hb_script_t script;
+ hb_direction_t direction;
+ hb_font_t *p_hb_font;
+ hb_buffer_t *p_buffer;
+ hb_glyph_info_t *p_glyph_infos;
+ hb_glyph_position_t *p_glyph_positions;
+ unsigned int i_glyph_count;
+#endif
+
+} run_desc_t;
+
+/*
+ * Glyph bitmaps. Advance and offset are 26.6 values
+ */
+typedef struct glyph_bitmaps_t
+{
+ FT_Glyph p_glyph;
+ FT_Glyph p_outline;
+ FT_Glyph p_shadow;
+ FT_BBox glyph_bbox;
+ FT_BBox outline_bbox;
+ FT_BBox shadow_bbox;
+ int i_x_offset;
+ int i_y_offset;
+ int i_x_advance;
+ int i_y_advance;
+} glyph_bitmaps_t;
+
+typedef struct paragraph_t
+{
+ uni_char_t *p_code_points; //Unicode code points
+ int *pi_glyph_indices; //Glyph index values within the run's font face
+ text_style_t **pp_styles;
+ int *pi_run_ids; //The run to which each glyph belongs
+ glyph_bitmaps_t *p_glyph_bitmaps;
+ uint8_t *pi_karaoke_bar;
+ int i_size;
+ run_desc_t *p_runs;
+ int i_runs_count;
+ int i_runs_size;
+
+#ifdef HAVE_HARFBUZZ
+ hb_script_t *p_scripts;
+#endif
+
+#ifdef HAVE_FRIBIDI
+ FriBidiCharType *p_types;
+ FriBidiLevel *p_levels;
+ FriBidiStrIndex *pi_reordered_indices;
+ FriBidiParType paragraph_type;
+#endif
+
+} paragraph_t;
+
+void FreeLine( line_desc_t *p_line )
+{
+ for( int i = 0; i < p_line->i_character_count; i++ )
+ {
+ line_character_t *ch = &p_line->p_character[i];
+ FT_Done_Glyph( (FT_Glyph)ch->p_glyph );
+ if( ch->p_outline )
+ FT_Done_Glyph( (FT_Glyph)ch->p_outline );
+ if( ch->p_shadow )
+ FT_Done_Glyph( (FT_Glyph)ch->p_shadow );
+ }
+
+ free( p_line->p_character );
+ free( p_line );
+}
+
+void FreeLines( line_desc_t *p_lines )
+{
+ for( line_desc_t *p_line = p_lines; p_line != NULL; )
+ {
+ line_desc_t *p_next = p_line->p_next;
+ FreeLine( p_line );
+ p_line = p_next;
+ }
+}
+
+line_desc_t *NewLine( int i_count )
+{
+ line_desc_t *p_line = malloc( sizeof(*p_line) );
+
+ if( !p_line )
+ return NULL;
+
+ p_line->p_next = NULL;
+ p_line->i_width = 0;
+ p_line->i_base_line = 0;
+ p_line->i_character_count = 0;
+
+ p_line->bbox.xMin = INT_MAX;
+ p_line->bbox.yMin = INT_MAX;
+ p_line->bbox.xMax = INT_MIN;
+ p_line->bbox.yMax = INT_MIN;
+
+ p_line->p_character = calloc( i_count, sizeof(*p_line->p_character) );
+ if( !p_line->p_character )
+ {
+ free( p_line );
+ return NULL;
+ }
+ return p_line;
+}
+
+static void FixGlyph( FT_Glyph glyph, FT_BBox *p_bbox,
+ FT_Face face, const FT_Vector *p_pen )
+{
+ FT_BitmapGlyph glyph_bmp = (FT_BitmapGlyph)glyph;
+ if( p_bbox->xMin >= p_bbox->xMax )
+ {
+ p_bbox->xMin = FT_CEIL(p_pen->x);
+ p_bbox->xMax = FT_CEIL(p_pen->x + face->glyph->advance.x);
+ glyph_bmp->left = p_bbox->xMin;
+ }
+ if( p_bbox->yMin >= p_bbox->yMax )
+ {
+ p_bbox->yMax = FT_CEIL(p_pen->y);
+ p_bbox->yMin = FT_CEIL(p_pen->y + face->glyph->advance.y);
+ glyph_bmp->top = p_bbox->yMax;
+ }
+}
+
+static void BBoxEnlarge( FT_BBox *p_max, const FT_BBox *p )
+{
+ p_max->xMin = __MIN(p_max->xMin, p->xMin);
+ p_max->yMin = __MIN(p_max->yMin, p->yMin);
+ p_max->xMax = __MAX(p_max->xMax, p->xMax);
+ p_max->yMax = __MAX(p_max->yMax, p->yMax);
+}
+
+static paragraph_t *NewParagraph( filter_t *p_filter,
+ int i_size,
+ uni_char_t *p_code_points,
+ text_style_t **pp_styles,
+ uint32_t *pi_k_dates,
+ int i_runs_size )
+{
+ paragraph_t *p_paragraph = calloc( 1, sizeof( paragraph_t ) );
+ if( !p_paragraph )
+ return 0;
+
+ p_paragraph->i_size = i_size;
+ p_paragraph->p_code_points =
+ malloc( i_size * sizeof( *p_paragraph->p_code_points ) );
+ p_paragraph->pi_glyph_indices =
+ malloc( i_size * sizeof( *p_paragraph->pi_glyph_indices ) );
+ p_paragraph->pp_styles =
+ malloc( i_size * sizeof( *p_paragraph->pp_styles ) );
+ p_paragraph->pi_run_ids =
+ calloc( i_size, sizeof( *p_paragraph->pi_run_ids ) );
+ p_paragraph->p_glyph_bitmaps =
+ calloc( i_size, sizeof( *p_paragraph->p_glyph_bitmaps ) );
+ p_paragraph->pi_karaoke_bar =
+ calloc( i_size, sizeof( *p_paragraph->pi_karaoke_bar ) );
+
+ p_paragraph->p_runs = calloc( i_runs_size, sizeof( run_desc_t ) );
+ p_paragraph->i_runs_size = i_runs_size;
+ p_paragraph->i_runs_count = 0;
+
+ if( !p_paragraph->p_code_points || !p_paragraph->pi_glyph_indices
+ || !p_paragraph->pp_styles || !p_paragraph->pi_run_ids
+ || !p_paragraph->p_glyph_bitmaps || !p_paragraph->pi_karaoke_bar )
+ goto error;
+
+ if( p_code_points )
+ memcpy( p_paragraph->p_code_points, p_code_points,
+ i_size * sizeof( *p_code_points ) );
+ if( pp_styles )
+ memcpy( p_paragraph->pp_styles, pp_styles,
+ i_size * sizeof( *pp_styles ) );
+ if( pi_k_dates )
+ {
+ int64_t i_elapsed = var_GetTime( p_filter, "spu-elapsed" ) / 1000;
+ for( int i = 0; i < i_size; ++i )
+ {
+ p_paragraph->pi_karaoke_bar[ i ] = pi_k_dates[ i ] >= i_elapsed;
+ }
+ }
+
+#ifdef HAVE_HARFBUZZ
+ p_paragraph->p_scripts = malloc( i_size * sizeof( *p_paragraph->p_scripts ) );
+ if( !p_paragraph->p_scripts )
+ goto error;
+#endif
+
+#ifdef HAVE_FRIBIDI
+ p_paragraph->p_levels = malloc( i_size * sizeof( *p_paragraph->p_levels ) );
+ p_paragraph->p_types = malloc( i_size * sizeof( *p_paragraph->p_types ) );
+ p_paragraph->pi_reordered_indices =
+ malloc( i_size * sizeof( *p_paragraph->pi_reordered_indices ) );
+
+ if( !p_paragraph->p_levels || !p_paragraph->p_types
+ || !p_paragraph->pi_reordered_indices )
+ goto error;
+
+ int i_direction = var_InheritInteger( p_filter, "freetype-text-direction" );
+ if( i_direction == 0 )
+ p_paragraph->paragraph_type = FRIBIDI_PAR_LTR;
+ else if( i_direction == 1 )
+ p_paragraph->paragraph_type = FRIBIDI_PAR_RTL;
+ else
+ p_paragraph->paragraph_type = FRIBIDI_PAR_ON;
+#endif
+
+ return p_paragraph;
+
+error:
+ if( p_paragraph->p_code_points ) free( p_paragraph->p_code_points );
+ if( p_paragraph->pi_glyph_indices ) free( p_paragraph->pi_glyph_indices );
+ if( p_paragraph->pp_styles ) free( p_paragraph->pp_styles );
+ if( p_paragraph->pi_run_ids ) free( p_paragraph->pi_run_ids );
+ if( p_paragraph->p_glyph_bitmaps ) free( p_paragraph->p_glyph_bitmaps );
+ if (p_paragraph->pi_karaoke_bar ) free( p_paragraph->pi_karaoke_bar );
+ if( p_paragraph->p_runs ) free( p_paragraph->p_runs );
+#ifdef HAVE_HARFBUZZ
+ if( p_paragraph->p_scripts ) free( p_paragraph->p_scripts );
+#endif
+#ifdef HAVE_FRIBIDI
+ if( p_paragraph->p_levels ) free( p_paragraph->p_levels );
+ if( p_paragraph->p_types ) free( p_paragraph->p_types );
+ if( p_paragraph->pi_reordered_indices )
+ free( p_paragraph->pi_reordered_indices );
+#endif
+ free( p_paragraph );
+ return 0;
+}
+
+static void FreeParagraph( paragraph_t *p_paragraph )
+{
+ free( p_paragraph->p_runs );
+ free( p_paragraph->pi_glyph_indices );
+ free( p_paragraph->p_glyph_bitmaps );
+ free( p_paragraph->pi_karaoke_bar );
+ free( p_paragraph->pi_run_ids );
+ free( p_paragraph->pp_styles );
+ free( p_paragraph->p_code_points );
+
+#ifdef HAVE_HARFBUZZ
+ free( p_paragraph->p_scripts );
+#endif
+
+#ifdef HAVE_FRIBIDI
+ free( p_paragraph->pi_reordered_indices );
+ free( p_paragraph->p_types );
+ free( p_paragraph->p_levels );
+#endif
+
+ free( p_paragraph );
+}
+
+#ifdef HAVE_FRIBIDI
+static int AnalyzeParagraph( paragraph_t *p_paragraph )
+{
+ fribidi_get_bidi_types( p_paragraph->p_code_points,
+ p_paragraph->i_size,
+ p_paragraph->p_types );
+ fribidi_get_par_embedding_levels( p_paragraph->p_types,
+ p_paragraph->i_size,
+ &p_paragraph->paragraph_type,
+ p_paragraph->p_levels );
+
+#ifdef HAVE_HARFBUZZ
+ hb_unicode_funcs_t *p_funcs = hb_unicode_funcs_get_default();
+ for( int i = 0; i < p_paragraph->i_size; ++i )
+ p_paragraph->p_scripts[ i ] =
+ hb_unicode_script( p_funcs, p_paragraph->p_code_points[ i ] );
+
+ hb_script_t i_last_script;
+ int i_last_script_index = -1;
+ int i_last_set_index = -1;
+
+ /*
+ * For shaping to work, characters that are assigned HB_SCRIPT_COMMON or
+ * HB_SCRIPT_INHERITED should be resolved to the last encountered valid
+ * script value, if any, and to the first one following them otherwise
+ */
+ for( int i = 0; i < p_paragraph->i_size; ++i )
+ {
+ if( p_paragraph->p_scripts[ i ] == HB_SCRIPT_COMMON
+ || p_paragraph->p_scripts[ i ] == HB_SCRIPT_INHERITED)
+ {
+ if( i_last_script_index != -1)
+ {
+ p_paragraph->p_scripts[ i ] = i_last_script;
+ i_last_set_index = i;
+ }
+ }
+ else
+ {
+ for( int j = i_last_set_index + 1; j < i; ++j )
+ p_paragraph->p_scripts[ j ] = p_paragraph->p_scripts[ i ];
+
+ i_last_script = p_paragraph->p_scripts[ i ];
+ i_last_script_index = i;
+ i_last_set_index = i;
+ }
+ }
+#endif //HAVE_HARFBUZZ
+
+ return VLC_SUCCESS;
+}
+#endif //HAVE_FRIBIDI
+
+static int AddRun( filter_t *p_filter,
+ paragraph_t *p_paragraph,
+ int i_start_offset,
+ int i_end_offset,
+ FT_Face p_face )
+{
+ if( i_start_offset >= i_end_offset
+ || i_start_offset < 0 || i_start_offset >= p_paragraph->i_size
+ || i_end_offset <= 0 || i_end_offset > p_paragraph->i_size )
+ {
+ msg_Err( p_filter,
+ "AddRun() invalid parameters. Paragraph size: %d, "
+ "Start offset: %d, End offset: %d",
+ p_paragraph->i_size, i_start_offset, i_end_offset );
+ return VLC_EGENERIC;
+ }
+
+ if( p_paragraph->i_runs_count == p_paragraph->i_runs_size )
+ {
+ run_desc_t *p_new_runs =
+ realloc( p_paragraph->p_runs,
+ p_paragraph->i_runs_size * 2 * sizeof( *p_new_runs ) );
+ if( !p_new_runs )
+ return VLC_ENOMEM;
+ p_paragraph->p_runs = p_new_runs;
+ p_paragraph->i_runs_size *= 2;
+ }
+
+ int i_run_id = p_paragraph->i_runs_count;
+ run_desc_t *p_run = p_paragraph->p_runs + p_paragraph->i_runs_count++;
+ p_run->i_start_offset = i_start_offset;
+ p_run->i_end_offset = i_end_offset;
+ p_run->p_style = p_paragraph->pp_styles[ i_start_offset ];
+ p_run->p_face = p_face;
+
+#ifdef HAVE_HARFBUZZ
+ p_run->script = p_paragraph->p_scripts[ i_start_offset ];
+ p_run->direction = p_paragraph->p_levels[ i_start_offset ] & 1 ?
+ HB_DIRECTION_RTL : HB_DIRECTION_LTR;
+#endif
+
+ for( int i = i_start_offset; i < i_end_offset; ++i )
+ p_paragraph->pi_run_ids[ i ] = i_run_id;
+
+ return VLC_SUCCESS;
+}
+
+/*
+ * Segment a paragraph into runs
+ */
+static int ItemizeParagraph( filter_t *p_filter, paragraph_t *p_paragraph )
+{
+ if( p_paragraph->i_size <= 0 )
+ {
+ msg_Err( p_filter,
+ "ItemizeParagraph() invalid parameters. Paragraph size: %d",
+ p_paragraph->i_size );
+ return VLC_EGENERIC;
+ }
+
+ int i_last_run_start = 0;
+ text_style_t *p_last_style = p_paragraph->pp_styles[ 0 ];
+
+#ifdef HAVE_HARFBUZZ
+ hb_script_t last_script = p_paragraph->p_scripts[ 0 ];
+ FriBidiLevel last_level = p_paragraph->p_levels[ 0 ];
+#endif
+
+ for( int i = 0; i <= p_paragraph->i_size; ++i )
+ {
+ if( i == p_paragraph->i_size
+#ifdef HAVE_HARFBUZZ
+ || last_script != p_paragraph->p_scripts[ i ]
+ || last_level != p_paragraph->p_levels[ i ]
+#endif
+ || p_last_style->i_font_size != p_paragraph->pp_styles[ i ]->i_font_size
+ || ( ( p_last_style->i_style_flags
+ ^ p_paragraph->pp_styles[ i ]->i_style_flags )
+ & STYLE_HALFWIDTH )
+ ||!FaceStyleEquals( p_last_style, p_paragraph->pp_styles[ i ] ) )
+ {
+ int i_ret = AddRun( p_filter, p_paragraph, i_last_run_start, i, 0 );
+ if( i_ret )
+ return i_ret;
+
+ if( i < p_paragraph->i_size )
+ {
+ i_last_run_start = i;
+ p_last_style = p_paragraph->pp_styles[ i ];
+#ifdef HAVE_HARFBUZZ
+ last_script = p_paragraph->p_scripts[ i ];
+ last_level = p_paragraph->p_levels[ i ];
+#endif
+ }
+ }
+ }
+ return VLC_SUCCESS;
+}
--
1.9.1
More information about the vlc-devel
mailing list