nuiRect nuiFlowView::Layout(bool setLayout, float IdealWidth) { float x = 0; float y = 0; float VSpace = mVSpace; float HSpace = mHSpace; nuiWidgetList line; int32 count = mpChildren.size(); float w = 0; float h = 0; for (int32 i = 0; i < count; i++) { nuiWidgetPtr pWidget = mpChildren[i]; nuiRect r(pWidget->GetIdealRect()); if (w + r.GetWidth() > IdealWidth) { LayoutLine(line, x, y, w, h, HSpace, VSpace, setLayout); } h = MAX(h, r.GetHeight()); w += r.GetWidth(); if (!line.empty()) w += HSpace; line.push_back(pWidget); } if (!line.empty()) LayoutLine(line, x, y, w, h, HSpace, VSpace, setLayout); if (y > 0) y -= VSpace; return nuiRect(IdealWidth, y); }
static int LayoutLine( 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, "LayoutLine() 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 ); if( !p_line ) return VLC_ENOMEM; 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_bitmaps->i_x_advance, p_bitmaps->i_y_advance, &pen_new ); if( p_bitmaps->p_outline ) FixGlyph( p_bitmaps->p_outline, &p_bitmaps->outline_bbox, p_bitmaps->i_x_advance, p_bitmaps->i_y_advance, &pen_new ); if( p_bitmaps->p_shadow ) FixGlyph( p_bitmaps->p_shadow, &p_bitmaps->shadow_bbox, p_bitmaps->i_x_advance, p_bitmaps->i_y_advance, &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 ? ( uint32_t ) p_glyph_style->i_karaoke_background_color | p_glyph_style->i_karaoke_background_alpha << 24 : ( uint32_t ) p_glyph_style->i_font_color | p_glyph_style->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_pixel_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; FT_Pos i_max_width = i_max_pixel_width << 6; FT_Pos i_preferred_width = 0; FT_Pos i_total_width = 0; FT_Pos i_last_space_width = 0; int i_last_space = -1; line_desc_t *p_first_line = 0; line_desc_t **pp_line = &p_first_line; for( int i = 0; i < p_paragraph->i_size; ++i ) { #ifdef HAVE_FRIBIDI p_paragraph->pi_reordered_indices[ i ] = i; #endif i_total_width += p_paragraph->p_glyph_bitmaps[ i ].i_x_advance; } int i_line_count = i_total_width / i_max_width + 1; i_preferred_width = i_total_width / i_line_count; for( int i = 0; i <= p_paragraph->i_size; ++i ) { if( i == p_paragraph->i_size ) { if( i_line_start < i ) if( LayoutLine( 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_last_space_width = i_width; } i_width += p_paragraph->p_glyph_bitmaps[ i ].i_x_advance; if( i_last_space_width >= i_preferred_width || 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( LayoutLine( 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; i_last_space_width = 0; } } *pp_lines = p_first_line; return VLC_SUCCESS; error: for( int i = i_line_start; i < p_paragraph->i_size; ++i ) { 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 ); } 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; FreeParagraph( p_paragraph ); p_paragraph = 0; 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; }