X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/c315587c21ed44e54fbb3c1b36f0f70f78314081..51ec29ea5d1d1093bafbae27a8483211a90d9659:/src/msw/graphics.cpp diff --git a/src/msw/graphics.cpp b/src/msw/graphics.cpp index 751be8e5b1..e5161c5d5b 100644 --- a/src/msw/graphics.cpp +++ b/src/msw/graphics.cpp @@ -37,14 +37,24 @@ #include "wx/dcprint.h" #endif +#include "wx/stack.h" + #include "wx/private/graphics.h" #include "wx/msw/wrapgdip.h" #include "wx/msw/dc.h" +#if wxUSE_ENH_METAFILE + #include "wx/msw/enhmeta.h" +#endif #include "wx/dcgraph.h" -#include "wx/stack.h" +#include "wx/msw/private.h" // needs to be before #include -WX_DECLARE_STACK(GraphicsState, GraphicsStates); +#if wxUSE_COMMON_DIALOGS && !defined(__WXMICROWIN__) +#include +#endif + +namespace +{ //----------------------------------------------------------------------------- // constants @@ -56,11 +66,19 @@ const double RAD2DEG = 180.0 / M_PI; // Local functions //----------------------------------------------------------------------------- -static inline double dmin(double a, double b) { return a < b ? a : b; } -static inline double dmax(double a, double b) { return a > b ? a : b; } +inline double dmin(double a, double b) { return a < b ? a : b; } +inline double dmax(double a, double b) { return a > b ? a : b; } -static inline double DegToRad(double deg) { return (deg * M_PI) / 180.0; } -static inline double RadToDeg(double deg) { return (deg * 180.0) / M_PI; } +inline double DegToRad(double deg) { return (deg * M_PI) / 180.0; } +inline double RadToDeg(double deg) { return (deg * 180.0) / M_PI; } + +// translate a wxColour to a Color +inline Color wxColourToColor(const wxColour& col) +{ + return Color(col.Alpha(), col.Red(), col.Green(), col.Blue()); +} + +} // anonymous namespace //----------------------------------------------------------------------------- // device context implementation @@ -78,11 +96,7 @@ static inline double RadToDeg(double deg) { return (deg * 180.0) / M_PI; } // wxGraphicsPath implementation //----------------------------------------------------------------------------- -#include "wx/msw/private.h" // needs to be before #include - -#if wxUSE_COMMON_DIALOGS && !defined(__WXMICROWIN__) -#include -#endif +class wxGDIPlusContext; class wxGDIPlusPathData : public wxGraphicsPathData { @@ -235,16 +249,24 @@ public: wxGDIPlusBrushData( wxGraphicsRenderer* renderer, const wxBrush &brush ); ~wxGDIPlusBrushData (); - void CreateLinearGradientBrush( wxDouble x1, wxDouble y1, wxDouble x2, wxDouble y2, - const wxColour&c1, const wxColour&c2 ); - void CreateRadialGradientBrush( wxDouble xo, wxDouble yo, wxDouble xc, wxDouble yc, wxDouble radius, - const wxColour &oColor, const wxColour &cColor ); + void CreateLinearGradientBrush(wxDouble x1, wxDouble y1, + wxDouble x2, wxDouble y2, + const wxGraphicsGradientStops& stops); + void CreateRadialGradientBrush(wxDouble xo, wxDouble yo, + wxDouble xc, wxDouble yc, + wxDouble radius, + const wxGraphicsGradientStops& stops); + virtual Brush* GetGDIPlusBrush() { return m_brush; } protected: virtual void Init(); -private : +private: + // common part of Create{Linear,Radial}GradientBrush() + template + void SetGradientStops(T *brush, const wxGraphicsGradientStops& stops); + Brush* m_brush; Image* m_brushImage; GraphicsPath* m_brushPath; @@ -259,6 +281,10 @@ public: virtual Bitmap* GetGDIPlusBitmap() { return m_bitmap; } +#if wxUSE_IMAGE + wxImage ConvertToImage() const; +#endif // wxUSE_IMAGE + private : Bitmap* m_bitmap; Bitmap* m_helper; @@ -267,12 +293,28 @@ private : class wxGDIPlusFontData : public wxGraphicsObjectRefData { public: - wxGDIPlusFontData( wxGraphicsRenderer* renderer, const wxFont &font, const wxColour& col ); + wxGDIPlusFontData( wxGraphicsRenderer* renderer, + const wxFont &font, + const wxColour& col ); + wxGDIPlusFontData(wxGraphicsRenderer* renderer, + const wxString& name, + REAL sizeInPixels, + int style, + const wxColour& col); ~wxGDIPlusFontData(); virtual Brush* GetGDIPlusBrush() { return m_textBrush; } virtual Font* GetGDIPlusFont() { return m_font; } + private : + // Common part of all ctors, flags here is a combination of values of + // FontStyle GDI+ enum. + void Init(const wxString& name, + REAL size, + int style, + const wxColour& col, + Unit fontUnit); + Brush* m_textBrush; Font* m_font; }; @@ -280,10 +322,11 @@ private : class wxGDIPlusContext : public wxGraphicsContext { public: - wxGDIPlusContext( wxGraphicsRenderer* renderer, HDC hdc , wxDouble width, wxDouble height ); + wxGDIPlusContext( wxGraphicsRenderer* renderer, const wxDC& dc ); + wxGDIPlusContext( wxGraphicsRenderer* renderer, HDC hdc, wxDouble width, wxDouble height ); wxGDIPlusContext( wxGraphicsRenderer* renderer, HWND hwnd ); wxGDIPlusContext( wxGraphicsRenderer* renderer, Graphics* gr); - wxGDIPlusContext(); + wxGDIPlusContext(wxGraphicsRenderer* renderer); virtual ~wxGDIPlusContext(); @@ -299,6 +342,8 @@ public: virtual void StrokePath( const wxGraphicsPath& p ); virtual void FillPath( const wxGraphicsPath& p , wxPolygonFillMode fillStyle = wxODDEVEN_RULE ); + virtual void DrawRectangle( wxDouble x, wxDouble y, wxDouble w, wxDouble h ); + // stroke lines connecting each of the points virtual void StrokeLines( size_t n, const wxPoint2DDouble *points); @@ -307,6 +352,8 @@ public: virtual bool SetAntialiasMode(wxAntialiasMode antialias); + virtual bool SetInterpolationQuality(wxInterpolationQuality interpolation); + virtual bool SetCompositionMode(wxCompositionMode op); virtual void BeginLayer(wxDouble opacity); @@ -338,35 +385,68 @@ public: virtual bool ShouldOffset() const; virtual void GetSize( wxDouble* width, wxDouble *height ); -private: - void Init(); - void SetDefaults(); + Graphics* GetGraphics() const { return m_context; } + +protected: + + wxDouble m_fontScaleRatio; + // Used from ctors (including those in the derived classes) and takes + // ownership of the graphics pointer that must be non-NULL. + void Init(Graphics* graphics, int width, int height); + +private: virtual void DoDrawText(const wxString& str, wxDouble x, wxDouble y) { DoDrawFilledText(str, x, y, wxNullGraphicsBrush); } virtual void DoDrawFilledText(const wxString& str, wxDouble x, wxDouble y, const wxGraphicsBrush& backgroundBrush); Graphics* m_context; - GraphicsStates m_stateStack; + wxStack m_stateStack; GraphicsState m_state1; GraphicsState m_state2; - wxDouble m_width; - wxDouble m_height; + wxDECLARE_NO_COPY_CLASS(wxGDIPlusContext); +}; + +#if wxUSE_IMAGE + +class wxGDIPlusImageContext : public wxGDIPlusContext +{ +public: + wxGDIPlusImageContext(wxGraphicsRenderer* renderer, wxImage& image) : + wxGDIPlusContext(renderer), + m_image(image), + m_bitmap(renderer, image) + { + Init + ( + new Graphics(m_bitmap.GetGDIPlusBitmap()), + image.GetWidth(), + image.GetHeight() + ); + } - DECLARE_DYNAMIC_CLASS_NO_COPY(wxGDIPlusContext) + virtual ~wxGDIPlusImageContext() + { + m_image = m_bitmap.ConvertToImage(); + } + +private: + wxImage& m_image; + wxGDIPlusBitmapData m_bitmap; + + wxDECLARE_NO_COPY_CLASS(wxGDIPlusImageContext); }; +#endif // wxUSE_IMAGE + class wxGDIPlusMeasuringContext : public wxGDIPlusContext { public: wxGDIPlusMeasuringContext( wxGraphicsRenderer* renderer ) : wxGDIPlusContext( renderer , m_hdc = GetDC(NULL), 1000, 1000 ) { } - wxGDIPlusMeasuringContext() - { - } virtual ~wxGDIPlusMeasuringContext() { @@ -375,7 +455,120 @@ public: private: HDC m_hdc ; - DECLARE_DYNAMIC_CLASS_NO_COPY(wxGDIPlusMeasuringContext) +} ; + +class wxGDIPlusPrintingContext : public wxGDIPlusContext +{ +public: + wxGDIPlusPrintingContext( wxGraphicsRenderer* renderer, const wxDC& dc ); + virtual ~wxGDIPlusPrintingContext() { } +protected: +}; + +//----------------------------------------------------------------------------- +// wxGDIPlusRenderer declaration +//----------------------------------------------------------------------------- + +class wxGDIPlusRenderer : public wxGraphicsRenderer +{ +public : + wxGDIPlusRenderer() + { + m_loaded = -1; + m_gditoken = 0; + } + + virtual ~wxGDIPlusRenderer() + { + if ( m_loaded == 1 ) + { + Unload(); + } + } + + // Context + + virtual wxGraphicsContext * CreateContext( const wxWindowDC& dc); + + virtual wxGraphicsContext * CreateContext( const wxMemoryDC& dc); + +#if wxUSE_PRINTING_ARCHITECTURE + virtual wxGraphicsContext * CreateContext( const wxPrinterDC& dc); +#endif + +#if wxUSE_ENH_METAFILE + virtual wxGraphicsContext * CreateContext( const wxEnhMetaFileDC& dc); +#endif + + virtual wxGraphicsContext * CreateContextFromNativeContext( void * context ); + + virtual wxGraphicsContext * CreateContextFromNativeWindow( void * window ); + + virtual wxGraphicsContext * CreateContext( wxWindow* window ); + +#if wxUSE_IMAGE + virtual wxGraphicsContext * CreateContextFromImage(wxImage& image); +#endif // wxUSE_IMAGE + + virtual wxGraphicsContext * CreateMeasuringContext(); + + // Path + + virtual wxGraphicsPath CreatePath(); + + // Matrix + + virtual wxGraphicsMatrix CreateMatrix( wxDouble a=1.0, wxDouble b=0.0, wxDouble c=0.0, wxDouble d=1.0, + wxDouble tx=0.0, wxDouble ty=0.0); + + + virtual wxGraphicsPen CreatePen(const wxPen& pen) ; + + virtual wxGraphicsBrush CreateBrush(const wxBrush& brush ) ; + + virtual wxGraphicsBrush + CreateLinearGradientBrush(wxDouble x1, wxDouble y1, + wxDouble x2, wxDouble y2, + const wxGraphicsGradientStops& stops); + + virtual wxGraphicsBrush + CreateRadialGradientBrush(wxDouble xo, wxDouble yo, + wxDouble xc, wxDouble yc, + wxDouble radius, + const wxGraphicsGradientStops& stops); + + // create a native bitmap representation + virtual wxGraphicsBitmap CreateBitmap( const wxBitmap &bitmap ); +#if wxUSE_IMAGE + virtual wxGraphicsBitmap CreateBitmapFromImage(const wxImage& image); + virtual wxImage CreateImageFromBitmap(const wxGraphicsBitmap& bmp); +#endif // wxUSE_IMAGE + + virtual wxGraphicsFont CreateFont( const wxFont& font, + const wxColour& col); + + virtual wxGraphicsFont CreateFont(double size, + const wxString& facename, + int flags = wxFONTFLAG_DEFAULT, + const wxColour& col = *wxBLACK); + + // create a graphics bitmap from a native bitmap + virtual wxGraphicsBitmap CreateBitmapFromNativeBitmap( void* bitmap ); + + // create a subimage from a native image representation + virtual wxGraphicsBitmap CreateSubBitmap( const wxGraphicsBitmap &bitmap, wxDouble x, wxDouble y, wxDouble w, wxDouble h ); + +protected : + bool EnsureIsLoaded(); + void Load(); + void Unload(); + friend class wxGDIPlusRendererModule; + +private : + int m_loaded; + ULONG_PTR m_gditoken; + + DECLARE_DYNAMIC_CLASS_NO_COPY(wxGDIPlusRenderer) } ; //----------------------------------------------------------------------------- @@ -404,8 +597,7 @@ wxGDIPlusPenData::wxGDIPlusPenData( wxGraphicsRenderer* renderer, const wxPen &p if (m_width <= 0.0) m_width = 0.1; - m_pen = new Pen(Color( pen.GetColour().Alpha() , pen.GetColour().Red() , - pen.GetColour().Green() , pen.GetColour().Blue() ), m_width ); + m_pen = new Pen(wxColourToColor(pen.GetColour()), m_width ); LineCap cap; switch ( pen.GetCap() ) @@ -455,25 +647,25 @@ wxGDIPlusPenData::wxGDIPlusPenData( wxGraphicsRenderer* renderer, const wxPen &p DashStyle dashStyle = DashStyleSolid; switch ( pen.GetStyle() ) { - case wxSOLID : + case wxPENSTYLE_SOLID : break; - case wxDOT : + case wxPENSTYLE_DOT : dashStyle = DashStyleDot; break; - case wxLONG_DASH : + case wxPENSTYLE_LONG_DASH : dashStyle = DashStyleDash; // TODO verify break; - case wxSHORT_DASH : + case wxPENSTYLE_SHORT_DASH : dashStyle = DashStyleDash; break; - case wxDOT_DASH : + case wxPENSTYLE_DOT_DASH : dashStyle = DashStyleDashDot; break; - case wxUSER_DASH : + case wxPENSTYLE_USER_DASH : { dashStyle = DashStyleCustom; wxDash *dashes; @@ -490,12 +682,18 @@ wxGDIPlusPenData::wxGDIPlusPenData( wxGraphicsRenderer* renderer, const wxPen &p } } break; - case wxSTIPPLE : + case wxPENSTYLE_STIPPLE : { wxBitmap* bmp = pen.GetStipple(); - if ( bmp && bmp->Ok() ) + if ( bmp && bmp->IsOk() ) { - m_penImage = Bitmap::FromHBITMAP((HBITMAP)bmp->GetHBITMAP(),(HPALETTE)bmp->GetPalette()->GetHPALETTE()); + m_penImage = Bitmap::FromHBITMAP((HBITMAP)bmp->GetHBITMAP(), +#if wxUSE_PALETTE + (HPALETTE)bmp->GetPalette()->GetHPALETTE() +#else + NULL +#endif + ); m_penBrush = new TextureBrush(m_penImage); m_pen->SetBrush( m_penBrush ); } @@ -503,33 +701,39 @@ wxGDIPlusPenData::wxGDIPlusPenData( wxGraphicsRenderer* renderer, const wxPen &p } break; default : - if ( pen.GetStyle() >= wxFIRST_HATCH && pen.GetStyle() <= wxLAST_HATCH ) + if ( pen.GetStyle() >= wxPENSTYLE_FIRST_HATCH && + pen.GetStyle() <= wxPENSTYLE_LAST_HATCH ) { - HatchStyle style = HatchStyleHorizontal; + HatchStyle style; switch( pen.GetStyle() ) { - case wxBDIAGONAL_HATCH : + case wxPENSTYLE_BDIAGONAL_HATCH : style = HatchStyleBackwardDiagonal; break ; - case wxCROSSDIAG_HATCH : + case wxPENSTYLE_CROSSDIAG_HATCH : style = HatchStyleDiagonalCross; break ; - case wxFDIAGONAL_HATCH : + case wxPENSTYLE_FDIAGONAL_HATCH : style = HatchStyleForwardDiagonal; break ; - case wxCROSS_HATCH : + case wxPENSTYLE_CROSS_HATCH : style = HatchStyleCross; break ; - case wxHORIZONTAL_HATCH : + case wxPENSTYLE_HORIZONTAL_HATCH : style = HatchStyleHorizontal; break ; - case wxVERTICAL_HATCH : + case wxPENSTYLE_VERTICAL_HATCH : style = HatchStyleVertical; break ; - + default: + style = HatchStyleHorizontal; } - m_penBrush = new HatchBrush(style,Color( pen.GetColour().Alpha() , pen.GetColour().Red() , - pen.GetColour().Green() , pen.GetColour().Blue() ), Color::Transparent ); + m_penBrush = new HatchBrush + ( + style, + wxColourToColor(pen.GetColour()), + Color::Transparent + ); m_pen->SetBrush( m_penBrush ); } break; @@ -554,44 +758,54 @@ wxGDIPlusBrushData::wxGDIPlusBrushData( wxGraphicsRenderer* renderer , const wxB Init(); if ( brush.GetStyle() == wxSOLID) { - m_brush = new SolidBrush( Color( brush.GetColour().Alpha() , brush.GetColour().Red() , - brush.GetColour().Green() , brush.GetColour().Blue() ) ); + m_brush = new SolidBrush(wxColourToColor( brush.GetColour())); } else if ( brush.IsHatch() ) { - HatchStyle style = HatchStyleHorizontal; + HatchStyle style; switch( brush.GetStyle() ) { - case wxBDIAGONAL_HATCH : + case wxBRUSHSTYLE_BDIAGONAL_HATCH : style = HatchStyleBackwardDiagonal; break ; - case wxCROSSDIAG_HATCH : + case wxBRUSHSTYLE_CROSSDIAG_HATCH : style = HatchStyleDiagonalCross; break ; - case wxFDIAGONAL_HATCH : + case wxBRUSHSTYLE_FDIAGONAL_HATCH : style = HatchStyleForwardDiagonal; break ; - case wxCROSS_HATCH : + case wxBRUSHSTYLE_CROSS_HATCH : style = HatchStyleCross; break ; - case wxHORIZONTAL_HATCH : + case wxBRUSHSTYLE_HORIZONTAL_HATCH : style = HatchStyleHorizontal; break ; - case wxVERTICAL_HATCH : + case wxBRUSHSTYLE_VERTICAL_HATCH : style = HatchStyleVertical; break ; - + default: + style = HatchStyleHorizontal; } - m_brush = new HatchBrush(style,Color( brush.GetColour().Alpha() , brush.GetColour().Red() , - brush.GetColour().Green() , brush.GetColour().Blue() ), Color::Transparent ); + m_brush = new HatchBrush + ( + style, + wxColourToColor(brush.GetColour()), + Color::Transparent + ); } else { wxBitmap* bmp = brush.GetStipple(); - if ( bmp && bmp->Ok() ) + if ( bmp && bmp->IsOk() ) { wxDELETE( m_brushImage ); - m_brushImage = Bitmap::FromHBITMAP((HBITMAP)bmp->GetHBITMAP(),(HPALETTE)bmp->GetPalette()->GetHPALETTE()); + m_brushImage = Bitmap::FromHBITMAP((HBITMAP)bmp->GetHBITMAP(), +#if wxUSE_PALETTE + (HPALETTE)bmp->GetPalette()->GetHPALETTE() +#else + NULL +#endif + ); m_brush = new TextureBrush(m_brushImage); } } @@ -611,42 +825,90 @@ void wxGDIPlusBrushData::Init() m_brushPath= NULL; } -void wxGDIPlusBrushData::CreateLinearGradientBrush( wxDouble x1, wxDouble y1, wxDouble x2, wxDouble y2, const wxColour&c1, const wxColour&c2) +template +void +wxGDIPlusBrushData::SetGradientStops(T *brush, + const wxGraphicsGradientStops& stops) { - m_brush = new LinearGradientBrush( PointF( x1,y1) , PointF( x2,y2), - Color( c1.Alpha(), c1.Red(),c1.Green() , c1.Blue() ), - Color( c2.Alpha(), c2.Red(),c2.Green() , c2.Blue() )); + const unsigned numStops = stops.GetCount(); + if ( numStops <= 2 ) + { + // initial and final colours are set during the brush creation, nothing + // more to do + return; + } + + wxVector colors(numStops); + wxVector positions(numStops); + + for ( unsigned i = 0; i < numStops; i++ ) + { + wxGraphicsGradientStop stop = stops.Item(i); + + colors[i] = wxColourToColor(stop.GetColour()); + positions[i] = stop.GetPosition(); + } + + brush->SetInterpolationColors(&colors[0], &positions[0], numStops); +} + +void +wxGDIPlusBrushData::CreateLinearGradientBrush(wxDouble x1, wxDouble y1, + wxDouble x2, wxDouble y2, + const wxGraphicsGradientStops& stops) +{ + LinearGradientBrush * const + brush = new LinearGradientBrush(PointF(x1, y1) , PointF(x2, y2), + wxColourToColor(stops.GetStartColour()), + wxColourToColor(stops.GetEndColour())); + m_brush = brush; + + SetGradientStops(brush, stops); } -void wxGDIPlusBrushData::CreateRadialGradientBrush( wxDouble xo, wxDouble yo, wxDouble xc, wxDouble yc, wxDouble radius, - const wxColour &oColor, const wxColour &cColor) +void +wxGDIPlusBrushData::CreateRadialGradientBrush(wxDouble xo, wxDouble yo, + wxDouble xc, wxDouble yc, + wxDouble radius, + const wxGraphicsGradientStops& stops) { - // Create a path that consists of a single circle. m_brushPath = new GraphicsPath(); - m_brushPath->AddEllipse( (REAL)(xc-radius), (REAL)(yc-radius), (REAL)(2*radius), (REAL)(2*radius)); + m_brushPath->AddEllipse( (REAL)(xc-radius), (REAL)(yc-radius), + (REAL)(2*radius), (REAL)(2*radius)); - PathGradientBrush *b = new PathGradientBrush(m_brushPath); - m_brush = b; - b->SetCenterPoint( PointF(xo,yo)); - b->SetCenterColor(Color( oColor.Alpha(), oColor.Red(),oColor.Green() , oColor.Blue() )); + PathGradientBrush * const brush = new PathGradientBrush(m_brushPath); + m_brush = brush; + brush->SetCenterPoint(PointF(xo, yo)); + brush->SetCenterColor(wxColourToColor(stops.GetStartColour())); - Color colors[] = {Color( cColor.Alpha(), cColor.Red(),cColor.Green() , cColor.Blue() )}; + const Color col(wxColourToColor(stops.GetEndColour())); int count = 1; - b->SetSurroundColors(colors, &count); + brush->SetSurroundColors(&col, &count); + + SetGradientStops(brush, stops); } //----------------------------------------------------------------------------- // wxGDIPlusFont implementation //----------------------------------------------------------------------------- -wxGDIPlusFontData::wxGDIPlusFontData( wxGraphicsRenderer* renderer, const wxFont &font, - const wxColour& col ) : wxGraphicsObjectRefData( renderer ) +void +wxGDIPlusFontData::Init(const wxString& name, + REAL size, + int style, + const wxColour& col, + Unit fontUnit) { - m_textBrush = NULL; - m_font = NULL; + m_font = new Font(name.wc_str(), size, style, fontUnit); + + m_textBrush = new SolidBrush(wxColourToColor(col)); +} - wxWCharBuffer s = font.GetFaceName().wc_str( *wxConvUI ); - int size = font.GetPointSize(); +wxGDIPlusFontData::wxGDIPlusFontData( wxGraphicsRenderer* renderer, + const wxFont &font, + const wxColour& col ) + : wxGraphicsObjectRefData( renderer ) +{ int style = FontStyleRegular; if ( font.GetStyle() == wxFONTSTYLE_ITALIC ) style |= FontStyleItalic; @@ -654,9 +916,18 @@ wxGDIPlusFontData::wxGDIPlusFontData( wxGraphicsRenderer* renderer, const wxFont style |= FontStyleUnderline; if ( font.GetWeight() == wxFONTWEIGHT_BOLD ) style |= FontStyleBold; - m_font = new Font( s , size , style ); - m_textBrush = new SolidBrush( Color( col.Alpha() , col.Red() , - col.Green() , col.Blue() )); + + Init(font.GetFaceName(), font.GetPointSize(), style, col, UnitPoint); +} + +wxGDIPlusFontData::wxGDIPlusFontData(wxGraphicsRenderer* renderer, + const wxString& name, + REAL sizeInPixels, + int style, + const wxColour& col) : + wxGraphicsObjectRefData(renderer) +{ + Init(name, sizeInPixels, style, col, UnitPixel); } wxGDIPlusFontData::~wxGDIPlusFontData() @@ -689,7 +960,13 @@ wxGDIPlusBitmapData::wxGDIPlusBitmapData( wxGraphicsRenderer* renderer, Bitmap* image = NULL; if ( bmp.GetMask() ) { - Bitmap interim((HBITMAP)bmp.GetHBITMAP(),(HPALETTE)bmp.GetPalette()->GetHPALETTE()) ; + Bitmap interim((HBITMAP)bmp.GetHBITMAP(), +#if wxUSE_PALETTE + (HPALETTE)bmp.GetPalette()->GetHPALETTE() +#else + NULL +#endif + ); size_t width = interim.GetWidth(); size_t height = interim.GetHeight(); @@ -745,7 +1022,13 @@ wxGDIPlusBitmapData::wxGDIPlusBitmapData( wxGraphicsRenderer* renderer, } else { - image = Bitmap::FromHBITMAP((HBITMAP)bmp.GetHBITMAP(),(HPALETTE)bmp.GetPalette()->GetHPALETTE()); + image = Bitmap::FromHBITMAP((HBITMAP)bmp.GetHBITMAP(), +#if wxUSE_PALETTE + (HPALETTE)bmp.GetPalette()->GetHPALETTE() +#else + NULL +#endif + ); if ( bmp.HasAlpha() && GetPixelFormatSize(image->GetPixelFormat()) == 32 ) { size_t width = image->GetWidth(); @@ -768,6 +1051,28 @@ wxGDIPlusBitmapData::wxGDIPlusBitmapData( wxGraphicsRenderer* renderer, m_bitmap = image; } +#if wxUSE_IMAGE + +wxImage wxGDIPlusBitmapData::ConvertToImage() const +{ + // We could use Bitmap::LockBits() and convert to wxImage directly but + // passing by wxBitmap is easier. It would be nice to measure performance + // of the two methods but for this the second one would need to be written + // first... + HBITMAP hbmp; + if ( m_bitmap->GetHBITMAP(Color(0xffffffff), &hbmp) != Gdiplus::Ok ) + return wxNullImage; + + wxBitmap bmp; + bmp.SetWidth(m_bitmap->GetWidth()); + bmp.SetHeight(m_bitmap->GetHeight()); + bmp.SetHBITMAP(hbmp); + bmp.SetDepth(IsAlphaPixelFormat(m_bitmap->GetPixelFormat()) ? 32 : 24); + return bmp.ConvertToImage(); +} + +#endif // wxUSE_IMAGE + wxGDIPlusBitmapData::~wxGDIPlusBitmapData() { delete m_bitmap; @@ -980,7 +1285,7 @@ void wxGDIPlusMatrixData::Scale( wxDouble xScale , wxDouble yScale ) // add the rotation to this matrix (radians) void wxGDIPlusMatrixData::Rotate( wxDouble angle ) { - m_matrix->Rotate( angle ); + m_matrix->Rotate( RadToDeg(angle) ); } // @@ -1015,9 +1320,6 @@ void * wxGDIPlusMatrixData::GetNativeMatrix() const // wxGDIPlusContext implementation //----------------------------------------------------------------------------- -IMPLEMENT_DYNAMIC_CLASS(wxGDIPlusContext,wxGraphicsContext) -IMPLEMENT_DYNAMIC_CLASS(wxGDIPlusMeasuringContext,wxGDIPlusContext) - class wxGDIPlusOffsetHelper { public : @@ -1041,48 +1343,50 @@ public : wxGDIPlusContext::wxGDIPlusContext( wxGraphicsRenderer* renderer, HDC hdc, wxDouble width, wxDouble height ) : wxGraphicsContext(renderer) { - Init(); - m_context = new Graphics( hdc); - m_width = width; - m_height = height; - SetDefaults(); + Init(new Graphics(hdc), width, height); +} + +wxGDIPlusContext::wxGDIPlusContext( wxGraphicsRenderer* renderer, const wxDC& dc ) + : wxGraphicsContext(renderer) +{ + wxMSWDCImpl *msw = wxDynamicCast( dc.GetImpl() , wxMSWDCImpl ); + HDC hdc = (HDC) msw->GetHDC(); + wxSize sz = dc.GetSize(); + + Init(new Graphics(hdc), sz.x, sz.y); } wxGDIPlusContext::wxGDIPlusContext( wxGraphicsRenderer* renderer, HWND hwnd ) : wxGraphicsContext(renderer) { - Init(); - m_context = new Graphics( hwnd); RECT rect = wxGetWindowRect(hwnd); - m_width = rect.right - rect.left; - m_height = rect.bottom - rect.top; - SetDefaults(); + Init(new Graphics(hwnd), rect.right - rect.left, rect.bottom - rect.top); + m_enableOffset = true; } wxGDIPlusContext::wxGDIPlusContext( wxGraphicsRenderer* renderer, Graphics* gr ) : wxGraphicsContext(renderer) { - Init(); - m_context = gr; - SetDefaults(); + Init(gr, 0, 0); } -wxGDIPlusContext::wxGDIPlusContext() : wxGraphicsContext(NULL) +wxGDIPlusContext::wxGDIPlusContext(wxGraphicsRenderer* renderer) + : wxGraphicsContext(renderer) { - Init(); + // Derived class must call Init() later but just set m_context to NULL for + // safety to avoid crashing in our dtor if Init() ends up not being called. + m_context = NULL; } -void wxGDIPlusContext::Init() +void wxGDIPlusContext::Init(Graphics* graphics, int width, int height) { - m_context = NULL; + m_context = graphics; m_state1 = 0; - m_state2= 0; - m_height = 0; - m_width = 0; -} + m_state2 = 0; + m_width = width; + m_height = height; + m_fontScaleRatio = 1.0; -void wxGDIPlusContext::SetDefaults() -{ m_context->SetTextRenderingHint(TextRenderingHintSystemDefault); m_context->SetPixelOffsetMode(PixelOffsetModeHalf); m_context->SetSmoothingMode(SmoothingModeHighQuality); @@ -1117,6 +1421,31 @@ void wxGDIPlusContext::ResetClip() m_context->ResetClip(); } +void wxGDIPlusContext::DrawRectangle( wxDouble x, wxDouble y, wxDouble w, wxDouble h ) +{ + if (m_composition == wxCOMPOSITION_DEST) + return; + + wxGDIPlusOffsetHelper helper( m_context , ShouldOffset() ); + Brush *brush = m_brush.IsNull() ? NULL : ((wxGDIPlusBrushData*)m_brush.GetRefData())->GetGDIPlusBrush(); + Pen *pen = m_pen.IsNull() ? NULL : ((wxGDIPlusPenData*)m_pen.GetGraphicsData())->GetGDIPlusPen(); + + if ( brush ) + { + // the offset is used to fill only the inside of the rectangle and not paint underneath + // its border which may influence a transparent Pen + REAL offset = 0; + if ( pen ) + offset = pen->GetWidth(); + m_context->FillRectangle( brush, (REAL)x + offset/2, (REAL)y + offset/2, (REAL)w - offset, (REAL)h - offset); + } + + if ( pen ) + { + m_context->DrawRectangle( pen, (REAL)x, (REAL)y, (REAL)w, (REAL)h ); + } +} + void wxGDIPlusContext::StrokeLines( size_t n, const wxPoint2DDouble *points) { if (m_composition == wxCOMPOSITION_DEST) @@ -1206,6 +1535,12 @@ bool wxGDIPlusContext::SetAntialiasMode(wxAntialiasMode antialias) return true; } +bool wxGDIPlusContext::SetInterpolationQuality(wxInterpolationQuality WXUNUSED(interpolation)) +{ + // placeholder + return false; +} + bool wxGDIPlusContext::SetCompositionMode(wxCompositionMode op) { if ( m_composition == op ) @@ -1266,6 +1601,8 @@ void wxGDIPlusContext::PushState() void wxGDIPlusContext::PopState() { + wxCHECK_RET( !m_stateStack.empty(), wxT("No state to pop") ); + GraphicsState state = m_stateStack.top(); m_stateStack.pop(); m_context->Restore(state); @@ -1283,7 +1620,7 @@ void wxGDIPlusContext::DrawBitmap( const wxGraphicsBitmap &bmp, wxDouble x, wxDo { Rect drawRect((REAL) x, (REAL)y, (REAL)w, (REAL)h); m_context->SetPixelOffsetMode( PixelOffsetModeNone ); - m_context->DrawImage(image, drawRect, 0 , 0 , image->GetWidth()-1, image->GetHeight()-1, UnitPixel ) ; + m_context->DrawImage(image, drawRect, 0 , 0 , image->GetWidth(), image->GetHeight(), UnitPixel ) ; m_context->SetPixelOffsetMode( PixelOffsetModeHalf ); } else @@ -1402,7 +1739,7 @@ void wxGDIPlusContext::GetTextExtent( const wxString &str, wxDouble *width, wxDo f->GetFamily(&ffamily) ; - REAL factorY = m_context->GetDpiY() / 72.0 ; + REAL factorY = m_fontScaleRatio; REAL rDescent = ffamily.GetCellDescent(FontStyleRegular) * f->GetSize() / ffamily.GetEmHeight(FontStyleRegular); @@ -1433,6 +1770,8 @@ void wxGDIPlusContext::GetTextExtent( const wxString &str, wxDouble *width, wxDo m_context->MeasureString((const wchar_t *) s , wcslen(s) , f, layoutRect, &strFormat, &bounds ) ; if ( width ) *width = bounds.Width; + if ( height ) + *height = bounds.Height; } } @@ -1455,14 +1794,14 @@ void wxGDIPlusContext::GetPartialTextExtents(const wxString& text, wxArrayDouble StringFormat strFormat( StringFormat::GenericTypographic() ); size_t startPosition = 0; - size_t reminder = len; + size_t remainder = len; const size_t maxSpan = 32; CharacterRange* ranges = new CharacterRange[maxSpan] ; Region* regions = new Region[maxSpan]; - while( reminder > 0 ) + while( remainder > 0 ) { - size_t span = wxMin( maxSpan, reminder ); + size_t span = wxMin( maxSpan, remainder ); for( size_t i = 0 ; i < span ; ++i) { @@ -1479,7 +1818,7 @@ void wxGDIPlusContext::GetPartialTextExtents(const wxString& text, wxArrayDouble regions[i].GetBounds(&bbox,m_context); widths[startPosition+i] = bbox.Width; } - reminder -= span; + remainder -= span; startPosition += span; } @@ -1489,6 +1828,9 @@ void wxGDIPlusContext::GetPartialTextExtents(const wxString& text, wxArrayDouble bool wxGDIPlusContext::ShouldOffset() const { + if ( !m_enableOffset ) + return false; + int penwidth = 0 ; if ( !m_pen.IsNull() ) { @@ -1529,90 +1871,32 @@ void wxGDIPlusContext::GetSize( wxDouble* width, wxDouble *height ) *width = m_width; *height = m_height; } + //----------------------------------------------------------------------------- -// wxGDIPlusRenderer declaration +// wxGDIPlusPrintingContext implementation //----------------------------------------------------------------------------- -class wxGDIPlusRenderer : public wxGraphicsRenderer +wxGDIPlusPrintingContext::wxGDIPlusPrintingContext( wxGraphicsRenderer* renderer, + const wxDC& dc ) + : wxGDIPlusContext(renderer, dc) { -public : - wxGDIPlusRenderer() - { - m_loaded = -1; - m_gditoken = 0; - } - - virtual ~wxGDIPlusRenderer() - { - if ( m_loaded == 1 ) - { - Unload(); - } - } - - // Context - - virtual wxGraphicsContext * CreateContext( const wxWindowDC& dc); - - virtual wxGraphicsContext * CreateContext( const wxMemoryDC& dc); - - virtual wxGraphicsContext * CreateContext( const wxPrinterDC& dc); - - virtual wxGraphicsContext * CreateContextFromNativeContext( void * context ); - - virtual wxGraphicsContext * CreateContextFromNativeWindow( void * window ); - - virtual wxGraphicsContext * CreateContext( wxWindow* window ); - - virtual wxGraphicsContext * CreateMeasuringContext(); - - // Path - - virtual wxGraphicsPath CreatePath(); - - // Matrix - - virtual wxGraphicsMatrix CreateMatrix( wxDouble a=1.0, wxDouble b=0.0, wxDouble c=0.0, wxDouble d=1.0, - wxDouble tx=0.0, wxDouble ty=0.0); - - - virtual wxGraphicsPen CreatePen(const wxPen& pen) ; - - virtual wxGraphicsBrush CreateBrush(const wxBrush& brush ) ; - - // sets the brush to a linear gradient, starting at (x1,y1) with color c1 to (x2,y2) with color c2 - virtual wxGraphicsBrush CreateLinearGradientBrush( wxDouble x1, wxDouble y1, wxDouble x2, wxDouble y2, - const wxColour&c1, const wxColour&c2) ; - - // sets the brush to a radial gradient originating at (xo,yc) with color oColor and ends on a circle around (xc,yc) - // with radius r and color cColor - virtual wxGraphicsBrush CreateRadialGradientBrush( wxDouble xo, wxDouble yo, wxDouble xc, wxDouble yc, wxDouble radius, - const wxColour &oColor, const wxColour &cColor) ; - - // sets the font - virtual wxGraphicsFont CreateFont( const wxFont &font , const wxColour &col = *wxBLACK ) ; - - // create a native bitmap representation - virtual wxGraphicsBitmap CreateBitmap( const wxBitmap &bitmap ); - - // create a graphics bitmap from a native bitmap - virtual wxGraphicsBitmap CreateBitmapFromNativeBitmap( void* bitmap ); - - // create a subimage from a native image representation - virtual wxGraphicsBitmap CreateSubBitmap( const wxGraphicsBitmap &bitmap, wxDouble x, wxDouble y, wxDouble w, wxDouble h ); + Graphics* context = GetGraphics(); -protected : - bool EnsureIsLoaded(); - void Load(); - void Unload(); - friend class wxGDIPlusRendererModule; + //m_context->SetPageUnit(UnitDocument); -private : - int m_loaded; - ULONG_PTR m_gditoken; + // Setup page scale, based on DPI ratio. + // Antecedent should be 100dpi when the default page unit + // (UnitDisplay) is used. Page unit UnitDocument would require 300dpi + // instead. Note that calling SetPageScale() does not have effect on + // non-printing DCs (that is, any other than wxPrinterDC or + // wxEnhMetaFileDC). + REAL dpiRatio = 100.0 / context->GetDpiY(); + context->SetPageScale(dpiRatio); - DECLARE_DYNAMIC_CLASS_NO_COPY(wxGDIPlusRenderer) -} ; + // We use this modifier when measuring fonts. It is needed because the + // page scale is modified above. + m_fontScaleRatio = context->GetDpiY() / 72.0; +} //----------------------------------------------------------------------------- // wxGDIPlusRenderer implementation @@ -1668,7 +1952,7 @@ void wxGDIPlusRenderer::Unload() if ( m_gditoken ) { GdiplusShutdown(m_gditoken); - m_gditoken = NULL; + m_gditoken = 0; } m_loaded = -1; // next Load() will try again } @@ -1676,27 +1960,48 @@ void wxGDIPlusRenderer::Unload() wxGraphicsContext * wxGDIPlusRenderer::CreateContext( const wxWindowDC& dc) { ENSURE_LOADED_OR_RETURN(NULL); - wxMSWDCImpl *msw = wxDynamicCast( dc.GetImpl() , wxMSWDCImpl ); - wxSize sz = dc.GetSize(); - return new wxGDIPlusContext(this,(HDC) msw->GetHDC(), sz.x, sz.y); + wxGDIPlusContext* context = new wxGDIPlusContext(this, dc); + context->EnableOffset(true); + return context; } +#if wxUSE_PRINTING_ARCHITECTURE wxGraphicsContext * wxGDIPlusRenderer::CreateContext( const wxPrinterDC& dc) { ENSURE_LOADED_OR_RETURN(NULL); - wxMSWDCImpl *msw = wxDynamicCast( dc.GetImpl() , wxMSWDCImpl ); - wxSize sz = dc.GetSize(); - return new wxGDIPlusContext(this,(HDC) msw->GetHDC(), sz.x, sz.y); + wxGDIPlusContext* context = new wxGDIPlusPrintingContext(this, dc); + return context; } +#endif + +#if wxUSE_ENH_METAFILE +wxGraphicsContext * wxGDIPlusRenderer::CreateContext( const wxEnhMetaFileDC& dc) +{ + ENSURE_LOADED_OR_RETURN(NULL); + wxGDIPlusContext* context = new wxGDIPlusPrintingContext(this, dc); + return context; +} +#endif wxGraphicsContext * wxGDIPlusRenderer::CreateContext( const wxMemoryDC& dc) { ENSURE_LOADED_OR_RETURN(NULL); - wxMSWDCImpl *msw = wxDynamicCast( dc.GetImpl() , wxMSWDCImpl ); - wxSize sz = dc.GetSize(); - return new wxGDIPlusContext(this,(HDC) msw->GetHDC(), sz.x, sz.y); + wxGDIPlusContext* context = new wxGDIPlusContext(this, dc); + context->EnableOffset(true); + return context; } +#if wxUSE_IMAGE +wxGraphicsContext * wxGDIPlusRenderer::CreateContextFromImage(wxImage& image) +{ + ENSURE_LOADED_OR_RETURN(NULL); + wxGDIPlusContext* context = new wxGDIPlusImageContext(this, image); + context->EnableOffset(true); + return context; +} + +#endif // wxUSE_IMAGE + wxGraphicsContext * wxGDIPlusRenderer::CreateMeasuringContext() { ENSURE_LOADED_OR_RETURN(NULL); @@ -1750,7 +2055,7 @@ wxGraphicsMatrix wxGDIPlusRenderer::CreateMatrix( wxDouble a, wxDouble b, wxDoub wxGraphicsPen wxGDIPlusRenderer::CreatePen(const wxPen& pen) { ENSURE_LOADED_OR_RETURN(wxNullGraphicsPen); - if ( !pen.Ok() || pen.GetStyle() == wxTRANSPARENT ) + if ( !pen.IsOk() || pen.GetStyle() == wxTRANSPARENT ) return wxNullGraphicsPen; else { @@ -1763,7 +2068,7 @@ wxGraphicsPen wxGDIPlusRenderer::CreatePen(const wxPen& pen) wxGraphicsBrush wxGDIPlusRenderer::CreateBrush(const wxBrush& brush ) { ENSURE_LOADED_OR_RETURN(wxNullGraphicsBrush); - if ( !brush.Ok() || brush.GetStyle() == wxTRANSPARENT ) + if ( !brush.IsOk() || brush.GetStyle() == wxTRANSPARENT ) return wxNullGraphicsBrush; else { @@ -1773,49 +2078,77 @@ wxGraphicsBrush wxGDIPlusRenderer::CreateBrush(const wxBrush& brush ) } } -// sets the brush to a linear gradient, starting at (x1,y1) with color c1 to (x2,y2) with color c2 -wxGraphicsBrush wxGDIPlusRenderer::CreateLinearGradientBrush( wxDouble x1, wxDouble y1, wxDouble x2, wxDouble y2, - const wxColour&c1, const wxColour&c2) +wxGraphicsBrush +wxGDIPlusRenderer::CreateLinearGradientBrush(wxDouble x1, wxDouble y1, + wxDouble x2, wxDouble y2, + const wxGraphicsGradientStops& stops) { ENSURE_LOADED_OR_RETURN(wxNullGraphicsBrush); wxGraphicsBrush p; wxGDIPlusBrushData* d = new wxGDIPlusBrushData( this ); - d->CreateLinearGradientBrush(x1, y1, x2, y2, c1, c2); + d->CreateLinearGradientBrush(x1, y1, x2, y2, stops); p.SetRefData(d); return p; } -// sets the brush to a radial gradient originating at (xo,yc) with color oColor and ends on a circle around (xc,yc) -// with radius r and color cColor -wxGraphicsBrush wxGDIPlusRenderer::CreateRadialGradientBrush( wxDouble xo, wxDouble yo, wxDouble xc, wxDouble yc, wxDouble radius, - const wxColour &oColor, const wxColour &cColor) +wxGraphicsBrush +wxGDIPlusRenderer::CreateRadialGradientBrush(wxDouble xo, wxDouble yo, + wxDouble xc, wxDouble yc, + wxDouble radius, + const wxGraphicsGradientStops& stops) { ENSURE_LOADED_OR_RETURN(wxNullGraphicsBrush); wxGraphicsBrush p; wxGDIPlusBrushData* d = new wxGDIPlusBrushData( this ); - d->CreateRadialGradientBrush(xo,yo,xc,yc,radius,oColor,cColor); + d->CreateRadialGradientBrush(xo,yo,xc,yc,radius,stops); p.SetRefData(d); return p; } -// sets the font -wxGraphicsFont wxGDIPlusRenderer::CreateFont( const wxFont &font , const wxColour &col ) +wxGraphicsFont +wxGDIPlusRenderer::CreateFont( const wxFont &font, + const wxColour &col ) { ENSURE_LOADED_OR_RETURN(wxNullGraphicsFont); - if ( font.Ok() ) + if ( font.IsOk() ) { wxGraphicsFont p; - p.SetRefData(new wxGDIPlusFontData( this , font, col )); + p.SetRefData(new wxGDIPlusFontData( this, font, col )); return p; } else return wxNullGraphicsFont; } +wxGraphicsFont +wxGDIPlusRenderer::CreateFont(double size, + const wxString& facename, + int flags, + const wxColour& col) +{ + ENSURE_LOADED_OR_RETURN(wxNullGraphicsFont); + + // Convert wxFont flags to GDI+ style: + int style = FontStyleRegular; + if ( flags & wxFONTFLAG_ITALIC ) + style |= FontStyleItalic; + if ( flags & wxFONTFLAG_UNDERLINED ) + style |= FontStyleUnderline; + if ( flags & wxFONTFLAG_BOLD ) + style |= FontStyleBold; + if ( flags & wxFONTFLAG_STRIKETHROUGH ) + style |= FontStyleStrikeout; + + + wxGraphicsFont f; + f.SetRefData(new wxGDIPlusFontData(this, facename, size, style, col)); + return f; +} + wxGraphicsBitmap wxGDIPlusRenderer::CreateBitmap( const wxBitmap &bitmap ) { ENSURE_LOADED_OR_RETURN(wxNullGraphicsBitmap); - if ( bitmap.Ok() ) + if ( bitmap.IsOk() ) { wxGraphicsBitmap p; p.SetRefData(new wxGDIPlusBitmapData( this , bitmap )); @@ -1825,6 +2158,40 @@ wxGraphicsBitmap wxGDIPlusRenderer::CreateBitmap( const wxBitmap &bitmap ) return wxNullGraphicsBitmap; } +#if wxUSE_IMAGE + +wxGraphicsBitmap wxGDIPlusRenderer::CreateBitmapFromImage(const wxImage& image) +{ + ENSURE_LOADED_OR_RETURN(wxNullGraphicsBitmap); + if ( image.IsOk() ) + { + // Notice that we rely on conversion from wxImage to wxBitmap here but + // we could probably do it more efficiently by converting from wxImage + // to GDI+ Bitmap directly, i.e. copying wxImage pixels to the buffer + // returned by Bitmap::LockBits(). However this would require writing + // code specific for this task while like this we can reuse existing + // code (see also wxGDIPlusBitmapData::ConvertToImage()). + wxGraphicsBitmap gb; + gb.SetRefData(new wxGDIPlusBitmapData(this, image)); + return gb; + } + else + return wxNullGraphicsBitmap; +} + + +wxImage wxGDIPlusRenderer::CreateImageFromBitmap(const wxGraphicsBitmap& bmp) +{ + ENSURE_LOADED_OR_RETURN(wxNullImage); + const wxGDIPlusBitmapData* const + data = static_cast(bmp.GetGraphicsData()); + + return data ? data->ConvertToImage() : wxNullImage; +} + +#endif // wxUSE_IMAGE + + wxGraphicsBitmap wxGDIPlusRenderer::CreateBitmapFromNativeBitmap( void *bitmap ) { ENSURE_LOADED_OR_RETURN(wxNullGraphicsBitmap); @@ -1875,6 +2242,14 @@ WXHDC wxGCDC::AcquireHDC() if ( !gc ) return NULL; +#if wxUSE_CAIRO + // we can't get the HDC if it is not a GDI+ context + wxGraphicsRenderer* r1 = gc->GetRenderer(); + wxGraphicsRenderer* r2 = wxGraphicsRenderer::GetCairoRenderer(); + if (r1 == r2) + return NULL; +#endif + Graphics * const g = static_cast(gc->GetNativeContext()); return g ? g->GetHDC() : NULL; } @@ -1887,6 +2262,14 @@ void wxGCDC::ReleaseHDC(WXHDC hdc) wxGraphicsContext * const gc = GetGraphicsContext(); wxCHECK_RET( gc, "can't release HDC because there is no wxGraphicsContext" ); +#if wxUSE_CAIRO + // we can't get the HDC if it is not a GDI+ context + wxGraphicsRenderer* r1 = gc->GetRenderer(); + wxGraphicsRenderer* r2 = wxGraphicsRenderer::GetCairoRenderer(); + if (r1 == r2) + return; +#endif + Graphics * const g = static_cast(gc->GetNativeContext()); wxCHECK_RET( g, "can't release HDC because there is no Graphics" );