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