Restore support for using faster dotted pens in wxMSW
Changes of d245dc9e1f
(Fix drawing of dotted lines with wxDC in wxMSW,
2020-03-27) improved the appearance of dotted and dashed lines in wxMSW
but at the expense of significant (up to a factor of 300) slowdown.
Allow the applications for which the drawing performance is important to
explicitly request the old behaviour, with uglier, but faster, pens by
choosing to use low quality pens.
Update the graphics benchmark to allow specifying the pen quality and
verify that the performance when using it is the same as before 3.1.4.
See https://github.com/wxWidgets/wxWidgets/pull/2218
See #7097.
Closes #18875.
This commit is contained in:
parent
a5afa85c0a
commit
b53f7ac904
@ -47,6 +47,11 @@ Changes in behaviour not resulting in compilation errors
|
||||
event handlers if you want the standard key combinations such as Alt-Space or
|
||||
Alt-F4 to work.
|
||||
|
||||
- wxMSW port now uses better appearing but much slower pens for dotted and
|
||||
dashed lines. Use wxPenInfo::LowQuality() or wxPen::SetQuality() to return to
|
||||
the previous version behaviour and performance characteristics if you are
|
||||
drawing many lines using such pens.
|
||||
|
||||
- wxOSX port uses default button margins for wxBitmapButton by default, for
|
||||
consistency with the other ports. You now need to call SetMargins(0, 0)
|
||||
explicitly if you really don't want to have any margins in your buttons.
|
||||
|
@ -43,12 +43,14 @@ public:
|
||||
void SetDashes(int nb_dashes, const wxDash *dash) wxOVERRIDE;
|
||||
void SetJoin(wxPenJoin join) wxOVERRIDE;
|
||||
void SetCap(wxPenCap cap) wxOVERRIDE;
|
||||
void SetQuality(wxPenQuality quality) wxOVERRIDE;
|
||||
|
||||
wxColour GetColour() const wxOVERRIDE;
|
||||
int GetWidth() const wxOVERRIDE;
|
||||
wxPenStyle GetStyle() const wxOVERRIDE;
|
||||
wxPenJoin GetJoin() const wxOVERRIDE;
|
||||
wxPenCap GetCap() const wxOVERRIDE;
|
||||
wxPenQuality GetQuality() const wxOVERRIDE;
|
||||
int GetDashes(wxDash** ptr) const wxOVERRIDE;
|
||||
wxDash* GetDash() const;
|
||||
int GetDashCount() const;
|
||||
|
@ -14,6 +14,14 @@
|
||||
#include "wx/gdiobj.h"
|
||||
#include "wx/peninfobase.h"
|
||||
|
||||
// Possible values for pen quality.
|
||||
enum wxPenQuality
|
||||
{
|
||||
wxPEN_QUALITY_DEFAULT, // Select the appropriate quality automatically.
|
||||
wxPEN_QUALITY_LOW, // Less good looking but faster.
|
||||
wxPEN_QUALITY_HIGH // Best looking, at the expense of speed.
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// wxPenInfo contains all parameters describing a wxPen
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -27,6 +35,7 @@ public:
|
||||
: wxPenInfoBase<wxPenInfo>(colour, style)
|
||||
{
|
||||
m_width = width;
|
||||
m_quality = wxPEN_QUALITY_DEFAULT;
|
||||
}
|
||||
|
||||
// Setters
|
||||
@ -34,12 +43,20 @@ public:
|
||||
wxPenInfo& Width(int width)
|
||||
{ m_width = width; return *this; }
|
||||
|
||||
wxPenInfo& Quality(wxPenQuality quality)
|
||||
{ m_quality = quality; return *this; }
|
||||
wxPenInfo& LowQuality() { return Quality(wxPEN_QUALITY_LOW); }
|
||||
wxPenInfo& HighQuality() { return Quality(wxPEN_QUALITY_HIGH); }
|
||||
|
||||
// Accessors
|
||||
|
||||
int GetWidth() const { return m_width; }
|
||||
|
||||
wxPenQuality GetQuality() const { return m_quality; }
|
||||
|
||||
private:
|
||||
int m_width;
|
||||
wxPenQuality m_quality;
|
||||
};
|
||||
|
||||
|
||||
@ -57,12 +74,14 @@ public:
|
||||
virtual void SetDashes(int nb_dashes, const wxDash *dash) = 0;
|
||||
virtual void SetJoin(wxPenJoin join) = 0;
|
||||
virtual void SetCap(wxPenCap cap) = 0;
|
||||
virtual void SetQuality(wxPenQuality quality) { wxUnusedVar(quality); }
|
||||
|
||||
virtual wxColour GetColour() const = 0;
|
||||
virtual wxBitmap *GetStipple() const = 0;
|
||||
virtual wxPenStyle GetStyle() const = 0;
|
||||
virtual wxPenJoin GetJoin() const = 0;
|
||||
virtual wxPenCap GetCap() const = 0;
|
||||
virtual wxPenQuality GetQuality() const { return wxPEN_QUALITY_DEFAULT; }
|
||||
virtual int GetWidth() const = 0;
|
||||
virtual int GetDashes(wxDash **ptr) const = 0;
|
||||
|
||||
|
@ -70,6 +70,31 @@ enum wxPenStyle
|
||||
/**< Last of the hatch styles (inclusive). */
|
||||
};
|
||||
|
||||
/**
|
||||
Possible values for pen quality.
|
||||
|
||||
Pen quality is currently only used in wxMSW, the other ports ignore it and
|
||||
always use the same default pen quality.
|
||||
|
||||
In wxMSW the choice of quality affects whether "cosmetic" or "geometric"
|
||||
native pens are used in situations when both are usable. Notably, for
|
||||
dotted and dashed pens of width 1, high quality geometric pens are used by
|
||||
default since wxWidgets 3.1.4, while previous versions used lower quality
|
||||
but much faster cosmetic pens. If drawing performance is more important
|
||||
than the exact appearance of the lines drawn using this pen, low quality
|
||||
may be explicitly selected.
|
||||
|
||||
See wxPenInfo::Quality() and wxPen::SetQuality().
|
||||
|
||||
@since 3.1.5
|
||||
*/
|
||||
enum wxPenQuality
|
||||
{
|
||||
wxPEN_QUALITY_DEFAULT, ///< Select the appropriate quality automatically.
|
||||
wxPEN_QUALITY_LOW, ///< Less good looking but faster.
|
||||
wxPEN_QUALITY_HIGH ///< Best looking, at the expense of speed.
|
||||
};
|
||||
|
||||
/**
|
||||
The possible join values of a wxPen.
|
||||
|
||||
@ -138,11 +163,42 @@ public:
|
||||
|
||||
wxPenInfo& Cap(wxPenCap cap);
|
||||
|
||||
/**
|
||||
Set the pen quality.
|
||||
|
||||
Using LowQuality() or HighQuality() is usually more convenient.
|
||||
|
||||
@see wxPen::SetQuality()
|
||||
|
||||
@since 3.1.5
|
||||
*/
|
||||
wxPenInfo& Quality(wxPenQuality quality);
|
||||
|
||||
/**
|
||||
Set low pen quality.
|
||||
|
||||
This is the same as calling Quality() with ::wxPEN_QUALITY_LOW.
|
||||
|
||||
@since 3.1.5
|
||||
*/
|
||||
wxPenInfo& LowQuality();
|
||||
|
||||
/**
|
||||
Set high pen quality.
|
||||
|
||||
This is the same as calling Quality() with ::wxPEN_QUALITY_HIGH.
|
||||
|
||||
@since 3.1.5
|
||||
*/
|
||||
wxPenInfo& HighQuality();
|
||||
|
||||
wxPenInfo& LowQuality();
|
||||
wxColour GetColour() const;
|
||||
wxBitmap GetStipple() const;
|
||||
wxPenStyle GetStyle() const;
|
||||
wxPenJoin GetJoin() const;
|
||||
wxPenCap GetCap() const;
|
||||
wxPenQuality GetQuality() const;
|
||||
int GetDashes(wxDash **ptr);
|
||||
int GetDashCount() const;
|
||||
wxDash* GetDash() const;
|
||||
@ -279,6 +335,15 @@ public:
|
||||
*/
|
||||
virtual wxPenCap GetCap() const;
|
||||
|
||||
/**
|
||||
Returns the pen quality.
|
||||
|
||||
The default is ::wxPEN_QUALITY_DEFAULT.
|
||||
|
||||
@see wxPenQuality, SetQuality()
|
||||
*/
|
||||
wxPenQuality GetQuality() const;
|
||||
|
||||
/**
|
||||
Returns a reference to the pen colour.
|
||||
|
||||
@ -375,6 +440,19 @@ public:
|
||||
*/
|
||||
virtual void SetCap(wxPenCap capStyle);
|
||||
|
||||
/**
|
||||
Sets the pen quality.
|
||||
|
||||
Explicitly selecting low pen quality may be useful in wxMSW if drawing
|
||||
performance is more important than the exact appearance of the lines
|
||||
drawn with this pen.
|
||||
|
||||
@see wxPenQuality
|
||||
|
||||
@since 3.1.5
|
||||
*/
|
||||
void SetQuality(wxPenQuality quality);
|
||||
|
||||
//@{
|
||||
/**
|
||||
The pen's colour is changed to the given colour.
|
||||
|
@ -53,6 +53,7 @@ public:
|
||||
m_width == data.m_width &&
|
||||
m_join == data.m_join &&
|
||||
m_cap == data.m_cap &&
|
||||
m_quality == data.m_quality &&
|
||||
m_colour == data.m_colour &&
|
||||
(m_style != wxPENSTYLE_STIPPLE || m_stipple.IsSameAs(data.m_stipple)) &&
|
||||
(m_style != wxPENSTYLE_USER_DASH ||
|
||||
@ -69,6 +70,7 @@ public:
|
||||
wxPenStyle GetStyle() const { return m_style; }
|
||||
wxPenJoin GetJoin() const { return m_join; }
|
||||
wxPenCap GetCap() const { return m_cap; }
|
||||
wxPenQuality GetQuality() const { return m_quality; }
|
||||
wxDash* GetDash() const { return m_dash; }
|
||||
int GetDashCount() const { return m_nbDash; }
|
||||
wxBitmap* GetStipple() const { return const_cast<wxBitmap *>(&m_stipple); }
|
||||
@ -94,6 +96,7 @@ public:
|
||||
|
||||
void SetJoin(wxPenJoin join) { Free(); m_join = join; }
|
||||
void SetCap(wxPenCap cap) { Free(); m_cap = cap; }
|
||||
void SetQuality(wxPenQuality quality) { Free(); m_quality = quality; }
|
||||
|
||||
|
||||
// HPEN management
|
||||
@ -119,6 +122,7 @@ private:
|
||||
{
|
||||
m_join = wxJOIN_ROUND;
|
||||
m_cap = wxCAP_ROUND;
|
||||
m_quality = wxPEN_QUALITY_DEFAULT;
|
||||
m_nbDash = 0;
|
||||
m_dash = NULL;
|
||||
m_hPen = 0;
|
||||
@ -128,6 +132,7 @@ private:
|
||||
wxPenStyle m_style;
|
||||
wxPenJoin m_join;
|
||||
wxPenCap m_cap;
|
||||
wxPenQuality m_quality;
|
||||
wxBitmap m_stipple;
|
||||
int m_nbDash;
|
||||
wxDash * m_dash;
|
||||
@ -161,6 +166,7 @@ wxPenRefData::wxPenRefData(const wxPenRefData& data)
|
||||
m_width = data.m_width;
|
||||
m_join = data.m_join;
|
||||
m_cap = data.m_cap;
|
||||
m_quality = data.m_quality;
|
||||
m_nbDash = data.m_nbDash;
|
||||
m_dash = data.m_dash;
|
||||
m_hPen = 0;
|
||||
@ -176,6 +182,7 @@ wxPenRefData::wxPenRefData(const wxPenInfo& info)
|
||||
m_width = info.GetWidth();
|
||||
m_join = info.GetJoin();
|
||||
m_cap = info.GetCap();
|
||||
m_quality = info.GetQuality();
|
||||
m_nbDash = info.GetDashes(&m_dash);
|
||||
}
|
||||
|
||||
@ -278,10 +285,54 @@ bool wxPenRefData::Alloc()
|
||||
const COLORREF col = m_colour.GetPixel();
|
||||
|
||||
// check if it's a standard kind of pen which can be created with just
|
||||
// CreatePen()
|
||||
if ( m_join == wxJOIN_ROUND &&
|
||||
m_cap == wxCAP_ROUND &&
|
||||
m_style == wxPENSTYLE_SOLID )
|
||||
// CreatePen(), which always creates cosmetic pens that don't support all
|
||||
// wxPen features and are less precise (e.g. draw dotted lines as dashes
|
||||
// rather than real dots), but much, much faster than geometric pens created
|
||||
// by ExtCreatePen(), see #18875, so we still prefer to use them if possible
|
||||
// unless it's explicitly disabled by setting the quality to "high"
|
||||
bool useCreatePen = m_quality != wxPEN_QUALITY_HIGH;
|
||||
|
||||
if ( useCreatePen )
|
||||
{
|
||||
switch ( m_style )
|
||||
{
|
||||
case wxPENSTYLE_SOLID:
|
||||
// No problem with using cosmetic pens for solid lines.
|
||||
break;
|
||||
|
||||
case wxPENSTYLE_DOT:
|
||||
case wxPENSTYLE_LONG_DASH:
|
||||
case wxPENSTYLE_SHORT_DASH:
|
||||
case wxPENSTYLE_DOT_DASH:
|
||||
if ( m_width > 1 )
|
||||
{
|
||||
// Cosmetic pens with these styles would result in solid
|
||||
// lines for pens wider than a single pixel, so never use
|
||||
// them in this case.
|
||||
useCreatePen = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// For the single pixel pens we can use cosmetic pens, but
|
||||
// they look ugly, so we prefer to not do it by default,
|
||||
// however this can be explicitly requested if speed is more
|
||||
// important than the exact appearance.
|
||||
useCreatePen = m_quality == wxPEN_QUALITY_LOW;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Other styles are not supported by cosmetic pens at all.
|
||||
useCreatePen = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Join and cap styles are also not supported for cosmetic pens.
|
||||
if ( m_join != wxJOIN_ROUND || m_cap != wxCAP_ROUND )
|
||||
useCreatePen = false;
|
||||
|
||||
if ( useCreatePen )
|
||||
{
|
||||
m_hPen = ::CreatePen(ConvertPenStyle(m_style), m_width, col);
|
||||
}
|
||||
@ -505,6 +556,13 @@ void wxPen::SetCap(wxPenCap cap)
|
||||
M_PENDATA->SetCap(cap);
|
||||
}
|
||||
|
||||
void wxPen::SetQuality(wxPenQuality quality)
|
||||
{
|
||||
AllocExclusive();
|
||||
|
||||
M_PENDATA->SetQuality(quality);
|
||||
}
|
||||
|
||||
wxColour wxPen::GetColour() const
|
||||
{
|
||||
wxCHECK_MSG( IsOk(), wxNullColour, wxT("invalid pen") );
|
||||
@ -540,6 +598,13 @@ wxPenCap wxPen::GetCap() const
|
||||
return M_PENDATA->GetCap();
|
||||
}
|
||||
|
||||
wxPenQuality wxPen::GetQuality() const
|
||||
{
|
||||
wxCHECK_MSG( IsOk(), wxPEN_QUALITY_DEFAULT, wxT("invalid pen") );
|
||||
|
||||
return M_PENDATA->GetQuality();
|
||||
}
|
||||
|
||||
int wxPen::GetDashes(wxDash** ptr) const
|
||||
{
|
||||
wxCHECK_MSG( IsOk(), -1, wxT("invalid pen") );
|
||||
|
@ -52,6 +52,7 @@ struct GraphicsBenchmarkOptions
|
||||
mapMode = 0;
|
||||
penWidth = 0;
|
||||
penStyle = wxPENSTYLE_INVALID;
|
||||
penQuality = wxPEN_QUALITY_DEFAULT;
|
||||
|
||||
width = 800;
|
||||
height = 600;
|
||||
@ -87,6 +88,7 @@ struct GraphicsBenchmarkOptions
|
||||
numIters;
|
||||
|
||||
wxPenStyle penStyle;
|
||||
wxPenQuality penQuality;
|
||||
|
||||
bool testBitmaps,
|
||||
testImages,
|
||||
@ -410,16 +412,29 @@ private:
|
||||
{
|
||||
if ( opts.mapMode != 0 )
|
||||
dc.SetMapMode((wxMappingMode)opts.mapMode);
|
||||
|
||||
bool setPen = false;
|
||||
wxPenInfo penInfo(*wxWHITE);
|
||||
if ( opts.penWidth != 0 )
|
||||
dc.SetPen(wxPen(*wxWHITE, opts.penWidth));
|
||||
{
|
||||
penInfo.Width(opts.penWidth);
|
||||
setPen = true;
|
||||
}
|
||||
|
||||
if ( opts.penStyle != wxPENSTYLE_INVALID )
|
||||
{
|
||||
wxPen pen = dc.GetPen();
|
||||
if ( !pen.IsOk() )
|
||||
pen = wxPen(*wxWHITE, 1);
|
||||
pen.SetStyle(opts.penStyle);
|
||||
dc.SetPen(pen);
|
||||
penInfo.Style(opts.penStyle);
|
||||
setPen = true;
|
||||
}
|
||||
|
||||
if ( opts.penQuality != wxPEN_QUALITY_DEFAULT )
|
||||
{
|
||||
penInfo.Quality(opts.penQuality);
|
||||
setPen = true;
|
||||
}
|
||||
|
||||
if ( setPen )
|
||||
dc.SetPen(penInfo);
|
||||
}
|
||||
|
||||
void BenchmarkLines(const wxString& msg, wxDC& dc)
|
||||
@ -865,6 +880,7 @@ public:
|
||||
{ wxCMD_LINE_OPTION, "m", "map-mode", "", wxCMD_LINE_VAL_NUMBER },
|
||||
{ wxCMD_LINE_OPTION, "p", "pen-width", "", wxCMD_LINE_VAL_NUMBER },
|
||||
{ wxCMD_LINE_OPTION, "s", "pen-style", "solid | dot | long_dash | short_dash", wxCMD_LINE_VAL_STRING },
|
||||
{ wxCMD_LINE_OPTION, "", "pen-quality", "default | low | high", wxCMD_LINE_VAL_STRING },
|
||||
{ wxCMD_LINE_OPTION, "w", "width", "", wxCMD_LINE_VAL_NUMBER },
|
||||
{ wxCMD_LINE_OPTION, "h", "height", "", wxCMD_LINE_VAL_NUMBER },
|
||||
{ wxCMD_LINE_OPTION, "I", "images", "", wxCMD_LINE_VAL_NUMBER },
|
||||
@ -913,6 +929,19 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
wxString penQuality;
|
||||
if ( parser.Found("pen-quality", &penQuality) )
|
||||
{
|
||||
if ( penQuality == "low" )
|
||||
opts.penQuality = wxPEN_QUALITY_LOW;
|
||||
else if ( penQuality == "high" )
|
||||
opts.penQuality = wxPEN_QUALITY_HIGH;
|
||||
else if ( penQuality != "default" )
|
||||
{
|
||||
wxLogError("Unsupported pen quality.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ( parser.Found("w", &opts.width) && opts.width < 1 )
|
||||
return false;
|
||||
if ( parser.Found("h", &opts.height) && opts.height < 1 )
|
||||
|
Loading…
Reference in New Issue
Block a user