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