Unfortunately Direct2D-based wxGraphicsRenderer is not ready for use yet as it doesn't draw lines with width 0, which are commonly used. The code using it in GetDefaultRenderer() will need to be restored when this bug is fixed. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@77694 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2400 lines
69 KiB
C++
2400 lines
69 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/msw/graphics.cpp
|
|
// Purpose: wxGCDC class
|
|
// Author: Stefan Csomor
|
|
// Modified by:
|
|
// Created: 2006-09-30
|
|
// Copyright: (c) 2006 Stefan Csomor
|
|
// Licence: wxWindows licence
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#include "wx/dc.h"
|
|
|
|
#if wxUSE_GRAPHICS_GDIPLUS
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/msw/wrapcdlg.h"
|
|
#include "wx/image.h"
|
|
#include "wx/window.h"
|
|
#include "wx/utils.h"
|
|
#include "wx/dialog.h"
|
|
#include "wx/app.h"
|
|
#include "wx/bitmap.h"
|
|
#include "wx/log.h"
|
|
#include "wx/icon.h"
|
|
#include "wx/math.h"
|
|
#include "wx/module.h"
|
|
// include all dc types that are used as a param
|
|
#include "wx/dc.h"
|
|
#include "wx/dcclient.h"
|
|
#include "wx/dcmemory.h"
|
|
#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/msw/private.h" // needs to be before #include <commdlg.h>
|
|
|
|
#if wxUSE_COMMON_DIALOGS && !defined(__WXMICROWIN__)
|
|
#include <commdlg.h>
|
|
#endif
|
|
|
|
namespace
|
|
{
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Local functions
|
|
//-----------------------------------------------------------------------------
|
|
|
|
inline double dmin(double a, double b) { return a < b ? a : b; }
|
|
inline double dmax(double a, double b) { return a > b ? a : b; }
|
|
|
|
// translate a wxColour to a Color
|
|
inline Color wxColourToColor(const wxColour& col)
|
|
{
|
|
return Color(col.Alpha(), col.Red(), col.Green(), col.Blue());
|
|
}
|
|
|
|
// Do not use this pointer directly, it's only used by
|
|
// GetDrawTextStringFormat() and the cleanup code in wxGDIPlusRendererModule.
|
|
StringFormat* gs_drawTextStringFormat = NULL;
|
|
|
|
// Get the string format used for the text drawing and measuring functions:
|
|
// notice that it must be the same one for all of them, otherwise the drawn
|
|
// text might be of different size than what measuring it returned.
|
|
inline StringFormat* GetDrawTextStringFormat()
|
|
{
|
|
if ( !gs_drawTextStringFormat )
|
|
{
|
|
gs_drawTextStringFormat = new StringFormat(StringFormat::GenericTypographic());
|
|
|
|
// This doesn't make any difference for DrawText() actually but we want
|
|
// this behaviour when measuring text.
|
|
gs_drawTextStringFormat->SetFormatFlags
|
|
(
|
|
gs_drawTextStringFormat->GetFormatFlags()
|
|
| StringFormatFlagsMeasureTrailingSpaces
|
|
);
|
|
}
|
|
|
|
return gs_drawTextStringFormat;
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// device context implementation
|
|
//
|
|
// more and more of the dc functionality should be implemented by calling
|
|
// the appropricate wxGDIPlusContext, but we will have to do that step by step
|
|
// also coordinate conversions should be moved to native matrix ops
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// we always stock two context states, one at entry, to be able to preserve the
|
|
// state we were called with, the other one after changing to HI Graphics orientation
|
|
// (this one is used for getting back clippings etc)
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// wxGraphicsPath implementation
|
|
//-----------------------------------------------------------------------------
|
|
|
|
class wxGDIPlusContext;
|
|
|
|
class wxGDIPlusPathData : public wxGraphicsPathData
|
|
{
|
|
public :
|
|
wxGDIPlusPathData(wxGraphicsRenderer* renderer, GraphicsPath* path = NULL);
|
|
~wxGDIPlusPathData();
|
|
|
|
virtual wxGraphicsObjectRefData *Clone() const;
|
|
|
|
//
|
|
// These are the path primitives from which everything else can be constructed
|
|
//
|
|
|
|
// begins a new subpath at (x,y)
|
|
virtual void MoveToPoint( wxDouble x, wxDouble y );
|
|
|
|
// adds a straight line from the current point to (x,y)
|
|
virtual void AddLineToPoint( wxDouble x, wxDouble y );
|
|
|
|
// adds a cubic Bezier curve from the current point, using two control points and an end point
|
|
virtual void AddCurveToPoint( wxDouble cx1, wxDouble cy1, wxDouble cx2, wxDouble cy2, wxDouble x, wxDouble y );
|
|
|
|
|
|
// adds an arc of a circle centering at (x,y) with radius (r) from startAngle to endAngle
|
|
virtual void AddArc( wxDouble x, wxDouble y, wxDouble r, wxDouble startAngle, wxDouble endAngle, bool clockwise ) ;
|
|
|
|
// gets the last point of the current path, (0,0) if not yet set
|
|
virtual void GetCurrentPoint( wxDouble* x, wxDouble* y) const;
|
|
|
|
// adds another path
|
|
virtual void AddPath( const wxGraphicsPathData* path );
|
|
|
|
// closes the current sub-path
|
|
virtual void CloseSubpath();
|
|
|
|
//
|
|
// These are convenience functions which - if not available natively will be assembled
|
|
// using the primitives from above
|
|
//
|
|
|
|
// appends a rectangle as a new closed subpath
|
|
virtual void AddRectangle( wxDouble x, wxDouble y, wxDouble w, wxDouble h ) ;
|
|
/*
|
|
|
|
// appends an ellipsis as a new closed subpath fitting the passed rectangle
|
|
virtual void AddEllipsis( wxDouble x, wxDouble y, wxDouble w , wxDouble h ) ;
|
|
|
|
// draws a an arc to two tangents connecting (current) to (x1,y1) and (x1,y1) to (x2,y2), also a straight line from (current) to (x1,y1)
|
|
virtual void AddArcToPoint( wxDouble x1, wxDouble y1 , wxDouble x2, wxDouble y2, wxDouble r ) ;
|
|
*/
|
|
|
|
// returns the native path
|
|
virtual void * GetNativePath() const { return m_path; }
|
|
|
|
// give the native path returned by GetNativePath() back (there might be some deallocations necessary)
|
|
virtual void UnGetNativePath(void * WXUNUSED(path)) const {}
|
|
|
|
// transforms each point of this path by the matrix
|
|
virtual void Transform( const wxGraphicsMatrixData* matrix ) ;
|
|
|
|
// gets the bounding box enclosing all points (possibly including control points)
|
|
virtual void GetBox(wxDouble *x, wxDouble *y, wxDouble *w, wxDouble *h) const;
|
|
|
|
virtual bool Contains( wxDouble x, wxDouble y, wxPolygonFillMode fillStyle = wxODDEVEN_RULE) const;
|
|
|
|
private :
|
|
GraphicsPath* m_path;
|
|
};
|
|
|
|
class wxGDIPlusMatrixData : public wxGraphicsMatrixData
|
|
{
|
|
public :
|
|
wxGDIPlusMatrixData(wxGraphicsRenderer* renderer, Matrix* matrix = NULL) ;
|
|
virtual ~wxGDIPlusMatrixData() ;
|
|
|
|
virtual wxGraphicsObjectRefData* Clone() const ;
|
|
|
|
// concatenates the matrix
|
|
virtual void Concat( const wxGraphicsMatrixData *t );
|
|
|
|
// sets the matrix to the respective values
|
|
virtual void Set(wxDouble a=1.0, wxDouble b=0.0, wxDouble c=0.0, wxDouble d=1.0,
|
|
wxDouble tx=0.0, wxDouble ty=0.0);
|
|
|
|
// gets the component valuess of the matrix
|
|
virtual void Get(wxDouble* a=NULL, wxDouble* b=NULL, wxDouble* c=NULL,
|
|
wxDouble* d=NULL, wxDouble* tx=NULL, wxDouble* ty=NULL) const;
|
|
|
|
// makes this the inverse matrix
|
|
virtual void Invert();
|
|
|
|
// returns true if the elements of the transformation matrix are equal ?
|
|
virtual bool IsEqual( const wxGraphicsMatrixData* t) const ;
|
|
|
|
// return true if this is the identity matrix
|
|
virtual bool IsIdentity() const;
|
|
|
|
//
|
|
// transformation
|
|
//
|
|
|
|
// add the translation to this matrix
|
|
virtual void Translate( wxDouble dx , wxDouble dy );
|
|
|
|
// add the scale to this matrix
|
|
virtual void Scale( wxDouble xScale , wxDouble yScale );
|
|
|
|
// add the rotation to this matrix (radians)
|
|
virtual void Rotate( wxDouble angle );
|
|
|
|
//
|
|
// apply the transforms
|
|
//
|
|
|
|
// applies that matrix to the point
|
|
virtual void TransformPoint( wxDouble *x, wxDouble *y ) const;
|
|
|
|
// applies the matrix except for translations
|
|
virtual void TransformDistance( wxDouble *dx, wxDouble *dy ) const;
|
|
|
|
// returns the native representation
|
|
virtual void * GetNativeMatrix() const;
|
|
private:
|
|
Matrix* m_matrix ;
|
|
} ;
|
|
|
|
class wxGDIPlusPenData : public wxGraphicsObjectRefData
|
|
{
|
|
public:
|
|
wxGDIPlusPenData( wxGraphicsRenderer* renderer, const wxPen &pen );
|
|
~wxGDIPlusPenData();
|
|
|
|
void Init();
|
|
|
|
virtual wxDouble GetWidth() { return m_width; }
|
|
virtual Pen* GetGDIPlusPen() { return m_pen; }
|
|
|
|
protected :
|
|
Pen* m_pen;
|
|
Image* m_penImage;
|
|
Brush* m_penBrush;
|
|
|
|
wxDouble m_width;
|
|
};
|
|
|
|
class wxGDIPlusBrushData : public wxGraphicsObjectRefData
|
|
{
|
|
public:
|
|
wxGDIPlusBrushData( wxGraphicsRenderer* renderer );
|
|
wxGDIPlusBrushData( wxGraphicsRenderer* renderer, const wxBrush &brush );
|
|
~wxGDIPlusBrushData ();
|
|
|
|
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:
|
|
// common part of Create{Linear,Radial}GradientBrush()
|
|
template <typename T>
|
|
void SetGradientStops(T *brush,
|
|
const wxGraphicsGradientStops& stops,
|
|
bool reversed = false);
|
|
|
|
Brush* m_brush;
|
|
Image* m_brushImage;
|
|
GraphicsPath* m_brushPath;
|
|
};
|
|
|
|
class WXDLLIMPEXP_CORE wxGDIPlusBitmapData : public wxGraphicsBitmapData
|
|
{
|
|
public:
|
|
wxGDIPlusBitmapData( wxGraphicsRenderer* renderer, Bitmap* bitmap );
|
|
wxGDIPlusBitmapData( wxGraphicsRenderer* renderer, const wxBitmap &bmp );
|
|
~wxGDIPlusBitmapData ();
|
|
|
|
virtual Bitmap* GetGDIPlusBitmap() { return m_bitmap; }
|
|
virtual void* GetNativeBitmap() const { return m_bitmap; }
|
|
|
|
#if wxUSE_IMAGE
|
|
wxImage ConvertToImage() const;
|
|
#endif // wxUSE_IMAGE
|
|
|
|
private :
|
|
Bitmap* m_bitmap;
|
|
Bitmap* m_helper;
|
|
};
|
|
|
|
class wxGDIPlusFontData : public wxGraphicsObjectRefData
|
|
{
|
|
public:
|
|
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;
|
|
};
|
|
|
|
class wxGDIPlusContext : public wxGraphicsContext
|
|
{
|
|
public:
|
|
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(wxGraphicsRenderer* renderer);
|
|
|
|
virtual ~wxGDIPlusContext();
|
|
|
|
virtual void Clip( const wxRegion ®ion );
|
|
// clips drawings to the rect
|
|
virtual void Clip( wxDouble x, wxDouble y, wxDouble w, wxDouble h );
|
|
|
|
// resets the clipping to original extent
|
|
virtual void ResetClip();
|
|
|
|
virtual void * GetNativeContext();
|
|
|
|
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);
|
|
|
|
// We don't have any specific implementation for this one in wxMSW but
|
|
// override it just to avoid warnings about hiding the base class virtual.
|
|
virtual void StrokeLines( size_t n, const wxPoint2DDouble *beginPoints, const wxPoint2DDouble *endPoints)
|
|
{
|
|
wxGraphicsContext::StrokeLines(n, beginPoints, endPoints);
|
|
}
|
|
|
|
// draws a polygon
|
|
virtual void DrawLines( size_t n, const wxPoint2DDouble *points, wxPolygonFillMode fillStyle = wxODDEVEN_RULE );
|
|
|
|
virtual bool SetAntialiasMode(wxAntialiasMode antialias);
|
|
|
|
virtual bool SetInterpolationQuality(wxInterpolationQuality interpolation);
|
|
|
|
virtual bool SetCompositionMode(wxCompositionMode op);
|
|
|
|
virtual void BeginLayer(wxDouble opacity);
|
|
|
|
virtual void EndLayer();
|
|
|
|
virtual void Translate( wxDouble dx , wxDouble dy );
|
|
virtual void Scale( wxDouble xScale , wxDouble yScale );
|
|
virtual void Rotate( wxDouble angle );
|
|
|
|
// concatenates this transform with the current transform of this context
|
|
virtual void ConcatTransform( const wxGraphicsMatrix& matrix );
|
|
|
|
// sets the transform of this context
|
|
virtual void SetTransform( const wxGraphicsMatrix& matrix );
|
|
|
|
// gets the matrix of this context
|
|
virtual wxGraphicsMatrix GetTransform() const;
|
|
|
|
virtual void DrawBitmap( const wxGraphicsBitmap &bmp, wxDouble x, wxDouble y, wxDouble w, wxDouble h );
|
|
virtual void DrawBitmap( const wxBitmap &bmp, wxDouble x, wxDouble y, wxDouble w, wxDouble h );
|
|
virtual void DrawIcon( const wxIcon &icon, wxDouble x, wxDouble y, wxDouble w, wxDouble h );
|
|
virtual void PushState();
|
|
virtual void PopState();
|
|
|
|
virtual void GetTextExtent( const wxString &str, wxDouble *width, wxDouble *height,
|
|
wxDouble *descent, wxDouble *externalLeading ) const;
|
|
virtual void GetPartialTextExtents(const wxString& text, wxArrayDouble& widths) const;
|
|
virtual bool ShouldOffset() const;
|
|
virtual void GetSize( wxDouble* width, wxDouble *height );
|
|
|
|
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);
|
|
|
|
Graphics* m_context;
|
|
wxStack<GraphicsState> m_stateStack;
|
|
GraphicsState m_state1;
|
|
GraphicsState m_state2;
|
|
|
|
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()
|
|
);
|
|
}
|
|
|
|
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 )
|
|
{
|
|
}
|
|
|
|
virtual ~wxGDIPlusMeasuringContext()
|
|
{
|
|
ReleaseDC( NULL, m_hdc );
|
|
}
|
|
|
|
private:
|
|
HDC m_hdc ;
|
|
} ;
|
|
|
|
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 );
|
|
|
|
virtual wxString GetName() const wxOVERRIDE;
|
|
virtual void GetVersion(int *major, int *minor, int *micro) const wxOVERRIDE;
|
|
|
|
protected :
|
|
bool EnsureIsLoaded();
|
|
void Load();
|
|
void Unload();
|
|
friend class wxGDIPlusRendererModule;
|
|
|
|
private :
|
|
int m_loaded;
|
|
ULONG_PTR m_gditoken;
|
|
|
|
DECLARE_DYNAMIC_CLASS_NO_COPY(wxGDIPlusRenderer)
|
|
} ;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// wxGDIPlusPen implementation
|
|
//-----------------------------------------------------------------------------
|
|
|
|
wxGDIPlusPenData::~wxGDIPlusPenData()
|
|
{
|
|
delete m_pen;
|
|
delete m_penImage;
|
|
delete m_penBrush;
|
|
}
|
|
|
|
void wxGDIPlusPenData::Init()
|
|
{
|
|
m_pen = NULL ;
|
|
m_penImage = NULL;
|
|
m_penBrush = NULL;
|
|
}
|
|
|
|
wxGDIPlusPenData::wxGDIPlusPenData( wxGraphicsRenderer* renderer, const wxPen &pen )
|
|
: wxGraphicsObjectRefData(renderer)
|
|
{
|
|
Init();
|
|
m_width = pen.GetWidth();
|
|
if (m_width <= 0.0)
|
|
m_width = 0.1;
|
|
|
|
m_pen = new Pen(wxColourToColor(pen.GetColour()), m_width );
|
|
|
|
LineCap cap;
|
|
switch ( pen.GetCap() )
|
|
{
|
|
case wxCAP_ROUND :
|
|
cap = LineCapRound;
|
|
break;
|
|
|
|
case wxCAP_PROJECTING :
|
|
cap = LineCapSquare;
|
|
break;
|
|
|
|
case wxCAP_BUTT :
|
|
cap = LineCapFlat; // TODO verify
|
|
break;
|
|
|
|
default :
|
|
cap = LineCapFlat;
|
|
break;
|
|
}
|
|
m_pen->SetLineCap(cap,cap, DashCapFlat);
|
|
|
|
LineJoin join;
|
|
switch ( pen.GetJoin() )
|
|
{
|
|
case wxJOIN_BEVEL :
|
|
join = LineJoinBevel;
|
|
break;
|
|
|
|
case wxJOIN_MITER :
|
|
join = LineJoinMiter;
|
|
break;
|
|
|
|
case wxJOIN_ROUND :
|
|
join = LineJoinRound;
|
|
break;
|
|
|
|
default :
|
|
join = LineJoinMiter;
|
|
break;
|
|
}
|
|
|
|
m_pen->SetLineJoin(join);
|
|
|
|
m_pen->SetDashStyle(DashStyleSolid);
|
|
|
|
DashStyle dashStyle = DashStyleSolid;
|
|
switch ( pen.GetStyle() )
|
|
{
|
|
case wxPENSTYLE_SOLID :
|
|
break;
|
|
|
|
case wxPENSTYLE_DOT :
|
|
dashStyle = DashStyleDot;
|
|
break;
|
|
|
|
case wxPENSTYLE_LONG_DASH :
|
|
dashStyle = DashStyleDash; // TODO verify
|
|
break;
|
|
|
|
case wxPENSTYLE_SHORT_DASH :
|
|
dashStyle = DashStyleDash;
|
|
break;
|
|
|
|
case wxPENSTYLE_DOT_DASH :
|
|
dashStyle = DashStyleDashDot;
|
|
break;
|
|
case wxPENSTYLE_USER_DASH :
|
|
{
|
|
dashStyle = DashStyleCustom;
|
|
wxDash *dashes;
|
|
int count = pen.GetDashes( &dashes );
|
|
if ((dashes != NULL) && (count > 0))
|
|
{
|
|
REAL *userLengths = new REAL[count];
|
|
for ( int i = 0; i < count; ++i )
|
|
{
|
|
userLengths[i] = dashes[i];
|
|
}
|
|
m_pen->SetDashPattern( userLengths, count);
|
|
delete[] userLengths;
|
|
}
|
|
}
|
|
break;
|
|
case wxPENSTYLE_STIPPLE :
|
|
{
|
|
wxBitmap* bmp = pen.GetStipple();
|
|
if ( bmp && bmp->IsOk() )
|
|
{
|
|
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 );
|
|
}
|
|
|
|
}
|
|
break;
|
|
default :
|
|
if ( pen.GetStyle() >= wxPENSTYLE_FIRST_HATCH &&
|
|
pen.GetStyle() <= wxPENSTYLE_LAST_HATCH )
|
|
{
|
|
HatchStyle style;
|
|
switch( pen.GetStyle() )
|
|
{
|
|
case wxPENSTYLE_BDIAGONAL_HATCH :
|
|
style = HatchStyleBackwardDiagonal;
|
|
break ;
|
|
case wxPENSTYLE_CROSSDIAG_HATCH :
|
|
style = HatchStyleDiagonalCross;
|
|
break ;
|
|
case wxPENSTYLE_FDIAGONAL_HATCH :
|
|
style = HatchStyleForwardDiagonal;
|
|
break ;
|
|
case wxPENSTYLE_CROSS_HATCH :
|
|
style = HatchStyleCross;
|
|
break ;
|
|
case wxPENSTYLE_HORIZONTAL_HATCH :
|
|
style = HatchStyleHorizontal;
|
|
break ;
|
|
case wxPENSTYLE_VERTICAL_HATCH :
|
|
style = HatchStyleVertical;
|
|
break ;
|
|
default:
|
|
style = HatchStyleHorizontal;
|
|
}
|
|
m_penBrush = new HatchBrush
|
|
(
|
|
style,
|
|
wxColourToColor(pen.GetColour()),
|
|
Color::Transparent
|
|
);
|
|
m_pen->SetBrush( m_penBrush );
|
|
}
|
|
break;
|
|
}
|
|
if ( dashStyle != DashStyleSolid )
|
|
m_pen->SetDashStyle(dashStyle);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// wxGDIPlusBrush implementation
|
|
//-----------------------------------------------------------------------------
|
|
|
|
wxGDIPlusBrushData::wxGDIPlusBrushData( wxGraphicsRenderer* renderer )
|
|
: wxGraphicsObjectRefData(renderer)
|
|
{
|
|
Init();
|
|
}
|
|
|
|
wxGDIPlusBrushData::wxGDIPlusBrushData( wxGraphicsRenderer* renderer , const wxBrush &brush )
|
|
: wxGraphicsObjectRefData(renderer)
|
|
{
|
|
Init();
|
|
if ( brush.GetStyle() == wxBRUSHSTYLE_SOLID)
|
|
{
|
|
m_brush = new SolidBrush(wxColourToColor( brush.GetColour()));
|
|
}
|
|
else if ( brush.IsHatch() )
|
|
{
|
|
HatchStyle style;
|
|
switch( brush.GetStyle() )
|
|
{
|
|
case wxBRUSHSTYLE_BDIAGONAL_HATCH :
|
|
style = HatchStyleBackwardDiagonal;
|
|
break ;
|
|
case wxBRUSHSTYLE_CROSSDIAG_HATCH :
|
|
style = HatchStyleDiagonalCross;
|
|
break ;
|
|
case wxBRUSHSTYLE_FDIAGONAL_HATCH :
|
|
style = HatchStyleForwardDiagonal;
|
|
break ;
|
|
case wxBRUSHSTYLE_CROSS_HATCH :
|
|
style = HatchStyleCross;
|
|
break ;
|
|
case wxBRUSHSTYLE_HORIZONTAL_HATCH :
|
|
style = HatchStyleHorizontal;
|
|
break ;
|
|
case wxBRUSHSTYLE_VERTICAL_HATCH :
|
|
style = HatchStyleVertical;
|
|
break ;
|
|
default:
|
|
style = HatchStyleHorizontal;
|
|
}
|
|
m_brush = new HatchBrush
|
|
(
|
|
style,
|
|
wxColourToColor(brush.GetColour()),
|
|
Color::Transparent
|
|
);
|
|
}
|
|
else
|
|
{
|
|
wxBitmap* bmp = brush.GetStipple();
|
|
if ( bmp && bmp->IsOk() )
|
|
{
|
|
wxDELETE( m_brushImage );
|
|
m_brushImage = Bitmap::FromHBITMAP((HBITMAP)bmp->GetHBITMAP(),
|
|
#if wxUSE_PALETTE
|
|
(HPALETTE)bmp->GetPalette()->GetHPALETTE()
|
|
#else
|
|
NULL
|
|
#endif
|
|
);
|
|
m_brush = new TextureBrush(m_brushImage);
|
|
}
|
|
}
|
|
}
|
|
|
|
wxGDIPlusBrushData::~wxGDIPlusBrushData()
|
|
{
|
|
delete m_brush;
|
|
delete m_brushImage;
|
|
delete m_brushPath;
|
|
};
|
|
|
|
void wxGDIPlusBrushData::Init()
|
|
{
|
|
m_brush = NULL;
|
|
m_brushImage= NULL;
|
|
m_brushPath= NULL;
|
|
}
|
|
|
|
template <typename T>
|
|
void
|
|
wxGDIPlusBrushData::SetGradientStops(T *brush,
|
|
const wxGraphicsGradientStops& stops,
|
|
bool reversed)
|
|
{
|
|
const unsigned numStops = stops.GetCount();
|
|
if ( numStops <= 2 )
|
|
{
|
|
// initial and final colours are set during the brush creation, nothing
|
|
// more to do
|
|
return;
|
|
}
|
|
|
|
wxVector<Color> colors(numStops);
|
|
wxVector<REAL> positions(numStops);
|
|
|
|
if ( reversed )
|
|
{
|
|
for ( unsigned i = 0; i < numStops; i++ )
|
|
{
|
|
wxGraphicsGradientStop stop = stops.Item(numStops - i - 1);
|
|
|
|
colors[i] = wxColourToColor(stop.GetColour());
|
|
positions[i] = 1.0 - stop.GetPosition();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
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 wxGraphicsGradientStops& stops)
|
|
{
|
|
m_brushPath = new GraphicsPath();
|
|
m_brushPath->AddEllipse( (REAL)(xc-radius), (REAL)(yc-radius),
|
|
(REAL)(2*radius), (REAL)(2*radius));
|
|
|
|
PathGradientBrush * const brush = new PathGradientBrush(m_brushPath);
|
|
m_brush = brush;
|
|
brush->SetCenterPoint(PointF(xo, yo));
|
|
brush->SetCenterColor(wxColourToColor(stops.GetStartColour()));
|
|
|
|
const Color col(wxColourToColor(stops.GetEndColour()));
|
|
int count = 1;
|
|
brush->SetSurroundColors(&col, &count);
|
|
|
|
// Because the GDI+ API draws radial gradients from outside towards the
|
|
// center we have to reverse the order of the gradient stops.
|
|
SetGradientStops(brush, stops, true);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// wxGDIPlusFont implementation
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void
|
|
wxGDIPlusFontData::Init(const wxString& name,
|
|
REAL size,
|
|
int style,
|
|
const wxColour& col,
|
|
Unit fontUnit)
|
|
{
|
|
m_font = new Font(name.wc_str(), size, style, fontUnit);
|
|
|
|
m_textBrush = new SolidBrush(wxColourToColor(col));
|
|
}
|
|
|
|
wxGDIPlusFontData::wxGDIPlusFontData( wxGraphicsRenderer* renderer,
|
|
const wxFont &font,
|
|
const wxColour& col )
|
|
: wxGraphicsObjectRefData( renderer )
|
|
{
|
|
int style = FontStyleRegular;
|
|
if ( font.GetStyle() == wxFONTSTYLE_ITALIC )
|
|
style |= FontStyleItalic;
|
|
if ( font.GetUnderlined() )
|
|
style |= FontStyleUnderline;
|
|
if ( font.GetWeight() == wxFONTWEIGHT_BOLD )
|
|
style |= FontStyleBold;
|
|
|
|
// Create font which size is measured in logical units
|
|
// and let the system rescale it according to the target resolution.
|
|
Init(font.GetFaceName(), font.GetPixelSize().GetHeight(), style, col, UnitPixel);
|
|
}
|
|
|
|
wxGDIPlusFontData::wxGDIPlusFontData(wxGraphicsRenderer* renderer,
|
|
const wxString& name,
|
|
REAL sizeInPixels,
|
|
int style,
|
|
const wxColour& col) :
|
|
wxGraphicsObjectRefData(renderer)
|
|
{
|
|
Init(name, sizeInPixels, style, col, UnitPixel);
|
|
}
|
|
|
|
wxGDIPlusFontData::~wxGDIPlusFontData()
|
|
{
|
|
delete m_textBrush;
|
|
delete m_font;
|
|
}
|
|
|
|
// the built-in conversions functions create non-premultiplied bitmaps, while GDIPlus needs them in the
|
|
// premultiplied format, therefore in the failing cases we create a new bitmap using the non-premultiplied
|
|
// bytes as parameter, since there is no real copying of the data going in, only references are stored
|
|
// m_helper has to be kept alive as well
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// wxGDIPlusBitmapData implementation
|
|
//-----------------------------------------------------------------------------
|
|
|
|
wxGDIPlusBitmapData::wxGDIPlusBitmapData( wxGraphicsRenderer* renderer, Bitmap* bitmap ) :
|
|
wxGraphicsBitmapData( renderer ), m_bitmap( bitmap )
|
|
{
|
|
m_helper = NULL;
|
|
}
|
|
|
|
wxGDIPlusBitmapData::wxGDIPlusBitmapData( wxGraphicsRenderer* renderer,
|
|
const wxBitmap &bmp) : wxGraphicsBitmapData( renderer )
|
|
{
|
|
m_bitmap = NULL;
|
|
m_helper = NULL;
|
|
|
|
Bitmap* image = NULL;
|
|
if ( bmp.GetMask() )
|
|
{
|
|
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();
|
|
Rect bounds(0,0,width,height);
|
|
|
|
image = new Bitmap(width,height,PixelFormat32bppPARGB) ;
|
|
|
|
Bitmap interimMask((HBITMAP)bmp.GetMask()->GetMaskBitmap(),NULL);
|
|
wxASSERT(interimMask.GetPixelFormat() == PixelFormat1bppIndexed);
|
|
|
|
BitmapData dataMask ;
|
|
interimMask.LockBits(&bounds,ImageLockModeRead,
|
|
interimMask.GetPixelFormat(),&dataMask);
|
|
|
|
|
|
BitmapData imageData ;
|
|
image->LockBits(&bounds,ImageLockModeWrite, PixelFormat32bppPARGB, &imageData);
|
|
|
|
BYTE maskPattern = 0 ;
|
|
BYTE maskByte = 0;
|
|
size_t maskIndex ;
|
|
|
|
for ( size_t y = 0 ; y < height ; ++y)
|
|
{
|
|
maskIndex = 0 ;
|
|
for( size_t x = 0 ; x < width; ++x)
|
|
{
|
|
if ( x % 8 == 0)
|
|
{
|
|
maskPattern = 0x80;
|
|
maskByte = *((BYTE*)dataMask.Scan0 + dataMask.Stride*y + maskIndex);
|
|
maskIndex++;
|
|
}
|
|
else
|
|
maskPattern = maskPattern >> 1;
|
|
|
|
ARGB *dest = (ARGB*)((BYTE*)imageData.Scan0 + imageData.Stride*y + x*4);
|
|
if ( (maskByte & maskPattern) == 0 )
|
|
*dest = 0x00000000;
|
|
else
|
|
{
|
|
Color c ;
|
|
interim.GetPixel(x,y,&c) ;
|
|
*dest = (c.GetValue() | Color::AlphaMask);
|
|
}
|
|
}
|
|
}
|
|
|
|
image->UnlockBits(&imageData);
|
|
|
|
interimMask.UnlockBits(&dataMask);
|
|
interim.UnlockBits(&dataMask);
|
|
}
|
|
else
|
|
{
|
|
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();
|
|
size_t height = image->GetHeight();
|
|
Rect bounds(0,0,width,height);
|
|
static BitmapData data ;
|
|
|
|
m_helper = image ;
|
|
image = NULL ;
|
|
m_helper->LockBits(&bounds, ImageLockModeRead,
|
|
m_helper->GetPixelFormat(),&data);
|
|
|
|
image = new Bitmap(data.Width, data.Height, data.Stride,
|
|
PixelFormat32bppPARGB , (BYTE*) data.Scan0);
|
|
|
|
m_helper->UnlockBits(&data);
|
|
}
|
|
}
|
|
if ( image )
|
|
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;
|
|
delete m_helper;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// wxGDIPlusPath implementation
|
|
//-----------------------------------------------------------------------------
|
|
|
|
wxGDIPlusPathData::wxGDIPlusPathData(wxGraphicsRenderer* renderer, GraphicsPath* path ) : wxGraphicsPathData(renderer)
|
|
{
|
|
if ( path )
|
|
m_path = path;
|
|
else
|
|
m_path = new GraphicsPath();
|
|
}
|
|
|
|
wxGDIPlusPathData::~wxGDIPlusPathData()
|
|
{
|
|
delete m_path;
|
|
}
|
|
|
|
wxGraphicsObjectRefData* wxGDIPlusPathData::Clone() const
|
|
{
|
|
return new wxGDIPlusPathData( GetRenderer() , m_path->Clone());
|
|
}
|
|
|
|
//
|
|
// The Primitives
|
|
//
|
|
|
|
void wxGDIPlusPathData::MoveToPoint( wxDouble x , wxDouble y )
|
|
{
|
|
m_path->StartFigure();
|
|
m_path->AddLine((REAL) x,(REAL) y,(REAL) x,(REAL) y);
|
|
}
|
|
|
|
void wxGDIPlusPathData::AddLineToPoint( wxDouble x , wxDouble y )
|
|
{
|
|
m_path->AddLine((REAL) x,(REAL) y,(REAL) x,(REAL) y);
|
|
}
|
|
|
|
void wxGDIPlusPathData::CloseSubpath()
|
|
{
|
|
m_path->CloseFigure();
|
|
}
|
|
|
|
void wxGDIPlusPathData::AddCurveToPoint( wxDouble cx1, wxDouble cy1, wxDouble cx2, wxDouble cy2, wxDouble x, wxDouble y )
|
|
{
|
|
PointF c1(cx1,cy1);
|
|
PointF c2(cx2,cy2);
|
|
PointF end(x,y);
|
|
PointF start;
|
|
m_path->GetLastPoint(&start);
|
|
m_path->AddBezier(start,c1,c2,end);
|
|
}
|
|
|
|
// gets the last point of the current path, (0,0) if not yet set
|
|
void wxGDIPlusPathData::GetCurrentPoint( wxDouble* x, wxDouble* y) const
|
|
{
|
|
PointF start;
|
|
m_path->GetLastPoint(&start);
|
|
*x = start.X ;
|
|
*y = start.Y ;
|
|
}
|
|
|
|
void wxGDIPlusPathData::AddArc( wxDouble x, wxDouble y, wxDouble r, double startAngle, double endAngle, bool clockwise )
|
|
{
|
|
double sweepAngle = endAngle - startAngle ;
|
|
if( fabs(sweepAngle) >= 2*M_PI)
|
|
{
|
|
sweepAngle = 2 * M_PI;
|
|
}
|
|
else
|
|
{
|
|
if ( clockwise )
|
|
{
|
|
if( sweepAngle < 0 )
|
|
sweepAngle += 2 * M_PI;
|
|
}
|
|
else
|
|
{
|
|
if( sweepAngle > 0 )
|
|
sweepAngle -= 2 * M_PI;
|
|
|
|
}
|
|
}
|
|
m_path->AddArc((REAL) (x-r),(REAL) (y-r),(REAL) (2*r),(REAL) (2*r),wxRadToDeg(startAngle),wxRadToDeg(sweepAngle));
|
|
}
|
|
|
|
void wxGDIPlusPathData::AddRectangle( wxDouble x, wxDouble y, wxDouble w, wxDouble h )
|
|
{
|
|
m_path->AddRectangle(RectF(x,y,w,h));
|
|
}
|
|
|
|
void wxGDIPlusPathData::AddPath( const wxGraphicsPathData* path )
|
|
{
|
|
m_path->AddPath( (GraphicsPath*) path->GetNativePath(), FALSE);
|
|
}
|
|
|
|
|
|
// transforms each point of this path by the matrix
|
|
void wxGDIPlusPathData::Transform( const wxGraphicsMatrixData* matrix )
|
|
{
|
|
m_path->Transform( (Matrix*) matrix->GetNativeMatrix() );
|
|
}
|
|
|
|
// gets the bounding box enclosing all points (possibly including control points)
|
|
void wxGDIPlusPathData::GetBox(wxDouble *x, wxDouble *y, wxDouble *w, wxDouble *h) const
|
|
{
|
|
RectF bounds;
|
|
m_path->GetBounds( &bounds, NULL, NULL) ;
|
|
*x = bounds.X;
|
|
*y = bounds.Y;
|
|
*w = bounds.Width;
|
|
*h = bounds.Height;
|
|
}
|
|
|
|
bool wxGDIPlusPathData::Contains( wxDouble x, wxDouble y, wxPolygonFillMode fillStyle ) const
|
|
{
|
|
m_path->SetFillMode( fillStyle == wxODDEVEN_RULE ? FillModeAlternate : FillModeWinding);
|
|
return m_path->IsVisible( (FLOAT) x,(FLOAT) y) == TRUE ;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// wxGDIPlusMatrixData implementation
|
|
//-----------------------------------------------------------------------------
|
|
|
|
wxGDIPlusMatrixData::wxGDIPlusMatrixData(wxGraphicsRenderer* renderer, Matrix* matrix )
|
|
: wxGraphicsMatrixData(renderer)
|
|
{
|
|
if ( matrix )
|
|
m_matrix = matrix ;
|
|
else
|
|
m_matrix = new Matrix();
|
|
}
|
|
|
|
wxGDIPlusMatrixData::~wxGDIPlusMatrixData()
|
|
{
|
|
delete m_matrix;
|
|
}
|
|
|
|
wxGraphicsObjectRefData *wxGDIPlusMatrixData::Clone() const
|
|
{
|
|
return new wxGDIPlusMatrixData( GetRenderer(), m_matrix->Clone());
|
|
}
|
|
|
|
// concatenates the matrix
|
|
void wxGDIPlusMatrixData::Concat( const wxGraphicsMatrixData *t )
|
|
{
|
|
m_matrix->Multiply( (Matrix*) t->GetNativeMatrix());
|
|
}
|
|
|
|
// sets the matrix to the respective values
|
|
void wxGDIPlusMatrixData::Set(wxDouble a, wxDouble b, wxDouble c, wxDouble d,
|
|
wxDouble tx, wxDouble ty)
|
|
{
|
|
m_matrix->SetElements(a,b,c,d,tx,ty);
|
|
}
|
|
|
|
// gets the component valuess of the matrix
|
|
void wxGDIPlusMatrixData::Get(wxDouble* a, wxDouble* b, wxDouble* c,
|
|
wxDouble* d, wxDouble* tx, wxDouble* ty) const
|
|
{
|
|
REAL elements[6];
|
|
m_matrix->GetElements(elements);
|
|
if (a) *a = elements[0];
|
|
if (b) *b = elements[1];
|
|
if (c) *c = elements[2];
|
|
if (d) *d = elements[3];
|
|
if (tx) *tx= elements[4];
|
|
if (ty) *ty= elements[5];
|
|
}
|
|
|
|
// makes this the inverse matrix
|
|
void wxGDIPlusMatrixData::Invert()
|
|
{
|
|
m_matrix->Invert();
|
|
}
|
|
|
|
// returns true if the elements of the transformation matrix are equal ?
|
|
bool wxGDIPlusMatrixData::IsEqual( const wxGraphicsMatrixData* t) const
|
|
{
|
|
return m_matrix->Equals((Matrix*) t->GetNativeMatrix())== TRUE ;
|
|
}
|
|
|
|
// return true if this is the identity matrix
|
|
bool wxGDIPlusMatrixData::IsIdentity() const
|
|
{
|
|
return m_matrix->IsIdentity() == TRUE ;
|
|
}
|
|
|
|
//
|
|
// transformation
|
|
//
|
|
|
|
// add the translation to this matrix
|
|
void wxGDIPlusMatrixData::Translate( wxDouble dx , wxDouble dy )
|
|
{
|
|
m_matrix->Translate(dx,dy);
|
|
}
|
|
|
|
// add the scale to this matrix
|
|
void wxGDIPlusMatrixData::Scale( wxDouble xScale , wxDouble yScale )
|
|
{
|
|
m_matrix->Scale(xScale,yScale);
|
|
}
|
|
|
|
// add the rotation to this matrix (radians)
|
|
void wxGDIPlusMatrixData::Rotate( wxDouble angle )
|
|
{
|
|
m_matrix->Rotate( wxRadToDeg(angle) );
|
|
}
|
|
|
|
//
|
|
// apply the transforms
|
|
//
|
|
|
|
// applies that matrix to the point
|
|
void wxGDIPlusMatrixData::TransformPoint( wxDouble *x, wxDouble *y ) const
|
|
{
|
|
PointF pt(*x,*y);
|
|
m_matrix->TransformPoints(&pt);
|
|
*x = pt.X;
|
|
*y = pt.Y;
|
|
}
|
|
|
|
// applies the matrix except for translations
|
|
void wxGDIPlusMatrixData::TransformDistance( wxDouble *dx, wxDouble *dy ) const
|
|
{
|
|
PointF pt(*dx,*dy);
|
|
m_matrix->TransformVectors(&pt);
|
|
*dx = pt.X;
|
|
*dy = pt.Y;
|
|
}
|
|
|
|
// returns the native representation
|
|
void * wxGDIPlusMatrixData::GetNativeMatrix() const
|
|
{
|
|
return m_matrix;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// wxGDIPlusContext implementation
|
|
//-----------------------------------------------------------------------------
|
|
|
|
class wxGDIPlusOffsetHelper
|
|
{
|
|
public :
|
|
wxGDIPlusOffsetHelper( Graphics* gr , bool offset )
|
|
{
|
|
m_gr = gr;
|
|
m_offset = offset;
|
|
if ( m_offset )
|
|
m_gr->TranslateTransform( 0.5, 0.5 );
|
|
}
|
|
~wxGDIPlusOffsetHelper( )
|
|
{
|
|
if ( m_offset )
|
|
m_gr->TranslateTransform( -0.5, -0.5 );
|
|
}
|
|
public :
|
|
Graphics* m_gr;
|
|
bool m_offset;
|
|
} ;
|
|
|
|
wxGDIPlusContext::wxGDIPlusContext( wxGraphicsRenderer* renderer, HDC hdc, wxDouble width, wxDouble height )
|
|
: wxGraphicsContext(renderer)
|
|
{
|
|
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)
|
|
{
|
|
RECT rect = wxGetWindowRect(hwnd);
|
|
Init(new Graphics(hwnd), rect.right - rect.left, rect.bottom - rect.top);
|
|
m_enableOffset = true;
|
|
}
|
|
|
|
wxGDIPlusContext::wxGDIPlusContext( wxGraphicsRenderer* renderer, Graphics* gr )
|
|
: wxGraphicsContext(renderer)
|
|
{
|
|
Init(gr, 0, 0);
|
|
}
|
|
|
|
wxGDIPlusContext::wxGDIPlusContext(wxGraphicsRenderer* renderer)
|
|
: wxGraphicsContext(renderer)
|
|
{
|
|
// 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(Graphics* graphics, int width, int height)
|
|
{
|
|
m_context = graphics;
|
|
m_state1 = 0;
|
|
m_state2 = 0;
|
|
m_width = width;
|
|
m_height = height;
|
|
m_fontScaleRatio = 1.0;
|
|
|
|
m_context->SetTextRenderingHint(TextRenderingHintSystemDefault);
|
|
m_context->SetPixelOffsetMode(PixelOffsetModeHalf);
|
|
m_context->SetSmoothingMode(SmoothingModeHighQuality);
|
|
|
|
m_state1 = m_context->Save();
|
|
m_state2 = m_context->Save();
|
|
}
|
|
|
|
wxGDIPlusContext::~wxGDIPlusContext()
|
|
{
|
|
if ( m_context )
|
|
{
|
|
m_context->Restore( m_state2 );
|
|
m_context->Restore( m_state1 );
|
|
delete m_context;
|
|
}
|
|
}
|
|
|
|
|
|
void wxGDIPlusContext::Clip( const wxRegion ®ion )
|
|
{
|
|
Region rgn((HRGN)region.GetHRGN());
|
|
m_context->SetClip(&rgn,CombineModeIntersect);
|
|
}
|
|
|
|
void wxGDIPlusContext::Clip( wxDouble x, wxDouble y, wxDouble w, wxDouble h )
|
|
{
|
|
m_context->SetClip(RectF(x,y,w,h),CombineModeIntersect);
|
|
}
|
|
|
|
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)
|
|
return;
|
|
|
|
if ( !m_pen.IsNull() )
|
|
{
|
|
wxGDIPlusOffsetHelper helper( m_context , ShouldOffset() );
|
|
PointF *cpoints = new PointF[n];
|
|
for (size_t i = 0; i < n; i++)
|
|
{
|
|
cpoints[i].X = static_cast<REAL>(points[i].m_x);
|
|
cpoints[i].Y = static_cast<REAL>(points[i].m_y);
|
|
|
|
} // for (size_t i = 0; i < n; i++)
|
|
m_context->DrawLines( ((wxGDIPlusPenData*)m_pen.GetGraphicsData())->GetGDIPlusPen() , cpoints , n ) ;
|
|
delete[] cpoints;
|
|
}
|
|
}
|
|
|
|
void wxGDIPlusContext::DrawLines( size_t n, const wxPoint2DDouble *points, wxPolygonFillMode WXUNUSED(fillStyle) )
|
|
{
|
|
if (m_composition == wxCOMPOSITION_DEST)
|
|
return;
|
|
|
|
wxGDIPlusOffsetHelper helper( m_context , ShouldOffset() );
|
|
PointF *cpoints = new PointF[n];
|
|
for (size_t i = 0; i < n; i++)
|
|
{
|
|
cpoints[i].X = static_cast<REAL>(points[i].m_x);
|
|
cpoints[i].Y = static_cast<REAL>(points[i].m_y);
|
|
|
|
} // for (int i = 0; i < n; i++)
|
|
if ( !m_brush.IsNull() )
|
|
m_context->FillPolygon( ((wxGDIPlusBrushData*)m_brush.GetRefData())->GetGDIPlusBrush() , cpoints , n ) ;
|
|
if ( !m_pen.IsNull() )
|
|
m_context->DrawLines( ((wxGDIPlusPenData*)m_pen.GetGraphicsData())->GetGDIPlusPen() , cpoints , n ) ;
|
|
delete[] cpoints;
|
|
}
|
|
|
|
void wxGDIPlusContext::StrokePath( const wxGraphicsPath& path )
|
|
{
|
|
if (m_composition == wxCOMPOSITION_DEST)
|
|
return;
|
|
|
|
if ( !m_pen.IsNull() )
|
|
{
|
|
wxGDIPlusOffsetHelper helper( m_context , ShouldOffset() );
|
|
m_context->DrawPath( ((wxGDIPlusPenData*)m_pen.GetGraphicsData())->GetGDIPlusPen() , (GraphicsPath*) path.GetNativePath() );
|
|
}
|
|
}
|
|
|
|
void wxGDIPlusContext::FillPath( const wxGraphicsPath& path , wxPolygonFillMode fillStyle )
|
|
{
|
|
if (m_composition == wxCOMPOSITION_DEST)
|
|
return;
|
|
|
|
if ( !m_brush.IsNull() )
|
|
{
|
|
wxGDIPlusOffsetHelper helper( m_context , ShouldOffset() );
|
|
((GraphicsPath*) path.GetNativePath())->SetFillMode( fillStyle == wxODDEVEN_RULE ? FillModeAlternate : FillModeWinding);
|
|
m_context->FillPath( ((wxGDIPlusBrushData*)m_brush.GetRefData())->GetGDIPlusBrush() ,
|
|
(GraphicsPath*) path.GetNativePath());
|
|
}
|
|
}
|
|
|
|
bool wxGDIPlusContext::SetAntialiasMode(wxAntialiasMode antialias)
|
|
{
|
|
if (m_antialias == antialias)
|
|
return true;
|
|
|
|
// MinGW currently doesn't provide InterpolationModeInvalid in its headers,
|
|
// so use our own definition.
|
|
static const SmoothingMode
|
|
wxSmoothingModeInvalid = static_cast<SmoothingMode>(-1);
|
|
|
|
SmoothingMode antialiasMode = wxSmoothingModeInvalid;
|
|
switch (antialias)
|
|
{
|
|
case wxANTIALIAS_DEFAULT:
|
|
antialiasMode = SmoothingModeHighQuality;
|
|
break;
|
|
|
|
case wxANTIALIAS_NONE:
|
|
antialiasMode = SmoothingModeNone;
|
|
break;
|
|
}
|
|
|
|
wxCHECK_MSG( antialiasMode != wxSmoothingModeInvalid, false,
|
|
wxS("Unknown antialias mode") );
|
|
|
|
if ( m_context->SetSmoothingMode(antialiasMode) != Gdiplus::Ok )
|
|
return false;
|
|
|
|
m_antialias = antialias;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxGDIPlusContext::SetInterpolationQuality(wxInterpolationQuality interpolation)
|
|
{
|
|
if (m_interpolation == interpolation)
|
|
return true;
|
|
|
|
// MinGW currently doesn't provide InterpolationModeInvalid in its headers,
|
|
// so use our own definition.
|
|
static const InterpolationMode
|
|
wxInterpolationModeInvalid = static_cast<InterpolationMode>(-1);
|
|
|
|
InterpolationMode interpolationMode = wxInterpolationModeInvalid;
|
|
switch (interpolation)
|
|
{
|
|
case wxINTERPOLATION_DEFAULT:
|
|
interpolationMode = InterpolationModeDefault;
|
|
break;
|
|
|
|
case wxINTERPOLATION_NONE:
|
|
interpolationMode = InterpolationModeNearestNeighbor;
|
|
break;
|
|
|
|
case wxINTERPOLATION_FAST:
|
|
interpolationMode = InterpolationModeLowQuality;
|
|
break;
|
|
|
|
case wxINTERPOLATION_GOOD:
|
|
interpolationMode = InterpolationModeHighQuality;
|
|
break;
|
|
|
|
case wxINTERPOLATION_BEST:
|
|
interpolationMode = InterpolationModeHighQualityBicubic;
|
|
break;
|
|
}
|
|
|
|
wxCHECK_MSG( interpolationMode != wxInterpolationModeInvalid, false,
|
|
wxS("Unknown interpolation mode") );
|
|
|
|
if ( m_context->SetInterpolationMode(interpolationMode) != Gdiplus::Ok )
|
|
return false;
|
|
|
|
m_interpolation = interpolation;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxGDIPlusContext::SetCompositionMode(wxCompositionMode op)
|
|
{
|
|
if ( m_composition == op )
|
|
return true;
|
|
|
|
m_composition = op;
|
|
|
|
if (m_composition == wxCOMPOSITION_DEST)
|
|
return true;
|
|
|
|
CompositingMode cop;
|
|
switch (op)
|
|
{
|
|
case wxCOMPOSITION_SOURCE:
|
|
cop = CompositingModeSourceCopy;
|
|
break;
|
|
case wxCOMPOSITION_OVER:
|
|
cop = CompositingModeSourceOver;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
m_context->SetCompositingMode(cop);
|
|
return true;
|
|
}
|
|
|
|
void wxGDIPlusContext::BeginLayer(wxDouble /* opacity */)
|
|
{
|
|
// TODO
|
|
}
|
|
|
|
void wxGDIPlusContext::EndLayer()
|
|
{
|
|
// TODO
|
|
}
|
|
|
|
void wxGDIPlusContext::Rotate( wxDouble angle )
|
|
{
|
|
m_context->RotateTransform( wxRadToDeg(angle) );
|
|
}
|
|
|
|
void wxGDIPlusContext::Translate( wxDouble dx , wxDouble dy )
|
|
{
|
|
m_context->TranslateTransform( dx , dy );
|
|
}
|
|
|
|
void wxGDIPlusContext::Scale( wxDouble xScale , wxDouble yScale )
|
|
{
|
|
m_context->ScaleTransform(xScale,yScale);
|
|
}
|
|
|
|
void wxGDIPlusContext::PushState()
|
|
{
|
|
GraphicsState state = m_context->Save();
|
|
m_stateStack.push(state);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void wxGDIPlusContext::DrawBitmap( const wxGraphicsBitmap &bmp, wxDouble x, wxDouble y, wxDouble w, wxDouble h )
|
|
{
|
|
if (m_composition == wxCOMPOSITION_DEST)
|
|
return;
|
|
|
|
Bitmap* image = static_cast<wxGDIPlusBitmapData*>(bmp.GetRefData())->GetGDIPlusBitmap();
|
|
if ( image )
|
|
{
|
|
if( image->GetWidth() != (UINT) w || image->GetHeight() != (UINT) h )
|
|
{
|
|
Rect drawRect((REAL) x, (REAL)y, (REAL)w, (REAL)h);
|
|
m_context->SetPixelOffsetMode( PixelOffsetModeNone );
|
|
m_context->DrawImage(image, drawRect, 0 , 0 , image->GetWidth(), image->GetHeight(), UnitPixel ) ;
|
|
m_context->SetPixelOffsetMode( PixelOffsetModeHalf );
|
|
}
|
|
else
|
|
m_context->DrawImage(image,(REAL) x,(REAL) y,(REAL) w,(REAL) h) ;
|
|
}
|
|
}
|
|
|
|
void wxGDIPlusContext::DrawBitmap( const wxBitmap &bmp, wxDouble x, wxDouble y, wxDouble w, wxDouble h )
|
|
{
|
|
wxGraphicsBitmap bitmap = GetRenderer()->CreateBitmap(bmp);
|
|
DrawBitmap(bitmap, x, y, w, h);
|
|
}
|
|
|
|
void wxGDIPlusContext::DrawIcon( const wxIcon &icon, wxDouble x, wxDouble y, wxDouble w, wxDouble h )
|
|
{
|
|
if (m_composition == wxCOMPOSITION_DEST)
|
|
return;
|
|
|
|
// the built-in conversion fails when there is alpha in the HICON (eg XP style icons), we can only
|
|
// find out by looking at the bitmap data whether there really was alpha in it
|
|
HICON hIcon = (HICON)icon.GetHICON();
|
|
ICONINFO iconInfo ;
|
|
// IconInfo creates the bitmaps for color and mask, we must dispose of them after use
|
|
if (!GetIconInfo(hIcon,&iconInfo))
|
|
return;
|
|
|
|
Bitmap interim(iconInfo.hbmColor,NULL);
|
|
|
|
Bitmap* image = NULL ;
|
|
|
|
// if it's not 32 bit, it doesn't have an alpha channel, note that since the conversion doesn't
|
|
// work correctly, asking IsAlphaPixelFormat at this point fails as well
|
|
if( GetPixelFormatSize(interim.GetPixelFormat())!= 32 )
|
|
{
|
|
image = Bitmap::FromHICON(hIcon);
|
|
}
|
|
else
|
|
{
|
|
size_t width = interim.GetWidth();
|
|
size_t height = interim.GetHeight();
|
|
Rect bounds(0,0,width,height);
|
|
BitmapData data ;
|
|
|
|
interim.LockBits(&bounds, ImageLockModeRead,
|
|
interim.GetPixelFormat(),&data);
|
|
|
|
bool hasAlpha = false;
|
|
for ( size_t y = 0 ; y < height && !hasAlpha ; ++y)
|
|
{
|
|
for( size_t x = 0 ; x < width && !hasAlpha; ++x)
|
|
{
|
|
ARGB *dest = (ARGB*)((BYTE*)data.Scan0 + data.Stride*y + x*4);
|
|
if ( ( *dest & Color::AlphaMask ) != 0 )
|
|
hasAlpha = true;
|
|
}
|
|
}
|
|
|
|
if ( hasAlpha )
|
|
{
|
|
image = new Bitmap(data.Width, data.Height, data.Stride,
|
|
PixelFormat32bppARGB , (BYTE*) data.Scan0);
|
|
}
|
|
else
|
|
{
|
|
image = Bitmap::FromHICON(hIcon);
|
|
}
|
|
|
|
interim.UnlockBits(&data);
|
|
}
|
|
|
|
m_context->DrawImage(image,(REAL) x,(REAL) y,(REAL) w,(REAL) h) ;
|
|
|
|
delete image ;
|
|
DeleteObject(iconInfo.hbmColor);
|
|
DeleteObject(iconInfo.hbmMask);
|
|
}
|
|
|
|
void wxGDIPlusContext::DoDrawText(const wxString& str,
|
|
wxDouble x, wxDouble y )
|
|
{
|
|
if (m_composition == wxCOMPOSITION_DEST)
|
|
return;
|
|
|
|
wxCHECK_RET( !m_font.IsNull(),
|
|
wxT("wxGDIPlusContext::DrawText - no valid font set") );
|
|
|
|
if ( str.IsEmpty())
|
|
return ;
|
|
|
|
wxGDIPlusFontData * const
|
|
fontData = (wxGDIPlusFontData *)m_font.GetRefData();
|
|
|
|
m_context->DrawString
|
|
(
|
|
str.wc_str(*wxConvUI), // string to draw, always Unicode
|
|
-1, // length: string is NUL-terminated
|
|
fontData->GetGDIPlusFont(),
|
|
PointF(x, y),
|
|
GetDrawTextStringFormat(),
|
|
fontData->GetGDIPlusBrush()
|
|
);
|
|
}
|
|
|
|
void wxGDIPlusContext::GetTextExtent( const wxString &str, wxDouble *width, wxDouble *height,
|
|
wxDouble *descent, wxDouble *externalLeading ) const
|
|
{
|
|
wxCHECK_RET( !m_font.IsNull(), wxT("wxGDIPlusContext::GetTextExtent - no valid font set") );
|
|
|
|
wxWCharBuffer s = str.wc_str( *wxConvUI );
|
|
FontFamily ffamily ;
|
|
Font* f = ((wxGDIPlusFontData*)m_font.GetRefData())->GetGDIPlusFont();
|
|
|
|
f->GetFamily(&ffamily) ;
|
|
|
|
REAL factorY = m_fontScaleRatio;
|
|
|
|
// Notice that we must use the real font style or the results would be
|
|
// incorrect for italic/bold fonts.
|
|
const INT style = f->GetStyle();
|
|
const REAL size = f->GetSize();
|
|
const REAL emHeight = ffamily.GetEmHeight(style);
|
|
REAL rDescent = ffamily.GetCellDescent(style) * size / emHeight;
|
|
REAL rAscent = ffamily.GetCellAscent(style) * size / emHeight;
|
|
REAL rHeight = ffamily.GetLineSpacing(style) * size / emHeight;
|
|
|
|
if ( height )
|
|
*height = rHeight * factorY;
|
|
if ( descent )
|
|
*descent = rDescent * factorY;
|
|
if ( externalLeading )
|
|
*externalLeading = (rHeight - rAscent - rDescent) * factorY;
|
|
// measuring empty strings is not guaranteed, so do it by hand
|
|
if ( str.IsEmpty())
|
|
{
|
|
if ( width )
|
|
*width = 0 ;
|
|
}
|
|
else
|
|
{
|
|
RectF layoutRect(0,0, 100000.0f, 100000.0f);
|
|
|
|
RectF bounds ;
|
|
m_context->MeasureString((const wchar_t *) s , wcslen(s) , f, layoutRect, GetDrawTextStringFormat(), &bounds ) ;
|
|
if ( width )
|
|
*width = bounds.Width;
|
|
if ( height )
|
|
*height = bounds.Height;
|
|
}
|
|
}
|
|
|
|
void wxGDIPlusContext::GetPartialTextExtents(const wxString& text, wxArrayDouble& widths) const
|
|
{
|
|
widths.Empty();
|
|
widths.Add(0, text.length());
|
|
|
|
wxCHECK_RET( !m_font.IsNull(), wxT("wxGDIPlusContext::GetPartialTextExtents - no valid font set") );
|
|
|
|
if (text.empty())
|
|
return;
|
|
|
|
Font* f = ((wxGDIPlusFontData*)m_font.GetRefData())->GetGDIPlusFont();
|
|
wxWCharBuffer ws = text.wc_str( *wxConvUI );
|
|
size_t len = wcslen( ws ) ;
|
|
wxASSERT_MSG(text.length() == len , wxT("GetPartialTextExtents not yet implemented for multichar situations"));
|
|
|
|
RectF layoutRect(0,0, 100000.0f, 100000.0f);
|
|
StringFormat strFormat( GetDrawTextStringFormat() );
|
|
|
|
size_t startPosition = 0;
|
|
size_t remainder = len;
|
|
const size_t maxSpan = 32;
|
|
CharacterRange* ranges = new CharacterRange[maxSpan] ;
|
|
Region* regions = new Region[maxSpan];
|
|
|
|
while( remainder > 0 )
|
|
{
|
|
size_t span = wxMin( maxSpan, remainder );
|
|
|
|
for( size_t i = 0 ; i < span ; ++i)
|
|
{
|
|
ranges[i].First = 0 ;
|
|
ranges[i].Length = startPosition+i+1 ;
|
|
}
|
|
strFormat.SetMeasurableCharacterRanges(span,ranges);
|
|
m_context->MeasureCharacterRanges(ws, -1 , f,layoutRect, &strFormat,span,regions) ;
|
|
|
|
RectF bbox ;
|
|
for ( size_t i = 0 ; i < span ; ++i)
|
|
{
|
|
regions[i].GetBounds(&bbox,m_context);
|
|
widths[startPosition+i] = bbox.Width;
|
|
}
|
|
remainder -= span;
|
|
startPosition += span;
|
|
}
|
|
|
|
delete[] ranges;
|
|
delete[] regions;
|
|
}
|
|
|
|
bool wxGDIPlusContext::ShouldOffset() const
|
|
{
|
|
if ( !m_enableOffset )
|
|
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;
|
|
}
|
|
|
|
void* wxGDIPlusContext::GetNativeContext()
|
|
{
|
|
return m_context;
|
|
}
|
|
|
|
// concatenates this transform with the current transform of this context
|
|
void wxGDIPlusContext::ConcatTransform( const wxGraphicsMatrix& matrix )
|
|
{
|
|
m_context->MultiplyTransform((Matrix*) matrix.GetNativeMatrix());
|
|
}
|
|
|
|
// sets the transform of this context
|
|
void wxGDIPlusContext::SetTransform( const wxGraphicsMatrix& matrix )
|
|
{
|
|
m_context->SetTransform((Matrix*) matrix.GetNativeMatrix());
|
|
}
|
|
|
|
// gets the matrix of this context
|
|
wxGraphicsMatrix wxGDIPlusContext::GetTransform() const
|
|
{
|
|
wxGraphicsMatrix matrix = CreateMatrix();
|
|
m_context->GetTransform((Matrix*) matrix.GetNativeMatrix());
|
|
return matrix;
|
|
}
|
|
|
|
void wxGDIPlusContext::GetSize( wxDouble* width, wxDouble *height )
|
|
{
|
|
*width = m_width;
|
|
*height = m_height;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// wxGDIPlusPrintingContext implementation
|
|
//-----------------------------------------------------------------------------
|
|
|
|
wxGDIPlusPrintingContext::wxGDIPlusPrintingContext( wxGraphicsRenderer* renderer,
|
|
const wxDC& dc )
|
|
: wxGDIPlusContext(renderer, dc)
|
|
{
|
|
Graphics* context = GetGraphics();
|
|
|
|
//m_context->SetPageUnit(UnitDocument);
|
|
|
|
// 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);
|
|
|
|
// 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
|
|
//-----------------------------------------------------------------------------
|
|
|
|
IMPLEMENT_DYNAMIC_CLASS(wxGDIPlusRenderer,wxGraphicsRenderer)
|
|
|
|
static wxGDIPlusRenderer gs_GDIPlusRenderer;
|
|
|
|
wxGraphicsRenderer* wxGraphicsRenderer::GetGDIPlusRenderer()
|
|
{
|
|
return &gs_GDIPlusRenderer;
|
|
}
|
|
|
|
wxGraphicsRenderer* wxGraphicsRenderer::GetDefaultRenderer()
|
|
{
|
|
return wxGraphicsRenderer::GetGDIPlusRenderer();
|
|
}
|
|
|
|
bool wxGDIPlusRenderer::EnsureIsLoaded()
|
|
{
|
|
// load gdiplus.dll if not yet loaded, but don't bother doing it again
|
|
// if we already tried and failed (we don't want to spend lot of time
|
|
// returning NULL from wxGraphicsContext::Create(), which may be called
|
|
// relatively frequently):
|
|
if ( m_loaded == -1 )
|
|
{
|
|
Load();
|
|
}
|
|
|
|
return m_loaded == 1;
|
|
}
|
|
|
|
// call EnsureIsLoaded() and return returnOnFail value if it fails
|
|
#define ENSURE_LOADED_OR_RETURN(returnOnFail) \
|
|
if ( !EnsureIsLoaded() ) \
|
|
return (returnOnFail)
|
|
|
|
|
|
void wxGDIPlusRenderer::Load()
|
|
{
|
|
GdiplusStartupInput input;
|
|
GdiplusStartupOutput output;
|
|
if ( GdiplusStartup(&m_gditoken,&input,&output) == Gdiplus::Ok )
|
|
{
|
|
wxLogTrace("gdiplus", "successfully initialized GDI+");
|
|
m_loaded = 1;
|
|
}
|
|
else
|
|
{
|
|
wxLogTrace("gdiplus", "failed to initialize GDI+, missing gdiplus.dll?");
|
|
m_loaded = 0;
|
|
}
|
|
}
|
|
|
|
void wxGDIPlusRenderer::Unload()
|
|
{
|
|
if ( m_gditoken )
|
|
{
|
|
GdiplusShutdown(m_gditoken);
|
|
m_gditoken = 0;
|
|
}
|
|
m_loaded = -1; // next Load() will try again
|
|
}
|
|
|
|
wxGraphicsContext * wxGDIPlusRenderer::CreateContext( const wxWindowDC& dc)
|
|
{
|
|
ENSURE_LOADED_OR_RETURN(NULL);
|
|
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);
|
|
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);
|
|
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);
|
|
return new wxGDIPlusMeasuringContext(this);
|
|
}
|
|
|
|
wxGraphicsContext * wxGDIPlusRenderer::CreateContextFromNativeContext( void * context )
|
|
{
|
|
ENSURE_LOADED_OR_RETURN(NULL);
|
|
return new wxGDIPlusContext(this,(Graphics*) context);
|
|
}
|
|
|
|
|
|
wxGraphicsContext * wxGDIPlusRenderer::CreateContextFromNativeWindow( void * window )
|
|
{
|
|
ENSURE_LOADED_OR_RETURN(NULL);
|
|
return new wxGDIPlusContext(this,(HWND) window);
|
|
}
|
|
|
|
wxGraphicsContext * wxGDIPlusRenderer::CreateContext( wxWindow* window )
|
|
{
|
|
ENSURE_LOADED_OR_RETURN(NULL);
|
|
return new wxGDIPlusContext(this, (HWND) window->GetHWND() );
|
|
}
|
|
|
|
// Path
|
|
|
|
wxGraphicsPath wxGDIPlusRenderer::CreatePath()
|
|
{
|
|
ENSURE_LOADED_OR_RETURN(wxNullGraphicsPath);
|
|
wxGraphicsPath m;
|
|
m.SetRefData( new wxGDIPlusPathData(this));
|
|
return m;
|
|
}
|
|
|
|
|
|
// Matrix
|
|
|
|
wxGraphicsMatrix wxGDIPlusRenderer::CreateMatrix( wxDouble a, wxDouble b, wxDouble c, wxDouble d,
|
|
wxDouble tx, wxDouble ty)
|
|
|
|
{
|
|
ENSURE_LOADED_OR_RETURN(wxNullGraphicsMatrix);
|
|
wxGraphicsMatrix m;
|
|
wxGDIPlusMatrixData* data = new wxGDIPlusMatrixData( this );
|
|
data->Set( a,b,c,d,tx,ty ) ;
|
|
m.SetRefData(data);
|
|
return m;
|
|
}
|
|
|
|
wxGraphicsPen wxGDIPlusRenderer::CreatePen(const wxPen& pen)
|
|
{
|
|
ENSURE_LOADED_OR_RETURN(wxNullGraphicsPen);
|
|
if ( !pen.IsOk() || pen.GetStyle() == wxPENSTYLE_TRANSPARENT )
|
|
return wxNullGraphicsPen;
|
|
else
|
|
{
|
|
wxGraphicsPen p;
|
|
p.SetRefData(new wxGDIPlusPenData( this, pen ));
|
|
return p;
|
|
}
|
|
}
|
|
|
|
wxGraphicsBrush wxGDIPlusRenderer::CreateBrush(const wxBrush& brush )
|
|
{
|
|
ENSURE_LOADED_OR_RETURN(wxNullGraphicsBrush);
|
|
if ( !brush.IsOk() || brush.GetStyle() == wxBRUSHSTYLE_TRANSPARENT )
|
|
return wxNullGraphicsBrush;
|
|
else
|
|
{
|
|
wxGraphicsBrush p;
|
|
p.SetRefData(new wxGDIPlusBrushData( this, brush ));
|
|
return p;
|
|
}
|
|
}
|
|
|
|
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, stops);
|
|
p.SetRefData(d);
|
|
return p;
|
|
}
|
|
|
|
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,stops);
|
|
p.SetRefData(d);
|
|
return p;
|
|
}
|
|
|
|
wxGraphicsFont
|
|
wxGDIPlusRenderer::CreateFont( const wxFont &font,
|
|
const wxColour &col )
|
|
{
|
|
ENSURE_LOADED_OR_RETURN(wxNullGraphicsFont);
|
|
if ( font.IsOk() )
|
|
{
|
|
wxGraphicsFont p;
|
|
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.IsOk() )
|
|
{
|
|
wxGraphicsBitmap p;
|
|
p.SetRefData(new wxGDIPlusBitmapData( this , bitmap ));
|
|
return p;
|
|
}
|
|
else
|
|
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<wxGDIPlusBitmapData*>(bmp.GetGraphicsData());
|
|
|
|
return data ? data->ConvertToImage() : wxNullImage;
|
|
}
|
|
|
|
#endif // wxUSE_IMAGE
|
|
|
|
|
|
wxGraphicsBitmap wxGDIPlusRenderer::CreateBitmapFromNativeBitmap( void *bitmap )
|
|
{
|
|
ENSURE_LOADED_OR_RETURN(wxNullGraphicsBitmap);
|
|
if ( bitmap != NULL )
|
|
{
|
|
wxGraphicsBitmap p;
|
|
p.SetRefData(new wxGDIPlusBitmapData( this , (Bitmap*) bitmap ));
|
|
return p;
|
|
}
|
|
else
|
|
return wxNullGraphicsBitmap;
|
|
}
|
|
|
|
wxGraphicsBitmap wxGDIPlusRenderer::CreateSubBitmap( const wxGraphicsBitmap &bitmap, wxDouble x, wxDouble y, wxDouble w, wxDouble h )
|
|
{
|
|
ENSURE_LOADED_OR_RETURN(wxNullGraphicsBitmap);
|
|
Bitmap* image = static_cast<wxGDIPlusBitmapData*>(bitmap.GetRefData())->GetGDIPlusBitmap();
|
|
if ( image )
|
|
{
|
|
wxGraphicsBitmap p;
|
|
p.SetRefData(new wxGDIPlusBitmapData( this , image->Clone( (REAL) x , (REAL) y , (REAL) w , (REAL) h , PixelFormat32bppPARGB) ));
|
|
return p;
|
|
}
|
|
else
|
|
return wxNullGraphicsBitmap;
|
|
}
|
|
|
|
wxString wxGDIPlusRenderer::GetName() const
|
|
{
|
|
return "gdiplus";
|
|
}
|
|
|
|
void wxGDIPlusRenderer::GetVersion(int *major, int *minor, int *micro) const
|
|
{
|
|
if ( major )
|
|
*major = wxPlatformInfo::Get().GetOSMajorVersion();
|
|
if ( minor )
|
|
*minor = wxPlatformInfo::Get().GetOSMinorVersion();
|
|
if ( micro )
|
|
*micro = 0;
|
|
}
|
|
|
|
// Shutdown GDI+ at app exit, before possible dll unload
|
|
class wxGDIPlusRendererModule : public wxModule
|
|
{
|
|
public:
|
|
wxGDIPlusRendererModule()
|
|
{
|
|
// We must be uninitialized before GDI+ DLL itself is unloaded.
|
|
AddDependency("wxGdiPlusModule");
|
|
}
|
|
|
|
virtual bool OnInit() { return true; }
|
|
virtual void OnExit()
|
|
{
|
|
wxDELETE(gs_drawTextStringFormat);
|
|
|
|
gs_GDIPlusRenderer.Unload();
|
|
}
|
|
|
|
private:
|
|
DECLARE_DYNAMIC_CLASS(wxGDIPlusRendererModule)
|
|
};
|
|
|
|
IMPLEMENT_DYNAMIC_CLASS(wxGDIPlusRendererModule, wxModule)
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxMSW-specific parts of wxGCDC
|
|
// ----------------------------------------------------------------------------
|
|
|
|
WXHDC wxGCDC::AcquireHDC()
|
|
{
|
|
wxGraphicsContext * const gc = GetGraphicsContext();
|
|
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<Graphics *>(gc->GetNativeContext());
|
|
return g ? g->GetHDC() : NULL;
|
|
}
|
|
|
|
void wxGCDC::ReleaseHDC(WXHDC hdc)
|
|
{
|
|
if ( !hdc )
|
|
return;
|
|
|
|
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<Graphics *>(gc->GetNativeContext());
|
|
wxCHECK_RET( g, "can't release HDC because there is no Graphics" );
|
|
|
|
g->ReleaseHDC((HDC)hdc);
|
|
}
|
|
|
|
#endif // wxUSE_GRAPHICS_GDIPLUS
|