support for GTK3
[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 cairo_surface_t* wxMask::GetBitmap() const
278 #else
279 GdkPixmap* wxMask::GetBitmap() 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)
446 {
447 if (pixbuf)
448 {
449 wxBitmapRefData* bmpData = new wxBitmapRefData(
450 gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf),
451 gdk_pixbuf_get_n_channels(pixbuf) * 8);
452 m_refData = bmpData;
453 #ifdef __WXGTK3__
454 bmpData->m_pixbufNoMask = pixbuf;
455 #else
456 bmpData->m_pixbuf = pixbuf;
457 #endif
458 }
459 }
460
461 wxBitmap::~wxBitmap()
462 {
463 }
464
465 bool wxBitmap::Create( int width, int height, int depth )
466 {
467 UnRef();
468 wxCHECK_MSG(width >= 0 && height >= 0, false, "invalid bitmap size");
469 m_refData = new wxBitmapRefData(width, height, depth);
470 return true;
471 }
472
473 #ifdef __WXGTK3__
474 static void CopyImageData(
475 guchar* dst, int dstChannels, int dstStride,
476 const guchar* src, int srcChannels, int srcStride,
477 int w, int h)
478 {
479 if (dstChannels == srcChannels)
480 {
481 if (dstStride == srcStride)
482 memcpy(dst, src, size_t(dstStride) * h);
483 else
484 {
485 const int stride = dstStride < srcStride ? dstStride : srcStride;
486 for (int j = 0; j < h; j++, src += srcStride, dst += dstStride)
487 memcpy(dst, src, stride);
488 }
489 }
490 else
491 {
492 for (int j = 0; j < h; j++, src += srcStride, dst += dstStride)
493 {
494 guchar* d = dst;
495 const guchar* s = src;
496 if (dstChannels == 4)
497 {
498 for (int i = 0; i < w; i++, d += 4, s += 3)
499 {
500 d[0] = s[0];
501 d[1] = s[1];
502 d[2] = s[2];
503 d[3] = 0xff;
504 }
505 }
506 else
507 {
508 for (int i = 0; i < w; i++, d += 3, s += 4)
509 {
510 d[0] = s[0];
511 d[1] = s[1];
512 d[2] = s[2];
513 }
514 }
515 }
516 }
517 }
518 #endif
519
520 #if wxUSE_IMAGE
521 #ifdef __WXGTK3__
522 wxBitmap::wxBitmap(const wxImage& image, int depth)
523 {
524 wxCHECK_RET(image.IsOk(), "invalid image");
525
526 const int w = image.GetWidth();
527 const int h = image.GetHeight();
528 const guchar* alpha = image.GetAlpha();
529 if (depth < 0)
530 depth = alpha ? 32 : 24;
531 else if (depth != 1 && depth != 32)
532 depth = 24;
533 wxBitmapRefData* bmpData = new wxBitmapRefData(w, h, depth);
534 m_refData = bmpData;
535 GdkPixbuf* pixbuf_dst = gdk_pixbuf_new(GDK_COLORSPACE_RGB, depth == 32, 8, w, h);
536 bmpData->m_pixbufNoMask = pixbuf_dst;
537 wxASSERT(bmpData->m_bpp == 32 || !gdk_pixbuf_get_has_alpha(bmpData->m_pixbufNoMask));
538 const guchar* src = image.GetData();
539
540 guchar* dst = gdk_pixbuf_get_pixels(pixbuf_dst);
541 const int dstStride = gdk_pixbuf_get_rowstride(pixbuf_dst);
542 CopyImageData(dst, gdk_pixbuf_get_n_channels(pixbuf_dst), dstStride, src, 3, 3 * w, w, h);
543
544 if (depth == 32 && alpha)
545 {
546 for (int j = 0; j < h; j++, dst += dstStride)
547 for (int i = 0; i < w; i++)
548 dst[i * 4 + 3] = *alpha++;
549 }
550 if (image.HasMask())
551 {
552 const guchar r = image.GetMaskRed();
553 const guchar g = image.GetMaskGreen();
554 const guchar b = image.GetMaskBlue();
555 cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_A8, w, h);
556 const int stride = cairo_image_surface_get_stride(surface);
557 dst = cairo_image_surface_get_data(surface);
558 memset(dst, 0xff, stride * h);
559 for (int j = 0; j < h; j++, dst += stride)
560 for (int i = 0; i < w; i++, src += 3)
561 if (src[0] == r && src[1] == g && src[2] == b)
562 dst[i] = 0;
563 cairo_surface_mark_dirty(surface);
564 bmpData->m_mask = new wxMask(surface);
565 }
566 }
567 #else
568 wxBitmap::wxBitmap(const wxImage& image, int depth)
569 {
570 wxCHECK_RET(image.IsOk(), "invalid image");
571
572 if (depth == 32 || (depth == -1 && image.HasAlpha()))
573 CreateFromImageAsPixbuf(image);
574 else
575 // otherwise create pixmap, if alpha is present it will be converted to mask
576 CreateFromImageAsPixmap(image, depth);
577 }
578
579 bool wxBitmap::CreateFromImageAsPixmap(const wxImage& image, int depth)
580 {
581 const int w = image.GetWidth();
582 const int h = image.GetHeight();
583 if (depth == 1)
584 {
585 // create XBM format bitmap
586
587 // one bit per pixel, each row starts on a byte boundary
588 const size_t out_size = size_t((w + 7) / 8) * unsigned(h);
589 wxByte* out = new wxByte[out_size];
590 // set bits are black
591 memset(out, 0xff, out_size);
592 const wxByte* in = image.GetData();
593 unsigned bit_index = 0;
594 for (int y = 0; y < h; y++)
595 {
596 for (int x = 0; x < w; x++, in += 3, bit_index++)
597 if (in[0] == 255 && in[1] == 255 && in[2] == 255)
598 out[bit_index >> 3] ^= 1 << (bit_index & 7);
599 // move index to next byte boundary
600 bit_index = (bit_index + 7) & ~7u;
601 }
602 SetPixmap(gdk_bitmap_create_from_data(wxGetRootWindow()->window, (char*)out, w, h));
603 delete[] out;
604
605 if (!M_BMPDATA) // SetPixmap may have failed
606 return false;
607 }
608 else
609 {
610 SetPixmap(gdk_pixmap_new(wxGetRootWindow()->window, w, h, depth));
611 if (!M_BMPDATA)
612 return false;
613
614 wxGtkObject<GdkGC> gc(gdk_gc_new(M_BMPDATA->m_pixmap));
615 gdk_draw_rgb_image(
616 M_BMPDATA->m_pixmap, gc,
617 0, 0, w, h,
618 GDK_RGB_DITHER_NONE, image.GetData(), w * 3);
619 }
620
621 const wxByte* alpha = image.GetAlpha();
622 if (alpha != NULL || image.HasMask())
623 {
624 // create mask as XBM format bitmap
625
626 const size_t out_size = size_t((w + 7) / 8) * unsigned(h);
627 wxByte* out = new wxByte[out_size];
628 memset(out, 0xff, out_size);
629 unsigned bit_index = 0;
630 if (alpha != NULL)
631 {
632 for (int y = 0; y < h; y++)
633 {
634 for (int x = 0; x < w; x++, bit_index++)
635 if (*alpha++ < wxIMAGE_ALPHA_THRESHOLD)
636 out[bit_index >> 3] ^= 1 << (bit_index & 7);
637 bit_index = (bit_index + 7) & ~7u;
638 }
639 }
640 else
641 {
642 const wxByte r_mask = image.GetMaskRed();
643 const wxByte g_mask = image.GetMaskGreen();
644 const wxByte b_mask = image.GetMaskBlue();
645 const wxByte* in = image.GetData();
646 for (int y = 0; y < h; y++)
647 {
648 for (int x = 0; x < w; x++, in += 3, bit_index++)
649 if (in[0] == r_mask && in[1] == g_mask && in[2] == b_mask)
650 out[bit_index >> 3] ^= 1 << (bit_index & 7);
651 bit_index = (bit_index + 7) & ~7u;
652 }
653 }
654 SetMask(new wxMask(gdk_bitmap_create_from_data(M_BMPDATA->m_pixmap, (char*)out, w, h)));
655 delete[] out;
656 }
657 return IsOk();
658 }
659
660 bool wxBitmap::CreateFromImageAsPixbuf(const wxImage& image)
661 {
662 int width = image.GetWidth();
663 int height = image.GetHeight();
664
665 Create(width, height, 32);
666 GdkPixbuf* pixbuf = GetPixbuf();
667 if (!pixbuf)
668 return false;
669
670 // Copy the data:
671 const unsigned char* in = image.GetData();
672 unsigned char *out = gdk_pixbuf_get_pixels(pixbuf);
673 unsigned char *alpha = image.GetAlpha();
674
675 int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - 4 * width;
676
677 for (int y = 0; y < height; y++, out += rowpad)
678 {
679 for (int x = 0; x < width; x++, out += 4, in += 3)
680 {
681 out[0] = in[0];
682 out[1] = in[1];
683 out[2] = in[2];
684 if (alpha)
685 out[3] = *alpha++;
686 }
687 }
688
689 return true;
690 }
691 #endif
692
693 wxImage wxBitmap::ConvertToImage() const
694 {
695 #ifdef __WXGTK3__
696 wxImage image;
697 wxCHECK_MSG(IsOk(), image, "invalid bitmap");
698 wxBitmapRefData* bmpData = M_BMPDATA;
699 const int w = bmpData->m_width;
700 const int h = bmpData->m_height;
701 image.Create(w, h, false);
702 guchar* dst = image.GetData();
703 GdkPixbuf* pixbuf_src = NULL;
704 if (bmpData->m_pixbufNoMask)
705 pixbuf_src = bmpData->m_pixbufNoMask;
706 else if (bmpData->m_surface)
707 {
708 pixbuf_src = gdk_pixbuf_get_from_surface(bmpData->m_surface, 0, 0, w, h);
709 bmpData->m_pixbufNoMask = pixbuf_src;
710 wxASSERT(bmpData->m_bpp == 32 || !gdk_pixbuf_get_has_alpha(bmpData->m_pixbufNoMask));
711 }
712 if (pixbuf_src)
713 {
714 const guchar* src = gdk_pixbuf_get_pixels(pixbuf_src);
715 const int srcStride = gdk_pixbuf_get_rowstride(pixbuf_src);
716 const int srcChannels = gdk_pixbuf_get_n_channels(pixbuf_src);
717 CopyImageData(dst, 3, 3 * w, src, srcChannels, srcStride, w, h);
718
719 if (srcChannels == 4)
720 {
721 image.SetAlpha();
722 guchar* alpha = image.GetAlpha();
723 for (int j = 0; j < h; j++, src += srcStride)
724 {
725 const guchar* s = src;
726 for (int i = 0; i < w; i++, s += 4)
727 *alpha++ = s[3];
728 }
729 }
730 }
731 cairo_surface_t* maskSurf = NULL;
732 if (bmpData->m_mask)
733 maskSurf = bmpData->m_mask->GetBitmap();
734 if (maskSurf)
735 {
736 const guchar r = 1;
737 const guchar g = 2;
738 const guchar b = 3;
739 image.SetMaskColour(r, g, b);
740 wxASSERT(cairo_image_surface_get_format(maskSurf) == CAIRO_FORMAT_A8);
741 const int stride = cairo_image_surface_get_stride(maskSurf);
742 const guchar* src = cairo_image_surface_get_data(maskSurf);
743 for (int j = 0; j < h; j++, src += stride)
744 {
745 for (int i = 0; i < w; i++, dst += 3)
746 if (src[i] == 0)
747 {
748 dst[0] = r;
749 dst[1] = g;
750 dst[2] = b;
751 }
752 else if (dst[0] == r && dst[1] == g && dst[2] == b)
753 dst[2]--;
754 }
755 }
756 #else
757 wxCHECK_MSG( IsOk(), wxNullImage, wxT("invalid bitmap") );
758
759 const int w = GetWidth();
760 const int h = GetHeight();
761 wxImage image(w, h, false);
762 unsigned char *data = image.GetData();
763
764 wxCHECK_MSG(data != NULL, wxNullImage, wxT("couldn't create image") );
765
766 // prefer pixbuf if available, it will preserve alpha and should be quicker
767 if (HasPixbuf())
768 {
769 GdkPixbuf *pixbuf = GetPixbuf();
770 unsigned char* alpha = NULL;
771 if (gdk_pixbuf_get_has_alpha(pixbuf))
772 {
773 image.SetAlpha();
774 alpha = image.GetAlpha();
775 }
776 const unsigned char* in = gdk_pixbuf_get_pixels(pixbuf);
777 unsigned char *out = data;
778 const int inc = 3 + int(alpha != NULL);
779 const int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - inc * w;
780
781 for (int y = 0; y < h; y++, in += rowpad)
782 {
783 for (int x = 0; x < w; x++, in += inc, out += 3)
784 {
785 out[0] = in[0];
786 out[1] = in[1];
787 out[2] = in[2];
788 if (alpha != NULL)
789 *alpha++ = in[3];
790 }
791 }
792 }
793 else
794 {
795 GdkPixmap* pixmap = GetPixmap();
796 GdkPixmap* pixmap_invert = NULL;
797 if (GetDepth() == 1)
798 {
799 // mono bitmaps are inverted, i.e. 0 is white
800 pixmap_invert = gdk_pixmap_new(pixmap, w, h, 1);
801 wxGtkObject<GdkGC> gc(gdk_gc_new(pixmap_invert));
802 gdk_gc_set_function(gc, GDK_COPY_INVERT);
803 gdk_draw_drawable(pixmap_invert, gc, pixmap, 0, 0, 0, 0, w, h);
804 pixmap = pixmap_invert;
805 }
806 // create a pixbuf which shares data with the wxImage
807 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data(
808 data, GDK_COLORSPACE_RGB, false, 8, w, h, 3 * w, NULL, NULL);
809
810 gdk_pixbuf_get_from_drawable(pixbuf, pixmap, NULL, 0, 0, 0, 0, w, h);
811
812 g_object_unref(pixbuf);
813 if (pixmap_invert != NULL)
814 g_object_unref(pixmap_invert);
815 }
816 // convert mask, unless there is already alpha
817 if (GetMask() && !image.HasAlpha())
818 {
819 // we hard code the mask colour for now but we could also make an
820 // effort (and waste time) to choose a colour not present in the
821 // image already to avoid having to fudge the pixels below --
822 // whether it's worth to do it is unclear however
823 const int MASK_RED = 1;
824 const int MASK_GREEN = 2;
825 const int MASK_BLUE = 3;
826 const int MASK_BLUE_REPLACEMENT = 2;
827
828 image.SetMaskColour(MASK_RED, MASK_GREEN, MASK_BLUE);
829 GdkImage* image_mask = gdk_drawable_get_image(GetMask()->GetBitmap(), 0, 0, w, h);
830
831 for (int y = 0; y < h; y++)
832 {
833 for (int x = 0; x < w; x++, data += 3)
834 {
835 if (gdk_image_get_pixel(image_mask, x, y) == 0)
836 {
837 data[0] = MASK_RED;
838 data[1] = MASK_GREEN;
839 data[2] = MASK_BLUE;
840 }
841 else if (data[0] == MASK_RED && data[1] == MASK_GREEN && data[2] == MASK_BLUE)
842 {
843 // we have to fudge the colour a bit to prevent
844 // this pixel from appearing transparent
845 data[2] = MASK_BLUE_REPLACEMENT;
846 }
847 }
848 }
849 g_object_unref(image_mask);
850 }
851 #endif
852
853 return image;
854 }
855
856 #endif // wxUSE_IMAGE
857
858 int wxBitmap::GetHeight() const
859 {
860 wxCHECK_MSG( IsOk(), -1, wxT("invalid bitmap") );
861
862 return M_BMPDATA->m_height;
863 }
864
865 int wxBitmap::GetWidth() const
866 {
867 wxCHECK_MSG( IsOk(), -1, wxT("invalid bitmap") );
868
869 return M_BMPDATA->m_width;
870 }
871
872 int wxBitmap::GetDepth() const
873 {
874 wxCHECK_MSG( IsOk(), -1, wxT("invalid bitmap") );
875
876 return M_BMPDATA->m_bpp;
877 }
878
879 wxMask *wxBitmap::GetMask() const
880 {
881 wxCHECK_MSG( IsOk(), NULL, wxT("invalid bitmap") );
882
883 return M_BMPDATA->m_mask;
884 }
885
886 void wxBitmap::SetMask( wxMask *mask )
887 {
888 wxCHECK_RET( IsOk(), wxT("invalid bitmap") );
889
890 AllocExclusive();
891 delete M_BMPDATA->m_mask;
892 M_BMPDATA->m_mask = mask;
893 }
894
895 wxBitmap wxBitmap::GetMaskBitmap() const
896 {
897 wxBitmap bitmap;
898 wxBitmapRefData* bmpData = M_BMPDATA;
899 #ifdef __WXGTK3__
900 cairo_surface_t* mask = NULL;
901 if (bmpData && bmpData->m_mask)
902 mask = bmpData->m_mask->GetBitmap();
903 if (mask)
904 {
905 const int w = cairo_image_surface_get_width(mask);
906 const int h = cairo_image_surface_get_height(mask);
907 GdkPixbuf* pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, false, 8, w, h);
908 const guchar* src = cairo_image_surface_get_data(mask);
909 guchar* dst = gdk_pixbuf_get_pixels(pixbuf);
910 const int stride_src = cairo_image_surface_get_stride(mask);
911 const int stride_dst = gdk_pixbuf_get_rowstride(pixbuf);
912 for (int j = 0; j < h; j++, src += stride_src, dst += stride_dst)
913 {
914 guchar* d = dst;
915 for (int i = 0; i < w; i++, d += 3)
916 {
917 d[0] = src[i];
918 d[1] = src[i];
919 d[2] = src[i];
920 }
921 }
922 bitmap = wxBitmap(pixbuf);
923 }
924 #else
925 GdkPixmap* mask = NULL;
926 if (bmpData && bmpData->m_mask)
927 mask = bmpData->m_mask->GetBitmap();
928 if (mask)
929 {
930 int w, h;
931 gdk_drawable_get_size(mask, &w, &h);
932 GdkPixbuf* pixbuf = gdk_pixbuf_get_from_drawable(
933 NULL, mask, NULL, 0, 0, 0, 0, w, h);
934 bitmap = wxBitmap(pixbuf);
935 }
936 #endif
937 return bitmap;
938 }
939
940 bool wxBitmap::CopyFromIcon(const wxIcon& icon)
941 {
942 *this = icon;
943 return IsOk();
944 }
945
946 #ifdef __WXGTK3__
947 static cairo_surface_t* GetSubSurface(cairo_surface_t* surface, const wxRect& rect)
948 {
949 cairo_surface_flush(surface);
950 const cairo_format_t format = cairo_image_surface_get_format(surface);
951 int x = rect.x;
952 if (format != CAIRO_FORMAT_A8)
953 x *= 4;
954 cairo_surface_t* subSurface = cairo_image_surface_create(format, rect.width, rect.height);
955 const int srcStride = cairo_image_surface_get_stride(surface);
956 const int dstStride = cairo_image_surface_get_stride(subSurface);
957 const guchar* src = cairo_image_surface_get_data(surface) + rect.y * srcStride + x;
958 guchar* dst = cairo_image_surface_get_data(subSurface);
959 for (int j = 0; j < rect.height; j++, src += srcStride, dst += dstStride)
960 memcpy(dst, src, dstStride);
961 cairo_surface_mark_dirty(subSurface);
962 return subSurface;
963 }
964 #endif
965
966 wxBitmap wxBitmap::GetSubBitmap( const wxRect& rect) const
967 {
968 wxBitmap ret;
969
970 wxCHECK_MSG(IsOk(), ret, wxT("invalid bitmap"));
971
972 const int w = rect.width;
973 const int h = rect.height;
974 const wxBitmapRefData* bmpData = M_BMPDATA;
975
976 wxCHECK_MSG(rect.x >= 0 && rect.y >= 0 &&
977 rect.x + w <= bmpData->m_width &&
978 rect.y + h <= bmpData->m_height,
979 ret, wxT("invalid bitmap region"));
980
981 wxBitmapRefData * const newRef = new wxBitmapRefData(w, h, bmpData->m_bpp);
982 ret.m_refData = newRef;
983
984 #ifdef __WXGTK3__
985 if (bmpData->m_pixbufNoMask)
986 {
987 GdkPixbuf* pixbuf = gdk_pixbuf_new_subpixbuf(bmpData->m_pixbufNoMask, rect.x, rect.y, w, h);
988 newRef->m_pixbufNoMask = gdk_pixbuf_copy(pixbuf);
989 wxASSERT(newRef->m_bpp == 32 || !gdk_pixbuf_get_has_alpha(newRef->m_pixbufNoMask));
990 g_object_unref(pixbuf);
991 }
992 else if (bmpData->m_surface)
993 newRef->m_surface = GetSubSurface(bmpData->m_surface, rect);
994
995 cairo_surface_t* maskSurf = NULL;
996 if (bmpData->m_mask)
997 maskSurf = bmpData->m_mask->GetBitmap();
998 if (maskSurf)
999 {
1000 newRef->m_mask = new wxMask(GetSubSurface(maskSurf, rect));
1001 }
1002 #else
1003 if (bmpData->m_pixbuf)
1004 {
1005 GdkPixbuf* pixbuf =
1006 gdk_pixbuf_new_subpixbuf(bmpData->m_pixbuf, rect.x, rect.y, w, h);
1007 newRef->m_pixbuf = gdk_pixbuf_copy(pixbuf);
1008 g_object_unref(pixbuf);
1009 }
1010 if (bmpData->m_pixmap)
1011 {
1012 newRef->m_pixmap = gdk_pixmap_new(bmpData->m_pixmap, w, h, -1);
1013 GdkGC* gc = gdk_gc_new(newRef->m_pixmap);
1014 gdk_draw_drawable(
1015 newRef->m_pixmap, gc, bmpData->m_pixmap, rect.x, rect.y, 0, 0, w, h);
1016 g_object_unref(gc);
1017 }
1018 GdkPixmap* mask = NULL;
1019 if (bmpData->m_mask)
1020 mask = bmpData->m_mask->GetBitmap();
1021 if (mask)
1022 {
1023 GdkPixmap* sub_mask = gdk_pixmap_new(mask, w, h, 1);
1024 newRef->m_mask = new wxMask(sub_mask);
1025 GdkGC* gc = gdk_gc_new(sub_mask);
1026 gdk_draw_drawable(
1027 sub_mask, gc, mask, rect.x, rect.y, 0, 0, w, h);
1028 g_object_unref(gc);
1029 }
1030 #endif
1031
1032 return ret;
1033 }
1034
1035 bool wxBitmap::SaveFile( const wxString &name, wxBitmapType type, const wxPalette *WXUNUSED(palette) ) const
1036 {
1037 wxCHECK_MSG( IsOk(), false, wxT("invalid bitmap") );
1038
1039 #if wxUSE_IMAGE
1040 wxImage image = ConvertToImage();
1041 if (image.IsOk() && image.SaveFile(name, type))
1042 return true;
1043 #endif
1044 const char* type_name = NULL;
1045 switch (type)
1046 {
1047 case wxBITMAP_TYPE_BMP: type_name = "bmp"; break;
1048 case wxBITMAP_TYPE_ICO: type_name = "ico"; break;
1049 case wxBITMAP_TYPE_JPEG: type_name = "jpeg"; break;
1050 case wxBITMAP_TYPE_PNG: type_name = "png"; break;
1051 default: break;
1052 }
1053 return type_name &&
1054 gdk_pixbuf_save(GetPixbuf(), wxGTK_CONV_FN(name), type_name, NULL, NULL);
1055 }
1056
1057 bool wxBitmap::LoadFile( const wxString &name, wxBitmapType type )
1058 {
1059 #if wxUSE_IMAGE
1060 wxImage image;
1061 if (image.LoadFile(name, type) && image.IsOk())
1062 *this = wxBitmap(image);
1063 else
1064 #endif
1065 {
1066 wxUnusedVar(type); // The type is detected automatically by GDK.
1067
1068 *this = wxBitmap(gdk_pixbuf_new_from_file(wxGTK_CONV_FN(name), NULL));
1069 }
1070
1071 return IsOk();
1072 }
1073
1074 #if wxUSE_PALETTE
1075 wxPalette *wxBitmap::GetPalette() const
1076 {
1077 return NULL;
1078 }
1079
1080 void wxBitmap::SetPalette(const wxPalette& WXUNUSED(palette))
1081 {
1082 // TODO
1083 }
1084 #endif // wxUSE_PALETTE
1085
1086 void wxBitmap::SetHeight( int height )
1087 {
1088 AllocExclusive();
1089 M_BMPDATA->m_height = height;
1090 }
1091
1092 void wxBitmap::SetWidth( int width )
1093 {
1094 AllocExclusive();
1095 M_BMPDATA->m_width = width;
1096 }
1097
1098 void wxBitmap::SetDepth( int depth )
1099 {
1100 AllocExclusive();
1101 M_BMPDATA->m_bpp = depth;
1102 }
1103
1104 #ifndef __WXGTK3__
1105 void wxBitmap::SetPixmap( GdkPixmap *pixmap )
1106 {
1107 UnRef();
1108
1109 if (!pixmap)
1110 return;
1111
1112 int w, h;
1113 gdk_drawable_get_size(pixmap, &w, &h);
1114 wxBitmapRefData* bmpData = new wxBitmapRefData(w, h, 0);
1115 m_refData = bmpData;
1116 bmpData->m_pixmap = pixmap;
1117 bmpData->m_bpp = gdk_drawable_get_depth(pixmap);
1118 }
1119
1120 GdkPixmap *wxBitmap::GetPixmap() const
1121 {
1122 wxCHECK_MSG( IsOk(), NULL, wxT("invalid bitmap") );
1123
1124 wxBitmapRefData* bmpData = M_BMPDATA;
1125 if (bmpData->m_pixmap)
1126 return bmpData->m_pixmap;
1127
1128 if (bmpData->m_pixbuf)
1129 {
1130 GdkPixmap* pixmap = NULL;
1131 GdkPixmap** mask_pixmap = NULL;
1132 if (gdk_pixbuf_get_has_alpha(bmpData->m_pixbuf))
1133 {
1134 // make new mask from alpha
1135 mask_pixmap = &pixmap;
1136 }
1137 gdk_pixbuf_render_pixmap_and_mask(
1138 bmpData->m_pixbuf, &bmpData->m_pixmap, mask_pixmap, 128);
1139 if (pixmap)
1140 {
1141 delete bmpData->m_mask;
1142 bmpData->m_mask = new wxMask(pixmap);
1143 }
1144 }
1145 else
1146 {
1147 bmpData->m_pixmap = gdk_pixmap_new(wxGetRootWindow()->window,
1148 bmpData->m_width, bmpData->m_height, bmpData->m_bpp == 1 ? 1 : -1);
1149 }
1150 return bmpData->m_pixmap;
1151 }
1152
1153 bool wxBitmap::HasPixmap() const
1154 {
1155 wxCHECK_MSG( IsOk(), false, wxT("invalid bitmap") );
1156
1157 return M_BMPDATA->m_pixmap != NULL;
1158 }
1159 #endif
1160
1161 #ifdef __WXGTK3__
1162 GdkPixbuf* wxBitmap::GetPixbufNoMask() const
1163 {
1164 wxCHECK_MSG(IsOk(), NULL, "invalid bitmap");
1165
1166 wxBitmapRefData* bmpData = M_BMPDATA;
1167 GdkPixbuf* pixbuf = bmpData->m_pixbufNoMask;
1168 if (pixbuf)
1169 return pixbuf;
1170
1171 const int w = bmpData->m_width;
1172 const int h = bmpData->m_height;
1173 if (bmpData->m_surface)
1174 pixbuf = gdk_pixbuf_get_from_surface(bmpData->m_surface, 0, 0, w, h);
1175 else
1176 pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, bmpData->m_bpp == 32, 8, w, h);
1177 bmpData->m_pixbufNoMask = pixbuf;
1178 wxASSERT(bmpData->m_bpp == 32 || !gdk_pixbuf_get_has_alpha(bmpData->m_pixbufNoMask));
1179
1180 return pixbuf;
1181 }
1182
1183 // helper to set up a simulated depth 1 surface
1184 static void SetSourceSurface1(const wxBitmapRefData* bmpData, cairo_t* cr, int x, int y, const wxColour* fg, const wxColour* bg)
1185 {
1186 GdkPixbuf* pixbuf = gdk_pixbuf_copy(bmpData->m_pixbufNoMask);
1187 const int w = bmpData->m_width;
1188 const int h = bmpData->m_height;
1189 const int stride = gdk_pixbuf_get_rowstride(pixbuf);
1190 const int channels = gdk_pixbuf_get_n_channels(pixbuf);
1191 guchar* dst = gdk_pixbuf_get_pixels(pixbuf);
1192 guchar fg_r = 0, fg_g = 0, fg_b = 0;
1193 if (fg && fg->IsOk())
1194 {
1195 fg_r = fg->Red();
1196 fg_g = fg->Green();
1197 fg_b = fg->Blue();
1198 }
1199 guchar bg_r = 255, bg_g = 255, bg_b = 255;
1200 if (bg && bg->IsOk())
1201 {
1202 bg_r = bg->Red();
1203 bg_g = bg->Green();
1204 bg_b = bg->Blue();
1205 }
1206 for (int j = 0; j < h; j++, dst += stride)
1207 {
1208 guchar* d = dst;
1209 for (int i = 0; i < w; i++, d += channels)
1210 if (d[0])
1211 {
1212 d[0] = bg_r;
1213 d[1] = bg_g;
1214 d[2] = bg_b;
1215 }
1216 else
1217 {
1218 d[0] = fg_r;
1219 d[1] = fg_g;
1220 d[2] = fg_b;
1221 }
1222 }
1223 gdk_cairo_set_source_pixbuf(cr, pixbuf, x, y);
1224 g_object_unref(pixbuf);
1225 }
1226
1227 void wxBitmap::SetSourceSurface(cairo_t* cr, int x, int y, const wxColour* fg, const wxColour* bg) const
1228 {
1229 wxBitmapRefData* bmpData = M_BMPDATA;
1230 if (bmpData->m_surface)
1231 {
1232 cairo_set_source_surface(cr, bmpData->m_surface, x, y);
1233 return;
1234 }
1235 wxCHECK_RET(bmpData->m_pixbufNoMask, "no bitmap data");
1236 if (bmpData->m_bpp == 1)
1237 SetSourceSurface1(bmpData, cr, x, y, fg, bg);
1238 else
1239 {
1240 gdk_cairo_set_source_pixbuf(cr, bmpData->m_pixbufNoMask, x, y);
1241 cairo_pattern_get_surface(cairo_get_source(cr), &bmpData->m_surface);
1242 cairo_surface_reference(bmpData->m_surface);
1243 }
1244 }
1245
1246 cairo_t* wxBitmap::CairoCreate() const
1247 {
1248 wxCHECK_MSG(IsOk(), NULL, "invalid bitmap");
1249
1250 wxBitmapRefData* bmpData = M_BMPDATA;
1251 cairo_t* cr;
1252 if (bmpData->m_surface)
1253 cr = cairo_create(bmpData->m_surface);
1254 else
1255 {
1256 GdkPixbuf* pixbuf = bmpData->m_pixbufNoMask;
1257 const bool useAlpha = bmpData->m_bpp == 32 || (pixbuf && gdk_pixbuf_get_has_alpha(pixbuf));
1258 bmpData->m_surface = cairo_image_surface_create(
1259 useAlpha ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24,
1260 bmpData->m_width, bmpData->m_height);
1261 cr = cairo_create(bmpData->m_surface);
1262 if (pixbuf)
1263 {
1264 gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
1265 cairo_paint(cr);
1266 cairo_set_source_rgb(cr, 0, 0, 0);
1267 }
1268 }
1269 if (bmpData->m_pixbufNoMask)
1270 {
1271 g_object_unref(bmpData->m_pixbufNoMask);
1272 bmpData->m_pixbufNoMask = NULL;
1273 }
1274 if (bmpData->m_pixbufMask)
1275 {
1276 g_object_unref(bmpData->m_pixbufMask);
1277 bmpData->m_pixbufMask = NULL;
1278 }
1279 wxASSERT(cr && cairo_status(cr) == 0);
1280 return cr;
1281 }
1282
1283 void wxBitmap::Draw(cairo_t* cr, int x, int y, bool useMask, const wxColour* fg, const wxColour* bg) const
1284 {
1285 wxCHECK_RET(IsOk(), "invalid bitmap");
1286
1287 wxBitmapRefData* bmpData = M_BMPDATA;
1288 SetSourceSurface(cr, x, y, fg, bg);
1289 cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
1290 cairo_surface_t* mask = NULL;
1291 if (useMask && bmpData->m_mask)
1292 mask = bmpData->m_mask->GetBitmap();
1293 if (mask)
1294 cairo_mask_surface(cr, mask, x, y);
1295 else
1296 cairo_paint(cr);
1297 }
1298 #endif
1299
1300 GdkPixbuf *wxBitmap::GetPixbuf() const
1301 {
1302 wxCHECK_MSG( IsOk(), NULL, wxT("invalid bitmap") );
1303
1304 wxBitmapRefData* bmpData = M_BMPDATA;
1305 #ifdef __WXGTK3__
1306 if (bmpData->m_pixbufMask)
1307 return bmpData->m_pixbufMask;
1308
1309 if (bmpData->m_pixbufNoMask == NULL)
1310 GetPixbufNoMask();
1311 cairo_surface_t* mask = NULL;
1312 if (bmpData->m_mask)
1313 mask = bmpData->m_mask->GetBitmap();
1314 if (mask == NULL)
1315 return bmpData->m_pixbufNoMask;
1316
1317 const int w = bmpData->m_width;
1318 const int h = bmpData->m_height;
1319 bmpData->m_pixbufMask = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, w, h);
1320
1321 guchar* dst = gdk_pixbuf_get_pixels(bmpData->m_pixbufMask);
1322 const int dstStride = gdk_pixbuf_get_rowstride(bmpData->m_pixbufMask);
1323 CopyImageData(dst, 4, dstStride,
1324 gdk_pixbuf_get_pixels(bmpData->m_pixbufNoMask),
1325 gdk_pixbuf_get_n_channels(bmpData->m_pixbufNoMask),
1326 gdk_pixbuf_get_rowstride(bmpData->m_pixbufNoMask),
1327 w, h);
1328
1329 const guchar* src = cairo_image_surface_get_data(mask);
1330 const int srcStride = cairo_image_surface_get_stride(mask);
1331 for (int j = 0; j < h; j++, src += srcStride, dst += dstStride)
1332 for (int i = 0; i < w; i++)
1333 if (src[i] == 0)
1334 dst[i * 4 + 3] = 0;
1335
1336 return bmpData->m_pixbufMask;
1337 #else
1338 if (bmpData->m_pixbuf)
1339 return bmpData->m_pixbuf;
1340
1341 const int w = bmpData->m_width;
1342 const int h = bmpData->m_height;
1343 GdkPixmap* mask = NULL;
1344 if (bmpData->m_mask)
1345 mask = bmpData->m_mask->GetBitmap();
1346 const bool useAlpha = bmpData->m_alphaRequested || mask;
1347 bmpData->m_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, useAlpha, 8, w, h);
1348 if (bmpData->m_pixmap)
1349 PixmapToPixbuf(bmpData->m_pixmap, bmpData->m_pixbuf, w, h);
1350 if (mask)
1351 MaskToAlpha(mask, bmpData->m_pixbuf, w, h);
1352 return bmpData->m_pixbuf;
1353 #endif
1354 }
1355
1356 #ifndef __WXGTK3__
1357 bool wxBitmap::HasPixbuf() const
1358 {
1359 wxCHECK_MSG( IsOk(), false, wxT("invalid bitmap") );
1360
1361 return M_BMPDATA->m_pixbuf != NULL;
1362 }
1363
1364 void wxBitmap::PurgeOtherRepresentations(wxBitmap::Representation keep)
1365 {
1366 if (keep == Pixmap && HasPixbuf())
1367 {
1368 g_object_unref (M_BMPDATA->m_pixbuf);
1369 M_BMPDATA->m_pixbuf = NULL;
1370 }
1371 if (keep == Pixbuf && HasPixmap())
1372 {
1373 g_object_unref (M_BMPDATA->m_pixmap);
1374 M_BMPDATA->m_pixmap = NULL;
1375 }
1376 }
1377 #endif
1378
1379 #ifdef wxHAS_RAW_BITMAP
1380 void *wxBitmap::GetRawData(wxPixelDataBase& data, int bpp)
1381 {
1382 void* bits = NULL;
1383 #ifdef __WXGTK3__
1384 GdkPixbuf* pixbuf = GetPixbufNoMask();
1385 if ((bpp == 32) == (gdk_pixbuf_get_has_alpha(pixbuf) != 0))
1386 {
1387 bits = gdk_pixbuf_get_pixels(pixbuf);
1388 wxBitmapRefData* bmpData = M_BMPDATA;
1389 data.m_width = bmpData->m_width;
1390 data.m_height = bmpData->m_height;
1391 data.m_stride = gdk_pixbuf_get_rowstride(pixbuf);
1392 if (bmpData->m_pixbufMask)
1393 {
1394 g_object_unref(bmpData->m_pixbufMask);
1395 bmpData->m_pixbufMask = NULL;
1396 }
1397 if (bmpData->m_surface)
1398 {
1399 cairo_surface_destroy(bmpData->m_surface);
1400 bmpData->m_surface = NULL;
1401 }
1402 }
1403 #else
1404 GdkPixbuf *pixbuf = GetPixbuf();
1405 const bool hasAlpha = HasAlpha();
1406
1407 // allow access if bpp is valid and matches existence of alpha
1408 if ( pixbuf && ((bpp == 24 && !hasAlpha) || (bpp == 32 && hasAlpha)) )
1409 {
1410 data.m_height = gdk_pixbuf_get_height( pixbuf );
1411 data.m_width = gdk_pixbuf_get_width( pixbuf );
1412 data.m_stride = gdk_pixbuf_get_rowstride( pixbuf );
1413 bits = gdk_pixbuf_get_pixels(pixbuf);
1414 }
1415 #endif
1416 return bits;
1417 }
1418
1419 void wxBitmap::UngetRawData(wxPixelDataBase& WXUNUSED(data))
1420 {
1421 }
1422 #endif // wxHAS_RAW_BITMAP
1423
1424 bool wxBitmap::HasAlpha() const
1425 {
1426 const wxBitmapRefData* bmpData = M_BMPDATA;
1427 #ifdef __WXGTK3__
1428 return bmpData && bmpData->m_bpp == 32;
1429 #else
1430 return bmpData && (bmpData->m_alphaRequested ||
1431 (bmpData->m_pixbuf && gdk_pixbuf_get_has_alpha(bmpData->m_pixbuf)));
1432 #endif
1433 }
1434
1435 wxGDIRefData* wxBitmap::CreateGDIRefData() const
1436 {
1437 return new wxBitmapRefData(0, 0, 0);
1438 }
1439
1440 wxGDIRefData* wxBitmap::CloneGDIRefData(const wxGDIRefData* data) const
1441 {
1442 const wxBitmapRefData* oldRef = static_cast<const wxBitmapRefData*>(data);
1443 wxBitmapRefData * const newRef = new wxBitmapRefData(oldRef->m_width,
1444 oldRef->m_height,
1445 oldRef->m_bpp);
1446 #ifdef __WXGTK3__
1447 if (oldRef->m_pixbufNoMask)
1448 newRef->m_pixbufNoMask = gdk_pixbuf_copy(oldRef->m_pixbufNoMask);
1449 if (oldRef->m_surface)
1450 {
1451 const int w = oldRef->m_width;
1452 const int h = oldRef->m_height;
1453 cairo_surface_t* surface = cairo_image_surface_create(
1454 cairo_image_surface_get_format(oldRef->m_surface), w, h);
1455 newRef->m_surface = surface;
1456 cairo_surface_flush(oldRef->m_surface);
1457 const guchar* src = cairo_image_surface_get_data(oldRef->m_surface);
1458 guchar* dst = cairo_image_surface_get_data(surface);
1459 const int stride = cairo_image_surface_get_stride(surface);
1460 wxASSERT(stride == cairo_image_surface_get_stride(oldRef->m_surface));
1461 memcpy(dst, src, stride * h);
1462 cairo_surface_mark_dirty(surface);
1463 }
1464 #else
1465 if (oldRef->m_pixmap != NULL)
1466 {
1467 newRef->m_pixmap = gdk_pixmap_new(
1468 oldRef->m_pixmap, oldRef->m_width, oldRef->m_height,
1469 // use pixmap depth, m_bpp may not match
1470 gdk_drawable_get_depth(oldRef->m_pixmap));
1471 wxGtkObject<GdkGC> gc(gdk_gc_new(newRef->m_pixmap));
1472 gdk_draw_drawable(
1473 newRef->m_pixmap, gc, oldRef->m_pixmap, 0, 0, 0, 0, -1, -1);
1474 }
1475 if (oldRef->m_pixbuf != NULL)
1476 {
1477 newRef->m_pixbuf = gdk_pixbuf_copy(oldRef->m_pixbuf);
1478 }
1479 #endif
1480 if (oldRef->m_mask != NULL)
1481 {
1482 newRef->m_mask = new wxMask(*oldRef->m_mask);
1483 }
1484
1485 return newRef;
1486 }
1487
1488 /* static */ void wxBitmap::InitStandardHandlers()
1489 {
1490 // TODO: Insert handler based on GdkPixbufs handler later
1491 }