Performance enhancements for fonts.

This commit is contained in:
Jeff Young 2022-01-07 17:42:43 +00:00
parent 438c63f587
commit 04c76f10e9
30 changed files with 582 additions and 470 deletions

View File

@ -303,7 +303,6 @@ set( FONT_SRCS
font/stroke_font.cpp
font/outline_font.cpp
font/outline_decomposer.cpp
font/triangulate.cpp
font/fontconfig.cpp
)

View File

@ -107,7 +107,8 @@ GR_TEXT_V_ALIGN_T EDA_TEXT::MapVertJustify( int aVertJustify )
EDA_TEXT::EDA_TEXT( const wxString& text ) :
m_text( text )
m_text( text ),
m_bounding_box_cache_valid( false )
{
int sz = Mils2iu( DEFAULT_SIZE_TEXT );
SetTextSize( wxSize( sz, sz ) );
@ -134,6 +135,9 @@ EDA_TEXT::EDA_TEXT( const EDA_TEXT& aText )
KIFONT::OUTLINE_GLYPH* outline_glyph = static_cast<KIFONT::OUTLINE_GLYPH*>( glyph.get() );
m_render_cache.emplace_back( std::make_unique<KIFONT::OUTLINE_GLYPH>( *outline_glyph ) );
}
m_bounding_box_cache_valid = aText.m_bounding_box_cache_valid;
m_bounding_box_cache = aText.m_bounding_box_cache;
}
@ -162,6 +166,9 @@ EDA_TEXT& EDA_TEXT::operator=( const EDA_TEXT& aText )
m_render_cache.emplace_back( std::make_unique<KIFONT::OUTLINE_GLYPH>( *outline_glyph ) );
}
m_bounding_box_cache_valid = aText.m_bounding_box_cache_valid;
m_bounding_box_cache = aText.m_bounding_box_cache;
return *this;
}
@ -169,7 +176,11 @@ EDA_TEXT& EDA_TEXT::operator=( const EDA_TEXT& aText )
void EDA_TEXT::SetText( const wxString& aText )
{
m_text = aText;
cacheShownText();
ClearRenderCache();
m_bounding_box_cache_valid = false;
}
@ -179,77 +190,87 @@ void EDA_TEXT::CopyText( const EDA_TEXT& aSrc )
m_shown_text = aSrc.m_shown_text;
m_shown_text_has_text_var_refs = aSrc.m_shown_text_has_text_var_refs;
m_render_cache.clear();
ClearRenderCache();
m_bounding_box_cache_valid = false;
}
void EDA_TEXT::SetTextThickness( int aWidth )
{
m_attributes.m_StrokeWidth = aWidth;
m_render_cache.clear();
ClearRenderCache();
m_bounding_box_cache_valid = false;
}
void EDA_TEXT::SetTextAngle( const EDA_ANGLE& aAngle )
{
m_attributes.m_Angle = aAngle;
m_render_cache.clear();
ClearRenderCache();
m_bounding_box_cache_valid = false;
}
void EDA_TEXT::SetItalic( bool aItalic )
{
m_attributes.m_Italic = aItalic;
m_render_cache.clear();
ClearRenderCache();
m_bounding_box_cache_valid = false;
}
void EDA_TEXT::SetBold( bool aBold )
{
m_attributes.m_Bold = aBold;
m_render_cache.clear();
ClearRenderCache();
m_bounding_box_cache_valid = false;
}
void EDA_TEXT::SetVisible( bool aVisible )
{
m_attributes.m_Visible = aVisible;
m_render_cache.clear();
ClearRenderCache();
}
void EDA_TEXT::SetMirrored( bool isMirrored )
{
m_attributes.m_Mirrored = isMirrored;
m_render_cache.clear();
ClearRenderCache();
m_bounding_box_cache_valid = false;
}
void EDA_TEXT::SetMultilineAllowed( bool aAllow )
{
m_attributes.m_Multiline = aAllow;
m_render_cache.clear();
ClearRenderCache();
m_bounding_box_cache_valid = false;
}
void EDA_TEXT::SetHorizJustify( GR_TEXT_H_ALIGN_T aType )
{
m_attributes.m_Halign = aType;
m_render_cache.clear();
ClearRenderCache();
m_bounding_box_cache_valid = false;
}
void EDA_TEXT::SetVertJustify( GR_TEXT_V_ALIGN_T aType )
{
m_attributes.m_Valign = aType;
m_render_cache.clear();
ClearRenderCache();
m_bounding_box_cache_valid = false;
}
void EDA_TEXT::SetKeepUpright( bool aKeepUpright )
{
m_attributes.m_KeepUpright = aKeepUpright;
m_render_cache.clear();
ClearRenderCache();
m_bounding_box_cache_valid = false;
}
@ -257,7 +278,8 @@ void EDA_TEXT::SetAttributes( const EDA_TEXT& aSrc )
{
m_attributes = aSrc.m_attributes;
m_pos = aSrc.m_pos;
m_render_cache.clear();
ClearRenderCache();
m_bounding_box_cache_valid = false;
}
@ -267,7 +289,8 @@ void EDA_TEXT::SwapText( EDA_TEXT& aTradingPartner )
std::swap( m_shown_text, aTradingPartner.m_shown_text );
std::swap( m_shown_text_has_text_var_refs, aTradingPartner.m_shown_text_has_text_var_refs );
m_render_cache.clear();
ClearRenderCache();
m_bounding_box_cache_valid = false;
}
@ -276,7 +299,11 @@ void EDA_TEXT::SwapAttributes( EDA_TEXT& aTradingPartner )
std::swap( m_attributes, aTradingPartner.m_attributes );
std::swap( m_pos, aTradingPartner.m_pos );
m_render_cache.clear();
ClearRenderCache();
aTradingPartner.ClearRenderCache();
m_bounding_box_cache_valid = false;
aTradingPartner.m_bounding_box_cache_valid = false;
}
@ -304,8 +331,12 @@ int EDA_TEXT::GetEffectiveTextPenWidth( int aDefaultWidth ) const
bool EDA_TEXT::Replace( const wxFindReplaceData& aSearchData )
{
bool retval = EDA_ITEM::Replace( aSearchData, m_text );
cacheShownText();
ClearRenderCache();
m_bounding_box_cache_valid = false;
return retval;
}
@ -313,35 +344,40 @@ bool EDA_TEXT::Replace( const wxFindReplaceData& aSearchData )
void EDA_TEXT::SetFont( KIFONT::FONT* aFont )
{
m_attributes.m_Font = aFont;
m_render_cache.clear();
ClearRenderCache();
m_bounding_box_cache_valid = false;
}
void EDA_TEXT::SetLineSpacing( double aLineSpacing )
{
m_attributes.m_LineSpacing = aLineSpacing;
m_render_cache.clear();
ClearRenderCache();
m_bounding_box_cache_valid = false;
}
void EDA_TEXT::SetTextSize( const wxSize& aNewSize )
{
m_attributes.m_Size = aNewSize;
m_render_cache.clear();
ClearRenderCache();
m_bounding_box_cache_valid = false;
}
void EDA_TEXT::SetTextWidth( int aWidth )
{
m_attributes.m_Size.x = aWidth;
m_render_cache.clear();
ClearRenderCache();
m_bounding_box_cache_valid = false;
}
void EDA_TEXT::SetTextHeight( int aHeight )
{
m_attributes.m_Size.y = aHeight;
m_render_cache.clear();
ClearRenderCache();
m_bounding_box_cache_valid = false;
}
@ -368,17 +404,17 @@ void EDA_TEXT::Offset( const VECTOR2I& aOffset )
m_pos += aOffset;
for( std::unique_ptr<KIFONT::GLYPH>& glyph : m_render_cache )
{
KIFONT::OUTLINE_GLYPH* outline_glyph = static_cast<KIFONT::OUTLINE_GLYPH*>( glyph.get() );
outline_glyph->Move( aOffset );
}
static_cast<KIFONT::OUTLINE_GLYPH*>( glyph.get() )->Move( aOffset );
m_bounding_box_cache.Move( aOffset );
}
void EDA_TEXT::Empty()
{
m_text.Empty();
m_render_cache.clear();
ClearRenderCache();
m_bounding_box_cache_valid = false;
}
@ -395,7 +431,8 @@ void EDA_TEXT::cacheShownText()
m_shown_text_has_text_var_refs = m_shown_text.Contains( wxT( "${" ) );
}
m_render_cache.clear();
ClearRenderCache();
m_bounding_box_cache_valid = false;
}
@ -410,6 +447,19 @@ KIFONT::FONT* EDA_TEXT::GetDrawFont() const
}
void EDA_TEXT::ClearRenderCache()
{
m_render_cache.clear();
}
void EDA_TEXT::ClearBoundingBoxCache()
{
m_bounding_box_cache_valid = false;
}
std::vector<std::unique_ptr<KIFONT::GLYPH>>*
EDA_TEXT::GetRenderCache( const wxString& forResolvedText ) const
{
@ -424,7 +474,7 @@ EDA_TEXT::GetRenderCache( const wxString& forResolvedText ) const
m_render_cache.clear();
KIFONT::OUTLINE_FONT* font = static_cast<KIFONT::OUTLINE_FONT*>( GetFont() );
font->GetLinesAsGlyphs( m_render_cache, this );
font->GetLinesAsGlyphs( &m_render_cache, this );
m_render_cache_angle = resolvedAngle;
m_render_cache_text = forResolvedText;
@ -474,6 +524,9 @@ int EDA_TEXT::GetInterline() const
EDA_RECT EDA_TEXT::GetTextBox( int aLine, bool aInvertY ) const
{
if( m_bounding_box_cache_valid && aLine < 0 && !aInvertY )
return m_bounding_box_cache;
EDA_RECT rect;
wxArrayString strings;
wxString text = GetShownText();
@ -498,8 +551,7 @@ EDA_RECT EDA_TEXT::GetTextBox( int aLine, bool aInvertY ) const
double penWidth( thickness );
bool bold = IsBold();
bool italic = IsItalic();
VECTOR2D extents = font->StringBoundaryLimits( text, fontSize, penWidth, bold, italic );
int dx = KiROUND( extents.x );
int dx = font->StringBoundaryLimits( text, fontSize, penWidth, bold, italic ).x;
int dy = GetInterline();
// Creates bounding box (rectangle) for horizontal, left and top justified text. The
@ -521,7 +573,7 @@ EDA_RECT EDA_TEXT::GetTextBox( int aLine, bool aInvertY ) const
for( unsigned ii = 1; ii < strings.GetCount(); ii++ )
{
text = strings.Item( ii );
dx = KiROUND( font->StringBoundaryLimits( text, fontSize, penWidth, bold, italic ).x );
dx = font->StringBoundaryLimits( text, fontSize, penWidth, bold, italic ).x;
textsize.x = std::max( textsize.x, dx );
textsize.y += dy;
}
@ -561,6 +613,12 @@ EDA_RECT EDA_TEXT::GetTextBox( int aLine, bool aInvertY ) const
rect.Normalize(); // Make h and v sizes always >= 0
if( aLine < 0 && !aInvertY )
{
m_bounding_box_cache_valid = true;
m_bounding_box_cache = rect;
}
return rect;
}
@ -797,25 +855,18 @@ std::shared_ptr<SHAPE_COMPOUND> EDA_TEXT::GetEffectiveTextShape( ) const
{
KIFONT::OUTLINE_GLYPH* glyph = static_cast<KIFONT::OUTLINE_GLYPH*>( baseGlyph.get() );
glyph->CacheTriangulation();
glyph->Triangulate(
[&]( int aPolygonIndex, const VECTOR2D& aVertex1, const VECTOR2D& aVertex2,
const VECTOR2D& aVertex3 )
{
SHAPE_SIMPLE* triShape = new SHAPE_SIMPLE;
for( unsigned int ii = 0; ii < glyph->TriangulatedPolyCount(); ++ii )
{
const SHAPE_POLY_SET::TRIANGULATED_POLYGON* tri = glyph->TriangulatedPolygon( ii );
triShape->Append( aVertex1 );
triShape->Append( aVertex2 );
triShape->Append( aVertex3 );
for( size_t jj = 0; jj < tri->GetTriangleCount(); ++jj )
{
VECTOR2I a, b, c;
tri->GetTriangle( jj, a, b, c );
SHAPE_SIMPLE* triShape = new SHAPE_SIMPLE;
triShape->Append( a );
triShape->Append( b );
triShape->Append( c );
shape->AddShape( triShape );
}
}
shape->AddShape( triShape );
} );
}
}
else

View File

@ -104,72 +104,9 @@ bool FONT::IsStroke( const wxString& aFontName )
}
/**
* Draw a string.
*
* @param aGal
* @param aTextItem is the underlying text item
* @param aPosition is the text position
* @return bounding box width/height
*/
VECTOR2D FONT::doDrawString( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2I& aPosition,
bool aParse, const TEXT_ATTRIBUTES& aAttrs ) const
{
if( aText.empty() )
return VECTOR2D( 0.0, 0.0 );
wxArrayString strings;
std::vector<VECTOR2I> positions;
VECTOR2D boundingBox;
std::vector<VECTOR2D> lineBoundingBoxes;
getLinePositions( aText, aPosition, strings, positions, lineBoundingBoxes, aAttrs );
for( size_t i = 0; i < strings.GetCount(); i++ )
{
VECTOR2D lineBoundingBox;
if( aParse )
{
MARKUP::MARKUP_PARSER markupParser( std::string( strings.Item( i ) ) );
//auto parse_result = markupParser.Parse();
VECTOR2I cursor = positions[i];
std::function<void( const std::unique_ptr<MARKUP::NODE>& )> nodeHandler =
[&]( const std::unique_ptr<MARKUP::NODE>& aNode )
{
if( !aNode->is_root() && aNode->has_content() )
{
VECTOR2D itemBoundingBox = Draw( aGal, aNode->string(), cursor,
aPosition, aAttrs );
lineBoundingBox += itemBoundingBox;
cursor += itemBoundingBox;
}
for( const auto& child : aNode->children )
nodeHandler( child );
};
nodeHandler( markupParser.Parse() );
}
else
{
lineBoundingBox = Draw( aGal, strings.Item( i ), positions[i], aPosition, aAttrs );
}
boundingBox.x = fmax( boundingBox.x, lineBoundingBox.x );
}
boundingBox.y = ( strings.GetCount() + 1 ) * GetInterline( aAttrs.m_Size.y );
return boundingBox;
}
void FONT::getLinePositions( const UTF8& aText, const VECTOR2I& aPosition,
wxArrayString& aTextLines, std::vector<VECTOR2I>& aPositions,
std::vector<VECTOR2D>& aBoundingBoxes,
const TEXT_ATTRIBUTES& aAttrs ) const
std::vector<VECTOR2I>& aExtents, const TEXT_ATTRIBUTES& aAttrs ) const
{
wxStringSplit( aText, aTextLines, '\n' );
int lineCount = aTextLines.Count();
@ -180,12 +117,12 @@ void FONT::getLinePositions( const UTF8& aText, const VECTOR2I& aPosition,
for( int i = 0; i < lineCount; i++ )
{
VECTOR2D pos( aPosition.x, aPosition.y + i * interline );
VECTOR2D end = boundingBoxSingleLine( nullptr, aTextLines[i], pos, aAttrs.m_Size,
VECTOR2I pos( aPosition.x, aPosition.y + i * interline );
VECTOR2I end = boundingBoxSingleLine( nullptr, aTextLines[i], pos, aAttrs.m_Size,
aAttrs.m_Italic );
VECTOR2D bBox( end - pos );
VECTOR2I bBox( end - pos );
aBoundingBoxes.push_back( bBox );
aExtents.push_back( bBox );
if( i == 0 )
height += aAttrs.m_Size.y;
@ -205,7 +142,7 @@ void FONT::getLinePositions( const UTF8& aText, const VECTOR2I& aPosition,
for( int i = 0; i < lineCount; i++ )
{
VECTOR2I lineSize = aBoundingBoxes.at( i );
VECTOR2I lineSize = aExtents.at( i );
wxPoint lineOffset( offset );
lineOffset.y += i * interline;
@ -247,46 +184,35 @@ void FONT::DrawText( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2I& aPosit
* object, such as a run of superscript characters)
* @param aAttrs are the styling attributes of the text, including its rotation
*/
VECTOR2D FONT::Draw( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2I& aPosition,
const VECTOR2I& aCursor, const TEXT_ATTRIBUTES& aAttrs ) const
void FONT::Draw( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2I& aPosition,
const VECTOR2I& aCursor, const TEXT_ATTRIBUTES& aAttrs ) const
{
if( !aGal || aText.empty() )
return VECTOR2D( 0, 0 );
return;
VECTOR2D position( aPosition - aCursor );
VECTOR2I position( aPosition - aCursor );
// Split multiline strings into separate ones and draw them line by line
wxArrayString strings_list;
std::vector<VECTOR2I> positions;
std::vector<VECTOR2D> boundingBoxes;
std::vector<VECTOR2I> extents;
getLinePositions( aText, position, strings_list, positions, boundingBoxes, aAttrs );
VECTOR2D boundingBox( 0, 0 );
BOX2I lineBoundingBox;
getLinePositions( aText, position, strings_list, positions, extents, aAttrs );
aGal->SetLineWidth( aAttrs.m_StrokeWidth );
for( size_t i = 0; i < strings_list.GetCount(); i++ )
{
(void) drawSingleLineText( aGal, &lineBoundingBox, strings_list[i], positions[i],
aAttrs.m_Size, aAttrs.m_Angle, aAttrs.m_Mirrored, aPosition,
aAttrs.m_Italic );
// expand bounding box of whole text
boundingBox.x = std::max( boundingBox.x, (double) lineBoundingBox.GetWidth() );
double lineHeight = GetInterline( aAttrs.m_Size.y, aAttrs.m_LineSpacing );
boundingBox.y += lineHeight;
drawSingleLineText( aGal, nullptr, strings_list[i], positions[i], aAttrs.m_Size,
aAttrs.m_Angle, aAttrs.m_Mirrored, aPosition, aAttrs.m_Italic );
}
return boundingBox;
}
/**
* @return position of cursor for drawing next substring
*/
VECTOR2D drawMarkup( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>& aGlyphs,
VECTOR2I drawMarkup( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>* aGlyphs,
const std::unique_ptr<MARKUP::NODE>& aNode, const VECTOR2I& aPosition,
const KIFONT::FONT* aFont, const VECTOR2D& aSize, const EDA_ANGLE& aAngle,
bool aMirror, const VECTOR2I& aOrigin, TEXT_STYLE_FLAGS aTextStyle )
@ -328,7 +254,7 @@ VECTOR2D drawMarkup( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>& a
}
VECTOR2D FONT::drawMarkup( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>& aGlyphs,
VECTOR2I FONT::drawMarkup( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>* aGlyphs,
const UTF8& aText, const VECTOR2I& aPosition, const VECTOR2D& aSize,
const EDA_ANGLE& aAngle, bool aMirror, const VECTOR2I& aOrigin,
TEXT_STYLE_FLAGS aTextStyle ) const
@ -341,16 +267,13 @@ VECTOR2D FONT::drawMarkup( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYP
}
VECTOR2D FONT::drawSingleLineText( KIGFX::GAL* aGal, BOX2I* aBoundingBox, const UTF8& aText,
const VECTOR2I& aPosition, const VECTOR2D& aSize,
const EDA_ANGLE& aAngle, bool aMirror, const VECTOR2I& aOrigin,
bool aItalic ) const
void FONT::drawSingleLineText( KIGFX::GAL* aGal, BOX2I* aBoundingBox, const UTF8& aText,
const VECTOR2I& aPosition, const VECTOR2D& aSize,
const EDA_ANGLE& aAngle, bool aMirror, const VECTOR2I& aOrigin,
bool aItalic ) const
{
if( !aGal )
{
// do nothing, cursor does not move
return aPosition;
}
return;
TEXT_STYLE_FLAGS textStyle = 0;
@ -358,23 +281,21 @@ VECTOR2D FONT::drawSingleLineText( KIGFX::GAL* aGal, BOX2I* aBoundingBox, const
textStyle |= TEXT_STYLE::ITALIC;
std::vector<std::unique_ptr<GLYPH>> glyphs;
VECTOR2D nextPosition = drawMarkup( aBoundingBox, glyphs, aText, aPosition, aSize, aAngle,
aMirror, aOrigin, textStyle );
(void) drawMarkup( aBoundingBox, &glyphs, aText, aPosition, aSize, aAngle, aMirror, aOrigin,
textStyle );
for( const std::unique_ptr<GLYPH>& glyph : glyphs )
aGal->DrawGlyph( *glyph.get() );
return nextPosition;
}
VECTOR2D FONT::StringBoundaryLimits( const UTF8& aText, const VECTOR2D& aSize, int aThickness,
VECTOR2I FONT::StringBoundaryLimits( const UTF8& aText, const VECTOR2D& aSize, int aThickness,
bool aBold, bool aItalic ) const
{
// TODO do we need to parse every time - have we already parsed?
std::vector<std::unique_ptr<GLYPH>> glyphs; // ignored
BOX2I boundingBox;
TEXT_STYLE_FLAGS textStyle = 0;
BOX2I boundingBox;
TEXT_STYLE_FLAGS textStyle = 0;
if( aBold )
textStyle |= TEXT_STYLE::BOLD;
@ -382,8 +303,8 @@ VECTOR2D FONT::StringBoundaryLimits( const UTF8& aText, const VECTOR2D& aSize, i
if( aItalic )
textStyle |= TEXT_STYLE::ITALIC;
(void) drawMarkup( &boundingBox, glyphs, aText, VECTOR2D(), aSize, EDA_ANGLE::ANGLE_0,
false, VECTOR2D(), textStyle );
(void) drawMarkup( &boundingBox, nullptr, aText, VECTOR2I(), aSize, EDA_ANGLE::ANGLE_0, false,
VECTOR2I(), textStyle );
if( IsStroke() )
{
@ -399,18 +320,16 @@ VECTOR2D FONT::StringBoundaryLimits( const UTF8& aText, const VECTOR2D& aSize, i
}
VECTOR2D FONT::boundingBoxSingleLine( BOX2I* aBoundingBox, const UTF8& aText,
const VECTOR2I& aPosition, const VECTOR2D& aSize,
bool aItalic ) const
VECTOR2I FONT::boundingBoxSingleLine( BOX2I* aBBox, const UTF8& aText, const VECTOR2I& aPosition,
const VECTOR2D& aSize, bool aItalic ) const
{
TEXT_STYLE_FLAGS textStyle = 0;
if( aItalic )
textStyle |= TEXT_STYLE::ITALIC;
std::vector<std::unique_ptr<GLYPH>> glyphs; // ignored
VECTOR2D nextPosition = drawMarkup( aBoundingBox, glyphs, aText, aPosition, aSize,
EDA_ANGLE::ANGLE_0, false, VECTOR2I(), textStyle );
VECTOR2I extents = drawMarkup( aBBox, nullptr, aText, aPosition, aSize, EDA_ANGLE::ANGLE_0,
false, VECTOR2I(), textStyle );
return nextPosition;
return extents;
}

View File

@ -108,3 +108,23 @@ BOX2D OUTLINE_GLYPH::BoundingBox()
BOX2I bbox = BBox();
return BOX2D( bbox.GetOrigin(), bbox.GetSize() );
}
void OUTLINE_GLYPH::Triangulate( TRIANGULATE_CALLBACK aCallback ) const
{
const_cast<OUTLINE_GLYPH*>( this )->CacheTriangulation();
for( unsigned int i = 0; i < TriangulatedPolyCount(); i++ )
{
const SHAPE_POLY_SET::TRIANGULATED_POLYGON* polygon = TriangulatedPolygon( i );
for ( size_t j = 0; j < polygon->GetTriangleCount(); j++ )
{
VECTOR2I a, b, c;
polygon->GetTriangle( j, a, b, c );
aCallback( i, a, b, c );
}
}
}

View File

@ -227,35 +227,43 @@ BOX2I OUTLINE_FONT::getBoundingBox( const std::vector<std::unique_ptr<GLYPH>>& a
}
VECTOR2I OUTLINE_FONT::GetLinesAsGlyphs( std::vector<std::unique_ptr<GLYPH>>& aGlyphs,
const EDA_TEXT* aText ) const
void OUTLINE_FONT::GetLinesAsGlyphs( std::vector<std::unique_ptr<GLYPH>>* aGlyphs,
const EDA_TEXT* aText ) const
{
wxArrayString strings;
std::vector<VECTOR2I> positions;
VECTOR2I ret;
std::vector<VECTOR2D> boundingBoxes;
std::vector<VECTOR2I> extents;
TEXT_ATTRIBUTES attrs = aText->GetAttributes();
TEXT_STYLE_FLAGS textStyle = 0;
attrs.m_Angle = aText->GetDrawRotation();
if( aText->IsItalic() )
textStyle |= TEXT_STYLE::ITALIC;
getLinePositions( aText->GetShownText(), aText->GetTextPos(), strings, positions, boundingBoxes,
attrs );
for( size_t i = 0; i < strings.GetCount(); i++ )
{
ret = drawMarkup( nullptr, aGlyphs, UTF8( strings.Item( i ) ), positions[i], attrs.m_Size,
attrs.m_Angle, attrs.m_Mirrored, aText->GetTextPos(), textStyle );
}
return ret;
return GetLinesAsGlyphs( aGlyphs, aText->GetShownText(), aText->GetTextPos(), attrs );
}
VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_ptr<GLYPH>>& aGlyphs,
void OUTLINE_FONT::GetLinesAsGlyphs( std::vector<std::unique_ptr<GLYPH>>* aGlyphs,
const UTF8& aText, const VECTOR2I& aPosition,
const TEXT_ATTRIBUTES& aAttrs ) const
{
wxArrayString strings;
std::vector<VECTOR2I> positions;
std::vector<VECTOR2I> extents;
TEXT_STYLE_FLAGS textStyle = 0;
if( aAttrs.m_Italic )
textStyle |= TEXT_STYLE::ITALIC;
getLinePositions( aText, aPosition, strings, positions, extents, aAttrs );
for( size_t i = 0; i < strings.GetCount(); i++ )
{
(void) drawMarkup( nullptr, aGlyphs, UTF8( strings.Item( i ) ), positions[i],
aAttrs.m_Size, aAttrs.m_Angle, aAttrs.m_Mirrored, aPosition, textStyle );
}
}
VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_ptr<GLYPH>>* aGlyphs,
const UTF8& aText, const VECTOR2D& aSize,
const VECTOR2I& aPosition, const EDA_ANGLE& aAngle,
bool aMirror, const VECTOR2I& aOrigin,
@ -294,104 +302,88 @@ VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_pt
hb_glyph_position_t& pos = glyphPos[i];
int codepoint = glyphInfo[i].codepoint;
FT_Load_Glyph( face, codepoint, FT_LOAD_NO_BITMAP );
FT_GlyphSlot faceGlyph = face->glyph;
// contours is a collection of all outlines in the glyph;
// example: glyph for 'o' generally contains 2 contours,
// one for the glyph outline and one for the hole
CONTOURS contours;
OUTLINE_DECOMPOSER decomposer( faceGlyph->outline );
decomposer.OutlineToSegments( &contours );
std::unique_ptr<OUTLINE_GLYPH> glyph = std::make_unique<OUTLINE_GLYPH>();
std::vector<SHAPE_LINE_CHAIN> holes;
std::vector<SHAPE_LINE_CHAIN> outlines;
for( CONTOUR& c : contours )
if( aGlyphs )
{
GLYPH_POINTS points = c.points;
SHAPE_LINE_CHAIN shape;
FT_Load_Glyph( face, codepoint, FT_LOAD_NO_BITMAP );
for( const VECTOR2D& v : points )
FT_GlyphSlot faceGlyph = face->glyph;
// contours is a collection of all outlines in the glyph;
// example: glyph for 'o' generally contains 2 contours,
// one for the glyph outline and one for the hole
CONTOURS contours;
OUTLINE_DECOMPOSER decomposer( faceGlyph->outline );
decomposer.OutlineToSegments( &contours );
std::unique_ptr<OUTLINE_GLYPH> glyph = std::make_unique<OUTLINE_GLYPH>();
std::vector<SHAPE_LINE_CHAIN> holes;
for( CONTOUR& c : contours )
{
VECTOR2D pt( v + cursor );
GLYPH_POINTS points = c.points;
SHAPE_LINE_CHAIN shape;
topLeft.x = std::min( topLeft.x, pt.x );
topLeft.y = std::max( topLeft.y, pt.y );
topRight.x = std::max( topRight.x, pt.x );
topRight.y = std::max( topRight.y, pt.y );
if( IsSubscript( aTextStyle ) )
pt.y -= 0.25 * scaler;
else if( IsSuperscript( aTextStyle ) )
pt.y += 0.45 * scaler;
pt *= scaleFactor;
pt += aPosition;
if( aMirror )
pt.x = aOrigin.x - ( pt.x - aOrigin.x );
if( !aAngle.IsZero() )
RotatePoint( pt, aOrigin, aAngle );
shape.Append( pt.x, pt.y );
}
shape.SetClosed( true );
if( contourIsHole( c ) )
holes.push_back( std::move( shape ) );
else
outlines.push_back( std::move( shape ) );
}
for( SHAPE_LINE_CHAIN& outline : outlines )
{
if( outline.PointCount() )
glyph->AddOutline( outline );
}
int nthHole = 0;
for( SHAPE_LINE_CHAIN& hole : holes )
{
if( hole.PointCount() )
{
VECTOR2I firstPoint = hole.GetPoint( 0 );
int nthOutline = -1;
int n = 0;
for( SHAPE_LINE_CHAIN& outline : outlines )
for( const VECTOR2D& v : points )
{
if( outline.PointInside( firstPoint ) )
{
nthOutline = n;
break;
}
VECTOR2D pt( v + cursor );
n++;
topLeft.x = std::min( topLeft.x, pt.x );
topLeft.y = std::max( topLeft.y, pt.y );
topRight.x = std::max( topRight.x, pt.x );
topRight.y = std::max( topRight.y, pt.y );
if( IsSubscript( aTextStyle ) )
pt.y -= 0.25 * scaler;
else if( IsSuperscript( aTextStyle ) )
pt.y += 0.45 * scaler;
pt *= scaleFactor;
pt += aPosition;
if( aMirror )
pt.x = aOrigin.x - ( pt.x - aOrigin.x );
if( !aAngle.IsZero() )
RotatePoint( pt, aOrigin, aAngle );
shape.Append( pt.x, pt.y );
}
if( nthOutline > -1 )
glyph->AddHole( hole, n );
shape.SetClosed( true );
if( contourIsHole( c ) )
holes.push_back( std::move( shape ) );
else
glyph->AddOutline( std::move( shape ) );
}
nthHole++;
}
for( SHAPE_LINE_CHAIN& hole : holes )
{
if( hole.PointCount() )
{
for( int ii = 0; ii < glyph->OutlineCount(); ++ii )
{
if( glyph->Outline( ii ).PointInside( hole.GetPoint( 0 ) ) )
{
glyph->AddHole( std::move( hole ), ii );
break;
}
}
}
}
aGlyphs.push_back( std::move( glyph ) );
if( glyph->HasHoles() )
glyph->Fracture( SHAPE_POLY_SET::PM_FAST ); // FONT TODO verify aFastMode
aGlyphs->push_back( std::move( glyph ) );
}
cursor.x += pos.x_advance;
cursor.y += pos.y_advance;
}
VECTOR2I cursorEnd( cursor );
if( IsOverbar( aTextStyle ) )
if( IsOverbar( aTextStyle ) && aGlyphs )
{
topLeft *= scaleFactor;
topRight *= scaleFactor;
@ -415,12 +407,12 @@ VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_pt
ERROR_INSIDE );
std::unique_ptr<OUTLINE_GLYPH> overbarGlyph = std::make_unique<OUTLINE_GLYPH>( overbar );
aGlyphs.push_back( std::move( overbarGlyph ) );
aGlyphs->push_back( std::move( overbarGlyph ) );
}
hb_buffer_destroy( buf );
VECTOR2I cursorDisplacement( cursorEnd.x * scaleFactor.x, -cursorEnd.y * scaleFactor.y );
VECTOR2I cursorDisplacement( cursor.x * scaleFactor.x, -cursor.y * scaleFactor.y );
if( aBBox )
{

View File

@ -195,7 +195,7 @@ double STROKE_FONT::ComputeOverbarVerticalPosition( double aGlyphHeight ) const
}
VECTOR2I STROKE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_ptr<GLYPH>>& aGlyphs,
VECTOR2I STROKE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_ptr<GLYPH>>* aGlyphs,
const UTF8& aText, const VECTOR2D& aSize,
const VECTOR2I& aPosition, const EDA_ANGLE& aAngle,
bool aMirror, const VECTOR2I& aOrigin,
@ -252,10 +252,20 @@ VECTOR2I STROKE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_ptr
{
STROKE_GLYPH* source = static_cast<STROKE_GLYPH*>( m_glyphs->at( dd ).get() );
aGlyphs.push_back( source->Transform( glyphSize, cursor, tilt, aAngle, aMirror,
aOrigin ) );
if( aGlyphs )
{
aGlyphs->push_back( source->Transform( glyphSize, cursor, tilt, aAngle, aMirror,
aOrigin ) );
}
cursor.x = aGlyphs.back()->BoundingBox().GetEnd().x;
VECTOR2D glyphExtents = source->BoundingBox().GetEnd();
glyphExtents *= glyphSize;
if( tilt )
glyphExtents.x -= glyphExtents.y * tilt;
cursor.x += glyphExtents.x;
}
}
@ -263,8 +273,6 @@ VECTOR2I STROKE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_ptr
if( aTextStyle & TEXT_STYLE::OVERBAR )
{
std::unique_ptr<STROKE_GLYPH> overbarGlyph = std::make_unique<STROKE_GLYPH>();
barOffset.y = ComputeOverbarVerticalPosition( glyphSize.y );
if( aTextStyle & TEXT_STYLE::ITALIC )
@ -279,11 +287,16 @@ VECTOR2I STROKE_FONT::GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_ptr
RotatePoint( barEnd, aOrigin, aAngle );
}
overbarGlyph->AddPoint( barStart );
overbarGlyph->AddPoint( barEnd );
overbarGlyph->Finalize();
if( aGlyphs )
{
std::unique_ptr<STROKE_GLYPH> overbarGlyph = std::make_unique<STROKE_GLYPH>();
aGlyphs.push_back( std::move( overbarGlyph ) );
overbarGlyph->AddPoint( barStart );
overbarGlyph->AddPoint( barEnd );
overbarGlyph->Finalize();
aGlyphs->push_back( std::move( overbarGlyph ) );
}
}
if( aBBox )

View File

@ -1,47 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021 Ola Rinta-Koski
* Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <limits>
#include <font/triangulate.h>
void Triangulate( const SHAPE_POLY_SET& aPolylist, TRIANGULATE_CALLBACK aCallback,
void* aCallbackData )
{
SHAPE_POLY_SET polys( aPolylist );
polys.Fracture( SHAPE_POLY_SET::PM_FAST ); // TODO verify aFastMode
polys.CacheTriangulation();
for( unsigned int i = 0; i < polys.TriangulatedPolyCount(); i++ )
{
const SHAPE_POLY_SET::TRIANGULATED_POLYGON* polygon = polys.TriangulatedPolygon( i );
for ( size_t j = 0; j < polygon->GetTriangleCount(); j++ )
{
VECTOR2I a;
VECTOR2I b;
VECTOR2I c;
polygon->GetTriangle( j, a, b, c );
aCallback( i, a, b, c, aCallbackData );
}
}
}

View File

@ -1786,44 +1786,44 @@ void CAIRO_GAL_BASE::DrawGlyph( const KIFONT::GLYPH& aGlyph, int aNth, int aTota
// eventually glyphs should not be drawn as polygons at all,
// but as bitmaps with antialiasing, this is just a stopgap measure
// of getting some form of outline font display
auto triangleCallback = [&]( int aPolygonIndex, const VECTOR2D& aVertex1,
const VECTOR2D& aVertex2, const VECTOR2D& aVertex3,
void* aCallbackData )
{
#if 1
syncLineWidth();
auto triangleCallback =
[&]( int aPolygonIndex, const VECTOR2D& aVertex1, const VECTOR2D& aVertex2,
const VECTOR2D& aVertex3, void* aCallbackData )
{
#if 1
syncLineWidth();
const auto p0 = roundp( xform( aVertex1 ) );
const auto p1 = roundp( xform( aVertex2 ) );
const auto p2 = roundp( xform( aVertex3 ) );
const auto p0 = roundp( xform( aVertex1 ) );
const auto p1 = roundp( xform( aVertex2 ) );
const auto p2 = roundp( xform( aVertex3 ) );
/*
cairo_move_to( currentContext, aVertex1.x, aVertex1.y );
cairo_line_to( currentContext, aVertex2.x, aVertex2.y );
cairo_line_to( currentContext, aVertex3.x, aVertex3.y );
cairo_line_to( currentContext, aVertex1.x, aVertex1.y );
*/
cairo_move_to( m_currentContext, p0.x, p0.y );
cairo_line_to( m_currentContext, p1.x, p1.y );
cairo_line_to( m_currentContext, p2.x, p2.y );
cairo_close_path( m_currentContext );
/*
setSourceRgba( currentContext, fillColor );
SetIsFill( true );
cairo_set_fill_rule( currentContext, CAIRO_FILL_RULE_EVEN_ODD );
flushPath();
*/
//cairo_fill( currentContext );
#else
// just a silly test
/*
DrawRectangle(aVertex1, aVertex2);
DrawRectangle(aVertex2, aVertex3);
DrawRectangle(aVertex3, aVertex1);
*/
DrawTriangle( aVertex1, aVertex2, aVertex3 );
#endif
};
/*
cairo_move_to( currentContext, aVertex1.x, aVertex1.y );
cairo_line_to( currentContext, aVertex2.x, aVertex2.y );
cairo_line_to( currentContext, aVertex3.x, aVertex3.y );
cairo_line_to( currentContext, aVertex1.x, aVertex1.y );
*/
cairo_move_to( m_currentContext, p0.x, p0.y );
cairo_line_to( m_currentContext, p1.x, p1.y );
cairo_line_to( m_currentContext, p2.x, p2.y );
cairo_close_path( m_currentContext );
/*
setSourceRgba( currentContext, fillColor );
SetIsFill( true );
cairo_set_fill_rule( currentContext, CAIRO_FILL_RULE_EVEN_ODD );
flushPath();
*/
//cairo_fill( currentContext );
#else
// just a silly test
/*
DrawRectangle(aVertex1, aVertex2);
DrawRectangle(aVertex2, aVertex3);
DrawRectangle(aVertex3, aVertex1);
*/
DrawTriangle( aVertex1, aVertex2, aVertex3 );
#endif
};
Triangulate( aGlyph, triangleCallback );

View File

@ -44,7 +44,6 @@
#include <bezier_curves.h>
#include <math/util.h> // for KiROUND
#include <trace_helpers.h>
#include <font/triangulate.h>
#include <wx/frame.h>
@ -2425,24 +2424,16 @@ void OPENGL_GAL::DrawGlyph( const KIFONT::GLYPH& aGlyph, int aNth, int aTotal )
{
const auto& outlineGlyph = static_cast<const KIFONT::OUTLINE_GLYPH&>( aGlyph );
fillPolygonAsTriangles( outlineGlyph );
m_currentManager->Shader( SHADER_NONE );
m_currentManager->Color( m_fillColor );
outlineGlyph.Triangulate(
[&]( int aPolygonIndex, const VECTOR2D& aVertex1, const VECTOR2D& aVertex2,
const VECTOR2D& aVertex3 )
{
m_currentManager->Vertex( aVertex1.x, aVertex1.y, m_layerDepth );
m_currentManager->Vertex( aVertex2.x, aVertex2.y, m_layerDepth );
m_currentManager->Vertex( aVertex3.x, aVertex3.y, m_layerDepth );
} );
}
}
void OPENGL_GAL::fillPolygonAsTriangles( const SHAPE_POLY_SET& aPolyList )
{
m_currentManager->Shader( SHADER_NONE );
m_currentManager->Color( m_fillColor );
auto triangleCallback = [&]( int aPolygonIndex, const VECTOR2D& aVertex1,
const VECTOR2D& aVertex2, const VECTOR2D& aVertex3,
void* aCallbackData )
{
m_currentManager->Vertex( aVertex1.x, aVertex1.y, m_layerDepth );
m_currentManager->Vertex( aVertex2.x, aVertex2.y, m_layerDepth );
m_currentManager->Vertex( aVertex3.x, aVertex3.y, m_layerDepth );
};
Triangulate( aPolyList, triangleCallback );
}

View File

@ -1118,10 +1118,10 @@ const EDA_RECT LIB_PIN::GetBoundingBox( bool aIncludeInvisibles, bool aPinOnly )
if( showNum )
{
VECTOR2D fontSize( m_numTextSize, m_numTextSize );
VECTOR2D numSize = font->StringBoundaryLimits( number, fontSize, penWidth, false, false );
VECTOR2I numSize = font->StringBoundaryLimits( number, fontSize, penWidth, false, false );
numberTextLength = KiROUND( numSize.x );
numberTextHeight = KiROUND( numSize.y );
numberTextLength = numSize.x;
numberTextHeight = numSize.y;
}
if( m_shape == GRAPHIC_PINSHAPE::INVERTED || m_shape == GRAPHIC_PINSHAPE::INVERTED_CLOCK )
@ -1136,10 +1136,10 @@ const EDA_RECT LIB_PIN::GetBoundingBox( bool aIncludeInvisibles, bool aPinOnly )
if( showName )
{
VECTOR2D fontSize( m_nameTextSize, m_nameTextSize );
VECTOR2D nameSize = font->StringBoundaryLimits( name, fontSize, penWidth, false, false );
VECTOR2I nameSize = font->StringBoundaryLimits( name, fontSize, penWidth, false, false );
nameTextLength = KiROUND( nameSize.x ) + nameTextOffset;
nameTextHeight = KiROUND( nameSize.y ) + Mils2iu( PIN_TEXT_MARGIN );
nameTextLength = nameSize.x + nameTextOffset;
nameTextHeight = nameSize.y + Mils2iu( PIN_TEXT_MARGIN );
}
if( nameTextOffset ) // for values > 0, pin name is inside the body

View File

@ -432,6 +432,19 @@ std::unique_ptr< LIB_SYMBOL > LIB_SYMBOL::Flatten() const
}
void LIB_SYMBOL::ClearCaches()
{
for( LIB_ITEM& item : m_drawings )
{
if( EDA_TEXT* eda_text = dynamic_cast<EDA_TEXT*>( &item ) )
{
eda_text->ClearBoundingBoxCache();
eda_text->ClearRenderCache();
}
}
}
const wxString LIB_SYMBOL::GetLibraryName() const
{
if( m_library )

View File

@ -126,6 +126,8 @@ public:
LIB_SYMBOL_REF& GetParent() { return m_parent; }
const LIB_SYMBOL_REF& GetParent() const { return m_parent; }
void ClearCaches();
virtual wxString GetClass() const override
{
return wxT( "LIB_SYMBOL" );

View File

@ -213,7 +213,8 @@ public:
/**
* Mark an item for refresh.
*/
void UpdateItem( EDA_ITEM* aItem, bool isAddOrDelete = false, bool aUpdateRtree = false );
virtual void UpdateItem( EDA_ITEM* aItem, bool isAddOrDelete = false,
bool aUpdateRtree = false );
/**
* Mark selected items for refresh.

View File

@ -649,6 +649,14 @@ void SCH_EDIT_FRAME::SetCurrentSheet( const SCH_SHEET_PATH& aSheet )
void SCH_EDIT_FRAME::HardRedraw()
{
SCH_SCREEN* screen = GetCurrentSheet().LastScreen();
for( SCH_ITEM* item : screen->Items() )
item->ClearCaches();
for( std::pair<const wxString, LIB_SYMBOL*>& libSymbol : screen->GetLibSymbols() )
libSymbol.second->ClearCaches();
RecalculateConnections( LOCAL_CLEANUP );
FocusOnItem( nullptr );
@ -1752,3 +1760,12 @@ void SCH_EDIT_FRAME::SaveSymbolToSchematic( const LIB_SYMBOL& aSymbol,
GetCanvas()->Refresh();
OnModify();
}
void SCH_EDIT_FRAME::UpdateItem( EDA_ITEM* aItem, bool isAddOrDelete, bool aUpdateRtree )
{
SCH_BASE_FRAME::UpdateItem( aItem, isAddOrDelete, aUpdateRtree );
if( SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( aItem ) )
sch_item->ClearCaches();
}

View File

@ -429,6 +429,9 @@ public:
void SetCurrentSheet( const SCH_SHEET_PATH& aSheet );
void UpdateItem( EDA_ITEM* aItem, bool isAddOrDelete = false,
bool aUpdateRtree = false ) override;
/**
* Rebuild the GAL and redraw the screen. Call when something went wrong.
*/

View File

@ -52,13 +52,16 @@
#include <eeschema_id.h>
#include <tool/tool_manager.h>
#include <tools/ee_actions.h>
#include <font/outline_font.h>
SCH_FIELD::SCH_FIELD( const VECTOR2I& aPos, int aFieldId, SCH_ITEM* aParent,
const wxString& aName ) :
SCH_ITEM( aParent, SCH_FIELD_T ),
EDA_TEXT( wxEmptyString ),
m_id( 0 ),
m_name( aName )
m_name( aName ),
m_renderCacheValid( false )
{
SetTextPos( aPos );
SetId( aFieldId ); // will also set the layer
@ -66,11 +69,53 @@ SCH_FIELD::SCH_FIELD( const VECTOR2I& aPos, int aFieldId, SCH_ITEM* aParent,
}
SCH_FIELD::SCH_FIELD( const SCH_FIELD& aField ) :
SCH_ITEM( aField ),
EDA_TEXT( aField )
{
m_id = aField.m_id;
m_name = aField.m_name;
m_renderCache.clear();
for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aField.m_renderCache )
{
KIFONT::OUTLINE_GLYPH* outline_glyph = static_cast<KIFONT::OUTLINE_GLYPH*>( glyph.get() );
m_renderCache.emplace_back( std::make_unique<KIFONT::OUTLINE_GLYPH>( *outline_glyph ) );
}
m_renderCacheValid = aField.m_renderCacheValid;
m_renderCachePos = aField.m_renderCachePos;
}
SCH_FIELD::~SCH_FIELD()
{
}
SCH_FIELD& SCH_FIELD::operator=( const SCH_FIELD& aField )
{
EDA_TEXT::operator=( aField );
m_id = aField.m_id;
m_name = aField.m_name;
m_renderCache.clear();
for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aField.m_renderCache )
{
KIFONT::OUTLINE_GLYPH* outline_glyph = static_cast<KIFONT::OUTLINE_GLYPH*>( glyph.get() );
m_renderCache.emplace_back( std::make_unique<KIFONT::OUTLINE_GLYPH>( *outline_glyph ) );
}
m_renderCacheValid = aField.m_renderCacheValid;
m_renderCachePos = aField.m_renderCachePos;
return *this;
}
EDA_ITEM* SCH_FIELD::Clone() const
{
return new SCH_FIELD( *this );
@ -226,6 +271,54 @@ KIFONT::FONT* SCH_FIELD::GetDrawFont() const
}
void SCH_FIELD::ClearCaches()
{
ClearRenderCache();
EDA_TEXT::ClearBoundingBoxCache();
}
void SCH_FIELD::ClearRenderCache()
{
EDA_TEXT::ClearRenderCache();
m_renderCacheValid = false;
}
std::vector<std::unique_ptr<KIFONT::GLYPH>>*
SCH_FIELD::GetRenderCache( const wxString& forResolvedText, const VECTOR2I& forPosition,
TEXT_ATTRIBUTES& aAttrs ) const
{
if( GetDrawFont()->IsOutline() )
{
if( m_renderCache.empty() || !m_renderCacheValid )
{
m_renderCache.clear();
KIFONT::OUTLINE_FONT* font = static_cast<KIFONT::OUTLINE_FONT*>( GetDrawFont() );
font->GetLinesAsGlyphs( &m_renderCache, forResolvedText, forPosition, aAttrs );
m_renderCachePos = forPosition;
m_renderCacheValid = true;
}
if( m_renderCachePos != forPosition )
{
VECTOR2I delta = forPosition - m_renderCachePos;
for( std::unique_ptr<KIFONT::GLYPH>& glyph : m_renderCache )
static_cast<KIFONT::OUTLINE_GLYPH*>( glyph.get() )->Move( delta );
m_renderCachePos = forPosition;
}
return &m_renderCache;
}
return nullptr;
}
void SCH_FIELD::Print( const RENDER_SETTINGS* aSettings, const VECTOR2I& aOffset )
{
wxDC* DC = aSettings->GetPrintDC();

View File

@ -52,10 +52,12 @@ public:
SCH_FIELD( const VECTOR2I& aPos, int aFieldId, SCH_ITEM* aParent,
const wxString& aName = wxEmptyString );
// Do not create a copy constructor. The one generated by the compiler is adequate.
SCH_FIELD( const SCH_FIELD& aText );
~SCH_FIELD();
SCH_FIELD& operator=( const SCH_FIELD& aField );
static inline bool ClassOf( const EDA_ITEM* aItem )
{
return aItem && SCH_FIELD_T == aItem->Type();
@ -156,6 +158,13 @@ public:
KIFONT::FONT* GetDrawFont() const override;
void ClearCaches() override;
void ClearRenderCache() override;
std::vector<std::unique_ptr<KIFONT::GLYPH>>*
GetRenderCache( const wxString& forResolvedText, const VECTOR2I& forPosition,
TEXT_ATTRIBUTES& aAttrs ) const;
void Print( const RENDER_SETTINGS* aSettings, const VECTOR2I& aOffset ) override;
void Move( const VECTOR2I& aMoveVector ) override
@ -222,6 +231,10 @@ private:
int m_id; ///< Field index, @see enum MANDATORY_FIELD_T
wxString m_name;
mutable bool m_renderCacheValid;
mutable VECTOR2I m_renderCachePos;
mutable std::vector<std::unique_ptr<KIFONT::GLYPH>> m_renderCache;
};

View File

@ -230,6 +230,26 @@ void SCH_ITEM::SwapData( SCH_ITEM* aItem )
}
void SCH_ITEM::ClearCaches()
{
auto clearTextCaches =
[]( SCH_ITEM* aItem )
{
EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( aItem );
if( text )
{
text->ClearBoundingBoxCache();
text->ClearRenderCache();
}
};
clearTextCaches( this );
RunOnChildren( clearTextCaches );
}
bool SCH_ITEM::operator < ( const SCH_ITEM& aItem ) const
{
if( Type() != aItem.Type() )

View File

@ -429,6 +429,8 @@ public:
virtual void RunOnChildren( const std::function<void( SCH_ITEM* )>& aFunction ) { }
virtual void ClearCaches();
/**
* Check if this schematic item has line stoke properties.
*

View File

@ -464,7 +464,7 @@ void SCH_PAINTER::boxText( const wxString& aText, const VECTOR2D& aPosition,
aAttrs.m_Italic );
}
VECTOR2D extents = font->StringBoundaryLimits( aText, aAttrs.m_Size, aAttrs.m_StrokeWidth,
VECTOR2I extents = font->StringBoundaryLimits( aText, aAttrs.m_Size, aAttrs.m_StrokeWidth,
aAttrs.m_Bold, aAttrs.m_Italic );
EDA_RECT box( (VECTOR2I) aPosition, wxSize( extents.x, aAttrs.m_Size.y ) );
@ -718,11 +718,10 @@ void SCH_PAINTER::draw( const LIB_FIELD *aField, int aLayer )
if( drawingShadows && eeconfig()->m_Selection.text_as_box )
{
bbox.RevertYAxis();
m_gal->SetIsStroke( true );
m_gal->SetIsFill( true );
m_gal->SetLineWidth( getTextThickness( aField, drawingShadows ) );
m_gal->DrawRectangle( mapCoords( bbox.GetPosition() ), mapCoords( bbox.GetEnd() ) );
m_gal->DrawRectangle( bbox.GetPosition(), bbox.GetEnd() );
}
else
{
@ -765,18 +764,27 @@ void SCH_PAINTER::draw( const LIB_TEXT *aText, int aLayer )
}
EDA_RECT bBox = aText->GetBoundingBox();
bBox.RevertYAxis();
VECTOR2D pos = mapCoords( bBox.Centre() );
VECTOR2D pos = bBox.Centre();
m_gal->SetFillColor( color );
m_gal->SetStrokeColor( color );
TEXT_ATTRIBUTES attrs( aText->GetAttributes() );
attrs.m_Halign = GR_TEXT_H_ALIGN_CENTER;
attrs.m_Valign = GR_TEXT_V_ALIGN_CENTER;
attrs.m_StrokeWidth = getTextThickness( aText, drawingShadows );
if( drawingShadows && eeconfig()->m_Selection.text_as_box )
{
m_gal->SetIsStroke( true );
m_gal->SetIsFill( true );
m_gal->SetLineWidth( getTextThickness( aText, drawingShadows ) );
m_gal->DrawRectangle( bBox.GetPosition(), bBox.GetEnd() );
}
else
{
TEXT_ATTRIBUTES attrs( aText->GetAttributes() );
attrs.m_Halign = GR_TEXT_H_ALIGN_CENTER;
attrs.m_Valign = GR_TEXT_V_ALIGN_CENTER;
attrs.m_StrokeWidth = getTextThickness( aText, drawingShadows );
strokeText( aText->GetText(), pos, attrs );
strokeText( aText->GetText(), pos, attrs );
}
}
@ -1477,7 +1485,6 @@ void SCH_PAINTER::draw( const SCH_TEXT *aText, int aLayer )
if( drawingShadows && eeconfig()->m_Selection.text_as_box )
{
EDA_RECT bBox = aText->GetBoundingBox();
bBox.Offset( text_offset.x, text_offset.y );
bBox.RevertYAxis();
m_gal->SetIsStroke( true );
m_gal->SetIsFill( true );
@ -1503,7 +1510,7 @@ void SCH_PAINTER::draw( const SCH_TEXT *aText, int aLayer )
}
else
{
strokeText( shownText, aText->GetTextPos(), attrs );
strokeText( shownText, aText->GetTextPos() + text_offset, attrs );
}
}
}
@ -1705,13 +1712,27 @@ void SCH_PAINTER::draw( const SCH_FIELD *aField, int aLayer )
}
else
{
wxString shownText = aField->GetShownText();
TEXT_ATTRIBUTES attributes = aField->GetAttributes();
attributes.m_Halign = GR_TEXT_H_ALIGN_CENTER;
attributes.m_Valign = GR_TEXT_V_ALIGN_CENTER;
attributes.m_StrokeWidth = getTextThickness( aField, drawingShadows );
attributes.m_Angle = orient;
strokeText( aField->GetShownText(), textpos, attributes );
std::vector<std::unique_ptr<KIFONT::GLYPH>>* cache = nullptr;
cache = aField->GetRenderCache( shownText, textpos, attributes );
if( cache )
{
for( const std::unique_ptr<KIFONT::GLYPH>& glyph : *cache )
m_gal->DrawGlyph( *glyph.get() );
}
else
{
strokeText( shownText, textpos, attributes );
}
}
// Draw the umbilical line

View File

@ -1049,7 +1049,10 @@ const EDA_RECT SCH_LABEL_BASE::GetBoundingBox() const
EDA_RECT box( GetBodyBoundingBox() );
for( const SCH_FIELD& field : m_fields )
box.Merge( field.GetBoundingBox() );
{
if( field.IsVisible() )
box.Merge( field.GetBoundingBox() );
}
box.Normalize();

View File

@ -1165,6 +1165,8 @@ void SYMBOL_EDIT_FRAME::HardRedraw()
else
item.SetSelected();
}
m_symbol->ClearCaches();
}
RebuildView();
@ -1482,3 +1484,15 @@ bool SYMBOL_EDIT_FRAME::IsSymbolEditable() const
{
return m_symbol && ( !IsSymbolFromLegacyLibrary() || IsSymbolFromSchematic() );
}
void SYMBOL_EDIT_FRAME::UpdateItem( EDA_ITEM* aItem, bool isAddOrDelete, bool aUpdateRtree )
{
SCH_BASE_FRAME::UpdateItem( aItem, isAddOrDelete, aUpdateRtree );
if( EDA_TEXT* eda_text = dynamic_cast<EDA_TEXT*>( aItem ) )
{
eda_text->ClearBoundingBoxCache();
eda_text->ClearRenderCache();
}
}

View File

@ -323,6 +323,9 @@ public:
void RebuildView();
void UpdateItem( EDA_ITEM* aItem, bool isAddOrDelete = false,
bool aUpdateRtree = false ) override;
/**
* Rebuild the GAL and redraw the screen. Call when something went wrong.
*/

View File

@ -342,7 +342,8 @@ public:
virtual GR_TEXT_H_ALIGN_T GetDrawHorizJustify() const { return GetHorizJustify(); };
virtual GR_TEXT_V_ALIGN_T GetDrawVertJustify() const { return GetVertJustify(); };
void ClearRenderCache() { m_render_cache.clear(); }
virtual void ClearRenderCache();
virtual void ClearBoundingBoxCache();
std::vector<std::unique_ptr<KIFONT::GLYPH>>*
GetRenderCache( const wxString& forResolvedText ) const;
@ -369,16 +370,19 @@ private:
const COLOR4D& aColor, OUTLINE_MODE aFillMode, const wxString& aText,
const VECTOR2I& aPos );
wxString m_text;
wxString m_shown_text; // Cache of unescaped text for efficient access
bool m_shown_text_has_text_var_refs;
wxString m_text;
wxString m_shown_text; // Cache of unescaped text for efficient access
bool m_shown_text_has_text_var_refs;
mutable wxString m_render_cache_text;
mutable EDA_ANGLE m_render_cache_angle;
mutable std::vector<std::unique_ptr<KIFONT::GLYPH>> m_render_cache;
TEXT_ATTRIBUTES m_attributes;
VECTOR2I m_pos;
mutable bool m_bounding_box_cache_valid;
mutable EDA_RECT m_bounding_box_cache;
TEXT_ATTRIBUTES m_attributes;
VECTOR2I m_pos;
};

View File

@ -122,15 +122,14 @@ public:
* @param aCursor is the current text position (for multiple text blocks within a single text
* object, such as a run of superscript characters)
* @param aAttrs are the styling attributes of the text, including its rotation
* @return bounding box
*/
VECTOR2D Draw( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2I& aPosition,
const VECTOR2I& aCursor, const TEXT_ATTRIBUTES& aAttrs ) const;
void Draw( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2I& aPosition,
const VECTOR2I& aCursor, const TEXT_ATTRIBUTES& aAttrs ) const;
VECTOR2D Draw( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2I& aPosition,
const TEXT_ATTRIBUTES& aAttributes ) const
void Draw( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2I& aPosition,
const TEXT_ATTRIBUTES& aAttributes ) const
{
return Draw( aGal, aText, aPosition, VECTOR2I( 0, 0 ), aAttributes );
Draw( aGal, aText, aPosition, VECTOR2I( 0, 0 ), aAttributes );
}
virtual void DrawText( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2I& aPosition,
@ -141,7 +140,7 @@ public:
*
* @return a VECTOR2D giving the width and height of text.
*/
VECTOR2D StringBoundaryLimits( const UTF8& aText, const VECTOR2D& aSize, int aThickness,
VECTOR2I StringBoundaryLimits( const UTF8& aText, const VECTOR2D& aSize, int aThickness,
bool aBold, bool aItalic ) const;
/**
@ -170,7 +169,7 @@ public:
* @param aTextStyle text style flags
* @return text cursor position after this text
*/
virtual VECTOR2I GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_ptr<GLYPH>>& aGlyphs,
virtual VECTOR2I GetTextAsGlyphs( BOX2I* aBBox, std::vector<std::unique_ptr<GLYPH>>* aGlyphs,
const UTF8& aText, const VECTOR2D& aSize,
const VECTOR2I& aPosition, const EDA_ANGLE& aAngle,
bool aMirror, const VECTOR2I& aOrigin,
@ -206,10 +205,10 @@ protected:
* @param aOrigin is the point around which the text should be rotated, mirrored, etc.
* @return new cursor position in non-rotated, non-mirrored coordinates
*/
VECTOR2D drawSingleLineText( KIGFX::GAL* aGal, BOX2I* aBoundingBox, const UTF8& aText,
const VECTOR2I& aPosition, const VECTOR2D& aSize,
const EDA_ANGLE& aAngle, bool aMirror, const VECTOR2I& aOrigin,
bool aItalic ) const;
void drawSingleLineText( KIGFX::GAL* aGal, BOX2I* aBoundingBox, const UTF8& aText,
const VECTOR2I& aPosition, const VECTOR2D& aSize,
const EDA_ANGLE& aAngle, bool aMirror, const VECTOR2I& aOrigin,
bool aItalic ) const;
/**
* Computes the bounding box for a single line of text.
@ -221,15 +220,14 @@ protected:
* @param aSize is the cap-height and em-width of the text.
* @return new cursor position
*/
VECTOR2D boundingBoxSingleLine( BOX2I* aBBox, const UTF8& aText, const VECTOR2I& aPosition,
VECTOR2I boundingBoxSingleLine( BOX2I* aBBox, const UTF8& aText, const VECTOR2I& aPosition,
const VECTOR2D& aSize, bool aItalic ) const;
void getLinePositions( const UTF8& aText, const VECTOR2I& aPosition,
wxArrayString& aTextLines, std::vector<VECTOR2I>& aPositions,
std::vector<VECTOR2D>& aBoundingBoxes,
const TEXT_ATTRIBUTES& aAttributes ) const;
std::vector<VECTOR2I>& aExtents, const TEXT_ATTRIBUTES& aAttrs ) const;
VECTOR2D drawMarkup( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>& aGlyphs,
VECTOR2I drawMarkup( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>* aGlyphs,
const UTF8& aText, const VECTOR2I& aPosition, const VECTOR2D& aSize,
const EDA_ANGLE& aAngle, bool aMirror, const VECTOR2I& aOrigin,
TEXT_STYLE_FLAGS aTextStyle ) const;
@ -240,9 +238,6 @@ protected:
private:
static FONT* getDefaultFont();
VECTOR2D doDrawString( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2I& aPosition,
bool aParse, const TEXT_ATTRIBUTES& aAttrs ) const;
protected:
wxString m_fontName; ///< Font name
wxString m_fontFileName; ///< Font file name

View File

@ -31,6 +31,10 @@
#include <geometry/shape_poly_set.h>
#include <wx/debug.h>
typedef std::function<void( int, const VECTOR2I& aPoint1, const VECTOR2I& aPoint2,
const VECTOR2I& aPoint3 )>
TRIANGULATE_CALLBACK;
namespace KIFONT
{
class GLYPH
@ -64,6 +68,8 @@ public:
bool IsOutline() const override { return true; }
BOX2D BoundingBox() override;
void Triangulate( TRIANGULATE_CALLBACK aCallback ) const;
};

View File

@ -93,7 +93,7 @@ public:
*/
double GetInterline( double aGlyphHeight = 0.0, double aLineSpacing = 1.0 ) const override;
VECTOR2I GetTextAsGlyphs( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>& aGlyphs,
VECTOR2I GetTextAsGlyphs( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>* aGlyphs,
const UTF8& aText, const VECTOR2D& aSize, const VECTOR2I& aPosition,
const EDA_ANGLE& aAngle, bool aMirror, const VECTOR2I& aOrigin,
TEXT_STYLE_FLAGS aTextStyle ) const override;
@ -106,8 +106,11 @@ public:
* @param aGlyphs returns text glyphs
* @param aText the text item
*/
VECTOR2I GetLinesAsGlyphs( std::vector<std::unique_ptr<GLYPH>>& aGlyphs,
const EDA_TEXT* aText ) const;
void GetLinesAsGlyphs( std::vector<std::unique_ptr<GLYPH>>* aGlyphs,
const EDA_TEXT* aText ) const;
void GetLinesAsGlyphs( std::vector<std::unique_ptr<GLYPH>>* aGlyphs, const UTF8& aText,
const VECTOR2I& aPosition, const TEXT_ATTRIBUTES& aAttrs ) const;
const FT_Face& GetFace() const { return m_face; }

View File

@ -76,7 +76,7 @@ public:
*/
double GetInterline( double aGlyphHeight, double aLineSpacing = 1.0 ) const override;
VECTOR2I GetTextAsGlyphs( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>& aGlyphs,
VECTOR2I GetTextAsGlyphs( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>* aGlyphs,
const UTF8& aText, const VECTOR2D& aSize, const VECTOR2I& aPosition,
const EDA_ANGLE& aAngle, bool aMirror, const VECTOR2I& aOrigin,
TEXT_STYLE_FLAGS aTextStyle ) const override;

View File

@ -1,37 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021 Ola Rinta-Koski
* Copyright (C) 2021-2022 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRIANGULATE_H
#define TRIANGULATE_H
#include <math/vector2d.h>
#include <font/glyph.h>
#include <geometry/shape_poly_set.h>
#include <functional>
typedef std::function<void( int, const VECTOR2I& aPoint1, const VECTOR2I& aPoint2,
const VECTOR2I& aPoint3, void* aCallbackData )>
TRIANGULATE_CALLBACK;
void Triangulate( const SHAPE_POLY_SET& aPolylist, TRIANGULATE_CALLBACK aCallback,
void* aCallbackData = nullptr );
#endif // TRIANGULATE_H

View File

@ -524,8 +524,6 @@ private:
VECTOR2D getScreenPixelSize() const;
void fillPolygonAsTriangles( const SHAPE_POLY_SET& aPolyList );
/**
* Basic OpenGL initialization and feature checks.
*