[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