[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