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