1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/graphicsd2d.cpp
3 // Purpose: Implementation of Direct2D Render Context
4 // Author: Pana Alexandru <astronothing@gmail.com>
5 // Created: 2014-05-20
6 // Copyright: (c) 2014 wxWidgets development team
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 #include "wx/wxprec.h"
11
12 #if wxUSE_GRAPHICS_DIRECT2D
13
14 // Minimum supported client: Windows 8 and Platform Update for Windows 7
15 #define wxD2D_DEVICE_CONTEXT_SUPPORTED 0
16
17 // We load these functions at runtime from the d2d1.dll.
18 // However, since they are also used inside the d2d1.h header we must provide
19 // implementations matching the exact declarations. These defines ensures we
20 // are not violating the ODR rule.
21 #define D2D1CreateFactory wxD2D1CreateFactory
22 #define D2D1MakeRotateMatrix wxD2D1MakeRotateMatrix
23 #define D2D1MakeSkewMatrix wxD2D1MakeSkewMatrix
24 #define D2D1IsMatrixInvertible wxD2D1IsMatrixInvertible
25 #define D2D1InvertMatrix wxD2D1InvertMatrix
26 #if wxD2D_DEVICE_CONTEXT_SUPPORTED
27 #define D3D11CreateDevice wxD3D11CreateDevice
28 #endif
29
30 // There are clashes between the names of the member fields and parameters
31 // in the standard d2d1helper.h header resulting in C4458 with VC14,
32 // so disable this warning for this file as there is no other way to
33 // avoid it.
34 #ifdef __VISUALC__
35 #pragma warning(push)
36 #pragma warning(disable:4458) // declaration of 'xxx' hides class member
37 #endif
38
39 #include "wx/msw/private/graphicsd2d.h"
40
41 #ifdef __MINGW64_TOOLCHAIN__
42 #ifndef DWRITE_E_NOFONT
43 #define DWRITE_E_NOFONT _HRESULT_TYPEDEF_(0x88985002L)
44 #endif
45 #endif
46
47 #if wxD2D_DEVICE_CONTEXT_SUPPORTED
48 #include <d3d11.h>
49 #include <d2d1_1.h>
50 #include <dxgi1_2.h>
51 #endif
52
53 #ifdef __VISUALC__
54 #pragma warning(pop)
55 #endif
56
57 #include <float.h> // for FLT_MAX, FLT_MIN
58
59 #ifndef WX_PRECOMP
60 #include "wx/dc.h"
61 #include "wx/dcclient.h"
62 #include "wx/dcmemory.h"
63 #include "wx/image.h"
64 #include "wx/module.h"
65 #include "wx/window.h"
66 #include "wx/msw/private.h"
67 #endif // !WX_PRECOMP
68
69 #include "wx/graphics.h"
70 #include "wx/dynlib.h"
71 #include "wx/msw/ole/comimpl.h"
72 #include "wx/msw/private/comptr.h"
73 #include "wx/private/graphics.h"
74 #include "wx/stack.h"
75 #include "wx/sharedptr.h"
76
77 // This must be the last header included to only affect the DEFINE_GUID()
78 // occurrences below but not any GUIDs declared in the standard files included
79 // above.
80 #include <initguid.h>
81
82 // Generic error message for a failed direct2d operation
83 #define wxFAILED_HRESULT_MSG(result) \
84 wxString::Format("Direct2D failed with HRESULT %x", (result))
85
86 // Checks a HRESULT value for success, otherwise displays an error message and
87 // returns from the enclosing function.
88 #define wxCHECK_HRESULT_RET(result) \
89 wxCHECK_RET(SUCCEEDED(result), wxFAILED_HRESULT_MSG(result))
90
91 #define wxCHECK2_HRESULT_RET(result, returnValue) \
92 wxCHECK2_MSG(SUCCEEDED(result), return returnValue, \
93 wxFAILED_HRESULT_MSG(result))
94
95 // Variation of wxCHECK_HRESULT_RET for functions which must return a pointer
96 #define wxCHECK_HRESULT_RET_PTR(result) wxCHECK2_HRESULT_RET(result, NULL)
97
98 // Checks the precondition of wxManagedResourceHolder::AcquireResource, namely
99 // that it is bound to a manager.
100 #define wxCHECK_RESOURCE_HOLDER_PRE() \
101 { \
102 if (IsResourceAcquired()) return; \
103 wxCHECK_RET(IsBound(), \
104 "Cannot acquire a native resource without being bound to a manager"); \
105 }
106
107 // Checks the postcondition of wxManagedResourceHolder::AcquireResource, namely
108 // that it was successful in acquiring the native resource.
109 #define wxCHECK_RESOURCE_HOLDER_POST() \
110 wxCHECK_RET(m_nativeResource != NULL, "Could not acquire native resource");
111
112
113 // Helper class used to check for direct2d availability at runtime and to
114 // dynamically load the required symbols from d2d1.dll and dwrite.dll
115 class wxDirect2D
116 {
117 public:
118
119 enum wxD2DVersion
120 {
121 wxD2D_VERSION_1_0,
122 wxD2D_VERSION_1_1,
123 wxD2D_VERSION_NONE
124 };
125
Initialize()126 static bool Initialize()
127 {
128 if (!m_initialized)
129 {
130 m_hasDirect2DSupport = LoadLibraries();
131 m_initialized = true;
132 }
133
134 return m_hasDirect2DSupport;
135 }
136
HasDirect2DSupport()137 static bool HasDirect2DSupport()
138 {
139 Initialize();
140
141 return m_hasDirect2DSupport;
142 }
143
GetDirect2DVersion()144 static wxD2DVersion GetDirect2DVersion()
145 {
146 return m_D2DRuntimeVersion;
147 }
148
149 private:
LoadLibraries()150 static bool LoadLibraries()
151 {
152 if ( !m_dllDirect2d.Load(wxT("d2d1.dll"), wxDL_VERBATIM | wxDL_QUIET) )
153 return false;
154
155 if ( !m_dllDirectWrite.Load(wxT("dwrite.dll"), wxDL_VERBATIM | wxDL_QUIET) )
156 return false;
157
158 #if wxD2D_DEVICE_CONTEXT_SUPPORTED
159 if (!m_dllDirect3d.Load(wxT("d3d11.dll"), wxDL_VERBATIM | wxDL_QUIET))
160 return false;
161 #endif
162
163 #define wxLOAD_FUNC(dll, name) \
164 name = (name##_t)dll.RawGetSymbol(#name); \
165 if ( !name ) \
166 return false;
167
168 wxLOAD_FUNC(m_dllDirect2d, D2D1CreateFactory);
169 wxLOAD_FUNC(m_dllDirect2d, D2D1MakeRotateMatrix);
170 wxLOAD_FUNC(m_dllDirect2d, D2D1MakeSkewMatrix);
171 wxLOAD_FUNC(m_dllDirect2d, D2D1IsMatrixInvertible);
172 wxLOAD_FUNC(m_dllDirect2d, D2D1InvertMatrix);
173 wxLOAD_FUNC(m_dllDirectWrite, DWriteCreateFactory);
174
175 #if wxD2D_DEVICE_CONTEXT_SUPPORTED
176 wxLOAD_FUNC(m_dllDirect3d, D3D11CreateDevice);
177 m_D2DRuntimeVersion = wxD2D_VERSION_1_1;
178 #else
179 m_D2DRuntimeVersion = wxD2D_VERSION_1_0;
180 #endif
181
182 return true;
183 }
184
185 public:
186 typedef HRESULT (WINAPI *D2D1CreateFactory_t)(D2D1_FACTORY_TYPE, REFIID, CONST D2D1_FACTORY_OPTIONS*, void**);
187 static D2D1CreateFactory_t D2D1CreateFactory;
188
189 typedef void (WINAPI *D2D1MakeRotateMatrix_t)(FLOAT, D2D1_POINT_2F, D2D1_MATRIX_3X2_F*);
190 static D2D1MakeRotateMatrix_t D2D1MakeRotateMatrix;
191
192 typedef void (WINAPI *D2D1MakeSkewMatrix_t)(FLOAT, FLOAT, D2D1_POINT_2F, D2D1_MATRIX_3X2_F*);
193 static D2D1MakeSkewMatrix_t D2D1MakeSkewMatrix;
194
195 typedef BOOL (WINAPI *D2D1IsMatrixInvertible_t)(const D2D1_MATRIX_3X2_F*);
196 static D2D1IsMatrixInvertible_t D2D1IsMatrixInvertible;
197
198 typedef BOOL (WINAPI *D2D1InvertMatrix_t)(D2D1_MATRIX_3X2_F*);
199 static D2D1InvertMatrix_t D2D1InvertMatrix;
200
201 typedef HRESULT (WINAPI *DWriteCreateFactory_t)(DWRITE_FACTORY_TYPE, REFIID, IUnknown**);
202 static DWriteCreateFactory_t DWriteCreateFactory;
203
204 #if wxD2D_DEVICE_CONTEXT_SUPPORTED
205 typedef HRESULT (WINAPI *D3D11CreateDevice_t)(IDXGIAdapter*, D3D_DRIVER_TYPE, HMODULE, UINT, CONST D3D_FEATURE_LEVEL*, UINT, UINT, ID3D11Device**, D3D_FEATURE_LEVEL*, ID3D11DeviceContext**);
206 static D3D11CreateDevice_t D3D11CreateDevice;
207 #endif
208
209 private:
210 static bool m_initialized;
211 static bool m_hasDirect2DSupport;
212 static wxD2DVersion m_D2DRuntimeVersion;
213
214 static wxDynamicLibrary m_dllDirect2d;
215 static wxDynamicLibrary m_dllDirectWrite;
216 #if wxD2D_DEVICE_CONTEXT_SUPPORTED
217 static wxDynamicLibrary m_dllDirect3d;
218 #endif
219 };
220
221 // define the members
222 bool wxDirect2D::m_initialized = false;
223 bool wxDirect2D::m_hasDirect2DSupport = false;
224 wxDirect2D::wxD2DVersion wxDirect2D::m_D2DRuntimeVersion = wxD2D_VERSION_NONE;
225
226 wxDynamicLibrary wxDirect2D::m_dllDirect2d;
227 wxDynamicLibrary wxDirect2D::m_dllDirectWrite;
228 #if wxD2D_DEVICE_CONTEXT_SUPPORTED
229 wxDynamicLibrary wxDirect2D::m_dllDirect3d;
230 #endif
231
232 // define the (not yet imported) functions
233 wxDirect2D::D2D1CreateFactory_t wxDirect2D::D2D1CreateFactory = NULL;
234 wxDirect2D::D2D1MakeRotateMatrix_t wxDirect2D::D2D1MakeRotateMatrix = NULL;
235 wxDirect2D::D2D1MakeSkewMatrix_t wxDirect2D::D2D1MakeSkewMatrix = NULL;
236 wxDirect2D::D2D1IsMatrixInvertible_t wxDirect2D::D2D1IsMatrixInvertible = NULL;
237 wxDirect2D::D2D1InvertMatrix_t wxDirect2D::D2D1InvertMatrix = NULL;
238 wxDirect2D::DWriteCreateFactory_t wxDirect2D::DWriteCreateFactory = NULL;
239
240 #if wxD2D_DEVICE_CONTEXT_SUPPORTED
241 wxDirect2D::D3D11CreateDevice_t wxDirect2D::D3D11CreateDevice = NULL;
242 #endif
243
244 // define the interface GUIDs
245 DEFINE_GUID(wxIID_IWICImagingFactory,
246 0xec5ec8a9, 0xc395, 0x4314, 0x9c, 0x77, 0x54, 0xd7, 0xa9, 0x35, 0xff, 0x70);
247
248 DEFINE_GUID(wxIID_ID2D1Factory,
249 0x06152247, 0x6f50, 0x465a, 0x92, 0x45, 0x11, 0x8b, 0xfd, 0x3b, 0x60, 0x07);
250
251 DEFINE_GUID(wxIID_IDWriteFactory,
252 0xb859ee5a, 0xd838, 0x4b5b, 0xa2, 0xe8, 0x1a, 0xdc, 0x7d, 0x93, 0xdb, 0x48);
253
254 DEFINE_GUID(wxIID_IWICBitmapSource,
255 0x00000120, 0xa8f2, 0x4877, 0xba, 0x0a, 0xfd, 0x2b, 0x66, 0x45, 0xfb, 0x94);
256
257 DEFINE_GUID(GUID_WICPixelFormat32bppPBGRA,
258 0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x10);
259
260 DEFINE_GUID(GUID_WICPixelFormat32bppBGR,
261 0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0e);
262
263 #if wxD2D_DEVICE_CONTEXT_SUPPORTED
264 DEFINE_GUID(IID_IDXGIDevice,
265 0x54ec77fa, 0x1377, 0x44e6, 0x8c, 0x32, 0x88, 0xfd, 0x5f, 0x44, 0xc8, 0x4c);
266 #endif
267
268 #ifndef CLSID_WICImagingFactory
269 DEFINE_GUID(CLSID_WICImagingFactory,
270 0xcacaf262, 0x9370, 0x4615, 0xa1, 0x3b, 0x9f, 0x55, 0x39, 0xda, 0x4c, 0xa);
271 #endif
272
273 #if wxUSE_PRIVATE_FONTS
274 DEFINE_GUID(wxIID_IDWriteFontFileEnumerator,
275 0x72755049, 0x5ff7, 0x435d, 0x83, 0x48, 0x4b, 0xe9, 0x7c, 0xfa, 0x6c, 0x7c);
276
277 DEFINE_GUID(wxIID_IDWriteFontCollectionLoader,
278 0xcca920e4, 0x52f0, 0x492b, 0xbf, 0xa8, 0x29, 0xc7, 0x2e, 0xe0, 0xa4, 0x68);
279 #endif // wxUSE_PRIVATE_FONTS
280
281 // Implementation of the Direct2D functions
wxD2D1CreateFactory(D2D1_FACTORY_TYPE factoryType,REFIID riid,CONST D2D1_FACTORY_OPTIONS * pFactoryOptions,void ** ppIFactory)282 HRESULT WINAPI wxD2D1CreateFactory(
283 D2D1_FACTORY_TYPE factoryType,
284 REFIID riid,
285 CONST D2D1_FACTORY_OPTIONS *pFactoryOptions,
286 void **ppIFactory)
287 {
288 if (!wxDirect2D::Initialize())
289 return S_FALSE;
290
291 return wxDirect2D::D2D1CreateFactory(
292 factoryType,
293 riid,
294 pFactoryOptions,
295 ppIFactory);
296 }
297
wxD2D1MakeRotateMatrix(FLOAT angle,D2D1_POINT_2F center,D2D1_MATRIX_3X2_F * matrix)298 void WINAPI wxD2D1MakeRotateMatrix(
299 FLOAT angle,
300 D2D1_POINT_2F center,
301 D2D1_MATRIX_3X2_F *matrix)
302 {
303 if (!wxDirect2D::Initialize())
304 return;
305
306 wxDirect2D::D2D1MakeRotateMatrix(angle, center, matrix);
307 }
308
wxD2D1MakeSkewMatrix(FLOAT angleX,FLOAT angleY,D2D1_POINT_2F center,D2D1_MATRIX_3X2_F * matrix)309 void WINAPI wxD2D1MakeSkewMatrix(
310 FLOAT angleX,
311 FLOAT angleY,
312 D2D1_POINT_2F center,
313 D2D1_MATRIX_3X2_F *matrix)
314 {
315 if (!wxDirect2D::Initialize())
316 return;
317
318 wxDirect2D::D2D1MakeSkewMatrix(angleX, angleY, center, matrix);
319 }
320
wxD2D1IsMatrixInvertible(const D2D1_MATRIX_3X2_F * matrix)321 BOOL WINAPI wxD2D1IsMatrixInvertible(
322 const D2D1_MATRIX_3X2_F *matrix)
323 {
324 if (!wxDirect2D::Initialize())
325 return FALSE;
326
327 return wxDirect2D::D2D1IsMatrixInvertible(matrix);
328 }
329
wxD2D1InvertMatrix(D2D1_MATRIX_3X2_F * matrix)330 BOOL WINAPI wxD2D1InvertMatrix(
331 D2D1_MATRIX_3X2_F *matrix)
332 {
333 if (!wxDirect2D::Initialize())
334 return FALSE;
335
336 return wxDirect2D::D2D1InvertMatrix(matrix);
337 }
338
339 #if wxD2D_DEVICE_CONTEXT_SUPPORTED
wxD3D11CreateDevice(IDXGIAdapter * pAdapter,D3D_DRIVER_TYPE DriverType,HMODULE Software,UINT Flags,CONST D3D_FEATURE_LEVEL * pFeatureLevels,UINT FeatureLevels,UINT SDKVersion,ID3D11Device ** ppDevice,D3D_FEATURE_LEVEL * pFeatureLevel,ID3D11DeviceContext ** ppImmediateContext)340 HRESULT WINAPI wxD3D11CreateDevice(
341 IDXGIAdapter* pAdapter,
342 D3D_DRIVER_TYPE DriverType,
343 HMODULE Software,
344 UINT Flags,
345 CONST D3D_FEATURE_LEVEL* pFeatureLevels,
346 UINT FeatureLevels,
347 UINT SDKVersion,
348 ID3D11Device** ppDevice,
349 D3D_FEATURE_LEVEL* pFeatureLevel,
350 ID3D11DeviceContext** ppImmediateContext)
351 {
352 if (!wxDirect2D::Initialize())
353 return S_FALSE;
354
355 return wxDirect2D::D3D11CreateDevice(
356 pAdapter,
357 DriverType,
358 Software,
359 Flags,
360 pFeatureLevels,
361 FeatureLevels,
362 SDKVersion,
363 ppDevice,
364 pFeatureLevel,
365 ppImmediateContext);
366 }
367 #endif
368
369 #if wxUSE_PRIVATE_FONTS
370
371 // This function is defined in src/msw/font.cpp.
372 extern const wxArrayString& wxGetPrivateFontFileNames();
373
374 namespace
375 {
376 wxCOMPtr<IDWriteFontCollection> gs_pPrivateFontCollection;
377
378 typedef unsigned int wxDirect2DFontKey;
379
380 class wxDirect2DFontFileEnumerator : public IDWriteFontFileEnumerator
381 {
382 public:
wxDirect2DFontFileEnumerator(IDWriteFactory * pFactory,const wxArrayString & fontCollection)383 wxDirect2DFontFileEnumerator(IDWriteFactory* pFactory, const wxArrayString& fontCollection)
384 : m_factory(pFactory)
385 , m_filePaths(fontCollection)
386 , m_nextIndex(0)
387 {
388 }
389
~wxDirect2DFontFileEnumerator()390 virtual ~wxDirect2DFontFileEnumerator()
391 {
392 }
393
394 // IDWriteFontFileEnumerator methods
MoveNext(BOOL * pHasCurrentFile)395 virtual wxSTDMETHODIMP MoveNext(BOOL* pHasCurrentFile) wxOVERRIDE
396 {
397 HRESULT hr = S_OK;
398
399 *pHasCurrentFile = FALSE;
400 m_currentFile.reset();
401
402 if ( m_nextIndex < m_filePaths.size() )
403 {
404 hr = m_factory->CreateFontFileReference(m_filePaths[m_nextIndex].wc_str(), NULL, &m_currentFile);
405 if ( SUCCEEDED(hr) )
406 {
407 *pHasCurrentFile = TRUE;
408 ++m_nextIndex;
409 }
410 }
411
412 return hr;
413 }
414
GetCurrentFontFile(IDWriteFontFile ** ppFontFile)415 virtual wxSTDMETHODIMP GetCurrentFontFile(IDWriteFontFile** ppFontFile) wxOVERRIDE
416 {
417 if ( m_currentFile )
418 {
419 m_currentFile.get()->AddRef();
420 }
421 *ppFontFile = m_currentFile;
422
423 return m_currentFile ? S_OK : E_FAIL;
424 }
425
426 // IUnknown methods
427 DECLARE_IUNKNOWN_METHODS;
428
429 private:
430 wxCOMPtr<IDWriteFactory> m_factory;
431 wxCOMPtr<IDWriteFontFile> m_currentFile;
432 wxArrayString m_filePaths;
433 size_t m_nextIndex;
434
435 wxDECLARE_NO_COPY_CLASS(wxDirect2DFontFileEnumerator);
436 };
437
438 BEGIN_IID_TABLE(wxDirect2DFontFileEnumerator)
439 ADD_IID(Unknown)
440 ADD_RAW_IID(wxIID_IDWriteFontFileEnumerator)
441 END_IID_TABLE;
442
443 IMPLEMENT_IUNKNOWN_METHODS(wxDirect2DFontFileEnumerator)
444
445 class wxDirect2DFontCollectionLoader : public IDWriteFontCollectionLoader
446 {
447 public:
wxDirect2DFontCollectionLoader()448 wxDirect2DFontCollectionLoader()
449 {
450 ms_isInitialized = true;
451 }
452
~wxDirect2DFontCollectionLoader()453 virtual ~wxDirect2DFontCollectionLoader()
454 {
455 }
456
457 // IDWriteFontCollectionLoader methods
CreateEnumeratorFromKey(IDWriteFactory * pFactory,void const * pCollectionKey,UINT32 collectionKeySize,IDWriteFontFileEnumerator ** pFontFileEnumerator)458 virtual wxSTDMETHODIMP CreateEnumeratorFromKey(IDWriteFactory* pFactory,
459 void const* pCollectionKey, UINT32 collectionKeySize,
460 IDWriteFontFileEnumerator** pFontFileEnumerator) wxOVERRIDE
461 {
462 if ( !pFontFileEnumerator )
463 return E_INVALIDARG;
464
465 *pFontFileEnumerator = NULL;
466
467 if ( collectionKeySize != sizeof(wxDirect2DFontKey) )
468 return E_INVALIDARG;
469
470 wxDirect2DFontKey key = *static_cast<wxDirect2DFontKey const*>(pCollectionKey);
471 if ( key != ms_key )
472 return E_INVALIDARG;
473
474 if ( ms_fontList.empty() )
475 return E_INVALIDARG;
476
477 wxDirect2DFontFileEnumerator* pEnumerator = new wxDirect2DFontFileEnumerator(pFactory, ms_fontList);
478 if ( !pEnumerator )
479 return E_OUTOFMEMORY;
480
481 pEnumerator->AddRef();
482 *pFontFileEnumerator = pEnumerator;
483
484 return S_OK;
485 }
486
487 // Singleton loader instance
GetLoader()488 static IDWriteFontCollectionLoader* GetLoader()
489 {
490 static wxCOMPtr<wxDirect2DFontCollectionLoader> instance(new wxDirect2DFontCollectionLoader());
491
492 return instance;
493 }
494
IsInitialized()495 static bool IsInitialized()
496 {
497 return ms_isInitialized;
498 }
499
SetFontList(const wxArrayString & list)500 static wxDirect2DFontKey SetFontList(const wxArrayString& list)
501 {
502 ms_fontList = list;
503 // Every time font collection is changed, generate unique key
504 return ++ms_key;
505 }
506
GetFontList()507 static const wxArrayString& GetFontList()
508 {
509 return ms_fontList;
510 }
511
512 // IUnknown methods
513 DECLARE_IUNKNOWN_METHODS;
514
515 private:
516 static bool ms_isInitialized;
517 static wxArrayString ms_fontList;
518 static wxDirect2DFontKey ms_key;
519
520 wxDECLARE_NO_COPY_CLASS(wxDirect2DFontCollectionLoader);
521 };
522
523 BEGIN_IID_TABLE(wxDirect2DFontCollectionLoader)
524 ADD_IID(Unknown)
525 ADD_RAW_IID(wxIID_IDWriteFontCollectionLoader)
526 END_IID_TABLE;
527
528 IMPLEMENT_IUNKNOWN_METHODS(wxDirect2DFontCollectionLoader)
529
530 bool wxDirect2DFontCollectionLoader::ms_isInitialized(false);
531 wxArrayString wxDirect2DFontCollectionLoader::ms_fontList;
532 wxDirect2DFontKey wxDirect2DFontCollectionLoader::ms_key(0);
533 } // anonymous namespace
534
535 #endif // wxUSE_PRIVATE_FONTS
536
537 static IWICImagingFactory* gs_WICImagingFactory = NULL;
538
wxWICImagingFactory()539 IWICImagingFactory* wxWICImagingFactory()
540 {
541 if (gs_WICImagingFactory == NULL) {
542 HRESULT hr = CoCreateInstance(
543 CLSID_WICImagingFactory,
544 NULL,
545 CLSCTX_INPROC_SERVER,
546 wxIID_IWICImagingFactory,
547 (LPVOID*)&gs_WICImagingFactory);
548 wxCHECK_HRESULT_RET_PTR(hr);
549 }
550 return gs_WICImagingFactory;
551 }
552
553 static ID2D1Factory* gs_ID2D1Factory = NULL;
554
wxD2D1Factory()555 ID2D1Factory* wxD2D1Factory()
556 {
557 if (!wxDirect2D::Initialize())
558 return NULL;
559
560 if (gs_ID2D1Factory == NULL)
561 {
562 D2D1_FACTORY_OPTIONS factoryOptions = {D2D1_DEBUG_LEVEL_NONE};
563
564 // According to
565 // https://msdn.microsoft.com/en-us/library/windows/desktop/ee794287(v=vs.85).aspx
566 // the Direct2D Debug Layer is only available starting with Windows 8
567 // and Visual Studio 2012.
568 #if defined(__WXDEBUG__) && defined(__VISUALC__) && wxCHECK_VISUALC_VERSION(11)
569 if ( wxGetWinVersion() >= wxWinVersion_8 )
570 {
571 factoryOptions.debugLevel = D2D1_DEBUG_LEVEL_WARNING;
572 }
573 #endif //__WXDEBUG__
574
575 HRESULT hr = wxDirect2D::D2D1CreateFactory(
576 D2D1_FACTORY_TYPE_SINGLE_THREADED,
577 wxIID_ID2D1Factory,
578 &factoryOptions,
579 reinterpret_cast<void**>(&gs_ID2D1Factory)
580 );
581 wxCHECK_HRESULT_RET_PTR(hr);
582 }
583 return gs_ID2D1Factory;
584 }
585
586 static IDWriteFactory* gs_IDWriteFactory = NULL;
587
wxDWriteFactory()588 IDWriteFactory* wxDWriteFactory()
589 {
590 if (!wxDirect2D::Initialize())
591 return NULL;
592
593 if (gs_IDWriteFactory == NULL)
594 {
595 wxDirect2D::DWriteCreateFactory(
596 DWRITE_FACTORY_TYPE_SHARED,
597 wxIID_IDWriteFactory,
598 reinterpret_cast<IUnknown**>(&gs_IDWriteFactory)
599 );
600 #if wxUSE_PRIVATE_FONTS
601 // Register our custom font loader
602 HRESULT hr = gs_IDWriteFactory->RegisterFontCollectionLoader(wxDirect2DFontCollectionLoader::GetLoader());
603 if ( FAILED(hr) )
604 {
605 wxLogError(_("Could not register custom DirectWrite font loader."));
606 }
607 #endif // wxUSE_PRIVATE_FONTS
608 }
609 return gs_IDWriteFactory;
610 }
611
612 extern WXDLLIMPEXP_DATA_CORE(wxGraphicsPen) wxNullGraphicsPen;
613 extern WXDLLIMPEXP_DATA_CORE(wxGraphicsBrush) wxNullGraphicsBrush;
614
615 // We use the notion of a context supplier because the context
616 // needed to create Direct2D resources (namely the RenderTarget)
617 // is itself device-dependent and might change during the lifetime
618 // of the resources which were created from it.
619 template <typename C>
620 class wxContextSupplier
621 {
622 public:
623 typedef C ContextType;
624
625 virtual C GetContext() = 0;
626 };
627
628 typedef wxContextSupplier<ID2D1RenderTarget*> wxD2DContextSupplier;
629
630 // A resource holder manages a generic resource by acquiring
631 // and releasing it on demand.
632 class wxResourceHolder
633 {
634 public:
635 // Acquires the managed resource if necessary (not already acquired)
636 virtual void AcquireResource() = 0;
637
638 // Releases the managed resource
639 virtual void ReleaseResource() = 0;
640
641 // Checks if the resources was previously acquired
642 virtual bool IsResourceAcquired() = 0;
643
644 // Returns the managed resource or NULL if the resources
645 // was not previously acquired
646 virtual void* GetResource() = 0;
647
~wxResourceHolder()648 virtual ~wxResourceHolder() {}
649 };
650
651 class wxD2DResourceManager;
652
653 class wxD2DManagedObject
654 {
655 public:
656 virtual void Bind(wxD2DResourceManager* manager) = 0;
657 virtual void UnBind() = 0;
658 virtual bool IsBound() = 0;
659 virtual wxD2DResourceManager* GetManager() = 0;
660
~wxD2DManagedObject()661 virtual ~wxD2DManagedObject() {}
662 };
663
664 class wxManagedResourceHolder : public wxResourceHolder, public wxD2DManagedObject
665 {
666 public:
~wxManagedResourceHolder()667 virtual ~wxManagedResourceHolder() {}
668 };
669
670 // A Direct2D resource manager handles the device-dependent
671 // resource holders attached to it by requesting them to
672 // release their resources when the API invalidates.
673 // NOTE: We're using a list because we expect to have multiple
674 // insertions but very rarely a traversal (if ever).
675 WX_DECLARE_LIST(wxManagedResourceHolder, wxManagedResourceListType);
676 #include <wx/listimpl.cpp>
677 WX_DEFINE_LIST(wxManagedResourceListType);
678
679 class wxD2DResourceManager: public wxD2DContextSupplier
680 {
681 public:
RegisterResourceHolder(wxManagedResourceHolder * resourceHolder)682 void RegisterResourceHolder(wxManagedResourceHolder* resourceHolder)
683 {
684 m_resources.push_back(resourceHolder);
685 }
686
UnregisterResourceHolder(wxManagedResourceHolder * resourceHolder)687 void UnregisterResourceHolder(wxManagedResourceHolder* resourceHolder)
688 {
689 m_resources.remove(resourceHolder);
690 }
691
ReleaseResources()692 void ReleaseResources()
693 {
694 wxManagedResourceListType::iterator it;
695 for (it = m_resources.begin(); it != m_resources.end(); ++it)
696 {
697 (*it)->ReleaseResource();
698 }
699
700 // Check that all resources were released
701 for (it = m_resources.begin(); it != m_resources.end(); ++it)
702 {
703 wxCHECK_RET(!(*it)->IsResourceAcquired(), "One or more device-dependent resources failed to release");
704 }
705 }
706
~wxD2DResourceManager()707 virtual ~wxD2DResourceManager()
708 {
709 while (!m_resources.empty())
710 {
711 m_resources.front()->ReleaseResource();
712 m_resources.front()->UnBind();
713 }
714 }
715
716 private:
717 wxManagedResourceListType m_resources;
718 };
719
720 // A Direct2D resource holder manages device dependent resources
721 // by storing any information necessary for acquiring the resource
722 // and releasing the resource when the API invalidates it.
723 template<typename T>
724 class wxD2DResourceHolder: public wxManagedResourceHolder
725 {
726 public:
wxD2DResourceHolder()727 wxD2DResourceHolder() : m_resourceManager(NULL)
728 {
729 }
730
~wxD2DResourceHolder()731 virtual ~wxD2DResourceHolder()
732 {
733 UnBind();
734 ReleaseResource();
735 }
736
IsResourceAcquired()737 bool IsResourceAcquired() wxOVERRIDE
738 {
739 return m_nativeResource != NULL;
740 }
741
GetResource()742 void* GetResource() wxOVERRIDE
743 {
744 return GetD2DResource();
745 }
746
GetD2DResource()747 wxCOMPtr<T>& GetD2DResource()
748 {
749 if (!IsResourceAcquired())
750 {
751 AcquireResource();
752 }
753
754 return m_nativeResource;
755 }
756
AcquireResource()757 void AcquireResource() wxOVERRIDE
758 {
759 wxCHECK_RESOURCE_HOLDER_PRE();
760
761 DoAcquireResource();
762
763 wxCHECK_RESOURCE_HOLDER_POST();
764 }
765
ReleaseResource()766 void ReleaseResource() wxOVERRIDE
767 {
768 m_nativeResource.reset();
769 }
770
GetContext()771 wxD2DContextSupplier::ContextType GetContext()
772 {
773 return m_resourceManager->GetContext();
774 }
775
Bind(wxD2DResourceManager * manager)776 void Bind(wxD2DResourceManager* manager) wxOVERRIDE
777 {
778 if (IsBound())
779 return;
780
781 m_resourceManager = manager;
782 m_resourceManager->RegisterResourceHolder(this);
783 }
784
UnBind()785 void UnBind() wxOVERRIDE
786 {
787 if (!IsBound())
788 return;
789
790 m_resourceManager->UnregisterResourceHolder(this);
791 m_resourceManager = NULL;
792 }
793
IsBound()794 bool IsBound() wxOVERRIDE
795 {
796 return m_resourceManager != NULL;
797 }
798
GetManager()799 wxD2DResourceManager* GetManager() wxOVERRIDE
800 {
801 return m_resourceManager;
802 }
803
804 protected:
805 virtual void DoAcquireResource() = 0;
806
807 private:
808 wxD2DResourceManager* m_resourceManager;
809
810 protected:
811 wxCOMPtr<T> m_nativeResource;
812 };
813
814 // Used as super class for graphics data objects
815 // to forward the bindings to their internal resource holder.
816 class wxD2DManagedGraphicsData : public wxD2DManagedObject
817 {
818 public:
Bind(wxD2DResourceManager * manager)819 void Bind(wxD2DResourceManager* manager) wxOVERRIDE
820 {
821 GetManagedObject()->Bind(manager);
822 }
823
UnBind()824 void UnBind() wxOVERRIDE
825 {
826 GetManagedObject()->UnBind();
827 }
828
IsBound()829 bool IsBound() wxOVERRIDE
830 {
831 return GetManagedObject()->IsBound();
832 }
833
GetManager()834 wxD2DResourceManager* GetManager() wxOVERRIDE
835 {
836 return GetManagedObject()->GetManager();
837 }
838
839 virtual wxD2DManagedObject* GetManagedObject() = 0;
840
~wxD2DManagedGraphicsData()841 ~wxD2DManagedGraphicsData() {}
842 };
843
wxD2DConvertPenCap(wxPenCap cap)844 D2D1_CAP_STYLE wxD2DConvertPenCap(wxPenCap cap)
845 {
846 switch (cap)
847 {
848 case wxCAP_ROUND:
849 return D2D1_CAP_STYLE_ROUND;
850 case wxCAP_PROJECTING:
851 return D2D1_CAP_STYLE_SQUARE;
852 case wxCAP_BUTT:
853 return D2D1_CAP_STYLE_FLAT;
854 case wxCAP_INVALID:
855 return D2D1_CAP_STYLE_FLAT;
856 }
857
858 wxFAIL_MSG("unknown pen cap");
859 return D2D1_CAP_STYLE_FLAT;
860 }
861
wxD2DConvertPenJoin(wxPenJoin join)862 D2D1_LINE_JOIN wxD2DConvertPenJoin(wxPenJoin join)
863 {
864 switch (join)
865 {
866 case wxJOIN_BEVEL:
867 return D2D1_LINE_JOIN_BEVEL;
868 case wxJOIN_MITER:
869 return D2D1_LINE_JOIN_MITER;
870 case wxJOIN_ROUND:
871 return D2D1_LINE_JOIN_ROUND;
872 case wxJOIN_INVALID:
873 return D2D1_LINE_JOIN_MITER;
874 }
875
876 wxFAIL_MSG("unknown pen join");
877 return D2D1_LINE_JOIN_MITER;
878 }
879
wxD2DConvertPenStyle(wxPenStyle dashStyle)880 D2D1_DASH_STYLE wxD2DConvertPenStyle(wxPenStyle dashStyle)
881 {
882 switch (dashStyle)
883 {
884 case wxPENSTYLE_SOLID:
885 return D2D1_DASH_STYLE_SOLID;
886 case wxPENSTYLE_DOT:
887 return D2D1_DASH_STYLE_DOT;
888 case wxPENSTYLE_LONG_DASH:
889 return D2D1_DASH_STYLE_DASH;
890 case wxPENSTYLE_SHORT_DASH:
891 return D2D1_DASH_STYLE_DASH;
892 case wxPENSTYLE_DOT_DASH:
893 return D2D1_DASH_STYLE_DASH_DOT;
894 case wxPENSTYLE_USER_DASH:
895 return D2D1_DASH_STYLE_CUSTOM;
896
897 // NB: These styles cannot be converted to a D2D1_DASH_STYLE
898 // and must be handled separately.
899 case wxPENSTYLE_TRANSPARENT:
900 wxFALLTHROUGH;
901 case wxPENSTYLE_INVALID:
902 wxFALLTHROUGH;
903 case wxPENSTYLE_STIPPLE_MASK_OPAQUE:
904 wxFALLTHROUGH;
905 case wxPENSTYLE_STIPPLE_MASK:
906 wxFALLTHROUGH;
907 case wxPENSTYLE_STIPPLE:
908 wxFALLTHROUGH;
909 case wxPENSTYLE_BDIAGONAL_HATCH:
910 wxFALLTHROUGH;
911 case wxPENSTYLE_CROSSDIAG_HATCH:
912 wxFALLTHROUGH;
913 case wxPENSTYLE_FDIAGONAL_HATCH:
914 wxFALLTHROUGH;
915 case wxPENSTYLE_CROSS_HATCH:
916 wxFALLTHROUGH;
917 case wxPENSTYLE_HORIZONTAL_HATCH:
918 wxFALLTHROUGH;
919 case wxPENSTYLE_VERTICAL_HATCH:
920 return D2D1_DASH_STYLE_SOLID;
921 }
922
923 wxFAIL_MSG("unknown pen style");
924 return D2D1_DASH_STYLE_SOLID;
925 }
926
wxD2DConvertColour(wxColour colour)927 D2D1_COLOR_F wxD2DConvertColour(wxColour colour)
928 {
929 return D2D1::ColorF(
930 colour.Red() / 255.0f,
931 colour.Green() / 255.0f,
932 colour.Blue() / 255.0f,
933 colour.Alpha() / 255.0f);
934 }
935
936 #if wxD2D_DEVICE_CONTEXT_SUPPORTED
wxD2DCompositionModeSupported(wxCompositionMode compositionMode)937 bool wxD2DCompositionModeSupported(wxCompositionMode compositionMode)
938 {
939 if (compositionMode == wxCOMPOSITION_CLEAR || compositionMode == wxCOMPOSITION_INVALID)
940 {
941 return false;
942 }
943
944 return true;
945 }
946
wxD2DConvertCompositionMode(wxCompositionMode compositionMode)947 D2D1_COMPOSITE_MODE wxD2DConvertCompositionMode(wxCompositionMode compositionMode)
948 {
949 switch (compositionMode)
950 {
951 case wxCOMPOSITION_SOURCE:
952 return D2D1_COMPOSITE_MODE_SOURCE_COPY;
953 case wxCOMPOSITION_OVER:
954 return D2D1_COMPOSITE_MODE_SOURCE_OVER;
955 case wxCOMPOSITION_IN:
956 return D2D1_COMPOSITE_MODE_SOURCE_IN;
957 case wxCOMPOSITION_OUT:
958 return D2D1_COMPOSITE_MODE_SOURCE_OUT;
959 case wxCOMPOSITION_ATOP:
960 return D2D1_COMPOSITE_MODE_SOURCE_ATOP;
961 case wxCOMPOSITION_DEST_OVER:
962 return D2D1_COMPOSITE_MODE_DESTINATION_OVER;
963 case wxCOMPOSITION_DEST_IN:
964 return D2D1_COMPOSITE_MODE_DESTINATION_IN;
965 case wxCOMPOSITION_DEST_OUT:
966 return D2D1_COMPOSITE_MODE_DESTINATION_OUT;
967 case wxCOMPOSITION_DEST_ATOP:
968 return D2D1_COMPOSITE_MODE_DESTINATION_ATOP;
969 case wxCOMPOSITION_XOR:
970 return D2D1_COMPOSITE_MODE_XOR;
971 case wxCOMPOSITION_ADD:
972 return D2D1_COMPOSITE_MODE_PLUS;
973
974 // unsupported composition modes
975 case wxCOMPOSITION_DEST:
976 wxFALLTHROUGH;
977 case wxCOMPOSITION_CLEAR:
978 wxFALLTHROUGH;
979 case wxCOMPOSITION_INVALID:
980 return D2D1_COMPOSITE_MODE_SOURCE_COPY;
981 }
982
983 wxFAIL_MSG("unknown composition mode");
984 return D2D1_COMPOSITE_MODE_SOURCE_COPY;
985 }
986 #endif // wxD2D_DEVICE_CONTEXT_SUPPORTED
987
988 // Direct2D 1.1 introduces a new enum for specifying the interpolation quality
989 // which is only used with the ID2D1DeviceContext::DrawImage method.
990 #if wxD2D_DEVICE_CONTEXT_SUPPORTED
wxD2DConvertInterpolationMode(wxInterpolationQuality interpolationQuality)991 D2D1_INTERPOLATION_MODE wxD2DConvertInterpolationMode(wxInterpolationQuality interpolationQuality)
992 {
993 switch (interpolationQuality)
994 {
995 case wxINTERPOLATION_DEFAULT:
996 wxFALLTHROUGH;
997 case wxINTERPOLATION_NONE:
998 wxFALLTHROUGH;
999 case wxINTERPOLATION_FAST:
1000 return D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
1001 case wxINTERPOLATION_GOOD:
1002 return D2D1_INTERPOLATION_MODE_LINEAR;
1003 case wxINTERPOLATION_BEST:
1004 return D2D1_INTERPOLATION_MODE_CUBIC;
1005 }
1006
1007 wxFAIL_MSG("unknown interpolation quality");
1008 return D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
1009 }
1010 #endif // wxD2D_DEVICE_CONTEXT_SUPPORTED
1011
wxD2DConvertBitmapInterpolationMode(wxInterpolationQuality interpolationQuality)1012 D2D1_BITMAP_INTERPOLATION_MODE wxD2DConvertBitmapInterpolationMode(wxInterpolationQuality interpolationQuality)
1013 {
1014 switch (interpolationQuality)
1015 {
1016 case wxINTERPOLATION_DEFAULT:
1017 wxFALLTHROUGH;
1018 case wxINTERPOLATION_NONE:
1019 wxFALLTHROUGH;
1020 case wxINTERPOLATION_FAST:
1021 wxFALLTHROUGH;
1022 case wxINTERPOLATION_GOOD:
1023 return D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
1024 case wxINTERPOLATION_BEST:
1025 return D2D1_BITMAP_INTERPOLATION_MODE_LINEAR;
1026 }
1027
1028 wxFAIL_MSG("unknown interpolation quality");
1029 return D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
1030 }
1031
wxD2DConvertRect(const wxRect & rect)1032 D2D1_RECT_F wxD2DConvertRect(const wxRect& rect)
1033 {
1034 return D2D1::RectF(rect.GetLeft(), rect.GetTop(), rect.GetRight(), rect.GetBottom());
1035 }
1036
wxD2DConvertRegionToGeometry(ID2D1Factory * direct2dFactory,const wxRegion & region)1037 wxCOMPtr<ID2D1Geometry> wxD2DConvertRegionToGeometry(ID2D1Factory* direct2dFactory, const wxRegion& region)
1038 {
1039 // Build the array of geometries
1040 HRESULT hr;
1041 int i;
1042 ID2D1Geometry** geometries;
1043 int rectCount;
1044 if ( region.IsEmpty() )
1045 {
1046 // Empty region is skipped by iterator
1047 // so we have to create it in a special way.
1048 rectCount = 1;
1049 geometries = new ID2D1Geometry*[rectCount];
1050
1051 geometries[0] = NULL;
1052 hr = direct2dFactory->CreateRectangleGeometry(
1053 D2D1::RectF(0.0F, 0.0F, 0.0F, 0.0F),
1054 (ID2D1RectangleGeometry**)(&geometries[0]));
1055 wxFAILED_HRESULT_MSG(hr);
1056 }
1057 else
1058 {
1059 // Count the number of rectangles which compose the region
1060 wxRegionIterator regionIterator(region);
1061 rectCount = 0;
1062 while(regionIterator++)
1063 rectCount++;
1064
1065 geometries = new ID2D1Geometry*[rectCount];
1066 regionIterator.Reset(region);
1067
1068 i = 0;
1069 while(regionIterator)
1070 {
1071 geometries[i] = NULL;
1072
1073 wxRect rect = regionIterator.GetRect();
1074 rect.SetWidth(rect.GetWidth() + 1);
1075 rect.SetHeight(rect.GetHeight() + 1);
1076
1077 hr = direct2dFactory->CreateRectangleGeometry(
1078 wxD2DConvertRect(rect),
1079 (ID2D1RectangleGeometry**)(&geometries[i]));
1080 wxFAILED_HRESULT_MSG(hr);
1081
1082 i++;
1083 ++regionIterator;
1084 }
1085 }
1086
1087 // Create a geometry group to hold all the rectangles
1088 wxCOMPtr<ID2D1GeometryGroup> resultGeometry;
1089 hr = direct2dFactory->CreateGeometryGroup(
1090 D2D1_FILL_MODE_WINDING,
1091 geometries,
1092 rectCount,
1093 &resultGeometry);
1094 wxFAILED_HRESULT_MSG(hr);
1095
1096 // Cleanup temporaries
1097 for (i = 0; i < rectCount; ++i)
1098 {
1099 geometries[i]->Release();
1100 }
1101
1102 delete[] geometries;
1103
1104 return wxCOMPtr<ID2D1Geometry>(resultGeometry);
1105 }
1106
1107 class wxD2DOffsetHelper
1108 {
1109 public:
wxD2DOffsetHelper(wxGraphicsContext * g)1110 explicit wxD2DOffsetHelper(wxGraphicsContext* g)
1111 : m_context(g)
1112 {
1113 m_offset = 0;
1114 if (m_context->ShouldOffset())
1115 {
1116 const wxGraphicsMatrix matrix(m_context->GetTransform());
1117 double x = m_context->GetContentScaleFactor(), y = x;
1118 matrix.TransformDistance(&x, &y);
1119 m_offset = 0.5 / wxMin(fabs(x), fabs(y));
1120 m_context->Translate(m_offset, m_offset);
1121 }
1122 }
1123
~wxD2DOffsetHelper()1124 ~wxD2DOffsetHelper()
1125 {
1126 if (m_offset > 0)
1127 {
1128 m_context->Translate(-m_offset, -m_offset);
1129 }
1130 }
1131
1132 private:
1133 wxGraphicsContext* m_context;
1134 double m_offset;
1135 };
1136
operator ==(const D2D1::Matrix3x2F & lhs,const D2D1::Matrix3x2F & rhs)1137 bool operator==(const D2D1::Matrix3x2F& lhs, const D2D1::Matrix3x2F& rhs)
1138 {
1139 return
1140 lhs._11 == rhs._11 && lhs._12 == rhs._12 &&
1141 lhs._21 == rhs._21 && lhs._22 == rhs._22 &&
1142 lhs._31 == rhs._31 && lhs._32 == rhs._32;
1143 }
1144
1145 //-----------------------------------------------------------------------------
1146 // wxD2DMatrixData declaration
1147 //-----------------------------------------------------------------------------
1148
1149 class wxD2DMatrixData : public wxGraphicsMatrixData
1150 {
1151 public:
1152 wxD2DMatrixData(wxGraphicsRenderer* renderer);
1153 wxD2DMatrixData(wxGraphicsRenderer* renderer, const D2D1::Matrix3x2F& matrix);
1154
1155 virtual wxGraphicsObjectRefData* Clone() const wxOVERRIDE;
1156
1157 void Concat(const wxGraphicsMatrixData* t) wxOVERRIDE;
1158
1159 void Set(wxDouble a = 1.0, wxDouble b = 0.0, wxDouble c = 0.0, wxDouble d = 1.0,
1160 wxDouble tx = 0.0, wxDouble ty = 0.0) wxOVERRIDE;
1161
1162 void Get(wxDouble* a = NULL, wxDouble* b = NULL, wxDouble* c = NULL,
1163 wxDouble* d = NULL, wxDouble* tx = NULL, wxDouble* ty = NULL) const wxOVERRIDE;
1164
1165 void Invert() wxOVERRIDE;
1166
1167 bool IsEqual(const wxGraphicsMatrixData* t) const wxOVERRIDE;
1168
1169 bool IsIdentity() const wxOVERRIDE;
1170
1171 void Translate(wxDouble dx, wxDouble dy) wxOVERRIDE;
1172
1173 void Scale(wxDouble xScale, wxDouble yScale) wxOVERRIDE;
1174
1175 void Rotate(wxDouble angle) wxOVERRIDE;
1176
1177 void TransformPoint(wxDouble* x, wxDouble* y) const wxOVERRIDE;
1178
1179 void TransformDistance(wxDouble* dx, wxDouble* dy) const wxOVERRIDE;
1180
1181 void* GetNativeMatrix() const wxOVERRIDE;
1182
1183 D2D1::Matrix3x2F GetMatrix3x2F() const;
1184
1185 private:
1186 D2D1::Matrix3x2F m_matrix;
1187 };
1188
1189 //-----------------------------------------------------------------------------
1190 // wxD2DMatrixData implementation
1191 //-----------------------------------------------------------------------------
1192
wxD2DMatrixData(wxGraphicsRenderer * renderer)1193 wxD2DMatrixData::wxD2DMatrixData(wxGraphicsRenderer* renderer) : wxGraphicsMatrixData(renderer)
1194 {
1195 m_matrix = D2D1::Matrix3x2F::Identity();
1196 }
1197
wxD2DMatrixData(wxGraphicsRenderer * renderer,const D2D1::Matrix3x2F & matrix)1198 wxD2DMatrixData::wxD2DMatrixData(wxGraphicsRenderer* renderer, const D2D1::Matrix3x2F& matrix) :
1199 wxGraphicsMatrixData(renderer), m_matrix(matrix)
1200 {
1201 }
1202
Clone() const1203 wxGraphicsObjectRefData* wxD2DMatrixData::Clone() const
1204 {
1205 return new wxD2DMatrixData(GetRenderer(), m_matrix);
1206 }
1207
Concat(const wxGraphicsMatrixData * t)1208 void wxD2DMatrixData::Concat(const wxGraphicsMatrixData* t)
1209 {
1210 // Elements of resulting matrix are modified in-place in SetProduct()
1211 // so multiplied matrices cannot be the instances of the resulting matrix.
1212 // Note that parameter matrix (t) is the multiplicand.
1213 const D2D1::Matrix3x2F m1(static_cast<const wxD2DMatrixData*>(t)->m_matrix);
1214 const D2D1::Matrix3x2F m2(m_matrix);
1215 m_matrix.SetProduct(m1, m2);
1216 }
1217
Set(wxDouble a,wxDouble b,wxDouble c,wxDouble d,wxDouble tx,wxDouble ty)1218 void wxD2DMatrixData::Set(wxDouble a, wxDouble b, wxDouble c, wxDouble d, wxDouble tx, wxDouble ty)
1219 {
1220 m_matrix._11 = a;
1221 m_matrix._12 = b;
1222 m_matrix._21 = c;
1223 m_matrix._22 = d;
1224 m_matrix._31 = tx;
1225 m_matrix._32 = ty;
1226 }
1227
Get(wxDouble * a,wxDouble * b,wxDouble * c,wxDouble * d,wxDouble * tx,wxDouble * ty) const1228 void wxD2DMatrixData::Get(wxDouble* a, wxDouble* b, wxDouble* c, wxDouble* d, wxDouble* tx, wxDouble* ty) const
1229 {
1230 *a = m_matrix._11;
1231 *b = m_matrix._12;
1232 *c = m_matrix._21;
1233 *d = m_matrix._22;
1234 *tx = m_matrix._31;
1235 *ty = m_matrix._32;
1236 }
1237
Invert()1238 void wxD2DMatrixData::Invert()
1239 {
1240 m_matrix.Invert();
1241 }
1242
IsEqual(const wxGraphicsMatrixData * t) const1243 bool wxD2DMatrixData::IsEqual(const wxGraphicsMatrixData* t) const
1244 {
1245 return m_matrix == static_cast<const wxD2DMatrixData*>(t)->m_matrix;
1246 }
1247
IsIdentity() const1248 bool wxD2DMatrixData::IsIdentity() const
1249 {
1250 return m_matrix.IsIdentity();
1251 }
1252
Translate(wxDouble dx,wxDouble dy)1253 void wxD2DMatrixData::Translate(wxDouble dx, wxDouble dy)
1254 {
1255 m_matrix = D2D1::Matrix3x2F::Translation(dx, dy) * m_matrix;
1256 }
1257
Scale(wxDouble xScale,wxDouble yScale)1258 void wxD2DMatrixData::Scale(wxDouble xScale, wxDouble yScale)
1259 {
1260 m_matrix = D2D1::Matrix3x2F::Scale(xScale, yScale) * m_matrix;
1261 }
1262
Rotate(wxDouble angle)1263 void wxD2DMatrixData::Rotate(wxDouble angle)
1264 {
1265 m_matrix = D2D1::Matrix3x2F::Rotation(wxRadToDeg(angle)) * m_matrix;
1266 }
1267
TransformPoint(wxDouble * x,wxDouble * y) const1268 void wxD2DMatrixData::TransformPoint(wxDouble* x, wxDouble* y) const
1269 {
1270 D2D1_POINT_2F result = m_matrix.TransformPoint(D2D1::Point2F(*x, *y));
1271 *x = result.x;
1272 *y = result.y;
1273 }
1274
TransformDistance(wxDouble * dx,wxDouble * dy) const1275 void wxD2DMatrixData::TransformDistance(wxDouble* dx, wxDouble* dy) const
1276 {
1277 D2D1::Matrix3x2F noTranslationMatrix = m_matrix;
1278 noTranslationMatrix._31 = 0;
1279 noTranslationMatrix._32 = 0;
1280 D2D1_POINT_2F result = noTranslationMatrix.TransformPoint(D2D1::Point2F(*dx, *dy));
1281 *dx = result.x;
1282 *dy = result.y;
1283 }
1284
GetNativeMatrix() const1285 void* wxD2DMatrixData::GetNativeMatrix() const
1286 {
1287 return const_cast<void*>(static_cast<const void*>(&m_matrix));
1288 }
1289
GetMatrix3x2F() const1290 D2D1::Matrix3x2F wxD2DMatrixData::GetMatrix3x2F() const
1291 {
1292 return m_matrix;
1293 }
1294
wxGetD2DMatrixData(const wxGraphicsMatrix & matrix)1295 const wxD2DMatrixData* wxGetD2DMatrixData(const wxGraphicsMatrix& matrix)
1296 {
1297 return static_cast<const wxD2DMatrixData*>(matrix.GetMatrixData());
1298 }
1299
1300 //-----------------------------------------------------------------------------
1301 // wxD2DPathData declaration
1302 //-----------------------------------------------------------------------------
1303
operator ==(const D2D1_POINT_2F & lhs,const D2D1_POINT_2F & rhs)1304 bool operator==(const D2D1_POINT_2F& lhs, const D2D1_POINT_2F& rhs)
1305 {
1306 return lhs.x == rhs.x && lhs.y == rhs.y;
1307 }
1308
1309 class wxD2DPathData : public wxGraphicsPathData
1310 {
1311 public :
1312
1313 // ID2D1PathGeometry objects are device-independent resources created
1314 // from a ID2D1Factory. This means we can safely create the resource outside
1315 // (the wxD2DRenderer handles this) and store it here since it never gets
1316 // thrown away by the GPU.
1317 wxD2DPathData(wxGraphicsRenderer* renderer, ID2D1Factory* d2dFactory);
1318
1319 ~wxD2DPathData();
1320
SetFillMode(D2D1_FILL_MODE fillMode)1321 void SetFillMode(D2D1_FILL_MODE fillMode)
1322 {
1323 m_fillMode = fillMode;
1324 }
1325
GetFillMode() const1326 D2D1_FILL_MODE GetFillMode() const
1327 {
1328 return m_fillMode;
1329 }
1330
1331 ID2D1PathGeometry* GetPathGeometry();
1332
1333 // This closes the geometry sink, ensuring all the figures are stored inside
1334 // the ID2D1PathGeometry. Calling this method is required before any draw operation
1335 // involving a path.
1336 void Flush();
1337
1338 wxGraphicsObjectRefData* Clone() const wxOVERRIDE;
1339
1340 // begins a new subpath at (x,y)
1341 void MoveToPoint(wxDouble x, wxDouble y) wxOVERRIDE;
1342
1343 // adds a straight line from the current point to (x,y)
1344 void AddLineToPoint(wxDouble x, wxDouble y) wxOVERRIDE;
1345
1346 // adds a cubic Bezier curve from the current point, using two control points and an end point
1347 void AddCurveToPoint(wxDouble cx1, wxDouble cy1, wxDouble cx2, wxDouble cy2, wxDouble x, wxDouble y) wxOVERRIDE;
1348
1349 // adds an arc of a circle centering at (x,y) with radius (r) from startAngle to endAngle
1350 void AddArc(wxDouble x, wxDouble y, wxDouble r, wxDouble startAngle, wxDouble endAngle, bool clockwise) wxOVERRIDE;
1351
1352 // gets the last point of the current path, (0,0) if not yet set
1353 void GetCurrentPoint(wxDouble* x, wxDouble* y) const wxOVERRIDE;
1354
1355 // adds another path
1356 void AddPath(const wxGraphicsPathData* path) wxOVERRIDE;
1357
1358 // closes the current sub-path
1359 void CloseSubpath() wxOVERRIDE;
1360
1361 // returns the native path
1362 void* GetNativePath() const wxOVERRIDE;
1363
1364 // give the native path returned by GetNativePath() back (there might be some deallocations necessary)
UnGetNativePath(void * WXUNUSED (p)) const1365 void UnGetNativePath(void* WXUNUSED(p)) const wxOVERRIDE {}
1366
1367 // transforms each point of this path by the matrix
1368 void Transform(const wxGraphicsMatrixData* matrix) wxOVERRIDE;
1369
1370 // gets the bounding box enclosing all points (possibly including control points)
1371 void GetBox(wxDouble* x, wxDouble* y, wxDouble* w, wxDouble *h) const wxOVERRIDE;
1372
1373 bool Contains(wxDouble x, wxDouble y, wxPolygonFillMode fillStyle = wxODDEVEN_RULE) const wxOVERRIDE;
1374
1375 // appends an ellipsis as a new closed subpath fitting the passed rectangle
1376 void AddCircle(wxDouble x, wxDouble y, wxDouble r) wxOVERRIDE;
1377
1378 // appends an ellipse
1379 void AddEllipse(wxDouble x, wxDouble y, wxDouble w, wxDouble h) wxOVERRIDE;
1380
1381 private:
1382 void EnsureGeometryOpen();
1383
1384 void EnsureSinkOpen();
1385
1386 void EnsureFigureOpen(const D2D1_POINT_2F& pos);
1387
1388 void EndFigure(D2D1_FIGURE_END figureEnd);
1389
1390 ID2D1Geometry* GetFullGeometry(D2D1_FILL_MODE fillMode) const;
1391
1392 bool IsEmpty() const;
1393 bool IsStateSafeForFlush() const;
1394
1395 struct GeometryStateData
1396 {
1397 bool m_isCurrentPointSet;
1398 D2D1_POINT_2F m_currentPoint;
1399 bool m_isFigureOpen;
1400 D2D1_POINT_2F m_figureStart;
1401 bool m_isFigureLogStartSet;
1402 D2D1_POINT_2F m_figureLogStart;
1403 };
1404 void SaveGeometryState(GeometryStateData& data) const;
1405 void RestoreGeometryState(const GeometryStateData& data);
1406
1407 private :
1408 wxCOMPtr<ID2D1PathGeometry> m_pathGeometry;
1409
1410 wxCOMPtr<ID2D1GeometrySink> m_geometrySink;
1411
1412 wxCOMPtr<ID2D1Factory> m_direct2dfactory;
1413
1414 mutable wxCOMPtr<ID2D1GeometryGroup> m_combinedGeometry;
1415 wxVector<ID2D1Geometry*> m_pTransformedGeometries;
1416
1417 bool m_currentPointSet;
1418 D2D1_POINT_2F m_currentPoint;
1419
1420 bool m_figureOpened;
1421 D2D1_POINT_2F m_figureStart;
1422 bool m_figureLogStartSet;
1423 D2D1_POINT_2F m_figureLogStart;
1424
1425 bool m_geometryWritable;
1426
1427 D2D1_FILL_MODE m_fillMode;
1428 };
1429
1430 //-----------------------------------------------------------------------------
1431 // wxD2DPathData implementation
1432 //-----------------------------------------------------------------------------
1433
wxD2DPathData(wxGraphicsRenderer * renderer,ID2D1Factory * d2dFactory)1434 wxD2DPathData::wxD2DPathData(wxGraphicsRenderer* renderer, ID2D1Factory* d2dFactory) :
1435 wxGraphicsPathData(renderer),
1436 m_direct2dfactory(d2dFactory),
1437 m_currentPointSet(false),
1438 m_currentPoint(D2D1::Point2F(0.0f, 0.0f)),
1439 m_figureOpened(false),
1440 m_figureStart(D2D1::Point2F(0.0f, 0.0f)),
1441 m_figureLogStartSet(false),
1442 m_figureLogStart(D2D1::Point2F(0.0f, 0.0f)),
1443 m_geometryWritable(true),
1444 m_fillMode(D2D1_FILL_MODE_ALTERNATE)
1445 {
1446 m_direct2dfactory->CreatePathGeometry(&m_pathGeometry);
1447 // To properly initialize path geometry there is also
1448 // necessary to open at least once its geometry sink.
1449 m_pathGeometry->Open(&m_geometrySink);
1450 }
1451
~wxD2DPathData()1452 wxD2DPathData::~wxD2DPathData()
1453 {
1454 Flush();
1455 for( size_t i = 0; i < m_pTransformedGeometries.size(); i++ )
1456 {
1457 m_pTransformedGeometries[i]->Release();
1458 }
1459 }
1460
GetPathGeometry()1461 ID2D1PathGeometry* wxD2DPathData::GetPathGeometry()
1462 {
1463 return m_pathGeometry;
1464 }
1465
Clone() const1466 wxD2DPathData::wxGraphicsObjectRefData* wxD2DPathData::Clone() const
1467 {
1468 wxD2DPathData* newPathData = new wxD2DPathData(GetRenderer(), m_direct2dfactory);
1469
1470 newPathData->EnsureGeometryOpen();
1471
1472 // Only geometry with closed sink can be
1473 // transferred to another geometry object with
1474 // ID2D1PathGeometry::Stream() so we have to check
1475 // if actual transfer succeeded.
1476
1477 // Transfer geometry to the new geometry sink.
1478 HRESULT hr = m_pathGeometry->Stream(newPathData->m_geometrySink);
1479 wxASSERT_MSG( SUCCEEDED(hr), wxS("Current geometry is in invalid state") );
1480 if ( FAILED(hr) )
1481 {
1482 delete newPathData;
1483 return NULL;
1484 }
1485
1486 // Copy the collection of transformed geometries.
1487 ID2D1TransformedGeometry* pTransformedGeometry;
1488 for ( size_t i = 0; i < m_pTransformedGeometries.size(); i++ )
1489 {
1490 pTransformedGeometry = NULL;
1491 hr = m_direct2dfactory->CreateTransformedGeometry(
1492 m_pTransformedGeometries[i],
1493 D2D1::Matrix3x2F::Identity(), &pTransformedGeometry);
1494 wxASSERT_MSG( SUCCEEDED(hr), wxFAILED_HRESULT_MSG(hr) );
1495 newPathData->m_pTransformedGeometries.push_back(pTransformedGeometry);
1496 }
1497
1498 // Copy positional data.
1499 GeometryStateData curState;
1500 SaveGeometryState(curState);
1501 newPathData->RestoreGeometryState(curState);
1502
1503 return newPathData;
1504 }
1505
Flush()1506 void wxD2DPathData::Flush()
1507 {
1508 if (m_geometrySink != NULL)
1509 {
1510 if ( m_figureOpened )
1511 {
1512 m_geometrySink->EndFigure(D2D1_FIGURE_END_OPEN);
1513 m_figureOpened = false;
1514 }
1515
1516 if( m_geometryWritable )
1517 {
1518 HRESULT hr = m_geometrySink->Close();
1519 wxCHECK_HRESULT_RET(hr);
1520 m_geometryWritable = false;
1521 }
1522 }
1523 }
1524
EnsureGeometryOpen()1525 void wxD2DPathData::EnsureGeometryOpen()
1526 {
1527 if (!m_geometryWritable)
1528 {
1529 wxCOMPtr<ID2D1PathGeometry> newPathGeometry;
1530 HRESULT hr;
1531 hr = m_direct2dfactory->CreatePathGeometry(&newPathGeometry);
1532 wxCHECK_HRESULT_RET(hr);
1533
1534 m_geometrySink.reset();
1535 hr = newPathGeometry->Open(&m_geometrySink);
1536 wxCHECK_HRESULT_RET(hr);
1537
1538 if (m_pathGeometry != NULL)
1539 {
1540 hr = m_pathGeometry->Stream(m_geometrySink);
1541 wxCHECK_HRESULT_RET(hr);
1542 }
1543
1544 m_pathGeometry = newPathGeometry;
1545 m_geometryWritable = true;
1546 }
1547 }
1548
EnsureSinkOpen()1549 void wxD2DPathData::EnsureSinkOpen()
1550 {
1551 EnsureGeometryOpen();
1552
1553 if (m_geometrySink == NULL)
1554 {
1555 HRESULT hr = m_pathGeometry->Open(&m_geometrySink);
1556 wxCHECK_HRESULT_RET(hr);
1557 }
1558 }
1559
EnsureFigureOpen(const D2D1_POINT_2F & pos)1560 void wxD2DPathData::EnsureFigureOpen(const D2D1_POINT_2F& pos)
1561 {
1562 EnsureSinkOpen();
1563
1564 if (!m_figureOpened)
1565 {
1566 m_figureStart = pos;
1567 m_geometrySink->BeginFigure(m_figureStart, D2D1_FIGURE_BEGIN_FILLED);
1568 m_figureOpened = true;
1569 m_currentPoint = m_figureStart;
1570 }
1571 }
1572
EndFigure(D2D1_FIGURE_END figureEnd)1573 void wxD2DPathData::EndFigure(D2D1_FIGURE_END figureEnd)
1574 {
1575 if (m_figureOpened)
1576 {
1577 // Ensure that sub-path being closed contains at least one point.
1578 if( figureEnd == D2D1_FIGURE_END_CLOSED )
1579 m_geometrySink->AddLine(m_currentPoint);
1580
1581 if( figureEnd == D2D1_FIGURE_END_OPEN ||
1582 !m_figureLogStartSet ||
1583 m_figureLogStart == m_figureStart )
1584 {
1585 // If figure will remain open or if its logical startpoint
1586 // is not used or if it is the same as the actual
1587 // startpoint then we can end the figure in a standard way.
1588 m_geometrySink->EndFigure(figureEnd);
1589 }
1590 else
1591 {
1592 // If we want to end and close the figure for which
1593 // logical startpoint is not the same as actual startpoint
1594 // we have to fill the gap between the actual and logical
1595 // endpoints on our own.
1596 m_geometrySink->AddLine(m_figureLogStart);
1597 m_geometrySink->EndFigure(D2D1_FIGURE_END_OPEN);
1598 m_figureStart = m_figureLogStart;
1599 }
1600 m_figureOpened = false;
1601 m_figureLogStartSet = false;
1602
1603 // If the figure is closed then current point
1604 // should be moved to the beginning of the figure.
1605 if( figureEnd == D2D1_FIGURE_END_CLOSED )
1606 m_currentPoint = m_figureStart;
1607 }
1608 }
1609
GetFullGeometry(D2D1_FILL_MODE fillMode) const1610 ID2D1Geometry* wxD2DPathData::GetFullGeometry(D2D1_FILL_MODE fillMode) const
1611 {
1612 // Our final path geometry is represented by geometry group
1613 // which contains all transformed geometries plus current geometry.
1614
1615 // We have to store pointers to all transformed geometries
1616 // as well as pointer to the current geometry in the auxiliary array.
1617 const size_t numGeometries = m_pTransformedGeometries.size();
1618 ID2D1Geometry** pGeometries = new ID2D1Geometry*[numGeometries+1];
1619 for( size_t i = 0; i < numGeometries; i++ )
1620 pGeometries[i] = m_pTransformedGeometries[i];
1621
1622 pGeometries[numGeometries] = m_pathGeometry;
1623
1624 // And use this array as a source to create geometry group.
1625 m_combinedGeometry.reset();
1626 HRESULT hr = m_direct2dfactory->CreateGeometryGroup(fillMode,
1627 pGeometries, numGeometries+1, &m_combinedGeometry);
1628 wxFAILED_HRESULT_MSG(hr);
1629 delete []pGeometries;
1630
1631 return m_combinedGeometry;
1632 }
1633
IsEmpty() const1634 bool wxD2DPathData::IsEmpty() const
1635 {
1636 return !m_currentPointSet && !m_figureOpened &&
1637 m_pTransformedGeometries.size() == 0;
1638 }
1639
IsStateSafeForFlush() const1640 bool wxD2DPathData::IsStateSafeForFlush() const
1641 {
1642 // Only geometry with not yet started figure
1643 // or with started but empty figure can be fully
1644 // restored to its initial state after invoking Flush().
1645 if( !m_figureOpened )
1646 return true;
1647
1648 D2D1_POINT_2F actFigureStart = m_figureLogStartSet ?
1649 m_figureLogStart : m_figureStart;
1650 return m_currentPoint == actFigureStart;
1651 }
1652
SaveGeometryState(GeometryStateData & data) const1653 void wxD2DPathData::SaveGeometryState(GeometryStateData& data) const
1654 {
1655 data.m_isFigureOpen = m_figureOpened;
1656 data.m_isFigureLogStartSet = m_figureLogStartSet;
1657 data.m_isCurrentPointSet = m_currentPointSet;
1658 data.m_currentPoint = m_currentPoint;
1659 data.m_figureStart = m_figureStart;
1660 data.m_figureLogStart = m_figureLogStart;
1661 }
1662
RestoreGeometryState(const GeometryStateData & data)1663 void wxD2DPathData::RestoreGeometryState(const GeometryStateData& data)
1664 {
1665 if( data.m_isFigureOpen )
1666 {
1667 // If the figure has to be re-started at the startpoint
1668 // which is not the current point then we have to start it
1669 // physically at the current point but with storing also its
1670 // logical startpoint to use it later on to close the figure,
1671 // if necessary.
1672 // Ending and closing the figure using this proxy startpoint
1673 // is only a simulation of regular closure and figure can behave
1674 // in a slightly different way than figure closed with physical
1675 // startpoint so this action should be avoided if only possible.
1676 D2D1_POINT_2F actFigureStart = data.m_isFigureLogStartSet ?
1677 data.m_figureLogStart : data.m_figureStart;
1678 if ( !(data.m_currentPoint == actFigureStart) )
1679 {
1680 m_figureLogStart = actFigureStart;
1681 m_figureLogStartSet = true;
1682 EnsureFigureOpen(data.m_currentPoint);
1683 }
1684 else
1685 {
1686 EnsureFigureOpen(actFigureStart);
1687 }
1688 }
1689 else
1690 {
1691 m_figureOpened = false;
1692 }
1693
1694 m_currentPointSet = data.m_isCurrentPointSet;
1695 m_currentPoint = data.m_isCurrentPointSet ?
1696 data.m_currentPoint : D2D1::Point2F(0.0F, 0.0F);
1697 }
1698
MoveToPoint(wxDouble x,wxDouble y)1699 void wxD2DPathData::MoveToPoint(wxDouble x, wxDouble y)
1700 {
1701 // Close current sub-path (leaving the figure as is).
1702 EndFigure(D2D1_FIGURE_END_OPEN);
1703 // Store new current point
1704 m_currentPoint = D2D1::Point2F(x, y);
1705 m_currentPointSet = true;
1706 }
1707
1708 // adds a straight line from the current point to (x,y)
AddLineToPoint(wxDouble x,wxDouble y)1709 void wxD2DPathData::AddLineToPoint(wxDouble x, wxDouble y)
1710 {
1711 // If current point is not yet set then
1712 // this function should behave as MoveToPoint.
1713 if( !m_currentPointSet )
1714 {
1715 MoveToPoint(x, y);
1716 return;
1717 }
1718
1719 EnsureFigureOpen(m_currentPoint);
1720 m_geometrySink->AddLine(D2D1::Point2F(x, y));
1721
1722 m_currentPoint = D2D1::Point2F(x, y);
1723 }
1724
1725 // adds a cubic Bezier curve from the current point, using two control points and an end point
AddCurveToPoint(wxDouble cx1,wxDouble cy1,wxDouble cx2,wxDouble cy2,wxDouble x,wxDouble y)1726 void wxD2DPathData::AddCurveToPoint(wxDouble cx1, wxDouble cy1, wxDouble cx2, wxDouble cy2, wxDouble x, wxDouble y)
1727 {
1728 // If no current point is set then this function should behave
1729 // as if preceded by a call to MoveToPoint(cx1, cy1).
1730 if( !m_currentPointSet )
1731 MoveToPoint(cx1, cy1);
1732
1733 EnsureFigureOpen(m_currentPoint);
1734
1735 D2D1_BEZIER_SEGMENT bezierSegment = {
1736 { (FLOAT)cx1, (FLOAT)cy1 },
1737 { (FLOAT)cx2, (FLOAT)cy2 },
1738 { (FLOAT)x, (FLOAT)y } };
1739 m_geometrySink->AddBezier(bezierSegment);
1740
1741 m_currentPoint = D2D1::Point2F(x, y);
1742 }
1743
1744 // adds an arc of a circle centering at (x,y) with radius (r) from startAngle to endAngle
AddArc(wxDouble x,wxDouble y,wxDouble r,wxDouble startAngle,wxDouble endAngle,bool clockwise)1745 void wxD2DPathData::AddArc(wxDouble x, wxDouble y, wxDouble r, wxDouble startAngle, wxDouble endAngle, bool clockwise)
1746 {
1747 double angle;
1748
1749 // For the sake of compatibility normalize angles the same way
1750 // as it is done in Cairo.
1751 if ( clockwise )
1752 {
1753 // If endAngle < startAngle it needs to be progressively
1754 // increased by 2*M_PI until endAngle > startAngle.
1755 if ( endAngle < startAngle )
1756 {
1757 while ( endAngle <= startAngle )
1758 {
1759 endAngle += 2.0*M_PI;
1760 }
1761 }
1762
1763 angle = endAngle - startAngle;
1764 }
1765 else
1766 {
1767 // If endAngle > startAngle it needs to be progressively
1768 // decreased by 2*M_PI until endAngle < startAngle.
1769 if ( endAngle > startAngle )
1770 {
1771 while ( endAngle >= startAngle )
1772 {
1773 endAngle -= 2.0*M_PI;
1774 }
1775 }
1776
1777 angle = startAngle - endAngle;
1778 }
1779
1780 wxPoint2DDouble start = wxPoint2DDouble(cos(startAngle) * r, sin(startAngle) * r);
1781 wxPoint2DDouble end = wxPoint2DDouble(cos(endAngle) * r, sin(endAngle) * r);
1782
1783 // To ensure compatibility with Cairo an initial
1784 // line segment to the beginning of the arc needs
1785 // to be added to the path.
1786 if ( m_figureOpened )
1787 {
1788 AddLineToPoint(start.m_x + x, start.m_y + y);
1789 }
1790 else if ( m_currentPointSet )
1791 {
1792 EnsureFigureOpen(m_currentPoint);
1793 AddLineToPoint(start.m_x + x, start.m_y + y);
1794 }
1795 else
1796 {
1797 MoveToPoint(start.m_x + x, start.m_y + y);
1798 EnsureFigureOpen(m_currentPoint);
1799 }
1800
1801 D2D1_SWEEP_DIRECTION sweepDirection = clockwise ?
1802 D2D1_SWEEP_DIRECTION_CLOCKWISE : D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE;
1803 D2D1_SIZE_F size = D2D1::SizeF((FLOAT)r, (FLOAT)r);
1804
1805 if ( angle >= 2.0*M_PI )
1806 {
1807 // In addition to arc we need to draw full circle(s).
1808 // Remarks:
1809 // 1. Parity of the number of the circles has to be
1810 // preserved because this matters when path would be
1811 // filled with wxODDEVEN_RULE flag set (using
1812 // D2D1_FILL_MODE_ALTERNATE mode) when number of the
1813 // edges is counted.
1814 // 2. ID2D1GeometrySink::AddArc() doesn't work
1815 // with 360-degree arcs so we need to construct
1816 // the circle from two halves.
1817 D2D1_ARC_SEGMENT circleSegment1 =
1818 {
1819 D2D1::Point2((FLOAT)(x - start.m_x), (FLOAT)(y - start.m_y)), // end point
1820 size, // size
1821 0.0f, // rotation
1822 sweepDirection, // sweep direction
1823 D2D1_ARC_SIZE_SMALL // arc size
1824 };
1825 D2D1_ARC_SEGMENT circleSegment2 =
1826 {
1827 D2D1::Point2((FLOAT)(x + start.m_x), (FLOAT)(y + start.m_y)), // end point
1828 size, // size
1829 0.0f, // rotation
1830 sweepDirection, // sweep direction
1831 D2D1_ARC_SIZE_SMALL // arc size
1832 };
1833
1834 int numCircles = (int)(angle / (2.0*M_PI));
1835 numCircles = (numCircles - 1) % 2 + 1;
1836 for( int i = 0; i < numCircles; i++ )
1837 {
1838 m_geometrySink->AddArc(circleSegment1);
1839 m_geometrySink->AddArc(circleSegment2);
1840 }
1841
1842 // Reduce the angle to [0..2*M_PI) range.
1843 angle = fmod(angle, 2.0*M_PI);
1844 }
1845
1846 D2D1_ARC_SIZE arcSize = angle > M_PI ?
1847 D2D1_ARC_SIZE_LARGE : D2D1_ARC_SIZE_SMALL;
1848 D2D1_POINT_2F endPoint =
1849 D2D1::Point2((FLOAT)(end.m_x + x), (FLOAT)(end.m_y + y));
1850
1851 D2D1_ARC_SEGMENT arcSegment =
1852 {
1853 endPoint, // end point
1854 size, // size
1855 0.0f, // rotation
1856 sweepDirection, // sweep direction
1857 arcSize // arc size
1858 };
1859
1860 m_geometrySink->AddArc(arcSegment);
1861
1862 m_currentPoint = endPoint;
1863 }
1864
1865 // appends an ellipsis as a new closed subpath fitting the passed rectangle
AddCircle(wxDouble x,wxDouble y,wxDouble r)1866 void wxD2DPathData::AddCircle(wxDouble x, wxDouble y, wxDouble r)
1867 {
1868 AddEllipse(x - r, y - r, r * 2, r * 2);
1869 }
1870
1871 // appends an ellipse
AddEllipse(wxDouble x,wxDouble y,wxDouble w,wxDouble h)1872 void wxD2DPathData::AddEllipse(wxDouble x, wxDouble y, wxDouble w, wxDouble h)
1873 {
1874 if ( w <= 0.0 || h <= 0.0 )
1875 return;
1876
1877 // Calculate radii
1878 const wxDouble rx = w / 2.0;
1879 const wxDouble ry = h / 2.0;
1880
1881 MoveToPoint(x + w, y + ry);
1882 // Open new subpath
1883 EnsureFigureOpen(m_currentPoint);
1884
1885 D2D1_ARC_SEGMENT arcSegmentLower =
1886 {
1887 D2D1::Point2((FLOAT)(x), (FLOAT)(y + ry)), // end point
1888 D2D1::SizeF((FLOAT)(rx), (FLOAT)(ry)), // size
1889 0.0f,
1890 D2D1_SWEEP_DIRECTION_CLOCKWISE,
1891 D2D1_ARC_SIZE_SMALL
1892 };
1893 m_geometrySink->AddArc(arcSegmentLower);
1894
1895 D2D1_ARC_SEGMENT arcSegmentUpper =
1896 {
1897 D2D1::Point2((FLOAT)(x + w), (FLOAT)(y + ry)), // end point
1898 D2D1::SizeF((FLOAT)(rx), (FLOAT)(ry)), // size
1899 0.0f,
1900 D2D1_SWEEP_DIRECTION_CLOCKWISE,
1901 D2D1_ARC_SIZE_SMALL
1902 };
1903 m_geometrySink->AddArc(arcSegmentUpper);
1904
1905 CloseSubpath();
1906 }
1907
1908 // gets the last point of the current path, (0,0) if not yet set
GetCurrentPoint(wxDouble * x,wxDouble * y) const1909 void wxD2DPathData::GetCurrentPoint(wxDouble* x, wxDouble* y) const
1910 {
1911 if (x != NULL) *x = m_currentPoint.x;
1912 if (y != NULL) *y = m_currentPoint.y;
1913 }
1914
1915 // adds another path
AddPath(const wxGraphicsPathData * path)1916 void wxD2DPathData::AddPath(const wxGraphicsPathData* path)
1917 {
1918 wxD2DPathData* pathSrc =
1919 const_cast<wxD2DPathData*>(static_cast<const wxD2DPathData*>(path));
1920
1921 // Nothing to do if geometry of appended path is not initialized.
1922 if ( pathSrc->m_pathGeometry == NULL || pathSrc->m_geometrySink == NULL )
1923 return;
1924
1925 // Because only closed geometry (with closed sink)
1926 // can be transferred to another geometry object with
1927 // ID2D1PathGeometry::Stream() so we have to close it
1928 // before any operation and to re-open afterwards.
1929 // Unfortunately, to close the sink it is also necessary
1930 // to end the figure it contains, if it was open.
1931 // After re-opening the geometry we should also re-start
1932 // the figure (if it was open) and restore its state but
1933 // it seems there is no straightforward way to do so
1934 // if the figure is not empty.
1935 //
1936 // So, only if appended path has a sub-path closed or
1937 // has an empty sub-path open there is possible to restore
1938 // its state after appending it to the current path and only
1939 // in this case the operation doesn't introduce side effects.
1940
1941 // Nothing to do if appended path is empty.
1942 if ( pathSrc->IsEmpty() )
1943 return;
1944
1945 // Save positional and auxiliary data
1946 // of the appended path and its geometry.
1947 GeometryStateData curStateSrc;
1948 pathSrc->SaveGeometryState(curStateSrc);
1949
1950 // Close appended geometry.
1951 pathSrc->Flush();
1952
1953 // Close current geometry (leaving the figure as is).
1954 Flush();
1955
1956 HRESULT hr;
1957 ID2D1TransformedGeometry* pTransformedGeometry = NULL;
1958 // Add current geometry to the collection transformed geometries.
1959 hr = m_direct2dfactory->CreateTransformedGeometry(m_pathGeometry,
1960 D2D1::Matrix3x2F::Identity(), &pTransformedGeometry);
1961 wxCHECK_HRESULT_RET(hr);
1962 m_pTransformedGeometries.push_back(pTransformedGeometry);
1963
1964 // Add to the collection transformed geometries from the appended path.
1965 for ( size_t i = 0; i < pathSrc->m_pTransformedGeometries.size(); i++ )
1966 {
1967 pTransformedGeometry = NULL;
1968 hr = m_direct2dfactory->CreateTransformedGeometry(
1969 pathSrc->m_pTransformedGeometries[i],
1970 D2D1::Matrix3x2F::Identity(), &pTransformedGeometry);
1971 wxCHECK_HRESULT_RET(hr);
1972 m_pTransformedGeometries.push_back(pTransformedGeometry);
1973 }
1974
1975 // Clear and reopen current geometry.
1976 m_pathGeometry.reset();
1977 EnsureGeometryOpen();
1978
1979 // Transfer appended geometry to the current geometry sink.
1980 hr = pathSrc->m_pathGeometry->Stream(m_geometrySink);
1981 wxCHECK_HRESULT_RET(hr);
1982
1983 // Apply to the current path positional data from the appended path.
1984 // This operation fully sets geometry to the required state
1985 // only if it represents geometry without started figure
1986 // or with started but empty figure.
1987 RestoreGeometryState(curStateSrc);
1988
1989 // Reopen appended geometry.
1990 pathSrc->EnsureGeometryOpen();
1991 // Restore its positional data.
1992 // This operation fully restores geometry to the required state
1993 // only if it represents geometry without started figure
1994 // or with started but empty figure.
1995 pathSrc->RestoreGeometryState(curStateSrc);
1996 }
1997
1998 // closes the current sub-path
CloseSubpath()1999 void wxD2DPathData::CloseSubpath()
2000 {
2001 // If we have a sub-path open by call to MoveToPoint(),
2002 // which doesn't open a new figure by itself,
2003 // we have to open a new figure now to get a required 1-point path.
2004 if ( !m_figureOpened && m_currentPointSet )
2005 {
2006 EnsureFigureOpen(m_currentPoint);
2007 }
2008 // Close sub-path and close the figure.
2009 if ( m_figureOpened )
2010 {
2011 EndFigure(D2D1_FIGURE_END_CLOSED);
2012 MoveToPoint(m_figureStart.x, m_figureStart.y);
2013 }
2014 }
2015
GetNativePath() const2016 void* wxD2DPathData::GetNativePath() const
2017 {
2018 return GetFullGeometry(GetFillMode());
2019 }
2020
Transform(const wxGraphicsMatrixData * matrix)2021 void wxD2DPathData::Transform(const wxGraphicsMatrixData* matrix)
2022 {
2023 // Unfortunately, it looks there is no straightforward way to apply
2024 // transformation to the current underlying path geometry
2025 // (ID2D1PathGeometry object) "in-place" (ie. transform it and use
2026 // for further graphics operations, including next transformations too).
2027 // Some simple methods offered by D2D are not useful for these purposes:
2028 // 1. ID2D1Factory::CreateTransformedGeometry() converts ID2D1PathGeometry
2029 // object to ID2D1TransformedGeometry object but ID2D1TransformedGeometry
2030 // inherits from ID2D1Geometry (not from ID2D1PathGeometry)
2031 // and hence cannot be used for further path operations.
2032 // 2. ID2D1Geometry::CombineWithGeometry() which could be used to get final
2033 // path geometry by combining empty geometry with transformed geometry
2034 // doesn't offer any combine mode which would produce a "sum" of geometries
2035 // (D2D1_COMBINE_MODE_UNION produces kind of outline). Moreover the result
2036 // is stored in ID2D1SimplifiedGeometrySink not in ID2DGeometrySink.
2037
2038 // So, it seems that ability to transform the wxGraphicsPath
2039 // (several times) and still use it after this operation(s)
2040 // can be achieved (only?) by using a geometry group object
2041 // (ID2D1GeometryGroup) this way:
2042 // 1. After applying transformation to the current path geometry with
2043 // ID2D1Factory::CreateTransformedGeometry() the result is stored
2044 // in the collection of transformed geometries (an auxiliary array)
2045 // and after that a new (empty) geometry is open (in the same state
2046 // as just closed one) and this geometry is used as a current one
2047 // for further graphics operations.
2048 // 2. Since above steps are done at every transformation so our effective
2049 // geometry will be a superposition of all previously transformed
2050 // geometries stored in the collection (array) and the current
2051 // operational geometry.
2052 // 3. If there is necessary to use this combined effective geometry
2053 // in any operation then ID2D1GeometryGroup created with
2054 // ID2D1Factory::CreateGeometryGroup() from the collection
2055 // of stored geometries will act as a proxy geometry.
2056
2057 const D2D1::Matrix3x2F* m = static_cast<D2D1::Matrix3x2F*>(matrix->GetNativeMatrix());
2058
2059 // Save current positional data.
2060 GeometryStateData curState;
2061 SaveGeometryState(curState);
2062 // We need to close the geometry what requires also to end a figure
2063 // (if started). This ended figure should be re-started in its initial
2064 // state when all path processing is done but due to the Direct2D
2065 // constraints this can be fully done only if open figure was empty.
2066 // So, Transform() can be safely called if path doesn't contain the open
2067 // sub-path or if open sub-path is empty.
2068
2069 // Close current geometry.
2070 Flush();
2071
2072 HRESULT hr;
2073 ID2D1TransformedGeometry* pTransformedGeometry;
2074 // Apply given transformation to all previously stored geometries too.
2075 for( size_t i = 0; i < m_pTransformedGeometries.size(); i++ )
2076 {
2077 pTransformedGeometry = NULL;
2078 hr = m_direct2dfactory->CreateTransformedGeometry(m_pTransformedGeometries[i], m, &pTransformedGeometry);
2079 wxCHECK_HRESULT_RET(hr);
2080
2081 m_pTransformedGeometries[i]->Release();
2082 m_pTransformedGeometries[i] = pTransformedGeometry;
2083 }
2084
2085 // Transform current geometry and add the result
2086 // to the collection of transformed geometries.
2087 pTransformedGeometry = NULL;
2088 hr = m_direct2dfactory->CreateTransformedGeometry(m_pathGeometry, m, &pTransformedGeometry);
2089 wxCHECK_HRESULT_RET(hr);
2090 m_pTransformedGeometries.push_back(pTransformedGeometry);
2091
2092 // Clear and reopen current geometry.
2093 m_pathGeometry.reset();
2094 EnsureGeometryOpen();
2095 // Restore the figure with transformed positional data.
2096 // This operation fully restores geometry to the required state
2097 // only if IsStateSafeForFlush() returns true.
2098 curState.m_currentPoint = m->TransformPoint(curState.m_currentPoint);
2099 curState.m_figureLogStart = m->TransformPoint(curState.m_figureLogStart);
2100 curState.m_figureStart = m->TransformPoint(curState.m_figureStart);
2101 RestoreGeometryState(curState);
2102 }
2103
GetBox(wxDouble * x,wxDouble * y,wxDouble * w,wxDouble * h) const2104 void wxD2DPathData::GetBox(wxDouble* x, wxDouble* y, wxDouble* w, wxDouble *h) const
2105 {
2106 D2D1_RECT_F bounds;
2107 ID2D1Geometry *curGeometry = GetFullGeometry(GetFillMode());
2108 HRESULT hr = curGeometry->GetBounds(D2D1::Matrix3x2F::Identity(), &bounds);
2109 wxCHECK_HRESULT_RET(hr);
2110 // Check if bounds are empty
2111 if ( bounds.left > bounds.right )
2112 {
2113 bounds.left = bounds.top = bounds.right = bounds.bottom = 0.0F;
2114 }
2115 if (x) *x = bounds.left;
2116 if (y) *y = bounds.top;
2117 if (w) *w = bounds.right - bounds.left;
2118 if (h) *h = bounds.bottom - bounds.top;
2119 }
2120
Contains(wxDouble x,wxDouble y,wxPolygonFillMode fillStyle) const2121 bool wxD2DPathData::Contains(wxDouble x, wxDouble y, wxPolygonFillMode fillStyle) const
2122 {
2123 BOOL result;
2124 D2D1_FILL_MODE fillMode = (fillStyle == wxODDEVEN_RULE) ? D2D1_FILL_MODE_ALTERNATE : D2D1_FILL_MODE_WINDING;
2125 ID2D1Geometry *curGeometry = GetFullGeometry(fillMode);
2126 curGeometry->FillContainsPoint(D2D1::Point2F(x, y), D2D1::Matrix3x2F::Identity(), &result);
2127 return result != FALSE;
2128 }
2129
wxGetD2DPathData(const wxGraphicsPath & path)2130 wxD2DPathData* wxGetD2DPathData(const wxGraphicsPath& path)
2131 {
2132 return static_cast<wxD2DPathData*>(path.GetGraphicsData());
2133 }
2134
2135 // This utility class is used to read a color value with the format
2136 // PBGRA from a byte stream and to write a color back to the stream.
2137 // It's used in conjunction with the IWICBitmapSource or IWICBitmap
2138 // pixel data to easily read and write color values.
2139 struct wxPBGRAColor
2140 {
wxPBGRAColorwxPBGRAColor2141 wxPBGRAColor(BYTE* stream) :
2142 b(*stream), g(*(stream + 1)), r(*(stream + 2)), a(*(stream + 3))
2143 {}
2144
wxPBGRAColorwxPBGRAColor2145 wxPBGRAColor(const wxColor& color) :
2146 b(color.Blue()), g(color.Green()), r(color.Red()), a(color.Alpha())
2147 {}
2148
IsBlackwxPBGRAColor2149 bool IsBlack() const { return r == 0 && g == 0 && b == 0; }
2150
WritewxPBGRAColor2151 void Write(BYTE* stream) const
2152 {
2153 *(stream + 0) = b;
2154 *(stream + 1) = g;
2155 *(stream + 2) = r;
2156 *(stream + 3) = a;
2157 }
2158
2159 BYTE b, g, r, a;
2160 };
2161
2162 namespace
2163 {
wxCreateWICBitmap(const WXHBITMAP sourceBitmap,bool hasAlpha,bool forceAlpha)2164 wxCOMPtr<IWICBitmapSource> wxCreateWICBitmap(const WXHBITMAP sourceBitmap, bool hasAlpha, bool forceAlpha)
2165 {
2166 HRESULT hr;
2167
2168 wxCOMPtr<IWICBitmap> wicBitmap;
2169 hr = wxWICImagingFactory()->CreateBitmapFromHBITMAP(sourceBitmap, NULL, hasAlpha ? WICBitmapUsePremultipliedAlpha : WICBitmapIgnoreAlpha, &wicBitmap);
2170 wxCHECK2_HRESULT_RET(hr, wxCOMPtr<IWICBitmapSource>(NULL));
2171
2172 wxCOMPtr<IWICFormatConverter> converter;
2173 hr = wxWICImagingFactory()->CreateFormatConverter(&converter);
2174 wxCHECK2_HRESULT_RET(hr, wxCOMPtr<IWICBitmapSource>(NULL));
2175
2176 WICPixelFormatGUID pixelFormat = hasAlpha || forceAlpha ? GUID_WICPixelFormat32bppPBGRA : GUID_WICPixelFormat32bppBGR;
2177
2178 hr = converter->Initialize(
2179 wicBitmap,
2180 pixelFormat,
2181 WICBitmapDitherTypeNone, NULL, 0.f,
2182 WICBitmapPaletteTypeMedianCut);
2183 wxCHECK2_HRESULT_RET(hr, wxCOMPtr<IWICBitmapSource>(NULL));
2184
2185 return wxCOMPtr<IWICBitmapSource>(converter);
2186 }
2187
wxCreateWICBitmap(const wxBitmap & sourceBitmap,bool forceAlpha)2188 inline wxCOMPtr<IWICBitmapSource> wxCreateWICBitmap(const wxBitmap& sourceBitmap, bool forceAlpha)
2189 {
2190 return wxCreateWICBitmap(sourceBitmap.GetHBITMAP(), sourceBitmap.HasAlpha(), forceAlpha);
2191 }
2192
2193 #if wxUSE_IMAGE
CreateWICBitmapFromImage(const wxImage & img,bool forceAlpha,IWICBitmap ** ppBmp)2194 void CreateWICBitmapFromImage(const wxImage& img, bool forceAlpha, IWICBitmap** ppBmp)
2195 {
2196 const int width = img.GetWidth();
2197 const int height = img.GetHeight();
2198 // Create a compatible WIC Bitmap
2199 WICPixelFormatGUID fmt = img.HasAlpha() || img.HasMask() || forceAlpha ? GUID_WICPixelFormat32bppPBGRA : GUID_WICPixelFormat32bppBGR;
2200 HRESULT hr = wxWICImagingFactory()->CreateBitmap(width, height, fmt, WICBitmapCacheOnLoad, ppBmp);
2201 wxCHECK_HRESULT_RET(hr);
2202
2203 // Copy contents of source image to the WIC bitmap.
2204 WICRect rcLock = { 0, 0, width, height };
2205 wxCOMPtr<IWICBitmapLock> pLock;
2206 hr = (*ppBmp)->Lock(&rcLock, WICBitmapLockWrite, &pLock);
2207 wxCHECK_HRESULT_RET(hr);
2208
2209 UINT rowStride = 0;
2210 hr = pLock->GetStride(&rowStride);
2211 wxCHECK_HRESULT_RET(hr);
2212
2213 UINT bufferSize = 0;
2214 BYTE* pBuffer = NULL;
2215 hr = pLock->GetDataPointer(&bufferSize, &pBuffer);
2216 wxCHECK_HRESULT_RET(hr);
2217
2218 const unsigned char* imgRGB = img.GetData(); // source RGB buffer
2219 const unsigned char* imgAlpha = img.GetAlpha(); // source alpha buffer
2220 BYTE* pBmpBuffer = pBuffer;
2221 for ( int y = 0; y < height; y++ )
2222 {
2223 BYTE* pPixByte = pBmpBuffer;
2224 for ( int x = 0; x < width; x++ )
2225 {
2226 unsigned char r = *imgRGB++;
2227 unsigned char g = *imgRGB++;
2228 unsigned char b = *imgRGB++;
2229 if ( imgAlpha )
2230 {
2231 unsigned char a = *imgAlpha++;
2232 // Premultiply RGB values
2233 *pPixByte++ = (b * a + 127) / 255;
2234 *pPixByte++ = (g * a + 127) / 255;
2235 *pPixByte++ = (r * a + 127) / 255;
2236 *pPixByte++ = a;
2237 }
2238 else
2239 {
2240 *pPixByte++ = b;
2241 *pPixByte++ = g;
2242 *pPixByte++ = r;
2243 *pPixByte++ = 255;
2244 }
2245 }
2246
2247 pBmpBuffer += rowStride;
2248 }
2249
2250 // If there is a mask, set the alpha bytes in the target buffer to
2251 // fully transparent or retain original value
2252 if ( img.HasMask() )
2253 {
2254 unsigned char mr = img.GetMaskRed();
2255 unsigned char mg = img.GetMaskGreen();
2256 unsigned char mb = img.GetMaskBlue();
2257
2258 imgRGB = img.GetData();
2259 pBmpBuffer = pBuffer;
2260 for ( int y = 0; y < height; y++ )
2261 {
2262 BYTE* pPixByte = pBmpBuffer;
2263 for ( int x = 0; x < width; x++ )
2264 {
2265 if ( imgRGB[0] == mr && imgRGB[1] == mg && imgRGB[2] == mb )
2266 pPixByte[0] = pPixByte[1] = pPixByte[2] = pPixByte[3] = 0;
2267
2268 imgRGB += 3;
2269 pPixByte += 4;
2270 }
2271
2272 pBmpBuffer += rowStride;
2273 }
2274 }
2275 }
2276
CreateImageFromWICBitmap(IWICBitmap * pBmp,wxImage * pImg)2277 void CreateImageFromWICBitmap(IWICBitmap* pBmp, wxImage* pImg)
2278 {
2279 UINT width, height;
2280 HRESULT hr = pBmp->GetSize(&width, &height);
2281 wxCHECK_HRESULT_RET(hr);
2282
2283 WICRect rcLock = { 0, 0, (INT)width, (INT)height };
2284 wxCOMPtr<IWICBitmapLock> pLock;
2285 hr = pBmp->Lock(&rcLock, WICBitmapLockRead, &pLock);
2286 wxCHECK_HRESULT_RET(hr);
2287
2288 UINT rowStride = 0;
2289 hr = pLock->GetStride(&rowStride);
2290 wxCHECK_HRESULT_RET(hr);
2291
2292 UINT bufferSize = 0;
2293 BYTE* pBmpBuffer = NULL;
2294 hr = pLock->GetDataPointer(&bufferSize, &pBmpBuffer);
2295 wxCHECK_HRESULT_RET(hr);
2296
2297 WICPixelFormatGUID pixelFormat;
2298 hr = pLock->GetPixelFormat(&pixelFormat);
2299 wxCHECK_HRESULT_RET(hr);
2300 wxASSERT_MSG(pixelFormat == GUID_WICPixelFormat32bppPBGRA || pixelFormat == GUID_WICPixelFormat32bppBGR,
2301 "Unsupported pixel format");
2302
2303 // Only premultiplied ARGB bitmaps are supported.
2304 const bool hasAlpha = pixelFormat == GUID_WICPixelFormat32bppPBGRA;
2305
2306 if ( pImg->IsOk() )
2307 {
2308 if ( pImg->GetWidth() != (int)width || pImg->GetHeight() != (int)height )
2309 {
2310 pImg->Resize(wxSize(width, height), wxPoint(0, 0));
2311 }
2312 }
2313 else
2314 {
2315 pImg->Create(width, height);
2316 }
2317 if ( hasAlpha && !pImg->HasAlpha() )
2318 {
2319 pImg->SetAlpha();
2320 }
2321 pImg->SetMask(false);
2322
2323 unsigned char* destRGB = pImg->GetData();
2324 unsigned char* destAlpha = pImg->GetAlpha();
2325 for ( UINT y = 0; y < height; y++ )
2326 {
2327 BYTE* pPixByte = pBmpBuffer;
2328 for ( UINT x = 0; x < width; x++ )
2329 {
2330 wxPBGRAColor color = wxPBGRAColor(pPixByte);
2331 unsigned char a = hasAlpha ? color.a : wxIMAGE_ALPHA_OPAQUE;
2332 // Undo premultiplication for ARGB bitmap
2333 *destRGB++ = (a > 0 && a < 255)?(color.r * 255) / a : color.r;
2334 *destRGB++ = (a > 0 && a < 255)?(color.g * 255) / a : color.g;
2335 *destRGB++ = (a > 0 && a < 255)?(color.b * 255) / a : color.b;
2336 if ( destAlpha )
2337 *destAlpha++ = a;
2338
2339 pPixByte += 4;
2340 }
2341
2342 pBmpBuffer += rowStride;
2343 }
2344 }
2345 #endif // wxUSE_IMAGE
2346 };
2347
2348 // WIC Bitmap Source for creating hatch patterned bitmaps
2349 class wxHatchBitmapSource : public IWICBitmapSource
2350 {
2351 public:
wxHatchBitmapSource(wxBrushStyle brushStyle,const wxColor & color)2352 wxHatchBitmapSource(wxBrushStyle brushStyle, const wxColor& color) :
2353 m_brushStyle(brushStyle), m_color(color), m_refCount(0l)
2354 {
2355 }
2356
~wxHatchBitmapSource()2357 virtual ~wxHatchBitmapSource() {}
2358
GetSize(__RPC__out UINT * width,__RPC__out UINT * height)2359 HRESULT STDMETHODCALLTYPE GetSize(__RPC__out UINT *width, __RPC__out UINT *height) wxOVERRIDE
2360 {
2361 if (width != NULL) *width = 8;
2362 if (height != NULL) *height = 8;
2363 return S_OK;
2364 }
2365
GetPixelFormat(__RPC__out WICPixelFormatGUID * pixelFormat)2366 HRESULT STDMETHODCALLTYPE GetPixelFormat(__RPC__out WICPixelFormatGUID *pixelFormat) wxOVERRIDE
2367 {
2368 if (pixelFormat != NULL) *pixelFormat = GUID_WICPixelFormat32bppPBGRA;
2369 return S_OK;
2370 }
2371
GetResolution(__RPC__out double * dpiX,__RPC__out double * dpiY)2372 HRESULT STDMETHODCALLTYPE GetResolution(__RPC__out double *dpiX, __RPC__out double *dpiY) wxOVERRIDE
2373 {
2374 if (dpiX != NULL) *dpiX = 96.0;
2375 if (dpiY != NULL) *dpiY = 96.0;
2376 return S_OK;
2377 }
2378
CopyPalette(__RPC__in_opt IWICPalette * WXUNUSED (palette))2379 HRESULT STDMETHODCALLTYPE CopyPalette(__RPC__in_opt IWICPalette* WXUNUSED(palette)) wxOVERRIDE
2380 {
2381 return WINCODEC_ERR_PALETTEUNAVAILABLE;
2382 }
2383
CopyPixels(const WICRect * WXUNUSED (prc),UINT WXUNUSED (stride),UINT WXUNUSED (bufferSize),BYTE * buffer)2384 HRESULT STDMETHODCALLTYPE CopyPixels(
2385 const WICRect* WXUNUSED(prc),
2386 UINT WXUNUSED(stride),
2387 UINT WXUNUSED(bufferSize),
2388 BYTE *buffer) wxOVERRIDE
2389 {
2390 // patterns are encoded in a bit map of size 8 x 8
2391 static const unsigned char BDIAGONAL_PATTERN[8] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
2392 static const unsigned char FDIAGONAL_PATTERN[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
2393 static const unsigned char CROSSDIAG_PATTERN[8] = { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 };
2394 static const unsigned char CROSS_PATTERN[8] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFF };
2395 static const unsigned char HORIZONTAL_PATTERN[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF };
2396 static const unsigned char VERTICAL_PATTERN[8] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 };
2397
2398 switch (m_brushStyle)
2399 {
2400 case wxBRUSHSTYLE_BDIAGONAL_HATCH:
2401 CopyPattern(buffer, BDIAGONAL_PATTERN);
2402 break;
2403 case wxBRUSHSTYLE_CROSSDIAG_HATCH:
2404 CopyPattern(buffer, CROSSDIAG_PATTERN);
2405 break;
2406 case wxBRUSHSTYLE_FDIAGONAL_HATCH:
2407 CopyPattern(buffer, FDIAGONAL_PATTERN);
2408 break;
2409 case wxBRUSHSTYLE_CROSS_HATCH:
2410 CopyPattern(buffer, CROSS_PATTERN);
2411 break;
2412 case wxBRUSHSTYLE_HORIZONTAL_HATCH:
2413 CopyPattern(buffer, HORIZONTAL_PATTERN);
2414 break;
2415 case wxBRUSHSTYLE_VERTICAL_HATCH:
2416 CopyPattern(buffer, VERTICAL_PATTERN);
2417 break;
2418 default:
2419 break;
2420 }
2421
2422 return S_OK;
2423 }
2424
2425 // Implementations adapted from: "Implementing IUnknown in C++"
2426 // http://msdn.microsoft.com/en-us/library/office/cc839627%28v=office.15%29.aspx
2427
QueryInterface(REFIID referenceId,void ** object)2428 HRESULT STDMETHODCALLTYPE QueryInterface(REFIID referenceId, void** object) wxOVERRIDE
2429 {
2430 if (!object)
2431 {
2432 return E_INVALIDARG;
2433 }
2434
2435 *object = NULL;
2436
2437 if (referenceId == IID_IUnknown || referenceId == wxIID_IWICBitmapSource)
2438 {
2439 *object = (LPVOID)this;
2440 AddRef();
2441 return NOERROR;
2442 }
2443
2444 return E_NOINTERFACE;
2445 }
2446
AddRef(void)2447 ULONG STDMETHODCALLTYPE AddRef(void) wxOVERRIDE
2448 {
2449 InterlockedIncrement(&m_refCount);
2450 return m_refCount;
2451 }
2452
Release(void)2453 ULONG STDMETHODCALLTYPE Release(void) wxOVERRIDE
2454 {
2455 wxCHECK_MSG(m_refCount > 0, 0, "Unbalanced number of calls to Release");
2456
2457 ULONG refCount = InterlockedDecrement(&m_refCount);
2458 if (m_refCount == 0)
2459 {
2460 delete this;
2461 }
2462 return refCount;
2463 }
2464
2465 private:
2466
2467 // Copies an 8x8 bit pattern to a PBGRA byte buffer
CopyPattern(BYTE * buffer,const unsigned char * pattern) const2468 void CopyPattern(BYTE* buffer, const unsigned char* pattern) const
2469 {
2470 static const wxPBGRAColor transparent(wxTransparentColour);
2471
2472 int k = 0;
2473
2474 for (int i = 0; i < 8; ++i)
2475 {
2476 for (int j = 7; j >= 0; --j)
2477 {
2478 bool isColorBit = (pattern[i] & (1 << j)) > 0;
2479 (isColorBit ? m_color : transparent).Write(buffer + k);
2480 k += 4;
2481 }
2482 }
2483 }
2484
2485 private:
2486 // The hatch style produced by this bitmap source
2487 const wxBrushStyle m_brushStyle;
2488
2489 // The colour of the hatch
2490 const wxPBGRAColor m_color;
2491
2492 // Internally used to implement IUnknown's reference counting
2493 ULONG m_refCount;
2494 };
2495
2496 // RAII class hosting a WIC bitmap lock used for writing
2497 // pixel data to a WICBitmap
2498 class wxBitmapPixelWriteLock
2499 {
2500 public:
wxBitmapPixelWriteLock(IWICBitmap * bitmap)2501 wxBitmapPixelWriteLock(IWICBitmap* bitmap)
2502 {
2503 // Retrieve the size of the bitmap
2504 UINT w, h;
2505 bitmap->GetSize(&w, &h);
2506 WICRect lockSize = {0, 0, (INT)w, (INT)h};
2507
2508 // Obtain a bitmap lock for exclusive write
2509 bitmap->Lock(&lockSize, WICBitmapLockWrite, &m_pixelLock);
2510 }
2511
GetLock()2512 IWICBitmapLock* GetLock() { return m_pixelLock; }
2513
2514 private:
2515 wxCOMPtr<IWICBitmapLock> m_pixelLock;
2516 };
2517
2518 class wxD2DBitmapResourceHolder : public wxD2DResourceHolder<ID2D1Bitmap>
2519 {
2520 public:
wxD2DBitmapResourceHolder(const wxBitmap & sourceBitmap)2521 wxD2DBitmapResourceHolder(const wxBitmap& sourceBitmap)
2522 {
2523 HRESULT hr;
2524 if ( sourceBitmap.GetMask() )
2525 {
2526 int w = sourceBitmap.GetWidth();
2527 int h = sourceBitmap.GetHeight();
2528
2529 wxCOMPtr<IWICBitmapSource> colorBitmap = wxCreateWICBitmap(sourceBitmap, true);
2530 wxCOMPtr<IWICBitmapSource> maskBitmap = wxCreateWICBitmap(sourceBitmap.GetMask()->GetBitmap(), false);
2531
2532 hr = wxWICImagingFactory()->CreateBitmap(w, h, GUID_WICPixelFormat32bppPBGRA, WICBitmapCacheOnLoad, &m_srcBitmap);
2533 wxCHECK_HRESULT_RET(hr);
2534
2535 BYTE* colorBuffer = new BYTE[4 * w * h];
2536 BYTE* maskBuffer = new BYTE[4 * w * h];
2537 BYTE* resultBuffer;
2538
2539 hr = colorBitmap->CopyPixels(NULL, w * 4, 4 * w * h, colorBuffer);
2540 wxCHECK_HRESULT_RET(hr);
2541 hr = maskBitmap->CopyPixels(NULL, w * 4, 4 * w * h, maskBuffer);
2542 wxCHECK_HRESULT_RET(hr);
2543
2544 {
2545 wxBitmapPixelWriteLock lock(m_srcBitmap);
2546
2547 UINT bufferSize = 0;
2548 hr = lock.GetLock()->GetDataPointer(&bufferSize, &resultBuffer);
2549
2550 static const wxPBGRAColor transparentColor(wxTransparentColour);
2551
2552 // Create the result bitmap
2553 for ( int i = 0; i < w * h * 4; i += 4 )
2554 {
2555 wxPBGRAColor color(colorBuffer + i);
2556 wxPBGRAColor mask(maskBuffer + i);
2557
2558 if ( mask.IsBlack() )
2559 {
2560 transparentColor.Write(resultBuffer + i);
2561 }
2562 else
2563 {
2564 color.Write(resultBuffer + i);
2565 }
2566 }
2567 }
2568
2569 delete[] colorBuffer;
2570 delete[] maskBuffer;
2571 }
2572 else
2573 {
2574 wxCOMPtr<IWICBitmapSource> srcBmp = wxCreateWICBitmap(sourceBitmap, false);
2575 hr = wxWICImagingFactory()->CreateBitmapFromSource(srcBmp, WICBitmapNoCache, &m_srcBitmap);
2576 wxCHECK_HRESULT_RET(hr);
2577 }
2578 }
2579
wxD2DBitmapResourceHolder(IWICBitmap * pSrcBmp)2580 wxD2DBitmapResourceHolder(IWICBitmap* pSrcBmp) :
2581 m_srcBitmap(pSrcBmp)
2582 {
2583 }
2584
GetSize() const2585 wxSize GetSize() const
2586 {
2587 UINT w, h;
2588 HRESULT hr = m_srcBitmap->GetSize(&w, &h);
2589 wxCHECK2_HRESULT_RET(hr, wxSize());
2590
2591 return wxSize((int)w, (int)h);
2592 }
2593
2594 #if wxUSE_IMAGE
wxD2DBitmapResourceHolder(const wxImage & img)2595 wxD2DBitmapResourceHolder(const wxImage& img)
2596 {
2597 CreateWICBitmapFromImage(img, false, &m_srcBitmap);
2598 }
2599
ConvertToImage() const2600 wxImage ConvertToImage() const
2601 {
2602 wxImage img;
2603 CreateImageFromWICBitmap(m_srcBitmap, &img);
2604
2605 return img;
2606 }
2607 #endif // wxUSE_IMAGE
2608
GetSubBitmap(wxDouble x,wxDouble y,wxDouble w,wxDouble h) const2609 wxD2DBitmapResourceHolder* GetSubBitmap(wxDouble x, wxDouble y, wxDouble w, wxDouble h) const
2610 {
2611 wxCOMPtr<IWICBitmapClipper> clipper;
2612 HRESULT hr = wxWICImagingFactory()->CreateBitmapClipper(&clipper);
2613 wxCHECK2_HRESULT_RET(hr, NULL);
2614
2615 WICRect r = { (INT)x, (INT)y, (INT)w, (INT)h };
2616 hr = clipper->Initialize(m_srcBitmap, &r);
2617 wxCHECK2_HRESULT_RET(hr, NULL);
2618
2619 wxCOMPtr<IWICBitmap> subBmp;
2620 hr = wxWICImagingFactory()->CreateBitmapFromSource(clipper, WICBitmapNoCache, &subBmp);
2621 wxCHECK2_HRESULT_RET(hr, NULL);
2622
2623 return new wxD2DBitmapResourceHolder(subBmp);
2624 }
2625
2626 protected:
DoAcquireResource()2627 void DoAcquireResource() wxOVERRIDE
2628 {
2629 HRESULT hr = GetContext()->CreateBitmapFromWicBitmap(m_srcBitmap, 0, &m_nativeResource);
2630 wxCHECK_HRESULT_RET(hr);
2631 }
2632
2633 private:
2634 wxCOMPtr<IWICBitmap> m_srcBitmap;
2635 };
2636
2637 //-----------------------------------------------------------------------------
2638 // wxD2DBitmapData declaration
2639 //-----------------------------------------------------------------------------
2640
2641 class wxD2DBitmapData : public wxGraphicsBitmapData, public wxD2DManagedGraphicsData
2642 {
2643 public:
2644 typedef wxD2DBitmapResourceHolder NativeType;
2645
wxD2DBitmapData(wxGraphicsRenderer * renderer,const wxBitmap & bitmap)2646 wxD2DBitmapData(wxGraphicsRenderer* renderer, const wxBitmap& bitmap) :
2647 wxGraphicsBitmapData(renderer)
2648 {
2649 m_bitmapHolder = new NativeType(bitmap);
2650 }
2651
wxD2DBitmapData(wxGraphicsRenderer * renderer,const wxImage & image)2652 wxD2DBitmapData(wxGraphicsRenderer* renderer, const wxImage& image) :
2653 wxGraphicsBitmapData(renderer)
2654 {
2655 m_bitmapHolder = new NativeType(image);
2656 }
2657
wxD2DBitmapData(wxGraphicsRenderer * renderer,NativeType * pseudoNativeBitmap)2658 wxD2DBitmapData(wxGraphicsRenderer* renderer, NativeType* pseudoNativeBitmap) :
2659 wxGraphicsBitmapData(renderer), m_bitmapHolder(pseudoNativeBitmap) {}
2660
2661 ~wxD2DBitmapData();
2662
2663 // returns the native representation
2664 void* GetNativeBitmap() const wxOVERRIDE;
2665
2666 wxCOMPtr<ID2D1Bitmap> GetD2DBitmap();
2667
GetManagedObject()2668 wxD2DManagedObject* GetManagedObject() wxOVERRIDE
2669 {
2670 return m_bitmapHolder;
2671 }
2672
2673 private:
2674 NativeType* m_bitmapHolder;
2675 };
2676
2677 //-----------------------------------------------------------------------------
2678 // wxD2DBitmapData implementation
2679 //-----------------------------------------------------------------------------
2680
~wxD2DBitmapData()2681 wxD2DBitmapData::~wxD2DBitmapData()
2682 {
2683 delete m_bitmapHolder;
2684 }
2685
GetNativeBitmap() const2686 void* wxD2DBitmapData::GetNativeBitmap() const
2687 {
2688 return static_cast<void*>(m_bitmapHolder);
2689 }
2690
GetD2DBitmap()2691 wxCOMPtr<ID2D1Bitmap> wxD2DBitmapData::GetD2DBitmap()
2692 {
2693 return m_bitmapHolder->GetD2DResource();
2694 }
2695
wxGetD2DBitmapData(const wxGraphicsBitmap & bitmap)2696 wxD2DBitmapData* wxGetD2DBitmapData(const wxGraphicsBitmap& bitmap)
2697 {
2698 return static_cast<wxD2DBitmapData*>(bitmap.GetRefData());
2699 }
2700
2701 // Helper class used to create and safely release a ID2D1GradientStopCollection from wxGraphicsGradientStops
2702 class wxD2DGradientStopsHelper : public wxD2DResourceHolder<ID2D1GradientStopCollection>
2703 {
2704 public:
wxD2DGradientStopsHelper(const wxGraphicsGradientStops & gradientStops)2705 wxD2DGradientStopsHelper(const wxGraphicsGradientStops& gradientStops)
2706 {
2707 const int stopCount = gradientStops.GetCount();
2708 m_gradientStops.reserve(stopCount);
2709 for ( int i = 0; i < stopCount; ++i )
2710 {
2711 D2D1_GRADIENT_STOP stop;
2712 stop.position = gradientStops.Item(i).GetPosition();
2713 stop.color = wxD2DConvertColour(gradientStops.Item(i).GetColour());
2714 m_gradientStops.push_back(stop);
2715 }
2716 }
2717
2718 protected:
DoAcquireResource()2719 void DoAcquireResource() wxOVERRIDE
2720 {
2721 wxCHECK_RET(!m_gradientStops.empty(), "No gradient stops provided");
2722
2723 HRESULT hr = GetContext()->CreateGradientStopCollection(&m_gradientStops[0],
2724 m_gradientStops.size(), D2D1_GAMMA_2_2, D2D1_EXTEND_MODE_CLAMP, &m_nativeResource);
2725 wxCHECK_HRESULT_RET(hr);
2726 }
2727
2728 private:
2729 wxVector<D2D1_GRADIENT_STOP> m_gradientStops;
2730 };
2731
2732 template <typename B>
2733 class wxD2DBrushResourceHolder : public wxD2DResourceHolder<B>
2734 {
2735 public:
wxD2DBrushResourceHolder(const wxBrush & brush)2736 wxD2DBrushResourceHolder(const wxBrush& brush) : m_sourceBrush(brush) {}
~wxD2DBrushResourceHolder()2737 virtual ~wxD2DBrushResourceHolder() {}
2738 protected:
2739 const wxBrush m_sourceBrush;
2740 };
2741
2742 class wxD2DSolidBrushResourceHolder : public wxD2DBrushResourceHolder<ID2D1SolidColorBrush>
2743 {
2744 public:
wxD2DSolidBrushResourceHolder(const wxBrush & brush)2745 wxD2DSolidBrushResourceHolder(const wxBrush& brush) : wxD2DBrushResourceHolder(brush) {}
2746
2747 protected:
DoAcquireResource()2748 void DoAcquireResource() wxOVERRIDE
2749 {
2750 wxColour colour = m_sourceBrush.GetColour();
2751 HRESULT hr = GetContext()->CreateSolidColorBrush(wxD2DConvertColour(colour), &m_nativeResource);
2752 wxCHECK_HRESULT_RET(hr);
2753 }
2754 };
2755
2756 class wxD2DBitmapBrushResourceHolder : public wxD2DBrushResourceHolder<ID2D1BitmapBrush>
2757 {
2758 public:
wxD2DBitmapBrushResourceHolder(const wxBrush & brush)2759 wxD2DBitmapBrushResourceHolder(const wxBrush& brush) : wxD2DBrushResourceHolder(brush) {}
2760
2761 protected:
DoAcquireResource()2762 void DoAcquireResource() wxOVERRIDE
2763 {
2764 // TODO: cache this bitmap
2765 wxD2DBitmapResourceHolder bitmap(*(m_sourceBrush.GetStipple()));
2766 bitmap.Bind(GetManager());
2767
2768 HRESULT result = GetContext()->CreateBitmapBrush(
2769 bitmap.GetD2DResource(),
2770 D2D1::BitmapBrushProperties(
2771 D2D1_EXTEND_MODE_WRAP,
2772 D2D1_EXTEND_MODE_WRAP,
2773 D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR),
2774 &m_nativeResource);
2775
2776 wxCHECK_HRESULT_RET(result);
2777 }
2778 };
2779
2780 class wxD2DHatchBrushResourceHolder : public wxD2DBrushResourceHolder<ID2D1BitmapBrush>
2781 {
2782 public:
wxD2DHatchBrushResourceHolder(const wxBrush & brush)2783 wxD2DHatchBrushResourceHolder(const wxBrush& brush) : wxD2DBrushResourceHolder(brush) {}
2784
2785 protected:
DoAcquireResource()2786 void DoAcquireResource() wxOVERRIDE
2787 {
2788 wxCOMPtr<wxHatchBitmapSource> hatchBitmapSource(new wxHatchBitmapSource(m_sourceBrush.GetStyle(), m_sourceBrush.GetColour()));
2789
2790 wxCOMPtr<ID2D1Bitmap> bitmap;
2791
2792 HRESULT hr = GetContext()->CreateBitmapFromWicBitmap(hatchBitmapSource, &bitmap);
2793 wxCHECK_HRESULT_RET(hr);
2794
2795 hr = GetContext()->CreateBitmapBrush(
2796 bitmap,
2797 D2D1::BitmapBrushProperties(
2798 D2D1_EXTEND_MODE_WRAP,
2799 D2D1_EXTEND_MODE_WRAP,
2800 D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR),
2801 &m_nativeResource);
2802 wxCHECK_HRESULT_RET(hr);
2803 }
2804 };
2805
2806 class wxD2DLinearGradientBrushResourceHolder : public wxD2DResourceHolder<ID2D1LinearGradientBrush>
2807 {
2808 public:
2809 struct LinearGradientInfo {
2810 const wxDouble x1;
2811 const wxDouble y1;
2812 const wxDouble x2;
2813 const wxDouble y2;
2814 const wxGraphicsGradientStops stops;
2815 const wxGraphicsMatrix matrix;
LinearGradientInfowxD2DLinearGradientBrushResourceHolder::LinearGradientInfo2816 LinearGradientInfo(wxDouble& x1_, wxDouble& y1_,
2817 wxDouble& x2_, wxDouble& y2_,
2818 const wxGraphicsGradientStops& stops_,
2819 const wxGraphicsMatrix& matrix_)
2820 : x1(x1_), y1(y1_), x2(x2_), y2(y2_), stops(stops_), matrix(matrix_) {}
2821 };
2822
wxD2DLinearGradientBrushResourceHolder(wxDouble & x1,wxDouble & y1,wxDouble & x2,wxDouble & y2,const wxGraphicsGradientStops & stops,const wxGraphicsMatrix & matrix)2823 wxD2DLinearGradientBrushResourceHolder(wxDouble& x1, wxDouble& y1,
2824 wxDouble& x2, wxDouble& y2,
2825 const wxGraphicsGradientStops& stops,
2826 const wxGraphicsMatrix& matrix)
2827 : m_linearGradientInfo(x1, y1, x2, y2, stops, matrix) {}
2828
2829 protected:
DoAcquireResource()2830 void DoAcquireResource() wxOVERRIDE
2831 {
2832 wxD2DGradientStopsHelper helper(m_linearGradientInfo.stops);
2833 helper.Bind(GetManager());
2834
2835 HRESULT hr = GetContext()->CreateLinearGradientBrush(
2836 D2D1::LinearGradientBrushProperties(
2837 D2D1::Point2F(m_linearGradientInfo.x1, m_linearGradientInfo.y1),
2838 D2D1::Point2F(m_linearGradientInfo.x2, m_linearGradientInfo.y2)),
2839 helper.GetD2DResource(),
2840 &m_nativeResource);
2841 wxCHECK_HRESULT_RET(hr);
2842
2843 if (! m_linearGradientInfo.matrix.IsNull())
2844 {
2845 D2D1::Matrix3x2F matrix = wxGetD2DMatrixData(m_linearGradientInfo.matrix)->GetMatrix3x2F();
2846 matrix.Invert();
2847 m_nativeResource->SetTransform(matrix);
2848 }
2849 }
2850 private:
2851 const LinearGradientInfo m_linearGradientInfo;
2852 };
2853
2854 class wxD2DRadialGradientBrushResourceHolder : public wxD2DResourceHolder<ID2D1RadialGradientBrush>
2855 {
2856 public:
2857 struct RadialGradientInfo {
2858 const wxDouble x1;
2859 const wxDouble y1;
2860 const wxDouble x2;
2861 const wxDouble y2;
2862 const wxDouble radius;
2863 const wxGraphicsGradientStops stops;
2864 const wxGraphicsMatrix matrix;
2865
RadialGradientInfowxD2DRadialGradientBrushResourceHolder::RadialGradientInfo2866 RadialGradientInfo(wxDouble x1_, wxDouble y1_,
2867 wxDouble x2_, wxDouble y2_,
2868 wxDouble r,
2869 const wxGraphicsGradientStops& stops_,
2870 const wxGraphicsMatrix& matrix_)
2871 : x1(x1_), y1(y1_), x2(x2_), y2(y2_), radius(r), stops(stops_), matrix(matrix_) {}
2872 };
2873
wxD2DRadialGradientBrushResourceHolder(wxDouble & x1,wxDouble & y1,wxDouble & x2,wxDouble & y2,wxDouble & r,const wxGraphicsGradientStops & stops,const wxGraphicsMatrix & matrix)2874 wxD2DRadialGradientBrushResourceHolder(wxDouble& x1, wxDouble& y1,
2875 wxDouble& x2, wxDouble& y2,
2876 wxDouble& r,
2877 const wxGraphicsGradientStops& stops,
2878 const wxGraphicsMatrix& matrix)
2879 : m_radialGradientInfo(x1, y1, x2, y2, r, stops, matrix) {}
2880
2881 protected:
DoAcquireResource()2882 void DoAcquireResource() wxOVERRIDE
2883 {
2884 wxD2DGradientStopsHelper helper(m_radialGradientInfo.stops);
2885 helper.Bind(GetManager());
2886
2887 wxDouble xo = m_radialGradientInfo.x1 - m_radialGradientInfo.x2;
2888 wxDouble yo = m_radialGradientInfo.y1 - m_radialGradientInfo.y2;
2889
2890 HRESULT hr = GetContext()->CreateRadialGradientBrush(
2891 D2D1::RadialGradientBrushProperties(
2892 D2D1::Point2F(m_radialGradientInfo.x1, m_radialGradientInfo.y1),
2893 D2D1::Point2F(xo, yo),
2894 m_radialGradientInfo.radius, m_radialGradientInfo.radius),
2895 helper.GetD2DResource(),
2896 &m_nativeResource);
2897 wxCHECK_HRESULT_RET(hr);
2898
2899 if (! m_radialGradientInfo.matrix.IsNull())
2900 {
2901 D2D1::Matrix3x2F matrix = wxGetD2DMatrixData(m_radialGradientInfo.matrix)->GetMatrix3x2F();
2902 matrix.Invert();
2903 m_nativeResource->SetTransform(matrix);
2904 }
2905 }
2906
2907 private:
2908 const RadialGradientInfo m_radialGradientInfo;
2909 };
2910
2911 //-----------------------------------------------------------------------------
2912 // wxD2DBrushData declaration
2913 //-----------------------------------------------------------------------------
2914
2915 class wxD2DBrushData : public wxGraphicsObjectRefData, public wxD2DManagedGraphicsData
2916 {
2917 public:
2918 wxD2DBrushData(wxGraphicsRenderer* renderer, const wxBrush brush);
2919
2920 wxD2DBrushData(wxGraphicsRenderer* renderer);
2921
2922 void CreateLinearGradientBrush(wxDouble x1, wxDouble y1,
2923 wxDouble x2, wxDouble y2,
2924 const wxGraphicsGradientStops& stops,
2925 const wxGraphicsMatrix& matrix = wxNullGraphicsMatrix);
2926
2927 void CreateRadialGradientBrush(wxDouble startX, wxDouble startY,
2928 wxDouble endX, wxDouble endY,
2929 wxDouble radius,
2930 const wxGraphicsGradientStops& stops,
2931 const wxGraphicsMatrix& matrix = wxNullGraphicsMatrix);
2932
GetBrush() const2933 ID2D1Brush* GetBrush() const
2934 {
2935 return (ID2D1Brush*)(m_brushResourceHolder->GetResource());
2936 }
2937
GetManagedObject()2938 wxD2DManagedObject* GetManagedObject() wxOVERRIDE
2939 {
2940 return m_brushResourceHolder.get();
2941 }
2942
2943 private:
2944 wxSharedPtr<wxManagedResourceHolder> m_brushResourceHolder;
2945 };
2946
2947 //-----------------------------------------------------------------------------
2948 // wxD2DBrushData implementation
2949 //-----------------------------------------------------------------------------
2950
wxD2DBrushData(wxGraphicsRenderer * renderer,const wxBrush brush)2951 wxD2DBrushData::wxD2DBrushData(wxGraphicsRenderer* renderer, const wxBrush brush)
2952 : wxGraphicsObjectRefData(renderer), m_brushResourceHolder(NULL)
2953 {
2954 if (brush.GetStyle() == wxBRUSHSTYLE_SOLID)
2955 {
2956 m_brushResourceHolder = new wxD2DSolidBrushResourceHolder(brush);
2957 }
2958 else if (brush.IsHatch())
2959 {
2960 m_brushResourceHolder = new wxD2DHatchBrushResourceHolder(brush);
2961 }
2962 else
2963 {
2964 m_brushResourceHolder = new wxD2DBitmapBrushResourceHolder(brush);
2965 }
2966 }
2967
wxD2DBrushData(wxGraphicsRenderer * renderer)2968 wxD2DBrushData::wxD2DBrushData(wxGraphicsRenderer* renderer)
2969 : wxGraphicsObjectRefData(renderer), m_brushResourceHolder(NULL)
2970 {
2971 }
2972
CreateLinearGradientBrush(wxDouble x1,wxDouble y1,wxDouble x2,wxDouble y2,const wxGraphicsGradientStops & stops,const wxGraphicsMatrix & matrix)2973 void wxD2DBrushData::CreateLinearGradientBrush(
2974 wxDouble x1, wxDouble y1,
2975 wxDouble x2, wxDouble y2,
2976 const wxGraphicsGradientStops& stops,
2977 const wxGraphicsMatrix& matrix)
2978 {
2979 m_brushResourceHolder = new wxD2DLinearGradientBrushResourceHolder(
2980 x1, y1, x2, y2, stops, matrix);
2981 }
2982
CreateRadialGradientBrush(wxDouble startX,wxDouble startY,wxDouble endX,wxDouble endY,wxDouble radius,const wxGraphicsGradientStops & stops,const wxGraphicsMatrix & matrix)2983 void wxD2DBrushData::CreateRadialGradientBrush(
2984 wxDouble startX, wxDouble startY,
2985 wxDouble endX, wxDouble endY,
2986 wxDouble radius,
2987 const wxGraphicsGradientStops& stops,
2988 const wxGraphicsMatrix& matrix)
2989 {
2990 m_brushResourceHolder = new wxD2DRadialGradientBrushResourceHolder(
2991 startX, startY, endX, endY, radius, stops, matrix);
2992 }
2993
wxGetD2DBrushData(const wxGraphicsBrush & brush)2994 wxD2DBrushData* wxGetD2DBrushData(const wxGraphicsBrush& brush)
2995 {
2996 return static_cast<wxD2DBrushData*>(brush.GetGraphicsData());
2997 }
2998
wxIsHatchPenStyle(wxPenStyle penStyle)2999 bool wxIsHatchPenStyle(wxPenStyle penStyle)
3000 {
3001 return penStyle >= wxPENSTYLE_FIRST_HATCH && penStyle <= wxPENSTYLE_LAST_HATCH;
3002 }
3003
wxConvertPenStyleToBrushStyle(wxPenStyle penStyle)3004 wxBrushStyle wxConvertPenStyleToBrushStyle(wxPenStyle penStyle)
3005 {
3006 switch(penStyle)
3007 {
3008 case wxPENSTYLE_BDIAGONAL_HATCH:
3009 return wxBRUSHSTYLE_BDIAGONAL_HATCH;
3010 case wxPENSTYLE_CROSSDIAG_HATCH:
3011 return wxBRUSHSTYLE_CROSSDIAG_HATCH;
3012 case wxPENSTYLE_FDIAGONAL_HATCH:
3013 return wxBRUSHSTYLE_FDIAGONAL_HATCH;
3014 case wxPENSTYLE_CROSS_HATCH:
3015 return wxBRUSHSTYLE_CROSS_HATCH;
3016 case wxPENSTYLE_HORIZONTAL_HATCH:
3017 return wxBRUSHSTYLE_HORIZONTAL_HATCH;
3018 case wxPENSTYLE_VERTICAL_HATCH:
3019 return wxBRUSHSTYLE_VERTICAL_HATCH;
3020 default:
3021 break;
3022 }
3023
3024 return wxBRUSHSTYLE_SOLID;
3025 }
3026
3027 //-----------------------------------------------------------------------------
3028 // wxD2DPenData declaration
3029 //-----------------------------------------------------------------------------
3030
3031 class wxD2DPenData : public wxGraphicsObjectRefData, public wxD2DManagedGraphicsData
3032 {
3033 public:
3034 wxD2DPenData(wxGraphicsRenderer* renderer,
3035 ID2D1Factory* direct2dFactory,
3036 const wxGraphicsPenInfo& info);
3037
3038 void CreateStrokeStyle(ID2D1Factory* const direct2dfactory);
3039
3040 ID2D1Brush* GetBrush();
3041
3042 FLOAT GetWidth();
3043 bool IsZeroWidth() const;
3044 void SetWidth(const wxGraphicsContext* context);
3045
3046 ID2D1StrokeStyle* GetStrokeStyle();
3047
GetManagedObject()3048 wxD2DManagedObject* GetManagedObject() wxOVERRIDE
3049 {
3050 return m_stippleBrush->GetManagedObject();
3051 }
3052
3053 private:
3054 // We store the original pen description for later when we need to recreate
3055 // the device-dependent resources.
3056 const wxGraphicsPenInfo m_penInfo;
3057
3058 // A stroke style is a device-independent resource.
3059 // Describes the caps, miter limit, line join, and dash information.
3060 wxCOMPtr<ID2D1StrokeStyle> m_strokeStyle;
3061
3062 // Drawing outlines with Direct2D requires a brush for the color or stipple.
3063 wxSharedPtr<wxD2DBrushData> m_stippleBrush;
3064
3065 // The width of the stroke
3066 FLOAT m_width;
3067 };
3068
3069 //-----------------------------------------------------------------------------
3070 // wxD2DPenData implementation
3071 //-----------------------------------------------------------------------------
3072
wxD2DPenData(wxGraphicsRenderer * renderer,ID2D1Factory * direct2dFactory,const wxGraphicsPenInfo & info)3073 wxD2DPenData::wxD2DPenData(
3074 wxGraphicsRenderer* renderer,
3075 ID2D1Factory* direct2dFactory,
3076 const wxGraphicsPenInfo& info)
3077 : wxGraphicsObjectRefData(renderer),
3078 m_penInfo(info),
3079 m_width(info.GetWidth())
3080 {
3081 CreateStrokeStyle(direct2dFactory);
3082
3083 wxBrush strokeBrush;
3084
3085 if (m_penInfo.GetStyle() == wxPENSTYLE_STIPPLE)
3086 {
3087 strokeBrush.SetStipple(m_penInfo.GetStipple());
3088 strokeBrush.SetStyle(wxBRUSHSTYLE_STIPPLE);
3089 }
3090 else if(wxIsHatchPenStyle(m_penInfo.GetStyle()))
3091 {
3092 strokeBrush.SetStyle(wxConvertPenStyleToBrushStyle(m_penInfo.GetStyle()));
3093 strokeBrush.SetColour(m_penInfo.GetColour());
3094 }
3095 else
3096 {
3097 strokeBrush.SetColour(m_penInfo.GetColour());
3098 strokeBrush.SetStyle(wxBRUSHSTYLE_SOLID);
3099 }
3100
3101 switch ( m_penInfo.GetGradientType() )
3102 {
3103 case wxGRADIENT_NONE:
3104 m_stippleBrush = new wxD2DBrushData(renderer, strokeBrush);
3105 break;
3106
3107 case wxGRADIENT_LINEAR:
3108 m_stippleBrush = new wxD2DBrushData(renderer);
3109 m_stippleBrush->CreateLinearGradientBrush(
3110 m_penInfo.GetX1(), m_penInfo.GetY1(),
3111 m_penInfo.GetX2(), m_penInfo.GetY2(),
3112 m_penInfo.GetStops(),
3113 m_penInfo.GetMatrix());
3114 break;
3115
3116 case wxGRADIENT_RADIAL:
3117 m_stippleBrush = new wxD2DBrushData(renderer);
3118 m_stippleBrush->CreateRadialGradientBrush(
3119 m_penInfo.GetStartX(), m_penInfo.GetStartY(),
3120 m_penInfo.GetEndX(), m_penInfo.GetEndY(),
3121 m_penInfo.GetRadius(),
3122 m_penInfo.GetStops(),
3123 m_penInfo.GetMatrix());
3124 break;
3125 }
3126 }
3127
3128
CreateStrokeStyle(ID2D1Factory * const direct2dfactory)3129 void wxD2DPenData::CreateStrokeStyle(ID2D1Factory* const direct2dfactory)
3130 {
3131 D2D1_CAP_STYLE capStyle = wxD2DConvertPenCap(m_penInfo.GetCap());
3132 D2D1_LINE_JOIN lineJoin = wxD2DConvertPenJoin(m_penInfo.GetJoin());
3133 D2D1_DASH_STYLE dashStyle = wxD2DConvertPenStyle(m_penInfo.GetStyle());
3134
3135 int dashCount = 0;
3136 FLOAT* dashes = NULL;
3137
3138 if (dashStyle == D2D1_DASH_STYLE_CUSTOM)
3139 {
3140 dashCount = m_penInfo.GetDashCount();
3141 dashes = new FLOAT[dashCount];
3142
3143 for (int i = 0; i < dashCount; ++i)
3144 {
3145 dashes[i] = m_penInfo.GetDash()[i];
3146 }
3147
3148 }
3149
3150 direct2dfactory->CreateStrokeStyle(
3151 D2D1::StrokeStyleProperties(capStyle, capStyle, capStyle, lineJoin, 0, dashStyle, 0.0f),
3152 dashes, dashCount,
3153 &m_strokeStyle);
3154
3155 delete[] dashes;
3156 }
3157
SetWidth(const wxGraphicsContext * context)3158 void wxD2DPenData::SetWidth(const wxGraphicsContext* context)
3159 {
3160 if (m_penInfo.GetWidth() <= 0)
3161 {
3162 const wxGraphicsMatrix matrix(context->GetTransform());
3163 double x = context->GetContentScaleFactor(), y = x;
3164 matrix.TransformDistance(&x, &y);
3165 m_width = 1 / wxMin(fabs(x), fabs(y));
3166 }
3167 }
3168
GetBrush()3169 ID2D1Brush* wxD2DPenData::GetBrush()
3170 {
3171 return m_stippleBrush->GetBrush();
3172 }
3173
GetWidth()3174 FLOAT wxD2DPenData::GetWidth()
3175 {
3176 return m_width;
3177 }
3178
IsZeroWidth() const3179 bool wxD2DPenData::IsZeroWidth() const
3180 {
3181 return m_penInfo.GetWidth() <= 0;
3182 }
3183
GetStrokeStyle()3184 ID2D1StrokeStyle* wxD2DPenData::GetStrokeStyle()
3185 {
3186 return m_strokeStyle;
3187 }
3188
wxGetD2DPenData(const wxGraphicsPen & pen)3189 wxD2DPenData* wxGetD2DPenData(const wxGraphicsPen& pen)
3190 {
3191 return static_cast<wxD2DPenData*>(pen.GetGraphicsData());
3192 }
3193
3194 class wxD2DFontData : public wxGraphicsObjectRefData
3195 {
3196 public:
3197 wxD2DFontData(wxGraphicsRenderer* renderer, const wxFont& font, const wxRealPoint& dpi, const wxColor& color);
3198
3199 wxCOMPtr<IDWriteTextLayout> CreateTextLayout(const wxString& text) const;
3200
GetBrushData()3201 wxD2DBrushData& GetBrushData() { return m_brushData; }
3202
GetTextFormat() const3203 wxCOMPtr<IDWriteTextFormat> GetTextFormat() const { return m_textFormat; }
3204
GetFont()3205 wxCOMPtr<IDWriteFont> GetFont() { return m_font; }
3206
3207 private:
3208 // The native, device-independent font object
3209 wxCOMPtr<IDWriteFont> m_font;
3210
3211 // The native, device-independent font object
3212 wxCOMPtr<IDWriteTextFormat> m_textFormat;
3213
3214 // We use a color brush to render the font
3215 wxD2DBrushData m_brushData;
3216
3217 bool m_underlined;
3218
3219 bool m_strikethrough;
3220 };
3221
wxD2DFontData(wxGraphicsRenderer * renderer,const wxFont & font,const wxRealPoint & dpi,const wxColor & color)3222 wxD2DFontData::wxD2DFontData(wxGraphicsRenderer* renderer, const wxFont& font, const wxRealPoint& dpi, const wxColor& color) :
3223 wxGraphicsObjectRefData(renderer), m_brushData(renderer, wxBrush(color)),
3224 m_underlined(font.GetUnderlined()), m_strikethrough(font.GetStrikethrough())
3225 {
3226 HRESULT hr;
3227
3228 wxCOMPtr<IDWriteGdiInterop> gdiInterop;
3229 hr = wxDWriteFactory()->GetGdiInterop(&gdiInterop);
3230 wxCHECK_HRESULT_RET(hr);
3231
3232 LOGFONTW logfont;
3233 int n = GetObjectW(font.GetHFONT(), sizeof(logfont), &logfont);
3234 wxCHECK_RET( n > 0, wxS("Failed to obtain font info") );
3235
3236 // Ensure the LOGFONT object contains the correct font face name
3237 if (logfont.lfFaceName[0] == L'\0')
3238 {
3239 // The length of the font name must not exceed LF_FACESIZE TCHARs,
3240 // including the terminating NULL.
3241 wxString name = font.GetFaceName().Mid(0, WXSIZEOF(logfont.lfFaceName)-1);
3242 for (unsigned int i = 0; i < name.Length(); ++i)
3243 {
3244 logfont.lfFaceName[i] = name.GetChar(i);
3245 }
3246 logfont.lfFaceName[name.Length()] = L'\0';
3247 }
3248
3249 wxCOMPtr<IDWriteFontFamily> fontFamily;
3250 wxCOMPtr<IDWriteFontCollection> fontCollection; // NULL if font is taken from the system collection
3251
3252 hr = gdiInterop->CreateFontFromLOGFONT(&logfont, &m_font);
3253 if ( hr == DWRITE_E_NOFONT )
3254 {
3255 // It was attempted to create DirectWrite font from non-TrueType GDI font
3256 // or from private GDI font.
3257 #if wxUSE_PRIVATE_FONTS
3258 // Make private fonts available to D2D.
3259 const wxArrayString& privateFonts = wxGetPrivateFontFileNames();
3260 if ( privateFonts.empty() )
3261 {
3262 wxLogApiError(wxString::Format("IDWriteGdiInterop::CreateFontFromLOGFONT() for '%s'", logfont.lfFaceName), hr);
3263 return;
3264 }
3265 // Update font collection if the list of private fonts has changed.
3266 if ( privateFonts != wxDirect2DFontCollectionLoader::GetFontList() )
3267 {
3268 wxDirect2DFontKey collectionKey = wxDirect2DFontCollectionLoader::SetFontList(privateFonts);
3269
3270 gs_pPrivateFontCollection.reset();
3271 hr = wxDWriteFactory()->CreateCustomFontCollection(
3272 wxDirect2DFontCollectionLoader::GetLoader(),
3273 &collectionKey, sizeof(collectionKey),
3274 &gs_pPrivateFontCollection);
3275 wxCHECK_HRESULT_RET(hr);
3276 }
3277 wxCHECK_RET(gs_pPrivateFontCollection != NULL, "No custom font collection created");
3278
3279 UINT32 fontIdx = ~0U;
3280 BOOL fontFound = FALSE;
3281 hr = gs_pPrivateFontCollection->FindFamilyName(logfont.lfFaceName, &fontIdx, &fontFound);
3282 wxCHECK_HRESULT_RET(hr);
3283 if ( !fontFound )
3284 {
3285 wxFAIL_MSG(wxString::Format("Couldn't find custom font family '%s'", logfont.lfFaceName));
3286 return;
3287 }
3288 hr = gs_pPrivateFontCollection->GetFontFamily(fontIdx, &fontFamily);
3289 wxCHECK_HRESULT_RET(hr);
3290
3291 // Even though DWRITE_FONT_WEIGHT is an enum, it's values are within the same range
3292 // as font width values in LOGFONT (0-1000) so we can cast LONG to this enum.
3293 DWRITE_FONT_WEIGHT fWeight = static_cast<DWRITE_FONT_WEIGHT>(logfont.lfWeight);
3294
3295 DWRITE_FONT_STYLE fStyle;
3296 if ( logfont.lfItalic == TRUE )
3297 fStyle = DWRITE_FONT_STYLE_ITALIC;
3298 else
3299 fStyle = DWRITE_FONT_STYLE_NORMAL;
3300
3301 DWRITE_FONT_STRETCH fStretch = DWRITE_FONT_STRETCH_NORMAL;
3302
3303 hr = fontFamily->GetFirstMatchingFont(fWeight, fStretch, fStyle, &m_font);
3304 wxCHECK_RET(SUCCEEDED(hr),
3305 wxString::Format("Failed to find custom font '%s' (HRESULT = %x)", logfont.lfFaceName, hr));
3306
3307 fontCollection = gs_pPrivateFontCollection;
3308 #else
3309 return;
3310 #endif // wxUSE_PRIVATE_FONTS
3311 }
3312 else
3313 {
3314 wxCHECK_RET(SUCCEEDED(hr),
3315 wxString::Format("Failed to create font '%s' (HRESULT = %x)", logfont.lfFaceName, hr));
3316
3317 hr = m_font->GetFontFamily(&fontFamily);
3318 wxCHECK_HRESULT_RET(hr);
3319 }
3320
3321 wxCOMPtr<IDWriteLocalizedStrings> familyNames;
3322 hr = fontFamily->GetFamilyNames(&familyNames);
3323 wxCHECK_HRESULT_RET(hr);
3324
3325 UINT32 length;
3326 hr = familyNames->GetStringLength(0, &length);
3327 wxCHECK_HRESULT_RET(hr);
3328
3329 wchar_t* name = new wchar_t[length+1];
3330 hr = familyNames->GetString(0, name, length+1);
3331 wxCHECK_HRESULT_RET(hr);
3332
3333 FLOAT fontSize = !dpi.y
3334 ? FLOAT(font.GetPixelSize().GetHeight())
3335 : FLOAT(font.GetFractionalPointSize() * dpi.y / 72);
3336
3337 hr = wxDWriteFactory()->CreateTextFormat(
3338 name,
3339 fontCollection,
3340 m_font->GetWeight(),
3341 m_font->GetStyle(),
3342 m_font->GetStretch(),
3343 fontSize,
3344 L"en-us",
3345 &m_textFormat);
3346
3347 delete[] name;
3348
3349 wxCHECK_HRESULT_RET(hr);
3350 }
3351
CreateTextLayout(const wxString & text) const3352 wxCOMPtr<IDWriteTextLayout> wxD2DFontData::CreateTextLayout(const wxString& text) const
3353 {
3354 static const FLOAT MAX_WIDTH = FLT_MAX;
3355 static const FLOAT MAX_HEIGHT = FLT_MAX;
3356
3357 HRESULT hr;
3358
3359 wxCOMPtr<IDWriteTextLayout> textLayout;
3360
3361 hr = wxDWriteFactory()->CreateTextLayout(
3362 text.c_str(),
3363 text.length(),
3364 m_textFormat,
3365 MAX_WIDTH,
3366 MAX_HEIGHT,
3367 &textLayout);
3368 wxCHECK2_HRESULT_RET(hr, wxCOMPtr<IDWriteTextLayout>(NULL));
3369
3370 DWRITE_TEXT_RANGE textRange = { 0, (UINT32) text.length() };
3371
3372 if (m_underlined)
3373 {
3374 textLayout->SetUnderline(true, textRange);
3375 }
3376
3377 if (m_strikethrough)
3378 {
3379 textLayout->SetStrikethrough(true, textRange);
3380 }
3381
3382 return textLayout;
3383 }
3384
wxGetD2DFontData(const wxGraphicsFont & font)3385 wxD2DFontData* wxGetD2DFontData(const wxGraphicsFont& font)
3386 {
3387 return static_cast<wxD2DFontData*>(font.GetGraphicsData());
3388 }
3389
3390 // A render target resource holder exposes methods relevant
3391 // for native render targets such as resize
3392 class wxD2DRenderTargetResourceHolder : public wxD2DResourceHolder<ID2D1RenderTarget>
3393 {
3394 public:
3395 // This method is called when an external event signals the underlying DC
3396 // is resized (e.g. the resizing of a window). Some implementations can leave
3397 // this method empty, while others must adjust the render target size to match
3398 // the underlying DC.
Resize()3399 virtual void Resize()
3400 {
3401 }
3402
3403 // We use this method instead of the one provided by the native render target
3404 // because Direct2D 1.0 render targets do not accept a composition mode
3405 // parameter, while the device context in Direct2D 1.1 does. This way, we make
3406 // best use of the capabilities of each render target.
3407 //
3408 // The default implementation works for all render targets, but the D2D 1.0
3409 // render target holders shouldn't need to override it, since none of the
3410 // 1.0 render targets offer a better version of this method.
DrawBitmap(ID2D1Bitmap * bitmap,const D2D1_RECT_F & srcRect,const D2D1_RECT_F & destRect,wxInterpolationQuality interpolationQuality,wxCompositionMode WXUNUSED (compositionMode))3411 virtual void DrawBitmap(ID2D1Bitmap* bitmap,
3412 const D2D1_RECT_F& srcRect, const D2D1_RECT_F& destRect,
3413 wxInterpolationQuality interpolationQuality,
3414 wxCompositionMode WXUNUSED(compositionMode))
3415 {
3416 m_nativeResource->DrawBitmap(
3417 bitmap,
3418 destRect,
3419 1.0f,
3420 wxD2DConvertBitmapInterpolationMode(interpolationQuality),
3421 srcRect);
3422 }
3423
3424 // We use this method instead of the one provided by the native render target
3425 // because some contexts might require writing to a buffer (e.g. an image
3426 // context), and some render targets might require additional operations to
3427 // be executed (e.g. the device context must present the swap chain)
Flush()3428 virtual HRESULT Flush()
3429 {
3430 return m_nativeResource->Flush();
3431 }
3432
3433 // Composition is not supported at in D2D 1.0, and we only allow for:
3434 // wxCOMPOSITION_DEST - which is essentially a no-op and is handled
3435 // externally by preventing any draw calls.
3436 // wxCOMPOSITION_OVER - which copies the source over the destination using
3437 // alpha blending. This is the default way D2D 1.0
3438 // draws images.
SetCompositionMode(wxCompositionMode compositionMode)3439 virtual bool SetCompositionMode(wxCompositionMode compositionMode)
3440 {
3441 if (compositionMode == wxCOMPOSITION_DEST ||
3442 compositionMode == wxCOMPOSITION_OVER)
3443 {
3444 // There's nothing we can do but notify the caller the composition
3445 // mode is supported
3446 return true;
3447 }
3448
3449 return false;
3450 }
3451 };
3452
3453 #if wxUSE_IMAGE
3454 class wxD2DImageRenderTargetResourceHolder : public wxD2DRenderTargetResourceHolder
3455 {
3456 public:
wxD2DImageRenderTargetResourceHolder(wxImage * image,ID2D1Factory * factory)3457 wxD2DImageRenderTargetResourceHolder(wxImage* image, ID2D1Factory* factory) :
3458 m_resultImage(image), m_factory(factory)
3459 {
3460 }
3461
Flush()3462 HRESULT Flush() wxOVERRIDE
3463 {
3464 HRESULT hr = m_nativeResource->Flush();
3465 FlushRenderTargetToImage();
3466 return hr;
3467 }
3468
~wxD2DImageRenderTargetResourceHolder()3469 ~wxD2DImageRenderTargetResourceHolder()
3470 {
3471 FlushRenderTargetToImage();
3472 }
3473
3474 protected:
DoAcquireResource()3475 void DoAcquireResource() wxOVERRIDE
3476 {
3477 CreateWICBitmapFromImage(*m_resultImage, true, &m_wicBitmap);
3478
3479 // Create the render target
3480 HRESULT hr = m_factory->CreateWicBitmapRenderTarget(
3481 m_wicBitmap,
3482 D2D1::RenderTargetProperties(
3483 D2D1_RENDER_TARGET_TYPE_SOFTWARE,
3484 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)),
3485 &m_nativeResource);
3486 wxCHECK_HRESULT_RET(hr);
3487 }
3488
3489 private:
FlushRenderTargetToImage()3490 void FlushRenderTargetToImage()
3491 {
3492 CreateImageFromWICBitmap(m_wicBitmap, m_resultImage);
3493 }
3494
3495 private:
3496 wxImage* m_resultImage;
3497 wxCOMPtr<IWICBitmap> m_wicBitmap;
3498
3499 ID2D1Factory* m_factory;
3500 };
3501 #endif // wxUSE_IMAGE
3502
3503 class wxD2DHwndRenderTargetResourceHolder : public wxD2DRenderTargetResourceHolder
3504 {
3505 public:
3506 typedef ID2D1HwndRenderTarget* ImplementationType;
3507
wxD2DHwndRenderTargetResourceHolder(HWND hwnd,ID2D1Factory * factory)3508 wxD2DHwndRenderTargetResourceHolder(HWND hwnd, ID2D1Factory* factory) :
3509 m_hwnd(hwnd), m_factory(factory)
3510 {
3511 }
3512
Resize()3513 void Resize() wxOVERRIDE
3514 {
3515 RECT clientRect = wxGetClientRect(m_hwnd);
3516
3517 D2D1_SIZE_U hwndSize = D2D1::SizeU(
3518 clientRect.right - clientRect.left,
3519 clientRect.bottom - clientRect.top);
3520
3521 D2D1_SIZE_U renderTargetSize = GetRenderTarget()->GetPixelSize();
3522
3523 if (hwndSize.width != renderTargetSize.width || hwndSize.height != renderTargetSize.height)
3524 {
3525 GetRenderTarget()->Resize(hwndSize);
3526 }
3527 }
3528
3529 protected:
DoAcquireResource()3530 void DoAcquireResource() wxOVERRIDE
3531 {
3532 wxCOMPtr<ID2D1HwndRenderTarget> renderTarget;
3533
3534 HRESULT result;
3535
3536 RECT clientRect = wxGetClientRect(m_hwnd);
3537
3538 D2D1_SIZE_U size = D2D1::SizeU(
3539 clientRect.right - clientRect.left,
3540 clientRect.bottom - clientRect.top);
3541
3542 result = m_factory->CreateHwndRenderTarget(
3543 D2D1::RenderTargetProperties(),
3544 D2D1::HwndRenderTargetProperties(m_hwnd, size),
3545 &renderTarget);
3546
3547 if (FAILED(result))
3548 {
3549 wxFAIL_MSG("Could not create Direct2D render target");
3550 }
3551
3552 renderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
3553
3554 m_nativeResource = renderTarget;
3555 }
3556
3557 private:
3558 // Converts the underlying resource pointer of type
3559 // ID2D1RenderTarget* to the actual implementation type
GetRenderTarget()3560 ImplementationType GetRenderTarget()
3561 {
3562 return static_cast<ImplementationType>(GetD2DResource().get());
3563 }
3564
3565 private:
3566 HWND m_hwnd;
3567 ID2D1Factory* m_factory;
3568 };
3569
3570 #if wxD2D_DEVICE_CONTEXT_SUPPORTED
3571 class wxD2DDeviceContextResourceHolder : public wxD2DRenderTargetResourceHolder
3572 {
3573 public:
wxD2DDeviceContextResourceHolder(ID2D1Factory * factory,HWND hwnd)3574 wxD2DDeviceContextResourceHolder(ID2D1Factory* factory, HWND hwnd) :
3575 m_hwnd(hwnd)
3576 {
3577 HRESULT hr = factory->QueryInterface(IID_ID2D1Factory1, reinterpret_cast<void**>(&m_factory));
3578 wxCHECK_HRESULT_RET(hr);
3579 }
3580
DrawBitmap(ID2D1Bitmap * bitmap,const D2D1_RECT_F & srcRect,const D2D1_RECT_F & destRect,wxInterpolationQuality interpolationQuality,wxCompositionMode compositionMode)3581 void DrawBitmap(ID2D1Bitmap* bitmap,
3582 const D2D1_RECT_F& srcRect, const D2D1_RECT_F& destRect,
3583 wxInterpolationQuality interpolationQuality,
3584 wxCompositionMode compositionMode) wxOVERRIDE
3585 {
3586 D2D1_POINT_2F offset = D2D1::Point2(destRect.left, destRect.top);
3587 m_context->DrawImage(bitmap,
3588 offset,
3589 srcRect,
3590 wxD2DConvertInterpolationMode(interpolationQuality),
3591 wxD2DConvertCompositionMode(compositionMode));
3592 }
3593
Flush()3594 HRESULT Flush() wxOVERRIDE
3595 {
3596 HRESULT hr = m_nativeResource->Flush();
3597 DXGI_PRESENT_PARAMETERS params = { 0 };
3598 m_swapChain->Present1(1, 0, ¶ms);
3599 return hr;
3600 }
3601
3602 protected:
3603
3604 // Adapted from http://msdn.microsoft.com/en-us/library/windows/desktop/hh780339%28v=vs.85%29.aspx
DoAcquireResource()3605 void DoAcquireResource() wxOVERRIDE
3606 {
3607 HRESULT hr;
3608
3609 // This flag adds support for surfaces with a different color channel ordering than the API default.
3610 // You need it for compatibility with Direct2D.
3611 UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
3612
3613 // This array defines the set of DirectX hardware feature levels this app supports.
3614 // The ordering is important and you should preserve it.
3615 // Don't forget to declare your app's minimum required feature level in its
3616 // description. All apps are assumed to support 9.1 unless otherwise stated.
3617 D3D_FEATURE_LEVEL featureLevels[] =
3618 {
3619 D3D_FEATURE_LEVEL_11_1,
3620 D3D_FEATURE_LEVEL_11_0,
3621 D3D_FEATURE_LEVEL_10_1,
3622 D3D_FEATURE_LEVEL_10_0,
3623 D3D_FEATURE_LEVEL_9_3,
3624 D3D_FEATURE_LEVEL_9_2,
3625 D3D_FEATURE_LEVEL_9_1
3626 };
3627
3628 // Create the DX11 API device object, and get a corresponding context.
3629 wxCOMPtr<ID3D11Device> device;
3630 wxCOMPtr<ID3D11DeviceContext> context;
3631
3632 hr = D3D11CreateDevice(
3633 NULL, // specify null to use the default adapter
3634 D3D_DRIVER_TYPE_HARDWARE,
3635 0,
3636 creationFlags, // optionally set debug and Direct2D compatibility flags
3637 featureLevels, // list of feature levels this app can support
3638 ARRAYSIZE(featureLevels), // number of possible feature levels
3639 D3D11_SDK_VERSION,
3640 &device, // returns the Direct3D device created
3641 &m_featureLevel, // returns feature level of device created
3642 &context); // returns the device immediate context
3643 wxCHECK_HRESULT_RET(hr);
3644
3645 // Obtain the underlying DXGI device of the Direct3D11 device.
3646 hr = device->QueryInterface(IID_IDXGIDevice, (void**)&m_dxgiDevice);
3647 wxCHECK_HRESULT_RET(hr);
3648
3649 // Obtain the Direct2D device for 2-D rendering.
3650 hr = m_factory->CreateDevice(m_dxgiDevice, &m_device);
3651 wxCHECK_HRESULT_RET(hr);
3652
3653 // Get Direct2D device's corresponding device context object.
3654 hr = m_device->CreateDeviceContext(
3655 D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
3656 &m_context);
3657 wxCHECK_HRESULT_RET(hr);
3658
3659 m_nativeResource = m_context;
3660
3661 AttachSurface();
3662 }
3663
3664 private:
AttachSurface()3665 void AttachSurface()
3666 {
3667 HRESULT hr;
3668
3669 // Allocate a descriptor.
3670 DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
3671 swapChainDesc.Width = 0;
3672 swapChainDesc.Height = 0;
3673 swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
3674 swapChainDesc.Stereo = false;
3675 swapChainDesc.SampleDesc.Count = 1;
3676 swapChainDesc.SampleDesc.Quality = 0;
3677 swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
3678 swapChainDesc.BufferCount = 2;
3679 swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
3680 swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
3681 swapChainDesc.Flags = 0;
3682
3683 // Identify the physical adapter (GPU or card) this device is runs on.
3684 wxCOMPtr<IDXGIAdapter> dxgiAdapter;
3685 hr = m_dxgiDevice->GetAdapter(&dxgiAdapter);
3686 wxCHECK_HRESULT_RET(hr);
3687
3688 // Get the factory object that created the DXGI device.
3689 wxCOMPtr<IDXGIFactory2> dxgiFactory;
3690 hr = dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory));
3691 wxCHECK_HRESULT_RET(hr);
3692
3693 // Get the final swap chain for this window from the DXGI factory.
3694 hr = dxgiFactory->CreateSwapChainForHwnd(
3695 m_dxgiDevice,
3696 m_hwnd,
3697 &swapChainDesc,
3698 NULL, // allow on all displays
3699 NULL,
3700 &m_swapChain);
3701 wxCHECK_HRESULT_RET(hr);
3702
3703 // Ensure that DXGI doesn't queue more than one frame at a time.
3704 hr = m_dxgiDevice->SetMaximumFrameLatency(1);
3705 wxCHECK_HRESULT_RET(hr);
3706
3707 // Get the backbuffer for this window which is be the final 3D render target.
3708 wxCOMPtr<ID3D11Texture2D> backBuffer;
3709 hr = m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer));
3710 wxCHECK_HRESULT_RET(hr);
3711
3712 FLOAT dpiX, dpiY;
3713 dpiX = dpiY = (FLOAT)::GetDpiForWindow(m_hwnd);
3714
3715 // Now we set up the Direct2D render target bitmap linked to the swapchain.
3716 // Whenever we render to this bitmap, it is directly rendered to the
3717 // swap chain associated with the window.
3718 D2D1_BITMAP_PROPERTIES1 bitmapProperties = D2D1::BitmapProperties1(
3719 D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
3720 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE),
3721 dpiX, dpiY);
3722
3723 // Direct2D needs the dxgi version of the backbuffer surface pointer.
3724 wxCOMPtr<IDXGISurface> dxgiBackBuffer;
3725 hr = m_swapChain->GetBuffer(0, IID_PPV_ARGS(&dxgiBackBuffer));
3726 wxCHECK_HRESULT_RET(hr);
3727
3728 // Get a D2D surface from the DXGI back buffer to use as the D2D render target.
3729 hr = m_context->CreateBitmapFromDxgiSurface(
3730 dxgiBackBuffer.get(),
3731 &bitmapProperties,
3732 &m_targetBitmap);
3733 wxCHECK_HRESULT_RET(hr);
3734
3735 // Now we can set the Direct2D render target.
3736 m_context->SetTarget(m_targetBitmap);
3737 }
3738
~wxD2DDeviceContextResourceHolder()3739 ~wxD2DDeviceContextResourceHolder()
3740 {
3741 DXGI_PRESENT_PARAMETERS params = { 0 };
3742 m_swapChain->Present1(1, 0, ¶ms);
3743 }
3744
3745 private:
3746 wxCOMPtr<ID2D1Factory1> m_factory;
3747
3748 HWND m_hwnd;
3749
3750 D3D_FEATURE_LEVEL m_featureLevel;
3751 wxCOMPtr<IDXGIDevice1> m_dxgiDevice;
3752 wxCOMPtr<ID2D1Device> m_device;
3753 wxCOMPtr<ID2D1DeviceContext> m_context;
3754 wxCOMPtr<ID2D1Bitmap1> m_targetBitmap;
3755 wxCOMPtr<IDXGISwapChain1> m_swapChain;
3756 };
3757 #endif
3758
3759 class wxD2DDCRenderTargetResourceHolder : public wxD2DRenderTargetResourceHolder
3760 {
3761 public:
wxD2DDCRenderTargetResourceHolder(ID2D1Factory * factory,HDC hdc,D2D1_ALPHA_MODE alphaMode)3762 wxD2DDCRenderTargetResourceHolder(ID2D1Factory* factory, HDC hdc, D2D1_ALPHA_MODE alphaMode) :
3763 m_factory(factory), m_hdc(hdc), m_alphaMode(alphaMode)
3764 {
3765 }
3766
3767 protected:
DoAcquireResource()3768 void DoAcquireResource() wxOVERRIDE
3769 {
3770 wxCOMPtr<ID2D1DCRenderTarget> renderTarget;
3771 D2D1_RENDER_TARGET_PROPERTIES renderTargetProperties = D2D1::RenderTargetProperties(
3772 D2D1_RENDER_TARGET_TYPE_DEFAULT,
3773 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, m_alphaMode));
3774
3775 HRESULT hr = m_factory->CreateDCRenderTarget(
3776 &renderTargetProperties,
3777 &renderTarget);
3778 wxCHECK_HRESULT_RET(hr);
3779
3780 // We want draw on the entire device area.
3781 // GetClipBox() retrieves logical size of DC
3782 // what is what we need to pass to BindDC.
3783 RECT r;
3784 int status = ::GetClipBox(m_hdc, &r);
3785 wxCHECK_RET( status != ERROR, wxS("Error retrieving DC dimensions") );
3786
3787 hr = renderTarget->BindDC(m_hdc, &r);
3788 wxCHECK_HRESULT_RET(hr);
3789 renderTarget->SetTransform(
3790 D2D1::Matrix3x2F::Translation(-r.left, -r.top));
3791
3792 m_nativeResource = renderTarget;
3793 }
3794
3795 private:
3796 ID2D1Factory* m_factory;
3797 HDC m_hdc;
3798 D2D1_ALPHA_MODE m_alphaMode;
3799 };
3800
3801 // The null context has no state of its own and does nothing.
3802 // It is only used as a base class for the lightweight
3803 // measuring context. The measuring context cannot inherit from
3804 // the default implementation wxD2DContext, because some methods
3805 // from wxD2DContext require the presence of a "context"
3806 // (render target) in order to acquire various device-dependent
3807 // resources. Without a proper context, those methods would fail.
3808 // The methods implemented in the null context are fundamentally no-ops.
3809 class wxNullContext : public wxGraphicsContext
3810 {
3811 public:
wxNullContext(wxGraphicsRenderer * renderer)3812 wxNullContext(wxGraphicsRenderer* renderer) : wxGraphicsContext(renderer) {}
GetTextExtent(const wxString &,wxDouble *,wxDouble *,wxDouble *,wxDouble *) const3813 void GetTextExtent(const wxString&, wxDouble*, wxDouble*, wxDouble*, wxDouble*) const wxOVERRIDE {}
GetPartialTextExtents(const wxString &,wxArrayDouble &) const3814 void GetPartialTextExtents(const wxString&, wxArrayDouble&) const wxOVERRIDE {}
Clip(const wxRegion &)3815 void Clip(const wxRegion&) wxOVERRIDE {}
Clip(wxDouble,wxDouble,wxDouble,wxDouble)3816 void Clip(wxDouble, wxDouble, wxDouble, wxDouble) wxOVERRIDE {}
ResetClip()3817 void ResetClip() wxOVERRIDE {}
GetClipBox(wxDouble *,wxDouble *,wxDouble *,wxDouble *)3818 void GetClipBox(wxDouble*, wxDouble*, wxDouble*, wxDouble*) wxOVERRIDE {}
GetNativeContext()3819 void* GetNativeContext() wxOVERRIDE { return NULL; }
SetAntialiasMode(wxAntialiasMode)3820 bool SetAntialiasMode(wxAntialiasMode) wxOVERRIDE { return false; }
SetInterpolationQuality(wxInterpolationQuality)3821 bool SetInterpolationQuality(wxInterpolationQuality) wxOVERRIDE { return false; }
SetCompositionMode(wxCompositionMode)3822 bool SetCompositionMode(wxCompositionMode) wxOVERRIDE { return false; }
BeginLayer(wxDouble)3823 void BeginLayer(wxDouble) wxOVERRIDE {}
EndLayer()3824 void EndLayer() wxOVERRIDE {}
Translate(wxDouble,wxDouble)3825 void Translate(wxDouble, wxDouble) wxOVERRIDE {}
Scale(wxDouble,wxDouble)3826 void Scale(wxDouble, wxDouble) wxOVERRIDE {}
Rotate(wxDouble)3827 void Rotate(wxDouble) wxOVERRIDE {}
ConcatTransform(const wxGraphicsMatrix &)3828 void ConcatTransform(const wxGraphicsMatrix&) wxOVERRIDE {}
SetTransform(const wxGraphicsMatrix &)3829 void SetTransform(const wxGraphicsMatrix&) wxOVERRIDE {}
GetTransform() const3830 wxGraphicsMatrix GetTransform() const wxOVERRIDE { return wxNullGraphicsMatrix; }
StrokePath(const wxGraphicsPath &)3831 void StrokePath(const wxGraphicsPath&) wxOVERRIDE {}
FillPath(const wxGraphicsPath &,wxPolygonFillMode)3832 void FillPath(const wxGraphicsPath&, wxPolygonFillMode) wxOVERRIDE {}
DrawBitmap(const wxGraphicsBitmap &,wxDouble,wxDouble,wxDouble,wxDouble)3833 void DrawBitmap(const wxGraphicsBitmap&, wxDouble, wxDouble, wxDouble, wxDouble) wxOVERRIDE {}
DrawBitmap(const wxBitmap &,wxDouble,wxDouble,wxDouble,wxDouble)3834 void DrawBitmap(const wxBitmap&, wxDouble, wxDouble, wxDouble, wxDouble) wxOVERRIDE {}
DrawIcon(const wxIcon &,wxDouble,wxDouble,wxDouble,wxDouble)3835 void DrawIcon(const wxIcon&, wxDouble, wxDouble, wxDouble, wxDouble) wxOVERRIDE {}
PushState()3836 void PushState() wxOVERRIDE {}
PopState()3837 void PopState() wxOVERRIDE {}
Flush()3838 void Flush() wxOVERRIDE {}
3839
3840 protected:
DoDrawText(const wxString &,wxDouble,wxDouble)3841 void DoDrawText(const wxString&, wxDouble, wxDouble) wxOVERRIDE {}
3842 };
3843
3844 class wxD2DMeasuringContext : public wxNullContext
3845 {
3846 public:
wxD2DMeasuringContext(wxGraphicsRenderer * renderer)3847 wxD2DMeasuringContext(wxGraphicsRenderer* renderer) : wxNullContext(renderer) {}
3848
GetTextExtent(const wxString & str,wxDouble * width,wxDouble * height,wxDouble * descent,wxDouble * externalLeading) const3849 void GetTextExtent(const wxString& str, wxDouble* width, wxDouble* height, wxDouble* descent, wxDouble* externalLeading) const wxOVERRIDE
3850 {
3851 GetTextExtent(wxGetD2DFontData(m_font), str, width, height, descent, externalLeading);
3852 }
3853
GetPartialTextExtents(const wxString & text,wxArrayDouble & widths) const3854 void GetPartialTextExtents(const wxString& text, wxArrayDouble& widths) const wxOVERRIDE
3855 {
3856 GetPartialTextExtents(wxGetD2DFontData(m_font), text, widths);
3857 }
3858
GetPartialTextExtents(wxD2DFontData * fontData,const wxString & text,wxArrayDouble & widths)3859 static void GetPartialTextExtents(wxD2DFontData* fontData, const wxString& text, wxArrayDouble& widths)
3860 {
3861 for (unsigned int i = 0; i < text.Length(); ++i)
3862 {
3863 wxDouble width;
3864 GetTextExtent(fontData, text.SubString(0, i), &width, NULL, NULL, NULL);
3865 widths.push_back(width);
3866 }
3867 }
3868
GetTextExtent(wxD2DFontData * fontData,const wxString & str,wxDouble * width,wxDouble * height,wxDouble * descent,wxDouble * externalLeading)3869 static void GetTextExtent(wxD2DFontData* fontData, const wxString& str, wxDouble* width, wxDouble* height, wxDouble* descent, wxDouble* externalLeading)
3870 {
3871 wxCOMPtr<IDWriteTextLayout> textLayout = fontData->CreateTextLayout(str);
3872 wxCOMPtr<IDWriteFont> font = fontData->GetFont();
3873
3874 DWRITE_TEXT_METRICS textMetrics;
3875 textLayout->GetMetrics(&textMetrics);
3876
3877 DWRITE_FONT_METRICS fontMetrics;
3878 font->GetMetrics(&fontMetrics);
3879
3880 FLOAT ratio = fontData->GetTextFormat()->GetFontSize() / (FLOAT)fontMetrics.designUnitsPerEm;
3881
3882 if (width != NULL) *width = textMetrics.widthIncludingTrailingWhitespace;
3883 if (height != NULL) *height = textMetrics.height;
3884
3885 if (descent != NULL) *descent = fontMetrics.descent * ratio;
3886 if (externalLeading != NULL) *externalLeading = wxMax(0.0f, (fontMetrics.ascent + fontMetrics.descent) * ratio - textMetrics.height);
3887 }
3888 };
3889
3890 //-----------------------------------------------------------------------------
3891 // wxD2DContext declaration
3892 //-----------------------------------------------------------------------------
3893
3894 class wxD2DContext : public wxGraphicsContext, wxD2DResourceManager
3895 {
3896 public:
3897 // Create the context for the given HWND, which may be associated (if it's
3898 // non-null) with the given wxWindow.
3899 wxD2DContext(wxGraphicsRenderer* renderer,
3900 ID2D1Factory* direct2dFactory,
3901 HWND hwnd,
3902 wxWindow* window = NULL);
3903
3904 // Create the context for the given HDC which may be associated (if it's
3905 // non-null) with the given wxDC.
3906 wxD2DContext(wxGraphicsRenderer* renderer,
3907 ID2D1Factory* direct2dFactory,
3908 HDC hdc,
3909 const wxDC* dc = NULL,
3910 D2D1_ALPHA_MODE alphaMode = D2D1_ALPHA_MODE_IGNORE);
3911
3912 #if wxUSE_IMAGE
3913 wxD2DContext(wxGraphicsRenderer* renderer, ID2D1Factory* direct2dFactory, wxImage& image);
3914 #endif // wxUSE_IMAGE
3915
3916 wxD2DContext(wxGraphicsRenderer* renderer, ID2D1Factory* direct2dFactory, void* nativeContext);
3917
3918 ~wxD2DContext();
3919
3920 void Clip(const wxRegion& region) wxOVERRIDE;
3921
3922 void Clip(wxDouble x, wxDouble y, wxDouble w, wxDouble h) wxOVERRIDE;
3923
3924 void ResetClip() wxOVERRIDE;
3925
3926 void GetClipBox(wxDouble* x, wxDouble* y, wxDouble* w, wxDouble* h) wxOVERRIDE;
3927
3928 // The native context used by wxD2DContext is a Direct2D render target.
3929 void* GetNativeContext() wxOVERRIDE;
3930
3931 bool SetAntialiasMode(wxAntialiasMode antialias) wxOVERRIDE;
3932
3933 bool SetInterpolationQuality(wxInterpolationQuality interpolation) wxOVERRIDE;
3934
3935 bool SetCompositionMode(wxCompositionMode op) wxOVERRIDE;
3936
3937 void BeginLayer(wxDouble opacity) wxOVERRIDE;
3938
3939 void EndLayer() wxOVERRIDE;
3940
3941 void Translate(wxDouble dx, wxDouble dy) wxOVERRIDE;
3942
3943 void Scale(wxDouble xScale, wxDouble yScale) wxOVERRIDE;
3944
3945 void Rotate(wxDouble angle) wxOVERRIDE;
3946
3947 void ConcatTransform(const wxGraphicsMatrix& matrix) wxOVERRIDE;
3948
3949 void SetTransform(const wxGraphicsMatrix& matrix) wxOVERRIDE;
3950
3951 wxGraphicsMatrix GetTransform() const wxOVERRIDE;
3952
3953 void StrokePath(const wxGraphicsPath& p) wxOVERRIDE;
3954
3955 void FillPath(const wxGraphicsPath& p , wxPolygonFillMode fillStyle = wxODDEVEN_RULE) wxOVERRIDE;
3956
3957 void DrawRectangle(wxDouble x, wxDouble y, wxDouble w, wxDouble h) wxOVERRIDE;
3958
3959 void DrawRoundedRectangle(wxDouble x, wxDouble y, wxDouble w, wxDouble h, wxDouble radius) wxOVERRIDE;
3960
3961 void DrawEllipse(wxDouble x, wxDouble y, wxDouble w, wxDouble h) wxOVERRIDE;
3962
3963 void DrawBitmap(const wxGraphicsBitmap& bmp, wxDouble x, wxDouble y, wxDouble w, wxDouble h) wxOVERRIDE;
3964
3965 void DrawBitmap(const wxBitmap& bmp, wxDouble x, wxDouble y, wxDouble w, wxDouble h) wxOVERRIDE;
3966
3967 void DrawIcon(const wxIcon& icon, wxDouble x, wxDouble y, wxDouble w, wxDouble h) wxOVERRIDE;
3968
3969 void PushState() wxOVERRIDE;
3970
3971 void PopState() wxOVERRIDE;
3972
3973 void GetTextExtent(
3974 const wxString& str,
3975 wxDouble* width,
3976 wxDouble* height,
3977 wxDouble* descent,
3978 wxDouble* externalLeading) const wxOVERRIDE;
3979
3980 void GetPartialTextExtents(const wxString& text, wxArrayDouble& widths) const wxOVERRIDE;
3981
3982 bool ShouldOffset() const wxOVERRIDE;
3983
3984 void SetPen(const wxGraphicsPen& pen) wxOVERRIDE;
3985
3986 void Flush() wxOVERRIDE;
3987
3988 void GetDPI(wxDouble* dpiX, wxDouble* dpiY) const wxOVERRIDE;
3989
GetContext()3990 wxD2DContextSupplier::ContextType GetContext() wxOVERRIDE
3991 {
3992 return GetRenderTarget();
3993 }
3994
3995 private:
3996 void Init();
3997
3998 void DoDrawText(const wxString& str, wxDouble x, wxDouble y) wxOVERRIDE;
3999
4000 void EnsureInitialized();
4001
4002 HRESULT CreateRenderTarget();
4003
4004 void AdjustRenderTargetSize();
4005
4006 void ReleaseDeviceDependentResources();
4007
4008 ID2D1RenderTarget* GetRenderTarget() const;
4009
4010 void SetClipLayer(ID2D1Geometry* clipGeometry);
4011
4012 private:
4013 enum LayerType
4014 {
4015 CLIP_LAYER,
4016 OTHER_LAYER
4017 };
4018
4019 struct LayerData
4020 {
4021 LayerType type;
4022 D2D1_LAYER_PARAMETERS params;
4023 wxCOMPtr<ID2D1Layer> layer;
4024 wxCOMPtr<ID2D1Geometry> geometry;
4025 D2D1_MATRIX_3X2_F transformMatrix;
4026 };
4027
4028 struct StateData
4029 {
4030 // A ID2D1DrawingStateBlock represents the drawing state of a render target:
4031 // the anti aliasing mode, transform, tags, and text-rendering options.
4032 // The context owns these pointers and is responsible for releasing them.
4033 wxCOMPtr<ID2D1DrawingStateBlock> drawingState;
4034 // We need to store also current layers.
4035 wxStack<LayerData> layers;
4036 };
4037
4038 private:
4039 ID2D1Factory* m_direct2dFactory;
4040 wxSharedPtr<wxD2DRenderTargetResourceHolder> m_renderTargetHolder;
4041 wxStack<StateData> m_stateStack;
4042 wxStack<LayerData> m_layers;
4043 ID2D1RenderTarget* m_cachedRenderTarget;
4044 D2D1::Matrix3x2F m_initTransform;
4045 // Clipping box
4046 bool m_isClipBoxValid;
4047 double m_clipX1, m_clipY1, m_clipX2, m_clipY2;
4048
4049 private:
4050 wxDECLARE_NO_COPY_CLASS(wxD2DContext);
4051 };
4052
4053 //-----------------------------------------------------------------------------
4054 // wxD2DContext implementation
4055 //-----------------------------------------------------------------------------
4056
wxD2DContext(wxGraphicsRenderer * renderer,ID2D1Factory * direct2dFactory,HWND hwnd,wxWindow * window)4057 wxD2DContext::wxD2DContext(wxGraphicsRenderer* renderer,
4058 ID2D1Factory* direct2dFactory,
4059 HWND hwnd,
4060 wxWindow* window) :
4061 wxGraphicsContext(renderer, window), m_direct2dFactory(direct2dFactory),
4062 #if wxD2D_DEVICE_CONTEXT_SUPPORTED
4063 m_renderTargetHolder(new wxD2DDeviceContextResourceHolder(direct2dFactory, hwnd))
4064 #else
4065 m_renderTargetHolder(new wxD2DHwndRenderTargetResourceHolder(hwnd, direct2dFactory))
4066 #endif
4067 {
4068 RECT r = wxGetWindowRect(hwnd);
4069 m_width = r.right - r.left;
4070 m_height = r.bottom - r.top;
4071 Init();
4072 }
4073
wxD2DContext(wxGraphicsRenderer * renderer,ID2D1Factory * direct2dFactory,HDC hdc,const wxDC * dc,D2D1_ALPHA_MODE alphaMode)4074 wxD2DContext::wxD2DContext(wxGraphicsRenderer* renderer,
4075 ID2D1Factory* direct2dFactory,
4076 HDC hdc,
4077 const wxDC* dc,
4078 D2D1_ALPHA_MODE alphaMode) :
4079 wxGraphicsContext(renderer, dc->GetWindow()), m_direct2dFactory(direct2dFactory),
4080 m_renderTargetHolder(new wxD2DDCRenderTargetResourceHolder(direct2dFactory, hdc, alphaMode))
4081 {
4082 if ( dc )
4083 {
4084 const wxSize dcSize = dc->GetSize();
4085 m_width = dcSize.GetWidth();
4086 m_height = dcSize.GetHeight();
4087 }
4088
4089 Init();
4090 }
4091
4092 #if wxUSE_IMAGE
wxD2DContext(wxGraphicsRenderer * renderer,ID2D1Factory * direct2dFactory,wxImage & image)4093 wxD2DContext::wxD2DContext(wxGraphicsRenderer* renderer, ID2D1Factory* direct2dFactory, wxImage& image) :
4094 wxGraphicsContext(renderer), m_direct2dFactory(direct2dFactory),
4095 m_renderTargetHolder(new wxD2DImageRenderTargetResourceHolder(&image, direct2dFactory))
4096 {
4097 m_width = image.GetWidth();
4098 m_height = image.GetHeight();
4099 Init();
4100 }
4101 #endif // wxUSE_IMAGE
4102
wxD2DContext(wxGraphicsRenderer * renderer,ID2D1Factory * direct2dFactory,void * nativeContext)4103 wxD2DContext::wxD2DContext(wxGraphicsRenderer* renderer, ID2D1Factory* direct2dFactory, void* nativeContext) :
4104 wxGraphicsContext(renderer), m_direct2dFactory(direct2dFactory)
4105 {
4106 m_renderTargetHolder = *((wxSharedPtr<wxD2DRenderTargetResourceHolder>*)nativeContext);
4107 m_width = 0;
4108 m_height = 0;
4109 Init();
4110 }
4111
Init()4112 void wxD2DContext::Init()
4113 {
4114 m_cachedRenderTarget = NULL;
4115 m_composition = wxCOMPOSITION_OVER;
4116 m_renderTargetHolder->Bind(this);
4117 m_enableOffset = true;
4118 m_isClipBoxValid = false;
4119 m_clipX1 = m_clipY1 = m_clipX2 = m_clipY2 = 0.0;
4120 EnsureInitialized();
4121 }
4122
~wxD2DContext()4123 wxD2DContext::~wxD2DContext()
4124 {
4125 // Remove all layers from the stack of layers.
4126 while ( !m_layers.empty() )
4127 {
4128 LayerData ld = m_layers.top();
4129 m_layers.pop();
4130
4131 GetRenderTarget()->PopLayer();
4132 ld.layer.reset();
4133 ld.geometry.reset();
4134 }
4135
4136 HRESULT result = GetRenderTarget()->EndDraw();
4137 wxCHECK_HRESULT_RET(result);
4138
4139 ReleaseResources();
4140 }
4141
GetRenderTarget() const4142 ID2D1RenderTarget* wxD2DContext::GetRenderTarget() const
4143 {
4144 return m_cachedRenderTarget;
4145 }
4146
Clip(const wxRegion & region)4147 void wxD2DContext::Clip(const wxRegion& region)
4148 {
4149 wxCOMPtr<ID2D1Geometry> clipGeometry = wxD2DConvertRegionToGeometry(m_direct2dFactory, region);
4150
4151 SetClipLayer(clipGeometry);
4152 }
4153
Clip(wxDouble x,wxDouble y,wxDouble w,wxDouble h)4154 void wxD2DContext::Clip(wxDouble x, wxDouble y, wxDouble w, wxDouble h)
4155 {
4156 wxCOMPtr<ID2D1RectangleGeometry> clipGeometry;
4157 HRESULT hr = m_direct2dFactory->CreateRectangleGeometry(
4158 D2D1::RectF(x, y, x + w, y + h), &clipGeometry);
4159 wxCHECK_HRESULT_RET(hr);
4160
4161 SetClipLayer(clipGeometry);
4162 }
4163
SetClipLayer(ID2D1Geometry * clipGeometry)4164 void wxD2DContext::SetClipLayer(ID2D1Geometry* clipGeometry)
4165 {
4166 EnsureInitialized();
4167
4168 wxCOMPtr<ID2D1Layer> clipLayer;
4169 HRESULT hr = GetRenderTarget()->CreateLayer(&clipLayer);
4170 wxCHECK_HRESULT_RET(hr);
4171
4172 LayerData ld;
4173 ld.type = CLIP_LAYER;
4174 ld.params = D2D1::LayerParameters(D2D1::InfiniteRect(), clipGeometry, GetRenderTarget()->GetAntialiasMode());
4175 ld.layer = clipLayer;
4176 ld.geometry = clipGeometry;
4177 // We need to store CTM to be able to re-apply
4178 // the layer at the original position later on.
4179 GetRenderTarget()->GetTransform(&ld.transformMatrix);
4180
4181 GetRenderTarget()->PushLayer(ld.params, clipLayer);
4182 // Store layer parameters.
4183 m_layers.push(ld);
4184
4185 m_isClipBoxValid = false;
4186 }
4187
ResetClip()4188 void wxD2DContext::ResetClip()
4189 {
4190 wxStack<LayerData> layersToRestore;
4191 // Remove all clipping layers from the stack of layers.
4192 while ( !m_layers.empty() )
4193 {
4194 LayerData ld = m_layers.top();
4195 m_layers.pop();
4196
4197 if ( ld.type == CLIP_LAYER )
4198 {
4199 GetRenderTarget()->PopLayer();
4200 ld.layer.reset();
4201 ld.geometry.reset();
4202 continue;
4203 }
4204
4205 GetRenderTarget()->PopLayer();
4206 // Save non-clipping layer
4207 layersToRestore.push(ld);
4208 }
4209
4210 HRESULT hr = GetRenderTarget()->Flush();
4211 wxCHECK_HRESULT_RET(hr);
4212
4213 // Re-apply all remaining non-clipping layers.
4214 // First, save current transformation matrix.
4215 D2D1_MATRIX_3X2_F currTransform;
4216 GetRenderTarget()->GetTransform(&currTransform);
4217 while ( !layersToRestore.empty() )
4218 {
4219 LayerData ld = layersToRestore.top();
4220 layersToRestore.pop();
4221
4222 // Restore layer at original position.
4223 GetRenderTarget()->SetTransform(&ld.transformMatrix);
4224 GetRenderTarget()->PushLayer(ld.params, ld.layer);
4225 // Store layer parameters.
4226 m_layers.push(ld);
4227 }
4228 // Restore current transformation matrix.
4229 GetRenderTarget()->SetTransform(&currTransform);
4230
4231 m_isClipBoxValid = false;
4232 }
4233
GetClipBox(wxDouble * x,wxDouble * y,wxDouble * w,wxDouble * h)4234 void wxD2DContext::GetClipBox(wxDouble* x, wxDouble* y, wxDouble* w, wxDouble* h)
4235 {
4236 if ( !m_isClipBoxValid )
4237 {
4238 // To obtain actual clipping box we have to start with rectangle
4239 // covering the entire render target and interesect with this rectangle
4240 // all clipping layers. Bounding box of the final geometry
4241 // (being intersection of all clipping layers) is a clipping box.
4242
4243 HRESULT hr;
4244 wxCOMPtr<ID2D1RectangleGeometry> rectGeometry;
4245 hr = m_direct2dFactory->CreateRectangleGeometry(
4246 D2D1::RectF(0.0F, 0.0F, (FLOAT)m_width, (FLOAT)m_height),
4247 &rectGeometry);
4248 wxCHECK_HRESULT_RET(hr);
4249
4250 wxCOMPtr<ID2D1Geometry> clipGeometry(rectGeometry);
4251
4252 wxStack<LayerData> layers(m_layers);
4253 while( !layers.empty() )
4254 {
4255 LayerData ld = layers.top();
4256 layers.pop();
4257
4258 if ( ld.type == CLIP_LAYER )
4259 {
4260 // If current geometry is empty (null region)
4261 // or there is no intersection between geometries
4262 // then final result is "null" rectangle geometry.
4263 FLOAT area;
4264 hr = ld.geometry->ComputeArea(ld.transformMatrix, &area);
4265 wxCHECK_HRESULT_RET(hr);
4266 D2D1_GEOMETRY_RELATION geomRel;
4267 hr = clipGeometry->CompareWithGeometry(ld.geometry, ld.transformMatrix, &geomRel);
4268 wxCHECK_HRESULT_RET(hr);
4269 if ( area <= FLT_MIN || geomRel == D2D1_GEOMETRY_RELATION_DISJOINT )
4270 {
4271 wxCOMPtr<ID2D1RectangleGeometry> nullGeometry;
4272 hr = m_direct2dFactory->CreateRectangleGeometry(
4273 D2D1::RectF(0.0F, 0.0F, 0.0F, 0.0F), &nullGeometry);
4274 wxCHECK_HRESULT_RET(hr);
4275
4276 clipGeometry.reset();
4277 clipGeometry = nullGeometry;
4278 break;
4279 }
4280
4281 wxCOMPtr<ID2D1PathGeometry> pathGeometryClip;
4282 hr = m_direct2dFactory->CreatePathGeometry(&pathGeometryClip);
4283 wxCHECK_HRESULT_RET(hr);
4284 wxCOMPtr<ID2D1GeometrySink> pGeometrySink;
4285 hr = pathGeometryClip->Open(&pGeometrySink);
4286 wxCHECK_HRESULT_RET(hr);
4287
4288 hr = clipGeometry->CombineWithGeometry(ld.geometry, D2D1_COMBINE_MODE_INTERSECT,
4289 ld.transformMatrix, pGeometrySink);
4290 wxCHECK_HRESULT_RET(hr);
4291 hr = pGeometrySink->Close();
4292 wxCHECK_HRESULT_RET(hr);
4293 pGeometrySink.reset();
4294
4295 clipGeometry = pathGeometryClip;
4296 pathGeometryClip.reset();
4297 }
4298 }
4299
4300 // Final clipping geometry is given in device coordinates
4301 // so we need to transform its bounds to logical coordinates.
4302 D2D1::Matrix3x2F currTransform;
4303 GetRenderTarget()->GetTransform(&currTransform);
4304 currTransform.Invert();
4305
4306 D2D1_RECT_F bounds;
4307 // First check if clip region is empty.
4308 FLOAT clipArea;
4309 hr = clipGeometry->ComputeArea(currTransform, &clipArea);
4310 wxCHECK_HRESULT_RET(hr);
4311 if ( clipArea <= FLT_MIN )
4312 {
4313 bounds.left = bounds.top = bounds.right = bounds.bottom = 0.0F;
4314 }
4315 else
4316 {
4317 // If it is not empty then get it bounds.
4318 hr = clipGeometry->GetBounds(currTransform, &bounds);
4319 wxCHECK_HRESULT_RET(hr);
4320 }
4321
4322 m_clipX1 = bounds.left;
4323 m_clipY1 = bounds.top;
4324 m_clipX2 = bounds.right;
4325 m_clipY2 = bounds.bottom;
4326 m_isClipBoxValid = true;
4327 }
4328
4329 if ( x )
4330 *x = m_clipX1;
4331 if ( y )
4332 *y = m_clipY1;
4333 if ( w )
4334 *w = m_clipX2 - m_clipX1;
4335 if ( h )
4336 *h = m_clipY2 - m_clipY1;
4337 }
4338
GetNativeContext()4339 void* wxD2DContext::GetNativeContext()
4340 {
4341 return &m_renderTargetHolder;
4342 }
4343
StrokePath(const wxGraphicsPath & p)4344 void wxD2DContext::StrokePath(const wxGraphicsPath& p)
4345 {
4346 if (m_composition == wxCOMPOSITION_DEST)
4347 return;
4348
4349 wxD2DOffsetHelper helper(this);
4350
4351 EnsureInitialized();
4352 AdjustRenderTargetSize();
4353
4354 wxD2DPathData* pathData = wxGetD2DPathData(p);
4355 pathData->Flush();
4356
4357 if (!m_pen.IsNull())
4358 {
4359 wxD2DPenData* penData = wxGetD2DPenData(m_pen);
4360 penData->SetWidth(this);
4361 penData->Bind(this);
4362 ID2D1Brush* nativeBrush = penData->GetBrush();
4363 GetRenderTarget()->DrawGeometry((ID2D1Geometry*)pathData->GetNativePath(), nativeBrush, penData->GetWidth(), penData->GetStrokeStyle());
4364 }
4365 }
4366
FillPath(const wxGraphicsPath & p,wxPolygonFillMode fillStyle)4367 void wxD2DContext::FillPath(const wxGraphicsPath& p , wxPolygonFillMode fillStyle)
4368 {
4369 if (m_composition == wxCOMPOSITION_DEST)
4370 return;
4371
4372 EnsureInitialized();
4373 AdjustRenderTargetSize();
4374
4375 wxD2DPathData* pathData = wxGetD2DPathData(p);
4376 pathData->SetFillMode(fillStyle == wxODDEVEN_RULE ? D2D1_FILL_MODE_ALTERNATE : D2D1_FILL_MODE_WINDING);
4377 pathData->Flush();
4378
4379 if (!m_brush.IsNull())
4380 {
4381 wxD2DBrushData* brushData = wxGetD2DBrushData(m_brush);
4382 brushData->Bind(this);
4383 GetRenderTarget()->FillGeometry((ID2D1Geometry*)pathData->GetNativePath(), brushData->GetBrush());
4384 }
4385 }
4386
SetAntialiasMode(wxAntialiasMode antialias)4387 bool wxD2DContext::SetAntialiasMode(wxAntialiasMode antialias)
4388 {
4389 if (m_antialias == antialias)
4390 {
4391 return true;
4392 }
4393
4394 D2D1_ANTIALIAS_MODE antialiasMode;
4395 D2D1_TEXT_ANTIALIAS_MODE textAntialiasMode;
4396 switch ( antialias )
4397 {
4398 case wxANTIALIAS_DEFAULT:
4399 antialiasMode = D2D1_ANTIALIAS_MODE_PER_PRIMITIVE;
4400 textAntialiasMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
4401 break;
4402
4403 case wxANTIALIAS_NONE:
4404 antialiasMode = D2D1_ANTIALIAS_MODE_ALIASED;
4405 textAntialiasMode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
4406 break;
4407
4408 default:
4409 wxFAIL_MSG("Unknown antialias mode");
4410 return false;
4411 }
4412
4413 GetRenderTarget()->SetAntialiasMode(antialiasMode);
4414 GetRenderTarget()->SetTextAntialiasMode(textAntialiasMode);
4415
4416 m_antialias = antialias;
4417 return true;
4418 }
4419
SetInterpolationQuality(wxInterpolationQuality interpolation)4420 bool wxD2DContext::SetInterpolationQuality(wxInterpolationQuality interpolation)
4421 {
4422 // Since different versions of Direct2D have different enumerations for
4423 // interpolation quality, we deffer the conversion to the method which
4424 // does the actual drawing.
4425
4426 m_interpolation = interpolation;
4427 return true;
4428 }
4429
SetCompositionMode(wxCompositionMode compositionMode)4430 bool wxD2DContext::SetCompositionMode(wxCompositionMode compositionMode)
4431 {
4432 if (m_composition == compositionMode)
4433 return true;
4434
4435 if (m_renderTargetHolder->SetCompositionMode(compositionMode))
4436 {
4437 // the composition mode is supported by the render target
4438 m_composition = compositionMode;
4439 return true;
4440 }
4441
4442 return false;
4443 }
4444
BeginLayer(wxDouble opacity)4445 void wxD2DContext::BeginLayer(wxDouble opacity)
4446 {
4447 EnsureInitialized();
4448
4449 wxCOMPtr<ID2D1Layer> layer;
4450 HRESULT hr = GetRenderTarget()->CreateLayer(&layer);
4451 wxCHECK_HRESULT_RET(hr);
4452
4453 LayerData ld;
4454 ld.type = OTHER_LAYER;
4455 ld.params = D2D1::LayerParameters(D2D1::InfiniteRect(),
4456 NULL,
4457 D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
4458 D2D1::IdentityMatrix(), opacity);
4459 ld.layer = layer;
4460 // We need to store CTM to be able to re-apply
4461 // the layer at the original position later on.
4462 GetRenderTarget()->GetTransform(&ld.transformMatrix);
4463
4464 GetRenderTarget()->PushLayer(ld.params, layer);
4465
4466 // Store layer parameters.
4467 m_layers.push(ld);
4468 }
4469
EndLayer()4470 void wxD2DContext::EndLayer()
4471 {
4472 wxStack<LayerData> layersToRestore;
4473 // Temporarily remove all clipping layers
4474 // above the first standard layer
4475 // and next permanently remove this layer.
4476 while ( !m_layers.empty() )
4477 {
4478 LayerData ld = m_layers.top();
4479 m_layers.pop();
4480
4481 if ( ld.type == CLIP_LAYER )
4482 {
4483 GetRenderTarget()->PopLayer();
4484 layersToRestore.push(ld);
4485 continue;
4486 }
4487
4488 // We found a non-clipping layer to remove.
4489 GetRenderTarget()->PopLayer();
4490 ld.layer.reset();
4491 break;
4492 }
4493
4494 if ( m_layers.empty() )
4495 {
4496 HRESULT hr = GetRenderTarget()->Flush();
4497 wxCHECK_HRESULT_RET(hr);
4498 }
4499
4500 // Re-apply all stored clipping layers.
4501 // First, save current transformation matrix.
4502 D2D1_MATRIX_3X2_F currTransform;
4503 GetRenderTarget()->GetTransform(&currTransform);
4504 while ( !layersToRestore.empty() )
4505 {
4506 LayerData ld = layersToRestore.top();
4507 layersToRestore.pop();
4508
4509 if ( ld.type == CLIP_LAYER )
4510 {
4511 // Restore layer at original position.
4512 GetRenderTarget()->SetTransform(&ld.transformMatrix);
4513 GetRenderTarget()->PushLayer(ld.params, ld.layer);
4514 }
4515 else
4516 {
4517 wxFAIL_MSG( wxS("Invalid layer type") );
4518 }
4519 // Store layer parameters.
4520 m_layers.push(ld);
4521 }
4522 // Restore current transformation matrix.
4523 GetRenderTarget()->SetTransform(&currTransform);
4524 }
4525
Translate(wxDouble dx,wxDouble dy)4526 void wxD2DContext::Translate(wxDouble dx, wxDouble dy)
4527 {
4528 wxGraphicsMatrix translationMatrix = CreateMatrix();
4529 translationMatrix.Translate(dx, dy);
4530 ConcatTransform(translationMatrix);
4531 }
4532
Scale(wxDouble xScale,wxDouble yScale)4533 void wxD2DContext::Scale(wxDouble xScale, wxDouble yScale)
4534 {
4535 wxGraphicsMatrix scaleMatrix = CreateMatrix();
4536 scaleMatrix.Scale(xScale, yScale);
4537 ConcatTransform(scaleMatrix);
4538 }
4539
Rotate(wxDouble angle)4540 void wxD2DContext::Rotate(wxDouble angle)
4541 {
4542 wxGraphicsMatrix rotationMatrix = CreateMatrix();
4543 rotationMatrix.Rotate(angle);
4544 ConcatTransform(rotationMatrix);
4545 }
4546
ConcatTransform(const wxGraphicsMatrix & matrix)4547 void wxD2DContext::ConcatTransform(const wxGraphicsMatrix& matrix)
4548 {
4549 D2D1::Matrix3x2F localMatrix = wxGetD2DMatrixData(GetTransform())->GetMatrix3x2F();
4550 D2D1::Matrix3x2F concatMatrix = wxGetD2DMatrixData(matrix)->GetMatrix3x2F();
4551
4552 D2D1::Matrix3x2F resultMatrix;
4553 resultMatrix.SetProduct(concatMatrix, localMatrix);
4554
4555 wxGraphicsMatrix resultTransform;
4556 resultTransform.SetRefData(new wxD2DMatrixData(GetRenderer(), resultMatrix));
4557
4558 SetTransform(resultTransform);
4559 }
4560
SetTransform(const wxGraphicsMatrix & matrix)4561 void wxD2DContext::SetTransform(const wxGraphicsMatrix& matrix)
4562 {
4563 EnsureInitialized();
4564
4565 D2D1::Matrix3x2F m;
4566 m.SetProduct(wxGetD2DMatrixData(matrix)->GetMatrix3x2F(), m_initTransform);
4567 GetRenderTarget()->SetTransform(&m);
4568
4569 m_isClipBoxValid = false;
4570 }
4571
GetTransform() const4572 wxGraphicsMatrix wxD2DContext::GetTransform() const
4573 {
4574 D2D1::Matrix3x2F transformMatrix;
4575
4576 if (GetRenderTarget() != NULL)
4577 {
4578 GetRenderTarget()->GetTransform(&transformMatrix);
4579
4580 if ( m_initTransform.IsInvertible() )
4581 {
4582 D2D1::Matrix3x2F invMatrix = m_initTransform;
4583 invMatrix.Invert();
4584
4585 D2D1::Matrix3x2F m;
4586 m.SetProduct(transformMatrix, invMatrix);
4587 transformMatrix = m;
4588 }
4589 }
4590 else
4591 {
4592 transformMatrix = D2D1::Matrix3x2F::Identity();
4593 }
4594
4595 wxD2DMatrixData* matrixData = new wxD2DMatrixData(GetRenderer(), transformMatrix);
4596
4597 wxGraphicsMatrix matrix;
4598 matrix.SetRefData(matrixData);
4599
4600 return matrix;
4601 }
4602
DrawBitmap(const wxGraphicsBitmap & bmp,wxDouble x,wxDouble y,wxDouble w,wxDouble h)4603 void wxD2DContext::DrawBitmap(const wxGraphicsBitmap& bmp, wxDouble x, wxDouble y, wxDouble w, wxDouble h)
4604 {
4605 if (m_composition == wxCOMPOSITION_DEST)
4606 return;
4607
4608 wxD2DBitmapData* bitmapData = wxGetD2DBitmapData(bmp);
4609 bitmapData->Bind(this);
4610 wxSize bmpSize = static_cast<wxD2DBitmapData::NativeType*>(bitmapData->GetNativeBitmap())->GetSize();
4611
4612 m_renderTargetHolder->DrawBitmap(
4613 bitmapData->GetD2DBitmap(),
4614 D2D1::RectF(0, 0, bmpSize.GetWidth(), bmpSize.GetHeight()),
4615 D2D1::RectF(x, y, x + w, y + h),
4616 GetInterpolationQuality(),
4617 GetCompositionMode());
4618 }
4619
DrawBitmap(const wxBitmap & bmp,wxDouble x,wxDouble y,wxDouble w,wxDouble h)4620 void wxD2DContext::DrawBitmap(const wxBitmap& bmp, wxDouble x, wxDouble y, wxDouble w, wxDouble h)
4621 {
4622 wxGraphicsBitmap graphicsBitmap = CreateBitmap(bmp);
4623 DrawBitmap(graphicsBitmap, x, y, w, h);
4624 }
4625
DrawIcon(const wxIcon & icon,wxDouble x,wxDouble y,wxDouble w,wxDouble h)4626 void wxD2DContext::DrawIcon(const wxIcon& icon, wxDouble x, wxDouble y, wxDouble w, wxDouble h)
4627 {
4628 DrawBitmap(wxBitmap(icon), x, y, w, h);
4629 }
4630
PushState()4631 void wxD2DContext::PushState()
4632 {
4633 EnsureInitialized();
4634
4635 StateData state;
4636 m_direct2dFactory->CreateDrawingStateBlock(&state.drawingState);
4637 GetRenderTarget()->SaveDrawingState(state.drawingState);
4638 state.layers = m_layers;
4639
4640 m_stateStack.push(state);
4641 }
4642
PopState()4643 void wxD2DContext::PopState()
4644 {
4645 wxCHECK_RET(!m_stateStack.empty(), wxS("No state to pop"));
4646
4647 // Remove all layers from the stack of layers.
4648 while ( !m_layers.empty() )
4649 {
4650 LayerData ld = m_layers.top();
4651 m_layers.pop();
4652
4653 GetRenderTarget()->PopLayer();
4654 ld.layer.reset();
4655 ld.geometry.reset();
4656 }
4657
4658 // Retrieve state data.
4659 StateData state;
4660 state = m_stateStack.top();
4661 m_stateStack.pop();
4662
4663 // Restore all saved layers.
4664 wxStack<LayerData> layersToRestore;
4665 // We have to restore layers on the stack from "bottom" to "top",
4666 // so we have to create a "reverted" stack.
4667 while ( !state.layers.empty() )
4668 {
4669 LayerData ld = state.layers.top();
4670 state.layers.pop();
4671
4672 layersToRestore.push(ld);
4673 }
4674 // And next set layers from the top of "reverted" stack.
4675 while ( !layersToRestore.empty() )
4676 {
4677 LayerData ld = layersToRestore.top();
4678 layersToRestore.pop();
4679
4680 // Restore layer at original position.
4681 GetRenderTarget()->SetTransform(&ld.transformMatrix);
4682 GetRenderTarget()->PushLayer(ld.params, ld.layer);
4683
4684 // Store layer parameters.
4685 m_layers.push(ld);
4686 }
4687
4688 // Restore drawing state.
4689 GetRenderTarget()->RestoreDrawingState(state.drawingState);
4690
4691 m_isClipBoxValid = false;
4692 }
4693
GetTextExtent(const wxString & str,wxDouble * width,wxDouble * height,wxDouble * descent,wxDouble * externalLeading) const4694 void wxD2DContext::GetTextExtent(
4695 const wxString& str,
4696 wxDouble* width,
4697 wxDouble* height,
4698 wxDouble* descent,
4699 wxDouble* externalLeading) const
4700 {
4701 wxCHECK_RET(!m_font.IsNull(),
4702 wxS("wxD2DContext::GetTextExtent - no valid font set"));
4703
4704 wxD2DMeasuringContext::GetTextExtent(
4705 wxGetD2DFontData(m_font), str, width, height, descent, externalLeading);
4706 }
4707
GetPartialTextExtents(const wxString & text,wxArrayDouble & widths) const4708 void wxD2DContext::GetPartialTextExtents(const wxString& text, wxArrayDouble& widths) const
4709 {
4710 wxCHECK_RET(!m_font.IsNull(),
4711 wxS("wxD2DContext::GetPartialTextExtents - no valid font set"));
4712
4713 wxD2DMeasuringContext::GetPartialTextExtents(
4714 wxGetD2DFontData(m_font), text, widths);
4715 }
4716
ShouldOffset() const4717 bool wxD2DContext::ShouldOffset() const
4718 {
4719 if (!m_enableOffset || m_pen.IsNull())
4720 return false;
4721
4722 wxD2DPenData* const penData = wxGetD2DPenData(m_pen);
4723
4724 // always offset for 1-pixel width
4725 if (penData->IsZeroWidth())
4726 return true;
4727
4728 // no offset if overall scale is not odd integer
4729 const wxGraphicsMatrix matrix(GetTransform());
4730 double x = GetContentScaleFactor(), y = x;
4731 matrix.TransformDistance(&x, &y);
4732 if (!wxIsSameDouble(fmod(wxMin(fabs(x), fabs(y)), 2.0), 1.0))
4733 return false;
4734
4735 // offset if pen width is odd integer
4736 return wxIsSameDouble(fmod(double(penData->GetWidth()), 2.0), 1.0);
4737 }
4738
DoDrawText(const wxString & str,wxDouble x,wxDouble y)4739 void wxD2DContext::DoDrawText(const wxString& str, wxDouble x, wxDouble y)
4740 {
4741 wxCHECK_RET(!m_font.IsNull(),
4742 wxS("wxD2DContext::DrawText - no valid font set"));
4743
4744 if (m_composition == wxCOMPOSITION_DEST)
4745 return;
4746
4747 wxD2DFontData* fontData = wxGetD2DFontData(m_font);
4748 fontData->GetBrushData().Bind(this);
4749
4750 wxCOMPtr<IDWriteTextLayout> textLayout = fontData->CreateTextLayout(str);
4751
4752 // Render the text
4753 GetRenderTarget()->DrawTextLayout(
4754 D2D1::Point2F(x, y),
4755 textLayout,
4756 fontData->GetBrushData().GetBrush());
4757 }
4758
EnsureInitialized()4759 void wxD2DContext::EnsureInitialized()
4760 {
4761 if (!m_renderTargetHolder->IsResourceAcquired())
4762 {
4763 m_cachedRenderTarget = m_renderTargetHolder->GetD2DResource();
4764 GetRenderTarget()->GetTransform(&m_initTransform);
4765 GetRenderTarget()->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
4766 GetRenderTarget()->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_DEFAULT);
4767 GetRenderTarget()->BeginDraw();
4768 }
4769 else
4770 {
4771 m_cachedRenderTarget = m_renderTargetHolder->GetD2DResource();
4772 }
4773 }
4774
SetPen(const wxGraphicsPen & pen)4775 void wxD2DContext::SetPen(const wxGraphicsPen& pen)
4776 {
4777 wxGraphicsContext::SetPen(pen);
4778
4779 if (!m_pen.IsNull())
4780 {
4781 EnsureInitialized();
4782
4783 wxD2DPenData* penData = wxGetD2DPenData(pen);
4784 penData->Bind(this);
4785 }
4786 }
4787
AdjustRenderTargetSize()4788 void wxD2DContext::AdjustRenderTargetSize()
4789 {
4790 m_renderTargetHolder->Resize();
4791
4792 // Currently GetSize() can only be called when using MSVC because gcc
4793 // doesn't handle returning aggregates by value as done by D2D libraries,
4794 // see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64384. Not updating the
4795 // size is not great, but it's better than crashing.
4796 #ifdef __VISUALC__
4797 D2D1_SIZE_F renderTargetSize = m_renderTargetHolder->GetD2DResource()->GetSize();
4798 m_width = renderTargetSize.width;
4799 m_height = renderTargetSize.height;
4800 #endif // __VISUALC__
4801 }
4802
ReleaseDeviceDependentResources()4803 void wxD2DContext::ReleaseDeviceDependentResources()
4804 {
4805 ReleaseResources();
4806 }
4807
DrawRectangle(wxDouble x,wxDouble y,wxDouble w,wxDouble h)4808 void wxD2DContext::DrawRectangle(wxDouble x, wxDouble y, wxDouble w, wxDouble h)
4809 {
4810 if (m_composition == wxCOMPOSITION_DEST)
4811 return;
4812
4813 wxD2DOffsetHelper helper(this);
4814
4815 EnsureInitialized();
4816 AdjustRenderTargetSize();
4817
4818 D2D1_RECT_F rect = { (FLOAT)x, (FLOAT)y, (FLOAT)(x + w), (FLOAT)(y + h) };
4819
4820
4821 if (!m_brush.IsNull())
4822 {
4823 wxD2DBrushData* brushData = wxGetD2DBrushData(m_brush);
4824 brushData->Bind(this);
4825 GetRenderTarget()->FillRectangle(rect, brushData->GetBrush());
4826 }
4827
4828 if (!m_pen.IsNull())
4829 {
4830 wxD2DPenData* penData = wxGetD2DPenData(m_pen);
4831 penData->SetWidth(this);
4832 penData->Bind(this);
4833 GetRenderTarget()->DrawRectangle(rect, penData->GetBrush(), penData->GetWidth(), penData->GetStrokeStyle());
4834 }
4835 }
4836
DrawRoundedRectangle(wxDouble x,wxDouble y,wxDouble w,wxDouble h,wxDouble radius)4837 void wxD2DContext::DrawRoundedRectangle(wxDouble x, wxDouble y, wxDouble w, wxDouble h, wxDouble radius)
4838 {
4839 if (m_composition == wxCOMPOSITION_DEST)
4840 return;
4841
4842 wxD2DOffsetHelper helper(this);
4843
4844 EnsureInitialized();
4845 AdjustRenderTargetSize();
4846
4847 D2D1_RECT_F rect = { (FLOAT)x, (FLOAT)y, (FLOAT)(x + w), (FLOAT)(y + h) };
4848
4849 D2D1_ROUNDED_RECT roundedRect = { rect, (FLOAT)radius, (FLOAT)radius };
4850
4851 if (!m_brush.IsNull())
4852 {
4853 wxD2DBrushData* brushData = wxGetD2DBrushData(m_brush);
4854 brushData->Bind(this);
4855 GetRenderTarget()->FillRoundedRectangle(roundedRect, brushData->GetBrush());
4856 }
4857
4858 if (!m_pen.IsNull())
4859 {
4860 wxD2DPenData* penData = wxGetD2DPenData(m_pen);
4861 penData->SetWidth(this);
4862 penData->Bind(this);
4863 GetRenderTarget()->DrawRoundedRectangle(roundedRect, penData->GetBrush(), penData->GetWidth(), penData->GetStrokeStyle());
4864 }
4865 }
4866
DrawEllipse(wxDouble x,wxDouble y,wxDouble w,wxDouble h)4867 void wxD2DContext::DrawEllipse(wxDouble x, wxDouble y, wxDouble w, wxDouble h)
4868 {
4869 if (m_composition == wxCOMPOSITION_DEST)
4870 return;
4871
4872 wxD2DOffsetHelper helper(this);
4873
4874 EnsureInitialized();
4875 AdjustRenderTargetSize();
4876
4877 D2D1_ELLIPSE ellipse = {
4878 { (FLOAT)(x + w / 2), (FLOAT)(y + h / 2) }, // center point
4879 (FLOAT)(w / 2), // radius x
4880 (FLOAT)(h / 2) // radius y
4881 };
4882
4883 if (!m_brush.IsNull())
4884 {
4885 wxD2DBrushData* brushData = wxGetD2DBrushData(m_brush);
4886 brushData->Bind(this);
4887 GetRenderTarget()->FillEllipse(ellipse, brushData->GetBrush());
4888 }
4889
4890 if (!m_pen.IsNull())
4891 {
4892 wxD2DPenData* penData = wxGetD2DPenData(m_pen);
4893 penData->SetWidth(this);
4894 penData->Bind(this);
4895 GetRenderTarget()->DrawEllipse(ellipse, penData->GetBrush(), penData->GetWidth(), penData->GetStrokeStyle());
4896 }
4897 }
4898
Flush()4899 void wxD2DContext::Flush()
4900 {
4901 wxStack<LayerData> layersToRestore;
4902 // Temporarily remove all layers from the stack of layers.
4903 while ( !m_layers.empty() )
4904 {
4905 LayerData ld = m_layers.top();
4906 m_layers.pop();
4907
4908 GetRenderTarget()->PopLayer();
4909
4910 // Save layer data.
4911 layersToRestore.push(ld);
4912 }
4913
4914 HRESULT hr = m_renderTargetHolder->Flush();
4915
4916 if ( hr == (HRESULT)D2DERR_RECREATE_TARGET )
4917 {
4918 ReleaseDeviceDependentResources();
4919 }
4920 else
4921 {
4922 wxCHECK_HRESULT_RET(hr);
4923 }
4924
4925 // Re-apply all layers.
4926 // First, save current transformation matrix.
4927 D2D1_MATRIX_3X2_F currTransform;
4928 GetRenderTarget()->GetTransform(&currTransform);
4929 while ( !layersToRestore.empty() )
4930 {
4931 LayerData ld = layersToRestore.top();
4932 layersToRestore.pop();
4933
4934 // Restore layer at original position.
4935 GetRenderTarget()->SetTransform(&ld.transformMatrix);
4936 GetRenderTarget()->PushLayer(ld.params, ld.layer);
4937
4938 // Store layer parameters.
4939 m_layers.push(ld);
4940 }
4941 // Restore current transformation matrix.
4942 GetRenderTarget()->SetTransform(&currTransform);
4943 }
4944
GetDPI(wxDouble * dpiX,wxDouble * dpiY) const4945 void wxD2DContext::GetDPI(wxDouble* dpiX, wxDouble* dpiY) const
4946 {
4947 if ( GetWindow() )
4948 {
4949 const wxSize dpi = GetWindow()->GetDPI();
4950
4951 if ( dpiX )
4952 *dpiX = dpi.x;
4953 if ( dpiY )
4954 *dpiY = dpi.y;
4955 }
4956 else
4957 {
4958 FLOAT x, y;
4959 GetRenderTarget()->GetDpi(&x, &y);
4960
4961 if ( dpiX )
4962 *dpiX = x;
4963 if ( dpiY )
4964 *dpiY = y;
4965 }
4966 }
4967
4968 //-----------------------------------------------------------------------------
4969 // wxD2DRenderer declaration
4970 //-----------------------------------------------------------------------------
4971
4972 class wxD2DRenderer : public wxGraphicsRenderer
4973 {
4974 public :
4975 wxD2DRenderer();
4976
4977 virtual ~wxD2DRenderer();
4978
4979 wxGraphicsContext* CreateContext(const wxWindowDC& dc) wxOVERRIDE;
4980
4981 wxGraphicsContext* CreateContext(const wxMemoryDC& dc) wxOVERRIDE;
4982
4983 #if wxUSE_PRINTING_ARCHITECTURE
4984 wxGraphicsContext* CreateContext(const wxPrinterDC& dc) wxOVERRIDE;
4985 #endif
4986
4987 #if wxUSE_ENH_METAFILE
4988 wxGraphicsContext* CreateContext(const wxEnhMetaFileDC& dc) wxOVERRIDE;
4989 #endif
4990
4991 wxGraphicsContext* CreateContextFromNativeContext(void* context) wxOVERRIDE;
4992
4993 wxGraphicsContext* CreateContextFromNativeWindow(void* window) wxOVERRIDE;
4994
4995 wxGraphicsContext * CreateContextFromNativeHDC(WXHDC dc) wxOVERRIDE;
4996
4997 wxGraphicsContext* CreateContext(wxWindow* window) wxOVERRIDE;
4998
4999 #if wxUSE_IMAGE
5000 wxGraphicsContext* CreateContextFromImage(wxImage& image) wxOVERRIDE;
5001 #endif // wxUSE_IMAGE
5002
5003 wxGraphicsContext* CreateMeasuringContext() wxOVERRIDE;
5004
5005 wxGraphicsPath CreatePath() wxOVERRIDE;
5006
5007 wxGraphicsMatrix CreateMatrix(
5008 wxDouble a = 1.0, wxDouble b = 0.0, wxDouble c = 0.0, wxDouble d = 1.0,
5009 wxDouble tx = 0.0, wxDouble ty = 0.0) wxOVERRIDE;
5010
5011 wxGraphicsPen CreatePen(const wxGraphicsPenInfo& info) wxOVERRIDE;
5012
5013 wxGraphicsBrush CreateBrush(const wxBrush& brush) wxOVERRIDE;
5014
5015 wxGraphicsBrush CreateLinearGradientBrush(
5016 wxDouble x1, wxDouble y1,
5017 wxDouble x2, wxDouble y2,
5018 const wxGraphicsGradientStops& stops,
5019 const wxGraphicsMatrix& matrix = wxNullGraphicsMatrix) wxOVERRIDE;
5020
5021 wxGraphicsBrush CreateRadialGradientBrush(
5022 wxDouble startX, wxDouble startY,
5023 wxDouble endX, wxDouble endY,
5024 wxDouble radius,
5025 const wxGraphicsGradientStops& stops,
5026 const wxGraphicsMatrix& matrix = wxNullGraphicsMatrix) wxOVERRIDE;
5027
5028 // create a native bitmap representation
5029 wxGraphicsBitmap CreateBitmap(const wxBitmap& bitmap) wxOVERRIDE;
5030
5031 #if wxUSE_IMAGE
5032 wxGraphicsBitmap CreateBitmapFromImage(const wxImage& image) wxOVERRIDE;
5033 wxImage CreateImageFromBitmap(const wxGraphicsBitmap& bmp) wxOVERRIDE;
5034 #endif
5035
5036 wxGraphicsFont CreateFont(const wxFont& font, const wxColour& col) wxOVERRIDE;
5037
5038 wxGraphicsFont CreateFont(
5039 double sizeInPixels, const wxString& facename,
5040 int flags = wxFONTFLAG_DEFAULT,
5041 const wxColour& col = *wxBLACK) wxOVERRIDE;
5042
5043 virtual wxGraphicsFont CreateFontAtDPI(const wxFont& font,
5044 const wxRealPoint& dpi,
5045 const wxColour& col) wxOVERRIDE;
5046
5047 // create a graphics bitmap from a native bitmap
5048 wxGraphicsBitmap CreateBitmapFromNativeBitmap(void* bitmap) wxOVERRIDE;
5049
5050 // create a sub-image from a native image representation
5051 wxGraphicsBitmap CreateSubBitmap(const wxGraphicsBitmap& bitmap, wxDouble x, wxDouble y, wxDouble w, wxDouble h) wxOVERRIDE;
5052
5053 wxString GetName() const wxOVERRIDE;
5054 void GetVersion(int* major, int* minor, int* micro) const wxOVERRIDE;
5055
5056 ID2D1Factory* GetD2DFactory();
5057
5058 private:
5059 wxCOMPtr<ID2D1Factory> m_direct2dFactory;
5060
5061 private :
5062 wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxD2DRenderer);
5063 };
5064
5065 //-----------------------------------------------------------------------------
5066 // wxD2DRenderer implementation
5067 //-----------------------------------------------------------------------------
5068
5069 wxIMPLEMENT_DYNAMIC_CLASS(wxD2DRenderer,wxGraphicsRenderer);
5070
5071 static wxD2DRenderer *gs_D2DRenderer = NULL;
5072
GetDirect2DRenderer()5073 wxGraphicsRenderer* wxGraphicsRenderer::GetDirect2DRenderer()
5074 {
5075 if (!wxDirect2D::Initialize())
5076 return NULL;
5077
5078 if (!gs_D2DRenderer)
5079 {
5080 gs_D2DRenderer = new wxD2DRenderer();
5081 }
5082
5083 return gs_D2DRenderer;
5084 }
5085
wxD2DRenderer()5086 wxD2DRenderer::wxD2DRenderer()
5087 : m_direct2dFactory(wxD2D1Factory())
5088 {
5089 if ( m_direct2dFactory.get() == NULL )
5090 {
5091 wxFAIL_MSG("Could not create Direct2D Factory.");
5092 }
5093 }
5094
~wxD2DRenderer()5095 wxD2DRenderer::~wxD2DRenderer()
5096 {
5097 m_direct2dFactory.reset();
5098 }
5099
CreateContext(const wxWindowDC & dc)5100 wxGraphicsContext* wxD2DRenderer::CreateContext(const wxWindowDC& dc)
5101 {
5102 return new wxD2DContext(this, m_direct2dFactory, dc.GetHDC(), &dc);
5103 }
5104
CreateContext(const wxMemoryDC & dc)5105 wxGraphicsContext* wxD2DRenderer::CreateContext(const wxMemoryDC& dc)
5106 {
5107 wxBitmap bmp = dc.GetSelectedBitmap();
5108 wxASSERT_MSG( bmp.IsOk(), wxS("Should select a bitmap before creating wxGraphicsContext") );
5109
5110 return new wxD2DContext(this, m_direct2dFactory, dc.GetHDC(), &dc,
5111 bmp.HasAlpha() ? D2D1_ALPHA_MODE_PREMULTIPLIED : D2D1_ALPHA_MODE_IGNORE);
5112 }
5113
5114 #if wxUSE_PRINTING_ARCHITECTURE
CreateContext(const wxPrinterDC & WXUNUSED (dc))5115 wxGraphicsContext* wxD2DRenderer::CreateContext(const wxPrinterDC& WXUNUSED(dc))
5116 {
5117 wxFAIL_MSG("not implemented");
5118 return NULL;
5119 }
5120 #endif
5121
5122 #if wxUSE_ENH_METAFILE
CreateContext(const wxEnhMetaFileDC & WXUNUSED (dc))5123 wxGraphicsContext* wxD2DRenderer::CreateContext(const wxEnhMetaFileDC& WXUNUSED(dc))
5124 {
5125 wxFAIL_MSG("not implemented");
5126 return NULL;
5127 }
5128 #endif
5129
CreateContextFromNativeContext(void * nativeContext)5130 wxGraphicsContext* wxD2DRenderer::CreateContextFromNativeContext(void* nativeContext)
5131 {
5132 return new wxD2DContext(this, m_direct2dFactory, nativeContext);
5133 }
5134
CreateContextFromNativeWindow(void * window)5135 wxGraphicsContext* wxD2DRenderer::CreateContextFromNativeWindow(void* window)
5136 {
5137 return new wxD2DContext(this, m_direct2dFactory, (HWND)window);
5138 }
5139
CreateContextFromNativeHDC(WXHDC dc)5140 wxGraphicsContext* wxD2DRenderer::CreateContextFromNativeHDC(WXHDC dc)
5141 {
5142 return new wxD2DContext(this, m_direct2dFactory, (HDC)dc);
5143 }
5144
CreateContext(wxWindow * window)5145 wxGraphicsContext* wxD2DRenderer::CreateContext(wxWindow* window)
5146 {
5147 return new wxD2DContext(this, m_direct2dFactory, (HWND)window->GetHWND(), window);
5148 }
5149
5150 #if wxUSE_IMAGE
CreateContextFromImage(wxImage & image)5151 wxGraphicsContext* wxD2DRenderer::CreateContextFromImage(wxImage& image)
5152 {
5153 return new wxD2DContext(this, m_direct2dFactory, image);
5154 }
5155 #endif // wxUSE_IMAGE
5156
CreateMeasuringContext()5157 wxGraphicsContext* wxD2DRenderer::CreateMeasuringContext()
5158 {
5159 return new wxD2DMeasuringContext(this);
5160 }
5161
CreatePath()5162 wxGraphicsPath wxD2DRenderer::CreatePath()
5163 {
5164 wxGraphicsPath p;
5165 p.SetRefData(new wxD2DPathData(this, m_direct2dFactory));
5166
5167 return p;
5168 }
5169
CreateMatrix(wxDouble a,wxDouble b,wxDouble c,wxDouble d,wxDouble tx,wxDouble ty)5170 wxGraphicsMatrix wxD2DRenderer::CreateMatrix(
5171 wxDouble a, wxDouble b, wxDouble c, wxDouble d,
5172 wxDouble tx, wxDouble ty)
5173 {
5174 wxD2DMatrixData* matrixData = new wxD2DMatrixData(this);
5175 matrixData->Set(a, b, c, d, tx, ty);
5176
5177 wxGraphicsMatrix matrix;
5178 matrix.SetRefData(matrixData);
5179
5180 return matrix;
5181 }
5182
CreatePen(const wxGraphicsPenInfo & info)5183 wxGraphicsPen wxD2DRenderer::CreatePen(const wxGraphicsPenInfo& info)
5184 {
5185 if ( info.GetStyle() == wxPENSTYLE_TRANSPARENT )
5186 {
5187 return wxNullGraphicsPen;
5188 }
5189 else
5190 {
5191 wxGraphicsPen p;
5192 wxD2DPenData* penData = new wxD2DPenData(this, m_direct2dFactory, info);
5193 p.SetRefData(penData);
5194 return p;
5195 }
5196 }
5197
CreateBrush(const wxBrush & brush)5198 wxGraphicsBrush wxD2DRenderer::CreateBrush(const wxBrush& brush)
5199 {
5200 if ( !brush.IsOk() || brush.GetStyle() == wxBRUSHSTYLE_TRANSPARENT )
5201 {
5202 return wxNullGraphicsBrush;
5203 }
5204 else
5205 {
5206 wxGraphicsBrush b;
5207 b.SetRefData(new wxD2DBrushData(this, brush));
5208 return b;
5209 }
5210 }
5211
CreateLinearGradientBrush(wxDouble x1,wxDouble y1,wxDouble x2,wxDouble y2,const wxGraphicsGradientStops & stops,const wxGraphicsMatrix & matrix)5212 wxGraphicsBrush wxD2DRenderer::CreateLinearGradientBrush(
5213 wxDouble x1, wxDouble y1,
5214 wxDouble x2, wxDouble y2,
5215 const wxGraphicsGradientStops& stops,
5216 const wxGraphicsMatrix& matrix)
5217 {
5218 wxD2DBrushData* brushData = new wxD2DBrushData(this);
5219 brushData->CreateLinearGradientBrush(x1, y1, x2, y2, stops, matrix);
5220
5221 wxGraphicsBrush brush;
5222 brush.SetRefData(brushData);
5223
5224 return brush;
5225 }
5226
CreateRadialGradientBrush(wxDouble startX,wxDouble startY,wxDouble endX,wxDouble endY,wxDouble radius,const wxGraphicsGradientStops & stops,const wxGraphicsMatrix & matrix)5227 wxGraphicsBrush wxD2DRenderer::CreateRadialGradientBrush(
5228 wxDouble startX, wxDouble startY,
5229 wxDouble endX, wxDouble endY,
5230 wxDouble radius,
5231 const wxGraphicsGradientStops& stops,
5232 const wxGraphicsMatrix& matrix)
5233 {
5234 wxD2DBrushData* brushData = new wxD2DBrushData(this);
5235 brushData->CreateRadialGradientBrush(startX, startY, endX, endY, radius, stops, matrix);
5236
5237 wxGraphicsBrush brush;
5238 brush.SetRefData(brushData);
5239
5240 return brush;
5241 }
5242
5243 // create a native bitmap representation
CreateBitmap(const wxBitmap & bitmap)5244 wxGraphicsBitmap wxD2DRenderer::CreateBitmap(const wxBitmap& bitmap)
5245 {
5246 wxD2DBitmapData* bitmapData = new wxD2DBitmapData(this, bitmap);
5247
5248 wxGraphicsBitmap graphicsBitmap;
5249 graphicsBitmap.SetRefData(bitmapData);
5250
5251 return graphicsBitmap;
5252 }
5253
5254 // create a graphics bitmap from a native bitmap
CreateBitmapFromNativeBitmap(void * bitmap)5255 wxGraphicsBitmap wxD2DRenderer::CreateBitmapFromNativeBitmap(void* bitmap)
5256 {
5257 wxD2DBitmapData* bitmapData = new wxD2DBitmapData(this, static_cast<wxD2DBitmapResourceHolder*>(bitmap));
5258
5259 wxGraphicsBitmap graphicsBitmap;
5260 graphicsBitmap.SetRefData(bitmapData);
5261
5262 return graphicsBitmap;
5263 }
5264
5265 #if wxUSE_IMAGE
CreateBitmapFromImage(const wxImage & image)5266 wxGraphicsBitmap wxD2DRenderer::CreateBitmapFromImage(const wxImage& image)
5267 {
5268 wxD2DBitmapData* bitmapData = new wxD2DBitmapData(this, image);
5269
5270 wxGraphicsBitmap graphicsBitmap;
5271 graphicsBitmap.SetRefData(bitmapData);
5272
5273 return graphicsBitmap;
5274 }
5275
CreateImageFromBitmap(const wxGraphicsBitmap & bmp)5276 wxImage wxD2DRenderer::CreateImageFromBitmap(const wxGraphicsBitmap& bmp)
5277 {
5278 return static_cast<wxD2DBitmapData::NativeType*>(bmp.GetNativeBitmap())
5279 ->ConvertToImage();
5280 }
5281 #endif
5282
CreateFont(const wxFont & font,const wxColour & col)5283 wxGraphicsFont wxD2DRenderer::CreateFont(const wxFont& font, const wxColour& col)
5284 {
5285 return CreateFontAtDPI(font, wxRealPoint(), col);
5286 }
5287
CreateFont(double sizeInPixels,const wxString & facename,int flags,const wxColour & col)5288 wxGraphicsFont wxD2DRenderer::CreateFont(
5289 double sizeInPixels, const wxString& facename,
5290 int flags,
5291 const wxColour& col)
5292 {
5293 // Use the same DPI as wxFont will use in SetPixelSize, so these cancel
5294 // each other out and we are left with the actual pixel size.
5295 ScreenHDC hdc;
5296 wxRealPoint dpi(::GetDeviceCaps(hdc, LOGPIXELSX),
5297 ::GetDeviceCaps(hdc, LOGPIXELSY));
5298
5299 return CreateFontAtDPI(
5300 wxFontInfo(wxSize(sizeInPixels, sizeInPixels)).AllFlags(flags).FaceName(facename),
5301 dpi, col);
5302 }
5303
CreateFontAtDPI(const wxFont & font,const wxRealPoint & dpi,const wxColour & col)5304 wxGraphicsFont wxD2DRenderer::CreateFontAtDPI(const wxFont& font,
5305 const wxRealPoint& dpi,
5306 const wxColour& col)
5307 {
5308 wxD2DFontData* fontData = new wxD2DFontData(this, font, dpi, col);
5309 if ( !fontData->GetFont() )
5310 {
5311 // Apparently a non-TrueType font is given and hence
5312 // corresponding DirectWrite font couldn't be created.
5313 delete fontData;
5314 return wxNullGraphicsFont;
5315 }
5316
5317 wxGraphicsFont graphicsFont;
5318 graphicsFont.SetRefData(fontData);
5319
5320 return graphicsFont;
5321 }
5322
5323 // create a sub-image from a native image representation
CreateSubBitmap(const wxGraphicsBitmap & bitmap,wxDouble x,wxDouble y,wxDouble w,wxDouble h)5324 wxGraphicsBitmap wxD2DRenderer::CreateSubBitmap(const wxGraphicsBitmap& bitmap, wxDouble x, wxDouble y, wxDouble w, wxDouble h)
5325 {
5326 typedef wxD2DBitmapData::NativeType* NativeBitmap;
5327
5328 NativeBitmap natBmp = static_cast<NativeBitmap>(bitmap.GetNativeBitmap())->GetSubBitmap(x, y, w, h);
5329 wxGraphicsBitmap bmpRes;
5330 bmpRes.SetRefData(new wxD2DBitmapData(this, natBmp));
5331 return bmpRes;
5332 }
5333
GetName() const5334 wxString wxD2DRenderer::GetName() const
5335 {
5336 return "direct2d";
5337 }
5338
GetVersion(int * major,int * minor,int * micro) const5339 void wxD2DRenderer::GetVersion(int* major, int* minor, int* micro) const
5340 {
5341 if (wxDirect2D::HasDirect2DSupport())
5342 {
5343 if (major)
5344 *major = 1;
5345
5346 if (minor)
5347 {
5348 switch(wxDirect2D::GetDirect2DVersion())
5349 {
5350 case wxDirect2D::wxD2D_VERSION_1_0:
5351 *minor = 0;
5352 break;
5353 case wxDirect2D::wxD2D_VERSION_1_1:
5354 *minor = 1;
5355 break;
5356 case wxDirect2D::wxD2D_VERSION_NONE:
5357 // This is not supposed to happen, but we handle this value in
5358 // the switch to ensure that we'll get warnings if any new
5359 // values, not handled here, are added to the enum later.
5360 *minor = -1;
5361 break;
5362 }
5363 }
5364
5365 if (micro)
5366 *micro = 0;
5367 }
5368 }
5369
GetD2DFactory()5370 ID2D1Factory* wxD2DRenderer::GetD2DFactory()
5371 {
5372 return m_direct2dFactory;
5373 }
5374
wxGetD2DFactory(wxGraphicsRenderer * renderer)5375 ID2D1Factory* wxGetD2DFactory(wxGraphicsRenderer* renderer)
5376 {
5377 return static_cast<wxD2DRenderer*>(renderer)->GetD2DFactory();
5378 }
5379
5380 // ----------------------------------------------------------------------------
5381 // Module ensuring all global/singleton objects are destroyed on shutdown.
5382 // ----------------------------------------------------------------------------
5383
5384 class wxDirect2DModule : public wxModule
5385 {
5386 public:
wxDirect2DModule()5387 wxDirect2DModule()
5388 {
5389 // Using Direct2D requires OLE and, importantly, we must ensure our
5390 // OnExit() runs before it is uninitialized.
5391 AddDependency("wxOleInitModule");
5392 }
5393
OnInit()5394 virtual bool OnInit() wxOVERRIDE
5395 {
5396 return true;
5397 }
5398
OnExit()5399 virtual void OnExit() wxOVERRIDE
5400 {
5401 if ( gs_WICImagingFactory )
5402 {
5403 gs_WICImagingFactory->Release();
5404 gs_WICImagingFactory = NULL;
5405 }
5406
5407 if ( gs_IDWriteFactory )
5408 {
5409 #if wxUSE_PRIVATE_FONTS
5410 if ( wxDirect2DFontCollectionLoader::IsInitialized() )
5411 {
5412 gs_pPrivateFontCollection.reset();
5413 gs_IDWriteFactory->UnregisterFontCollectionLoader(wxDirect2DFontCollectionLoader::GetLoader());
5414 }
5415 #endif // wxUSE_PRIVATE_FONTS
5416 gs_IDWriteFactory->Release();
5417 gs_IDWriteFactory = NULL;
5418 }
5419
5420 if ( gs_D2DRenderer )
5421 {
5422 delete gs_D2DRenderer;
5423 gs_D2DRenderer = NULL;
5424 }
5425
5426 if ( gs_ID2D1Factory )
5427 {
5428 gs_ID2D1Factory->Release();
5429 gs_ID2D1Factory = NULL;
5430 }
5431 }
5432
5433 private:
5434 wxDECLARE_DYNAMIC_CLASS(wxDirect2DModule);
5435 };
5436
5437 wxIMPLEMENT_DYNAMIC_CLASS(wxDirect2DModule, wxModule);
5438
5439
5440 #endif // wxUSE_GRAPHICS_DIRECT2D
5441