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