[vlc-devel] [PATCH 8/9] freetype: text_layout.c: new shaping functions

Salah-Eddin Shaban salshaaban at gmail.com
Sat Apr 18 19:56:20 CEST 2015


New functions:

ShapeParagraphHarfBuzz()
ShapeParagraphFriBidi()
RemoveZeroWidthCharacters()
ZeroNsmAdvance()
LoadGlyphs()
---
 modules/text_renderer/text_layout.c | 388 ++++++++++++++++++++++++++++++++++++
 1 file changed, 388 insertions(+)

diff --git a/modules/text_renderer/text_layout.c b/modules/text_renderer/text_layout.c
index 4487e6d..aeb0919 100644
--- a/modules/text_renderer/text_layout.c
+++ b/modules/text_renderer/text_layout.c
@@ -463,3 +463,391 @@ static int ItemizeParagraph( filter_t *p_filter, paragraph_t *p_paragraph )
     }
     return VLC_SUCCESS;
 }
+
+/*
+ * Shape an itemized paragraph using HarfBuzz.
+ * This is where the glyphs of complex scripts get their positions
+ * (offsets and advance values) and final forms.
+ * Glyph substitutions of base glyphs and diacritics may take place,
+ * so the paragraph size may change.
+ */
+#ifdef HAVE_HARFBUZZ
+static int ShapeParagraphHarfBuzz( filter_t *p_filter,
+                                   paragraph_t **p_old_paragraph )
+{
+    paragraph_t *p_paragraph = *p_old_paragraph;
+    paragraph_t *p_new_paragraph = 0;
+    filter_sys_t *p_sys = p_filter->p_sys;
+    int i_total_glyphs = 0;
+    int i_ret = VLC_EGENERIC;
+
+    if( p_paragraph->i_size <= 0 || p_paragraph->i_runs_count <= 0 )
+    {
+        msg_Err( p_filter, "ShapeParagraphHarfBuzz() invalid parameters. "
+                 "Paragraph size: %d. Runs count %d",
+                 p_paragraph->i_size, p_paragraph->i_runs_count );
+        return VLC_EGENERIC;
+    }
+
+    for( int i = 0; i < p_paragraph->i_runs_count; ++i )
+    {
+        run_desc_t *p_run = p_paragraph->p_runs + i;
+        text_style_t *p_style = p_run->p_style;
+
+        /*
+         * When using HarfBuzz, this is where font faces are loaded.
+         * In the other two paths (shaping with FriBidi or no
+         * shaping at all), faces are loaded in LoadGlyphs()
+         */
+        FT_Face p_face = 0;
+        if( !p_run->p_face )
+        {
+            p_face = LoadFace( p_filter, p_style );
+            if( !p_face )
+                p_face = p_sys->p_face;
+            p_run->p_face = p_face;
+        }
+        else
+            p_face = p_run->p_face;
+
+        int i_font_width = p_style->i_style_flags & STYLE_HALFWIDTH
+                         ? p_style->i_font_size / 2 : p_style->i_font_size;
+
+        if( FT_Set_Pixel_Sizes( p_face, i_font_width, p_style->i_font_size ) )
+            msg_Err( p_filter,
+                     "Failed to set font size to %d", p_style->i_font_size );
+
+        p_run->p_hb_font = hb_ft_font_create( p_face, 0 );
+        if( !p_run->p_hb_font )
+        {
+            msg_Err( p_filter,
+                     "ShapeParagraphHarfBuzz(): hb_ft_font_create() error" );
+            i_ret = VLC_EGENERIC;
+            goto error;
+        }
+
+        p_run->p_buffer = hb_buffer_create();
+        if( !p_run->p_buffer )
+        {
+            msg_Err( p_filter,
+                     "ShapeParagraphHarfBuzz(): hb_buffer_create() error" );
+            i_ret = VLC_EGENERIC;
+            goto error;
+        }
+
+        hb_buffer_set_direction( p_run->p_buffer, p_run->direction );
+        hb_buffer_set_script( p_run->p_buffer, p_run->script );
+#ifdef __OS2__
+        hb_buffer_add_utf16( p_run->p_buffer,
+                             p_paragraph->p_code_points + p_run->i_start_offset,
+                             p_run->i_end_offset - p_run->i_start_offset, 0,
+                             p_run->i_end_offset - p_run->i_start_offset );
+#else
+        hb_buffer_add_utf32( p_run->p_buffer,
+                             p_paragraph->p_code_points + p_run->i_start_offset,
+                             p_run->i_end_offset - p_run->i_start_offset, 0,
+                             p_run->i_end_offset - p_run->i_start_offset );
+#endif
+        hb_shape( p_run->p_hb_font, p_run->p_buffer, 0, 0 );
+        p_run->p_glyph_infos =
+            hb_buffer_get_glyph_infos( p_run->p_buffer, &p_run->i_glyph_count );
+        p_run->p_glyph_positions =
+            hb_buffer_get_glyph_positions( p_run->p_buffer, &p_run->i_glyph_count );
+        i_total_glyphs += p_run->i_glyph_count;
+    }
+
+    if( i_total_glyphs <= 0 )
+    {
+        msg_Err( p_filter,
+                 "ShapeParagraphHarfBuzz() error. Shaped glyphs' count: %d",
+                 i_total_glyphs );
+        i_ret = VLC_EGENERIC;
+        goto error;
+    }
+
+    p_new_paragraph = NewParagraph( p_filter, i_total_glyphs, 0, 0, 0,
+                                    p_paragraph->i_runs_size );
+    if( !p_new_paragraph )
+    {
+        i_ret = VLC_ENOMEM;
+        goto error;
+    }
+    p_new_paragraph->paragraph_type = p_paragraph->paragraph_type;
+
+    int i_index = 0;
+    for( int i = 0; i < p_paragraph->i_runs_count; ++i )
+    {
+        run_desc_t *p_run = p_paragraph->p_runs + i;
+        hb_glyph_info_t *p_infos = p_run->p_glyph_infos;
+        hb_glyph_position_t *p_positions = p_run->p_glyph_positions;
+        for( unsigned int j = 0; j < p_run->i_glyph_count; ++j )
+        {
+            /*
+             * HarfBuzz reverses the order of glyphs in RTL runs. We reverse
+             * it again here to keep the glyphs in theirs logical order.
+             * For line breaking of paragraphs to work correctly, visual
+             * reordering should be done after line breaking has taken
+             * place.
+             */
+            int i_run_index = p_run->direction == HB_DIRECTION_LTR ?
+                    j : p_run->i_glyph_count - 1 - j;
+            int i_source_index =
+                    p_infos[ i_run_index ].cluster + p_run->i_start_offset;
+
+            p_new_paragraph->p_code_points[ i_index ] = 0;
+            p_new_paragraph->pi_glyph_indices[ i_index ] =
+                p_infos[ i_run_index ].codepoint;
+            p_new_paragraph->p_scripts[ i_index ] =
+                p_paragraph->p_scripts[ i_source_index ];
+            p_new_paragraph->p_types[ i_index ] =
+                p_paragraph->p_types[ i_source_index ];
+            p_new_paragraph->p_levels[ i_index ] =
+                p_paragraph->p_levels[ i_source_index ];
+            p_new_paragraph->pp_styles[ i_index ] =
+                p_paragraph->pp_styles[ i_source_index ];
+            p_new_paragraph->pi_karaoke_bar[ i_index ] =
+                p_paragraph->pi_karaoke_bar[ i_source_index ];
+            p_new_paragraph->p_glyph_bitmaps[ i_index ].i_x_offset =
+                p_positions[ i_run_index ].x_offset;
+            p_new_paragraph->p_glyph_bitmaps[ i_index ].i_y_offset =
+                p_positions[ i_run_index ].y_offset;
+            p_new_paragraph->p_glyph_bitmaps[ i_index ].i_x_advance =
+                p_positions[ i_run_index ].x_advance;
+            p_new_paragraph->p_glyph_bitmaps[ i_index ].i_y_advance =
+                p_positions[ i_run_index ].y_advance;
+
+            ++i_index;
+        }
+        AddRun( p_filter, p_new_paragraph, i_index - p_run->i_glyph_count,
+                i_index, p_run->p_face );
+    }
+
+    for( int i = 0; i < p_paragraph->i_runs_count; ++i )
+    {
+        hb_font_destroy( p_paragraph->p_runs[ i ].p_hb_font );
+        hb_buffer_destroy( p_paragraph->p_runs[ i ].p_buffer );
+    }
+    FreeParagraph( *p_old_paragraph );
+    *p_old_paragraph = p_new_paragraph;
+
+    return VLC_SUCCESS;
+
+error:
+    for( int i = 0; i < p_paragraph->i_runs_count; ++i )
+    {
+        if( p_paragraph->p_runs[ i ].p_hb_font )
+            hb_font_destroy( p_paragraph->p_runs[ i ].p_hb_font );
+        if( p_paragraph->p_runs[ i ].p_buffer )
+            hb_buffer_destroy( p_paragraph->p_runs[ i ].p_buffer );
+    }
+
+    if( p_new_paragraph )
+        FreeParagraph( p_new_paragraph );
+
+    return i_ret;
+}
+#endif
+
+/*
+ * Shape a paragraph with FriBidi.
+ * Shaping with FriBidi is currently limited to mirroring and simple
+ * Arabic shaping.
+ */
+#ifdef HAVE_FRIBIDI
+#ifndef HAVE_HARFBUZZ
+static int ShapeParagraphFriBidi( filter_t *p_filter, paragraph_t *p_paragraph )
+{
+
+    if( p_paragraph->i_size <= 0 )
+    {
+        msg_Err( p_filter,
+                "ShapeParagraphFriBidi() invalid parameters. Paragraph size: %d",
+                 p_paragraph->i_size );
+        return VLC_EGENERIC;
+    }
+
+    FriBidiJoiningType *p_joining_types =
+            malloc( p_paragraph->i_size * sizeof( *p_joining_types ) );
+    if( !p_joining_types )
+        return VLC_ENOMEM;
+
+    fribidi_get_joining_types( p_paragraph->p_code_points,
+                               p_paragraph->i_size, p_joining_types );
+    fribidi_join_arabic( p_paragraph->p_types, p_paragraph->i_size,
+                         p_paragraph->p_levels, p_joining_types );
+    fribidi_shape( FRIBIDI_FLAGS_DEFAULT | FRIBIDI_FLAGS_ARABIC,
+                   p_paragraph->p_levels,
+                   p_paragraph->i_size,
+                   p_joining_types,
+                   p_paragraph->p_code_points );
+
+    free( p_joining_types );
+
+    return VLC_SUCCESS;
+}
+
+/*
+ * Zero-width invisible characters include Unicode control characters and
+ * zero-width spaces among other things. If not removed they can show up in the
+ * text as squares or other glyphs depending on the font. Zero-width spaces are
+ * inserted when shaping with FriBidi, when it performs glyph substitution for
+ * ligatures.
+ */
+static int RemoveZeroWidthCharacters( paragraph_t *p_paragraph )
+{
+    for( int i = 0; i < p_paragraph->i_size; ++i )
+    {
+        uni_char_t ch = p_paragraph->p_code_points[ i ];
+        if( ch == 0xfeff
+         || ch == 0x061c
+         || ( ch >= 0x202a && ch <= 0x202e )
+         || ( ch >= 0x2060 && ch <= 0x2069 )
+         || ( ch >= 0x200b && ch <= 0x200f ) )
+        {
+            glyph_bitmaps_t *p_bitmaps = p_paragraph->p_glyph_bitmaps + i;
+            if( p_bitmaps->p_glyph )
+                FT_Done_Glyph( p_bitmaps->p_glyph );
+            if( p_bitmaps->p_outline )
+                FT_Done_Glyph( p_bitmaps->p_outline );
+            p_bitmaps->p_glyph = 0;
+            p_bitmaps->p_outline = 0;
+            p_bitmaps->p_shadow = 0;
+            p_bitmaps->i_x_advance = 0;
+            p_bitmaps->i_y_advance = 0;
+        }
+    }
+
+    return VLC_SUCCESS;
+}
+
+/*
+ * Set advance values of non-spacing marks to zero. Diacritics are
+ * not positioned correctly but the text is more readable.
+ * For full shaping HarfBuzz is required.
+ */
+static int ZeroNsmAdvance( paragraph_t *p_paragraph )
+{
+    for( int i = 0; i < p_paragraph->i_size; ++i )
+        if( p_paragraph->p_types[ i ] == FRIBIDI_TYPE_NSM )
+        {
+            p_paragraph->p_glyph_bitmaps[ i ].i_x_advance = 0;
+            p_paragraph->p_glyph_bitmaps[ i ].i_y_advance = 0;
+        }
+    return VLC_SUCCESS;
+}
+#endif
+#endif
+
+/*
+ * Load the glyphs of a paragraph. When shaping with HarfBuzz the glyph indices
+ * have already been determined at this point, as well as the advance values.
+ */
+static int LoadGlyphs( filter_t *p_filter, paragraph_t *p_paragraph,
+                       bool b_use_glyph_indices, bool b_overwrite_advance )
+{
+    if( p_paragraph->i_size <= 0 || p_paragraph->i_runs_count <= 0 )
+    {
+        msg_Err( p_filter, "LoadGlyphs() invalid parameters. "
+                 "Paragraph size: %d. Runs count %d", p_paragraph->i_size,
+                 p_paragraph->i_runs_count );
+        return VLC_EGENERIC;
+    }
+
+    filter_sys_t *p_sys = p_filter->p_sys;
+
+    for( int i = 0; i < p_paragraph->i_runs_count; ++i )
+    {
+        run_desc_t *p_run = p_paragraph->p_runs + i;
+        text_style_t *p_style = p_run->p_style;
+
+        FT_Face p_face;
+        if( !p_run->p_face )
+        {
+            p_face = LoadFace( p_filter, p_style );
+            if( !p_face )
+                p_face = p_sys->p_face;
+            p_run->p_face = p_face;
+        }
+        else
+            p_face = p_run->p_face;
+
+        int i_font_width = p_style->i_style_flags & STYLE_HALFWIDTH ?
+                p_style->i_font_size / 2 : p_style->i_font_size;
+
+        if( FT_Set_Pixel_Sizes( p_face, i_font_width, p_style->i_font_size ) )
+            msg_Err( p_filter,
+                     "Failed to set font size to %d", p_style->i_font_size );
+
+        if( p_sys->p_stroker )
+        {
+            double f_outline_thickness =
+                var_InheritInteger( p_filter, "freetype-outline-thickness" ) / 100.0;
+            f_outline_thickness = VLC_CLIP( f_outline_thickness, 0.0, 0.5 );
+            int i_radius = ( p_style->i_font_size << 6 ) * f_outline_thickness;
+            FT_Stroker_Set( p_sys->p_stroker,
+                            i_radius,
+                            FT_STROKER_LINECAP_ROUND,
+                            FT_STROKER_LINEJOIN_ROUND, 0 );
+        }
+
+        for( int j = p_run->i_start_offset; j < p_run->i_end_offset; ++j )
+        {
+            int i_glyph_index;
+            if( b_use_glyph_indices )
+                i_glyph_index = p_paragraph->pi_glyph_indices[ j ];
+            else
+                i_glyph_index =
+                    FT_Get_Char_Index( p_face, p_paragraph->p_code_points[ j ] );
+
+            glyph_bitmaps_t *p_bitmaps = p_paragraph->p_glyph_bitmaps + j;
+
+            if( FT_Load_Glyph( p_face, i_glyph_index,
+                               FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT )
+             && FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT ) )
+            {
+                p_bitmaps->p_glyph = 0;
+                p_bitmaps->p_outline = 0;
+                p_bitmaps->p_shadow = 0;
+                p_bitmaps->i_x_advance = 0;
+                p_bitmaps->i_y_advance = 0;
+                continue;
+            }
+
+            if( ( p_style->i_style_flags & STYLE_BOLD )
+                  && !( p_face->style_flags & FT_STYLE_FLAG_BOLD ) )
+                FT_GlyphSlot_Embolden( p_face->glyph );
+            if( ( p_style->i_style_flags & STYLE_ITALIC )
+                  && !( p_face->style_flags & FT_STYLE_FLAG_ITALIC ) )
+                FT_GlyphSlot_Oblique( p_face->glyph );
+
+            if( FT_Get_Glyph( p_face->glyph, &p_bitmaps->p_glyph ) )
+            {
+                p_bitmaps->p_glyph = 0;
+                p_bitmaps->p_outline = 0;
+                p_bitmaps->p_shadow = 0;
+                p_bitmaps->i_x_advance = 0;
+                p_bitmaps->i_y_advance = 0;
+                continue;
+            }
+
+            if( p_filter->p_sys->p_stroker )
+            {
+                p_bitmaps->p_outline = p_bitmaps->p_glyph;
+                if( FT_Glyph_StrokeBorder( &p_bitmaps->p_outline,
+                                           p_filter->p_sys->p_stroker, 0, 0 ) )
+                    p_bitmaps->p_outline = 0;
+            }
+
+            if( p_filter->p_sys->style.i_shadow_alpha > 0 )
+                p_bitmaps->p_shadow = p_bitmaps->p_outline ?
+                                      p_bitmaps->p_outline : p_bitmaps->p_glyph;
+
+            if( b_overwrite_advance )
+            {
+                p_bitmaps->i_x_advance = p_face->glyph->advance.x;
+                p_bitmaps->i_y_advance = p_face->glyph->advance.y;
+            }
+        }
+    }
+    return VLC_SUCCESS;
+}
-- 
1.9.1




More information about the vlc-devel mailing list