]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/gtk/bitmap.cpp
Disable wxUSE_ENH_METAFILE for wxGTK builds.
[wxWidgets.git] / src / gtk / bitmap.cpp
... / ...
CommitLineData
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
28extern GtkWidget *wxGetRootWindow();
29
30#ifndef __WXGTK3__
31static 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
51static 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
78IMPLEMENT_DYNAMIC_CLASS(wxMask, wxMaskBase)
79
80wxMask::wxMask()
81{
82 m_bitmap = NULL;
83}
84
85wxMask::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
118wxMask::wxMask( const wxBitmap& bitmap, const wxColour& colour )
119{
120 m_bitmap = NULL;
121 InitFromColour(bitmap, colour);
122}
123
124#if wxUSE_PALETTE
125wxMask::wxMask( const wxBitmap& bitmap, int paletteIndex )
126{
127 m_bitmap = NULL;
128 Create( bitmap, paletteIndex );
129}
130#endif // wxUSE_PALETTE
131
132wxMask::wxMask( const wxBitmap& bitmap )
133{
134 m_bitmap = NULL;
135 InitFromMonoBitmap(bitmap);
136}
137
138#ifdef __WXGTK3__
139wxMask::wxMask(cairo_surface_t* bitmap)
140#else
141wxMask::wxMask(GdkPixmap* bitmap)
142#endif
143{
144 m_bitmap = bitmap;
145}
146
147wxMask::~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
159void 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
172bool 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
255bool 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
276wxBitmap 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__
317wxMask::operator cairo_surface_t*() const
318#else
319wxMask::operator GdkPixmap*() const
320#endif
321{
322 return m_bitmap;
323}
324
325//-----------------------------------------------------------------------------
326// wxBitmapRefData
327//-----------------------------------------------------------------------------
328
329class wxBitmapRefData: public wxGDIRefData
330{
331public:
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
360wxBitmapRefData::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
384wxBitmapRefData::~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
402bool 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
413IMPLEMENT_DYNAMIC_CLASS(wxBitmap,wxGDIObject)
414
415wxBitmap::wxBitmap(const wxString &filename, wxBitmapType type)
416{
417 LoadFile(filename, type);
418}
419
420wxBitmap::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
452wxBitmap::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
485wxBitmap::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__
504wxBitmap::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
518wxBitmap::~wxBitmap()
519{
520}
521
522bool 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__
531static 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__
579wxBitmap::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
625wxBitmap::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
636bool 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
717bool 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
750wxImage 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
915int wxBitmap::GetHeight() const
916{
917 wxCHECK_MSG( IsOk(), -1, wxT("invalid bitmap") );
918
919 return M_BMPDATA->m_height;
920}
921
922int wxBitmap::GetWidth() const
923{
924 wxCHECK_MSG( IsOk(), -1, wxT("invalid bitmap") );
925
926 return M_BMPDATA->m_width;
927}
928
929int wxBitmap::GetDepth() const
930{
931 wxCHECK_MSG( IsOk(), -1, wxT("invalid bitmap") );
932
933 return M_BMPDATA->m_bpp;
934}
935
936wxMask *wxBitmap::GetMask() const
937{
938 wxCHECK_MSG( IsOk(), NULL, wxT("invalid bitmap") );
939
940 return M_BMPDATA->m_mask;
941}
942
943void 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
952bool wxBitmap::CopyFromIcon(const wxIcon& icon)
953{
954 *this = icon;
955 return IsOk();
956}
957
958#ifdef __WXGTK3__
959static 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
978wxBitmap 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
1047bool 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
1069bool 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
1087wxPalette *wxBitmap::GetPalette() const
1088{
1089 return NULL;
1090}
1091
1092void wxBitmap::SetPalette(const wxPalette& WXUNUSED(palette))
1093{
1094 // TODO
1095}
1096#endif // wxUSE_PALETTE
1097
1098void wxBitmap::SetHeight( int height )
1099{
1100 AllocExclusive();
1101 M_BMPDATA->m_height = height;
1102}
1103
1104void wxBitmap::SetWidth( int width )
1105{
1106 AllocExclusive();
1107 M_BMPDATA->m_width = width;
1108}
1109
1110void wxBitmap::SetDepth( int depth )
1111{
1112 AllocExclusive();
1113 M_BMPDATA->m_bpp = depth;
1114}
1115
1116#ifndef __WXGTK3__
1117void 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
1132GdkPixmap *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
1165bool 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__
1174GdkPixbuf* 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
1196static 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
1239void 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
1258cairo_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
1295void 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
1312GdkPixbuf *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__
1369bool wxBitmap::HasPixbuf() const
1370{
1371 wxCHECK_MSG( IsOk(), false, wxT("invalid bitmap") );
1372
1373 return M_BMPDATA->m_pixbuf != NULL;
1374}
1375
1376void 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
1392void *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
1431void wxBitmap::UngetRawData(wxPixelDataBase& WXUNUSED(data))
1432{
1433}
1434#endif // wxHAS_RAW_BITMAP
1435
1436bool 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
1447wxGDIRefData* wxBitmap::CreateGDIRefData() const
1448{
1449 return new wxBitmapRefData(0, 0, 0);
1450}
1451
1452wxGDIRefData* 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}