From 52cc838b124deffa0521dbb77b901b317e70195d Mon Sep 17 00:00:00 2001 From: Paul Cornett Date: Mon, 5 Apr 2021 09:56:28 -0700 Subject: [PATCH] Implement 0-width pen consistently in wxGraphicsContext Emulate a 1-pixel pen width as closely as possible. This reverts: 334cf1cc91 (Take HiDPI scale into account for wxGCDC 0-width pen, 2021-04-03) 0d80050057 (Make wxGCDC behavior with 0-width wxPen consistent with MSW wxDC, 2021-03-02) See #19077, #19115 --- include/wx/graphics.h | 4 +++ src/common/dcgraph.cpp | 30 ++-------------- src/common/graphcmn.cpp | 7 ++++ src/generic/graphicc.cpp | 70 +++++++++++++++++++++++-------------- src/gtk/dc.cpp | 12 +++---- src/msw/graphics.cpp | 59 +++++++++++++++++++------------ src/msw/graphicsd2d.cpp | 70 +++++++++++++++++++++++++++---------- src/osx/carbon/dcclient.cpp | 2 +- src/osx/carbon/graphics.cpp | 62 ++++++++++++++++++-------------- src/osx/core/dcmemory.cpp | 4 ++- 10 files changed, 190 insertions(+), 130 deletions(-) diff --git a/include/wx/graphics.h b/include/wx/graphics.h index 8196cb8ce3..084f308ba1 100644 --- a/include/wx/graphics.h +++ b/include/wx/graphics.h @@ -878,6 +878,9 @@ public: void DisableOffset() { EnableOffset(false); } bool OffsetEnabled() const { return m_enableOffset; } + void SetContentScaleFactor(double contentScaleFactor); + double GetContentScaleFactor() const { return m_contentScaleFactor; } + protected: // These fields must be initialized in the derived class ctors. wxDouble m_width, @@ -912,6 +915,7 @@ private: // Create() or the associated window of the wxDC this context was created // from. wxWindow* const m_window; + double m_contentScaleFactor; wxDECLARE_NO_COPY_CLASS(wxGraphicsContext); wxDECLARE_ABSTRACT_CLASS(wxGraphicsContext); diff --git a/src/common/dcgraph.cpp b/src/common/dcgraph.cpp index ea4af48ceb..eacd2b4c57 100644 --- a/src/common/dcgraph.cpp +++ b/src/common/dcgraph.cpp @@ -522,36 +522,10 @@ void wxGCDCImpl::SetFont( const wxFont &font ) void wxGCDCImpl::SetPen( const wxPen &pen ) { m_pen = pen; - if (m_graphicContext == NULL) - return; - - wxPenStyle style; - if (!pen.IsOk() || (style = pen.GetStyle()) == wxPENSTYLE_TRANSPARENT) + if ( m_graphicContext ) { - m_graphicContext->SetPen(wxGraphicsPen()); - return; + m_graphicContext->SetPen( m_pen ); } - - // 0-width pen is 1 pixel wide with MSW wxDC - const int w = pen.GetWidth(); - const double width = w ? double(w) : 1 / (wxMin(m_scaleX, m_scaleY) * m_contentScaleFactor); - - wxGraphicsPenInfo info(pen.GetColour(), width, style); - info.Join(pen.GetJoin()).Cap(pen.GetCap()); - - if (style == wxPENSTYLE_USER_DASH) - { - wxDash* dashes; - if (int n = pen.GetDashes(&dashes)) - info.Dashes(n, dashes); - } - else if (style == wxPENSTYLE_STIPPLE) - { - if (const wxBitmap* stipple = pen.GetStipple()) - info.Stipple(*stipple); - } - - m_graphicContext->SetPen(m_graphicContext->CreatePen(info)); } void wxGCDCImpl::SetBrush( const wxBrush &brush ) diff --git a/src/common/graphcmn.cpp b/src/common/graphcmn.cpp index a17d1602a1..15a74aa8d5 100644 --- a/src/common/graphcmn.cpp +++ b/src/common/graphcmn.cpp @@ -572,6 +572,7 @@ wxGraphicsContext::wxGraphicsContext(wxGraphicsRenderer* renderer, m_enableOffset(false), m_window(window) { + m_contentScaleFactor = window ? window->GetContentScaleFactor() : 1.0; } wxGraphicsContext::~wxGraphicsContext() @@ -605,6 +606,12 @@ void wxGraphicsContext::EnableOffset(bool enable) m_enableOffset = enable; } +void wxGraphicsContext::SetContentScaleFactor(double contentScaleFactor) +{ + m_enableOffset = true; + m_contentScaleFactor = contentScaleFactor; +} + #if 0 void wxGraphicsContext::SetAlpha( wxDouble WXUNUSED(alpha) ) { diff --git a/src/generic/graphicc.cpp b/src/generic/graphicc.cpp index a6920767e7..a14d15dab2 100644 --- a/src/generic/graphicc.cpp +++ b/src/generic/graphicc.cpp @@ -441,17 +441,23 @@ public: virtual bool ShouldOffset() const wxOVERRIDE { - if ( !m_enableOffset ) + if (!m_enableOffset || m_pen.IsNull()) return false; - int penwidth = 0 ; - if ( !m_pen.IsNull() ) - { - penwidth = (int)((wxCairoPenData*)m_pen.GetRefData())->GetWidth(); - if ( penwidth == 0 ) - penwidth = 1; - } - return ( penwidth % 2 ) == 1; + const double width = static_cast(m_pen.GetRefData())->GetWidth(); + + // always offset for 1-pixel width + if (width <= 0) + return true; + + // no offset if overall scale is not odd integer + double x = GetContentScaleFactor(), y = x; + cairo_user_to_device_distance(m_context, &x, &y); + if (!wxIsSameDouble(fmod(wxMin(fabs(x), fabs(y)), 2.0), 1.0)) + return false; + + // offset if pen width is odd integer + return wxIsSameDouble(fmod(width, 2.0), 1.0); } virtual void Clip( const wxRegion ®ion ) wxOVERRIDE; @@ -844,8 +850,6 @@ wxCairoPenData::wxCairoPenData( wxGraphicsRenderer* renderer, const wxGraphicsPe { Init(); m_width = info.GetWidth(); - if (m_width <= 0.0) - m_width = 0.1; switch ( info.GetCap() ) { @@ -1017,7 +1021,14 @@ void wxCairoPenData::Apply( wxGraphicsContext* context ) wxCairoPenBrushBaseData::Apply(context); cairo_t * ctext = (cairo_t*) context->GetNativeContext(); - cairo_set_line_width(ctext,m_width); + double width = m_width; + if (width <= 0) + { + double x = context->GetContentScaleFactor(), y = x; + cairo_user_to_device_distance(ctext, &x, &y); + width = 1 / wxMin(fabs(x), fabs(y)); + } + cairo_set_line_width(ctext, width); cairo_set_line_cap(ctext,m_cap); cairo_set_line_join(ctext,m_join); cairo_set_dash(ctext, m_lengths, m_count, 0); @@ -1912,21 +1923,26 @@ wxCairoBitmapData::~wxCairoBitmapData() class wxCairoOffsetHelper { public : - wxCairoOffsetHelper( cairo_t* ctx , bool offset ) + wxCairoOffsetHelper(cairo_t* ctx, double scaleFactor, bool offset) { m_ctx = ctx; - m_offset = offset; - if ( m_offset ) - cairo_translate( m_ctx, 0.5, 0.5 ); + m_offset = 0; + if (offset) + { + double x = scaleFactor, y = x; + cairo_user_to_device_distance(ctx, &x, &y); + m_offset = 0.5 / wxMin(fabs(x), fabs(y)); + cairo_translate(m_ctx, m_offset, m_offset); + } } ~wxCairoOffsetHelper( ) { - if ( m_offset ) - cairo_translate( m_ctx, -0.5, -0.5 ); + if (m_offset > 0) + cairo_translate(m_ctx, -m_offset, -m_offset); } -public : +private: cairo_t* m_ctx; - bool m_offset; + double m_offset; } ; #if wxUSE_PRINTING_ARCHITECTURE @@ -1976,7 +1992,7 @@ wxCairoContext::wxCairoContext( wxGraphicsRenderer* renderer, const wxWindowDC& m_width = width; m_height = height; - m_enableOffset = dc.GetContentScaleFactor() <= 1; + EnableOffset(); #ifdef __WXMSW__ HDC hdc = (HDC)dc.GetHDC(); @@ -2029,7 +2045,7 @@ wxCairoContext::wxCairoContext( wxGraphicsRenderer* renderer, const wxMemoryDC& m_width = width; m_height = height; - m_enableOffset = dc.GetContentScaleFactor() <= 1; + SetContentScaleFactor(dc.GetContentScaleFactor()); #ifdef __WXMSW__ wxBitmap bmp = dc.GetSelectedBitmap(); @@ -2352,7 +2368,7 @@ wxCairoContext::wxCairoContext(wxGraphicsRenderer* renderer, HWND hWnd) { // See remarks for wxWindowBase::GetContentScaleFactor double scaleY = ::GetDeviceCaps((HDC)m_mswWindowHDC, LOGPIXELSY) / 96.0f; - m_enableOffset = scaleY <= 1.0; + SetContentScaleFactor(scaleY); m_mswStateSavedDC = 0; m_mswSurface = cairo_win32_surface_create((HDC)m_mswWindowHDC); @@ -2393,7 +2409,7 @@ wxCairoContext::wxCairoContext( wxGraphicsRenderer* renderer, wxWindow *window) , m_mswWindowHDC(GetHwndOf(window)) #endif { - m_enableOffset = window->GetContentScaleFactor() <= 1; + EnableOffset(); #ifdef __WXGTK__ // something along these lines (copied from dcclient) @@ -2605,7 +2621,7 @@ void wxCairoContext::StrokePath( const wxGraphicsPath& path ) { if ( !m_pen.IsNull() ) { - wxCairoOffsetHelper helper( m_context, ShouldOffset() ) ; + wxCairoOffsetHelper helper(m_context, GetContentScaleFactor(), ShouldOffset()); cairo_path_t* cp = (cairo_path_t*) path.GetNativePath() ; cairo_append_path(m_context,cp); ((wxCairoPenData*)m_pen.GetRefData())->Apply(this); @@ -2618,7 +2634,7 @@ void wxCairoContext::FillPath( const wxGraphicsPath& path , wxPolygonFillMode fi { if ( !m_brush.IsNull() ) { - wxCairoOffsetHelper helper( m_context, ShouldOffset() ) ; + wxCairoOffsetHelper helper(m_context, GetContentScaleFactor(), ShouldOffset()); cairo_path_t* cp = (cairo_path_t*) path.GetNativePath() ; cairo_append_path(m_context,cp); ((wxCairoBrushData*)m_brush.GetRefData())->Apply(this); @@ -2647,7 +2663,7 @@ void wxCairoContext::DrawRectangle( wxDouble x, wxDouble y, wxDouble w, wxDouble } if ( !m_pen.IsNull() ) { - wxCairoOffsetHelper helper( m_context, ShouldOffset() ) ; + wxCairoOffsetHelper helper(m_context, GetContentScaleFactor(), ShouldOffset()); ((wxCairoPenData*)m_pen.GetRefData())->Apply(this); cairo_rectangle(m_context, x, y, w, h); cairo_stroke(m_context); diff --git a/src/gtk/dc.cpp b/src/gtk/dc.cpp index 519c998b3c..917a98acaa 100644 --- a/src/gtk/dc.cpp +++ b/src/gtk/dc.cpp @@ -407,7 +407,7 @@ wxWindowDCImpl::wxWindowDCImpl(wxWindowDC* owner, wxWindow* window) AdjustForRTL(cr); wxGraphicsContext* gc = wxGraphicsContext::CreateFromNative(cr); cairo_destroy(cr); - gc->EnableOffset(m_contentScaleFactor <= 1); + gc->SetContentScaleFactor(m_contentScaleFactor); SetGraphicsContext(gc); GtkAllocation a; gtk_widget_get_allocation(widget, &a); @@ -456,7 +456,7 @@ wxClientDCImpl::wxClientDCImpl(wxClientDC* owner, wxWindow* window) AdjustForRTL(cr); wxGraphicsContext* gc = wxGraphicsContext::CreateFromNative(cr); cairo_destroy(cr); - gc->EnableOffset(m_contentScaleFactor <= 1); + gc->SetContentScaleFactor(m_contentScaleFactor); SetGraphicsContext(gc); if (!gtk_widget_get_has_window(widget)) { @@ -480,7 +480,7 @@ wxPaintDCImpl::wxPaintDCImpl(wxPaintDC* owner, wxWindow* window) wxCHECK_RET(cr, "using wxPaintDC without being in a native paint event"); InitSize(gtk_widget_get_window(window->m_wxwindow)); wxGraphicsContext* gc = wxGraphicsContext::CreateFromNative(cr); - gc->EnableOffset(m_contentScaleFactor <= 1); + gc->SetContentScaleFactor(m_contentScaleFactor); SetGraphicsContext(gc); // context is already adjusted for RTL m_layoutDir = window->GetLayoutDirection(); @@ -510,7 +510,7 @@ wxScreenDCImpl::wxScreenDCImpl(wxScreenDC* owner) cairo_t* cr = gdk_cairo_create(window); wxGraphicsContext* gc = wxGraphicsContext::CreateFromNative(cr); cairo_destroy(cr); - gc->EnableOffset(m_contentScaleFactor <= 1); + gc->SetContentScaleFactor(m_contentScaleFactor); SetGraphicsContext(gc); } @@ -573,7 +573,7 @@ void wxMemoryDCImpl::Setup() AdjustForRTL(cr); gc = wxGraphicsContext::CreateFromNative(cr); cairo_destroy(cr); - gc->EnableOffset(m_contentScaleFactor <= 1); + gc->SetContentScaleFactor(m_contentScaleFactor); } SetGraphicsContext(gc); } @@ -583,7 +583,7 @@ wxGTKCairoDC::wxGTKCairoDC(cairo_t* cr, wxWindow* window, wxLayoutDirection dir, : wxDC(new wxGTKCairoDCImpl(this, window, dir, width)) { wxGraphicsContext* gc = wxGraphicsContext::CreateFromNative(cr); - gc->EnableOffset(window->GetContentScaleFactor() <= 1); + gc->SetContentScaleFactor(window->GetContentScaleFactor()); SetGraphicsContext(gc); if (dir == wxLayout_Default) SetLayoutDirection(window->GetLayoutDirection()); diff --git a/src/msw/graphics.cpp b/src/msw/graphics.cpp index 8a0207a437..de04226eea 100644 --- a/src/msw/graphics.cpp +++ b/src/msw/graphics.cpp @@ -827,8 +827,6 @@ wxGDIPlusPenData::wxGDIPlusPenData( wxGraphicsRenderer* renderer, { Init(); m_width = info.GetWidth(); - if (m_width <= 0.0) - m_width = 0.1; m_pen = new Pen(wxColourToColor(info.GetColour()), m_width ); @@ -1827,21 +1825,29 @@ void * wxGDIPlusMatrixData::GetNativeMatrix() const class wxGDIPlusOffsetHelper { public : - wxGDIPlusOffsetHelper( Graphics* gr , bool offset ) + wxGDIPlusOffsetHelper(Graphics* gr, double scaleFactor, bool offset) { m_gr = gr; - m_offset = offset; - if ( m_offset ) - m_gr->TranslateTransform( 0.5, 0.5 ); + m_offset = 0; + if (offset) + { + Matrix matrix; + gr->GetTransform(&matrix); + const float f = float(scaleFactor); + PointF pt(f, f); + matrix.TransformVectors(&pt); + m_offset = 0.5f / wxMin(std::abs(pt.X), std::abs(pt.Y)); + m_gr->TranslateTransform(m_offset, m_offset); + } } ~wxGDIPlusOffsetHelper( ) { - if ( m_offset ) - m_gr->TranslateTransform( -0.5, -0.5 ); + if (m_offset > 0) + m_gr->TranslateTransform(-m_offset, -m_offset); } public : Graphics* m_gr; - bool m_offset; + float m_offset; } ; wxGDIPlusContext::wxGDIPlusContext( wxGraphicsRenderer* renderer, HDC hdc, wxDouble width, wxDouble height ) @@ -1952,7 +1958,7 @@ void wxGDIPlusContext::DrawRectangle( wxDouble x, wxDouble y, wxDouble w, wxDoub if (m_composition == wxCOMPOSITION_DEST) return; - wxGDIPlusOffsetHelper helper( m_context , ShouldOffset() ); + wxGDIPlusOffsetHelper helper(m_context, GetContentScaleFactor(), ShouldOffset()); Brush *brush = m_brush.IsNull() ? NULL : ((wxGDIPlusBrushData*)m_brush.GetRefData())->GetGDIPlusBrush(); Pen *pen = m_pen.IsNull() ? NULL : ((wxGDIPlusPenData*)m_pen.GetGraphicsData())->GetGDIPlusPen(); @@ -1991,7 +1997,7 @@ void wxGDIPlusContext::StrokeLines( size_t n, const wxPoint2DDouble *points) if ( !m_pen.IsNull() ) { - wxGDIPlusOffsetHelper helper( m_context , ShouldOffset() ); + wxGDIPlusOffsetHelper helper(m_context, GetContentScaleFactor(), ShouldOffset()); PointF *cpoints = new PointF[n]; for (size_t i = 0; i < n; i++) { @@ -2009,7 +2015,7 @@ void wxGDIPlusContext::DrawLines( size_t n, const wxPoint2DDouble *points, wxPol if (m_composition == wxCOMPOSITION_DEST) return; - wxGDIPlusOffsetHelper helper( m_context , ShouldOffset() ); + wxGDIPlusOffsetHelper helper(m_context, GetContentScaleFactor(), ShouldOffset()); PointF *cpoints = new PointF[n]; for (size_t i = 0; i < n; i++) { @@ -2032,7 +2038,7 @@ void wxGDIPlusContext::StrokePath( const wxGraphicsPath& path ) if ( !m_pen.IsNull() ) { - wxGDIPlusOffsetHelper helper( m_context , ShouldOffset() ); + wxGDIPlusOffsetHelper helper(m_context, GetContentScaleFactor(), ShouldOffset()); m_context->DrawPath( ((wxGDIPlusPenData*)m_pen.GetGraphicsData())->GetGDIPlusPen() , (GraphicsPath*) path.GetNativePath() ); } } @@ -2044,7 +2050,7 @@ void wxGDIPlusContext::FillPath( const wxGraphicsPath& path , wxPolygonFillMode if ( !m_brush.IsNull() ) { - wxGDIPlusOffsetHelper helper( m_context , ShouldOffset() ); + wxGDIPlusOffsetHelper helper(m_context, GetContentScaleFactor(), ShouldOffset()); ((GraphicsPath*) path.GetNativePath())->SetFillMode( fillStyle == wxODDEVEN_RULE ? FillModeAlternate : FillModeWinding); m_context->FillPath( ((wxGDIPlusBrushData*)m_brush.GetRefData())->GetGDIPlusBrush() , (GraphicsPath*) path.GetNativePath()); @@ -2429,17 +2435,24 @@ void wxGDIPlusContext::GetPartialTextExtents(const wxString& text, wxArrayDouble bool wxGDIPlusContext::ShouldOffset() const { - if ( !m_enableOffset ) + if (!m_enableOffset || m_pen.IsNull()) return false; - int penwidth = 0 ; - if ( !m_pen.IsNull() ) - { - penwidth = (int)((wxGDIPlusPenData*)m_pen.GetRefData())->GetWidth(); - if ( penwidth == 0 ) - penwidth = 1; - } - return ( penwidth % 2 ) == 1; + double width = static_cast(m_pen.GetRefData())->GetWidth(); + + // always offset for 1-pixel width + if (width <= 0) + return true; + + // no offset if overall scale is not odd integer + const wxGraphicsMatrix matrix(GetTransform()); + double x = GetContentScaleFactor(), y = x; + matrix.TransformDistance(&x, &y); + if (!wxIsSameDouble(fmod(wxMin(fabs(x), fabs(y)), 2.0), 1.0)) + return false; + + // offset if pen width is odd integer + return wxIsSameDouble(fmod(width, 2.0), 1.0); } void* wxGDIPlusContext::GetNativeContext() diff --git a/src/msw/graphicsd2d.cpp b/src/msw/graphicsd2d.cpp index 9010698048..386be2d678 100644 --- a/src/msw/graphicsd2d.cpp +++ b/src/msw/graphicsd2d.cpp @@ -1107,24 +1107,31 @@ wxCOMPtr wxD2DConvertRegionToGeometry(ID2D1Factory* direct2dFacto class wxD2DOffsetHelper { public: - wxD2DOffsetHelper(wxGraphicsContext* g) : m_context(g) + wxD2DOffsetHelper(wxGraphicsContext* g, double scaleFactor) + : m_context(g) { + m_offset = 0; if (m_context->ShouldOffset()) { - m_context->Translate(0.5, 0.5); + const wxGraphicsMatrix matrix(m_context->GetTransform()); + double x = m_context->GetContentScaleFactor(), y = x; + matrix.TransformDistance(&x, &y); + m_offset = 0.5 / wxMin(fabs(x), fabs(y)); + m_context->Translate(m_offset, m_offset); } } ~wxD2DOffsetHelper() { - if (m_context->ShouldOffset()) + if (m_offset > 0) { - m_context->Translate(-0.5, -0.5); + m_context->Translate(-m_offset, -m_offset); } } private: wxGraphicsContext* m_context; + double m_offset; }; bool operator==(const D2D1::Matrix3x2F& lhs, const D2D1::Matrix3x2F& rhs) @@ -3033,6 +3040,8 @@ public: ID2D1Brush* GetBrush(); FLOAT GetWidth(); + bool IsZeroWidth() const; + void SetWidth(const wxGraphicsContext* context); ID2D1StrokeStyle* GetStrokeStyle(); @@ -3146,6 +3155,17 @@ void wxD2DPenData::CreateStrokeStyle(ID2D1Factory* const direct2dfactory) delete[] dashes; } +void wxD2DPenData::SetWidth(const wxGraphicsContext* context) +{ + if (m_penInfo.GetWidth() <= 0) + { + const wxGraphicsMatrix matrix(context->GetTransform()); + double x = context->GetContentScaleFactor(), y = x; + matrix.TransformDistance(&x, &y); + m_width = 1 / wxMin(fabs(x), fabs(y)); + } +} + ID2D1Brush* wxD2DPenData::GetBrush() { return m_stippleBrush->GetBrush(); @@ -3156,6 +3176,11 @@ FLOAT wxD2DPenData::GetWidth() return m_width; } +bool wxD2DPenData::IsZeroWidth() const +{ + return m_penInfo.GetWidth() <= 0; +} + ID2D1StrokeStyle* wxD2DPenData::GetStrokeStyle() { return m_strokeStyle; @@ -4321,7 +4346,7 @@ void wxD2DContext::StrokePath(const wxGraphicsPath& p) if (m_composition == wxCOMPOSITION_DEST) return; - wxD2DOffsetHelper helper(this); + wxD2DOffsetHelper helper(this, GetContentScaleFactor()); EnsureInitialized(); AdjustRenderTargetSize(); @@ -4332,6 +4357,7 @@ void wxD2DContext::StrokePath(const wxGraphicsPath& p) if (!m_pen.IsNull()) { wxD2DPenData* penData = wxGetD2DPenData(m_pen); + penData->SetWidth(this); penData->Bind(this); ID2D1Brush* nativeBrush = penData->GetBrush(); GetRenderTarget()->DrawGeometry((ID2D1Geometry*)pathData->GetNativePath(), nativeBrush, penData->GetWidth(), penData->GetStrokeStyle()); @@ -4690,19 +4716,24 @@ void wxD2DContext::GetPartialTextExtents(const wxString& text, wxArrayDouble& wi bool wxD2DContext::ShouldOffset() const { - if (!m_enableOffset) - { + if (!m_enableOffset || m_pen.IsNull()) return false; - } - int penWidth = 0; - if (!m_pen.IsNull()) - { - penWidth = wxGetD2DPenData(m_pen)->GetWidth(); - penWidth = wxMax(penWidth, 1); - } + wxD2DPenData* const penData = wxGetD2DPenData(m_pen); - return (penWidth % 2) == 1; + // always offset for 1-pixel width + if (penData->IsZeroWidth()) + return true; + + // no offset if overall scale is not odd integer + const wxGraphicsMatrix matrix(GetTransform()); + double x = GetContentScaleFactor(), y = x; + matrix.TransformDistance(&x, &y); + if (!wxIsSameDouble(fmod(wxMin(fabs(x), fabs(y)), 2.0), 1.0)) + return false; + + // offset if pen width is odd integer + return wxIsSameDouble(fmod(double(penData->GetWidth()), 2.0), 1.0); } void wxD2DContext::DoDrawText(const wxString& str, wxDouble x, wxDouble y) @@ -4779,7 +4810,7 @@ void wxD2DContext::DrawRectangle(wxDouble x, wxDouble y, wxDouble w, wxDouble h) if (m_composition == wxCOMPOSITION_DEST) return; - wxD2DOffsetHelper helper(this); + wxD2DOffsetHelper helper(this, GetContentScaleFactor()); EnsureInitialized(); AdjustRenderTargetSize(); @@ -4797,6 +4828,7 @@ void wxD2DContext::DrawRectangle(wxDouble x, wxDouble y, wxDouble w, wxDouble h) if (!m_pen.IsNull()) { wxD2DPenData* penData = wxGetD2DPenData(m_pen); + penData->SetWidth(this); penData->Bind(this); GetRenderTarget()->DrawRectangle(rect, penData->GetBrush(), penData->GetWidth(), penData->GetStrokeStyle()); } @@ -4807,7 +4839,7 @@ void wxD2DContext::DrawRoundedRectangle(wxDouble x, wxDouble y, wxDouble w, wxDo if (m_composition == wxCOMPOSITION_DEST) return; - wxD2DOffsetHelper helper(this); + wxD2DOffsetHelper helper(this, GetContentScaleFactor()); EnsureInitialized(); AdjustRenderTargetSize(); @@ -4826,6 +4858,7 @@ void wxD2DContext::DrawRoundedRectangle(wxDouble x, wxDouble y, wxDouble w, wxDo if (!m_pen.IsNull()) { wxD2DPenData* penData = wxGetD2DPenData(m_pen); + penData->SetWidth(this); penData->Bind(this); GetRenderTarget()->DrawRoundedRectangle(roundedRect, penData->GetBrush(), penData->GetWidth(), penData->GetStrokeStyle()); } @@ -4836,7 +4869,7 @@ void wxD2DContext::DrawEllipse(wxDouble x, wxDouble y, wxDouble w, wxDouble h) if (m_composition == wxCOMPOSITION_DEST) return; - wxD2DOffsetHelper helper(this); + wxD2DOffsetHelper helper(this, GetContentScaleFactor()); EnsureInitialized(); AdjustRenderTargetSize(); @@ -4857,6 +4890,7 @@ void wxD2DContext::DrawEllipse(wxDouble x, wxDouble y, wxDouble w, wxDouble h) if (!m_pen.IsNull()) { wxD2DPenData* penData = wxGetD2DPenData(m_pen); + penData->SetWidth(this); penData->Bind(this); GetRenderTarget()->DrawEllipse(ellipse, penData->GetBrush(), penData->GetWidth(), penData->GetStrokeStyle()); } diff --git a/src/osx/carbon/dcclient.cpp b/src/osx/carbon/dcclient.cpp index 519289e3c6..98061950e5 100644 --- a/src/osx/carbon/dcclient.cpp +++ b/src/osx/carbon/dcclient.cpp @@ -76,7 +76,7 @@ wxWindowDCImpl::wxWindowDCImpl( wxDC *owner, wxWindow *window ) CGContextTranslateCTM( cg , -window->MacGetLeftBorderSize() , -window->MacGetTopBorderSize() ); wxGraphicsContext* context = wxGraphicsContext::CreateFromNative( cg ); - context->EnableOffset(m_contentScaleFactor <= 1); + context->SetContentScaleFactor(m_contentScaleFactor); SetGraphicsContext( context ); } DoSetClippingRegion( 0 , 0 , m_width , m_height ) ; diff --git a/src/osx/carbon/graphics.cpp b/src/osx/carbon/graphics.cpp index 80f9431cf2..9bcf06a6b9 100644 --- a/src/osx/carbon/graphics.cpp +++ b/src/osx/carbon/graphics.cpp @@ -743,8 +743,6 @@ wxMacCoreGraphicsPenData::wxMacCoreGraphicsPenData( wxGraphicsRenderer* renderer // TODO: * m_dc->m_scaleX m_width = info.GetWidth(); - if (m_width <= 0.0) - m_width = (CGFloat) 0.1; switch ( info.GetCap() ) { @@ -912,7 +910,15 @@ void wxMacCoreGraphicsPenData::Init() void wxMacCoreGraphicsPenData::Apply( wxGraphicsContext* context ) { CGContextRef cg = (CGContextRef) context->GetNativeContext(); - CGContextSetLineWidth( cg , m_width ); + double width = m_width; + if (width <= 0) + { + const double scaleFactor = context->GetContentScaleFactor(); + CGSize s = { scaleFactor, scaleFactor }; + s = CGContextConvertSizeToUserSpace(cg, s); + width = 1 / wxMin(fabs(s.width), fabs(s.height)); + } + CGContextSetLineWidth( cg, width ); CGContextSetLineJoin( cg , m_join ); CGContextSetLineDash( cg , 0 , m_lengths , m_count ); @@ -1391,12 +1397,6 @@ public: ~wxMacCoreGraphicsContext(); - // Enable offset on non-high DPI displays, i.e. those with scale factor <= 1. - void SetEnableOffsetFromScaleFactor(double factor) - { - m_enableOffset = factor <= 1.0; - } - void Init(); virtual void StartPage( wxDouble width, wxDouble height ) wxOVERRIDE; @@ -1474,17 +1474,24 @@ public: virtual bool ShouldOffset() const wxOVERRIDE { - if ( !m_enableOffset ) + if (!m_enableOffset || m_pen.IsNull()) return false; - int penwidth = 0 ; - if ( !m_pen.IsNull() ) - { - penwidth = (int)((wxMacCoreGraphicsPenData*)m_pen.GetRefData())->GetWidth(); - if ( penwidth == 0 ) - penwidth = 1; - } - return ( penwidth % 2 ) == 1; + double width = static_cast(m_pen.GetRefData())->GetWidth(); + + // always offset for 1-pixel width + if (width <= 0) + return true; + + // no offset if overall scale is not odd integer + const double scaleFactor = GetContentScaleFactor(); + CGSize s = { scaleFactor, scaleFactor }; + s = CGContextConvertSizeToUserSpace(m_cgContext, s); + if (!wxIsSameDouble(fmod(wxMin(fabs(s.width), fabs(s.height)), 2.0), 1.0)) + return false; + + // offset if pen width is odd integer + return wxIsSameDouble(fmod(width, 2.0), 1.0); } // // text @@ -1555,13 +1562,16 @@ private: class wxQuartzOffsetHelper { public : - wxQuartzOffsetHelper( CGContextRef cg , bool offset ) + wxQuartzOffsetHelper( CGContextRef cg, double scaleFactor, bool offset ) { m_cg = cg; m_offset = offset; if ( m_offset ) { - m_userOffset = CGContextConvertSizeToUserSpace( m_cg, CGSizeMake( 0.5 , 0.5 ) ); + const CGSize s = { scaleFactor, scaleFactor }; + m_userOffset = CGContextConvertSizeToUserSpace(m_cg, s); + m_userOffset.width = 0.5 / m_userOffset.width; + m_userOffset.height = 0.5 / m_userOffset.height; CGContextTranslateCTM( m_cg, m_userOffset.width , m_userOffset.height ); } else @@ -1615,7 +1625,7 @@ wxMacCoreGraphicsContext::wxMacCoreGraphicsContext( wxGraphicsRenderer* renderer { Init(); - SetEnableOffsetFromScaleFactor(window->GetContentScaleFactor()); + EnableOffset(); wxSize sz = window->GetSize(); m_width = sz.x; m_height = sz.y; @@ -2136,7 +2146,7 @@ void wxMacCoreGraphicsContext::StrokePath( const wxGraphicsPath &path ) if (m_composition == wxCOMPOSITION_DEST) return; - wxQuartzOffsetHelper helper( m_cgContext , ShouldOffset() ); + wxQuartzOffsetHelper helper( m_cgContext, GetContentScaleFactor(), ShouldOffset() ); wxMacCoreGraphicsPenData* penData = (wxMacCoreGraphicsPenData*)m_pen.GetRefData(); penData->Apply(this); @@ -2216,7 +2226,7 @@ void wxMacCoreGraphicsContext::DrawPath( const wxGraphicsPath &path , wxPolygonF if ( !m_pen.IsNull() ) ((wxMacCoreGraphicsPenData*)m_pen.GetRefData())->Apply(this); - wxQuartzOffsetHelper helper( m_cgContext , ShouldOffset() ); + wxQuartzOffsetHelper helper( m_cgContext, GetContentScaleFactor(), ShouldOffset() ); CGContextAddPath( m_cgContext , (CGPathRef) path.GetNativePath() ); CGContextDrawPath( m_cgContext , mode ); @@ -2624,9 +2634,9 @@ void wxMacCoreGraphicsContext::DrawRectangle( wxDouble x, wxDouble y, wxDouble w CGContextFillRect(m_cgContext, rect); } - wxQuartzOffsetHelper helper( m_cgContext , ShouldOffset() ); if ( !m_pen.IsNull() ) { + wxQuartzOffsetHelper helper( m_cgContext, GetContentScaleFactor(), ShouldOffset() ); ((wxMacCoreGraphicsPenData*)m_pen.GetRefData())->Apply(this); CGContextStrokeRect(m_cgContext, rect); } @@ -2850,7 +2860,7 @@ wxGraphicsContext * wxMacCoreGraphicsRenderer::CreateContext( const wxWindowDC& CGContextRef cgctx = (CGContextRef)(win->MacGetCGContextRef()); wxMacCoreGraphicsContext *context = new wxMacCoreGraphicsContext( this, cgctx, sz.x, sz.y, win ); - context->SetEnableOffsetFromScaleFactor(dc.GetContentScaleFactor()); + context->SetContentScaleFactor(dc.GetContentScaleFactor()); return context; } @@ -2865,7 +2875,7 @@ wxGraphicsContext * wxMacCoreGraphicsRenderer::CreateContext( const wxMemoryDC& mem_impl->GetSize( &w, &h ); wxMacCoreGraphicsContext* context = new wxMacCoreGraphicsContext( this, (CGContextRef)(mem_impl->GetGraphicsContext()->GetNativeContext()), (wxDouble) w, (wxDouble) h ); - context->SetEnableOffsetFromScaleFactor(dc.GetContentScaleFactor()); + context->SetContentScaleFactor(dc.GetContentScaleFactor()); return context; } #endif diff --git a/src/osx/core/dcmemory.cpp b/src/osx/core/dcmemory.cpp index 1f68889bd9..0a78a5cead 100644 --- a/src/osx/core/dcmemory.cpp +++ b/src/osx/core/dcmemory.cpp @@ -89,7 +89,9 @@ void wxMemoryDCImpl::DoSelect( const wxBitmap& bitmap ) CGContextSetStrokeColorSpace( bmCtx, genericColorSpace ); SetGraphicsContext( wxGraphicsContext::CreateFromNative( bmCtx ) ); if (m_graphicContext) - m_graphicContext->EnableOffset(m_contentScaleFactor <= 1); + { + m_graphicContext->SetContentScaleFactor(m_contentScaleFactor); + } } m_ok = (m_graphicContext != NULL) ; }