From ebcdce46d2868eaf310467beafbcd26ebd0a5cc6 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 9 Jul 2011 23:36:19 +0000 Subject: [PATCH] Make coordinates transformations in wxDC really maximally precise. Use the maximal device space extent supported by Win32 GDI and only decrease it if the scale is so small that keeping the device space extent maximal would result in overflowing the int range for the logical space. This makes coordinate translations exact even for huge coordinates, while they could be significantly wrong before due to the integer rounding errors. Closes #13284. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@68206 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- src/msw/dc.cpp | 58 ++++++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/src/msw/dc.cpp b/src/msw/dc.cpp index 962054ae85..4420abf53a 100644 --- a/src/msw/dc.cpp +++ b/src/msw/dc.cpp @@ -83,7 +83,9 @@ IMPLEMENT_ABSTRACT_CLASS(wxMSWDCImpl, wxDCImpl) // constants // --------------------------------------------------------------------------- -static const int VIEWPORT_EXTENT = 1024; +// The device space in Win32 GDI measures 2^27*2^27 , so we use 2^27-1 as the +// maximal possible view port extent. +static const int VIEWPORT_EXTENT = 134217727; // ROPs which don't have standard names (see "Ternary Raster Operations" in the // MSDN docs for how this and other numbers in wxDC::Blit() are obtained) @@ -1960,6 +1962,31 @@ bool wxMSWDCImpl::DoGetPartialTextExtents(const wxString& text, wxArrayInt& widt return true; } +namespace +{ + +void ApplyEffectiveScale(double scale, int sign, int *device, int *logical) +{ + // To reduce rounding errors as much as possible, we try to use the largest + // possible extent (2^27-1) for the device space but we must also avoid + // overflowing the int range i.e. ensure that logical extents are less than + // 2^31 in magnitude. So the minimal scale we can use is 1/16 as for + // anything smaller VIEWPORT_EXTENT/scale would overflow the int range. + static const double MIN_LOGICAL_SCALE = 1./16; + + double physExtent = VIEWPORT_EXTENT; + if ( scale < MIN_LOGICAL_SCALE ) + { + physExtent *= scale/MIN_LOGICAL_SCALE; + scale = MIN_LOGICAL_SCALE; + } + + *device = wxRound(physExtent); + *logical = sign*wxRound(VIEWPORT_EXTENT/scale); +} + +} // anonymous namespace + void wxMSWDCImpl::RealizeScaleAndOrigin() { // although it may seem wasteful to always use MM_ANISOTROPIC here instead @@ -1970,34 +1997,13 @@ void wxMSWDCImpl::RealizeScaleAndOrigin() // wxWidgets API assumes that the coordinate space is "infinite" (i.e. only // limited by 2^32 range of the integer coordinates) but in MSW API we must - // actually specify the extents that we use. So we more or less arbitrarily - // decide to use "base" VIEWPORT_EXTENT and adjust it depending on scale. - // - // To avoid rounding errors we prefer to multiply by the scale if it's > 1 - // and to divide by it if it's < 1. + // actually specify the extents that we use so compute them here. + int devExtX, devExtY, // Viewport, i.e. device space, extents. logExtX, logExtY; // Window, i.e. logical coordinate space, extents. - if ( m_scaleX >= 1 ) - { - devExtX = wxRound(VIEWPORT_EXTENT*m_scaleX); - logExtX = m_signX*VIEWPORT_EXTENT; - } - else - { - devExtX = VIEWPORT_EXTENT; - logExtX = wxRound(m_signX*VIEWPORT_EXTENT/m_scaleX); - } - if ( m_scaleY >= 1 ) - { - devExtY = wxRound(VIEWPORT_EXTENT*m_scaleY); - logExtY = m_signY*VIEWPORT_EXTENT; - } - else - { - devExtY = VIEWPORT_EXTENT; - logExtY = wxRound(m_signY*VIEWPORT_EXTENT/m_scaleY); - } + ApplyEffectiveScale(m_scaleX, m_signX, &devExtX, &logExtX); + ApplyEffectiveScale(m_scaleY, m_signY, &devExtY, &logExtY); ::SetViewportExtEx(GetHdc(), devExtX, devExtY, NULL); ::SetWindowExtEx(GetHdc(), logExtX, logExtY, NULL); -- 2.45.2