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