From 581dc813588a31d4ff483fa16313cb39782474ee Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Tue, 3 Dec 2019 17:45:02 +0000 Subject: [PATCH] Performance optimizations for the stroke font. --- common/gal/stroke_font.cpp | 99 ++++++++++++++++++++++++-------------- include/gal/stroke_font.h | 10 ++-- 2 files changed, 69 insertions(+), 40 deletions(-) diff --git a/common/gal/stroke_font.cpp b/common/gal/stroke_font.cpp index 85d3dfa344..4b8054d988 100644 --- a/common/gal/stroke_font.cpp +++ b/common/gal/stroke_font.cpp @@ -39,6 +39,11 @@ const double STROKE_FONT::BOLD_FACTOR = 1.3; const double STROKE_FONT::STROKE_FONT_SCALE = 1.0 / 21.0; const double STROKE_FONT::ITALIC_TILT = 1.0 / 8; + +GLYPH_LIST* g_newStrokeFontGlyphs = nullptr; ///< Glyph list +std::vector* g_newStrokeFontGlyphBoundingBoxes; ///< Bounding boxes of the glyphs + + STROKE_FONT::STROKE_FONT( GAL* aGal ) : m_gal( aGal ) { @@ -47,41 +52,63 @@ STROKE_FONT::STROKE_FONT( GAL* aGal ) : bool STROKE_FONT::LoadNewStrokeFont( const char* const aNewStrokeFont[], int aNewStrokeFontSize ) { - m_glyphs.clear(); - m_glyphBoundingBoxes.clear(); - m_glyphs.resize( aNewStrokeFontSize ); - m_glyphBoundingBoxes.resize( aNewStrokeFontSize ); + if( g_newStrokeFontGlyphs ) + { + m_glyphs = g_newStrokeFontGlyphs; + m_glyphBoundingBoxes = g_newStrokeFontGlyphBoundingBoxes; + return true; + } + + g_newStrokeFontGlyphs = new GLYPH_LIST; + g_newStrokeFontGlyphs->resize( aNewStrokeFontSize ); + + g_newStrokeFontGlyphBoundingBoxes = new std::vector; + g_newStrokeFontGlyphBoundingBoxes->resize( aNewStrokeFontSize ); for( int j = 0; j < aNewStrokeFontSize; j++ ) { - GLYPH& glyph = m_glyphs[j]; + GLYPH& glyph = g_newStrokeFontGlyphs->at( j ); double glyphStartX = 0.0; double glyphEndX = 0.0; - VECTOR2D glyphBoundingX; + double glyphWidth; std::vector* pointList = nullptr; + int strokes = 0; int i = 0; + while( aNewStrokeFont[j][i] ) + { + if( aNewStrokeFont[j][i] == ' ' && aNewStrokeFont[j][i+1] == 'R' ) + strokes++; + + i += 2; + } + + glyph.reserve( strokes + 1 ); + + i = 0; + while( aNewStrokeFont[j][i] ) { VECTOR2D point( 0.0, 0.0 ); char coordinate[2] = { 0, }; - for( int k = 0; k < 2; k++ ) - { + for( int k : { 0, 1 } ) coordinate[k] = aNewStrokeFont[j][i + k]; - } if( i < 2 ) { // The first two values contain the width of the char - glyphStartX = ( coordinate[0] - 'R' ) * STROKE_FONT_SCALE; - glyphEndX = ( coordinate[1] - 'R' ) * STROKE_FONT_SCALE; - glyphBoundingX = VECTOR2D( 0, glyphEndX - glyphStartX ); + glyphStartX = ( coordinate[0] - 'R' ) * STROKE_FONT_SCALE; + glyphEndX = ( coordinate[1] - 'R' ) * STROKE_FONT_SCALE; + glyphWidth = glyphEndX - glyphStartX; } else if( ( coordinate[0] == ' ' ) && ( coordinate[1] == 'R' ) ) { + if( pointList ) + pointList->resize( pointList->size() ); + // Raise pen pointList = nullptr; } @@ -97,14 +124,14 @@ bool STROKE_FONT::LoadNewStrokeFont( const char* const aNewStrokeFont[], int aNe // * a few shapes have a height slightly bigger than 1.0 ( like '{' '[' ) point.x = (double) ( coordinate[0] - 'R' ) * STROKE_FONT_SCALE - glyphStartX; #define FONT_OFFSET -10 - // FONT_OFFSET is here for historical reasons, due to the way the stroke font + // FONT_OFFSET is here for historical reasons, due to the way the stroke font // was built. It allows shapes coordinates like W M ... to be >= 0 // Only shapes like j y have coordinates < 0 point.y = (double) ( coordinate[1] - 'R' + FONT_OFFSET ) * STROKE_FONT_SCALE; if( !pointList ) { - glyph.emplace_back( std::vector() ); + glyph.emplace_back(); pointList = &glyph.back(); } @@ -114,10 +141,15 @@ bool STROKE_FONT::LoadNewStrokeFont( const char* const aNewStrokeFont[], int aNe i += 2; } + if( pointList ) + pointList->resize( pointList->size() ); + // Compute the bounding box of the glyph - m_glyphBoundingBoxes[j] = computeBoundingBox( glyph, glyphBoundingX ); + g_newStrokeFontGlyphBoundingBoxes->at( j ) = computeBoundingBox( glyph, glyphWidth ); } + m_glyphs = g_newStrokeFontGlyphs; + m_glyphBoundingBoxes = g_newStrokeFontGlyphBoundingBoxes; return true; } @@ -131,24 +163,21 @@ double STROKE_FONT::GetInterline( double aGlyphHeight ) } -BOX2D STROKE_FONT::computeBoundingBox( const GLYPH& aGLYPH, const VECTOR2D& aGLYPHBoundingX ) const +BOX2D STROKE_FONT::computeBoundingBox( const GLYPH& aGLYPH, double aGlyphWidth ) const { - BOX2D boundingBox; - - std::vector boundingPoints; - - boundingPoints.emplace_back( VECTOR2D( aGLYPHBoundingX.x, 0 ) ); - boundingPoints.emplace_back( VECTOR2D( aGLYPHBoundingX.y, 0 ) ); + VECTOR2D min( 0, 0 ); + VECTOR2D max( aGlyphWidth, 0 ); for( const std::vector& pointList : aGLYPH ) { for( const VECTOR2D& point : pointList ) - boundingPoints.emplace_back( aGLYPHBoundingX.x, point.y ); + { + min.y = std::min( min.y, point.y ); + max.y = std::max( max.y, point.y ); + } } - boundingBox.Compute( boundingPoints ); - - return boundingBox; + return BOX2D( min, max - min ); } @@ -306,7 +335,7 @@ void STROKE_FONT::drawSingleLineText( const UTF8& aText, int markupFlags ) // The choice of spaces is somewhat arbitrary but sufficient for aligning text if( *chIt == '\t' ) { - double space = glyphSize.x * m_glyphBoundingBoxes[0].GetEnd().x; + double space = glyphSize.x * m_glyphBoundingBoxes->at( 0 ).GetEnd().x; // We align to the 4th column (fmod) but only need to account for 3 of // the four spaces here with the extra. This ensures that we have at @@ -388,14 +417,14 @@ void STROKE_FONT::drawSingleLineText( const UTF8& aText, int markupFlags ) // Index into bounding boxes table int dd = (signed) *chIt - ' '; - if( dd >= (int) m_glyphBoundingBoxes.size() || dd < 0 ) + if( dd >= (int) m_glyphBoundingBoxes->size() || dd < 0 ) { int substitute = *chIt == '\t' ? ' ' : '?'; dd = substitute - ' '; } - GLYPH& glyph = m_glyphs[dd]; - BOX2D& bbox = m_glyphBoundingBoxes[dd]; + const GLYPH& glyph = m_glyphs->at( dd ); + const BOX2D& bbox = m_glyphBoundingBoxes->at( dd ); if( in_overbar ) { @@ -422,11 +451,11 @@ void STROKE_FONT::drawSingleLineText( const UTF8& aText, int markupFlags ) last_had_overbar = false; } - for( std::vector& ptList : glyph ) + for( const std::vector& ptList : glyph ) { std::deque ptListScaled; - for( VECTOR2D& pt : ptList ) + for( const VECTOR2D& pt : ptList ) { VECTOR2D scaledPt( pt.x * glyphSize.x + xOffset, pt.y * glyphSize.y + yOffset ); @@ -501,7 +530,7 @@ VECTOR2D STROKE_FONT::ComputeStringBoundaryLimits( const UTF8& aText, const VECT // The choice of spaces is somewhat arbitrary but sufficient for aligning text if( *it == '\t' ) { - double spaces = m_glyphBoundingBoxes[0].GetEnd().x; + double spaces = m_glyphBoundingBoxes->at( 0 ).GetEnd().x; double addlSpace = 3.0 * spaces - std::fmod( curX, 4.0 * spaces ); // Add the remaining space (between 0 and 3 spaces) @@ -576,13 +605,13 @@ VECTOR2D STROKE_FONT::ComputeStringBoundaryLimits( const UTF8& aText, const VECT // Index in the bounding boxes table int dd = (signed) *it - ' '; - if( dd >= (int) m_glyphBoundingBoxes.size() || dd < 0 ) + if( dd >= (int) m_glyphBoundingBoxes->size() || dd < 0 ) { int substitute = *it == '\t' ? ' ' : '?'; dd = substitute - ' '; } - const BOX2D& box = m_glyphBoundingBoxes[dd]; + const BOX2D& box = m_glyphBoundingBoxes->at( dd ); curX += box.GetEnd().x * curScale; } diff --git a/include/gal/stroke_font.h b/include/gal/stroke_font.h index 98d7869302..16cb8a8dc0 100644 --- a/include/gal/stroke_font.h +++ b/include/gal/stroke_font.h @@ -120,9 +120,9 @@ public: private: - GAL* m_gal; ///< Pointer to the GAL - GLYPH_LIST m_glyphs; ///< Glyph list - std::vector m_glyphBoundingBoxes; ///< Bounding boxes of the glyphs + GAL* m_gal; ///< Pointer to the GAL + const GLYPH_LIST* m_glyphs; ///< Glyph list + const std::vector* m_glyphBoundingBoxes; ///< Bounding boxes of the glyphs /** * @brief Compute the X and Y size of a given text. The text is expected to be @@ -145,10 +145,10 @@ private: * @brief Compute the bounding box of a given glyph. * * @param aGlyph is the glyph. - * @param aGlyphBoundingX is the x-component of the bounding box size. + * @param aGlyphWidth is the x-component of the bounding box size. * @return is the complete bounding box size. */ - BOX2D computeBoundingBox( const GLYPH& aGlyph, const VECTOR2D& aGlyphBoundingX ) const; + BOX2D computeBoundingBox( const GLYPH& aGlyph, double aGlyphWidth ) const; /** * @brief Draws a single line of text. Multiline texts should be split before using the