X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/a152f137f6c21cd47d74df0e7a98cdb55e4ca178..12bb29f5432174ecbd65549bda832d70d34a98ae:/src/msw/dc.cpp diff --git a/src/msw/dc.cpp b/src/msw/dc.cpp index 17ffaae9bf..b828f025e1 100644 --- a/src/msw/dc.cpp +++ b/src/msw/dc.cpp @@ -34,6 +34,7 @@ #include "wx/bitmap.h" #include "wx/dcmemory.h" #include "wx/log.h" + #include "wx/math.h" #include "wx/icon.h" #include "wx/dcprint.h" #include "wx/module.h" @@ -50,6 +51,7 @@ #include #include "wx/msw/private/dc.h" +#include "wx/private/textmeasure.h" using namespace wxMSWImpl; @@ -82,7 +84,9 @@ IMPLEMENT_ABSTRACT_CLASS(wxMSWDCImpl, wxDCImpl) // constants // --------------------------------------------------------------------------- -static const int VIEWPORT_EXTENT = 1000; +// 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) @@ -145,6 +149,22 @@ wxAlphaBlend(HDC hdcDst, int xDst, int yDst, #endif // wxHAS_RAW_BITMAP +namespace wxMSW +{ + +// Wrappers for the dynamically loaded {Set,Get}Layout() functions. They work +// in exactly the same way as the standard functions and return GDI_ERROR if +// they're not actually available. +DWORD GetLayout(HDC hdc); +DWORD SetLayout(HDC hdc, DWORD dwLayout); + +// Create a compatible HDC and copy the layout of the source DC to it. This is +// necessary in order to draw bitmaps (which are usually blitted from a +// temporary compatible memory DC to the real target DC) using the same layout. +HDC CreateCompatibleDCWithLayout(HDC hdc); + +} // namespace wxMSW + // ---------------------------------------------------------------------------- // private classes // ---------------------------------------------------------------------------- @@ -877,7 +897,7 @@ void wxMSWDCImpl::DoDrawPoint(wxCoord x, wxCoord y) } void wxMSWDCImpl::DoDrawPolygon(int n, - wxPoint points[], + const wxPoint points[], wxCoord xoffset, wxCoord yoffset, wxPolygonFillMode WXUNUSED_IN_WINCE(fillStyle)) @@ -925,8 +945,8 @@ void wxMSWDCImpl::DoDrawPolygon(int n, void wxMSWDCImpl::DoDrawPolyPolygon(int n, - int count[], - wxPoint points[], + const int count[], + const wxPoint points[], wxCoord xoffset, wxCoord yoffset, wxPolygonFillMode fillStyle) @@ -978,7 +998,7 @@ wxMSWDCImpl::DoDrawPolyPolygon(int n, // __WXWINCE__ } -void wxMSWDCImpl::DoDrawLines(int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset) +void wxMSWDCImpl::DoDrawLines(int n, const wxPoint points[], wxCoord xoffset, wxCoord yoffset) { WXMICROWIN_CHECK_HDC @@ -1322,7 +1342,7 @@ void wxMSWDCImpl::DoDrawBitmap( const wxBitmap &bmp, wxCoord x, wxCoord y, bool #endif // wxUSE_SYSTEM_OPTIONS { HDC cdc = GetHdc(); - HDC hdcMem = ::CreateCompatibleDC(GetHdc()); + HDC hdcMem = wxMSW::CreateCompatibleDCWithLayout(cdc); HGDIOBJ hOldBitmap = ::SelectObject(hdcMem, GetHbitmapOf(bmp)); #if wxUSE_PALETTE wxPalette *pal = bmp.GetPalette(); @@ -1354,17 +1374,16 @@ void wxMSWDCImpl::DoDrawBitmap( const wxBitmap &bmp, wxCoord x, wxCoord y, bool // level wxMemoryDC memDC; + memDC.SetLayoutDirection(GetLayoutDirection()); memDC.SelectObjectAsSource(bmp); GetOwner()->Blit(x, y, width, height, &memDC, 0, 0, wxCOPY, useMask); - - memDC.SelectObject(wxNullBitmap); } } else // no mask, just use BitBlt() { HDC cdc = GetHdc(); - HDC memdc = ::CreateCompatibleDC( cdc ); + HDC memdc = wxMSW::CreateCompatibleDCWithLayout( cdc ); HBITMAP hbitmap = (HBITMAP) bmp.GetHBITMAP( ); wxASSERT_MSG( hbitmap, wxT("bitmap is ok but HBITMAP is NULL?") ); @@ -1854,111 +1873,44 @@ void wxMSWDCImpl::DoGetTextExtent(const wxString& string, wxCoord *x, wxCoord *y } #endif // __WXMICROWIN__ - HFONT hfontOld; - if ( font ) - { - wxASSERT_MSG( font->IsOk(), wxT("invalid font in wxMSWDCImpl::GetTextExtent") ); - - hfontOld = (HFONT)::SelectObject(GetHdc(), GetHfontOf(*font)); - } - else // don't change the font - { - hfontOld = 0; - } - - SIZE sizeRect; - const size_t len = string.length(); - if ( !::GetTextExtentPoint32(GetHdc(), string.wx_str(), len, &sizeRect) ) - { - wxLogLastError(wxT("GetTextExtentPoint32()")); - } - -#if !defined(_WIN32_WCE) || (_WIN32_WCE >= 400) - // the result computed by GetTextExtentPoint32() may be too small as it - // accounts for under/overhang of the first/last character while we want - // just the bounding rect for this string so adjust the width as needed - // (using API not available in 2002 SDKs of WinCE) - if ( len > 0 ) - { - ABC width; - const wxChar chFirst = *string.begin(); - if ( ::GetCharABCWidths(GetHdc(), chFirst, chFirst, &width) ) - { - if ( width.abcA < 0 ) - sizeRect.cx -= width.abcA; - - if ( len > 1 ) - { - const wxChar chLast = *string.rbegin(); - ::GetCharABCWidths(GetHdc(), chLast, chLast, &width); - } - //else: we already have the width of the last character - - if ( width.abcC < 0 ) - sizeRect.cx -= width.abcC; - } - //else: GetCharABCWidths() failed, not a TrueType font? - } -#endif // !defined(_WIN32_WCE) || (_WIN32_WCE >= 400) - - if (x) - *x = sizeRect.cx; - if (y) - *y = sizeRect.cy; - - if ( descent || externalLeading ) - { - DoGetFontMetrics(NULL, NULL, descent, NULL, externalLeading, NULL); - } + wxASSERT_MSG( !font || font->IsOk(), wxT("invalid font in wxMSWDCImpl::GetTextExtent") ); - if ( hfontOld ) - { - ::SelectObject(GetHdc(), hfontOld); - } + wxTextMeasure txm(GetOwner(), font); + txm.GetTextExtent(string, x, y, descent, externalLeading); } -// Each element of the array will be the width of the string up to and -// including the coresoponding character in text. - bool wxMSWDCImpl::DoGetPartialTextExtents(const wxString& text, wxArrayInt& widths) const { - static int maxLenText = -1; - static int maxWidth = -1; - int fit = 0; - SIZE sz = {0,0}; - int stlen = text.length(); + wxTextMeasure txm(GetOwner(), NULL); // don't change the font + return txm.GetPartialTextExtents(text, widths, 1.0); +} - if (maxLenText == -1) - { - // Win9x and WinNT+ have different limits - int version = wxGetOsVersion(); - maxLenText = version == wxOS_WINDOWS_NT ? 65535 : 8192; - maxWidth = version == wxOS_WINDOWS_NT ? INT_MAX : 32767; - } +namespace +{ - widths.Empty(); - widths.Add(0, stlen); // fill the array with zeros - if (stlen == 0) - return true; +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; - if (!::GetTextExtentExPoint(GetHdc(), - text.c_str(), // string to check - wxMin(stlen, maxLenText), - maxWidth, - &fit, // [out] count of chars - // that will fit - &widths[0], // array to fill - &sz)) - { - // API failed - wxLogLastError(wxT("GetTextExtentExPoint")); - return false; + double physExtent = VIEWPORT_EXTENT; + if ( scale < MIN_LOGICAL_SCALE ) + { + physExtent *= scale/MIN_LOGICAL_SCALE; + scale = MIN_LOGICAL_SCALE; } - return true; + *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 @@ -1969,34 +1921,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 = VIEWPORT_EXTENT*m_scaleX; - logExtX = m_signX*VIEWPORT_EXTENT; - } - else - { - devExtX = VIEWPORT_EXTENT; - logExtX = m_signX*VIEWPORT_EXTENT/m_scaleX; - } - if ( m_scaleY >= 1 ) - { - devExtY = VIEWPORT_EXTENT*m_scaleY; - logExtY = m_signY*VIEWPORT_EXTENT; - } - else - { - devExtY = VIEWPORT_EXTENT; - logExtY = 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); @@ -2310,7 +2241,8 @@ bool wxMSWDCImpl::DoStretchBlit(wxCoord xdest, wxCoord ydest, // than the wxWidgets fall-back implementation. So we need // to be able to switch this on and off at runtime. #if wxUSE_SYSTEM_OPTIONS - if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0) + static bool s_maskBltAllowed = wxSystemOptions::GetOptionInt("no-maskblt") == 0; + if ( s_maskBltAllowed ) #endif { if ( dstWidth == srcWidth && dstHeight == srcHeight ) @@ -2350,8 +2282,8 @@ bool wxMSWDCImpl::DoStretchBlit(wxCoord xdest, wxCoord ydest, buffer_bmap = (HBITMAP) bitmapCacheEntry->m_bitmap; #else // !wxUSE_DC_CACHEING // create a temp buffer bitmap and DCs to access it and the mask - dc_mask = ::CreateCompatibleDC(hdcSrc); - dc_buffer = ::CreateCompatibleDC(GetHdc()); + dc_mask = wxMSW::CreateCompatibleDCWithLayout(hdcSrc); + dc_buffer = wxMSW::CreateCompatibleDCWithLayout(GetHdc()); buffer_bmap = ::CreateCompatibleBitmap(GetHdc(), dstWidth, dstHeight); #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING HGDIOBJ hOldMaskBitmap = ::SelectObject(dc_mask, (HBITMAP) mask->GetMaskBitmap()); @@ -2434,6 +2366,17 @@ bool wxMSWDCImpl::DoStretchBlit(wxCoord xdest, wxCoord ydest, { SET_STRETCH_BLT_MODE(GetHdc()); + // Unlike all the other functions used here (i.e. AlphaBlt(), + // MaskBlt(), BitBlt() and StretchBlt()), StretchDIBits() does + // not take into account the source DC logical coordinates + // automatically as it doesn't even work with the source HDC. + // So do this manually to ensure that the coordinates are + // interpreted in the same way here as in all the other cases. + xsrc = source->LogicalToDeviceX(xsrc); + ysrc = source->LogicalToDeviceY(ysrc); + srcWidth = source->LogicalToDeviceXRel(srcWidth); + srcHeight = source->LogicalToDeviceYRel(srcHeight); + // Figure out what co-ordinate system we're supposed to specify // ysrc in. const LONG hDIB = ds.dsBmih.biHeight; @@ -2653,7 +2596,7 @@ wxDCCacheEntry* wxMSWDCImpl::FindDCInCache(wxDCCacheEntry* notThis, WXHDC dc) node = node->GetNext(); } - WXHDC hDC = (WXHDC) ::CreateCompatibleDC((HDC) dc); + WXHDC hDC = (WXHDC) wxMSW::CreateCompatibleDCWithLayout((HDC) dc); if ( !hDC) { wxLogLastError(wxT("CreateCompatibleDC")); @@ -2885,20 +2828,47 @@ void wxMSWDCImpl::DoGradientFillLinear (const wxRect& rect, #if wxUSE_DYNLIB_CLASS -static DWORD wxGetDCLayout(HDC hdc) +namespace wxMSW +{ + +DWORD GetLayout(HDC hdc) { typedef DWORD (WINAPI *GetLayout_t)(HDC); static GetLayout_t wxDL_INIT_FUNC(s_pfn, GetLayout, wxDynamicLibrary(wxT("gdi32.dll"))); - return s_pfnGetLayout ? s_pfnGetLayout(hdc) : (DWORD)-1; + return s_pfnGetLayout ? s_pfnGetLayout(hdc) : GDI_ERROR; } +DWORD SetLayout(HDC hdc, DWORD dwLayout) +{ + typedef DWORD (WINAPI *SetLayout_t)(HDC, DWORD); + static SetLayout_t + wxDL_INIT_FUNC(s_pfn, SetLayout, wxDynamicLibrary(wxT("gdi32.dll"))); + + return s_pfnSetLayout ? s_pfnSetLayout(hdc, dwLayout) : GDI_ERROR; +} + +HDC CreateCompatibleDCWithLayout(HDC hdc) +{ + HDC hdcNew = ::CreateCompatibleDC(hdc); + if ( hdcNew ) + { + DWORD dwLayout = wxMSW::GetLayout(hdc); + if ( dwLayout != GDI_ERROR ) + wxMSW::SetLayout(hdcNew, dwLayout); + } + + return hdcNew; +} + +} // namespace wxMSW + wxLayoutDirection wxMSWDCImpl::GetLayoutDirection() const { - DWORD layout = wxGetDCLayout(GetHdc()); + DWORD layout = wxMSW::GetLayout(GetHdc()); - if ( layout == (DWORD)-1 ) + if ( layout == GDI_ERROR ) return wxLayout_Default; return layout & LAYOUT_RTL ? wxLayout_RightToLeft : wxLayout_LeftToRight; @@ -2906,12 +2876,6 @@ wxLayoutDirection wxMSWDCImpl::GetLayoutDirection() const void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection dir) { - typedef DWORD (WINAPI *SetLayout_t)(HDC, DWORD); - static SetLayout_t - wxDL_INIT_FUNC(s_pfn, SetLayout, wxDynamicLibrary(wxT("gdi32.dll"))); - if ( !s_pfnSetLayout ) - return; - if ( dir == wxLayout_Default ) { dir = wxTheApp->GetLayoutDirection(); @@ -2919,17 +2883,41 @@ void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection dir) return; } - DWORD layout = wxGetDCLayout(GetHdc()); + DWORD layout = wxMSW::GetLayout(GetHdc()); + if ( layout == GDI_ERROR ) + return; + if ( dir == wxLayout_RightToLeft ) layout |= LAYOUT_RTL; else layout &= ~LAYOUT_RTL; - s_pfnSetLayout(GetHdc(), layout); + wxMSW::SetLayout(GetHdc(), layout); } #else // !wxUSE_DYNLIB_CLASS +// Provide stubs to avoid ifdefs in the code using these functions. +namespace wxMSW +{ + +DWORD GetLayout(HDC WXUNUSED(hdc)) +{ + return GDI_ERROR; +} + +DWORD SetLayout(HDC WXUNUSED(hdc), DWORD WXUNUSED(dwLayout)) +{ + return GDI_ERROR; +} + +HDC CreateCompatibleDCWithLayout(HDC hdc) +{ + return ::CreateCompatibleDC(hdc); +} + +} // namespace wxMSW + // we can't provide RTL support without dynamic loading, so stub it out wxLayoutDirection wxMSWDCImpl::GetLayoutDirection() const {