From 0c69d619dcc3e45cdd00482b54d388cf65d28ff2 Mon Sep 17 00:00:00 2001 From: Stefan Csomor Date: Sat, 6 Sep 2014 14:59:53 +0000 Subject: [PATCH] direct2d GSOC2014 add addtional file git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@77550 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- src/msw/graphicsd2d.cpp | 3731 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 3731 insertions(+) create mode 100644 src/msw/graphicsd2d.cpp diff --git a/src/msw/graphicsd2d.cpp b/src/msw/graphicsd2d.cpp new file mode 100644 index 0000000000..4fe97ebe7c --- /dev/null +++ b/src/msw/graphicsd2d.cpp @@ -0,0 +1,3731 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/msw/graphicsd2d.cpp +// Purpose: Implementation of Direct2D Render Context +// Author: Pana Alexandru +// Created: 2014-05-20 +// Copyright: (c) 2014 wxWidgets development team +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_GRAPHICS_DIRECT2D + +// Minimum supported client: Windows 8 and Platform Update for Windows 7 +#define wxD2D_DEVICE_CONTEXT_SUPPORTED 0 + +#include +#include +#include +#include + +// Ensure no previous defines interfere with the Direct2D API headers +#undef GetHwnd + +// We load these functions at runtime from the d2d1.dll. +// However, since they are also used inside the d2d1.h header we must provide +// implementations matching the exact declarations. These defines ensures we +// are not violating the ODR rule. +#define D2D1CreateFactory wxD2D1CreateFactory +#define D2D1MakeRotateMatrix wxD2D1MakeRotateMatrix +#define D2D1InvertMatrix wxD2D1InvertMatrix + +#include +#include +#include + +#if wxD2D_DEVICE_CONTEXT_SUPPORTED +#include +#include +#include +#endif + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#include "wx/graphics.h" +#include "wx/dc.h" +#include "wx/dynlib.h" +#include "wx/image.h" +#include "wx/msw/private/comptr.h" +#include "wx/private/graphics.h" +#include "wx/stack.h" +#include "wx/sharedptr.h" + +// This must be the last header included to only affect the DEFINE_GUID() +// occurrences below but not any GUIDs declared in the standard files included +// above. +#include + +// Generic error message for a failed direct2d operation +#define wxFAILED_HRESULT_MSG(result) \ + wxString::Format("Direct2D failed with HRESULT %x", (result)) + +// Checks a HRESULT value for success, otherwise displays an error message and +// returns from the enclosing function. +#define wxCHECK_HRESULT_RET(result) \ + wxCHECK_RET(SUCCEEDED(result), wxFAILED_HRESULT_MSG(result)) + +#define wxCHECK2_HRESULT_RET(result, returnValue) \ + wxCHECK2_MSG(SUCCEEDED(result), return returnValue, \ + wxFAILED_HRESULT_MSG(result)) + +// Variation of wxCHECK_HRESULT_RET for functions which must return a pointer +#define wxCHECK_HRESULT_RET_PTR(result) wxCHECK2_HRESULT_RET(result, NULL) + +// Checks the precondition of wxManagedResourceHolder::AcquireResource, namely +// that it is bound to a manager. +#define wxCHECK_RESOURCE_HOLDER_PRE() \ + { \ + if (IsResourceAcquired()) return; \ + wxCHECK_RET(IsBound(), \ + "Cannot acquire a native resource without being bound to a manager"); \ + } + +// Checks the postcondition of wxManagedResourceHolder::AcquireResource, namely +// that it was successful in acquiring the native resource. +#define wxCHECK_RESOURCE_HOLDER_POST() \ + wxCHECK_RET(m_nativeResource != NULL, "Could not acquire native resource"); + + +// Helper class used to check for direct2d availability at runtime and to +// dynamically load the required symbols from d2d1.dll and dwrite.dll +class wxDirect2D +{ +public: + + enum wxD2DVersion + { + wxD2D_VERSION_1_0, + wxD2D_VERSION_1_1, + wxD2D_VERSION_NONE + }; + + static bool Initialize() + { + if (!m_initialized) + { + m_hasDirect2DSupport = LoadLibraries(); + m_initialized = true; + } + + return m_hasDirect2DSupport; + } + + static bool HasDirect2DSupport() + { + Initialize(); + + return m_hasDirect2DSupport; + } + + static wxD2DVersion GetDirect2DVersion() + { + return m_D2DRuntimeVersion; + } + +private: + static bool LoadLibraries() + { + m_dllDirect2d.Load(wxT("d2d1.dll"), wxDL_VERBATIM); + m_dllDirectWrite.Load(wxT("dwrite.dll"), wxDL_VERBATIM); + + bool hasDirect2dSupport = m_dllDirect2d.IsLoaded() && m_dllDirectWrite.IsLoaded(); + + if (!hasDirect2dSupport) + return false; + + #define wxLOAD_FUNC(dll, name) \ + name = (name##_t)dll.RawGetSymbol(#name); \ + if ( !name ) \ + return false; + + wxLOAD_FUNC(m_dllDirect2d, D2D1CreateFactory); + wxLOAD_FUNC(m_dllDirect2d, D2D1MakeRotateMatrix); + wxLOAD_FUNC(m_dllDirect2d, D2D1InvertMatrix); + wxLOAD_FUNC(m_dllDirectWrite, DWriteCreateFactory); + + m_D2DRuntimeVersion = wxD2D_VERSION_1_0; + + return true; + } + +public: + typedef HRESULT (WINAPI *D2D1CreateFactory_t)(D2D1_FACTORY_TYPE, REFIID, CONST D2D1_FACTORY_OPTIONS*, void**); + static D2D1CreateFactory_t D2D1CreateFactory; + + typedef void (WINAPI *D2D1MakeRotateMatrix_t)(FLOAT, D2D1_POINT_2F, D2D1_MATRIX_3X2_F*); + static D2D1MakeRotateMatrix_t D2D1MakeRotateMatrix; + + typedef BOOL (WINAPI *D2D1InvertMatrix_t)(D2D1_MATRIX_3X2_F*); + static D2D1InvertMatrix_t D2D1InvertMatrix; + + typedef HRESULT (WINAPI *DWriteCreateFactory_t)(DWRITE_FACTORY_TYPE, REFIID, IUnknown**); + static DWriteCreateFactory_t DWriteCreateFactory; + +private: + static bool m_initialized; + static bool m_hasDirect2DSupport; + static wxD2DVersion m_D2DRuntimeVersion; + + static wxDynamicLibrary m_dllDirect2d; + static wxDynamicLibrary m_dllDirectWrite; +}; + +// define the members +bool wxDirect2D::m_initialized = false; +bool wxDirect2D::m_hasDirect2DSupport = false; +wxDirect2D::wxD2DVersion wxDirect2D::m_D2DRuntimeVersion = wxD2D_VERSION_NONE; + +wxDynamicLibrary wxDirect2D::m_dllDirect2d; +wxDynamicLibrary wxDirect2D::m_dllDirectWrite; + +// define the (not yet imported) functions +wxDirect2D::D2D1CreateFactory_t wxDirect2D::D2D1CreateFactory = NULL; +wxDirect2D::D2D1MakeRotateMatrix_t wxDirect2D::D2D1MakeRotateMatrix = NULL; +wxDirect2D::D2D1InvertMatrix_t wxDirect2D::D2D1InvertMatrix = NULL; +wxDirect2D::DWriteCreateFactory_t wxDirect2D::DWriteCreateFactory = NULL; + +// define the interface GUIDs +DEFINE_GUID(wxIID_IWICImagingFactory, + 0xec5ec8a9, 0xc395, 0x4314, 0x9c, 0x77, 0x54, 0xd7, 0xa9, 0x35, 0xff, 0x70); + +DEFINE_GUID(wxIID_IDWriteFactory, + 0xb859ee5a, 0xd838, 0x4b5b, 0xa2, 0xe8, 0x1a, 0xdc, 0x7d, 0x93, 0xdb, 0x48); + +DEFINE_GUID(wxIID_IWICBitmapSource, + 0x00000120, 0xa8f2, 0x4877, 0xba, 0x0a, 0xfd, 0x2b, 0x66, 0x45, 0xfb, 0x94); + +// Implementation of the Direct2D functions +HRESULT WINAPI wxD2D1CreateFactory( + D2D1_FACTORY_TYPE factoryType, + REFIID riid, + CONST D2D1_FACTORY_OPTIONS *pFactoryOptions, + void **ppIFactory) +{ + if (!wxDirect2D::Initialize()) + return S_FALSE; + + return wxDirect2D::D2D1CreateFactory( + factoryType, + riid, + pFactoryOptions, + ppIFactory); +} + +void WINAPI wxD2D1MakeRotateMatrix( + FLOAT angle, + D2D1_POINT_2F center, + D2D1_MATRIX_3X2_F *matrix) +{ + if (!wxDirect2D::Initialize()) + return; + + wxDirect2D::D2D1MakeRotateMatrix(angle, center, matrix); +} + +BOOL WINAPI wxD2D1InvertMatrix( + D2D1_MATRIX_3X2_F *matrix) +{ + if (!wxDirect2D::Initialize()) + return FALSE; + + return wxDirect2D::D2D1InvertMatrix(matrix); +} + +static bool gs_isComInitialized = false; + +void wxEnsureCOMLibraryInitialized() +{ + if (!gs_isComInitialized) + { + CoInitializeEx(NULL, COINIT_MULTITHREADED); + gs_isComInitialized = true; + } +} + +static IWICImagingFactory* gs_WICImagingFactory = NULL; + +IWICImagingFactory* wxWICImagingFactory() +{ + if (gs_WICImagingFactory == NULL) { + wxEnsureCOMLibraryInitialized(); + + HRESULT hr = CoCreateInstance( + CLSID_WICImagingFactory, + NULL, + CLSCTX_INPROC_SERVER, + wxIID_IWICImagingFactory, + (LPVOID*)&gs_WICImagingFactory); + wxCHECK_HRESULT_RET_PTR(hr); + } + return gs_WICImagingFactory; +} + +static IDWriteFactory* gs_IDWriteFactory = NULL; + +IDWriteFactory* wxDWriteFactory() +{ + if (!wxDirect2D::Initialize()) + return NULL; + + if (gs_IDWriteFactory == NULL) + { + wxDirect2D::DWriteCreateFactory( + DWRITE_FACTORY_TYPE_SHARED, + wxIID_IDWriteFactory, + reinterpret_cast(&gs_IDWriteFactory) + ); + } + return gs_IDWriteFactory; +} + +extern WXDLLIMPEXP_DATA_CORE(wxGraphicsPen) wxNullGraphicsPen; +extern WXDLLIMPEXP_DATA_CORE(wxGraphicsBrush) wxNullGraphicsBrush; + +// We use the notion of a context supplier because the context +// needed to create Direct2D resources (namely the RenderTarget) +// is itself device-dependent and might change during the lifetime +// of the resources which were created from it. +template +class wxContextSupplier +{ +public: + typedef C ContextType; + + virtual C GetContext() = 0; +}; + +typedef wxContextSupplier wxD2DContextSupplier; + +// A resource holder manages a generic resource by acquiring +// and releasing it on demand. +class wxResourceHolder +{ +public: + // Acquires the managed resource if necessary (not already acquired) + virtual void AcquireResource() = 0; + + // Releases the managed resource + virtual void ReleaseResource() = 0; + + // Checks if the resources was previously acquired + virtual bool IsResourceAcquired() = 0; + + // Returns the managed resource or NULL if the resources + // was not previously acquired + virtual void* GetResource() = 0; + + virtual ~wxResourceHolder(){}; +}; + +class wxD2DResourceManager; + +class wxD2DManagedObject +{ +public: + virtual void Bind(wxD2DResourceManager* manager) = NULL; + virtual void UnBind() = NULL; + virtual bool IsBound() = NULL; + virtual wxD2DResourceManager* GetManager() = NULL; + + virtual ~wxD2DManagedObject() {}; +}; + +class wxManagedResourceHolder : public wxResourceHolder, public wxD2DManagedObject +{ +public: + virtual ~wxManagedResourceHolder() {}; +}; + +// A Direct2D resource manager handles the device-dependent +// resource holders attached to it by requesting them to +// release their resources when the API invalidates. +class wxD2DResourceManager: public wxD2DContextSupplier +{ +public: + typedef wxManagedResourceHolder* ElementType; + + // NOTE: We're using a list because we expect to have multiple + // insertions but very rarely a traversal (if ever). + typedef std::list ListType; + + void RegisterResourceHolder(ElementType resourceHolder) + { + m_resources.push_back(resourceHolder); + } + + void UnregisterResourceHolder(ElementType resourceHolder) + { + m_resources.remove(resourceHolder); + } + + void ReleaseResources() + { + ListType::iterator it; + for (it = m_resources.begin(); it != m_resources.end(); ++it) + { + (*it)->ReleaseResource(); + } + + // Check that all resources were released + for (it = m_resources.begin(); it != m_resources.end(); ++it) + { + wxCHECK_RET(!(*it)->IsResourceAcquired(), "One or more device-dependent resources failed to release"); + } + } + + virtual ~wxD2DResourceManager() + { + while (!m_resources.empty()) + { + m_resources.front()->ReleaseResource(); + m_resources.front()->UnBind(); + } + } + +private: + ListType m_resources; +}; + +// A Direct2D resource holder manages device dependent resources +// by storing any information necessary for acquiring the resource +// and releasing the resource when the API invalidates it. +template +class wxD2DResourceHolder: public wxManagedResourceHolder +{ +public: + wxD2DResourceHolder() : m_resourceManager(NULL) + { + } + + virtual ~wxD2DResourceHolder() + { + UnBind(); + ReleaseResource(); + } + + bool IsResourceAcquired() wxOVERRIDE + { + return m_nativeResource != NULL; + } + + void* GetResource() wxOVERRIDE + { + return GetD2DResource(); + } + + wxCOMPtr& GetD2DResource() + { + if (!IsResourceAcquired()) + { + AcquireResource(); + } + + return m_nativeResource; + } + + void AcquireResource() wxOVERRIDE + { + wxCHECK_RESOURCE_HOLDER_PRE(); + + DoAcquireResource(); + + wxCHECK_RESOURCE_HOLDER_POST(); + } + + void ReleaseResource() wxOVERRIDE + { + m_nativeResource.reset(); + } + + wxD2DContextSupplier::ContextType GetContext() + { + return m_resourceManager->GetContext(); + } + + void Bind(wxD2DResourceManager* manager) wxOVERRIDE + { + if (IsBound()) + return; + + m_resourceManager = manager; + m_resourceManager->RegisterResourceHolder(this); + } + + void UnBind() wxOVERRIDE + { + if (!IsBound()) + return; + + m_resourceManager->UnregisterResourceHolder(this); + m_resourceManager = NULL; + } + + bool IsBound() wxOVERRIDE + { + return m_resourceManager != NULL; + } + + wxD2DResourceManager* GetManager() wxOVERRIDE + { + return m_resourceManager; + } + +protected: + virtual void DoAcquireResource() = 0; + +private: + wxD2DResourceManager* m_resourceManager; + +protected: + wxCOMPtr m_nativeResource; +}; + +// Used as super class for graphics data objects +// to forward the bindings to their internal resource holder. +class wxD2DManagedGraphicsData : public wxD2DManagedObject +{ +public: + void Bind(wxD2DResourceManager* manager) wxOVERRIDE + { + GetManagedObject()->Bind(manager); + } + + void UnBind() wxOVERRIDE + { + GetManagedObject()->UnBind(); + } + + bool IsBound() wxOVERRIDE + { + return GetManagedObject()->IsBound(); + } + + wxD2DResourceManager* GetManager() wxOVERRIDE + { + return GetManagedObject()->GetManager(); + } + + virtual wxD2DManagedObject* GetManagedObject() = 0; + + ~wxD2DManagedGraphicsData() {}; +}; + +D2D1_CAP_STYLE wxD2DConvertPenCap(wxPenCap cap) +{ + switch (cap) + { + case wxCAP_ROUND: + return D2D1_CAP_STYLE_ROUND; + case wxCAP_PROJECTING: + return D2D1_CAP_STYLE_SQUARE; + case wxCAP_BUTT: + return D2D1_CAP_STYLE_FLAT; + case wxCAP_INVALID: + return D2D1_CAP_STYLE_FLAT; + } + + wxFAIL_MSG("unknown pen cap"); + return D2D1_CAP_STYLE_FLAT; +} + +D2D1_LINE_JOIN wxD2DConvertPenJoin(wxPenJoin join) +{ + switch (join) + { + case wxJOIN_BEVEL: + return D2D1_LINE_JOIN_BEVEL; + case wxJOIN_MITER: + return D2D1_LINE_JOIN_MITER; + case wxJOIN_ROUND: + return D2D1_LINE_JOIN_ROUND; + case wxJOIN_INVALID: + return D2D1_LINE_JOIN_MITER; + } + + wxFAIL_MSG("unknown pen join"); + return D2D1_LINE_JOIN_MITER; +} + +D2D1_DASH_STYLE wxD2DConvertPenStyle(wxPenStyle dashStyle) +{ + switch (dashStyle) + { + case wxPENSTYLE_SOLID: + return D2D1_DASH_STYLE_SOLID; + case wxPENSTYLE_DOT: + return D2D1_DASH_STYLE_DOT; + case wxPENSTYLE_LONG_DASH: + return D2D1_DASH_STYLE_DASH; + case wxPENSTYLE_SHORT_DASH: + return D2D1_DASH_STYLE_DASH; + case wxPENSTYLE_DOT_DASH: + return D2D1_DASH_STYLE_DASH_DOT; + case wxPENSTYLE_USER_DASH: + return D2D1_DASH_STYLE_CUSTOM; + + // NB: These styles cannot be converted to a D2D1_DASH_STYLE + // and must be handled separately. + case wxPENSTYLE_TRANSPARENT: + wxFALLTHROUGH; + case wxPENSTYLE_INVALID: + wxFALLTHROUGH; + case wxPENSTYLE_STIPPLE_MASK_OPAQUE: + wxFALLTHROUGH; + case wxPENSTYLE_STIPPLE_MASK: + wxFALLTHROUGH; + case wxPENSTYLE_STIPPLE: + wxFALLTHROUGH; + case wxPENSTYLE_BDIAGONAL_HATCH: + wxFALLTHROUGH; + case wxPENSTYLE_CROSSDIAG_HATCH: + wxFALLTHROUGH; + case wxPENSTYLE_FDIAGONAL_HATCH: + wxFALLTHROUGH; + case wxPENSTYLE_CROSS_HATCH: + wxFALLTHROUGH; + case wxPENSTYLE_HORIZONTAL_HATCH: + wxFALLTHROUGH; + case wxPENSTYLE_VERTICAL_HATCH: + return D2D1_DASH_STYLE_SOLID; + } + + wxFAIL_MSG("unknown pen style"); + return D2D1_DASH_STYLE_SOLID; +} + +D2D1_COLOR_F wxD2DConvertColour(wxColour colour) +{ + return D2D1::ColorF( + colour.Red() / 255.0f, + colour.Green() / 255.0f, + colour.Blue() / 255.0f, + colour.Alpha() / 255.0f); +} + +D2D1_ANTIALIAS_MODE wxD2DConvertAntialiasMode(wxAntialiasMode antialiasMode) +{ + switch (antialiasMode) + { + case wxANTIALIAS_NONE: + return D2D1_ANTIALIAS_MODE_ALIASED; + case wxANTIALIAS_DEFAULT: + return D2D1_ANTIALIAS_MODE_PER_PRIMITIVE; + } + + wxFAIL_MSG("unknown antialias mode"); + return D2D1_ANTIALIAS_MODE_ALIASED; +} + +#if wxD2D_DEVICE_CONTEXT_SUPPORTED +bool wxD2DCompositionModeSupported(wxCompositionMode compositionMode) +{ + if (compositionMode == wxCOMPOSITION_CLEAR || compositionMode == wxCOMPOSITION_INVALID) + { + return false; + } + + return true; +} + +D2D1_COMPOSITE_MODE wxD2DConvertCompositionMode(wxCompositionMode compositionMode) +{ + switch (compositionMode) + { + case wxCOMPOSITION_SOURCE: + return D2D1_COMPOSITE_MODE_SOURCE_COPY; + case wxCOMPOSITION_OVER: + return D2D1_COMPOSITE_MODE_SOURCE_OVER; + case wxCOMPOSITION_IN: + return D2D1_COMPOSITE_MODE_SOURCE_IN; + case wxCOMPOSITION_OUT: + return D2D1_COMPOSITE_MODE_SOURCE_OUT; + case wxCOMPOSITION_ATOP: + return D2D1_COMPOSITE_MODE_SOURCE_ATOP; + case wxCOMPOSITION_DEST_OVER: + return D2D1_COMPOSITE_MODE_DESTINATION_OVER; + case wxCOMPOSITION_DEST_IN: + return D2D1_COMPOSITE_MODE_DESTINATION_IN; + case wxCOMPOSITION_DEST_OUT: + return D2D1_COMPOSITE_MODE_DESTINATION_OUT; + case wxCOMPOSITION_DEST_ATOP: + return D2D1_COMPOSITE_MODE_DESTINATION_ATOP; + case wxCOMPOSITION_XOR: + return D2D1_COMPOSITE_MODE_XOR; + case wxCOMPOSITION_ADD: + return D2D1_COMPOSITE_MODE_PLUS; + + // unsupported composition modes + case wxCOMPOSITION_DEST: + wxFALLTHROUGH; + case wxCOMPOSITION_CLEAR: + wxFALLTHROUGH; + case wxCOMPOSITION_INVALID: + return D2D1_COMPOSITE_MODE_SOURCE_COPY; + } + + wxFAIL_MSG("unknown composition mode"); + return D2D1_COMPOSITE_MODE_SOURCE_COPY; +} +#endif // wxD2D_DEVICE_CONTEXT_SUPPORTED + +// Direct2D 1.1 introduces a new enum for specifying the interpolation quality +// which is only used with the ID2D1DeviceContext::DrawImage method. +#if wxD2D_DEVICE_CONTEXT_SUPPORTED +D2D1_INTERPOLATION_MODE wxD2DConvertInterpolationMode(wxInterpolationQuality interpolationQuality) +{ + switch (interpolationQuality) + { + case wxINTERPOLATION_DEFAULT: + wxFALLTHROUGH; + case wxINTERPOLATION_NONE: + wxFALLTHROUGH; + case wxINTERPOLATION_FAST: + return D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR; + case wxINTERPOLATION_GOOD: + return D2D1_INTERPOLATION_MODE_LINEAR; + case wxINTERPOLATION_BEST: + return D2D1_INTERPOLATION_MODE_CUBIC; + } + + wxFAIL_MSG("unknown interpolation quality"); + return D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR; +} +#endif // wxD2D_DEVICE_CONTEXT_SUPPORTED + +D2D1_BITMAP_INTERPOLATION_MODE wxD2DConvertBitmapInterpolationMode(wxInterpolationQuality interpolationQuality) +{ + switch (interpolationQuality) + { + case wxINTERPOLATION_DEFAULT: + wxFALLTHROUGH; + case wxINTERPOLATION_NONE: + wxFALLTHROUGH; + case wxINTERPOLATION_FAST: + wxFALLTHROUGH; + case wxINTERPOLATION_GOOD: + return D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR; + case wxINTERPOLATION_BEST: + return D2D1_BITMAP_INTERPOLATION_MODE_LINEAR; + } + + wxFAIL_MSG("unknown interpolation quality"); + return D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR; +} + +D2D1_RECT_F wxD2DConvertRect(const wxRect& rect) +{ + return D2D1::RectF(rect.GetLeft(), rect.GetTop(), rect.GetRight(), rect.GetBottom()); +} + +wxCOMPtr wxD2DConvertRegionToGeometry(ID2D1Factory* direct2dFactory, const wxRegion& region) +{ + wxRegionIterator regionIterator(region); + + // Count the number of rectangles which compose the region + int rectCount = 0; + while(regionIterator++) + rectCount++; + + // Build the array of geometries + ID2D1Geometry** geometries = new ID2D1Geometry*[rectCount]; + regionIterator.Reset(region); + + int i = 0; + while(regionIterator) + { + geometries[i] = NULL; + + wxRect rect = regionIterator.GetRect(); + rect.SetWidth(rect.GetWidth() + 1); + rect.SetHeight(rect.GetHeight() + 1); + + direct2dFactory->CreateRectangleGeometry( + wxD2DConvertRect(rect), + (ID2D1RectangleGeometry**)(&geometries[i])); + + i++; regionIterator++; + } + + // Create a geometry group to hold all the rectangles + wxCOMPtr resultGeometry; + direct2dFactory->CreateGeometryGroup( + D2D1_FILL_MODE_WINDING, + geometries, + rectCount, + &resultGeometry); + + // Cleanup temporaries + for (int i = 0; i < rectCount; ++i) + { + geometries[i]->Release(); + } + + delete[] geometries; + + return wxCOMPtr(resultGeometry); +} + +class wxD2DOffsetHelper +{ +public: + wxD2DOffsetHelper(wxGraphicsContext* g) : m_context(g) + { + if (m_context->ShouldOffset()) + { + m_context->Translate(0.5, 0.5); + } + } + + ~wxD2DOffsetHelper() + { + if (m_context->ShouldOffset()) + { + m_context->Translate(-0.5, -0.5); + } + } + +private: + wxGraphicsContext* m_context; +}; + +bool operator==(const D2D1::Matrix3x2F& lhs, const D2D1::Matrix3x2F& rhs) +{ + return + lhs._11 == rhs._11 && lhs._12 == rhs._12 && + lhs._21 == rhs._21 && lhs._22 == rhs._22 && + lhs._31 == rhs._31 && lhs._32 == rhs._32; +} + +//----------------------------------------------------------------------------- +// wxD2DMatrixData declaration +//----------------------------------------------------------------------------- + +class wxD2DMatrixData : public wxGraphicsMatrixData +{ +public: + wxD2DMatrixData(wxGraphicsRenderer* renderer); + wxD2DMatrixData(wxGraphicsRenderer* renderer, const D2D1::Matrix3x2F& matrix); + + void Concat(const wxGraphicsMatrixData* t) wxOVERRIDE; + + 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) wxOVERRIDE; + + void Get(wxDouble* a = NULL, wxDouble* b = NULL, wxDouble* c = NULL, + wxDouble* d = NULL, wxDouble* tx = NULL, wxDouble* ty = NULL) const wxOVERRIDE; + + void Invert() wxOVERRIDE; + + bool IsEqual(const wxGraphicsMatrixData* t) const wxOVERRIDE; + + bool IsIdentity() const wxOVERRIDE; + + void Translate(wxDouble dx, wxDouble dy) wxOVERRIDE; + + void Scale(wxDouble xScale, wxDouble yScale) wxOVERRIDE; + + void Rotate(wxDouble angle) wxOVERRIDE; + + void TransformPoint(wxDouble* x, wxDouble* y) const wxOVERRIDE; + + void TransformDistance(wxDouble* dx, wxDouble* dy) const wxOVERRIDE; + + void* GetNativeMatrix() const wxOVERRIDE; + + D2D1::Matrix3x2F GetMatrix3x2F() const; + +private: + D2D1::Matrix3x2F m_matrix; +}; + +//----------------------------------------------------------------------------- +// wxD2DMatrixData implementation +//----------------------------------------------------------------------------- + +wxD2DMatrixData::wxD2DMatrixData(wxGraphicsRenderer* renderer) : wxGraphicsMatrixData(renderer) +{ + m_matrix = D2D1::Matrix3x2F::Identity(); +} + +wxD2DMatrixData::wxD2DMatrixData(wxGraphicsRenderer* renderer, const D2D1::Matrix3x2F& matrix) : + wxGraphicsMatrixData(renderer), m_matrix(matrix) +{ +} + +void wxD2DMatrixData::Concat(const wxGraphicsMatrixData* t) +{ + m_matrix.SetProduct(m_matrix, static_cast(t)->m_matrix); +} + +void wxD2DMatrixData::Set(wxDouble a, wxDouble b, wxDouble c, wxDouble d, wxDouble tx, wxDouble ty) +{ + m_matrix._11 = a; + m_matrix._12 = b; + m_matrix._21 = c; + m_matrix._22 = d; + m_matrix._31 = tx; + m_matrix._32 = ty; +} + +void wxD2DMatrixData::Get(wxDouble* a, wxDouble* b, wxDouble* c, wxDouble* d, wxDouble* tx, wxDouble* ty) const +{ + *a = m_matrix._11; + *b = m_matrix._12; + *c = m_matrix._21; + *d = m_matrix._22; + *tx = m_matrix._31; + *ty = m_matrix._32; +} + +void wxD2DMatrixData::Invert() +{ + m_matrix.Invert(); +} + +bool wxD2DMatrixData::IsEqual(const wxGraphicsMatrixData* t) const +{ + return m_matrix == static_cast(t)->m_matrix; +} + +bool wxD2DMatrixData::IsIdentity() const +{ + return m_matrix.IsIdentity(); +} + +void wxD2DMatrixData::Translate(wxDouble dx, wxDouble dy) +{ + m_matrix = D2D1::Matrix3x2F::Translation(dx, dy) * m_matrix; +} + +void wxD2DMatrixData::Scale(wxDouble xScale, wxDouble yScale) +{ + m_matrix = D2D1::Matrix3x2F::Scale(xScale, yScale) * m_matrix; +} + +void wxD2DMatrixData::Rotate(wxDouble angle) +{ + m_matrix = D2D1::Matrix3x2F::Rotation(wxRadToDeg(angle)) * m_matrix; +} + +void wxD2DMatrixData::TransformPoint(wxDouble* x, wxDouble* y) const +{ + D2D1_POINT_2F result = m_matrix.TransformPoint(D2D1::Point2F(*x, *y)); + *x = result.x; + *y = result.y; +} + +void wxD2DMatrixData::TransformDistance(wxDouble* dx, wxDouble* dy) const +{ + D2D1::Matrix3x2F noTranslationMatrix = m_matrix; + noTranslationMatrix._31 = 0; + noTranslationMatrix._32 = 0; + D2D1_POINT_2F result = m_matrix.TransformPoint(D2D1::Point2F(*dx, *dy)); + *dx = result.x; + *dy = result.y; +} + +void* wxD2DMatrixData::GetNativeMatrix() const +{ + return (void*)&m_matrix; +} + +D2D1::Matrix3x2F wxD2DMatrixData::GetMatrix3x2F() const +{ + return m_matrix; +} + +const wxD2DMatrixData* wxGetD2DMatrixData(const wxGraphicsMatrix& matrix) +{ + return static_cast(matrix.GetMatrixData()); +} + +//----------------------------------------------------------------------------- +// wxD2DPathData declaration +//----------------------------------------------------------------------------- + +class wxD2DPathData : public wxGraphicsPathData +{ +public : + + // ID2D1PathGeometry objects are device-independent resources created + // from a ID2D1Factory. This means we can safely create the resource outside + // (the wxD2DRenderer handles this) and store it here since it never gets + // thrown away by the GPU. + wxD2DPathData(wxGraphicsRenderer* renderer, ID2D1Factory* d2dFactory); + + ~wxD2DPathData(); + + ID2D1PathGeometry* GetPathGeometry(); + + // This closes the geometry sink, ensuring all the figures are stored inside + // the ID2D1PathGeometry. Calling this method is required before any draw operation + // involving a path. + void Flush(); + + wxGraphicsObjectRefData* Clone() const wxOVERRIDE; + + // begins a new subpath at (x,y) + void MoveToPoint(wxDouble x, wxDouble y) wxOVERRIDE; + + // adds a straight line from the current point to (x,y) + void AddLineToPoint(wxDouble x, wxDouble y) wxOVERRIDE; + + // adds a cubic Bezier curve from the current point, using two control points and an end point + void AddCurveToPoint(wxDouble cx1, wxDouble cy1, wxDouble cx2, wxDouble cy2, wxDouble x, wxDouble y) wxOVERRIDE; + + // adds an arc of a circle centering at (x,y) with radius (r) from startAngle to endAngle + void AddArc(wxDouble x, wxDouble y, wxDouble r, wxDouble startAngle, wxDouble endAngle, bool clockwise) wxOVERRIDE; + + // gets the last point of the current path, (0,0) if not yet set + void GetCurrentPoint(wxDouble* x, wxDouble* y) const wxOVERRIDE; + + // adds another path + void AddPath(const wxGraphicsPathData* path) wxOVERRIDE; + + // closes the current sub-path + void CloseSubpath() wxOVERRIDE; + + // returns the native path + void* GetNativePath() const wxOVERRIDE; + + // give the native path returned by GetNativePath() back (there might be some deallocations necessary) + void UnGetNativePath(void* WXUNUSED(p)) const wxOVERRIDE {}; + + // transforms each point of this path by the matrix + void Transform(const wxGraphicsMatrixData* matrix) wxOVERRIDE; + + // gets the bounding box enclosing all points (possibly including control points) + void GetBox(wxDouble* x, wxDouble* y, wxDouble* w, wxDouble *h) const wxOVERRIDE; + + bool Contains(wxDouble x, wxDouble y, wxPolygonFillMode fillStyle = wxODDEVEN_RULE) const wxOVERRIDE; + + // appends an ellipsis as a new closed subpath fitting the passed rectangle + void AddCircle(wxDouble x, wxDouble y, wxDouble r) wxOVERRIDE; + + // appends an ellipse + void AddEllipse(wxDouble x, wxDouble y, wxDouble w, wxDouble h) wxOVERRIDE; + +private: + void EnsureGeometryOpen(); + + void EnsureSinkOpen(); + + void EnsureFigureOpen(wxDouble x = 0, wxDouble y = 0); + +private : + wxCOMPtr m_pathGeometry; + + wxCOMPtr m_geometrySink; + + wxCOMPtr m_direct2dfactory; + + mutable wxCOMPtr m_transformedGeometry; + + D2D1_POINT_2F m_currentPoint; + + D2D1_MATRIX_3X2_F m_transformMatrix; + + bool m_figureOpened; + + bool m_geometryWritable; +}; + +//----------------------------------------------------------------------------- +// wxD2DPathData implementation +//----------------------------------------------------------------------------- + +wxD2DPathData::wxD2DPathData(wxGraphicsRenderer* renderer, ID2D1Factory* d2dFactory) : + wxGraphicsPathData(renderer), m_direct2dfactory(d2dFactory), + m_figureOpened(false), m_geometryWritable(false), + m_transformMatrix(D2D1::Matrix3x2F::Identity()) +{ + m_direct2dfactory->CreatePathGeometry(&m_pathGeometry); +} + +wxD2DPathData::~wxD2DPathData() +{ + Flush(); +} + +ID2D1PathGeometry* wxD2DPathData::GetPathGeometry() +{ + return m_pathGeometry; +} + +wxD2DPathData::wxGraphicsObjectRefData* wxD2DPathData::Clone() const +{ + wxD2DPathData* newPathData = new wxD2DPathData(GetRenderer(), m_direct2dfactory); + + newPathData->EnsureGeometryOpen(); + m_pathGeometry->Stream(newPathData->m_geometrySink); + + return newPathData; +} + +void wxD2DPathData::Flush() +{ + if (m_geometrySink != NULL) + { + if (m_figureOpened) + { + m_geometrySink->EndFigure(D2D1_FIGURE_END_OPEN); + } + + m_figureOpened = false; + m_geometrySink->Close(); + + m_geometryWritable = false; + } +} + +void wxD2DPathData::EnsureGeometryOpen() +{ + if (!m_geometryWritable) { + wxCOMPtr newPathGeometry; + m_direct2dfactory->CreatePathGeometry(&newPathGeometry); + + m_geometrySink.reset(); + newPathGeometry->Open(&m_geometrySink); + + if (m_pathGeometry != NULL) + { + m_pathGeometry->Stream(m_geometrySink); + } + + m_pathGeometry = newPathGeometry; + m_geometryWritable = true; + } +} + +void wxD2DPathData::EnsureSinkOpen() +{ + EnsureGeometryOpen(); + + if (m_geometrySink == NULL) + { + m_geometrySink = NULL; + m_pathGeometry->Open(&m_geometrySink); + } +} + +void wxD2DPathData::EnsureFigureOpen(wxDouble x, wxDouble y) +{ + EnsureSinkOpen(); + + if (!m_figureOpened) + { + m_geometrySink->BeginFigure(D2D1::Point2F(x, y), D2D1_FIGURE_BEGIN_FILLED); + m_figureOpened = true; + m_currentPoint = D2D1::Point2F(x, y); + } +} + +void wxD2DPathData::MoveToPoint(wxDouble x, wxDouble y) +{ + if (m_figureOpened) + { + CloseSubpath(); + } + + EnsureFigureOpen(x, y); + + m_currentPoint = D2D1::Point2F(x, y); +} + +// adds a straight line from the current point to (x,y) +void wxD2DPathData::AddLineToPoint(wxDouble x, wxDouble y) +{ + EnsureFigureOpen(); + m_geometrySink->AddLine(D2D1::Point2F(x, y)); + + m_currentPoint = D2D1::Point2F(x, y); +} + +// adds a cubic Bezier curve from the current point, using two control points and an end point +void wxD2DPathData::AddCurveToPoint(wxDouble cx1, wxDouble cy1, wxDouble cx2, wxDouble cy2, wxDouble x, wxDouble y) +{ + EnsureFigureOpen(); + D2D1_BEZIER_SEGMENT bezierSegment = { + {cx1, cy1}, + {cx2, cy2}, + {x, y}}; + m_geometrySink->AddBezier(bezierSegment); + + m_currentPoint = D2D1::Point2F(x, y); +} + +// adds an arc of a circle centering at (x,y) with radius (r) from startAngle to endAngle +void wxD2DPathData::AddArc(wxDouble x, wxDouble y, wxDouble r, wxDouble startAngle, wxDouble endAngle, bool clockwise) +{ + static wxDouble PI = std::atan(1) * 4; + wxPoint2DDouble center = wxPoint2DDouble(x, y); + wxPoint2DDouble start = wxPoint2DDouble(std::cos(startAngle) * r, std::sin(startAngle) * r); + wxPoint2DDouble end = wxPoint2DDouble(std::cos(endAngle) * r, std::sin(endAngle) * r); + + if (m_figureOpened) + { + AddLineToPoint(start.m_x + x, start.m_y + y); + } + else + { + MoveToPoint(start.m_x + x, start.m_y + y); + } + + double angle = (end.GetVectorAngle() - start.GetVectorAngle()); + + if (!clockwise) + { + angle = 360 - angle; + } + + while (abs(angle) > 360) + { + angle -= (angle / abs(angle)) * 360; + } + + if (angle == 360) + { + AddCircle(center.m_x, center.m_y, start.GetVectorLength()); + return; + } + + D2D1_SWEEP_DIRECTION sweepDirection = clockwise ? D2D1_SWEEP_DIRECTION_CLOCKWISE : D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE; + + D2D1_ARC_SIZE arcSize = angle > 180 ? D2D1_ARC_SIZE_LARGE : D2D1_ARC_SIZE_SMALL; + + D2D1_ARC_SEGMENT arcSegment = { + {end.m_x + x, end.m_y + y}, // end point + {r, r}, // size + 0, // rotation + sweepDirection, // sweep direction + arcSize // arc size + }; + + m_geometrySink->AddArc(arcSegment); + + m_currentPoint = D2D1::Point2F(end.m_x + x, end.m_y + y); +} + +// appends an ellipsis as a new closed subpath fitting the passed rectangle +void wxD2DPathData::AddCircle(wxDouble x, wxDouble y, wxDouble r) +{ + AddEllipse(x - r, y - r, r * 2, r * 2); +} + +// appends an ellipse +void wxD2DPathData::AddEllipse(wxDouble x, wxDouble y, wxDouble w, wxDouble h) +{ + Flush(); + + wxCOMPtr ellipseGeometry; + wxCOMPtr newPathGeometry; + + D2D1_ELLIPSE ellipse = { {x + w / 2, y + h / 2}, w / 2, h / 2 }; + + m_direct2dfactory->CreateEllipseGeometry(ellipse, &ellipseGeometry); + + m_direct2dfactory->CreatePathGeometry(&newPathGeometry); + + m_geometrySink = NULL; + newPathGeometry->Open(&m_geometrySink); + + m_geometryWritable = true; + + ellipseGeometry->CombineWithGeometry(m_pathGeometry, D2D1_COMBINE_MODE_UNION, NULL, m_geometrySink); + + m_pathGeometry = newPathGeometry; +} + +// gets the last point of the current path, (0,0) if not yet set +void wxD2DPathData::GetCurrentPoint(wxDouble* x, wxDouble* y) const +{ + D2D1_POINT_2F transformedPoint = D2D1::Matrix3x2F::ReinterpretBaseType(&m_transformMatrix)->TransformPoint(m_currentPoint); + + if (x != NULL) *x = transformedPoint.x; + if (y != NULL) *y = transformedPoint.y; +} + +// adds another path +void wxD2DPathData::AddPath(const wxGraphicsPathData* path) +{ + const wxD2DPathData* d2dPath = static_cast(path); + + EnsureFigureOpen(); + + d2dPath->m_pathGeometry->Stream(m_geometrySink); +} + +// closes the current sub-path +void wxD2DPathData::CloseSubpath() +{ + if (m_figureOpened) + { + m_geometrySink->EndFigure(D2D1_FIGURE_END_CLOSED); + m_figureOpened = false; + } +} + +void* wxD2DPathData::GetNativePath() const +{ + m_transformedGeometry.reset(); + m_direct2dfactory->CreateTransformedGeometry(m_pathGeometry, m_transformMatrix, &m_transformedGeometry); + return m_transformedGeometry; +} + +void wxD2DPathData::Transform(const wxGraphicsMatrixData* matrix) +{ + m_transformMatrix = *((D2D1_MATRIX_3X2_F*)(matrix->GetNativeMatrix())); +} + +void wxD2DPathData::GetBox(wxDouble* x, wxDouble* y, wxDouble* w, wxDouble *h) const +{ + D2D1_RECT_F bounds; + m_pathGeometry->GetBounds(D2D1::Matrix3x2F::Identity(), &bounds); + if (x != NULL) *x = bounds.left; + if (y != NULL) *y = bounds.top; + if (w != NULL) *w = bounds.right - bounds.left; + if (h != NULL) *h = bounds.bottom - bounds.top; +} + +bool wxD2DPathData::Contains(wxDouble x, wxDouble y, wxPolygonFillMode WXUNUSED(fillStyle)) const +{ + BOOL result; + m_pathGeometry->FillContainsPoint(D2D1::Point2F(x, y), D2D1::Matrix3x2F::Identity(), &result); + return result != 0; +} + +wxD2DPathData* wxGetD2DPathData(const wxGraphicsPath& path) +{ + return static_cast(path.GetGraphicsData()); +} + +// This utility class is used to read a color value with the format +// PBGRA from a byte stream and to write a color back to the stream. +// It's used in conjunction with the IWICBitmapSource or IWICBitmap +// pixel data to easily read and write color values. +struct wxPBGRAColor +{ + wxPBGRAColor(BYTE* stream) : + b(*stream), g(*(stream + 1)), r(*(stream + 2)), a(*(stream + 3)) + {} + + wxPBGRAColor(const wxColor& color) : + a(color.Alpha()), r(color.Red()), g(color.Green()), b(color.Blue()) + {} + + bool IsBlack() const { return r == 0 && g == 0 && b == 0; } + + void Write(BYTE* stream) const + { + *(stream + 0) = b; + *(stream + 1) = g; + *(stream + 2) = r; + *(stream + 3) = a; + } + + BYTE b, g, r, a; +}; + +wxCOMPtr wxCreateWICBitmap(const WXHBITMAP sourceBitmap, bool hasAlpha = false) +{ + HRESULT hr; + + wxCOMPtr wicBitmap; + hr = wxWICImagingFactory()->CreateBitmapFromHBITMAP(sourceBitmap, NULL, WICBitmapUseAlpha, &wicBitmap); + wxCHECK2_HRESULT_RET(hr, wxCOMPtr(NULL)); + + wxCOMPtr converter; + hr = wxWICImagingFactory()->CreateFormatConverter(&converter); + wxCHECK2_HRESULT_RET(hr, wxCOMPtr(NULL)); + + WICPixelFormatGUID pixelFormat = hasAlpha ? GUID_WICPixelFormat32bppPBGRA : GUID_WICPixelFormat32bppBGR; + + hr = converter->Initialize( + wicBitmap, + pixelFormat, + WICBitmapDitherTypeNone, NULL, 0.f, + WICBitmapPaletteTypeMedianCut); + wxCHECK2_HRESULT_RET(hr, wxCOMPtr(NULL)); + + return wxCOMPtr(converter); +} + +wxCOMPtr wxCreateWICBitmap(const wxBitmap& sourceBitmap, bool hasAlpha = false) +{ + return wxCreateWICBitmap(sourceBitmap.GetHBITMAP(), hasAlpha); +} + +// WIC Bitmap Source for creating hatch patterned bitmaps +class wxHatchBitmapSource : public IWICBitmapSource +{ +public: + wxHatchBitmapSource(wxBrushStyle brushStyle, const wxColor& color) : + m_brushStyle(brushStyle), m_color(color), m_refCount(0l) + { + } + + HRESULT STDMETHODCALLTYPE GetSize(__RPC__out UINT *width, __RPC__out UINT *height) wxOVERRIDE + { + if (width != NULL) *width = 8; + if (height != NULL) *height = 8; + return S_OK; + } + + HRESULT STDMETHODCALLTYPE GetPixelFormat(__RPC__out WICPixelFormatGUID *pixelFormat) wxOVERRIDE + { + if (pixelFormat != NULL) *pixelFormat = GUID_WICPixelFormat32bppPBGRA; + return S_OK; + } + + HRESULT STDMETHODCALLTYPE GetResolution(__RPC__out double *dpiX, __RPC__out double *dpiY) wxOVERRIDE + { + if (dpiX != NULL) *dpiX = 96.0; + if (dpiY != NULL) *dpiY = 96.0; + return S_OK; + } + + HRESULT STDMETHODCALLTYPE CopyPalette(__RPC__in_opt IWICPalette *palette) wxOVERRIDE + { + palette = NULL; + return S_OK; + } + + HRESULT STDMETHODCALLTYPE CopyPixels( + const WICRect* WXUNUSED(prc), + UINT WXUNUSED(stride), + UINT WXUNUSED(bufferSize), + BYTE *buffer) wxOVERRIDE + { + // patterns are encoded in a bit map of size 8 x 8 + static const unsigned char BDIAGONAL_PATTERN[8] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; + static const unsigned char FDIAGONAL_PATTERN[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; + static const unsigned char CROSSDIAG_PATTERN[8] = { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 }; + static const unsigned char CROSS_PATTERN[8] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFF }; + static const unsigned char HORIZONTAL_PATTERN[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF }; + static const unsigned char VERTICAL_PATTERN[8] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; + + switch (m_brushStyle) + { + case wxBRUSHSTYLE_BDIAGONAL_HATCH: + CopyPattern(buffer, BDIAGONAL_PATTERN); + break; + case wxBRUSHSTYLE_CROSSDIAG_HATCH: + CopyPattern(buffer, CROSSDIAG_PATTERN); + break; + case wxBRUSHSTYLE_FDIAGONAL_HATCH: + CopyPattern(buffer, FDIAGONAL_PATTERN); + break; + case wxBRUSHSTYLE_CROSS_HATCH: + CopyPattern(buffer, CROSS_PATTERN); + break; + case wxBRUSHSTYLE_HORIZONTAL_HATCH: + CopyPattern(buffer, HORIZONTAL_PATTERN); + break; + case wxBRUSHSTYLE_VERTICAL_HATCH: + CopyPattern(buffer, VERTICAL_PATTERN); + break; + } + + return S_OK; + } + + // Implementations adapted from: "Implementing IUnknown in C++" + // http://msdn.microsoft.com/en-us/library/office/cc839627%28v=office.15%29.aspx + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID referenceId, void** object) wxOVERRIDE + { + if (!object) + { + return E_INVALIDARG; + } + + *object = NULL; + + if (referenceId == IID_IUnknown || referenceId == wxIID_IWICBitmapSource) + { + *object = (LPVOID)this; + AddRef(); + return NOERROR; + } + + return E_NOINTERFACE; + } + + ULONG STDMETHODCALLTYPE AddRef(void) wxOVERRIDE + { + InterlockedIncrement(&m_refCount); + return m_refCount; + } + + ULONG STDMETHODCALLTYPE Release(void) wxOVERRIDE + { + wxCHECK2_MSG(m_refCount > 0, 0, "Unbalanced number of calls to Release"); + + ULONG refCount = InterlockedDecrement(&m_refCount); + if (m_refCount == 0) + { + delete this; + } + return refCount; + } + +private: + + // Copies an 8x8 bit pattern to a PBGRA byte buffer + void CopyPattern(BYTE* buffer, const unsigned char* pattern) const + { + static const wxPBGRAColor transparent(wxTransparentColour); + + int k = 0; + + for (int i = 0; i < 8; ++i) + { + for (int j = 7; j >= 0; --j) + { + bool isColorBit = (pattern[i] & (1 << j)) > 0; + (isColorBit ? m_color : transparent).Write(buffer + k); + k += 4; + } + } + } + +private: + // The hatch style produced by this bitmap source + const wxBrushStyle m_brushStyle; + + // The colour of the hatch + const wxPBGRAColor m_color; + + // Internally used to implement IUnknown's reference counting + ULONG m_refCount; +}; + +// RAII class hosting a WIC bitmap lock used for writing +// pixel data to a WICBitmap +class wxBitmapPixelWriteLock +{ +public: + wxBitmapPixelWriteLock(IWICBitmap* bitmap) + { + // Retrieve the size of the bitmap + UINT w, h; + bitmap->GetSize(&w, &h); + WICRect lockSize = {0, 0, w, h}; + + // Obtain a bitmap lock for exclusive write + bitmap->Lock(&lockSize, WICBitmapLockWrite, &m_pixelLock); + } + + IWICBitmapLock* GetLock() { return m_pixelLock; } + +private: + wxCOMPtr m_pixelLock; +}; + +class wxD2DBitmapResourceHolder : public wxD2DResourceHolder +{ +public: + wxD2DBitmapResourceHolder(const wxBitmap& sourceBitmap) : + m_sourceBitmap(sourceBitmap) + { + } + + const wxBitmap& GetSourceBitmap() const { return m_sourceBitmap; } + +protected: + void DoAcquireResource() wxOVERRIDE + { + ID2D1RenderTarget* renderTarget = GetContext(); + + HRESULT hr; + + if(m_sourceBitmap.GetMask()) + { + int w = m_sourceBitmap.GetWidth(); + int h = m_sourceBitmap.GetHeight(); + + wxCOMPtr colorBitmap = wxCreateWICBitmap(m_sourceBitmap); + wxCOMPtr maskBitmap = wxCreateWICBitmap(m_sourceBitmap.GetMask()->GetMaskBitmap()); + wxCOMPtr resultBitmap; + + wxWICImagingFactory()->CreateBitmap( + w, h, + GUID_WICPixelFormat32bppPBGRA, + WICBitmapCreateCacheOption::WICBitmapCacheOnLoad, + &resultBitmap); + + BYTE* colorBuffer = new BYTE[4 * w * h]; + BYTE* maskBuffer = new BYTE[4 * w * h]; + BYTE* resultBuffer; + + hr = colorBitmap->CopyPixels(NULL, w * 4, 4 * w * h, colorBuffer); + hr = maskBitmap->CopyPixels(NULL, w * 4, 4 * w * h, maskBuffer); + + { + wxBitmapPixelWriteLock lock(resultBitmap); + + UINT bufferSize = 0; + hr = lock.GetLock()->GetDataPointer(&bufferSize, &resultBuffer); + + static const wxPBGRAColor transparentColor(wxTransparentColour); + + // Create the result bitmap + for (int i = 0; i < w * h * 4; i += 4) + { + wxPBGRAColor color(colorBuffer + i); + wxPBGRAColor mask(maskBuffer + i); + + if (mask.IsBlack()) + { + transparentColor.Write(resultBuffer + i); + } + else + { + color.a = 255; + color.Write(resultBuffer + i); + } + } + } + + hr = renderTarget->CreateBitmapFromWicBitmap(resultBitmap, 0, &m_nativeResource); + wxCHECK_HRESULT_RET(hr); + + delete[] colorBuffer; + delete[] maskBuffer; + } + else + { + wxCOMPtr bitmapSource = wxCreateWICBitmap(m_sourceBitmap, m_sourceBitmap.HasAlpha()); + hr = renderTarget->CreateBitmapFromWicBitmap(bitmapSource, 0, &m_nativeResource); + } + } + +private: + const wxBitmap m_sourceBitmap; +}; + +//----------------------------------------------------------------------------- +// wxD2DBitmapData declaration +//----------------------------------------------------------------------------- + +class wxD2DBitmapData : public wxGraphicsBitmapData, public wxD2DManagedGraphicsData +{ +public: + typedef wxD2DBitmapResourceHolder NativeType; + + wxD2DBitmapData(wxGraphicsRenderer* renderer, const wxBitmap& bitmap) : + wxGraphicsBitmapData(renderer), m_bitmapHolder(bitmap) {} + + wxD2DBitmapData(wxGraphicsRenderer* renderer, const void* pseudoNativeBitmap) : + wxGraphicsBitmapData(renderer), m_bitmapHolder(*static_cast(pseudoNativeBitmap)) {}; + + // returns the native representation + void* GetNativeBitmap() const wxOVERRIDE; + + wxCOMPtr GetD2DBitmap(); + + wxD2DManagedObject* GetManagedObject() wxOVERRIDE + { + return &m_bitmapHolder; + } + +private: + NativeType m_bitmapHolder; +}; + +//----------------------------------------------------------------------------- +// wxD2DBitmapData implementation +//----------------------------------------------------------------------------- + +void* wxD2DBitmapData::GetNativeBitmap() const +{ + return (void*)&m_bitmapHolder; +} + +wxCOMPtr wxD2DBitmapData::GetD2DBitmap() +{ + return m_bitmapHolder.GetD2DResource(); +} + +wxD2DBitmapData* wxGetD2DBitmapData(const wxGraphicsBitmap& bitmap) +{ + return static_cast(bitmap.GetRefData()); +} + +// Helper class used to create and safely release a ID2D1GradientStopCollection from wxGraphicsGradientStops +class wxD2DGradientStopsHelper +{ +public: + wxD2DGradientStopsHelper(const wxGraphicsGradientStops& gradientStops, ID2D1RenderTarget* renderTarget) + { + int stopCount = gradientStops.GetCount(); + + D2D1_GRADIENT_STOP* gradientStopArray = new D2D1_GRADIENT_STOP[stopCount]; + + for (int i = 0; i < stopCount; ++i) + { + gradientStopArray[i].color = wxD2DConvertColour(gradientStops.Item(i).GetColour()); + gradientStopArray[i].position = gradientStops.Item(i).GetPosition(); + } + + renderTarget->CreateGradientStopCollection(gradientStopArray, stopCount, D2D1_GAMMA_2_2, D2D1_EXTEND_MODE_CLAMP, &m_gradientStopCollection); + + delete[] gradientStopArray; + } + + ID2D1GradientStopCollection* GetGradientStopCollection() + { + return m_gradientStopCollection; + } + +private: + wxCOMPtr m_gradientStopCollection; +}; + +template +class wxD2DBrushResourceHolder : public wxD2DResourceHolder +{ +public: + wxD2DBrushResourceHolder(const wxBrush& brush) : m_sourceBrush(brush) {}; + virtual ~wxD2DBrushResourceHolder() {}; +protected: + const wxBrush m_sourceBrush; +}; + +class wxD2DSolidBrushResourceHolder : public wxD2DBrushResourceHolder +{ +public: + wxD2DSolidBrushResourceHolder(const wxBrush& brush) : wxD2DBrushResourceHolder(brush) {} + +protected: + void DoAcquireResource() wxOVERRIDE + { + wxColour colour = m_sourceBrush.GetColour(); + HRESULT hr = GetContext()->CreateSolidColorBrush(wxD2DConvertColour(colour), &m_nativeResource); + wxCHECK_HRESULT_RET(hr); + } +}; + +class wxD2DBitmapBrushResourceHolder : public wxD2DBrushResourceHolder +{ +public: + wxD2DBitmapBrushResourceHolder(const wxBrush& brush) : wxD2DBrushResourceHolder(brush) {} + +protected: + void DoAcquireResource() wxOVERRIDE + { + // TODO: cache this bitmap + wxD2DBitmapResourceHolder bitmap(*(m_sourceBrush.GetStipple())); + bitmap.Bind(GetManager()); + + HRESULT result = GetContext()->CreateBitmapBrush( + bitmap.GetD2DResource(), + D2D1::BitmapBrushProperties( + D2D1_EXTEND_MODE_WRAP, + D2D1_EXTEND_MODE_WRAP, + D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR), + &m_nativeResource); + + wxCHECK_HRESULT_RET(result); + } +}; + +class wxD2DHatchBrushResourceHolder : public wxD2DBrushResourceHolder +{ +public: + wxD2DHatchBrushResourceHolder(const wxBrush& brush) : wxD2DBrushResourceHolder(brush) {} + +protected: + void DoAcquireResource() wxOVERRIDE + { + wxCOMPtr hatchBitmapSource(new wxHatchBitmapSource(m_sourceBrush.GetStyle(), m_sourceBrush.GetColour())); + + wxCOMPtr bitmap; + + HRESULT hr = GetContext()->CreateBitmapFromWicBitmap(hatchBitmapSource, &bitmap); + wxCHECK_HRESULT_RET(hr); + + hr = GetContext()->CreateBitmapBrush( + bitmap, + D2D1::BitmapBrushProperties( + D2D1_EXTEND_MODE_WRAP, + D2D1_EXTEND_MODE_WRAP, + D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR), + &m_nativeResource); + wxCHECK_HRESULT_RET(hr); + } +}; + +class wxD2DLinearGradientBrushResourceHolder : public wxD2DResourceHolder +{ +public: + struct LinearGradientInfo { + const wxRect direction; + const wxGraphicsGradientStops stops; + LinearGradientInfo(wxDouble& x1, wxDouble& y1, wxDouble& x2, wxDouble& y2, const wxGraphicsGradientStops& stops) + : direction(x1, y1, x2, y2), stops(stops) {} + }; + + wxD2DLinearGradientBrushResourceHolder(wxDouble& x1, wxDouble& y1, wxDouble& x2, wxDouble& y2, const wxGraphicsGradientStops& stops) + : m_linearGradientInfo(x1, y1, x2, y2, stops) {} + +protected: + void DoAcquireResource() wxOVERRIDE + { + wxD2DGradientStopsHelper helper(m_linearGradientInfo.stops, GetContext()); + + HRESULT hr = GetContext()->CreateLinearGradientBrush( + D2D1::LinearGradientBrushProperties( + D2D1::Point2F(m_linearGradientInfo.direction.GetX(), m_linearGradientInfo.direction.GetY()), + D2D1::Point2F(m_linearGradientInfo.direction.GetWidth(), m_linearGradientInfo.direction.GetHeight())), + helper.GetGradientStopCollection(), + &m_nativeResource); + wxCHECK_HRESULT_RET(hr); + } +private: + const LinearGradientInfo m_linearGradientInfo; +}; + +class wxD2DRadialGradientBrushResourceHolder : public wxD2DResourceHolder +{ +public: + struct RadialGradientInfo { + const wxRect direction; + const wxDouble radius; + const wxGraphicsGradientStops stops; + + RadialGradientInfo(wxDouble x1, wxDouble y1, wxDouble x2, wxDouble y2, wxDouble r, const wxGraphicsGradientStops& stops) + : direction(x1, y1, x2, y2), radius(r), stops(stops) {} + }; + + wxD2DRadialGradientBrushResourceHolder(wxDouble& x1, wxDouble& y1, wxDouble& x2, wxDouble& y2, wxDouble& r, const wxGraphicsGradientStops& stops) + : m_radialGradientInfo(x1, y1, x2, y2, r, stops) {} + +protected: + void DoAcquireResource() wxOVERRIDE + { + wxD2DGradientStopsHelper helper(m_radialGradientInfo.stops, GetContext()); + + int xo = m_radialGradientInfo.direction.GetLeft() - m_radialGradientInfo.direction.GetWidth(); + int yo = m_radialGradientInfo.direction.GetTop() - m_radialGradientInfo.direction.GetHeight(); + + HRESULT hr = GetContext()->CreateRadialGradientBrush( + D2D1::RadialGradientBrushProperties( + D2D1::Point2F(m_radialGradientInfo.direction.GetLeft(), m_radialGradientInfo.direction.GetTop()), + D2D1::Point2F(xo, yo), + m_radialGradientInfo.radius, m_radialGradientInfo.radius), + helper.GetGradientStopCollection(), + &m_nativeResource); + wxCHECK_HRESULT_RET(hr); + } + +private: + const RadialGradientInfo m_radialGradientInfo; +}; + +//----------------------------------------------------------------------------- +// wxD2DBrushData declaration +//----------------------------------------------------------------------------- + +class wxD2DBrushData : public wxGraphicsObjectRefData, public wxD2DManagedGraphicsData +{ +public: + wxD2DBrushData(wxGraphicsRenderer* renderer, const wxBrush brush); + + wxD2DBrushData(wxGraphicsRenderer* renderer); + + 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); + + ID2D1Brush* GetBrush() const + { + return (ID2D1Brush*)(m_brushResourceHolder->GetResource()); + } + + wxD2DManagedObject* GetManagedObject() wxOVERRIDE + { + return m_brushResourceHolder.get(); + } + +private: + wxSharedPtr m_brushResourceHolder; +}; + +//----------------------------------------------------------------------------- +// wxD2DBrushData implementation +//----------------------------------------------------------------------------- + +wxD2DBrushData::wxD2DBrushData(wxGraphicsRenderer* renderer, const wxBrush brush) + : wxGraphicsObjectRefData(renderer), m_brushResourceHolder(NULL) +{ + if (brush.GetStyle() == wxBRUSHSTYLE_SOLID) + { + m_brushResourceHolder = new wxD2DSolidBrushResourceHolder(brush); + } + else if (brush.IsHatch()) + { + m_brushResourceHolder = new wxD2DHatchBrushResourceHolder(brush); + } + else + { + m_brushResourceHolder = new wxD2DBitmapBrushResourceHolder(brush); + } +} + +wxD2DBrushData::wxD2DBrushData(wxGraphicsRenderer* renderer) + : wxGraphicsObjectRefData(renderer), m_brushResourceHolder(NULL) +{ +} + +void wxD2DBrushData::CreateLinearGradientBrush( + wxDouble x1, wxDouble y1, + wxDouble x2, wxDouble y2, + const wxGraphicsGradientStops& stops) +{ + m_brushResourceHolder = new wxD2DLinearGradientBrushResourceHolder(x1, y1, x2, y2, stops); +} + +void wxD2DBrushData::CreateRadialGradientBrush( + wxDouble xo, wxDouble yo, + wxDouble xc, wxDouble yc, + wxDouble radius, + const wxGraphicsGradientStops& stops) +{ + m_brushResourceHolder = new wxD2DRadialGradientBrushResourceHolder(xo, yo, xc, yc, radius, stops); +} + +wxD2DBrushData* wxGetD2DBrushData(const wxGraphicsBrush& brush) +{ + return static_cast(brush.GetGraphicsData()); +} + +bool wxIsHatchPenStyle(wxPenStyle penStyle) +{ + return penStyle >= wxPENSTYLE_FIRST_HATCH && penStyle <= wxPENSTYLE_LAST_HATCH; +} + +wxBrushStyle wxConvertPenStyleToBrushStyle(wxPenStyle penStyle) +{ + switch(penStyle) + { + case wxPENSTYLE_BDIAGONAL_HATCH: + return wxBRUSHSTYLE_BDIAGONAL_HATCH; + case wxPENSTYLE_CROSSDIAG_HATCH: + return wxBRUSHSTYLE_CROSSDIAG_HATCH; + case wxPENSTYLE_FDIAGONAL_HATCH: + return wxBRUSHSTYLE_FDIAGONAL_HATCH; + case wxPENSTYLE_CROSS_HATCH: + return wxBRUSHSTYLE_CROSS_HATCH; + case wxPENSTYLE_HORIZONTAL_HATCH: + return wxBRUSHSTYLE_HORIZONTAL_HATCH; + case wxPENSTYLE_VERTICAL_HATCH: + return wxBRUSHSTYLE_VERTICAL_HATCH; + } + + return wxBRUSHSTYLE_SOLID; +} + +//----------------------------------------------------------------------------- +// wxD2DPenData declaration +//----------------------------------------------------------------------------- + +class wxD2DPenData : public wxGraphicsObjectRefData, public wxD2DManagedGraphicsData +{ +public: + wxD2DPenData(wxGraphicsRenderer* renderer, ID2D1Factory* direct2dFactory, const wxPen& pen); + + void CreateStrokeStyle(ID2D1Factory* const direct2dfactory); + + ID2D1Brush* GetBrush(); + + FLOAT GetWidth(); + + ID2D1StrokeStyle* GetStrokeStyle(); + + wxD2DManagedObject* GetManagedObject() wxOVERRIDE + { + return m_stippleBrush->GetManagedObject(); + } + +private: + // We store the source pen for later when we need to recreate the + // device-dependent resources. + const wxPen m_sourcePen; + + // A stroke style is a device-independent resource. + // Describes the caps, miter limit, line join, and dash information. + wxCOMPtr m_strokeStyle; + + // Drawing outlines with Direct2D requires a brush for the color or stipple. + wxSharedPtr m_stippleBrush; + + // The width of the stroke + FLOAT m_width; +}; + +//----------------------------------------------------------------------------- +// wxD2DPenData implementation +//----------------------------------------------------------------------------- + +wxD2DPenData::wxD2DPenData( + wxGraphicsRenderer* renderer, + ID2D1Factory* direct2dFactory, + const wxPen& pen) + : wxGraphicsObjectRefData(renderer), m_sourcePen(pen), m_width(pen.GetWidth()) +{ + CreateStrokeStyle(direct2dFactory); + + wxBrush strokeBrush; + + if (m_sourcePen.GetStyle() == wxPENSTYLE_STIPPLE) + { + strokeBrush.SetStipple(*(m_sourcePen.GetStipple())); + strokeBrush.SetStyle(wxBRUSHSTYLE_STIPPLE); + } + else if(wxIsHatchPenStyle(m_sourcePen.GetStyle())) + { + strokeBrush.SetStyle(wxConvertPenStyleToBrushStyle(m_sourcePen.GetStyle())); + strokeBrush.SetColour(m_sourcePen.GetColour()); + } + else + { + strokeBrush.SetColour(m_sourcePen.GetColour()); + strokeBrush.SetStyle(wxBRUSHSTYLE_SOLID); + } + + m_stippleBrush = new wxD2DBrushData(renderer, strokeBrush); +} + +void wxD2DPenData::CreateStrokeStyle(ID2D1Factory* const direct2dfactory) +{ + D2D1_CAP_STYLE capStyle = wxD2DConvertPenCap(m_sourcePen.GetCap()); + D2D1_LINE_JOIN lineJoin = wxD2DConvertPenJoin(m_sourcePen.GetJoin()); + D2D1_DASH_STYLE dashStyle = wxD2DConvertPenStyle(m_sourcePen.GetStyle()); + + int dashCount = 0; + FLOAT* dashes = NULL; + + if (dashStyle == D2D1_DASH_STYLE_CUSTOM) + { + dashCount = m_sourcePen.GetDashCount(); + dashes = new FLOAT[dashCount]; + + for (int i = 0; i < dashCount; ++i) + { + dashes[i] = m_sourcePen.GetDash()[i]; + } + + } + + direct2dfactory->CreateStrokeStyle( + D2D1::StrokeStyleProperties(capStyle, capStyle, capStyle, lineJoin, 0, dashStyle, 0.0f), + dashes, dashCount, + &m_strokeStyle); + + delete[] dashes; +} + +ID2D1Brush* wxD2DPenData::GetBrush() +{ + return m_stippleBrush->GetBrush(); +} + +FLOAT wxD2DPenData::GetWidth() +{ + return m_width; +} + +ID2D1StrokeStyle* wxD2DPenData::GetStrokeStyle() +{ + return m_strokeStyle; +} + +wxD2DPenData* wxGetD2DPenData(const wxGraphicsPen& pen) +{ + return static_cast(pen.GetGraphicsData()); +} + +class wxD2DFontData : public wxGraphicsObjectRefData +{ +public: + wxD2DFontData(wxGraphicsRenderer* renderer, ID2D1Factory* d2d1Factory, const wxFont& font, const wxColor& color); + + wxCOMPtr CreateTextLayout(const wxString& text) const; + + wxD2DBrushData& GetBrushData() { return m_brushData; } + + wxCOMPtr GetTextFormat() const { return m_textFormat; } + + wxCOMPtr GetFont() { return m_font; }; + +private: + // The native, device-independent font object + wxCOMPtr m_font; + + // The native, device-independent font object + wxCOMPtr m_textFormat; + + // We use a color brush to render the font + wxD2DBrushData m_brushData; + + bool m_underlined; + + bool m_strikethrough; +}; + +wxD2DFontData::wxD2DFontData(wxGraphicsRenderer* renderer, ID2D1Factory* d2dFactory, const wxFont& font, const wxColor& color) : + wxGraphicsObjectRefData(renderer), m_brushData(renderer, wxBrush(color)), + m_underlined(font.GetUnderlined()), m_strikethrough(font.GetStrikethrough()) +{ + HRESULT hr; + + wxCOMPtr gdiInterop; + hr = wxDWriteFactory()->GetGdiInterop(&gdiInterop); + + LOGFONT logfont; + GetObject(font.GetHFONT(), sizeof(logfont), &logfont); + + // Ensure the LOGFONT object contains the correct font face name + if (logfont.lfFaceName[0] == '\0') + { + for (unsigned int i = 0; i < font.GetFaceName().Length(); ++i) + { + logfont.lfFaceName[i] = font.GetFaceName().GetChar(i); + } + } + + hr = gdiInterop->CreateFontFromLOGFONT(&logfont, &m_font); + + wxCOMPtr fontFamily; + m_font->GetFontFamily(&fontFamily); + + wxCOMPtr familyNames; + fontFamily->GetFamilyNames(&familyNames); + + UINT32 length; + familyNames->GetStringLength(0, &length); + + wchar_t* name = new wchar_t[length+1]; + familyNames->GetString(0, name, length+1); + + FLOAT dpiX, dpiY; + d2dFactory->GetDesktopDpi(&dpiX, &dpiY); + + hr = wxDWriteFactory()->CreateTextFormat( + name, + NULL, + m_font->GetWeight(), + m_font->GetStyle(), + m_font->GetStretch(), + (FLOAT)(font.GetPixelSize().GetHeight()) / (dpiY / 96.0), + L"en-us", + &m_textFormat); + + delete[] name; +} + +wxCOMPtr wxD2DFontData::CreateTextLayout(const wxString& text) const +{ + static const FLOAT MAX_WIDTH = FLT_MAX; + static const FLOAT MAX_HEIGHT = FLT_MAX; + + HRESULT hr; + + wxCOMPtr textLayout; + + hr = wxDWriteFactory()->CreateTextLayout( + text.c_str(), + text.length(), + m_textFormat, + MAX_WIDTH, + MAX_HEIGHT, + &textLayout); + + DWRITE_TEXT_RANGE textRange = { 0, text.length() }; + + if (m_underlined) + { + textLayout->SetUnderline(true, textRange); + } + + if (m_strikethrough) + { + textLayout->SetStrikethrough(true, textRange); + } + + return textLayout; +} + +wxD2DFontData* wxGetD2DFontData(const wxGraphicsFont& font) +{ + return static_cast(font.GetGraphicsData()); +} + +// A render target resource holder exposes methods relevant +// for native render targets such as resize +class wxD2DRenderTargetResourceHolder : public wxD2DResourceHolder +{ +public: + // This method is called when an external event signals the underlying DC + // is resized (e.g. the resizing of a window). Some implementations can leave + // this method empty, while others must adjust the render target size to match + // the underlying DC. + virtual void Resize() + { + } + + // We use this method instead of the one provided by the native render target + // because Direct2D 1.0 render targets do not accept a composition mode + // parameter, while the device context in Direct2D 1.1 does. This way, we make + // best use of the capabilities of each render target. + // + // The default implementation works for all render targets, but the D2D 1.0 + // render target holders shouldn't need to override it, since none of the + // 1.0 render targets offer a better version of this method. + virtual void DrawBitmap(ID2D1Image* image, D2D1_POINT_2F offset, + D2D1_RECT_F imageRectangle, wxInterpolationQuality interpolationQuality, + wxCompositionMode WXUNUSED(compositionMode)) + { + D2D1_RECT_F destinationRectangle = D2D1::RectF(offset.x, offset.y, offset.x + imageRectangle.right, offset.y + imageRectangle.bottom); + m_nativeResource->DrawBitmap( + (ID2D1Bitmap*)image, + destinationRectangle, + 1.0f, + wxD2DConvertBitmapInterpolationMode(interpolationQuality), + imageRectangle); + } + + // We use this method instead of the one provided by the native render target + // because some contexts might require writing to a buffer (e.g. an image + // context), and some render targets might require additional operations to + // be executed (e.g. the device context must present the swap chain) + virtual HRESULT Flush() + { + return m_nativeResource->Flush(); + } + + // Composition is not supported at in D2D 1.0, and we only allow for: + // wxCOMPOSITION_DEST - which is essentially a no-op and is handled + // externally by preventing any draw calls. + // wxCOMPOSITION_OVER - which copies the source over the destination using + // alpha blending. This is the default way D2D 1.0 + // draws images. + virtual bool SetCompositionMode(wxCompositionMode compositionMode) + { + if (compositionMode == wxCOMPOSITION_DEST || + compositionMode == wxCOMPOSITION_OVER) + { + // There's nothing we can do but notify the caller the composition + // mode is supported + return true; + } + + return false; + } +}; + +class wxD2DImageRenderTargetResourceHolder : public wxD2DRenderTargetResourceHolder +{ +public: + wxD2DImageRenderTargetResourceHolder(wxImage* image, ID2D1Factory* factory) : + m_resultImage(image), m_factory(factory) + { + } + + HRESULT Flush() wxOVERRIDE + { + HRESULT hr = m_nativeResource->Flush(); + FlushRenderTargetToImage(); + return hr; + } + + ~wxD2DImageRenderTargetResourceHolder() + { + FlushRenderTargetToImage(); + } + +protected: + void DoAcquireResource() wxOVERRIDE + { + HRESULT hr; + + // Create a compatible WIC Bitmap + hr = wxWICImagingFactory()->CreateBitmap( + m_resultImage->GetWidth(), + m_resultImage->GetHeight(), + GUID_WICPixelFormat32bppPBGRA, + WICBitmapCacheOnDemand, + &m_wicBitmap); + wxCHECK_HRESULT_RET(hr); + + // Create the render target + hr = m_factory->CreateWicBitmapRenderTarget( + m_wicBitmap, + D2D1::RenderTargetProperties( + D2D1_RENDER_TARGET_TYPE_SOFTWARE, + D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)), + &m_nativeResource); + wxCHECK_HRESULT_RET(hr); + } + +private: + void FlushRenderTargetToImage() + { + int width = m_resultImage->GetWidth(); + int height = m_resultImage->GetHeight(); + int bufferSize = 4 * width * height; + + BYTE* buffer = new BYTE[bufferSize]; + m_wicBitmap->CopyPixels(NULL, 4 * width, bufferSize, buffer); + unsigned char* dest = m_resultImage->GetData(); + + int k = 0; + while (k < width * height) + { + wxPBGRAColor color = wxPBGRAColor(buffer + k * 4); + dest[k * 3 + 0] = color.r; + dest[k * 3 + 1] = color.g; + dest[k * 3 + 2] = color.b; + ++k; + } + + delete[] buffer; + } + +private: + wxImage* m_resultImage; + wxCOMPtr m_wicBitmap; + + ID2D1Factory* m_factory; +}; + +class wxD2DHwndRenderTargetResourceHolder : public wxD2DRenderTargetResourceHolder +{ +public: + typedef ID2D1HwndRenderTarget* ImplementationType; + + wxD2DHwndRenderTargetResourceHolder(HWND hwnd, ID2D1Factory* factory) : + m_hwnd(hwnd), m_factory(factory) + { + } + + void Resize() wxOVERRIDE + { + RECT clientRect; + GetClientRect(m_hwnd, &clientRect); + + D2D1_SIZE_U hwndSize = D2D1::SizeU( + clientRect.right - clientRect.left, + clientRect.bottom - clientRect.top); + + D2D1_SIZE_U renderTargetSize = GetRenderTarget()->GetPixelSize(); + + if (hwndSize.width != renderTargetSize.width || hwndSize.height != renderTargetSize.height) + { + GetRenderTarget()->Resize(hwndSize); + } + } + +protected: + void DoAcquireResource() wxOVERRIDE + { + wxCOMPtr renderTarget; + + HRESULT result; + + RECT clientRect; + GetClientRect(m_hwnd, &clientRect); + + D2D1_SIZE_U size = D2D1::SizeU( + clientRect.right - clientRect.left, + clientRect.bottom - clientRect.top); + + result = m_factory->CreateHwndRenderTarget( + D2D1::RenderTargetProperties(), + D2D1::HwndRenderTargetProperties(m_hwnd, size), + &renderTarget); + + if (FAILED(result)) + { + wxFAIL_MSG("Could not create Direct2D render target"); + } + + renderTarget->SetTransform(D2D1::Matrix3x2F::Identity()); + + m_nativeResource = renderTarget; + } + +private: + // Converts the underlying resource pointer of type + // ID2D1RenderTarget* to the actual implementation type + ImplementationType GetRenderTarget() + { + return static_cast(GetD2DResource().get()); + } + +private: + HWND m_hwnd; + ID2D1Factory* m_factory; +}; + +#if wxD2D_DEVICE_CONTEXT_SUPPORTED +class wxD2DDeviceContextResourceHolder : public wxD2DRenderTargetResourceHolder +{ +public: + wxD2DDeviceContextResourceHolder(ID2D1Factory* factory, HWND hwnd) : + m_factory(NULL), m_hwnd(hwnd) + { + HRESULT hr = factory->QueryInterface(IID_ID2D1Factory1, (void**)&m_factory); + wxCHECK_HRESULT_RET(hr); + } + + void DrawBitmap(ID2D1Image* image, D2D1_POINT_2F offset, + D2D1_RECT_F imageRectangle, wxInterpolationQuality interpolationQuality, + wxCompositionMode compositionMode) wxOVERRIDE + { + m_context->DrawImage(image, + offset, + imageRectangle, + wxD2DConvertInterpolationMode(interpolationQuality), + wxD2DConvertCompositionMode(compositionMode)); + } + + HRESULT Flush() wxOVERRIDE + { + HRESULT hr = m_nativeResource->Flush(); + DXGI_PRESENT_PARAMETERS params = { 0 }; + m_swapChain->Present1(1, 0, ¶ms); + return hr; + } + +protected: + + // Adapted from http://msdn.microsoft.com/en-us/library/windows/desktop/hh780339%28v=vs.85%29.aspx + void DoAcquireResource() wxOVERRIDE + { + HRESULT hr; + + // This flag adds support for surfaces with a different color channel ordering than the API default. + // You need it for compatibility with Direct2D. + UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; + + // This array defines the set of DirectX hardware feature levels this app supports. + // The ordering is important and you should preserve it. + // Don't forget to declare your app's minimum required feature level in its + // description. All apps are assumed to support 9.1 unless otherwise stated. + D3D_FEATURE_LEVEL featureLevels[] = + { + D3D_FEATURE_LEVEL_11_1, + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, + D3D_FEATURE_LEVEL_9_3, + D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1 + }; + + // Create the DX11 API device object, and get a corresponding context. + wxCOMPtr device; + wxCOMPtr context; + + hr = D3D11CreateDevice( + NULL, // specify null to use the default adapter + D3D_DRIVER_TYPE_HARDWARE, + 0, + creationFlags, // optionally set debug and Direct2D compatibility flags + featureLevels, // list of feature levels this app can support + ARRAYSIZE(featureLevels), // number of possible feature levels + D3D11_SDK_VERSION, + &device, // returns the Direct3D device created + &m_featureLevel, // returns feature level of device created + &context); // returns the device immediate context + wxCHECK_HRESULT_RET(hr); + + // Obtain the underlying DXGI device of the Direct3D11 device. + hr = device->QueryInterface(IID_IDXGIDevice, (void**)&m_dxgiDevice); + wxCHECK_HRESULT_RET(hr); + + // Obtain the Direct2D device for 2-D rendering. + hr = m_factory->CreateDevice(m_dxgiDevice, &m_device); + wxCHECK_HRESULT_RET(hr); + + // Get Direct2D device's corresponding device context object. + hr = m_device->CreateDeviceContext( + D2D1_DEVICE_CONTEXT_OPTIONS_NONE, + &m_context); + wxCHECK_HRESULT_RET(hr); + + m_nativeResource = m_context; + + AttachSurface(); + } + +private: + void AttachSurface() + { + HRESULT hr; + + // Allocate a descriptor. + DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0}; + swapChainDesc.Width = 0; + swapChainDesc.Height = 0; + swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + swapChainDesc.Stereo = false; + swapChainDesc.SampleDesc.Count = 1; + swapChainDesc.SampleDesc.Quality = 0; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.BufferCount = 2; + swapChainDesc.Scaling = DXGI_SCALING_STRETCH; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + swapChainDesc.Flags = 0; + + // Identify the physical adapter (GPU or card) this device is runs on. + wxCOMPtr dxgiAdapter; + hr = m_dxgiDevice->GetAdapter(&dxgiAdapter); + wxCHECK_HRESULT_RET(hr); + + // Get the factory object that created the DXGI device. + wxCOMPtr dxgiFactory; + hr = dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory)); + wxCHECK_HRESULT_RET(hr); + + // Get the final swap chain for this window from the DXGI factory. + hr = dxgiFactory->CreateSwapChainForHwnd( + m_dxgiDevice, + m_hwnd, + &swapChainDesc, + NULL, // allow on all displays + NULL, + &m_swapChain); + wxCHECK_HRESULT_RET(hr); + + // Ensure that DXGI doesn't queue more than one frame at a time. + hr = m_dxgiDevice->SetMaximumFrameLatency(1); + wxCHECK_HRESULT_RET(hr); + + // Get the backbuffer for this window which is be the final 3D render target. + wxCOMPtr backBuffer; + hr = m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer)); + wxCHECK_HRESULT_RET(hr); + + FLOAT dpiX, dpiY; + m_factory->GetDesktopDpi(&dpiX, &dpiY); + + // Now we set up the Direct2D render target bitmap linked to the swapchain. + // Whenever we render to this bitmap, it is directly rendered to the + // swap chain associated with the window. + D2D1_BITMAP_PROPERTIES1 bitmapProperties = D2D1::BitmapProperties1( + D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW, + D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE), + dpiX, dpiY); + + // Direct2D needs the dxgi version of the backbuffer surface pointer. + wxCOMPtr dxgiBackBuffer; + hr = m_swapChain->GetBuffer(0, IID_PPV_ARGS(&dxgiBackBuffer)); + wxCHECK_HRESULT_RET(hr); + + // Get a D2D surface from the DXGI back buffer to use as the D2D render target. + hr = m_context->CreateBitmapFromDxgiSurface( + dxgiBackBuffer.get(), + &bitmapProperties, + &m_targetBitmap); + wxCHECK_HRESULT_RET(hr); + + // Now we can set the Direct2D render target. + m_context->SetTarget(m_targetBitmap); + } + + ~wxD2DDeviceContextResourceHolder() + { + DXGI_PRESENT_PARAMETERS params = { 0 }; + m_swapChain->Present1(1, 0, ¶ms); + } + +private: + ID2D1Factory1* m_factory; + + HWND m_hwnd; + + D3D_FEATURE_LEVEL m_featureLevel; + wxCOMPtr m_dxgiDevice; + wxCOMPtr m_device; + wxCOMPtr m_context; + wxCOMPtr m_targetBitmap; + wxCOMPtr m_swapChain; +}; +#endif + +class wxD2DDCRenderTargetResourceHolder : public wxD2DRenderTargetResourceHolder +{ +public: + wxD2DDCRenderTargetResourceHolder(ID2D1Factory* factory, HDC hdc, const wxSize dcSize) : + m_factory(factory), m_hdc(hdc) + { + m_dcSize.left = 0; + m_dcSize.top = 0; + m_dcSize.right = dcSize.GetWidth(); + m_dcSize.bottom = dcSize.GetHeight(); + } + +protected: + void DoAcquireResource() + { + ID2D1DCRenderTarget* renderTarget; + D2D1_RENDER_TARGET_PROPERTIES renderTargetProperties = D2D1::RenderTargetProperties( + D2D1_RENDER_TARGET_TYPE_DEFAULT, + D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)); + + HRESULT hr = m_factory->CreateDCRenderTarget( + &renderTargetProperties, + &renderTarget); + wxCHECK_HRESULT_RET(hr); + + hr = renderTarget->BindDC(m_hdc, &m_dcSize); + wxCHECK_HRESULT_RET(hr); + + m_nativeResource = renderTarget; + } + +private: + ID2D1Factory* m_factory; + HDC m_hdc; + RECT m_dcSize; +}; + +// The null context has no state of its own and does nothing. +// It is only used as a base class for the lightweight +// measuring context. The measuring context cannot inherit from +// the default implementation wxD2DContext, because some methods +// from wxD2DContext require the presence of a "context" +// (render target) in order to acquire various device-dependent +// resources. Without a proper context, those methods would fail. +// The methods implemented in the null context are fundamentally no-ops. +class wxNullContext : public wxGraphicsContext +{ +public: + wxNullContext(wxGraphicsRenderer* renderer) : wxGraphicsContext(renderer) {} + void GetTextExtent(const wxString&, wxDouble*, wxDouble*, wxDouble*, wxDouble*) const wxOVERRIDE {} + void GetPartialTextExtents(const wxString&, wxArrayDouble&) const wxOVERRIDE {} + void Clip(const wxRegion&) wxOVERRIDE {} + void Clip(wxDouble, wxDouble, wxDouble, wxDouble) wxOVERRIDE {} + void ResetClip() wxOVERRIDE {} + void* GetNativeContext() wxOVERRIDE { return NULL; } + bool SetAntialiasMode(wxAntialiasMode) wxOVERRIDE { return false; } + bool SetInterpolationQuality(wxInterpolationQuality) wxOVERRIDE { return false; } + bool SetCompositionMode(wxCompositionMode) wxOVERRIDE { return false; } + void BeginLayer(wxDouble) wxOVERRIDE {} + void EndLayer() wxOVERRIDE {} + void Translate(wxDouble, wxDouble) wxOVERRIDE {} + void Scale(wxDouble, wxDouble) wxOVERRIDE {} + void Rotate(wxDouble) wxOVERRIDE {} + void ConcatTransform(const wxGraphicsMatrix&) wxOVERRIDE {} + void SetTransform(const wxGraphicsMatrix&) wxOVERRIDE {} + wxGraphicsMatrix GetTransform() const wxOVERRIDE { return wxNullGraphicsMatrix; } + void StrokePath(const wxGraphicsPath&) wxOVERRIDE {} + void FillPath(const wxGraphicsPath&, wxPolygonFillMode) wxOVERRIDE {} + void DrawBitmap(const wxGraphicsBitmap&, wxDouble, wxDouble, wxDouble, wxDouble) wxOVERRIDE {} + void DrawBitmap(const wxBitmap&, wxDouble, wxDouble, wxDouble, wxDouble) wxOVERRIDE {} + void DrawIcon(const wxIcon&, wxDouble, wxDouble, wxDouble, wxDouble) wxOVERRIDE {} + void PushState() wxOVERRIDE {} + void PopState() wxOVERRIDE {} + void Flush() wxOVERRIDE {} + +protected: + void DoDrawText(const wxString&, wxDouble, wxDouble) wxOVERRIDE {} +}; + +class wxD2DMeasuringContext : public wxNullContext +{ +public: + wxD2DMeasuringContext(wxGraphicsRenderer* renderer) : wxNullContext(renderer) {} + + void GetTextExtent(const wxString& str, wxDouble* width, wxDouble* height, wxDouble* descent, wxDouble* externalLeading) const wxOVERRIDE + { + GetTextExtent(wxGetD2DFontData(m_font), str, width, height, descent, externalLeading); + } + + void GetPartialTextExtents(const wxString& text, wxArrayDouble& widths) const wxOVERRIDE + { + GetPartialTextExtents(wxGetD2DFontData(m_font), text, widths); + } + + static void GetPartialTextExtents(wxD2DFontData* fontData, const wxString& text, wxArrayDouble& widths) + { + for (unsigned int i = 0; i < text.Length(); ++i) + { + wxDouble width; + GetTextExtent(fontData, text.SubString(0, i), &width, NULL, NULL, NULL); + widths.push_back(width); + } + } + + static void GetTextExtent(wxD2DFontData* fontData, const wxString& str, wxDouble* width, wxDouble* height, wxDouble* descent, wxDouble* externalLeading) + { + wxCOMPtr textLayout = fontData->CreateTextLayout(str); + wxCOMPtr font = fontData->GetFont(); + + DWRITE_TEXT_METRICS textMetrics; + textLayout->GetMetrics(&textMetrics); + + DWRITE_FONT_METRICS fontMetrics; + font->GetMetrics(&fontMetrics); + + FLOAT ratio = fontData->GetTextFormat()->GetFontSize() / (FLOAT)fontMetrics.designUnitsPerEm; + + if (width != NULL) *width = textMetrics.widthIncludingTrailingWhitespace; + if (height != NULL) *height = textMetrics.height; + + if (descent != NULL) *descent = fontMetrics.descent * ratio; + if (externalLeading != NULL) *externalLeading = std::max(0.0f, (fontMetrics.ascent + fontMetrics.descent) * ratio - textMetrics.height); + } +}; + +//----------------------------------------------------------------------------- +// wxD2DContext declaration +//----------------------------------------------------------------------------- + +class wxD2DContext : public wxGraphicsContext, wxD2DResourceManager +{ +public: + wxD2DContext(wxGraphicsRenderer* renderer, ID2D1Factory* direct2dFactory, HWND hwnd); + + wxD2DContext(wxGraphicsRenderer* renderer, ID2D1Factory* direct2dFactory, HDC hdc, const wxSize& dcSize); + + wxD2DContext(wxGraphicsRenderer* renderer, ID2D1Factory* direct2dFactory, wxImage& image); + + wxD2DContext(wxGraphicsRenderer* renderer, ID2D1Factory* direct2dFactory, void* nativeContext); + + ~wxD2DContext() wxOVERRIDE; + + void Clip(const wxRegion& region) wxOVERRIDE; + + void Clip(wxDouble x, wxDouble y, wxDouble w, wxDouble h) wxOVERRIDE; + + void ResetClip() wxOVERRIDE; + + // The native context used by wxD2DContext is a Direct2D render target. + void* GetNativeContext() wxOVERRIDE; + + bool SetAntialiasMode(wxAntialiasMode antialias) wxOVERRIDE; + + bool SetInterpolationQuality(wxInterpolationQuality interpolation) wxOVERRIDE; + + bool SetCompositionMode(wxCompositionMode op) wxOVERRIDE; + + void BeginLayer(wxDouble opacity) wxOVERRIDE; + + void EndLayer() wxOVERRIDE; + + void Translate(wxDouble dx, wxDouble dy) wxOVERRIDE; + + void Scale(wxDouble xScale, wxDouble yScale) wxOVERRIDE; + + void Rotate(wxDouble angle) wxOVERRIDE; + + void ConcatTransform(const wxGraphicsMatrix& matrix) wxOVERRIDE; + + void SetTransform(const wxGraphicsMatrix& matrix) wxOVERRIDE; + + wxGraphicsMatrix GetTransform() const wxOVERRIDE; + + void StrokePath(const wxGraphicsPath& p) wxOVERRIDE; + + void FillPath(const wxGraphicsPath& p , wxPolygonFillMode fillStyle = wxODDEVEN_RULE) wxOVERRIDE; + + void DrawRectangle(wxDouble x, wxDouble y, wxDouble w, wxDouble h) wxOVERRIDE; + + void DrawRoundedRectangle(wxDouble x, wxDouble y, wxDouble w, wxDouble h, wxDouble radius) wxOVERRIDE; + + void DrawEllipse(wxDouble x, wxDouble y, wxDouble w, wxDouble h) wxOVERRIDE; + + void DrawBitmap(const wxGraphicsBitmap& bmp, wxDouble x, wxDouble y, wxDouble w, wxDouble h) wxOVERRIDE; + + void DrawBitmap(const wxBitmap& bmp, wxDouble x, wxDouble y, wxDouble w, wxDouble h) wxOVERRIDE; + + void DrawIcon(const wxIcon& icon, wxDouble x, wxDouble y, wxDouble w, wxDouble h) wxOVERRIDE; + + void PushState() wxOVERRIDE; + + void PopState() wxOVERRIDE; + + void GetTextExtent( + const wxString& str, + wxDouble* width, + wxDouble* height, + wxDouble* descent, + wxDouble* externalLeading) const wxOVERRIDE; + + void GetPartialTextExtents(const wxString& text, wxArrayDouble& widths) const wxOVERRIDE; + + bool ShouldOffset() const wxOVERRIDE; + + void SetPen(const wxGraphicsPen& pen) wxOVERRIDE; + + void Flush() wxOVERRIDE; + + void GetDPI(wxDouble* dpiX, wxDouble* dpiY) wxOVERRIDE; + + wxD2DContextSupplier::ContextType GetContext() wxOVERRIDE + { + return GetRenderTarget(); + } + +private: + void Init(); + + void DoDrawText(const wxString& str, wxDouble x, wxDouble y) wxOVERRIDE; + + void EnsureInitialized(); + + HRESULT CreateRenderTarget(); + + void AdjustRenderTargetSize(); + + void ReleaseDeviceDependentResources(); + + ID2D1RenderTarget* GetRenderTarget() const; + +private: + enum ClipMode + { + CLIP_MODE_NONE, + CLIP_MODE_AXIS_ALIGNED_RECTANGLE, + CLIP_MODE_GEOMETRY + }; + +private: + ID2D1Factory* m_direct2dFactory; + + wxSharedPtr m_renderTargetHolder; + + // A ID2D1DrawingStateBlock represents the drawing state of a render target: + // the anti aliasing mode, transform, tags, and text-rendering options. + // The context owns these pointers and is responsible for releasing them. + wxStack> m_stateStack; + + ClipMode m_clipMode; + + bool m_clipLayerAcquired; + + // A direct2d layer is a device-dependent resource. + wxCOMPtr m_clipLayer; + + wxStack> m_layers; + + ID2D1RenderTarget* m_cachedRenderTarget; + +private: + wxDECLARE_NO_COPY_CLASS(wxD2DContext); +}; + +//----------------------------------------------------------------------------- +// wxD2DContext implementation +//----------------------------------------------------------------------------- + +wxD2DContext::wxD2DContext(wxGraphicsRenderer* renderer, ID2D1Factory* direct2dFactory, HWND hwnd) : + wxGraphicsContext(renderer), m_direct2dFactory(direct2dFactory), +#if wxD2D_DEVICE_CONTEXT_SUPPORTED + m_renderTargetHolder(new wxD2DDeviceContextResourceHolder(direct2dFactory, hwnd)) +#else + m_renderTargetHolder(new wxD2DHwndRenderTargetResourceHolder(hwnd, direct2dFactory)) +#endif +{ + Init(); +} + +wxD2DContext::wxD2DContext(wxGraphicsRenderer* renderer, ID2D1Factory* direct2dFactory, HDC hdc, const wxSize& dcSize) : + wxGraphicsContext(renderer), m_direct2dFactory(direct2dFactory), + m_renderTargetHolder(new wxD2DDCRenderTargetResourceHolder(direct2dFactory, hdc, dcSize)) +{ + Init(); +} + +wxD2DContext::wxD2DContext(wxGraphicsRenderer* renderer, ID2D1Factory* direct2dFactory, wxImage& image) : + wxGraphicsContext(renderer), m_direct2dFactory(direct2dFactory), + m_renderTargetHolder(new wxD2DImageRenderTargetResourceHolder(&image, direct2dFactory)) +{ + Init(); +} + +wxD2DContext::wxD2DContext(wxGraphicsRenderer* renderer, ID2D1Factory* direct2dFactory, void* nativeContext) : + wxGraphicsContext(renderer), m_direct2dFactory(direct2dFactory) +{ + m_renderTargetHolder = *((wxSharedPtr*)nativeContext); + Init(); +} + +void wxD2DContext::Init() +{ + m_cachedRenderTarget = NULL; + m_clipMode = CLIP_MODE_NONE; + m_composition = wxCOMPOSITION_OVER; + m_clipLayerAcquired = false; + m_renderTargetHolder->Bind(this); + m_enableOffset = true; + EnsureInitialized(); +} + +wxD2DContext::~wxD2DContext() +{ + ResetClip(); + + while (!m_layers.empty()) + { + EndLayer(); + } + + HRESULT result = GetRenderTarget()->EndDraw(); + wxCHECK_HRESULT_RET(result); + + ReleaseResources(); +} + +ID2D1RenderTarget* wxD2DContext::GetRenderTarget() const +{ + return m_cachedRenderTarget; +} + +void wxD2DContext::Clip(const wxRegion& region) +{ + GetRenderTarget()->Flush(); + ResetClip(); + + wxCOMPtr clipGeometry = wxD2DConvertRegionToGeometry(m_direct2dFactory, region); + + if (!m_clipLayerAcquired) + { + GetRenderTarget()->CreateLayer(&m_clipLayer); + m_clipLayerAcquired = true; + } + + GetRenderTarget()->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), clipGeometry), m_clipLayer); + + m_clipMode = CLIP_MODE_GEOMETRY; +} + +void wxD2DContext::Clip(wxDouble x, wxDouble y, wxDouble w, wxDouble h) +{ + GetRenderTarget()->Flush(); + ResetClip(); + + GetRenderTarget()->PushAxisAlignedClip( + D2D1::RectF(x, y, x + w, y + h), + D2D1_ANTIALIAS_MODE_ALIASED); + + m_clipMode = CLIP_MODE_AXIS_ALIGNED_RECTANGLE; +} + +void wxD2DContext::ResetClip() +{ + if (m_clipMode == CLIP_MODE_AXIS_ALIGNED_RECTANGLE) + { + GetRenderTarget()->PopAxisAlignedClip(); + } + + if (m_clipMode == CLIP_MODE_GEOMETRY) + { + GetRenderTarget()->PopLayer(); + } + + m_clipMode = CLIP_MODE_NONE; +} + +void* wxD2DContext::GetNativeContext() +{ + return &m_renderTargetHolder; +} + +void wxD2DContext::StrokePath(const wxGraphicsPath& p) +{ + if (m_composition == wxCOMPOSITION_DEST) + return; + + wxD2DOffsetHelper helper(this); + + EnsureInitialized(); + AdjustRenderTargetSize(); + + wxD2DPathData* pathData = wxGetD2DPathData(p); + pathData->Flush(); + + if (!m_pen.IsNull()) + { + wxD2DPenData* penData = wxGetD2DPenData(m_pen); + penData->Bind(this); + ID2D1Brush* nativeBrush = penData->GetBrush(); + GetRenderTarget()->DrawGeometry((ID2D1Geometry*)pathData->GetNativePath(), nativeBrush, penData->GetWidth(), penData->GetStrokeStyle()); + } +} + +void wxD2DContext::FillPath(const wxGraphicsPath& p , wxPolygonFillMode WXUNUSED(fillStyle)) +{ + if (m_composition == wxCOMPOSITION_DEST) + return; + + EnsureInitialized(); + AdjustRenderTargetSize(); + + wxD2DPathData* pathData = wxGetD2DPathData(p); + pathData->Flush(); + + if (!m_brush.IsNull()) + { + wxD2DBrushData* brushData = wxGetD2DBrushData(m_brush); + brushData->Bind(this); + GetRenderTarget()->FillGeometry((ID2D1Geometry*)pathData->GetNativePath(), brushData->GetBrush()); + } +} + +bool wxD2DContext::SetAntialiasMode(wxAntialiasMode antialias) +{ + if (m_antialias == antialias) + { + return true; + } + + GetRenderTarget()->SetAntialiasMode(wxD2DConvertAntialiasMode(antialias)); + + m_antialias = antialias; + return true; +} + +bool wxD2DContext::SetInterpolationQuality(wxInterpolationQuality interpolation) +{ + // Since different versions of Direct2D have different enumerations for + // interpolation quality, we deffer the conversion to the method which + // does the actual drawing. + + m_interpolation = interpolation; + return true; +} + +bool wxD2DContext::SetCompositionMode(wxCompositionMode compositionMode) +{ + if (m_composition == compositionMode) + return true; + + if (m_renderTargetHolder->SetCompositionMode(compositionMode)) + { + // the composition mode is supported by the render target + m_composition = compositionMode; + return true; + } + + return false; +} + +void wxD2DContext::BeginLayer(wxDouble opacity) +{ + wxCOMPtr layer; + GetRenderTarget()->CreateLayer(&layer); + m_layers.push(layer); + + GetRenderTarget()->PushLayer( + D2D1::LayerParameters(D2D1::InfiniteRect(), + NULL, + D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, + D2D1::IdentityMatrix(), opacity), + layer); +} + +void wxD2DContext::EndLayer() +{ + if (!m_layers.empty()) + { + wxCOMPtr topLayer = m_layers.top(); + + GetRenderTarget()->PopLayer(); + + HRESULT hr = GetRenderTarget()->Flush(); + wxCHECK_HRESULT_RET(hr); + + m_layers.pop(); + } +} + +void wxD2DContext::Translate(wxDouble dx, wxDouble dy) +{ + wxGraphicsMatrix translationMatrix = CreateMatrix(); + translationMatrix.Translate(dx, dy); + ConcatTransform(translationMatrix); +} + +void wxD2DContext::Scale(wxDouble xScale, wxDouble yScale) +{ + wxGraphicsMatrix scaleMatrix = CreateMatrix(); + scaleMatrix.Scale(xScale, yScale); + ConcatTransform(scaleMatrix); +} + +void wxD2DContext::Rotate(wxDouble angle) +{ + wxGraphicsMatrix rotationMatrix = CreateMatrix(); + rotationMatrix.Rotate(angle); + ConcatTransform(rotationMatrix); +} + +void wxD2DContext::ConcatTransform(const wxGraphicsMatrix& matrix) +{ + D2D1::Matrix3x2F localMatrix = wxGetD2DMatrixData(GetTransform())->GetMatrix3x2F(); + D2D1::Matrix3x2F concatMatrix = wxGetD2DMatrixData(matrix)->GetMatrix3x2F(); + + D2D1::Matrix3x2F resultMatrix; + resultMatrix.SetProduct(concatMatrix, localMatrix); + + wxGraphicsMatrix resultTransform; + resultTransform.SetRefData(new wxD2DMatrixData(GetRenderer(), resultMatrix)); + + SetTransform(resultTransform); +} + +void wxD2DContext::SetTransform(const wxGraphicsMatrix& matrix) +{ + EnsureInitialized(); + + GetRenderTarget()->SetTransform(wxGetD2DMatrixData(matrix)->GetMatrix3x2F()); +} + +wxGraphicsMatrix wxD2DContext::GetTransform() const +{ + D2D1::Matrix3x2F transformMatrix; + + if (GetRenderTarget() != NULL) + { + GetRenderTarget()->GetTransform(&transformMatrix); + } + else + { + transformMatrix = D2D1::Matrix3x2F::Identity(); + } + + wxD2DMatrixData* matrixData = new wxD2DMatrixData(GetRenderer(), transformMatrix); + + wxGraphicsMatrix matrix; + matrix.SetRefData(matrixData); + + return matrix; +} + +void wxD2DContext::DrawBitmap(const wxGraphicsBitmap& bmp, wxDouble x, wxDouble y, wxDouble w, wxDouble h) +{ + if (m_composition == wxCOMPOSITION_DEST) + return; + + wxD2DBitmapData* bitmapData = wxGetD2DBitmapData(bmp); + bitmapData->Bind(this); + + m_renderTargetHolder->DrawBitmap( + bitmapData->GetD2DBitmap(), + D2D1::Point2F(x, y), + D2D1::RectF(0, 0, w, h), + GetInterpolationQuality(), + GetCompositionMode()); +} + +void wxD2DContext::DrawBitmap(const wxBitmap& bmp, wxDouble x, wxDouble y, wxDouble w, wxDouble h) +{ + wxGraphicsBitmap graphicsBitmap = CreateBitmap(bmp); + DrawBitmap(graphicsBitmap, x, y, w, h); +} + +void wxD2DContext::DrawIcon(const wxIcon& icon, wxDouble x, wxDouble y, wxDouble w, wxDouble h) +{ + DrawBitmap(wxBitmap(icon), x, y, w, h); +} + +void wxD2DContext::PushState() +{ + ID2D1Factory* wxGetD2DFactory(wxGraphicsRenderer* renderer); + + wxCOMPtr drawStateBlock; + wxGetD2DFactory(GetRenderer())->CreateDrawingStateBlock(&drawStateBlock); + GetRenderTarget()->SaveDrawingState(drawStateBlock); + + m_stateStack.push(drawStateBlock); +} + +void wxD2DContext::PopState() +{ + wxCHECK_RET(!m_stateStack.empty(), wxT("No state to pop")); + + wxCOMPtr drawStateBlock = m_stateStack.top(); + m_stateStack.pop(); + + GetRenderTarget()->RestoreDrawingState(drawStateBlock); +} + +void wxD2DContext::GetTextExtent( + const wxString& str, + wxDouble* width, + wxDouble* height, + wxDouble* descent, + wxDouble* externalLeading) const +{ + wxD2DMeasuringContext::GetTextExtent( + wxGetD2DFontData(m_font), str, width, height, descent, externalLeading); +} + +void wxD2DContext::GetPartialTextExtents(const wxString& text, wxArrayDouble& widths) const +{ + return wxD2DMeasuringContext::GetPartialTextExtents( + wxGetD2DFontData(m_font), text, widths); +} + +bool wxD2DContext::ShouldOffset() const +{ + if (!m_enableOffset) + { + return false; + } + + int penWidth = 0; + if (!m_pen.IsNull()) + { + penWidth = wxGetD2DPenData(m_pen)->GetWidth(); + penWidth = std::max(penWidth, 1); + } + + return (penWidth % 2) == 1; +} + +void wxD2DContext::DoDrawText(const wxString& str, wxDouble x, wxDouble y) +{ + wxCHECK_RET(!m_font.IsNull(), + wxT("wxGDIPlusContext::DrawText - no valid font set")); + + if (m_composition == wxCOMPOSITION_DEST) + return; + + wxD2DFontData* fontData = wxGetD2DFontData(m_font); + fontData->GetBrushData().Bind(this); + + wxCOMPtr textLayout = fontData->CreateTextLayout(str); + + // Render the text + GetRenderTarget()->DrawTextLayout( + D2D1::Point2F(x, y), + textLayout, + fontData->GetBrushData().GetBrush()); +} + +void wxD2DContext::EnsureInitialized() +{ + if (!m_renderTargetHolder->IsResourceAcquired()) + { + m_cachedRenderTarget = m_renderTargetHolder->GetD2DResource(); + GetRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity()); + GetRenderTarget()->BeginDraw(); + } + else + { + m_cachedRenderTarget = m_renderTargetHolder->GetD2DResource(); + } +} + +void wxD2DContext::SetPen(const wxGraphicsPen& pen) +{ + wxGraphicsContext::SetPen(pen); + + if (!m_pen.IsNull()) + { + EnsureInitialized(); + + wxD2DPenData* penData = wxGetD2DPenData(pen); + penData->Bind(this); + } +} + +void wxD2DContext::AdjustRenderTargetSize() +{ + m_renderTargetHolder->Resize(); + D2D1_SIZE_F renderTargetSize = m_renderTargetHolder->GetD2DResource()->GetSize(); + m_width = renderTargetSize.width; + m_height = renderTargetSize.height; +} + +void wxD2DContext::ReleaseDeviceDependentResources() +{ + ReleaseResources(); + + m_clipLayer.reset(); + m_clipLayerAcquired = false; +} + +void wxD2DContext::DrawRectangle(wxDouble x, wxDouble y, wxDouble w, wxDouble h) +{ + if (m_composition == wxCOMPOSITION_DEST) + return; + + wxD2DOffsetHelper helper(this); + + EnsureInitialized(); + AdjustRenderTargetSize(); + + D2D1_RECT_F rect = {x, y, x + w, y + h}; + + + if (!m_brush.IsNull()) + { + wxD2DBrushData* brushData = wxGetD2DBrushData(m_brush); + brushData->Bind(this); + GetRenderTarget()->FillRectangle(rect, brushData->GetBrush()); + } + + if (!m_pen.IsNull()) + { + wxD2DPenData* penData = wxGetD2DPenData(m_pen); + penData->Bind(this); + GetRenderTarget()->DrawRectangle(rect, penData->GetBrush(), penData->GetWidth(), penData->GetStrokeStyle()); + } +} + +void wxD2DContext::DrawRoundedRectangle(wxDouble x, wxDouble y, wxDouble w, wxDouble h, wxDouble radius) +{ + if (m_composition == wxCOMPOSITION_DEST) + return; + + wxD2DOffsetHelper helper(this); + + EnsureInitialized(); + AdjustRenderTargetSize(); + + D2D1_RECT_F rect = {x, y, x + w, y + h}; + + D2D1_ROUNDED_RECT roundedRect = {rect, radius, radius}; + + if (!m_brush.IsNull()) + { + wxD2DBrushData* brushData = wxGetD2DBrushData(m_brush); + brushData->Bind(this); + GetRenderTarget()->FillRoundedRectangle(roundedRect, brushData->GetBrush()); + } + + if (!m_pen.IsNull()) + { + wxD2DPenData* penData = wxGetD2DPenData(m_pen); + penData->Bind(this); + GetRenderTarget()->DrawRoundedRectangle(roundedRect, penData->GetBrush(), penData->GetWidth(), penData->GetStrokeStyle()); + } +} + +void wxD2DContext::DrawEllipse(wxDouble x, wxDouble y, wxDouble w, wxDouble h) +{ + if (m_composition == wxCOMPOSITION_DEST) + return; + + wxD2DOffsetHelper helper(this); + + EnsureInitialized(); + AdjustRenderTargetSize(); + + D2D1_ELLIPSE ellipse = { + {(x + w / 2), (y + h / 2)}, // center point + w / 2, // radius x + h / 2 // radius y + }; + + if (!m_brush.IsNull()) + { + wxD2DBrushData* brushData = wxGetD2DBrushData(m_brush); + brushData->Bind(this); + GetRenderTarget()->FillEllipse(ellipse, brushData->GetBrush()); + } + + if (!m_pen.IsNull()) + { + wxD2DPenData* penData = wxGetD2DPenData(m_pen); + penData->Bind(this); + GetRenderTarget()->DrawEllipse(ellipse, penData->GetBrush(), penData->GetWidth(), penData->GetStrokeStyle()); + } +} + +void wxD2DContext::Flush() +{ + HRESULT result = m_renderTargetHolder->Flush(); + + if (result == D2DERR_RECREATE_TARGET) + { + ReleaseDeviceDependentResources(); + } +} + +void wxD2DContext::GetDPI(wxDouble* dpiX, wxDouble* dpiY) +{ + FLOAT x, y; + GetRenderTarget()->GetDpi(&x, &y); + if (dpiX != NULL) *dpiX = x; + if (dpiY != NULL) *dpiY = y; +} + +//----------------------------------------------------------------------------- +// wxD2DRenderer declaration +//----------------------------------------------------------------------------- + +class wxD2DRenderer : public wxGraphicsRenderer +{ +public : + wxD2DRenderer(); + + virtual ~wxD2DRenderer(); + + wxGraphicsContext* CreateContext(const wxWindowDC& dc) wxOVERRIDE; + + wxGraphicsContext* CreateContext(const wxMemoryDC& dc) wxOVERRIDE; + +#if wxUSE_PRINTING_ARCHITECTURE + wxGraphicsContext* CreateContext(const wxPrinterDC& dc) wxOVERRIDE; +#endif + +#if wxUSE_ENH_METAFILE + wxGraphicsContext* CreateContext(const wxEnhMetaFileDC& dc) wxOVERRIDE; +#endif + + wxGraphicsContext* CreateContextFromNativeContext(void* context) wxOVERRIDE; + + wxGraphicsContext* CreateContextFromNativeWindow(void* window) wxOVERRIDE; + + wxGraphicsContext* CreateContext(wxWindow* window) wxOVERRIDE; + +#if wxUSE_IMAGE + wxGraphicsContext* CreateContextFromImage(wxImage& image) wxOVERRIDE; +#endif // wxUSE_IMAGE + + wxGraphicsContext* CreateMeasuringContext() wxOVERRIDE; + + wxGraphicsPath CreatePath() wxOVERRIDE; + + 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) wxOVERRIDE; + + wxGraphicsPen CreatePen(const wxPen& pen) wxOVERRIDE; + + wxGraphicsBrush CreateBrush(const wxBrush& brush) wxOVERRIDE; + + wxGraphicsBrush CreateLinearGradientBrush( + wxDouble x1, wxDouble y1, + wxDouble x2, wxDouble y2, + const wxGraphicsGradientStops& stops) wxOVERRIDE; + + wxGraphicsBrush CreateRadialGradientBrush( + wxDouble xo, wxDouble yo, + wxDouble xc, wxDouble yc, + wxDouble radius, + const wxGraphicsGradientStops& stops) wxOVERRIDE; + + // create a native bitmap representation + wxGraphicsBitmap CreateBitmap(const wxBitmap& bitmap) wxOVERRIDE; + +#if wxUSE_IMAGE + wxGraphicsBitmap CreateBitmapFromImage(const wxImage& image) wxOVERRIDE; + wxImage CreateImageFromBitmap(const wxGraphicsBitmap& bmp) wxOVERRIDE; +#endif + + wxGraphicsFont CreateFont(const wxFont& font, const wxColour& col) wxOVERRIDE; + + wxGraphicsFont CreateFont( + double size, const wxString& facename, + int flags = wxFONTFLAG_DEFAULT, + const wxColour& col = *wxBLACK) wxOVERRIDE; + + // create a graphics bitmap from a native bitmap + wxGraphicsBitmap CreateBitmapFromNativeBitmap(void* bitmap) wxOVERRIDE; + + // create a sub-image from a native image representation + wxGraphicsBitmap CreateSubBitmap(const wxGraphicsBitmap& bitmap, wxDouble x, wxDouble y, wxDouble w, wxDouble h) wxOVERRIDE; + + wxString GetName() const wxOVERRIDE; + void GetVersion(int* major, int* minor, int* micro) const wxOVERRIDE; + + ID2D1Factory* GetD2DFactory(); + +private: + wxCOMPtr m_direct2dFactory; + +private : + DECLARE_DYNAMIC_CLASS_NO_COPY(wxD2DRenderer) +}; + +//----------------------------------------------------------------------------- +// wxD2DRenderer implementation +//----------------------------------------------------------------------------- + +IMPLEMENT_DYNAMIC_CLASS(wxD2DRenderer,wxGraphicsRenderer) + +static wxD2DRenderer* gs_D2DRenderer = NULL; + +wxGraphicsRenderer* wxGraphicsRenderer::GetDirect2DRenderer() +{ + if (!wxDirect2D::Initialize()) + return NULL; + + if (gs_D2DRenderer == NULL) + { + gs_D2DRenderer = new wxD2DRenderer(); + } + + return gs_D2DRenderer; +} + +wxD2DRenderer::wxD2DRenderer() +{ + wxEnsureCOMLibraryInitialized(); + + HRESULT result; + result = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_direct2dFactory); + + if (FAILED(result)) + wxFAIL_MSG("Could not create Direct2D Factory."); +} + +wxD2DRenderer::~wxD2DRenderer() +{ + m_direct2dFactory.reset(); +} + +wxGraphicsContext* wxD2DRenderer::CreateContext(const wxWindowDC& dc) +{ + int width, height; + dc.GetSize(&width, &height); + + return new wxD2DContext(this, m_direct2dFactory, dc.GetHDC(), wxSize(width, height)); +} + +wxGraphicsContext* wxD2DRenderer::CreateContext(const wxMemoryDC& dc) +{ + int width, height; + dc.GetSize(&width, &height); + + return new wxD2DContext(this, m_direct2dFactory, dc.GetHDC(), wxSize(width, height)); +} + +#if wxUSE_PRINTING_ARCHITECTURE +wxGraphicsContext* wxD2DRenderer::CreateContext(const wxPrinterDC& WXUNUSED(dc)) +{ + wxFAIL_MSG("not implemented"); + return NULL; +} +#endif + +#if wxUSE_ENH_METAFILE +wxGraphicsContext* wxD2DRenderer::CreateContext(const wxEnhMetaFileDC& WXUNUSED(dc)) +{ + wxFAIL_MSG("not implemented"); + return NULL; +} +#endif + +wxGraphicsContext* wxD2DRenderer::CreateContextFromNativeContext(void* nativeContext) +{ + return new wxD2DContext(this, m_direct2dFactory, nativeContext); +} + +wxGraphicsContext* wxD2DRenderer::CreateContextFromNativeWindow(void* window) +{ + return new wxD2DContext(this, m_direct2dFactory, (HWND)window); +} + +wxGraphicsContext* wxD2DRenderer::CreateContext(wxWindow* window) +{ + return new wxD2DContext(this, m_direct2dFactory, (HWND)window->GetHWND()); +} + +#if wxUSE_IMAGE +wxGraphicsContext* wxD2DRenderer::CreateContextFromImage(wxImage& image) +{ + return new wxD2DContext(this, m_direct2dFactory, image); +} +#endif // wxUSE_IMAGE + +wxGraphicsContext* wxD2DRenderer::CreateMeasuringContext() +{ + return new wxD2DMeasuringContext(this); +} + +wxGraphicsPath wxD2DRenderer::CreatePath() +{ + wxGraphicsPath p; + p.SetRefData(new wxD2DPathData(this, m_direct2dFactory)); + + return p; +} + +wxGraphicsMatrix wxD2DRenderer::CreateMatrix( + wxDouble a, wxDouble b, wxDouble c, wxDouble d, + wxDouble tx, wxDouble ty) +{ + wxD2DMatrixData* matrixData = new wxD2DMatrixData(this); + matrixData->Set(a, b, c, d, tx, ty); + + wxGraphicsMatrix matrix; + matrix.SetRefData(matrixData); + + return matrix; +} + +wxGraphicsPen wxD2DRenderer::CreatePen(const wxPen& pen) +{ + if ( !pen.IsOk() || pen.GetStyle() == wxPENSTYLE_TRANSPARENT ) + { + return wxNullGraphicsPen; + } + else + { + wxGraphicsPen p; + wxD2DPenData* penData = new wxD2DPenData(this, m_direct2dFactory, pen); + p.SetRefData(penData); + return p; + } +} + +wxGraphicsBrush wxD2DRenderer::CreateBrush(const wxBrush& brush) +{ + if ( !brush.IsOk() || brush.GetStyle() == wxPENSTYLE_TRANSPARENT ) + { + return wxNullGraphicsBrush; + } + else + { + wxGraphicsBrush b; + b.SetRefData(new wxD2DBrushData(this, brush)); + return b; + } +} + +wxGraphicsBrush wxD2DRenderer::CreateLinearGradientBrush( + wxDouble x1, wxDouble y1, + wxDouble x2, wxDouble y2, + const wxGraphicsGradientStops& stops) +{ + wxD2DBrushData* brushData = new wxD2DBrushData(this); + brushData->CreateLinearGradientBrush(x1, y1, x2, y2, stops); + + wxGraphicsBrush brush; + brush.SetRefData(brushData); + + return brush; +} + +wxGraphicsBrush wxD2DRenderer::CreateRadialGradientBrush( + wxDouble xo, wxDouble yo, + wxDouble xc, wxDouble yc, + wxDouble radius, + const wxGraphicsGradientStops& stops) +{ + wxD2DBrushData* brushData = new wxD2DBrushData(this); + brushData->CreateRadialGradientBrush(xo, yo, xc, yc, radius, stops); + + wxGraphicsBrush brush; + brush.SetRefData(brushData); + + return brush; +} + +// create a native bitmap representation +wxGraphicsBitmap wxD2DRenderer::CreateBitmap(const wxBitmap& bitmap) +{ + wxD2DBitmapData* bitmapData = new wxD2DBitmapData(this, bitmap); + + wxGraphicsBitmap graphicsBitmap; + graphicsBitmap.SetRefData(bitmapData); + + return graphicsBitmap; +} + +// create a graphics bitmap from a native bitmap +wxGraphicsBitmap wxD2DRenderer::CreateBitmapFromNativeBitmap(void* bitmap) +{ + wxD2DBitmapData* bitmapData = new wxD2DBitmapData(this, bitmap); + + wxGraphicsBitmap graphicsBitmap; + graphicsBitmap.SetRefData(bitmapData); + + return graphicsBitmap; +} + +#if wxUSE_IMAGE +wxGraphicsBitmap wxD2DRenderer::CreateBitmapFromImage(const wxImage& image) +{ + return CreateBitmap(wxBitmap(image)); +} + +wxImage wxD2DRenderer::CreateImageFromBitmap(const wxGraphicsBitmap& bmp) +{ + return static_cast(bmp.GetNativeBitmap()) + ->GetSourceBitmap().ConvertToImage(); +} +#endif + +wxGraphicsFont wxD2DRenderer::CreateFont(const wxFont& font, const wxColour& col) +{ + wxD2DFontData* fontData = new wxD2DFontData(this, GetD2DFactory(), font, col); + + wxGraphicsFont graphicsFont; + graphicsFont.SetRefData(fontData); + + return graphicsFont; +} + +wxGraphicsFont wxD2DRenderer::CreateFont( + double size, const wxString& facename, + int flags, + const wxColour& col) +{ + return CreateFont( + wxFontInfo(size).AllFlags(flags).FaceName(facename), + col); +} + +// create a sub-image from a native image representation +wxGraphicsBitmap wxD2DRenderer::CreateSubBitmap(const wxGraphicsBitmap& bitmap, wxDouble x, wxDouble y, wxDouble w, wxDouble h) +{ + typedef wxD2DBitmapData::NativeType* NativeBitmap; + wxBitmap sourceBitmap = static_cast(bitmap.GetNativeBitmap())->GetSourceBitmap(); + return CreateBitmap(sourceBitmap.GetSubBitmap(wxRect(x, y, w, h))); +} + +wxString wxD2DRenderer::GetName() const +{ + return "direct2d"; +} + +void wxD2DRenderer::GetVersion(int* major, int* minor, int* micro) const +{ + if (wxDirect2D::HasDirect2DSupport()) + { + if (major) + *major = 1; + + if (minor) + { + switch(wxDirect2D::GetDirect2DVersion()) + { + case wxDirect2D::wxD2D_VERSION_1_0: + *minor = 0; + break; + case wxDirect2D::wxD2D_VERSION_1_1: + *minor = 1; + break; + } + } + + if (micro) + *micro = 0; + } +} + +ID2D1Factory* wxD2DRenderer::GetD2DFactory() +{ + return m_direct2dFactory; +} + +ID2D1Factory* wxGetD2DFactory(wxGraphicsRenderer* renderer) +{ + return static_cast(renderer)->GetD2DFactory(); +} + +#endif // wxUSE_GRAPHICS_DIRECT2D