1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk/bitmap.cpp
3 // Purpose:
4 // Author:      Robert Roebling
5 // Copyright:   (c) 1998 Robert Roebling
6 // Licence:     wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
8 
9 // For compilers that support precompilation, includes "wx.h".
10 #include "wx/wxprec.h"
11 
12 #include "wx/bitmap.h"
13 
14 #ifndef WX_PRECOMP
15     #include "wx/icon.h"
16     #include "wx/image.h"
17     #include "wx/colour.h"
18     #include "wx/cursor.h"
19 #endif
20 
21 #include "wx/rawbmp.h"
22 
23 #include "wx/gtk/private/object.h"
24 #include "wx/gtk/private.h"
25 
26 GdkWindow* wxGetTopLevelGDK();
27 
28 #ifndef __WXGTK3__
PixmapToPixbuf(GdkPixmap * pixmap,GdkPixbuf * pixbuf,int w,int h)29 static void PixmapToPixbuf(GdkPixmap* pixmap, GdkPixbuf* pixbuf, int w, int h)
30 {
31     gdk_pixbuf_get_from_drawable(pixbuf, pixmap, NULL, 0, 0, 0, 0, w, h);
32     if (gdk_drawable_get_depth(pixmap) == 1)
33     {
34         // invert to match XBM convention
35         guchar* p = gdk_pixbuf_get_pixels(pixbuf);
36         const int inc = 3 + int(gdk_pixbuf_get_has_alpha(pixbuf) != 0);
37         const int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - w * inc;
38         for (int y = h; y; y--, p += rowpad)
39             for (int x = w; x; x--, p += inc)
40             {
41                 // pixels are either (0,0,0) or (0xff,0xff,0xff)
42                 p[0] = ~p[0];
43                 p[1] = ~p[1];
44                 p[2] = ~p[2];
45             }
46     }
47 }
48 
MaskToAlpha(GdkPixmap * mask,GdkPixbuf * pixbuf,int w,int h)49 static void MaskToAlpha(GdkPixmap* mask, GdkPixbuf* pixbuf, int w, int h)
50 {
51     GdkPixbuf* mask_pixbuf = gdk_pixbuf_get_from_drawable(
52         NULL, mask, NULL, 0, 0, 0, 0, w, h);
53     guchar* p = gdk_pixbuf_get_pixels(pixbuf) + 3;
54     const guchar* mask_data = gdk_pixbuf_get_pixels(mask_pixbuf);
55     const int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - w * 4;
56     const int mask_rowpad = gdk_pixbuf_get_rowstride(mask_pixbuf) - w * 3;
57     for (int y = h; y; y--, p += rowpad, mask_data += mask_rowpad)
58     {
59         for (int x = w; x; x--, p += 4, mask_data += 3)
60         {
61             // no need to test all 3 components,
62             //   pixels are either (0,0,0) or (0xff,0xff,0xff)
63             if (mask_data[0] == 0)
64                 *p = 0;
65         }
66     }
67     g_object_unref(mask_pixbuf);
68 }
69 #endif
70 
71 //-----------------------------------------------------------------------------
72 // wxMask
73 //-----------------------------------------------------------------------------
74 
75 wxIMPLEMENT_DYNAMIC_CLASS(wxMask, wxMaskBase);
76 
wxMask()77 wxMask::wxMask()
78 {
79     m_bitmap = NULL;
80 }
81 
wxMask(const wxMask & mask)82 wxMask::wxMask(const wxMask& mask)
83 {
84 #ifdef __WXGTK3__
85     m_bitmap = NULL;
86     if (mask.m_bitmap)
87     {
88         const int w = cairo_image_surface_get_width(mask.m_bitmap);
89         const int h = cairo_image_surface_get_height(mask.m_bitmap);
90         m_bitmap = cairo_image_surface_create(CAIRO_FORMAT_A8, w, h);
91         const guchar* src = cairo_image_surface_get_data(mask.m_bitmap);
92         guchar* dst = cairo_image_surface_get_data(m_bitmap);
93         const int stride = cairo_image_surface_get_stride(m_bitmap);
94         wxASSERT(stride == cairo_image_surface_get_stride(mask.m_bitmap));
95         memcpy(dst, src, stride * h);
96         cairo_surface_mark_dirty(m_bitmap);
97     }
98 #else
99     if ( !mask.m_bitmap )
100     {
101         m_bitmap = NULL;
102         return;
103     }
104 
105     // create a copy of an existing mask
106     gint w, h;
107     gdk_drawable_get_size(mask.m_bitmap, &w, &h);
108     m_bitmap = gdk_pixmap_new(mask.m_bitmap, w, h, 1);
109 
110     wxGtkObject<GdkGC> gc(gdk_gc_new(m_bitmap));
111     gdk_draw_drawable(m_bitmap, gc, mask.m_bitmap, 0, 0, 0, 0, -1, -1);
112 #endif
113 }
114 
wxMask(const wxBitmap & bitmap,const wxColour & colour)115 wxMask::wxMask( const wxBitmap& bitmap, const wxColour& colour )
116 {
117     m_bitmap = NULL;
118     InitFromColour(bitmap, colour);
119 }
120 
121 #if wxUSE_PALETTE
wxMask(const wxBitmap & bitmap,int paletteIndex)122 wxMask::wxMask( const wxBitmap& bitmap, int paletteIndex )
123 {
124     m_bitmap = NULL;
125     Create( bitmap, paletteIndex );
126 }
127 #endif // wxUSE_PALETTE
128 
wxMask(const wxBitmap & bitmap)129 wxMask::wxMask( const wxBitmap& bitmap )
130 {
131     m_bitmap = NULL;
132     InitFromMonoBitmap(bitmap);
133 }
134 
135 #ifdef __WXGTK3__
wxMask(cairo_surface_t * bitmap)136 wxMask::wxMask(cairo_surface_t* bitmap)
137 #else
138 wxMask::wxMask(GdkPixmap* bitmap)
139 #endif
140 {
141     m_bitmap = bitmap;
142 }
143 
~wxMask()144 wxMask::~wxMask()
145 {
146     if (m_bitmap)
147     {
148 #ifdef __WXGTK3__
149         cairo_surface_destroy(m_bitmap);
150 #else
151         g_object_unref (m_bitmap);
152 #endif
153     }
154 }
155 
FreeData()156 void wxMask::FreeData()
157 {
158     if (m_bitmap)
159     {
160 #ifdef __WXGTK3__
161         cairo_surface_destroy(m_bitmap);
162 #else
163         g_object_unref (m_bitmap);
164 #endif
165         m_bitmap = NULL;
166     }
167 }
168 
InitFromColour(const wxBitmap & bitmap,const wxColour & colour)169 bool wxMask::InitFromColour(const wxBitmap& bitmap, const wxColour& colour)
170 {
171     const int w = bitmap.GetWidth();
172     const int h = bitmap.GetHeight();
173 
174 #ifdef __WXGTK3__
175     m_bitmap = cairo_image_surface_create(CAIRO_FORMAT_A8, w, h);
176     GdkPixbuf* pixbuf = bitmap.GetPixbufNoMask();
177     const guchar* src = gdk_pixbuf_get_pixels(pixbuf);
178     guchar* dst = cairo_image_surface_get_data(m_bitmap);
179     const int stride_src = gdk_pixbuf_get_rowstride(pixbuf);
180     const int stride_dst = cairo_image_surface_get_stride(m_bitmap);
181     const int src_inc = gdk_pixbuf_get_n_channels(pixbuf);
182     const guchar r = colour.Red();
183     const guchar g = colour.Green();
184     const guchar b = colour.Blue();
185     for (int j = 0; j < h; j++, src += stride_src, dst += stride_dst)
186     {
187         const guchar* s = src;
188         for (int i = 0; i < w; i++, s += src_inc)
189         {
190             dst[i] = 0xff;
191             if (s[0] == r && s[1] == g && s[2] == b)
192                 dst[i] = 0;
193         }
194     }
195     cairo_surface_mark_dirty(m_bitmap);
196 #else
197     // create mask as XBM format bitmap
198 
199     // one bit per pixel, each row starts on a byte boundary
200     const size_t out_size = size_t((w + 7) / 8) * unsigned(h);
201     wxByte* out = new wxByte[out_size];
202     // set bits are unmasked
203     memset(out, 0xff, out_size);
204     unsigned bit_index = 0;
205     if (bitmap.HasPixbuf())
206     {
207         const wxByte r_mask = colour.Red();
208         const wxByte g_mask = colour.Green();
209         const wxByte b_mask = colour.Blue();
210         GdkPixbuf* pixbuf = bitmap.GetPixbufNoMask();
211         const wxByte* in = gdk_pixbuf_get_pixels(pixbuf);
212         const int inc = 3 + int(gdk_pixbuf_get_has_alpha(pixbuf) != 0);
213         const int rowpadding = gdk_pixbuf_get_rowstride(pixbuf) - inc * w;
214         for (int y = 0; y < h; y++, in += rowpadding)
215         {
216             for (int x = 0; x < w; x++, in += inc, bit_index++)
217                 if (in[0] == r_mask && in[1] == g_mask && in[2] == b_mask)
218                     out[bit_index >> 3] ^= 1 << (bit_index & 7);
219             // move index to next byte boundary
220             bit_index = (bit_index + 7) & ~7u;
221         }
222     }
223     else
224     {
225         GdkImage* image = gdk_drawable_get_image(bitmap.GetPixmap(), 0, 0, w, h);
226         GdkColormap* colormap = gdk_image_get_colormap(image);
227         guint32 mask_pixel;
228         if (colormap == NULL)
229             // mono bitmap, white is pixel value 0
230             mask_pixel = guint32(colour.Red() != 255 || colour.Green() != 255 || colour.Blue() != 255);
231         else
232         {
233             wxColor c(colour);
234             c.CalcPixel(colormap);
235             mask_pixel = c.GetPixel();
236         }
237         for (int y = 0; y < h; y++)
238         {
239             for (int x = 0; x < w; x++, bit_index++)
240                 if (gdk_image_get_pixel(image, x, y) == mask_pixel)
241                     out[bit_index >> 3] ^= 1 << (bit_index & 7);
242             bit_index = (bit_index + 7) & ~7u;
243         }
244         g_object_unref(image);
245     }
246     m_bitmap = gdk_bitmap_create_from_data(wxGetTopLevelGDK(), (char*)out, w, h);
247     delete[] out;
248 #endif
249     return true;
250 }
251 
InitFromMonoBitmap(const wxBitmap & bitmap)252 bool wxMask::InitFromMonoBitmap(const wxBitmap& bitmap)
253 {
254     if (!bitmap.IsOk()) return false;
255 
256     wxCHECK_MSG( bitmap.GetDepth() == 1, false, wxT("Cannot create mask from colour bitmap") );
257 
258 #ifdef __WXGTK3__
259     InitFromColour(bitmap, *wxBLACK);
260 #else
261     m_bitmap = gdk_pixmap_new(wxGetTopLevelGDK(), bitmap.GetWidth(), bitmap.GetHeight(), 1);
262 
263     if (!m_bitmap) return false;
264 
265     wxGtkObject<GdkGC> gc(gdk_gc_new( m_bitmap ));
266     gdk_gc_set_function(gc, GDK_COPY_INVERT);
267     gdk_draw_drawable(m_bitmap, gc, bitmap.GetPixmap(), 0, 0, 0, 0, bitmap.GetWidth(), bitmap.GetHeight());
268 #endif
269 
270     return true;
271 }
272 
GetBitmap() const273 wxBitmap wxMask::GetBitmap() const
274 {
275     wxBitmap bitmap;
276     if (m_bitmap)
277     {
278 #ifdef __WXGTK3__
279         cairo_surface_t* mask = m_bitmap;
280         const int w = cairo_image_surface_get_width(mask);
281         const int h = cairo_image_surface_get_height(mask);
282         GdkPixbuf* pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, false, 8, w, h);
283         const guchar* src = cairo_image_surface_get_data(mask);
284         guchar* dst = gdk_pixbuf_get_pixels(pixbuf);
285         const int stride_src = cairo_image_surface_get_stride(mask);
286         const int stride_dst = gdk_pixbuf_get_rowstride(pixbuf);
287         for (int j = 0; j < h; j++, src += stride_src, dst += stride_dst)
288         {
289             guchar* d = dst;
290             for (int i = 0; i < w; i++, d += 3)
291             {
292                 d[0] = src[i];
293                 d[1] = src[i];
294                 d[2] = src[i];
295             }
296         }
297         bitmap = wxBitmap(pixbuf, 1);
298 #else
299         GdkPixmap* mask = m_bitmap;
300         int w, h;
301         gdk_drawable_get_size(mask, &w, &h);
302         GdkPixmap* pixmap = gdk_pixmap_new(mask, w, h, -1);
303         GdkGC* gc = gdk_gc_new(pixmap);
304         gdk_gc_set_function(gc, GDK_COPY_INVERT);
305         gdk_draw_drawable(pixmap, gc, mask, 0, 0, 0, 0, w, h);
306         g_object_unref(gc);
307         bitmap = wxBitmap(pixmap);
308 #endif
309     }
310     return bitmap;
311 }
312 
313 #ifdef __WXGTK3__
operator cairo_surface_t*() const314 wxMask::operator cairo_surface_t*() const
315 #else
316 wxMask::operator GdkPixmap*() const
317 #endif
318 {
319     return m_bitmap;
320 }
321 
322 //-----------------------------------------------------------------------------
323 // wxBitmapRefData
324 //-----------------------------------------------------------------------------
325 
326 class wxBitmapRefData: public wxGDIRefData
327 {
328 public:
329     wxBitmapRefData(int width, int height, int depth);
330     virtual ~wxBitmapRefData();
331 
332     virtual bool IsOk() const wxOVERRIDE;
333 
334 #ifdef __WXGTK3__
335     GdkPixbuf* m_pixbufNoMask;
336     cairo_surface_t* m_surface;
337     double m_scaleFactor;
338 #else
339     GdkPixmap      *m_pixmap;
340     GdkPixbuf      *m_pixbuf;
341 #endif
342     GdkPixbuf      *m_pixbufMask;
343     wxMask         *m_mask;
344     int             m_width;
345     int             m_height;
346     int             m_bpp;
347 #ifndef __WXGTK3__
348     bool m_alphaRequested;
349 #endif
350 
351     // We don't provide a copy ctor as copying m_pixmap and m_pixbuf properly
352     // is expensive and we don't want to do it implicitly (and possibly
353     // accidentally). wxBitmap::CloneGDIRefData() which does need to do it does
354     // it explicitly itself.
355     wxDECLARE_NO_COPY_CLASS(wxBitmapRefData);
356 };
357 
wxBitmapRefData(int width,int height,int depth)358 wxBitmapRefData::wxBitmapRefData(int width, int height, int depth)
359 {
360 #ifdef __WXGTK3__
361     m_pixbufNoMask = NULL;
362     m_surface = NULL;
363     m_scaleFactor = 1;
364 #else
365     m_pixmap = NULL;
366     m_pixbuf = NULL;
367 #endif
368     m_pixbufMask = NULL;
369     m_mask = NULL;
370     m_width = width;
371     m_height = height;
372     m_bpp = depth;
373 #ifdef __WXGTK3__
374     if (m_bpp != 1 && m_bpp != 32)
375         m_bpp = 24;
376 #else
377     if (m_bpp < 0)
378         m_bpp = gdk_drawable_get_depth(wxGetTopLevelGDK());
379     m_alphaRequested = depth == 32;
380 #endif
381 }
382 
~wxBitmapRefData()383 wxBitmapRefData::~wxBitmapRefData()
384 {
385 #ifdef __WXGTK3__
386     if (m_pixbufNoMask)
387         g_object_unref(m_pixbufNoMask);
388     if (m_surface)
389         cairo_surface_destroy(m_surface);
390 #else
391     if (m_pixbufMask)
392         g_object_unref(m_pixbufMask);
393     if (m_pixmap)
394         g_object_unref (m_pixmap);
395     if (m_pixbuf)
396         g_object_unref (m_pixbuf);
397 #endif
398     delete m_mask;
399 }
400 
IsOk() const401 bool wxBitmapRefData::IsOk() const
402 {
403     return m_bpp != 0;
404 }
405 
406 //-----------------------------------------------------------------------------
407 // wxBitmap
408 //-----------------------------------------------------------------------------
409 
410 #define M_BMPDATA static_cast<wxBitmapRefData*>(m_refData)
411 
412 wxIMPLEMENT_DYNAMIC_CLASS(wxBitmap,wxGDIObject);
413 
wxBitmap(const wxString & filename,wxBitmapType type)414 wxBitmap::wxBitmap(const wxString &filename, wxBitmapType type)
415 {
416     LoadFile(filename, type);
417 }
418 
wxBitmap(const char bits[],int width,int height,int depth)419 wxBitmap::wxBitmap(const char bits[], int width, int height, int depth)
420 {
421     wxASSERT(depth == 1);
422     if (width > 0 && height > 0 && depth == 1)
423     {
424         m_refData = new wxBitmapRefData(width, height, 1);
425 #ifdef __WXGTK3__
426         GdkPixbuf* pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, false, 8, width, height);
427         M_BMPDATA->m_pixbufNoMask = pixbuf;
428         const char* src = bits;
429         guchar* dst = gdk_pixbuf_get_pixels(pixbuf);
430         const int stride_src = (width + 7) / 8;
431         const int stride_dst = gdk_pixbuf_get_rowstride(pixbuf);
432         for (int j = 0; j < height; j++, src += stride_src, dst += stride_dst)
433         {
434             guchar* d = dst;
435             for (int i = 0; i < width; i++)
436             {
437                 guchar c = 0xff;
438                 if (src[i >> 3] & (1 << (i & 7)))
439                     c = 0;
440                 *d++ = c;
441                 *d++ = c;
442                 *d++ = c;
443             }
444         }
445 #else
446         M_BMPDATA->m_pixmap = gdk_bitmap_create_from_data(
447             wxGetTopLevelGDK(), bits, width, height);
448 #endif
449     }
450 }
451 
wxBitmap(const char * const * bits)452 wxBitmap::wxBitmap(const char* const* bits)
453 {
454     wxCHECK2_MSG(bits != NULL, return, wxT("invalid bitmap data"));
455 
456 #if wxUSE_IMAGE
457     *this = wxBitmap(wxImage(bits));
458 #elif defined __WXGTK3__
459     GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(const_cast<const char**>(bits));
460     if (pixbuf)
461     {
462         m_refData = new wxBitmapRefData(
463             gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf),
464             gdk_pixbuf_get_n_channels(pixbuf) * 8);
465         M_BMPDATA->m_pixbufNoMask = pixbuf;
466         wxASSERT(M_BMPDATA->m_bpp == 32 || !gdk_pixbuf_get_has_alpha(M_BMPDATA->m_pixbufNoMask));
467     }
468 #else
469     GdkBitmap* mask = NULL;
470     GdkPixmap* pixmap = gdk_pixmap_create_from_xpm_d(
471         wxGetTopLevelGDK(), &mask, NULL, const_cast<char**>(bits));
472     if (pixmap)
473     {
474         int width, height;
475         gdk_drawable_get_size(pixmap, &width, &height);
476         m_refData = new wxBitmapRefData(width, height, -1);
477         M_BMPDATA->m_pixmap = pixmap;
478         if (mask)
479         {
480             M_BMPDATA->m_mask = new wxMask(mask);
481         }
482     }
483 #endif
484 }
485 
wxBitmap(GdkPixbuf * pixbuf,int depth)486 wxBitmap::wxBitmap(GdkPixbuf* pixbuf, int depth)
487 {
488     if (pixbuf)
489     {
490         if (depth != 1)
491             depth = gdk_pixbuf_get_n_channels(pixbuf) * 8;
492         wxBitmapRefData* bmpData = new wxBitmapRefData(
493             gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf),
494             depth);
495         m_refData = bmpData;
496 #ifdef __WXGTK3__
497         bmpData->m_pixbufNoMask = pixbuf;
498 #else
499         bmpData->m_pixbuf = pixbuf;
500 #endif
501     }
502 }
503 
504 #ifndef __WXGTK3__
wxBitmap(GdkPixmap * pixmap)505 wxBitmap::wxBitmap(GdkPixmap* pixmap)
506 {
507     if (pixmap)
508     {
509         int w, h;
510         gdk_drawable_get_size(pixmap, &w, &h);
511         wxBitmapRefData* bmpData =
512             new wxBitmapRefData(w, h, gdk_drawable_get_depth(pixmap));
513         m_refData = bmpData;
514         bmpData->m_pixmap = pixmap;
515     }
516 }
517 #endif
518 
wxBitmap(const wxCursor & cursor)519 wxBitmap::wxBitmap(const wxCursor& cursor)
520 {
521 #if GTK_CHECK_VERSION(2,8,0)
522     if (wx_is_at_least_gtk2(8))
523     {
524         GdkPixbuf *pixbuf = gdk_cursor_get_image(cursor.GetCursor());
525         *this = wxBitmap(pixbuf);
526     }
527 #else
528     wxUnusedVar(cursor);
529 #endif
530 }
531 
~wxBitmap()532 wxBitmap::~wxBitmap()
533 {
534 }
535 
Create(int width,int height,int depth)536 bool wxBitmap::Create( int width, int height, int depth )
537 {
538     UnRef();
539     wxCHECK_MSG(width > 0 && height > 0, false, "invalid bitmap size");
540     m_refData = new wxBitmapRefData(width, height, depth);
541     return true;
542 }
543 
CopyImageData(guchar * dst,int dstChannels,int dstStride,const guchar * src,int srcChannels,int srcStride,int w,int h)544 static void CopyImageData(
545     guchar* dst, int dstChannels, int dstStride,
546     const guchar* src, int srcChannels, int srcStride,
547     int w, int h)
548 {
549     if (dstChannels == srcChannels)
550     {
551         if (dstStride == srcStride)
552             memcpy(dst, src, size_t(dstStride) * h);
553         else
554         {
555             const int stride = dstStride < srcStride ? dstStride : srcStride;
556             for (int j = 0; j < h; j++, src += srcStride, dst += dstStride)
557                 memcpy(dst, src, stride);
558         }
559     }
560     else
561     {
562         for (int j = 0; j < h; j++, src += srcStride, dst += dstStride)
563         {
564             guchar* d = dst;
565             const guchar* s = src;
566             if (dstChannels == 4)
567             {
568                 for (int i = 0; i < w; i++, d += 4, s += 3)
569                 {
570                     d[0] = s[0];
571                     d[1] = s[1];
572                     d[2] = s[2];
573                     d[3] = 0xff;
574                 }
575             }
576             else
577             {
578                 for (int i = 0; i < w; i++, d += 3, s += 4)
579                 {
580                     d[0] = s[0];
581                     d[1] = s[1];
582                     d[2] = s[2];
583                 }
584             }
585         }
586     }
587 }
588 
589 #if wxUSE_IMAGE
590 #ifdef __WXGTK3__
wxBitmap(const wxImage & image,int depth,double scale)591 wxBitmap::wxBitmap(const wxImage& image, int depth, double scale)
592 {
593     wxCHECK_RET(image.IsOk(), "invalid image");
594 
595     const int w = image.GetWidth();
596     const int h = image.GetHeight();
597     const guchar* alpha = image.GetAlpha();
598     if (depth < 0)
599         depth = alpha ? 32 : 24;
600     else if (depth != 1 && depth != 32)
601         depth = 24;
602     wxBitmapRefData* bmpData = new wxBitmapRefData(w, h, depth);
603     bmpData->m_scaleFactor = scale;
604     m_refData = bmpData;
605     GdkPixbuf* pixbuf_dst = gdk_pixbuf_new(GDK_COLORSPACE_RGB, depth == 32, 8, w, h);
606     bmpData->m_pixbufNoMask = pixbuf_dst;
607     wxASSERT(bmpData->m_bpp == 32 || !gdk_pixbuf_get_has_alpha(bmpData->m_pixbufNoMask));
608     const guchar* src = image.GetData();
609 
610     guchar* dst = gdk_pixbuf_get_pixels(pixbuf_dst);
611     const int dstStride = gdk_pixbuf_get_rowstride(pixbuf_dst);
612     CopyImageData(dst, gdk_pixbuf_get_n_channels(pixbuf_dst), dstStride, src, 3, 3 * w, w, h);
613 
614     if (depth == 32 && alpha)
615     {
616         for (int j = 0; j < h; j++, dst += dstStride)
617             for (int i = 0; i < w; i++)
618                 dst[i * 4 + 3] = *alpha++;
619     }
620     if (image.HasMask())
621     {
622         const guchar r = image.GetMaskRed();
623         const guchar g = image.GetMaskGreen();
624         const guchar b = image.GetMaskBlue();
625         cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_A8, w, h);
626         const int stride = cairo_image_surface_get_stride(surface);
627         dst = cairo_image_surface_get_data(surface);
628         memset(dst, 0xff, stride * h);
629         for (int j = 0; j < h; j++, dst += stride)
630             for (int i = 0; i < w; i++, src += 3)
631                 if (src[0] == r && src[1] == g && src[2] == b)
632                     dst[i] = 0;
633         cairo_surface_mark_dirty(surface);
634         bmpData->m_mask = new wxMask(surface);
635     }
636 }
637 #else
wxBitmap(const wxImage & image,int depth,double WXUNUSED (scale))638 wxBitmap::wxBitmap(const wxImage& image, int depth, double WXUNUSED(scale))
639 {
640     wxCHECK_RET(image.IsOk(), "invalid image");
641 
642     if (depth == 32 || (depth == -1 && image.HasAlpha()))
643         CreateFromImageAsPixbuf(image);
644     else
645         // otherwise create pixmap, if alpha is present it will be converted to mask
646         CreateFromImageAsPixmap(image, depth);
647 }
648 
CreateFromImageAsPixmap(const wxImage & image,int depth)649 bool wxBitmap::CreateFromImageAsPixmap(const wxImage& image, int depth)
650 {
651     const int w = image.GetWidth();
652     const int h = image.GetHeight();
653     if (depth == 1)
654     {
655         // create XBM format bitmap
656 
657         // one bit per pixel, each row starts on a byte boundary
658         const size_t out_size = size_t((w + 7) / 8) * unsigned(h);
659         wxByte* out = new wxByte[out_size];
660         // set bits are black
661         memset(out, 0xff, out_size);
662         const wxByte* in = image.GetData();
663         unsigned bit_index = 0;
664         for (int y = 0; y < h; y++)
665         {
666             for (int x = 0; x < w; x++, in += 3, bit_index++)
667                 if (in[0] == 255 && in[1] == 255 && in[2] == 255)
668                     out[bit_index >> 3] ^= 1 << (bit_index & 7);
669             // move index to next byte boundary
670             bit_index = (bit_index + 7) & ~7u;
671         }
672         SetPixmap(gdk_bitmap_create_from_data(wxGetTopLevelGDK(), (char*)out, w, h));
673         delete[] out;
674 
675         if (!M_BMPDATA)     // SetPixmap may have failed
676             return false;
677     }
678     else
679     {
680         SetPixmap(gdk_pixmap_new(wxGetTopLevelGDK(), w, h, depth));
681         if (!M_BMPDATA)
682             return false;
683 
684         wxGtkObject<GdkGC> gc(gdk_gc_new(M_BMPDATA->m_pixmap));
685         gdk_draw_rgb_image(
686             M_BMPDATA->m_pixmap, gc,
687             0, 0, w, h,
688             GDK_RGB_DITHER_NONE, image.GetData(), w * 3);
689     }
690 
691     const wxByte* alpha = image.GetAlpha();
692     if (alpha != NULL || image.HasMask())
693     {
694         // create mask as XBM format bitmap
695 
696         const size_t out_size = size_t((w + 7) / 8) * unsigned(h);
697         wxByte* out = new wxByte[out_size];
698         memset(out, 0xff, out_size);
699         unsigned bit_index = 0;
700         if (alpha != NULL)
701         {
702             for (int y = 0; y < h; y++)
703             {
704                 for (int x = 0; x < w; x++, bit_index++)
705                     if (*alpha++ < wxIMAGE_ALPHA_THRESHOLD)
706                         out[bit_index >> 3] ^= 1 << (bit_index & 7);
707                 bit_index = (bit_index + 7) & ~7u;
708             }
709         }
710         else
711         {
712             const wxByte r_mask = image.GetMaskRed();
713             const wxByte g_mask = image.GetMaskGreen();
714             const wxByte b_mask = image.GetMaskBlue();
715             const wxByte* in = image.GetData();
716             for (int y = 0; y < h; y++)
717             {
718                 for (int x = 0; x < w; x++, in += 3, bit_index++)
719                     if (in[0] == r_mask && in[1] == g_mask && in[2] == b_mask)
720                         out[bit_index >> 3] ^= 1 << (bit_index & 7);
721                 bit_index = (bit_index + 7) & ~7u;
722             }
723         }
724         SetMask(new wxMask(gdk_bitmap_create_from_data(M_BMPDATA->m_pixmap, (char*)out, w, h)));
725         delete[] out;
726     }
727     return IsOk();
728 }
729 
CreateFromImageAsPixbuf(const wxImage & image)730 bool wxBitmap::CreateFromImageAsPixbuf(const wxImage& image)
731 {
732     int width = image.GetWidth();
733     int height = image.GetHeight();
734 
735     Create(width, height, 32);
736     GdkPixbuf* pixbuf = GetPixbuf();
737     if (!pixbuf)
738         return false;
739 
740     // Copy the data:
741     const unsigned char* in = image.GetData();
742     unsigned char *out = gdk_pixbuf_get_pixels(pixbuf);
743     unsigned char *alpha = image.GetAlpha();
744 
745     int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - 4 * width;
746 
747     for (int y = 0; y < height; y++, out += rowpad)
748     {
749         for (int x = 0; x < width; x++, out += 4, in += 3)
750         {
751             out[0] = in[0];
752             out[1] = in[1];
753             out[2] = in[2];
754             if (alpha)
755                 out[3] = *alpha++;
756         }
757     }
758 
759     if ( image.HasMask() )
760     {
761         const size_t out_size = size_t((width + 7) / 8) * unsigned(height);
762         wxByte* out = new wxByte[out_size];
763         memset(out, 0xff, out_size);
764         const wxByte r_mask = image.GetMaskRed();
765         const wxByte g_mask = image.GetMaskGreen();
766         const wxByte b_mask = image.GetMaskBlue();
767         const wxByte* in = image.GetData();
768         unsigned bit_index = 0;
769         for (int y = 0; y < height; y++)
770         {
771             for (int x = 0; x < width; x++, in += 3, bit_index++)
772                 if (in[0] == r_mask && in[1] == g_mask && in[2] == b_mask)
773                     out[bit_index >> 3] ^= 1 << (bit_index & 7);
774             bit_index = (bit_index + 7) & ~7u;
775         }
776         SetMask(new wxMask(gdk_bitmap_create_from_data(wxGetTopLevelGDK(), reinterpret_cast<char*>(out), width, height)));
777         delete[] out;
778     }
779 
780     return true;
781 }
782 #endif
783 
ConvertToImage() const784 wxImage wxBitmap::ConvertToImage() const
785 {
786 #ifdef __WXGTK3__
787     wxImage image;
788     wxCHECK_MSG(IsOk(), image, "invalid bitmap");
789     wxBitmapRefData* bmpData = M_BMPDATA;
790     const int w = bmpData->m_width;
791     const int h = bmpData->m_height;
792     image.Create(w, h, false);
793     guchar* dst = image.GetData();
794     GdkPixbuf* pixbuf_src = NULL;
795     if (bmpData->m_pixbufNoMask)
796         pixbuf_src = bmpData->m_pixbufNoMask;
797     else if (bmpData->m_surface)
798     {
799         pixbuf_src = gdk_pixbuf_get_from_surface(bmpData->m_surface, 0, 0, w, h);
800         bmpData->m_pixbufNoMask = pixbuf_src;
801         wxASSERT(bmpData->m_bpp == 32 || !gdk_pixbuf_get_has_alpha(bmpData->m_pixbufNoMask));
802     }
803     if (pixbuf_src)
804     {
805         const guchar* src = gdk_pixbuf_get_pixels(pixbuf_src);
806         const int srcStride = gdk_pixbuf_get_rowstride(pixbuf_src);
807         const int srcChannels = gdk_pixbuf_get_n_channels(pixbuf_src);
808         CopyImageData(dst, 3, 3 * w, src, srcChannels, srcStride, w, h);
809 
810         if (srcChannels == 4)
811         {
812             image.SetAlpha();
813             guchar* alpha = image.GetAlpha();
814             for (int j = 0; j < h; j++, src += srcStride)
815             {
816                 const guchar* s = src;
817                 for (int i = 0; i < w; i++, s += 4)
818                     *alpha++ = s[3];
819             }
820         }
821     }
822     cairo_surface_t* maskSurf = NULL;
823     if (bmpData->m_mask)
824         maskSurf = *bmpData->m_mask;
825     if (maskSurf)
826     {
827         const guchar r = 1;
828         const guchar g = 2;
829         const guchar b = 3;
830         image.SetMaskColour(r, g, b);
831         wxASSERT(cairo_image_surface_get_format(maskSurf) == CAIRO_FORMAT_A8);
832         const int stride = cairo_image_surface_get_stride(maskSurf);
833         const guchar* src = cairo_image_surface_get_data(maskSurf);
834         for (int j = 0; j < h; j++, src += stride)
835         {
836             for (int i = 0; i < w; i++, dst += 3)
837                 if (src[i] == 0)
838                 {
839                     dst[0] = r;
840                     dst[1] = g;
841                     dst[2] = b;
842                 }
843                 else if (dst[0] == r && dst[1] == g && dst[2] == b)
844                     dst[2]--;
845         }
846     }
847 #else
848     wxCHECK_MSG( IsOk(), wxNullImage, wxT("invalid bitmap") );
849 
850     const int w = GetWidth();
851     const int h = GetHeight();
852     wxImage image(w, h, false);
853     unsigned char *data = image.GetData();
854 
855     wxCHECK_MSG(data != NULL, wxNullImage, wxT("couldn't create image") );
856 
857     // prefer pixbuf if available, it will preserve alpha and should be quicker
858     if (HasPixbuf())
859     {
860         GdkPixbuf *pixbuf = GetPixbufNoMask();
861         unsigned char* alpha = NULL;
862         if (gdk_pixbuf_get_has_alpha(pixbuf))
863         {
864             image.SetAlpha();
865             alpha = image.GetAlpha();
866         }
867         const unsigned char* in = gdk_pixbuf_get_pixels(pixbuf);
868         unsigned char *out = data;
869         const int inc = 3 + int(alpha != NULL);
870         const int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - inc * w;
871 
872         for (int y = 0; y < h; y++, in += rowpad)
873         {
874             for (int x = 0; x < w; x++, in += inc, out += 3)
875             {
876                 out[0] = in[0];
877                 out[1] = in[1];
878                 out[2] = in[2];
879                 if (alpha != NULL)
880                     *alpha++ = in[3];
881             }
882         }
883     }
884     else
885     {
886         GdkPixmap* pixmap = GetPixmap();
887         GdkPixmap* pixmap_invert = NULL;
888         if (GetDepth() == 1)
889         {
890             // mono bitmaps are inverted, i.e. 0 is white
891             pixmap_invert = gdk_pixmap_new(pixmap, w, h, 1);
892             wxGtkObject<GdkGC> gc(gdk_gc_new(pixmap_invert));
893             gdk_gc_set_function(gc, GDK_COPY_INVERT);
894             gdk_draw_drawable(pixmap_invert, gc, pixmap, 0, 0, 0, 0, w, h);
895             pixmap = pixmap_invert;
896         }
897         // create a pixbuf which shares data with the wxImage
898         GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data(
899             data, GDK_COLORSPACE_RGB, false, 8, w, h, 3 * w, NULL, NULL);
900 
901         gdk_pixbuf_get_from_drawable(pixbuf, pixmap, NULL, 0, 0, 0, 0, w, h);
902 
903         g_object_unref(pixbuf);
904         if (pixmap_invert != NULL)
905             g_object_unref(pixmap_invert);
906     }
907     // convert mask, even there is already alpha. Image can have both.
908     if ( GetMask() )
909     {
910         // we hard code the mask colour for now but we could also make an
911         // effort (and waste time) to choose a colour not present in the
912         // image already to avoid having to fudge the pixels below --
913         // whether it's worth to do it is unclear however
914         const int MASK_RED = 1;
915         const int MASK_GREEN = 2;
916         const int MASK_BLUE = 3;
917         const int MASK_BLUE_REPLACEMENT = 2;
918 
919         image.SetMaskColour(MASK_RED, MASK_GREEN, MASK_BLUE);
920         GdkImage* image_mask = gdk_drawable_get_image(*GetMask(), 0, 0, w, h);
921 
922         for (int y = 0; y < h; y++)
923         {
924             for (int x = 0; x < w; x++, data += 3)
925             {
926                 if (gdk_image_get_pixel(image_mask, x, y) == 0)
927                 {
928                     data[0] = MASK_RED;
929                     data[1] = MASK_GREEN;
930                     data[2] = MASK_BLUE;
931                 }
932                 else if (data[0] == MASK_RED && data[1] == MASK_GREEN && data[2] == MASK_BLUE)
933                 {
934                     // we have to fudge the colour a bit to prevent
935                     // this pixel from appearing transparent
936                     data[2] = MASK_BLUE_REPLACEMENT;
937                 }
938             }
939         }
940         g_object_unref(image_mask);
941     }
942 #endif
943 
944     return image;
945 }
946 
947 #endif // wxUSE_IMAGE
948 
GetHeight() const949 int wxBitmap::GetHeight() const
950 {
951     wxCHECK_MSG( IsOk(), -1, wxT("invalid bitmap") );
952 
953     return M_BMPDATA->m_height;
954 }
955 
GetWidth() const956 int wxBitmap::GetWidth() const
957 {
958     wxCHECK_MSG( IsOk(), -1, wxT("invalid bitmap") );
959 
960     return M_BMPDATA->m_width;
961 }
962 
GetDepth() const963 int wxBitmap::GetDepth() const
964 {
965     wxCHECK_MSG( IsOk(), -1, wxT("invalid bitmap") );
966 
967     return M_BMPDATA->m_bpp;
968 }
969 
GetMask() const970 wxMask *wxBitmap::GetMask() const
971 {
972     wxCHECK_MSG( IsOk(), NULL, wxT("invalid bitmap") );
973 
974     return M_BMPDATA->m_mask;
975 }
976 
SetMask(wxMask * mask)977 void wxBitmap::SetMask( wxMask *mask )
978 {
979     wxCHECK_RET( IsOk(), wxT("invalid bitmap") );
980 
981     AllocExclusive();
982     delete M_BMPDATA->m_mask;
983     M_BMPDATA->m_mask = mask;
984     if (M_BMPDATA->m_pixbufMask)
985     {
986         g_object_unref(M_BMPDATA->m_pixbufMask);
987         M_BMPDATA->m_pixbufMask = NULL;
988     }
989 }
990 
CopyFromIcon(const wxIcon & icon)991 bool wxBitmap::CopyFromIcon(const wxIcon& icon)
992 {
993     *this = icon;
994     return IsOk();
995 }
996 
997 #ifdef __WXGTK3__
CreateScaled(int w,int h,int depth,double scale)998 bool wxBitmap::CreateScaled(int w, int h, int depth, double scale)
999 {
1000     Create(int(w * scale), int(h * scale), depth);
1001     M_BMPDATA->m_scaleFactor = scale;
1002     return true;
1003 }
1004 
GetScaleFactor() const1005 double wxBitmap::GetScaleFactor() const
1006 {
1007     wxCHECK_MSG(m_refData, -1, "invalid bitmap");
1008 
1009     return M_BMPDATA->m_scaleFactor;
1010 }
1011 
GetSubSurface(cairo_surface_t * surface,const wxRect & rect)1012 static cairo_surface_t* GetSubSurface(cairo_surface_t* surface, const wxRect& rect)
1013 {
1014     cairo_surface_flush(surface);
1015     const cairo_format_t format = cairo_image_surface_get_format(surface);
1016     int x = rect.x;
1017     if (format != CAIRO_FORMAT_A8)
1018         x *= 4;
1019     cairo_surface_t* subSurface = cairo_image_surface_create(format, rect.width, rect.height);
1020     const int srcStride = cairo_image_surface_get_stride(surface);
1021     const int dstStride = cairo_image_surface_get_stride(subSurface);
1022     const guchar* src = cairo_image_surface_get_data(surface) + rect.y * srcStride + x;
1023     guchar* dst = cairo_image_surface_get_data(subSurface);
1024     for (int j = 0; j < rect.height; j++, src += srcStride, dst += dstStride)
1025         memcpy(dst, src, dstStride);
1026     cairo_surface_mark_dirty(subSurface);
1027     return subSurface;
1028 }
1029 #endif
1030 
GetSubBitmap(const wxRect & rect) const1031 wxBitmap wxBitmap::GetSubBitmap( const wxRect& rect) const
1032 {
1033     wxBitmap ret;
1034 
1035     wxCHECK_MSG(IsOk(), ret, wxT("invalid bitmap"));
1036 
1037     const int w = rect.width;
1038     const int h = rect.height;
1039     const wxBitmapRefData* bmpData = M_BMPDATA;
1040 
1041     wxCHECK_MSG(rect.x >= 0 && rect.y >= 0 &&
1042                 rect.x + w <= bmpData->m_width &&
1043                 rect.y + h <= bmpData->m_height,
1044                 ret, wxT("invalid bitmap region"));
1045 
1046     wxBitmapRefData * const newRef = new wxBitmapRefData(w, h, bmpData->m_bpp);
1047     ret.m_refData = newRef;
1048 
1049 #ifdef __WXGTK3__
1050     newRef->m_scaleFactor = bmpData->m_scaleFactor;
1051     if (bmpData->m_pixbufNoMask)
1052     {
1053         GdkPixbuf* pixbuf = gdk_pixbuf_new_subpixbuf(bmpData->m_pixbufNoMask, rect.x, rect.y, w, h);
1054         newRef->m_pixbufNoMask = gdk_pixbuf_copy(pixbuf);
1055         wxASSERT(newRef->m_bpp == 32 || !gdk_pixbuf_get_has_alpha(newRef->m_pixbufNoMask));
1056         g_object_unref(pixbuf);
1057     }
1058     else if (bmpData->m_surface)
1059         newRef->m_surface = GetSubSurface(bmpData->m_surface, rect);
1060 
1061     cairo_surface_t* maskSurf = NULL;
1062     if (bmpData->m_mask)
1063         maskSurf = *bmpData->m_mask;
1064     if (maskSurf)
1065     {
1066         newRef->m_mask = new wxMask(GetSubSurface(maskSurf, rect));
1067     }
1068 #else
1069     if (bmpData->m_pixbuf)
1070     {
1071         GdkPixbuf* pixbuf =
1072             gdk_pixbuf_new_subpixbuf(bmpData->m_pixbuf, rect.x, rect.y, w, h);
1073         newRef->m_pixbuf = gdk_pixbuf_copy(pixbuf);
1074         g_object_unref(pixbuf);
1075     }
1076     if (bmpData->m_pixmap)
1077     {
1078         newRef->m_pixmap = gdk_pixmap_new(bmpData->m_pixmap, w, h, -1);
1079         GdkGC* gc = gdk_gc_new(newRef->m_pixmap);
1080         gdk_draw_drawable(
1081             newRef->m_pixmap, gc, bmpData->m_pixmap, rect.x, rect.y, 0, 0, w, h);
1082         g_object_unref(gc);
1083     }
1084     GdkPixmap* mask = NULL;
1085     if (bmpData->m_mask)
1086         mask = *bmpData->m_mask;
1087     if (mask)
1088     {
1089         GdkPixmap* sub_mask = gdk_pixmap_new(mask, w, h, 1);
1090         newRef->m_mask = new wxMask(sub_mask);
1091         GdkGC* gc = gdk_gc_new(sub_mask);
1092         gdk_draw_drawable(
1093             sub_mask, gc, mask, rect.x, rect.y, 0, 0, w, h);
1094         g_object_unref(gc);
1095     }
1096 #endif
1097 
1098     return ret;
1099 }
1100 
SaveFile(const wxString & name,wxBitmapType type,const wxPalette * WXUNUSED (palette)) const1101 bool wxBitmap::SaveFile( const wxString &name, wxBitmapType type, const wxPalette *WXUNUSED(palette) ) const
1102 {
1103     wxCHECK_MSG( IsOk(), false, wxT("invalid bitmap") );
1104 
1105     const char* type_name = NULL;
1106     switch (type)
1107     {
1108         case wxBITMAP_TYPE_ANI:  type_name = "ani";  break;
1109         case wxBITMAP_TYPE_BMP:  type_name = "bmp";  break;
1110         case wxBITMAP_TYPE_GIF:  type_name = "gif";  break;
1111         case wxBITMAP_TYPE_ICO:  type_name = "ico";  break;
1112         case wxBITMAP_TYPE_JPEG: type_name = "jpeg"; break;
1113         case wxBITMAP_TYPE_PCX:  type_name = "pcx";  break;
1114         case wxBITMAP_TYPE_PNG:  type_name = "png";  break;
1115         case wxBITMAP_TYPE_PNM:  type_name = "pnm";  break;
1116         case wxBITMAP_TYPE_TGA:  type_name = "tga";  break;
1117         case wxBITMAP_TYPE_TIFF: type_name = "tiff"; break;
1118         case wxBITMAP_TYPE_XBM:  type_name = "xbm";  break;
1119         case wxBITMAP_TYPE_XPM:  type_name = "xpm";  break;
1120         default: break;
1121     }
1122     if (type_name &&
1123         gdk_pixbuf_save(GetPixbuf(), wxGTK_CONV_FN(name), type_name, NULL, NULL))
1124     {
1125         return true;
1126     }
1127 #if wxUSE_IMAGE
1128     return ConvertToImage().SaveFile(name, type);
1129 #else
1130     return false;
1131 #endif
1132 }
1133 
LoadFile(const wxString & name,wxBitmapType type)1134 bool wxBitmap::LoadFile( const wxString &name, wxBitmapType type )
1135 {
1136     GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file(wxGTK_CONV_FN(name), NULL);
1137     if (pixbuf)
1138     {
1139         *this = wxBitmap(pixbuf);
1140         return true;
1141     }
1142 #if wxUSE_IMAGE
1143     wxImage image;
1144     if (image.LoadFile(name, type) && image.IsOk())
1145     {
1146         *this = wxBitmap(image);
1147         return true;
1148     }
1149 #else
1150     wxUnusedVar(type);
1151 #endif
1152     return false;
1153 }
1154 
1155 #if wxUSE_PALETTE
GetPalette() const1156 wxPalette *wxBitmap::GetPalette() const
1157 {
1158     return NULL;
1159 }
1160 
SetPalette(const wxPalette & WXUNUSED (palette))1161 void wxBitmap::SetPalette(const wxPalette& WXUNUSED(palette))
1162 {
1163     // TODO
1164 }
1165 #endif // wxUSE_PALETTE
1166 
1167 #if WXWIN_COMPATIBILITY_3_0
SetHeight(int height)1168 void wxBitmap::SetHeight( int height )
1169 {
1170     AllocExclusive();
1171     M_BMPDATA->m_height = height;
1172 }
1173 
SetWidth(int width)1174 void wxBitmap::SetWidth( int width )
1175 {
1176     AllocExclusive();
1177     M_BMPDATA->m_width = width;
1178 }
1179 
SetDepth(int depth)1180 void wxBitmap::SetDepth( int depth )
1181 {
1182     AllocExclusive();
1183     M_BMPDATA->m_bpp = depth;
1184 }
1185 #endif
1186 
1187 #ifndef __WXGTK3__
SetPixmap(GdkPixmap * pixmap)1188 void wxBitmap::SetPixmap( GdkPixmap *pixmap )
1189 {
1190     UnRef();
1191 
1192     if (!pixmap)
1193         return;
1194 
1195     int w, h;
1196     gdk_drawable_get_size(pixmap, &w, &h);
1197     wxBitmapRefData* bmpData = new wxBitmapRefData(w, h, 0);
1198     m_refData = bmpData;
1199     bmpData->m_pixmap = pixmap;
1200     bmpData->m_bpp = gdk_drawable_get_depth(pixmap);
1201 }
1202 
GetPixmap() const1203 GdkPixmap *wxBitmap::GetPixmap() const
1204 {
1205     wxCHECK_MSG( IsOk(), NULL, wxT("invalid bitmap") );
1206 
1207     wxBitmapRefData* bmpData = M_BMPDATA;
1208     if (bmpData->m_pixmap)
1209         return bmpData->m_pixmap;
1210 
1211     if (bmpData->m_pixbuf)
1212     {
1213         GdkPixmap* pixmap = NULL;
1214         GdkPixmap** mask_pixmap = NULL;
1215         if (gdk_pixbuf_get_has_alpha(bmpData->m_pixbuf))
1216         {
1217             // make new mask from alpha
1218             mask_pixmap = &pixmap;
1219         }
1220         gdk_pixbuf_render_pixmap_and_mask(
1221             bmpData->m_pixbuf, &bmpData->m_pixmap, mask_pixmap, 128);
1222         if (pixmap)
1223         {
1224             delete bmpData->m_mask;
1225             bmpData->m_mask = new wxMask(pixmap);
1226         }
1227     }
1228     else
1229     {
1230         bmpData->m_pixmap = gdk_pixmap_new(wxGetTopLevelGDK(),
1231             bmpData->m_width, bmpData->m_height, bmpData->m_bpp == 1 ? 1 : -1);
1232     }
1233     return bmpData->m_pixmap;
1234 }
1235 
HasPixmap() const1236 bool wxBitmap::HasPixmap() const
1237 {
1238     wxCHECK_MSG( IsOk(), false, wxT("invalid bitmap") );
1239 
1240     return M_BMPDATA->m_pixmap != NULL;
1241 }
1242 #endif
1243 
1244 #ifdef __WXGTK3__
GetPixbufNoMask() const1245 GdkPixbuf* wxBitmap::GetPixbufNoMask() const
1246 {
1247     wxCHECK_MSG(IsOk(), NULL, "invalid bitmap");
1248 
1249     wxBitmapRefData* bmpData = M_BMPDATA;
1250     GdkPixbuf* pixbuf = bmpData->m_pixbufNoMask;
1251     if (pixbuf)
1252         return pixbuf;
1253 
1254     const int w = bmpData->m_width;
1255     const int h = bmpData->m_height;
1256     if (bmpData->m_surface)
1257         pixbuf = gdk_pixbuf_get_from_surface(bmpData->m_surface, 0, 0, w, h);
1258     else
1259         pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, bmpData->m_bpp == 32, 8, w, h);
1260     bmpData->m_pixbufNoMask = pixbuf;
1261     wxASSERT(bmpData->m_bpp == 32 || !gdk_pixbuf_get_has_alpha(bmpData->m_pixbufNoMask));
1262 
1263     return pixbuf;
1264 }
1265 
1266 // helper to set up a simulated depth 1 surface
SetSourceSurface1(const wxBitmapRefData * bmpData,cairo_t * cr,int x,int y,const wxColour * fg,const wxColour * bg)1267 static void SetSourceSurface1(const wxBitmapRefData* bmpData, cairo_t* cr, int x, int y, const wxColour* fg, const wxColour* bg)
1268 {
1269     GdkPixbuf* pixbuf = gdk_pixbuf_copy(bmpData->m_pixbufNoMask);
1270     const int w = bmpData->m_width;
1271     const int h = bmpData->m_height;
1272     const int stride = gdk_pixbuf_get_rowstride(pixbuf);
1273     const int channels = gdk_pixbuf_get_n_channels(pixbuf);
1274     guchar* dst = gdk_pixbuf_get_pixels(pixbuf);
1275     guchar fg_r = 0, fg_g = 0, fg_b = 0;
1276     if (fg && fg->IsOk())
1277     {
1278         fg_r = fg->Red();
1279         fg_g = fg->Green();
1280         fg_b = fg->Blue();
1281     }
1282     guchar bg_r = 255, bg_g = 255, bg_b = 255;
1283     if (bg && bg->IsOk())
1284     {
1285         bg_r = bg->Red();
1286         bg_g = bg->Green();
1287         bg_b = bg->Blue();
1288     }
1289     for (int j = 0; j < h; j++, dst += stride)
1290     {
1291         guchar* d = dst;
1292         for (int i = 0; i < w; i++, d += channels)
1293             if (d[0])
1294             {
1295                 d[0] = bg_r;
1296                 d[1] = bg_g;
1297                 d[2] = bg_b;
1298             }
1299             else
1300             {
1301                 d[0] = fg_r;
1302                 d[1] = fg_g;
1303                 d[2] = fg_b;
1304             }
1305     }
1306     gdk_cairo_set_source_pixbuf(cr, pixbuf, x, y);
1307     g_object_unref(pixbuf);
1308 }
1309 
SetSourceSurface(cairo_t * cr,int x,int y,const wxColour * fg,const wxColour * bg) const1310 void wxBitmap::SetSourceSurface(cairo_t* cr, int x, int y, const wxColour* fg, const wxColour* bg) const
1311 {
1312     wxBitmapRefData* bmpData = M_BMPDATA;
1313     if (bmpData->m_surface)
1314     {
1315         cairo_set_source_surface(cr, bmpData->m_surface, x, y);
1316         return;
1317     }
1318 
1319     // Silently ignore attempts to draw uninitialized bitmap,
1320     // because other platforms don't complain about it
1321     if (bmpData->m_pixbufNoMask == NULL)
1322         return;
1323 
1324     if (bmpData->m_bpp == 1)
1325         SetSourceSurface1(bmpData, cr, x, y, fg, bg);
1326     else
1327     {
1328         gdk_cairo_set_source_pixbuf(cr, bmpData->m_pixbufNoMask, x, y);
1329         cairo_pattern_get_surface(cairo_get_source(cr), &bmpData->m_surface);
1330         cairo_surface_reference(bmpData->m_surface);
1331     }
1332 }
1333 
CairoCreate() const1334 cairo_t* wxBitmap::CairoCreate() const
1335 {
1336     wxCHECK_MSG(IsOk(), NULL, "invalid bitmap");
1337 
1338     wxBitmapRefData* bmpData = M_BMPDATA;
1339     cairo_t* cr;
1340     if (bmpData->m_surface)
1341         cr = cairo_create(bmpData->m_surface);
1342     else
1343     {
1344         GdkPixbuf* pixbuf = bmpData->m_pixbufNoMask;
1345         const bool useAlpha = bmpData->m_bpp == 32 || (pixbuf && gdk_pixbuf_get_has_alpha(pixbuf));
1346         bmpData->m_surface = cairo_image_surface_create(
1347             useAlpha ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24,
1348             bmpData->m_width, bmpData->m_height);
1349         cr = cairo_create(bmpData->m_surface);
1350         if (pixbuf)
1351         {
1352             gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
1353             cairo_paint(cr);
1354             cairo_set_source_rgb(cr, 0, 0, 0);
1355         }
1356     }
1357     if (bmpData->m_pixbufNoMask)
1358     {
1359         g_object_unref(bmpData->m_pixbufNoMask);
1360         bmpData->m_pixbufNoMask = NULL;
1361     }
1362     if (bmpData->m_pixbufMask)
1363     {
1364         g_object_unref(bmpData->m_pixbufMask);
1365         bmpData->m_pixbufMask = NULL;
1366     }
1367     wxASSERT(cr && cairo_status(cr) == 0);
1368     if (!wxIsSameDouble(bmpData->m_scaleFactor, 1))
1369         cairo_scale(cr, bmpData->m_scaleFactor, bmpData->m_scaleFactor);
1370     return cr;
1371 }
1372 
Draw(cairo_t * cr,int x,int y,bool useMask,const wxColour * fg,const wxColour * bg) const1373 void wxBitmap::Draw(cairo_t* cr, int x, int y, bool useMask, const wxColour* fg, const wxColour* bg) const
1374 {
1375     wxCHECK_RET(IsOk(), "invalid bitmap");
1376 
1377     wxBitmapRefData* bmpData = M_BMPDATA;
1378     if (!wxIsSameDouble(bmpData->m_scaleFactor, 1))
1379     {
1380         cairo_translate(cr, x, y);
1381         const double scale = 1 / bmpData->m_scaleFactor;
1382         cairo_scale(cr, scale, scale);
1383         x = 0;
1384         y = 0;
1385     }
1386     SetSourceSurface(cr, x, y, fg, bg);
1387     cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
1388     cairo_surface_t* mask = NULL;
1389     if (useMask && bmpData->m_mask)
1390         mask = *bmpData->m_mask;
1391     if (mask)
1392         cairo_mask_surface(cr, mask, x, y);
1393     else
1394         cairo_paint(cr);
1395 }
1396 
CreateDisabled() const1397 wxBitmap wxBitmap::CreateDisabled() const
1398 {
1399     wxBitmap disabled;
1400     if (m_refData == NULL)
1401         return disabled;
1402 
1403     const wxBitmapRefData* bmpData = M_BMPDATA;
1404     wxBitmapRefData* newRef = new wxBitmapRefData(bmpData->m_width, bmpData->m_height, 32);
1405     newRef->m_scaleFactor = bmpData->m_scaleFactor;
1406     disabled.m_refData = newRef;
1407 
1408     cairo_t* cr = disabled.CairoCreate();
1409     cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1410     cairo_set_source_rgba(cr, 0, 0, 0, 0);
1411     // clear to transparent
1412     cairo_paint(cr);
1413     // draw in this bitmap
1414     Draw(cr, 0, 0);
1415     cairo_set_source_rgba(cr, 0, 0, 0, 0);
1416     // create disabled appearance
1417     cairo_paint_with_alpha(cr, 0.5);
1418     cairo_destroy(cr);
1419 
1420     return disabled;
1421 }
1422 #else
GetPixbufNoMask() const1423 GdkPixbuf* wxBitmap::GetPixbufNoMask() const
1424 {
1425     wxCHECK_MSG(IsOk(), NULL, "invalid bitmap");
1426 
1427     wxBitmapRefData* bmpData = M_BMPDATA;
1428     GdkPixbuf* pixbuf = bmpData->m_pixbuf;
1429     if (pixbuf)
1430         return pixbuf;
1431 
1432     const int w = bmpData->m_width;
1433     const int h = bmpData->m_height;
1434 
1435     pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, bmpData->m_alphaRequested, 8, w, h);
1436     if ( bmpData->m_pixmap )
1437         PixmapToPixbuf(bmpData->m_pixmap, pixbuf, w, h);
1438 
1439     bmpData->m_pixbuf = pixbuf;
1440     return pixbuf;
1441 }
1442 #endif
1443 
GetPixbuf() const1444 GdkPixbuf *wxBitmap::GetPixbuf() const
1445 {
1446     wxCHECK_MSG( IsOk(), NULL, wxT("invalid bitmap") );
1447 
1448     wxBitmapRefData* bmpData = M_BMPDATA;
1449 #ifdef __WXGTK3__
1450     if (bmpData->m_pixbufMask)
1451         return bmpData->m_pixbufMask;
1452 
1453     if (bmpData->m_pixbufNoMask == NULL)
1454         GetPixbufNoMask();
1455     cairo_surface_t* mask = NULL;
1456     if (bmpData->m_mask)
1457         mask = *bmpData->m_mask;
1458     if (mask == NULL)
1459         return bmpData->m_pixbufNoMask;
1460 
1461     const int w = bmpData->m_width;
1462     const int h = bmpData->m_height;
1463     bmpData->m_pixbufMask = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, w, h);
1464 
1465     guchar* dst = gdk_pixbuf_get_pixels(bmpData->m_pixbufMask);
1466     const int dstStride = gdk_pixbuf_get_rowstride(bmpData->m_pixbufMask);
1467     CopyImageData(dst, 4, dstStride,
1468         gdk_pixbuf_get_pixels(bmpData->m_pixbufNoMask),
1469         gdk_pixbuf_get_n_channels(bmpData->m_pixbufNoMask),
1470         gdk_pixbuf_get_rowstride(bmpData->m_pixbufNoMask),
1471         w, h);
1472 
1473     const guchar* src = cairo_image_surface_get_data(mask);
1474     const int srcStride = cairo_image_surface_get_stride(mask);
1475     for (int j = 0; j < h; j++, src += srcStride, dst += dstStride)
1476         for (int i = 0; i < w; i++)
1477             if (src[i] == 0)
1478                 dst[i * 4 + 3] = 0;
1479 
1480     return bmpData->m_pixbufMask;
1481 #else
1482     if ( bmpData->m_pixbufMask )
1483         return bmpData->m_pixbufMask;
1484 
1485     const int w = bmpData->m_width;
1486     const int h = bmpData->m_height;
1487     if ( !bmpData->m_pixbuf )
1488         GetPixbufNoMask();
1489 
1490     GdkPixmap* mask = NULL;
1491     if (bmpData->m_mask)
1492         mask = *bmpData->m_mask;
1493 
1494     if ( !mask )
1495         return bmpData->m_pixbuf;
1496 
1497     bmpData->m_pixbufMask = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, w, h);
1498     guchar* dst = gdk_pixbuf_get_pixels(bmpData->m_pixbufMask);
1499     guchar* src = gdk_pixbuf_get_pixels(bmpData->m_pixbuf);
1500     const int dstStride = gdk_pixbuf_get_rowstride(bmpData->m_pixbufMask);
1501     const int srcStride = gdk_pixbuf_get_rowstride(bmpData->m_pixbuf);
1502     CopyImageData(dst, 4, dstStride,
1503                   src, gdk_pixbuf_get_n_channels(bmpData->m_pixbuf), srcStride,
1504                   w, h);
1505     MaskToAlpha(mask, bmpData->m_pixbufMask, w, h);
1506     return bmpData->m_pixbufMask;
1507 #endif
1508 }
1509 
1510 #ifndef __WXGTK3__
HasPixbuf() const1511 bool wxBitmap::HasPixbuf() const
1512 {
1513     wxCHECK_MSG( IsOk(), false, wxT("invalid bitmap") );
1514 
1515     return M_BMPDATA->m_pixbuf != NULL;
1516 }
1517 
PurgeOtherRepresentations(wxBitmap::Representation keep)1518 void wxBitmap::PurgeOtherRepresentations(wxBitmap::Representation keep)
1519 {
1520     if (keep == Pixmap && HasPixbuf())
1521     {
1522         g_object_unref (M_BMPDATA->m_pixbuf);
1523         M_BMPDATA->m_pixbuf = NULL;
1524     }
1525     if (keep == Pixbuf && HasPixmap())
1526     {
1527         g_object_unref (M_BMPDATA->m_pixmap);
1528         M_BMPDATA->m_pixmap = NULL;
1529     }
1530 }
1531 #endif
1532 
1533 #ifdef wxHAS_RAW_BITMAP
GetRawData(wxPixelDataBase & data,int bpp)1534 void *wxBitmap::GetRawData(wxPixelDataBase& data, int bpp)
1535 {
1536     void* bits = NULL;
1537 #ifdef __WXGTK3__
1538     GdkPixbuf* pixbuf = GetPixbufNoMask();
1539     if ((bpp == 32) == (gdk_pixbuf_get_has_alpha(pixbuf) != 0))
1540     {
1541         bits = gdk_pixbuf_get_pixels(pixbuf);
1542         wxBitmapRefData* bmpData = M_BMPDATA;
1543         data.m_width = bmpData->m_width;
1544         data.m_height = bmpData->m_height;
1545         data.m_stride = gdk_pixbuf_get_rowstride(pixbuf);
1546         if (bmpData->m_pixbufMask)
1547         {
1548             g_object_unref(bmpData->m_pixbufMask);
1549             bmpData->m_pixbufMask = NULL;
1550         }
1551         if (bmpData->m_surface)
1552         {
1553             cairo_surface_destroy(bmpData->m_surface);
1554             bmpData->m_surface = NULL;
1555         }
1556     }
1557 #else
1558     GdkPixbuf *pixbuf = GetPixbufNoMask();
1559 
1560     // Pixmap will get out of date when our pixbuf is accessed directly, so
1561     // ensure we don't keep the old data in it.
1562     PurgeOtherRepresentations(Pixbuf);
1563     // Pixbuf with masked data will get out of date too
1564     if ( M_BMPDATA->m_pixbufMask )
1565     {
1566         g_object_unref(M_BMPDATA->m_pixbufMask);
1567         M_BMPDATA->m_pixbufMask = NULL;
1568     }
1569 
1570     const bool hasAlpha = HasAlpha();
1571 
1572     // allow access if bpp is valid and matches existence of alpha
1573     if ( pixbuf && ((bpp == 24 && !hasAlpha) || (bpp == 32 && hasAlpha)) )
1574     {
1575         data.m_height = gdk_pixbuf_get_height( pixbuf );
1576         data.m_width = gdk_pixbuf_get_width( pixbuf );
1577         data.m_stride = gdk_pixbuf_get_rowstride( pixbuf );
1578         bits = gdk_pixbuf_get_pixels(pixbuf);
1579     }
1580 #endif
1581     return bits;
1582 }
1583 
UngetRawData(wxPixelDataBase & WXUNUSED (data))1584 void wxBitmap::UngetRawData(wxPixelDataBase& WXUNUSED(data))
1585 {
1586 }
1587 #endif // wxHAS_RAW_BITMAP
1588 
HasAlpha() const1589 bool wxBitmap::HasAlpha() const
1590 {
1591     const wxBitmapRefData* bmpData = M_BMPDATA;
1592 #ifdef __WXGTK3__
1593     return bmpData && bmpData->m_bpp == 32;
1594 #else
1595     return bmpData && (bmpData->m_alphaRequested ||
1596         (bmpData->m_pixbuf && gdk_pixbuf_get_has_alpha(bmpData->m_pixbuf)));
1597 #endif
1598 }
1599 
CreateGDIRefData() const1600 wxGDIRefData* wxBitmap::CreateGDIRefData() const
1601 {
1602     return new wxBitmapRefData(0, 0, 0);
1603 }
1604 
CloneGDIRefData(const wxGDIRefData * data) const1605 wxGDIRefData* wxBitmap::CloneGDIRefData(const wxGDIRefData* data) const
1606 {
1607     const wxBitmapRefData* oldRef = static_cast<const wxBitmapRefData*>(data);
1608     wxBitmapRefData * const newRef = new wxBitmapRefData(oldRef->m_width,
1609                                                          oldRef->m_height,
1610                                                          oldRef->m_bpp);
1611 #ifdef __WXGTK3__
1612     newRef->m_scaleFactor = oldRef->m_scaleFactor;
1613     if (oldRef->m_pixbufNoMask)
1614         newRef->m_pixbufNoMask = gdk_pixbuf_copy(oldRef->m_pixbufNoMask);
1615     if (oldRef->m_surface)
1616     {
1617         const int w = oldRef->m_width;
1618         const int h = oldRef->m_height;
1619         cairo_surface_t* surface = cairo_image_surface_create(
1620             cairo_image_surface_get_format(oldRef->m_surface), w, h);
1621         newRef->m_surface = surface;
1622         cairo_surface_flush(oldRef->m_surface);
1623         const guchar* src = cairo_image_surface_get_data(oldRef->m_surface);
1624         guchar* dst = cairo_image_surface_get_data(surface);
1625         const int stride = cairo_image_surface_get_stride(surface);
1626         wxASSERT(stride == cairo_image_surface_get_stride(oldRef->m_surface));
1627         memcpy(dst, src, stride * h);
1628         cairo_surface_mark_dirty(surface);
1629     }
1630 #else
1631     if (oldRef->m_pixmap != NULL)
1632     {
1633         newRef->m_pixmap = gdk_pixmap_new(
1634             oldRef->m_pixmap, oldRef->m_width, oldRef->m_height,
1635             // use pixmap depth, m_bpp may not match
1636             gdk_drawable_get_depth(oldRef->m_pixmap));
1637         wxGtkObject<GdkGC> gc(gdk_gc_new(newRef->m_pixmap));
1638         gdk_draw_drawable(
1639             newRef->m_pixmap, gc, oldRef->m_pixmap, 0, 0, 0, 0, -1, -1);
1640     }
1641     if (oldRef->m_pixbuf != NULL)
1642     {
1643         newRef->m_pixbuf = gdk_pixbuf_copy(oldRef->m_pixbuf);
1644     }
1645 #endif
1646     if (oldRef->m_mask != NULL)
1647     {
1648         newRef->m_mask = new wxMask(*oldRef->m_mask);
1649     }
1650 
1651     return newRef;
1652 }
1653 
InitStandardHandlers()1654 /* static */ void wxBitmap::InitStandardHandlers()
1655 {
1656     // TODO: Insert handler based on GdkPixbufs handler later
1657 }
1658