From d4edc41f6e9f93e1bca0a3302b99844e3f9cf412 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 7 Aug 2014 21:07:18 +0000 Subject: [PATCH] Fix drawing on wxDC when using right-to-left layout in wxMSW. Avoid integer overflow when setting wxDC scale. This affected (i.e. broke) drawing in RTL but probably not only that. Closes #16254. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/branches/WX_3_0_BRANCH@77022 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- docs/changes.txt | 1 + src/msw/dc.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/docs/changes.txt b/docs/changes.txt index d1723932f8..1a7c665b57 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -602,6 +602,7 @@ wxMSW: - Fix parallel build of MSVC 11/12 solutions (Artur Wieczorek). - Fix background of wxRadioBox buttons and wxSlider (Artur Wieczorek). - Fix appearance of wxToggleButtons with non default colours (Artur Wieczorek). +- Fix drawing on wxDC when using right-to-left layout (Artur Wieczorek). 3.0.1: (released 2014-06-15) diff --git a/src/msw/dc.cpp b/src/msw/dc.cpp index 2910a5a07c..64f081e548 100644 --- a/src/msw/dc.cpp +++ b/src/msw/dc.cpp @@ -1932,6 +1932,50 @@ void ApplyEffectiveScale(double scale, int sign, int *device, int *logical) *logical = sign*wxRound(VIEWPORT_EXTENT/scale); } +// Binary GCD algorithm +// See: http://en.wikipedia.org/wiki/Binary_GCD_algorithm#Iterative_version_in_C +unsigned int CalcGCD(unsigned int u, unsigned int v) +{ + // GCD(0,v) == v; GCD(u,0) == u, GCD(0,0) == 0 + if (u == 0) + return v; + if (v == 0) + return u; + + int shift; + + // Let shift := lg K, where K is the greatest power of 2 + // dividing both u and v. + for (shift = 0; ((u | v) & 1) == 0; ++shift) + { + u >>= 1; + v >>= 1; + } + + while ((u & 1) == 0) + u >>= 1; + + // From here on, u is always odd. + do + { + // remove all factors of 2 in v -- they are not common + // note: v is not zero, so while will terminate + while ((v & 1) == 0) + v >>= 1; + + // Now u and v are both odd. Swap if necessary so u <= v, + // then set v = v - u (which is even) + if (u > v) + { + wxSwap(u, v); + } + v -= u; // Here v >= u + } while (v != 0); + + // restore common factors of 2 + return u << shift; +} + } // anonymous namespace void wxMSWDCImpl::RealizeScaleAndOrigin() @@ -1952,6 +1996,16 @@ void wxMSWDCImpl::RealizeScaleAndOrigin() ApplyEffectiveScale(m_scaleX, m_signX, &devExtX, &logExtX); ApplyEffectiveScale(m_scaleY, m_signY, &devExtY, &logExtY); + // Becaue only devExtX/logExtX ratio and devExtY/logExtY ratio are counted + // we can reduce the fractions to avoid large absolute numbers + // and possible arithmetic overflows. + unsigned int gcd = CalcGCD(abs(devExtX), abs(logExtX)); + devExtX /= gcd; + logExtX /= gcd; + gcd = CalcGCD(abs(devExtY), abs(logExtY)); + devExtY /= gcd; + logExtY /= gcd; + ::SetViewportExtEx(GetHdc(), devExtX, devExtY, NULL); ::SetWindowExtEx(GetHdc(), logExtX, logExtY, NULL);