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