Support pen and brush styles in wxSVGGraphicsContext

SVG does not allow redefining patterns with the same ID, so write each pattern only once.
This commit is contained in:
Maarten Bent
2026-06-13 22:21:30 +02:00
parent 89d1921478
commit 8bf3e155a7
3 changed files with 55 additions and 14 deletions
+7 -1
View File
@@ -26,6 +26,7 @@
#endif
#include <memory>
#include <set>
class WXDLLIMPEXP_FWD_CORE wxBitmap;
@@ -46,7 +47,7 @@ wxString GetPenStroke(const wxColour& c, int style = wxPENSTYLE_SOLID);
wxString GetBrushFill(const wxColour& c, int style = wxBRUSHSTYLE_SOLID);
// Returns a <pattern> element definition for hatched brushes, or empty.
wxString CreateBrushFill(const wxBrush& brush, wxSVGShapeRenderingMode mode);
wxString CreateBrushFill(const wxBrush& brush, wxSVGShapeRenderingMode mode, wxString& patternName);
// Returns a "shape-rendering=..." attribute fragment.
wxString GetRenderMode(wxSVGShapeRenderingMode style);
@@ -123,6 +124,9 @@ public:
// handler on first use). Sets the write-error flag on failure.
void WriteBitmap(const wxBitmap& bmp, wxCoord x, wxCoord y);
// Writes a brush fill pattern if it has not been written before
void WriteBrushFill(const wxBrush& brush);
// Returns the next gradient/clip id and increments the shared counter.
size_t GetNextGradientId() { return ms_gradientUniqueId++; }
size_t GetNextClipId() { return ms_clipUniqueId++; }
@@ -182,6 +186,8 @@ private:
int m_layerDepth = 0;
int m_clipNestingLevel = 0;
std::set<wxString> m_usedBrushPatterns;
static size_t ms_clipUniqueId;
static size_t ms_gradientUniqueId;
+16 -9
View File
@@ -319,10 +319,10 @@ wxString GetRenderMode(wxSVGShapeRenderingMode style)
return s;
}
wxString CreateBrushFill(const wxBrush& brush, wxSVGShapeRenderingMode mode)
wxString CreateBrushFill(const wxBrush& brush, wxSVGShapeRenderingMode mode, wxString& patternName)
{
wxString s;
wxString patternName = GetBrushStyleName(brush);
patternName = GetBrushStyleName(brush);
if (!patternName.empty())
{
@@ -1455,13 +1455,7 @@ void wxSVGFileDCImpl::SetBrush(const wxBrush& brush)
m_writer->MarkGraphicsChanged();
wxString pattern = CreateBrushFill(m_brush, m_writer->GetShapeRenderingMode());
if ( !pattern.empty() )
{
NewGraphicsIfNeeded();
write(pattern);
}
m_writer->WriteBrushFill(m_brush);
}
void wxSVGFileDCImpl::SetPen(const wxPen& pen)
@@ -1815,6 +1809,19 @@ void wxSVGWriter::WriteBitmap(const wxBitmap& bmp, wxCoord x, wxCoord y)
m_writeError = true;
}
void wxSVGWriter::WriteBrushFill(const wxBrush& brush)
{
if ( !brush.IsOk() )
return;
wxString patternName;
wxString pattern = CreateBrushFill(brush, GetShapeRenderingMode(), patternName);
if ( !pattern.empty() && !patternName.empty() && m_usedBrushPatterns.insert(patternName).second )
{
Write(pattern);
}
}
#if wxUSE_GRAPHICS_CONTEXT
bool wxSVGWriter::SetCompositionMode(wxCompositionMode mode)
+32 -4
View File
@@ -786,6 +786,21 @@ wxSVGGraphicsPenData::wxSVGGraphicsPenData(wxGraphicsRenderer* renderer,
m_pen.SetColour(info.GetColour());
m_pen.SetWidth(static_cast<int>(info.GetWidth() + 0.5));
m_pen.SetStyle(info.GetStyle());
m_pen.SetCap(info.GetCap());
m_pen.SetJoin(info.GetJoin());
if (m_pen.GetStyle() == wxPENSTYLE_USER_DASH)
{
wxDash* dashes;
if (int nb_dashes = info.GetDashes(&dashes))
m_pen.SetDashes(nb_dashes, dashes);
}
if (m_pen.GetStyle() == wxPENSTYLE_STIPPLE)
{
m_pen.SetStipple(info.GetStipple());
}
}
wxSVGGraphicsBrushData::wxSVGGraphicsBrushData(wxGraphicsRenderer* renderer, const wxBrush& brush)
@@ -1307,7 +1322,11 @@ void wxSVGGraphicsContext::SetBrush(const wxGraphicsBrush& brush)
auto* data = static_cast<wxSVGGraphicsBrushData*>(brush.GetRefData());
if ( data != nullptr )
{
m_writer->WriteBrushFill(data->GetBrush());
SyncBrushToDC(data->GetBrush());
}
}
void wxSVGGraphicsContext::SetFont(const wxGraphicsFont& font)
@@ -1408,11 +1427,12 @@ void wxSVGGraphicsContext::StrokePath(const wxGraphicsPath& path)
m_currentPen.GetStyle());
}
const wxString penPattern = wxSVG::GetPenPattern(m_currentPen);
const wxString transform = GetCurrentTransformAttr();
const wxString s = wxString::Format(
wxS(" <path d=\"%s\" fill=\"none\" %s stroke-width=\"%d\"%s/>\n"),
data->GetDString(), stroke, m_currentPen.GetWidth(), transform);
wxS(" <path d=\"%s\" fill=\"none\" %s stroke-width=\"%d\" %s%s/>\n"),
data->GetDString(), stroke, m_currentPen.GetWidth(), penPattern, transform);
m_writer->Write(s);
@@ -1435,6 +1455,13 @@ void wxSVGGraphicsContext::FillPath(const wxGraphicsPath& path, wxPolygonFillMod
if ( !m_brush.IsNull() )
fill = m_writer->WriteGraphicsBrushFill(m_brush);
if ( fill.empty() )
{
auto* brushData = static_cast<wxSVGGraphicsBrushData*>(m_brush.GetRefData());
if ( brushData )
fill = wxSVG::GetBrushPattern(brushData->GetBrush());
}
if ( fill.empty() )
{
fill = m_currentBrush.GetStyle() == wxBRUSHSTYLE_TRANSPARENT
@@ -1443,13 +1470,14 @@ void wxSVGGraphicsContext::FillPath(const wxGraphicsPath& path, wxPolygonFillMod
m_currentBrush.GetStyle());
}
const wxString penPattern = wxSVG::GetPenPattern(m_currentPen);
const wxString transform = GetCurrentTransformAttr();
const wxString rule = (fillStyle == wxODDEVEN_RULE)
? wxS("evenodd") : wxS("nonzero");
const wxString s = wxString::Format(
wxS(" <path d=\"%s\" %s fill-rule=\"%s\" stroke=\"none\"%s/>\n"),
data->GetDString(), fill, rule, transform);
wxS(" <path d=\"%s\" %s fill-rule=\"%s\" stroke=\"none\" %s%s/>\n"),
data->GetDString(), fill, rule, penPattern, transform);
m_writer->Write(s);