Try native method first in LoadFile() and SaveFile()
[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 const char* type_name = NULL;
1051 switch (type)
1052 {
1053 case wxBITMAP_TYPE_BMP: type_name = "bmp"; break;
1054 case wxBITMAP_TYPE_ICO: type_name = "ico"; break;
1055 case wxBITMAP_TYPE_JPEG: type_name = "jpeg"; break;
1056 case wxBITMAP_TYPE_PNG: type_name = "png"; break;
1057 default: break;
1058 }
1059 if (type_name &&
1060 gdk_pixbuf_save(GetPixbuf(), wxGTK_CONV_FN(name), type_name, NULL, NULL))
1061 {
1062 return true;
1063 }
1064 #if wxUSE_IMAGE
1065 return ConvertToImage().SaveFile(name, type);
1066 #else
1067 return false;
1068 #endif
1069 }
1070
1071 bool wxBitmap::LoadFile( const wxString &name, wxBitmapType type )
1072 {
1073 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file(wxGTK_CONV_FN(name), NULL);
1074 if (pixbuf)
1075 {
1076 *this = wxBitmap(pixbuf);
1077 return true;
1078 }
1079 #if wxUSE_IMAGE
1080 wxImage image;
1081 if (image.LoadFile(name, type) && image.IsOk())
1082 {
1083 *this = wxBitmap(image);
1084 return true;
1085 }
1086 #else
1087 wxUnusedVar(type);
1088 #endif
1089 return false;
1090 }
1091
1092 #if wxUSE_PALETTE
1093 wxPalette *wxBitmap::GetPalette() const
1094 {
1095 return NULL;
1096 }
1097
1098 void wxBitmap::SetPalette(const wxPalette& WXUNUSED(palette))
1099 {
1100 // TODO
1101 }
1102 #endif // wxUSE_PALETTE
1103
1104 void wxBitmap::SetHeight( int height )
1105 {
1106 AllocExclusive();
1107 M_BMPDATA->m_height = height;
1108 }
1109
1110 void wxBitmap::SetWidth( int width )
1111 {
1112 AllocExclusive();
1113 M_BMPDATA->m_width = width;
1114 }
1115
1116 void wxBitmap::SetDepth( int depth )
1117 {
1118 AllocExclusive();
1119 M_BMPDATA->m_bpp = depth;
1120 }
1121
1122 #ifndef __WXGTK3__
1123 void wxBitmap::SetPixmap( GdkPixmap *pixmap )
1124 {
1125 UnRef();
1126
1127 if (!pixmap)
1128 return;
1129
1130 int w, h;
1131 gdk_drawable_get_size(pixmap, &w, &h);
1132 wxBitmapRefData* bmpData = new wxBitmapRefData(w, h, 0);
1133 m_refData = bmpData;
1134 bmpData->m_pixmap = pixmap;
1135 bmpData->m_bpp = gdk_drawable_get_depth(pixmap);
1136 }
1137
1138 GdkPixmap *wxBitmap::GetPixmap() const
1139 {
1140 wxCHECK_MSG( IsOk(), NULL, wxT("invalid bitmap") );
1141
1142 wxBitmapRefData* bmpData = M_BMPDATA;
1143 if (bmpData->m_pixmap)
1144 return bmpData->m_pixmap;
1145
1146 if (bmpData->m_pixbuf)
1147 {
1148 GdkPixmap* pixmap = NULL;
1149 GdkPixmap** mask_pixmap = NULL;
1150 if (gdk_pixbuf_get_has_alpha(bmpData->m_pixbuf))
1151 {
1152 // make new mask from alpha
1153 mask_pixmap = &pixmap;
1154 }
1155 gdk_pixbuf_render_pixmap_and_mask(
1156 bmpData->m_pixbuf, &bmpData->m_pixmap, mask_pixmap, 128);
1157 if (pixmap)
1158 {
1159 delete bmpData->m_mask;
1160 bmpData->m_mask = new wxMask(pixmap);
1161 }
1162 }
1163 else
1164 {
1165 bmpData->m_pixmap = gdk_pixmap_new(wxGetRootWindow()->window,
1166 bmpData->m_width, bmpData->m_height, bmpData->m_bpp == 1 ? 1 : -1);
1167 }
1168 return bmpData->m_pixmap;
1169 }
1170
1171 bool wxBitmap::HasPixmap() const
1172 {
1173 wxCHECK_MSG( IsOk(), false, wxT("invalid bitmap") );
1174
1175 return M_BMPDATA->m_pixmap != NULL;
1176 }
1177 #endif
1178
1179 #ifdef __WXGTK3__
1180 GdkPixbuf* wxBitmap::GetPixbufNoMask() const
1181 {
1182 wxCHECK_MSG(IsOk(), NULL, "invalid bitmap");
1183
1184 wxBitmapRefData* bmpData = M_BMPDATA;
1185 GdkPixbuf* pixbuf = bmpData->m_pixbufNoMask;
1186 if (pixbuf)
1187 return pixbuf;
1188
1189 const int w = bmpData->m_width;
1190 const int h = bmpData->m_height;
1191 if (bmpData->m_surface)
1192 pixbuf = gdk_pixbuf_get_from_surface(bmpData->m_surface, 0, 0, w, h);
1193 else
1194 pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, bmpData->m_bpp == 32, 8, w, h);
1195 bmpData->m_pixbufNoMask = pixbuf;
1196 wxASSERT(bmpData->m_bpp == 32 || !gdk_pixbuf_get_has_alpha(bmpData->m_pixbufNoMask));
1197
1198 return pixbuf;
1199 }
1200
1201 // helper to set up a simulated depth 1 surface
1202 static void SetSourceSurface1(const wxBitmapRefData* bmpData, cairo_t* cr, int x, int y, const wxColour* fg, const wxColour* bg)
1203 {
1204 GdkPixbuf* pixbuf = gdk_pixbuf_copy(bmpData->m_pixbufNoMask);
1205 const int w = bmpData->m_width;
1206 const int h = bmpData->m_height;
1207 const int stride = gdk_pixbuf_get_rowstride(pixbuf);
1208 const int channels = gdk_pixbuf_get_n_channels(pixbuf);
1209 guchar* dst = gdk_pixbuf_get_pixels(pixbuf);
1210 guchar fg_r = 0, fg_g = 0, fg_b = 0;
1211 if (fg && fg->IsOk())
1212 {
1213 fg_r = fg->Red();
1214 fg_g = fg->Green();
1215 fg_b = fg->Blue();
1216 }
1217 guchar bg_r = 255, bg_g = 255, bg_b = 255;
1218 if (bg && bg->IsOk())
1219 {
1220 bg_r = bg->Red();
1221 bg_g = bg->Green();
1222 bg_b = bg->Blue();
1223 }
1224 for (int j = 0; j < h; j++, dst += stride)
1225 {
1226 guchar* d = dst;
1227 for (int i = 0; i < w; i++, d += channels)
1228 if (d[0])
1229 {
1230 d[0] = bg_r;
1231 d[1] = bg_g;
1232 d[2] = bg_b;
1233 }
1234 else
1235 {
1236 d[0] = fg_r;
1237 d[1] = fg_g;
1238 d[2] = fg_b;
1239 }
1240 }
1241 gdk_cairo_set_source_pixbuf(cr, pixbuf, x, y);
1242 g_object_unref(pixbuf);
1243 }
1244
1245 void wxBitmap::SetSourceSurface(cairo_t* cr, int x, int y, const wxColour* fg, const wxColour* bg) const
1246 {
1247 wxBitmapRefData* bmpData = M_BMPDATA;
1248 if (bmpData->m_surface)
1249 {
1250 cairo_set_source_surface(cr, bmpData->m_surface, x, y);
1251 return;
1252 }
1253 wxCHECK_RET(bmpData->m_pixbufNoMask, "no bitmap data");
1254 if (bmpData->m_bpp == 1)
1255 SetSourceSurface1(bmpData, cr, x, y, fg, bg);
1256 else
1257 {
1258 gdk_cairo_set_source_pixbuf(cr, bmpData->m_pixbufNoMask, x, y);
1259 cairo_pattern_get_surface(cairo_get_source(cr), &bmpData->m_surface);
1260 cairo_surface_reference(bmpData->m_surface);
1261 }
1262 }
1263
1264 cairo_t* wxBitmap::CairoCreate() const
1265 {
1266 wxCHECK_MSG(IsOk(), NULL, "invalid bitmap");
1267
1268 wxBitmapRefData* bmpData = M_BMPDATA;
1269 cairo_t* cr;
1270 if (bmpData->m_surface)
1271 cr = cairo_create(bmpData->m_surface);
1272 else
1273 {
1274 GdkPixbuf* pixbuf = bmpData->m_pixbufNoMask;
1275 const bool useAlpha = bmpData->m_bpp == 32 || (pixbuf && gdk_pixbuf_get_has_alpha(pixbuf));
1276 bmpData->m_surface = cairo_image_surface_create(
1277 useAlpha ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24,
1278 bmpData->m_width, bmpData->m_height);
1279 cr = cairo_create(bmpData->m_surface);
1280 if (pixbuf)
1281 {
1282 gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
1283 cairo_paint(cr);
1284 cairo_set_source_rgb(cr, 0, 0, 0);
1285 }
1286 }
1287 if (bmpData->m_pixbufNoMask)
1288 {
1289 g_object_unref(bmpData->m_pixbufNoMask);
1290 bmpData->m_pixbufNoMask = NULL;
1291 }
1292 if (bmpData->m_pixbufMask)
1293 {
1294 g_object_unref(bmpData->m_pixbufMask);
1295 bmpData->m_pixbufMask = NULL;
1296 }
1297 wxASSERT(cr && cairo_status(cr) == 0);
1298 return cr;
1299 }
1300
1301 void wxBitmap::Draw(cairo_t* cr, int x, int y, bool useMask, const wxColour* fg, const wxColour* bg) const
1302 {
1303 wxCHECK_RET(IsOk(), "invalid bitmap");
1304
1305 wxBitmapRefData* bmpData = M_BMPDATA;
1306 SetSourceSurface(cr, x, y, fg, bg);
1307 cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
1308 cairo_surface_t* mask = NULL;
1309 if (useMask && bmpData->m_mask)
1310 mask = *bmpData->m_mask;
1311 if (mask)
1312 cairo_mask_surface(cr, mask, x, y);
1313 else
1314 cairo_paint(cr);
1315 }
1316 #endif
1317
1318 GdkPixbuf *wxBitmap::GetPixbuf() const
1319 {
1320 wxCHECK_MSG( IsOk(), NULL, wxT("invalid bitmap") );
1321
1322 wxBitmapRefData* bmpData = M_BMPDATA;
1323 #ifdef __WXGTK3__
1324 if (bmpData->m_pixbufMask)
1325 return bmpData->m_pixbufMask;
1326
1327 if (bmpData->m_pixbufNoMask == NULL)
1328 GetPixbufNoMask();
1329 cairo_surface_t* mask = NULL;
1330 if (bmpData->m_mask)
1331 mask = *bmpData->m_mask;
1332 if (mask == NULL)
1333 return bmpData->m_pixbufNoMask;
1334
1335 const int w = bmpData->m_width;
1336 const int h = bmpData->m_height;
1337 bmpData->m_pixbufMask = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, w, h);
1338
1339 guchar* dst = gdk_pixbuf_get_pixels(bmpData->m_pixbufMask);
1340 const int dstStride = gdk_pixbuf_get_rowstride(bmpData->m_pixbufMask);
1341 CopyImageData(dst, 4, dstStride,
1342 gdk_pixbuf_get_pixels(bmpData->m_pixbufNoMask),
1343 gdk_pixbuf_get_n_channels(bmpData->m_pixbufNoMask),
1344 gdk_pixbuf_get_rowstride(bmpData->m_pixbufNoMask),
1345 w, h);
1346
1347 const guchar* src = cairo_image_surface_get_data(mask);
1348 const int srcStride = cairo_image_surface_get_stride(mask);
1349 for (int j = 0; j < h; j++, src += srcStride, dst += dstStride)
1350 for (int i = 0; i < w; i++)
1351 if (src[i] == 0)
1352 dst[i * 4 + 3] = 0;
1353
1354 return bmpData->m_pixbufMask;
1355 #else
1356 if (bmpData->m_pixbuf)
1357 return bmpData->m_pixbuf;
1358
1359 const int w = bmpData->m_width;
1360 const int h = bmpData->m_height;
1361 GdkPixmap* mask = NULL;
1362 if (bmpData->m_mask)
1363 mask = *bmpData->m_mask;
1364 const bool useAlpha = bmpData->m_alphaRequested || mask;
1365 bmpData->m_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, useAlpha, 8, w, h);
1366 if (bmpData->m_pixmap)
1367 PixmapToPixbuf(bmpData->m_pixmap, bmpData->m_pixbuf, w, h);
1368 if (mask)
1369 MaskToAlpha(mask, bmpData->m_pixbuf, w, h);
1370 return bmpData->m_pixbuf;
1371 #endif
1372 }
1373
1374 #ifndef __WXGTK3__
1375 bool wxBitmap::HasPixbuf() const
1376 {
1377 wxCHECK_MSG( IsOk(), false, wxT("invalid bitmap") );
1378
1379 return M_BMPDATA->m_pixbuf != NULL;
1380 }
1381
1382 void wxBitmap::PurgeOtherRepresentations(wxBitmap::Representation keep)
1383 {
1384 if (keep == Pixmap && HasPixbuf())
1385 {
1386 g_object_unref (M_BMPDATA->m_pixbuf);
1387 M_BMPDATA->m_pixbuf = NULL;
1388 }
1389 if (keep == Pixbuf && HasPixmap())
1390 {
1391 g_object_unref (M_BMPDATA->m_pixmap);
1392 M_BMPDATA->m_pixmap = NULL;
1393 }
1394 }
1395 #endif
1396
1397 #ifdef wxHAS_RAW_BITMAP
1398 void *wxBitmap::GetRawData(wxPixelDataBase& data, int bpp)
1399 {
1400 void* bits = NULL;
1401 #ifdef __WXGTK3__
1402 GdkPixbuf* pixbuf = GetPixbufNoMask();
1403 if ((bpp == 32) == (gdk_pixbuf_get_has_alpha(pixbuf) != 0))
1404 {
1405 bits = gdk_pixbuf_get_pixels(pixbuf);
1406 wxBitmapRefData* bmpData = M_BMPDATA;
1407 data.m_width = bmpData->m_width;
1408 data.m_height = bmpData->m_height;
1409 data.m_stride = gdk_pixbuf_get_rowstride(pixbuf);
1410 if (bmpData->m_pixbufMask)
1411 {
1412 g_object_unref(bmpData->m_pixbufMask);
1413 bmpData->m_pixbufMask = NULL;
1414 }
1415 if (bmpData->m_surface)
1416 {
1417 cairo_surface_destroy(bmpData->m_surface);
1418 bmpData->m_surface = NULL;
1419 }
1420 }
1421 #else
1422 GdkPixbuf *pixbuf = GetPixbuf();
1423 const bool hasAlpha = HasAlpha();
1424
1425 // allow access if bpp is valid and matches existence of alpha
1426 if ( pixbuf && ((bpp == 24 && !hasAlpha) || (bpp == 32 && hasAlpha)) )
1427 {
1428 data.m_height = gdk_pixbuf_get_height( pixbuf );
1429 data.m_width = gdk_pixbuf_get_width( pixbuf );
1430 data.m_stride = gdk_pixbuf_get_rowstride( pixbuf );
1431 bits = gdk_pixbuf_get_pixels(pixbuf);
1432 }
1433 #endif
1434 return bits;
1435 }
1436
1437 void wxBitmap::UngetRawData(wxPixelDataBase& WXUNUSED(data))
1438 {
1439 }
1440 #endif // wxHAS_RAW_BITMAP
1441
1442 bool wxBitmap::HasAlpha() const
1443 {
1444 const wxBitmapRefData* bmpData = M_BMPDATA;
1445 #ifdef __WXGTK3__
1446 return bmpData && bmpData->m_bpp == 32;
1447 #else
1448 return bmpData && (bmpData->m_alphaRequested ||
1449 (bmpData->m_pixbuf && gdk_pixbuf_get_has_alpha(bmpData->m_pixbuf)));
1450 #endif
1451 }
1452
1453 wxGDIRefData* wxBitmap::CreateGDIRefData() const
1454 {
1455 return new wxBitmapRefData(0, 0, 0);
1456 }
1457
1458 wxGDIRefData* wxBitmap::CloneGDIRefData(const wxGDIRefData* data) const
1459 {
1460 const wxBitmapRefData* oldRef = static_cast<const wxBitmapRefData*>(data);
1461 wxBitmapRefData * const newRef = new wxBitmapRefData(oldRef->m_width,
1462 oldRef->m_height,
1463 oldRef->m_bpp);
1464 #ifdef __WXGTK3__
1465 if (oldRef->m_pixbufNoMask)
1466 newRef->m_pixbufNoMask = gdk_pixbuf_copy(oldRef->m_pixbufNoMask);
1467 if (oldRef->m_surface)
1468 {
1469 const int w = oldRef->m_width;
1470 const int h = oldRef->m_height;
1471 cairo_surface_t* surface = cairo_image_surface_create(
1472 cairo_image_surface_get_format(oldRef->m_surface), w, h);
1473 newRef->m_surface = surface;
1474 cairo_surface_flush(oldRef->m_surface);
1475 const guchar* src = cairo_image_surface_get_data(oldRef->m_surface);
1476 guchar* dst = cairo_image_surface_get_data(surface);
1477 const int stride = cairo_image_surface_get_stride(surface);
1478 wxASSERT(stride == cairo_image_surface_get_stride(oldRef->m_surface));
1479 memcpy(dst, src, stride * h);
1480 cairo_surface_mark_dirty(surface);
1481 }
1482 #else
1483 if (oldRef->m_pixmap != NULL)
1484 {
1485 newRef->m_pixmap = gdk_pixmap_new(
1486 oldRef->m_pixmap, oldRef->m_width, oldRef->m_height,
1487 // use pixmap depth, m_bpp may not match
1488 gdk_drawable_get_depth(oldRef->m_pixmap));
1489 wxGtkObject<GdkGC> gc(gdk_gc_new(newRef->m_pixmap));
1490 gdk_draw_drawable(
1491 newRef->m_pixmap, gc, oldRef->m_pixmap, 0, 0, 0, 0, -1, -1);
1492 }
1493 if (oldRef->m_pixbuf != NULL)
1494 {
1495 newRef->m_pixbuf = gdk_pixbuf_copy(oldRef->m_pixbuf);
1496 }
1497 #endif
1498 if (oldRef->m_mask != NULL)
1499 {
1500 newRef->m_mask = new wxMask(*oldRef->m_mask);
1501 }
1502
1503 return newRef;
1504 }
1505
1506 /* static */ void wxBitmap::InitStandardHandlers()
1507 {
1508 // TODO: Insert handler based on GdkPixbufs handler later
1509 }