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