1 //--------------------------------------------------------------------------
2 // Name:        src/bitmap_ex.h
3 // Purpose:     Helper functions and etc. for copying bitmap data to/from
4 //              buffer objects.  This file is included in etg/bitmap.py and
5 //              used in the wxBitmap wrapper.
6 //
7 // Author:      Robin Dunn
8 //
9 // Created:     27-Apr-2012
10 // Copyright:   (c) 2012-2018 by Total Control Software
11 // Licence:     wxWindows license
12 //--------------------------------------------------------------------------
13 
14 
15 #include <wx/rawbmp.h>
16 
17 // TODO: Switch these APIs to use the new wxPyBuffer class
18 
wxPyCopyBitmapFromBuffer(wxBitmap * bmp,buffer data,Py_ssize_t DATASIZE,wxBitmapBufferFormat format,int stride)19 void wxPyCopyBitmapFromBuffer(wxBitmap* bmp,
20                               buffer data, Py_ssize_t DATASIZE,
21                               wxBitmapBufferFormat format, int stride)
22 {
23     int height = bmp->GetHeight();
24     int width = bmp->GetWidth();
25 
26     switch (format) {
27         // A simple sequence of RGB bytes
28         case wxBitmapBufferFormat_RGB:
29         {
30             if (DATASIZE < width * height * 3) {
31                 wxPyErr_SetString(PyExc_ValueError, "Invalid data buffer size.");
32                 return;
33             }
34             wxNativePixelData pixData(*bmp, wxPoint(0,0), wxSize(width, height));
35             if (! pixData) {
36                 wxPyErr_SetString(PyExc_RuntimeError,
37                                   "Failed to gain raw access to bitmap data.");
38                 return;
39             }
40 
41             wxNativePixelData::Iterator p(pixData);
42             for (int y=0; y<height; y++) {
43                 wxNativePixelData::Iterator rowStart = p;
44                 for (int x=0; x<width; x++) {
45                     p.Red()   = *(data++);
46                     p.Green() = *(data++);
47                     p.Blue()  = *(data++);
48                     ++p;
49                 }
50                 p = rowStart;
51                 p.OffsetY(pixData, 1);
52             }
53             break;
54         }
55 
56         // A simple sequence of RGBA bytes
57         case wxBitmapBufferFormat_RGBA:
58         {
59             if (DATASIZE < width * height * 4) {
60                 wxPyErr_SetString(PyExc_ValueError, "Invalid data buffer size.");
61                 return;
62             }
63             wxAlphaPixelData pixData(*bmp, wxPoint(0,0), wxSize(width, height));
64             if (! pixData) {
65                 wxPyErr_SetString(PyExc_RuntimeError,
66                                   "Failed to gain raw access to bitmap data.");
67                 return;
68             }
69             wxAlphaPixelData::Iterator p(pixData);
70             for (int y=0; y<height; y++) {
71                 wxAlphaPixelData::Iterator rowStart = p;
72                 for (int x=0; x<width; x++) {
73                     byte a = data[3];
74                     p.Red()   = wxPy_premultiply(*(data++), a);
75                     p.Green() = wxPy_premultiply(*(data++), a);
76                     p.Blue()  = wxPy_premultiply(*(data++), a);
77                     p.Alpha() = a; data++;
78                     ++p;
79                 }
80                 p = rowStart;
81                 p.OffsetY(pixData, 1);
82             }
83             break;
84         }
85 
86         // A sequence of 32-bit values in native endian order,
87         // where the alpha is in the upper 8 bits, then red, then
88         // green, then blue.  The stride is the distance in bytes
89         // from the beginning of one row of the image data to the
90         // beginning of the next row.  This may not be the same as
91         // width*4 if alignment or platform specific optimizations
92         // have been utilized.
93 
94         // NOTE: This is normally used with Cairo, which seems to
95         // already have the values premultiplied.  Should we have
96         // a way to optionally do it anyway?
97 
98         case wxBitmapBufferFormat_RGB32:
99         case wxBitmapBufferFormat_ARGB32:
100         {
101             bool useAlpha = (format == wxBitmapBufferFormat_ARGB32);
102             byte* rowStart = data;
103             wxUint32* bufptr;
104             wxUint32  value;
105 
106             if (stride == -1)
107                 stride = width * 4;
108 
109             if (DATASIZE < stride * height) {
110                 wxPyErr_SetString(PyExc_ValueError, "Invalid data buffer size.");
111                 return;
112             }
113 
114             wxAlphaPixelData pixData(*bmp, wxPoint(0,0), wxSize(width,height));
115             if (! pixData) {
116                 wxPyErr_SetString(PyExc_RuntimeError,
117                                   "Failed to gain raw access to bitmap data.");
118                 return;
119             }
120 
121             wxAlphaPixelData::Iterator pix(pixData);
122             for (int y=0; y<height; y++) {
123                 pix.MoveTo(pixData, 0, y);
124                 bufptr = (wxUint32*)rowStart;
125                 for (int x=0; x<width; x++) {
126                     value = *bufptr;
127                     pix.Alpha() = useAlpha ? (value >> 24) & 0xFF : 255;
128                     pix.Red()   = (value >> 16) & 0xFF;
129                     pix.Green() = (value >>  8) & 0xFF;
130                     pix.Blue()  = (value >>  0) & 0xFF;
131                     ++pix;
132                     ++bufptr;
133                 }
134                 rowStart += stride;
135             }
136             break;
137         }
138     }
139 }
140 
141 
142 // Some helper macros used below to help declutter the code
143 #define MAKE_PIXDATA(type) \
144     type pixData(*bmp, wxPoint(0,0), wxSize(width, height)); \
145     if (! pixData) { \
146         wxPyErr_SetString(PyExc_RuntimeError, "Failed to gain raw access to bitmap data."); \
147         return; \
148     } \
149     type::Iterator p(pixData); \
150     type::Iterator rowStart
151 
152 #define CHECK_BUFFERSIZE(size_needed) \
153     if (DATASIZE < size_needed) { \
154         wxPyErr_SetString(PyExc_ValueError, "Invalid data buffer size."); \
155         return; \
156     }
157 
158 
wxPyCopyBitmapToBuffer(wxBitmap * bmp,buffer data,Py_ssize_t DATASIZE,wxBitmapBufferFormat format,int stride)159 void wxPyCopyBitmapToBuffer(wxBitmap* bmp,
160                             buffer data, Py_ssize_t DATASIZE,
161                             wxBitmapBufferFormat format, int stride)
162 {
163     int height = bmp->GetHeight();
164     int width = bmp->GetWidth();
165     int depth = bmp->GetDepth();
166 
167     // images loaded from a file may not have set the depth, at least on Mac...
168     if (depth == -1) {
169         if (bmp->HasAlpha())
170             depth = 32;
171         else
172             depth = 24;
173     }
174 
175     switch (format) {
176         // A simple sequence of RGB bytes
177         case wxBitmapBufferFormat_RGB:
178         {
179             CHECK_BUFFERSIZE(width * height * 3);
180             if (depth == 24) {
181                 MAKE_PIXDATA(wxNativePixelData);
182 
183                 for (int y=0; y<height; y++) {
184                     rowStart = p;
185                     for (int x=0; x<width; x++) {
186                         *(data++) = p.Red();
187                         *(data++) = p.Green();
188                         *(data++) = p.Blue();
189                         ++p;
190                     }
191                     p = rowStart;
192                     p.OffsetY(pixData, 1);
193                 }
194             }
195             if (depth == 32) {
196                 // Source has alpha, but we won't be using it because the
197                 // destination buffer doesn't
198                 MAKE_PIXDATA(wxAlphaPixelData);
199 
200                 for (int y=0; y<height; y++) {
201                     rowStart = p;
202                     for (int x=0; x<width; x++) {
203                         *(data++) = p.Red();
204                         *(data++) = p.Green();
205                         *(data++) = p.Blue();
206                         ++p;
207                     }
208                     p = rowStart;
209                     p.OffsetY(pixData, 1);
210                 }
211             }
212             break;
213         }
214 
215         // A simple sequence of RGBA bytes
216         case wxBitmapBufferFormat_RGBA:
217         {
218             CHECK_BUFFERSIZE(width * height * 4);
219             if (depth == 24) {
220                 MAKE_PIXDATA(wxNativePixelData);
221                 for (int y=0; y<height; y++) {
222                     rowStart = p;
223                     for (int x=0; x<width; x++) {
224                         byte a = wxALPHA_OPAQUE;
225                         *(data++) = wxPy_unpremultiply(p.Red(), a);
226                         *(data++) = wxPy_unpremultiply(p.Green(), a);
227                         *(data++) = wxPy_unpremultiply(p.Blue(), a);
228                         *(data++) = a;
229                         ++p;
230                     }
231                     p = rowStart;
232                     p.OffsetY(pixData, 1);
233                 }
234             }
235             if (depth == 32) {
236                 MAKE_PIXDATA(wxAlphaPixelData);
237                 for (int y=0; y<height; y++) {
238                     rowStart = p;
239                     for (int x=0; x<width; x++) {
240                         byte a = p.Alpha();
241                         *(data++) = wxPy_unpremultiply(p.Red(), a);
242                         *(data++) = wxPy_unpremultiply(p.Green(), a);
243                         *(data++) = wxPy_unpremultiply(p.Blue(), a);
244                         *(data++) = a;
245                         ++p;
246                     }
247                     p = rowStart;
248                     p.OffsetY(pixData, 1);
249                 }
250             }
251             break;
252         }
253 
254         // A sequence of 32-bit values in native endian order,
255         // where the alpha is in the upper 8 bits, then red, then
256         // green, then blue.  The stride is the distance in bytes
257         // from the beginning of one row of the image data to the
258         // beginning of the next row.  This may not be the same as
259         // width*4 if alignment or platform specific optimizations
260         // have been utilized.
261 
262         // NOTE: This is normally used with Cairo, which seems to
263         // already have the values premultiplied.  Should we have
264         // a way to optionally do it anyway?
265 
266         case wxBitmapBufferFormat_RGB32:
267         case wxBitmapBufferFormat_ARGB32:
268         {
269             bool useAlpha = (format == wxBitmapBufferFormat_ARGB32);
270             byte* dataRow = data;
271             wxUint32* bufptr;
272             wxUint32  value;
273 
274             if (stride == -1)
275                 stride = width * 4;
276 
277             CHECK_BUFFERSIZE(stride * height);
278 
279             if (useAlpha && depth == 32) {
280                 MAKE_PIXDATA(wxAlphaPixelData);
281                 for (int y=0; y<height; y++) {
282                     p.MoveTo(pixData, 0, y);
283                     bufptr = (wxUint32*)dataRow;
284                     for (int x=0; x<width; x++) {
285                         value =
286                             (p.Alpha() << 24) |
287                             (p.Red() << 16) |
288                             (p.Green() << 8) |
289                             (p.Blue());
290                         *bufptr = value;
291                         ++p;
292                         ++bufptr;
293                     }
294                     dataRow += stride;
295                 }
296             }
297             else // if (!useAlpha /*depth == 24*/)
298             {
299                 MAKE_PIXDATA(wxNativePixelData);
300                 for (int y=0; y<height; y++) {
301                     p.MoveTo(pixData, 0, y);
302                     bufptr = (wxUint32*)dataRow;
303                     for (int x=0; x<width; x++) {
304                         value =
305                             (wxALPHA_OPAQUE << 24) |
306                             (p.Red() << 16) |
307                             (p.Green() << 8) |
308                             (p.Blue());
309                         *bufptr = value;
310                         ++p;
311                         ++bufptr;
312                     }
313                     dataRow += stride;
314                 }
315             }
316             break;
317         }
318     }
319 }
320 
321 //--------------------------------------------------------------------------
322