1 // This file is part of VSTGUI. It is subject to the license terms
2 // in the LICENSE file found in the top-level directory of this
3 // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
4
5 #include "d2dbitmap.h"
6
7 #if WINDOWS
8
9 #include "../win32support.h"
10 #include "../../../cstring.h"
11 #include <wincodec.h>
12 #include <d2d1.h>
13 #include <shlwapi.h>
14 #include <cassert>
15
16 namespace VSTGUI {
17
18 //-----------------------------------------------------------------------------
D2DBitmap()19 D2DBitmap::D2DBitmap ()
20 : scaleFactor (1.)
21 , source (nullptr)
22 {
23 }
24
25 //-----------------------------------------------------------------------------
D2DBitmap(const CPoint & size)26 D2DBitmap::D2DBitmap (const CPoint& size)
27 : size (size)
28 , scaleFactor (1.)
29 , source (nullptr)
30 {
31 REFWICPixelFormatGUID pixelFormat = GUID_WICPixelFormat32bppPBGRA;
32 WICBitmapCreateCacheOption options = WICBitmapCacheOnLoad;
33 IWICBitmap* bitmap = nullptr;
34 HRESULT hr = getWICImageingFactory ()->CreateBitmap ((UINT)size.x, (UINT)size.y, pixelFormat, options, &bitmap);
35 if (hr == S_OK && bitmap)
36 {
37 source = bitmap;
38 }
39 else
40 {
41 #if DEBUG
42 DebugPrint ("Could not create Bitmap with size : %d, %d\n", (int)size.x, (int)size.y);
43 #endif
44 }
45 }
46
47 //-----------------------------------------------------------------------------
~D2DBitmap()48 D2DBitmap::~D2DBitmap ()
49 {
50 if (source)
51 {
52 D2DBitmapCache* gCache = D2DBitmapCache::instance ();
53 vstgui_assert (gCache, "D2D resources are already freed");
54 gCache->removeBitmap (this);
55 if (source)
56 source->Release ();
57 }
58 }
59
60 //-----------------------------------------------------------------------------
getBitmap()61 IWICBitmap* D2DBitmap::getBitmap ()
62 {
63 if (getSource () == nullptr)
64 return nullptr;
65
66 IWICBitmap* icBitmap = nullptr;
67 if (!SUCCEEDED (getSource ()->QueryInterface (IID_IWICBitmap, (void**)&icBitmap)))
68 {
69 if (SUCCEEDED (getWICImageingFactory ()->CreateBitmapFromSource (getSource (), WICBitmapCacheOnDemand, &icBitmap)))
70 {
71 replaceBitmapSource (icBitmap);
72 }
73 }
74 if (icBitmap)
75 icBitmap->Release ();
76 return icBitmap;
77 }
78
79 //-----------------------------------------------------------------------------
createMemoryPNGRepresentation()80 PNGBitmapBuffer D2DBitmap::createMemoryPNGRepresentation ()
81 {
82 if (getSource () == nullptr)
83 return {};
84
85 PNGBitmapBuffer buffer;
86 IWICBitmapEncoder* encoder = nullptr;
87 if (SUCCEEDED (getWICImageingFactory ()->CreateEncoder (GUID_ContainerFormatPng, NULL, &encoder)))
88 {
89 IStream* stream = nullptr;
90 if (SUCCEEDED (CreateStreamOnHGlobal (NULL, TRUE, &stream)))
91 {
92 if (SUCCEEDED (encoder->Initialize (stream, WICBitmapEncoderNoCache)))
93 {
94 IWICBitmapFrameEncode* frame = nullptr;
95 if (SUCCEEDED (encoder->CreateNewFrame (&frame, NULL)))
96 {
97 if (SUCCEEDED (frame->Initialize (NULL)))
98 {
99 if (SUCCEEDED (frame->WriteSource (this->getSource (), NULL)))
100 {
101 if (SUCCEEDED (frame->Commit ()))
102 {
103 if (SUCCEEDED (encoder->Commit ()))
104 {
105 HGLOBAL hGlobal;
106 if (SUCCEEDED (GetHGlobalFromStream (stream, &hGlobal)))
107 {
108 void* globalAddress = GlobalLock (hGlobal);
109 SIZE_T globalSize = GlobalSize (hGlobal);
110 if (globalSize && globalAddress)
111 {
112 buffer.resize (globalSize);
113 memcpy (buffer.data (), globalAddress, globalSize);
114 }
115 GlobalUnlock (hGlobal);
116 }
117 }
118 }
119 }
120 }
121 frame->Release ();
122 }
123 }
124 stream->Release ();
125 }
126 encoder->Release ();
127 }
128 return buffer;
129 }
130
131 //-----------------------------------------------------------------------------
loadFromStream(IStream * iStream)132 bool D2DBitmap::loadFromStream (IStream* iStream)
133 {
134 IWICBitmapDecoder* decoder = nullptr;
135 IWICStream* stream = nullptr;
136 if (SUCCEEDED (getWICImageingFactory ()->CreateStream (&stream)))
137 {
138 if (SUCCEEDED (stream->InitializeFromIStream (iStream)))
139 {
140 getWICImageingFactory ()->CreateDecoderFromStream (stream, NULL, WICDecodeMetadataCacheOnLoad, &decoder);
141 }
142 stream->Release ();
143 }
144 if (decoder)
145 {
146 IWICBitmapFrameDecode* frame;
147 if (SUCCEEDED (decoder->GetFrame (0, &frame)))
148 {
149 UINT w = 0;
150 UINT h = 0;
151 frame->GetSize (&w, &h);
152 size.x = w;
153 size.y = h;
154 IWICFormatConverter* converter = nullptr;
155 getWICImageingFactory ()->CreateFormatConverter (&converter);
156 if (converter)
157 {
158 if (!SUCCEEDED (converter->Initialize (frame, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, NULL, 0.f, WICBitmapPaletteTypeMedianCut)))
159 {
160 converter->Release ();
161 converter = nullptr;
162 }
163 else
164 source = converter;
165 }
166 frame->Release ();
167 }
168 decoder->Release ();
169 }
170 return source != nullptr;
171 }
172
173 //-----------------------------------------------------------------------------
load(const CResourceDescription & resourceDesc)174 bool D2DBitmap::load (const CResourceDescription& resourceDesc)
175 {
176 if (source)
177 return true;
178 bool result = false;
179 if (resourceDesc.type == CResourceDescription::kStringType)
180 {
181 if (auto path = WinResourceInputStream::getBasePath ())
182 {
183 *path += resourceDesc.u.name;
184 UTF8StringHelper wpath (*path);
185 IStream* stream = nullptr;
186 if (SUCCEEDED (SHCreateStreamOnFileEx (wpath, STGM_READ|STGM_SHARE_DENY_WRITE, 0, false, 0, &stream)))
187 {
188 result = loadFromStream (stream);
189 stream->Release ();
190 }
191 }
192 }
193
194 if (result == false)
195 {
196 ResourceStream* resourceStream = new ResourceStream;
197 if (resourceStream->open (resourceDesc, "PNG"))
198 {
199 result = loadFromStream (resourceStream);
200 }
201 resourceStream->Release ();
202 }
203
204 #if DEBUG
205 if (result == false && resourceDesc.type == CResourceDescription::kStringType)
206 {
207 // In DEBUG mode we allow to load the bitmap from a path so that the WYSIWYG editor is usable
208 UTF8StringHelper path (resourceDesc.u.name);
209 IStream* stream = nullptr;
210 if (SUCCEEDED (SHCreateStreamOnFileEx (path, STGM_READ|STGM_SHARE_DENY_WRITE, 0, false, 0, &stream)))
211 {
212 result = loadFromStream (stream);
213 stream->Release ();
214 }
215 }
216 #endif
217 return result;
218 }
219
220 //-----------------------------------------------------------------------------
createHBitmap()221 HBITMAP D2DBitmap::createHBitmap ()
222 {
223 if (getSource () == nullptr)
224 return nullptr;
225
226 BITMAPINFO pbmi {};
227 pbmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
228 pbmi.bmiHeader.biPlanes = 1;
229 pbmi.bmiHeader.biCompression = BI_RGB;
230 pbmi.bmiHeader.biWidth = (LONG)size.x;
231 pbmi.bmiHeader.biHeight = (LONG)size.y;
232 pbmi.bmiHeader.biBitCount = 32;
233
234 HDC hdc = GetDC (NULL);
235 if (hdc == nullptr)
236 return nullptr;
237 BYTE* bits = nullptr;
238 HBITMAP result = CreateDIBSection (hdc, &pbmi, DIB_RGB_COLORS, reinterpret_cast<void**> (&bits), nullptr, 0);
239 if (result)
240 {
241 getSource ()->CopyPixels (NULL, (UINT)size.x * sizeof (DWORD), (UINT)size.x * sizeof (DWORD) * (UINT)size.y, bits);
242 }
243 return result;
244 }
245
246 //-----------------------------------------------------------------------------
replaceBitmapSource(IWICBitmapSource * newSourceBitmap)247 void D2DBitmap::replaceBitmapSource (IWICBitmapSource* newSourceBitmap)
248 {
249 if (source)
250 {
251 D2DBitmapCache::instance ()->removeBitmap (this);
252 source->Release ();
253 }
254 source = newSourceBitmap;
255 if (source)
256 source->AddRef ();
257 }
258
259 //-----------------------------------------------------------------------------
lockPixels(bool alphaPremultiplied)260 SharedPointer<IPlatformBitmapPixelAccess> D2DBitmap::lockPixels (bool alphaPremultiplied)
261 {
262 if (getSource () == nullptr)
263 return nullptr;
264 auto pixelAccess = owned (new PixelAccess);
265 if (pixelAccess->init (this, alphaPremultiplied))
266 return shared<IPlatformBitmapPixelAccess> (pixelAccess);
267 return nullptr;
268 }
269
270 //-----------------------------------------------------------------------------
PixelAccess()271 D2DBitmap::PixelAccess::PixelAccess ()
272 : bitmap (nullptr)
273 , bLock (nullptr)
274 , ptr (nullptr)
275 , bytesPerRow (0)
276 {
277 }
278
279 //-----------------------------------------------------------------------------
~PixelAccess()280 D2DBitmap::PixelAccess::~PixelAccess ()
281 {
282 if (bLock)
283 {
284 if (!alphaPremultiplied)
285 premultiplyAlpha (ptr, bytesPerRow, bitmap->getSize ());
286 bLock->Release ();
287 }
288 if (bitmap)
289 {
290 D2DBitmapCache::instance ()->removeBitmap (bitmap);
291 bitmap->forget ();
292 }
293 }
294
295 //-----------------------------------------------------------------------------
init(D2DBitmap * inBitmap,bool _alphaPremultiplied)296 bool D2DBitmap::PixelAccess::init (D2DBitmap* inBitmap, bool _alphaPremultiplied)
297 {
298 bool result = false;
299 vstgui_assert (inBitmap);
300 IWICBitmap* icBitmap = inBitmap->getBitmap ();
301 if (icBitmap)
302 {
303 WICRect rcLock = { 0, 0, (INT)inBitmap->getSize ().x, (INT)inBitmap->getSize ().y };
304 if (SUCCEEDED (icBitmap->Lock (&rcLock, WICBitmapLockRead | WICBitmapLockWrite, &bLock)))
305 {
306 bLock->GetStride (&bytesPerRow);
307 UINT bufferSize;
308 bLock->GetDataPointer (&bufferSize, &ptr);
309
310 bitmap = inBitmap;
311 bitmap->remember ();
312 alphaPremultiplied = _alphaPremultiplied;
313 if (!alphaPremultiplied)
314 unpremultiplyAlpha (ptr, bytesPerRow, bitmap->getSize ());
315 result = true;
316 }
317 }
318 return result;
319 }
320
321 //-----------------------------------------------------------------------------
premultiplyAlpha(BYTE * ptr,UINT bytesPerRow,const CPoint & size)322 void D2DBitmap::PixelAccess::premultiplyAlpha (BYTE* ptr, UINT bytesPerRow, const CPoint& size)
323 {
324 for (int32_t y = 0; y < (int32_t)size.y; y++, ptr += bytesPerRow)
325 {
326 uint32_t* pixelPtr = (uint32_t*)ptr;
327 for (int32_t x = 0; x < (int32_t)size.x; x++, pixelPtr++)
328 {
329 uint8_t* pixel = (uint8_t*)pixelPtr;
330 if (pixel[3] == 0)
331 {
332 *pixelPtr = 0;
333 continue;
334 }
335 pixel[0] = (uint32_t)((pixel[0] * pixel[3]) >> 8);
336 pixel[1] = (uint32_t)((pixel[1] * pixel[3]) >> 8);
337 pixel[2] = (uint32_t)((pixel[2] * pixel[3]) >> 8);
338 }
339 }
340 }
341
342 //-----------------------------------------------------------------------------
unpremultiplyAlpha(BYTE * ptr,UINT bytesPerRow,const CPoint & size)343 void D2DBitmap::PixelAccess::unpremultiplyAlpha (BYTE* ptr, UINT bytesPerRow, const CPoint& size)
344 {
345 for (int32_t y = 0; y < (int32_t)size.y; y++, ptr += bytesPerRow)
346 {
347 uint32_t* pixelPtr = (uint32_t*)ptr;
348 for (int32_t x = 0; x < (int32_t)size.x; x++, pixelPtr++)
349 {
350 uint8_t* pixel = (uint8_t*)pixelPtr;
351 if (pixel[3] == 0)
352 continue;
353 pixel[0] = (uint32_t)(pixel[0] * 255) / pixel[3];
354 pixel[1] = (uint32_t)(pixel[1] * 255) / pixel[3];
355 pixel[2] = (uint32_t)(pixel[2] * 255) / pixel[3];
356 }
357 }
358 }
359
360 //-----------------------------------------------------------------------------
361 //-----------------------------------------------------------------------------
362 //-----------------------------------------------------------------------------
getBitmap(D2DBitmap * bitmap,ID2D1RenderTarget * renderTarget)363 ID2D1Bitmap* D2DBitmapCache::getBitmap (D2DBitmap* bitmap, ID2D1RenderTarget* renderTarget)
364 {
365 BitmapCache::iterator it = cache.find (bitmap);
366 if (it != cache.end ())
367 {
368 RenderTargetBitmapMap::iterator it2 = it->second.find (renderTarget);
369 if (it2 != it->second.end ())
370 {
371 return it2->second;
372 }
373 ID2D1Bitmap* b = createBitmap (bitmap, renderTarget);
374 if (b)
375 it->second.emplace (renderTarget, b);
376 return b;
377 }
378 auto insertSuccess = cache.emplace (bitmap, RenderTargetBitmapMap ());
379 if (insertSuccess.second == true)
380 {
381 ID2D1Bitmap* b = createBitmap (bitmap, renderTarget);
382 if (b)
383 {
384 insertSuccess.first->second.emplace (renderTarget, b);
385 return b;
386 }
387 }
388 return nullptr;
389 }
390
391 //-----------------------------------------------------------------------------
removeBitmap(D2DBitmap * bitmap)392 void D2DBitmapCache::removeBitmap (D2DBitmap* bitmap)
393 {
394 BitmapCache::iterator it = cache.find (bitmap);
395 if (it != cache.end ())
396 {
397 RenderTargetBitmapMap::iterator it2 = it->second.begin ();
398 while (it2 != it->second.end ())
399 {
400 it2->second->Release ();
401 it2++;
402 }
403 cache.erase (it);
404 }
405 }
406
407 //-----------------------------------------------------------------------------
removeRenderTarget(ID2D1RenderTarget * renderTarget)408 void D2DBitmapCache::removeRenderTarget (ID2D1RenderTarget* renderTarget)
409 {
410 BitmapCache::iterator it = cache.begin ();
411 while (it != cache.end ())
412 {
413 RenderTargetBitmapMap::iterator it2 = it->second.begin ();
414 while (it2 != it->second.end ())
415 {
416 if (it2->first == renderTarget)
417 {
418 it2->second->Release ();
419 it->second.erase (it2++);
420 }
421 else
422 it2++;
423 }
424 it++;
425 }
426 }
427
428 //-----------------------------------------------------------------------------
createBitmap(D2DBitmap * bitmap,ID2D1RenderTarget * renderTarget)429 ID2D1Bitmap* D2DBitmapCache::createBitmap (D2DBitmap* bitmap, ID2D1RenderTarget* renderTarget)
430 {
431 if (bitmap->getSource () == nullptr)
432 return nullptr;
433 ID2D1Bitmap* d2d1Bitmap = nullptr;
434 renderTarget->CreateBitmapFromWicBitmap (bitmap->getSource (), &d2d1Bitmap);
435 return d2d1Bitmap;
436 }
437
438 static D2DBitmapCache* gD2DBitmapCache = nullptr;
439 //-----------------------------------------------------------------------------
D2DBitmapCache()440 D2DBitmapCache::D2DBitmapCache ()
441 {
442 gD2DBitmapCache = this;
443 }
444
445 //-----------------------------------------------------------------------------
~D2DBitmapCache()446 D2DBitmapCache::~D2DBitmapCache ()
447 {
448 #if DEBUG
449 for (BitmapCache::const_iterator it = cache.begin (); it != cache.end (); it++)
450 {
451 vstgui_assert (it->second.size () == 0);
452 }
453 #endif
454 gD2DBitmapCache = nullptr;
455 }
456
457 //-----------------------------------------------------------------------------
instance()458 D2DBitmapCache* D2DBitmapCache::instance ()
459 {
460 static D2DBitmapCache gInstance;
461 return gD2DBitmapCache;
462 }
463
464 } // VSTGUI
465
466 #endif // WINDOWS
467