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 "../../cpoint.h"
6 #include "../../cresourcedescription.h"
7 
8 #include "cairobitmap.h"
9 #include <memory>
10 #include <vector>
11 #include <iostream>
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <unistd.h>
15 
16 
17 
18 //------------------------------------------------------------------------
19 namespace VSTGUI {
20 namespace Cairo {
21 namespace CairoBitmapPrivate {
22 
23 //-----------------------------------------------------------------------------
24 struct PNGMemoryReader
25 {
PNGMemoryReaderVSTGUI::Cairo::CairoBitmapPrivate::PNGMemoryReader26 	PNGMemoryReader (const uint8_t* ptr, size_t size) : ptr (ptr), size (size) {}
27 
createVSTGUI::Cairo::CairoBitmapPrivate::PNGMemoryReader28 	cairo_surface_t* create () { return cairo_image_surface_create_from_png_stream (read, this); }
29 
30 private:
readVSTGUI::Cairo::CairoBitmapPrivate::PNGMemoryReader31 	static cairo_status_t read (void* closure, unsigned char* data, unsigned int length)
32 	{
33 		auto self = reinterpret_cast<PNGMemoryReader*> (closure);
34 		auto numBytes = std::min<size_t> (length, self->size);
35 		if (numBytes)
36 		{
37 			memcpy (data, self->ptr, numBytes);
38 			self->ptr += numBytes;
39 			self->size -= numBytes;
40 			return CAIRO_STATUS_SUCCESS;
41 		}
42 		return CAIRO_STATUS_READ_ERROR;
43 	}
44 
45 	const uint8_t* ptr;
46 	size_t size;
47 };
48 
49 //-----------------------------------------------------------------------------
50 struct PNGMemoryWriter
51 {
52 	using Buffer = PNGBitmapBuffer;
53 
createVSTGUI::Cairo::CairoBitmapPrivate::PNGMemoryWriter54 	Buffer create (cairo_surface_t* image)
55 	{
56 		Buffer buffer;
57 		cairo_surface_write_to_png_stream (image, write, &buffer);
58 		return buffer;
59 	}
60 
61 private:
writeVSTGUI::Cairo::CairoBitmapPrivate::PNGMemoryWriter62 	static cairo_status_t write (void* closure, const unsigned char* data, unsigned int length)
63 	{
64 		auto buffer = reinterpret_cast<Buffer*> (closure);
65 		if (!buffer)
66 			return CAIRO_STATUS_WRITE_ERROR;
67 		buffer->reserve (buffer->size () + length);
68 		std::copy_n (data, length, std::back_inserter (*buffer));
69 		return CAIRO_STATUS_SUCCESS;
70 	}
71 };
72 
73 //-----------------------------------------------------------------------------
createImageFromPath(const char * path)74 static SurfaceHandle createImageFromPath (const char* path)
75 {
76 	if (auto surface = cairo_image_surface_create_from_png (path))
77 	{
78 		if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS)
79 		{
80 			cairo_surface_destroy (surface);
81 			return {};
82 		}
83 		if (cairo_image_surface_get_format (surface) == CAIRO_FORMAT_ARGB32)
84 			return SurfaceHandle {surface};
85 
86 		// vstgui always works with 32 bit images
87 		auto x = cairo_image_surface_get_width (surface);
88 		auto y = cairo_image_surface_get_height (surface);
89 		auto surface32 = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, x, y);
90 		vstgui_assert (cairo_surface_status (surface32) == CAIRO_STATUS_SUCCESS);
91 		auto context = cairo_create (surface32);
92 		vstgui_assert (cairo_status (context) == CAIRO_STATUS_SUCCESS);
93 		cairo_set_source_surface (context, surface, 0, 0);
94 		vstgui_assert (cairo_status (context) == CAIRO_STATUS_SUCCESS);
95 		cairo_paint (context);
96 		vstgui_assert (cairo_status (context) == CAIRO_STATUS_SUCCESS);
97 		cairo_surface_flush (surface32);
98 		vstgui_assert (cairo_status (context) == CAIRO_STATUS_SUCCESS);
99 		cairo_destroy (context);
100 		cairo_surface_destroy (surface);
101 		return SurfaceHandle {surface32};
102 	}
103 	return {};
104 }
105 
106 //-----------------------------------------------------------------------------
107 class PixelAccess : public IPlatformBitmapPixelAccess
108 {
109 public:
110 	~PixelAccess () override;
111 
112 	bool init (Bitmap* bitmap, const SurfaceHandle& surface);
113 
114 private:
115 	uint8_t* address {nullptr};
116 	uint32_t bytesPerRow {0};
117 
getAddress() const118 	uint8_t* getAddress () const override { return address; }
getBytesPerRow() const119 	uint32_t getBytesPerRow () const override { return bytesPerRow; }
getPixelFormat() const120 	PixelFormat getPixelFormat () const override
121 	{
122 #if __LITTLE_ENDIAN
123 		return kBGRA;
124 #else
125 		return kARGB;
126 #endif
127 	}
128 
129 	SharedPointer<Bitmap> bitmap;
130 	SurfaceHandle surface;
131 };
132 
133 //-----------------------------------------------------------------------------
134 } // CairoBitmapPrivate
135 
136 //-----------------------------------------------------------------------------
__anon77eb67190102() 137 Bitmap::GetResourcePathFunc Bitmap::getResourcePath = [] () { return std::string (); };
138 
139 //-----------------------------------------------------------------------------
setGetResourcePathFunc(GetResourcePathFunc && func)140 void Bitmap::setGetResourcePathFunc (GetResourcePathFunc&& func)
141 {
142 	getResourcePath = std::move (func);
143 }
144 
145 //-----------------------------------------------------------------------------
Bitmap(const CPoint * _size)146 Bitmap::Bitmap (const CPoint* _size)
147 {
148 	if (_size)
149 	{
150 		size = *_size;
151 		surface = SurfaceHandle (cairo_image_surface_create (CAIRO_FORMAT_ARGB32, size.x, size.y));
152 	}
153 }
154 
155 //-----------------------------------------------------------------------------
Bitmap(const SurfaceHandle & surface)156 Bitmap::Bitmap (const SurfaceHandle& surface) : surface (surface)
157 {
158 	size.x = cairo_image_surface_get_width (surface);
159 	size.y = cairo_image_surface_get_height (surface);
160 }
161 
162 //-----------------------------------------------------------------------------
~Bitmap()163 Bitmap::~Bitmap ()
164 {
165 }
166 
167 //-----------------------------------------------------------------------------
load(const CResourceDescription & desc)168 bool Bitmap::load (const CResourceDescription& desc)
169 {
170 	auto path = getResourcePath ();
171 	if (!path.empty ())
172 	{
173 		if (desc.type == CResourceDescription::kIntegerType)
174 		{
175 			char filename[PATH_MAX];
176 			sprintf (filename, "bmp%05d.png", (int32_t)desc.u.id);
177 			path += filename;
178 		}
179 		else
180 		{
181                         // SURGE CHANGE - if we ask for a file OPEN THE FILE IF IT IS THERE. Thanks.
182 			struct stat sb;
183        			auto canStatWithoutResource = stat((char *)(desc.u.name), &sb);
184                         if( canStatWithoutResource == 0 )
185                            path = desc.u.name;
186                         else
187 			   path += desc.u.name;
188                         // END SURGE CHANGE
189 		}
190 		if (auto s = CairoBitmapPrivate::createImageFromPath (path.data ()))
191 		{
192 			if (cairo_surface_status (s) != CAIRO_STATUS_SUCCESS)
193 			{
194 				cairo_surface_destroy (s);
195 				return false;
196 			}
197 			surface = s;
198 			size.x = cairo_image_surface_get_width (surface);
199 			size.y = cairo_image_surface_get_height (surface);
200 			return true;
201 		}
202 	}
203 	return false;
204 }
205 
206 //-----------------------------------------------------------------------------
getSize() const207 const CPoint& Bitmap::getSize () const
208 {
209 	return size;
210 }
211 //-----------------------------------------------------------------------------
lockPixels(bool alphaPremultiplied)212 SharedPointer<IPlatformBitmapPixelAccess> Bitmap::lockPixels (bool alphaPremultiplied)
213 {
214 	if (locked)
215 		return nullptr;
216 #warning TODO: alphaPremultiplied is currently ignored, always treated as true
217 	locked = true;
218 	auto pixelAccess = owned (new CairoBitmapPrivate::PixelAccess ());
219 	if (pixelAccess->init (this, surface))
220 		return pixelAccess;
221 	return nullptr;
222 }
223 
224 //-----------------------------------------------------------------------------
setScaleFactor(double factor)225 void Bitmap::setScaleFactor (double factor)
226 {
227 	scaleFactor = factor;
228 }
229 
230 //-----------------------------------------------------------------------------
getScaleFactor() const231 double Bitmap::getScaleFactor () const
232 {
233 	return scaleFactor;
234 }
235 
236 //-----------------------------------------------------------------------------
237 namespace CairoBitmapPrivate {
238 
239 //-----------------------------------------------------------------------------
init(Bitmap * inBitmap,const SurfaceHandle & inSurface)240 bool PixelAccess::init (Bitmap* inBitmap, const SurfaceHandle& inSurface)
241 {
242 	cairo_surface_flush (inSurface);
243 	address = cairo_image_surface_get_data (inSurface);
244 	if (!address)
245 	{
246 #if DEBUG
247 		auto status = cairo_surface_status (inSurface);
248 		if (status != CAIRO_STATUS_SUCCESS)
249 		{
250 			auto msg = cairo_status_to_string (status);
251 			DebugPrint ("%s\n", msg);
252 		}
253 #endif
254 		return false;
255 	}
256 	surface = inSurface;
257 	bitmap = inBitmap;
258 	bytesPerRow = cairo_image_surface_get_stride (surface);
259 	return true;
260 }
261 
262 //-----------------------------------------------------------------------------
~PixelAccess()263 PixelAccess::~PixelAccess ()
264 {
265 	cairo_surface_mark_dirty (surface);
266 	bitmap->unlock ();
267 }
268 
269 //-----------------------------------------------------------------------------
270 } // CairoBitmapPrivate
271 } // Cairo
272 
273 //-----------------------------------------------------------------------------
create(CPoint * size)274 SharedPointer<IPlatformBitmap> IPlatformBitmap::create (CPoint* size)
275 {
276 	return owned (new Cairo::Bitmap (size));
277 }
278 
279 //-----------------------------------------------------------------------------
createFromPath(UTF8StringPtr absolutePath)280 SharedPointer<IPlatformBitmap> IPlatformBitmap::createFromPath (UTF8StringPtr absolutePath)
281 {
282 	if (auto surface = Cairo::CairoBitmapPrivate::createImageFromPath (absolutePath))
283 	{
284 		if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS)
285 		{
286 			cairo_surface_destroy (surface);
287 			return nullptr;
288 		}
289 		return owned (new Cairo::Bitmap (surface));
290 	}
291 	return nullptr;
292 }
293 
294 //-----------------------------------------------------------------------------
createFromMemory(const void * ptr,uint32_t memSize)295 SharedPointer<IPlatformBitmap> IPlatformBitmap::createFromMemory (const void* ptr, uint32_t memSize)
296 {
297 	Cairo::CairoBitmapPrivate::PNGMemoryReader reader (reinterpret_cast<const uint8_t*> (ptr),
298 													   memSize);
299 	if (auto surface = reader.create ())
300 	{
301 		return owned (new Cairo::Bitmap (Cairo::SurfaceHandle {surface}));
302 	}
303 	return nullptr;
304 }
305 
306 //-----------------------------------------------------------------------------
createMemoryPNGRepresentation(const SharedPointer<IPlatformBitmap> & bitmap)307 PNGBitmapBuffer IPlatformBitmap::createMemoryPNGRepresentation (const SharedPointer<IPlatformBitmap>& bitmap)
308 {
309 	if (auto cairoBitmap = bitmap.cast<Cairo::Bitmap> ())
310 	{
311 		Cairo::CairoBitmapPrivate::PNGMemoryWriter writer;
312 		return writer.create (cairoBitmap->getSurface ());
313 	}
314 	return {};
315 }
316 
317 //-----------------------------------------------------------------------------
318 } // VSTGUI
319