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

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


New functions:

NewLayoutLine()
LayoutParagraph()
LayoutText()
---
 modules/text_renderer/text_layout.c | 420 ++++++++++++++++++++++++++++++++++++
 1 file changed, 420 insertions(+)

diff --git a/modules/text_renderer/text_layout.c b/modules/text_renderer/text_layout.c
index aeb0919..cb441d4 100644
--- a/modules/text_renderer/text_layout.c
+++ b/modules/text_renderer/text_layout.c
@@ -851,3 +851,423 @@ static int LoadGlyphs( filter_t *p_filter, paragraph_t *p_paragraph,
     }
     return VLC_SUCCESS;
 }
+
+static int NewLayoutLine( filter_t *p_filter,
+                          paragraph_t *p_paragraph,
+                          int i_start_offset, int i_end_offset,
+                          line_desc_t **pp_line )
+{
+    if( p_paragraph->i_size <= 0 || p_paragraph->i_runs_count <= 0
+     || 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,
+                 "NewLayoutLine() invalid parameters. "
+                 "Paragraph size: %d. Runs count: %d. "
+                 "Start offset: %d. End offset: %d",
+                 p_paragraph->i_size, p_paragraph->i_runs_count,
+                 i_start_offset, i_end_offset );
+        return VLC_EGENERIC;
+    }
+
+    line_desc_t *p_line = NewLine( i_end_offset - i_start_offset );
+    filter_sys_t *p_sys = p_filter->p_sys;
+    int i_last_run = -1;
+    run_desc_t *p_run = 0;
+    text_style_t *p_style = 0;
+    FT_Face p_face = 0;
+    FT_Vector pen = { .x = 0, .y = 0 };
+    int i_line_index = 0;
+
+    int i_font_width = 0;
+    int i_ul_offset = 0;
+    int i_ul_thickness = 0;
+
+#ifdef HAVE_FRIBIDI
+    fribidi_reorder_line( 0, p_paragraph->p_types + i_start_offset,
+                          i_end_offset - i_start_offset,
+                          0, p_paragraph->paragraph_type,
+                          p_paragraph->p_levels + i_start_offset,
+                          0, p_paragraph->pi_reordered_indices + i_start_offset );
+#endif
+
+    for( int i = i_start_offset; i < i_end_offset; ++i, ++i_line_index )
+    {
+        int i_paragraph_index;
+#ifdef HAVE_FRIBIDI
+        i_paragraph_index = p_paragraph->pi_reordered_indices[ i ];
+#else
+        i_paragraph_index = i;
+#endif
+
+        line_character_t *p_ch = p_line->p_character + i_line_index;
+        glyph_bitmaps_t *p_bitmaps =
+                p_paragraph->p_glyph_bitmaps + i_paragraph_index;
+
+        if( !p_bitmaps->p_glyph )
+        {
+            --i_line_index;
+            continue;
+        }
+
+        if( i_last_run != p_paragraph->pi_run_ids[ i_paragraph_index ] )
+        {
+            i_last_run = p_paragraph->pi_run_ids[ i_paragraph_index ];
+            p_run = p_paragraph->p_runs + i_last_run;
+            p_style = p_run->p_style;
+            p_face = p_run->p_face;
+
+            i_font_width = p_style->i_style_flags & STYLE_HALFWIDTH ?
+                           p_style->i_font_size / 2 : p_style->i_font_size;
+        }
+
+        FT_Vector pen_new = {
+            .x = pen.x + p_paragraph->p_glyph_bitmaps[ i_paragraph_index ].i_x_offset,
+            .y = pen.y + p_paragraph->p_glyph_bitmaps[ i_paragraph_index ].i_y_offset
+        };
+        FT_Vector pen_shadow = {
+            .x = pen_new.x + p_sys->f_shadow_vector_x * ( i_font_width << 6 ),
+            .y = pen_new.y + p_sys->f_shadow_vector_y * ( p_style->i_font_size << 6 )
+        };
+
+        if( p_bitmaps->p_shadow )
+        {
+            if( FT_Glyph_To_Bitmap( &p_bitmaps->p_shadow, FT_RENDER_MODE_NORMAL,
+                                    &pen_shadow, 0 ) )
+                p_bitmaps->p_shadow = 0;
+            else
+                FT_Glyph_Get_CBox( p_bitmaps->p_shadow, ft_glyph_bbox_pixels,
+                                   &p_bitmaps->shadow_bbox );
+        }
+        if( p_bitmaps->p_glyph )
+        {
+            if( FT_Glyph_To_Bitmap( &p_bitmaps->p_glyph, FT_RENDER_MODE_NORMAL,
+                                    &pen_new, 1 ) )
+            {
+                FT_Done_Glyph( p_bitmaps->p_glyph );
+                if( p_bitmaps->p_outline )
+                    FT_Done_Glyph( p_bitmaps->p_outline );
+                if( p_bitmaps->p_shadow )
+                    FT_Done_Glyph( p_bitmaps->p_shadow );
+                --i_line_index;
+                continue;
+            }
+            else
+                FT_Glyph_Get_CBox( p_bitmaps->p_glyph, ft_glyph_bbox_pixels,
+                                   &p_bitmaps->glyph_bbox );
+        }
+        if( p_bitmaps->p_outline )
+        {
+            if( FT_Glyph_To_Bitmap( &p_bitmaps->p_outline, FT_RENDER_MODE_NORMAL,
+                                    &pen_new, 1 ) )
+            {
+                FT_Done_Glyph( p_bitmaps->p_outline );
+                p_bitmaps->p_outline = 0;
+            }
+            else
+                FT_Glyph_Get_CBox( p_bitmaps->p_outline, ft_glyph_bbox_pixels,
+                                   &p_bitmaps->outline_bbox );
+        }
+
+        FixGlyph( p_bitmaps->p_glyph, &p_bitmaps->glyph_bbox,
+                  p_face, &pen_new );
+        if( p_bitmaps->p_outline )
+            FixGlyph( p_bitmaps->p_outline, &p_bitmaps->outline_bbox,
+                      p_face, &pen_new );
+        if( p_bitmaps->p_shadow )
+            FixGlyph( p_bitmaps->p_shadow, &p_bitmaps->shadow_bbox,
+                      p_face, &pen_shadow );
+
+        int i_line_offset    = 0;
+        int i_line_thickness = 0;
+        text_style_t *p_glyph_style = p_paragraph->pp_styles[ i_paragraph_index ];
+        if( p_glyph_style->i_style_flags & (STYLE_UNDERLINE | STYLE_STRIKEOUT) )
+        {
+            i_line_offset =
+                abs( FT_FLOOR( FT_MulFix( p_face->underline_position,
+                                          p_face->size->metrics.y_scale ) ) );
+
+            i_line_thickness =
+                abs( FT_CEIL( FT_MulFix( p_face->underline_thickness,
+                                         p_face->size->metrics.y_scale ) ) );
+
+            if( p_glyph_style->i_style_flags & STYLE_STRIKEOUT )
+            {
+                /* Move the baseline to make it strikethrough instead of
+                 * underline. That means that strikethrough takes precedence
+                 */
+                i_line_offset -=
+                    abs( FT_FLOOR( FT_MulFix( p_face->descender * 2,
+                                              p_face->size->metrics.y_scale ) ) );
+            }
+            else if( i_line_thickness > 0 )
+            {
+                p_bitmaps->glyph_bbox.yMin =
+                    __MIN( p_bitmaps->glyph_bbox.yMin,
+                           - i_line_offset - i_line_thickness );
+
+                /* The real underline thickness and position are
+                 * updated once the whole line has been parsed */
+                i_ul_offset = __MAX( i_ul_offset, i_line_offset );
+                i_ul_thickness = __MAX( i_ul_thickness, i_line_thickness );
+                i_line_thickness = -1;
+            }
+        }
+
+        p_ch->p_glyph = ( FT_BitmapGlyph ) p_bitmaps->p_glyph;
+        p_ch->p_outline = ( FT_BitmapGlyph ) p_bitmaps->p_outline;
+        p_ch->p_shadow = ( FT_BitmapGlyph ) p_bitmaps->p_shadow;
+
+        bool b_karaoke = p_paragraph->pi_karaoke_bar[ i_paragraph_index ] != 0;
+        p_ch->i_color = b_karaoke ?
+            p_paragraph->pp_styles[ i_paragraph_index ]->i_karaoke_background_color
+          | p_paragraph->pp_styles[ i_paragraph_index ]->i_karaoke_background_alpha << 24
+          : p_paragraph->pp_styles[ i_paragraph_index ]->i_font_color
+          | p_paragraph->pp_styles[ i_paragraph_index ]->i_font_alpha << 24;
+
+        p_ch->i_line_thickness = i_line_thickness;
+        p_ch->i_line_offset = i_line_offset;
+
+        BBoxEnlarge( &p_line->bbox, &p_bitmaps->glyph_bbox );
+        if( p_bitmaps->p_outline )
+            BBoxEnlarge( &p_line->bbox, &p_bitmaps->outline_bbox );
+        if( p_bitmaps->p_shadow )
+            BBoxEnlarge( &p_line->bbox, &p_bitmaps->shadow_bbox );
+
+        pen.x += p_bitmaps->i_x_advance;
+        pen.y += p_bitmaps->i_y_advance;
+    }
+
+    p_line->i_width = __MAX( 0, p_line->bbox.xMax - p_line->bbox.xMin );
+    p_line->i_height = __MAX( 0, p_line->bbox.yMax - p_line->bbox.yMin );
+    p_line->i_character_count = i_line_index;
+
+    if( i_ul_thickness > 0 )
+    {
+        for( int i = 0; i < p_line->i_character_count; i++ )
+        {
+            line_character_t *ch = &p_line->p_character[i];
+            if( ch->i_line_thickness < 0 )
+            {
+                ch->i_line_offset    = i_ul_offset;
+                ch->i_line_thickness = i_ul_thickness;
+            }
+        }
+    }
+
+    *pp_line = p_line;
+    return VLC_SUCCESS;
+}
+
+static int LayoutParagraph( filter_t *p_filter, paragraph_t *p_paragraph,
+                            int i_max_width, line_desc_t **pp_lines )
+{
+    if( p_paragraph->i_size <= 0 || p_paragraph->i_runs_count <= 0 )
+    {
+        msg_Err( p_filter, "LayoutParagraph() invalid parameters. "
+                 "Paragraph size: %d. Runs count %d",
+                 p_paragraph->i_size, p_paragraph->i_runs_count );
+        return VLC_EGENERIC;
+    }
+
+    int i_line_start = 0;
+    FT_Pos i_width = 0;
+    int i_last_space = -1;
+    line_desc_t *p_first_line = 0;
+    line_desc_t **pp_line = &p_first_line;
+
+#ifdef HAVE_FRIBIDI
+    for( int i = 0; i < p_paragraph->i_size; ++i )
+        p_paragraph->pi_reordered_indices[ i ] = i;
+#endif
+
+    for( int i = 0; i <= p_paragraph->i_size; ++i )
+    {
+        if( i == p_paragraph->i_size )
+        {
+            if( i_line_start < i )
+                if( NewLayoutLine( p_filter, p_paragraph,
+                                   i_line_start, i, pp_line ) )
+                    goto error;
+
+            break;
+        }
+
+        if( p_paragraph->p_code_points[ i ] == ' '
+#ifdef HAVE_FRIBIDI
+            || p_paragraph->p_types[ i ] == FRIBIDI_TYPE_WS
+#endif
+          )
+        {
+            if( i_line_start == i )
+            {
+                /*
+                 * Free orphaned white space glyphs not belonging to any lines.
+                 * At this point p_shadow points to either p_glyph or p_outline,
+                 * so we should not free it explicitly.
+                 */
+                if( p_paragraph->p_glyph_bitmaps[ i ].p_glyph )
+                    FT_Done_Glyph( p_paragraph->p_glyph_bitmaps[ i ].p_glyph );
+                if( p_paragraph->p_glyph_bitmaps[ i ].p_outline )
+                    FT_Done_Glyph( p_paragraph->p_glyph_bitmaps[ i ].p_outline );
+
+                i_line_start = i + 1;
+                continue;
+            }
+
+            if( i_last_space == i - 1 )
+            {
+                p_paragraph->p_glyph_bitmaps[ i - 1 ].i_x_advance = 0;
+                i_last_space = i;
+                continue;
+            }
+
+            i_last_space = i;
+        }
+
+        i_width += p_paragraph->p_glyph_bitmaps[ i ].i_x_advance;
+
+        if( FT_CEIL( i_width ) >= i_max_width )
+        {
+            if( i_line_start == i )
+            {
+                msg_Err( p_filter,
+                         "LayoutParagraph(): Width of single glyph exceeds maximum" );
+                goto error;
+            }
+
+            int i_end_offset;
+            if( i_last_space > i_line_start )
+                i_end_offset = i_last_space;
+            else
+                i_end_offset = i;
+
+            if( NewLayoutLine( p_filter, p_paragraph, i_line_start,
+                               i_end_offset, pp_line ) )
+                goto error;
+
+            pp_line = &( *pp_line )->p_next;
+            i_line_start = i_end_offset;
+            i = i_line_start - 1;
+            i_width = 0;
+        }
+    }
+
+    *pp_lines = p_first_line;
+    return VLC_SUCCESS;
+
+error:
+    if( p_first_line )
+        FreeLines( p_first_line );
+    return VLC_EGENERIC;
+}
+
+int LayoutText( filter_t *p_filter, line_desc_t **pp_lines,
+                FT_BBox *p_bbox, int *pi_max_face_height,
+
+                uni_char_t *psz_text, text_style_t **pp_styles,
+                uint32_t *pi_k_dates, int i_len )
+{
+    line_desc_t *p_first_line = 0;
+    line_desc_t **pp_line = &p_first_line;
+    paragraph_t *p_paragraph = 0;
+    int i_paragraph_start = 0;
+    int i_max_height = 0;
+
+    for( int i = 0; i <= i_len; ++i )
+    {
+        if( i == i_len || psz_text[ i ] == '\n' )
+        {
+            if( i_paragraph_start == i )
+            {
+                i_paragraph_start = i + 1;
+                continue;
+            }
+
+            p_paragraph = NewParagraph( p_filter, i - i_paragraph_start,
+                                        psz_text + i_paragraph_start,
+                                        pp_styles + i_paragraph_start,
+                                        pi_k_dates ?
+                                        pi_k_dates + i_paragraph_start : 0,
+                                        20 );
+            if( !p_paragraph )
+            {
+                if( p_first_line ) FreeLines( p_first_line );
+                return VLC_ENOMEM;
+            }
+
+#ifdef HAVE_FRIBIDI
+            if( AnalyzeParagraph( p_paragraph ) )
+                goto error;
+#endif
+
+            if( ItemizeParagraph( p_filter, p_paragraph ) )
+                goto error;
+
+#if defined HAVE_HARFBUZZ
+            if( ShapeParagraphHarfBuzz( p_filter, &p_paragraph ) )
+                goto error;
+
+            if( LoadGlyphs( p_filter, p_paragraph, true, false ) )
+                goto error;
+
+#elif defined HAVE_FRIBIDI
+            if( ShapeParagraphFriBidi( p_filter, p_paragraph ) )
+                goto error;
+            if( LoadGlyphs( p_filter, p_paragraph, false, true ) )
+                goto error;
+            if( RemoveZeroWidthCharacters( p_paragraph ) )
+                goto error;
+            if( ZeroNsmAdvance( p_paragraph ) )
+                goto error;
+#else
+            if( LoadGlyphs( p_filter, p_paragraph, false, true ) )
+                goto error;
+#endif
+
+            /*
+             * Set max line width to allow for outline and shadow glyphs,
+             * and any extra width caused by visual reordering
+             */
+            int i_max_width = ( int ) p_filter->fmt_out.video.i_visible_width
+                              - 2 * p_filter->p_sys->style.i_font_size;
+            if( LayoutParagraph( p_filter, p_paragraph,
+                                 i_max_width, pp_line ) )
+                goto error;
+
+            for( ; *pp_line; pp_line = &( *pp_line )->p_next )
+                i_max_height = __MAX( i_max_height, ( *pp_line )->i_height );
+
+            i_paragraph_start = i + 1;
+        }
+    }
+
+    int i_base_line = 0;
+    FT_BBox bbox = {
+        .xMin = INT_MAX,
+        .yMin = INT_MAX,
+        .xMax = INT_MIN,
+        .yMax = INT_MIN
+    };
+
+    for( line_desc_t *p_line = p_first_line; p_line; p_line = p_line->p_next )
+    {
+        p_line->i_base_line = i_base_line;
+        p_line->bbox.yMin -= i_base_line;
+        p_line->bbox.yMax -= i_base_line;
+        BBoxEnlarge( &bbox, &p_line->bbox );
+
+        i_base_line += i_max_height;
+    }
+
+    *pp_lines = p_first_line;
+    *p_bbox = bbox;
+    *pi_max_face_height = i_max_height;
+    return VLC_SUCCESS;
+
+error:
+    if( p_first_line ) FreeLines( p_first_line );
+    if( p_paragraph ) FreeParagraph( p_paragraph );
+    return VLC_EGENERIC;
+}
-- 
1.9.1




More information about the vlc-devel mailing list