]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/bitmap.cpp
make some reference handling a little more direct and less obscure
[wxWidgets.git] / src / gtk / bitmap.cpp
CommitLineData
c801d85f 1/////////////////////////////////////////////////////////////////////////////
0b04c4e0 2// Name: src/gtk/bitmap.cpp
c801d85f
KB
3// Purpose:
4// Author: Robert Roebling
6f65e337 5// RCS-ID: $Id$
01111366 6// Copyright: (c) 1998 Robert Roebling
65571936 7// Licence: wxWindows licence
c801d85f
KB
8/////////////////////////////////////////////////////////////////////////////
9
14f355c2
VS
10// For compilers that support precompilation, includes "wx.h".
11#include "wx/wxprec.h"
12
559a723c
WS
13#include "wx/bitmap.h"
14
93763ad5 15#ifndef WX_PRECOMP
923d28da 16 #include "wx/icon.h"
155ecd4c 17 #include "wx/image.h"
5ff14574 18 #include "wx/colour.h"
93763ad5 19#endif
22bd9387 20
ce7c8a97 21#include "wx/rawbmp.h"
9e691f46 22
f3d74739 23#include "wx/gtk/private/object.h"
c282e47d 24#include "wx/gtk/private.h"
f3d74739 25
d76fe38b 26#include <gtk/gtk.h>
13111b2a 27
c2fa61e8 28extern GtkWidget *wxGetRootWindow();
c801d85f 29
9dc44eff 30#ifndef __WXGTK3__
06497cba
PC
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}
9dc44eff 72#endif
06497cba 73
c801d85f
KB
74//-----------------------------------------------------------------------------
75// wxMask
76//-----------------------------------------------------------------------------
77
60a3d1c6 78IMPLEMENT_DYNAMIC_CLASS(wxMask, wxMaskBase)
c801d85f 79
8bbe427f 80wxMask::wxMask()
c801d85f 81{
d3b9f782 82 m_bitmap = NULL;
ff7b1510 83}
c801d85f 84
27297c82
VZ
85wxMask::wxMask(const wxMask& mask)
86{
9dc44eff
PC
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
27297c82
VZ
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);
9dc44eff 115#endif
27297c82
VZ
116}
117
91b8de8d 118wxMask::wxMask( const wxBitmap& bitmap, const wxColour& colour )
c801d85f 119{
d3b9f782 120 m_bitmap = NULL;
60a3d1c6 121 InitFromColour(bitmap, colour);
ff7b1510 122}
c801d85f 123
0b04c4e0 124#if wxUSE_PALETTE
91b8de8d 125wxMask::wxMask( const wxBitmap& bitmap, int paletteIndex )
c801d85f 126{
d3b9f782 127 m_bitmap = NULL;
91b8de8d 128 Create( bitmap, paletteIndex );
ff7b1510 129}
0b04c4e0 130#endif // wxUSE_PALETTE
c801d85f 131
91b8de8d 132wxMask::wxMask( const wxBitmap& bitmap )
c801d85f 133{
d3b9f782 134 m_bitmap = NULL;
60a3d1c6 135 InitFromMonoBitmap(bitmap);
ff7b1510 136}
c801d85f 137
9dc44eff
PC
138#ifdef __WXGTK3__
139wxMask::wxMask(cairo_surface_t* bitmap)
140#else
2d13e22f 141wxMask::wxMask(GdkPixmap* bitmap)
9dc44eff 142#endif
2d13e22f
PC
143{
144 m_bitmap = bitmap;
145}
146
8bbe427f 147wxMask::~wxMask()
c801d85f 148{
13111b2a 149 if (m_bitmap)
9dc44eff
PC
150 {
151#ifdef __WXGTK3__
152 cairo_surface_destroy(m_bitmap);
153#else
3fe39b0c 154 g_object_unref (m_bitmap);
9dc44eff
PC
155#endif
156 }
ff7b1510 157}
c801d85f 158
60a3d1c6 159void wxMask::FreeData()
91b8de8d
RR
160{
161 if (m_bitmap)
284b4c88 162 {
9dc44eff
PC
163#ifdef __WXGTK3__
164 cairo_surface_destroy(m_bitmap);
165#else
3fe39b0c 166 g_object_unref (m_bitmap);
9dc44eff 167#endif
d3b9f782 168 m_bitmap = NULL;
91b8de8d 169 }
60a3d1c6 170}
13111b2a 171
60a3d1c6
PC
172bool wxMask::InitFromColour(const wxBitmap& bitmap, const wxColour& colour)
173{
13785a4b
PC
174 const int w = bitmap.GetWidth();
175 const int h = bitmap.GetHeight();
13111b2a 176
9dc44eff
PC
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
5ac1d44a 200 // create mask as XBM format bitmap
13111b2a 201
5ac1d44a
PC
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];
d0e79755 205 // set bits are unmasked
5ac1d44a
PC
206 memset(out, 0xff, out_size);
207 unsigned bit_index = 0;
13785a4b 208 if (bitmap.HasPixbuf())
1fb4de31 209 {
5ac1d44a
PC
210 const wxByte r_mask = colour.Red();
211 const wxByte g_mask = colour.Green();
212 const wxByte b_mask = colour.Blue();
13785a4b 213 GdkPixbuf* pixbuf = bitmap.GetPixbuf();
5ac1d44a
PC
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 }
2eefae6e 225 }
13785a4b 226 else
1fb4de31 227 {
5ac1d44a 228 GdkImage* image = gdk_drawable_get_image(bitmap.GetPixmap(), 0, 0, w, h);
13785a4b 229 GdkColormap* colormap = gdk_image_get_colormap(image);
a7cfe08a
PC
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
13785a4b
PC
235 {
236 wxColor c(colour);
237 c.CalcPixel(colormap);
238 mask_pixel = c.GetPixel();
239 }
5ac1d44a 240 for (int y = 0; y < h; y++)
1fb4de31 241 {
5ac1d44a
PC
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;
f9ee644e 246 }
13785a4b 247 g_object_unref(image);
5ac1d44a
PC
248 }
249 m_bitmap = gdk_bitmap_create_from_data(wxGetRootWindow()->window, (char*)out, w, h);
250 delete[] out;
9dc44eff 251#endif
902725ee 252 return true;
91b8de8d
RR
253}
254
60a3d1c6 255bool wxMask::InitFromMonoBitmap(const wxBitmap& bitmap)
91b8de8d 256{
3d37a968 257 if (!bitmap.IsOk()) return false;
284b4c88 258
b85229d1 259 wxCHECK_MSG( bitmap.GetDepth() == 1, false, wxT("Cannot create mask from colour bitmap") );
284b4c88 260
9dc44eff
PC
261#ifdef __WXGTK3__
262 InitFromColour(bitmap, *wxBLACK);
263#else
c2fa61e8 264 m_bitmap = gdk_pixmap_new( wxGetRootWindow()->window, bitmap.GetWidth(), bitmap.GetHeight(), 1 );
284b4c88 265
902725ee 266 if (!m_bitmap) return false;
284b4c88 267
f3d74739 268 wxGtkObject<GdkGC> gc(gdk_gc_new( m_bitmap ));
9be3f755
PC
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());
9dc44eff 271#endif
284b4c88 272
902725ee 273 return true;
91b8de8d
RR
274}
275
9dc44eff
PC
276#ifdef __WXGTK3__
277cairo_surface_t* wxMask::GetBitmap() const
278#else
29e461a2 279GdkPixmap* wxMask::GetBitmap() const
9dc44eff 280#endif
c801d85f 281{
fd0eed64 282 return m_bitmap;
ff7b1510 283}
8bbe427f 284
c801d85f 285//-----------------------------------------------------------------------------
732d8c74 286// wxBitmapRefData
c801d85f
KB
287//-----------------------------------------------------------------------------
288
8f884a0d 289class wxBitmapRefData: public wxGDIRefData
c801d85f 290{
fd0eed64 291public:
06497cba 292 wxBitmapRefData(int width, int height, int depth);
d3c7fc99 293 virtual ~wxBitmapRefData();
f2593d0d 294
06497cba 295 virtual bool IsOk() const;
8f884a0d 296
9dc44eff
PC
297#ifdef __WXGTK3__
298 GdkPixbuf* m_pixbufMask;
299 GdkPixbuf* m_pixbufNoMask;
300 cairo_surface_t* m_surface;
301#else
f2593d0d 302 GdkPixmap *m_pixmap;
feac7937 303 GdkPixbuf *m_pixbuf;
9dc44eff 304#endif
f2593d0d
RR
305 wxMask *m_mask;
306 int m_width;
307 int m_height;
308 int m_bpp;
9dc44eff 309#ifndef __WXGTK3__
06497cba 310 bool m_alphaRequested;
9dc44eff 311#endif
fc684792 312
fc684792
VZ
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);
c801d85f
KB
318};
319
06497cba 320wxBitmapRefData::wxBitmapRefData(int width, int height, int depth)
c801d85f 321{
9dc44eff
PC
322#ifdef __WXGTK3__
323 m_pixbufMask = NULL;
324 m_pixbufNoMask = NULL;
325 m_surface = NULL;
326#else
d3b9f782
VZ
327 m_pixmap = NULL;
328 m_pixbuf = NULL;
9dc44eff 329#endif
d3b9f782 330 m_mask = NULL;
06497cba
PC
331 m_width = width;
332 m_height = height;
333 m_bpp = depth;
9dc44eff
PC
334#ifdef __WXGTK3__
335 if (m_bpp != 1 && m_bpp != 32)
336 m_bpp = 24;
337#else
06497cba
PC
338 if (m_bpp < 0)
339 m_bpp = gdk_drawable_get_depth(wxGetRootWindow()->window);
340 m_alphaRequested = depth == 32;
9dc44eff 341#endif
ff7b1510 342}
c801d85f 343
8bbe427f 344wxBitmapRefData::~wxBitmapRefData()
c801d85f 345{
9dc44eff
PC
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
3ebcd89d 354 if (m_pixmap)
3fe39b0c 355 g_object_unref (m_pixmap);
feac7937 356 if (m_pixbuf)
3fe39b0c 357 g_object_unref (m_pixbuf);
9dc44eff 358#endif
3ebcd89d 359 delete m_mask;
ff7b1510 360}
c801d85f 361
06497cba
PC
362bool wxBitmapRefData::IsOk() const
363{
364 return m_bpp != 0;
365}
732d8c74
FM
366
367//-----------------------------------------------------------------------------
368// wxBitmap
c801d85f
KB
369//-----------------------------------------------------------------------------
370
5c33522f 371#define M_BMPDATA static_cast<wxBitmapRefData*>(m_refData)
c801d85f
KB
372
373IMPLEMENT_DYNAMIC_CLASS(wxBitmap,wxGDIObject)
374
23656673
PC
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)
c801d85f 381{
23656673
PC
382 wxASSERT(depth == 1);
383 if (width > 0 && height > 0 && depth == 1)
384 {
9dc44eff
PC
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
23656673 409 }
ff7b1510 410}
8bbe427f 411
452418c4
PC
412wxBitmap::wxBitmap(const char* const* bits)
413{
414 wxCHECK2_MSG(bits != NULL, return, wxT("invalid bitmap data"));
415
9dc44eff
PC
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
452418c4 429 GdkBitmap* mask = NULL;
9dc44eff
PC
430 GdkPixmap* pixmap = gdk_pixmap_create_from_xpm_d(wxGetRootWindow()->window, &mask, NULL, const_cast<char**>(bits));
431 if (pixmap)
452418c4 432 {
9dc44eff
PC
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 }
452418c4 441 }
9dc44eff 442#endif
452418c4
PC
443}
444
54195d23
PC
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;
9dc44eff
PC
453#ifdef __WXGTK3__
454 bmpData->m_pixbufNoMask = pixbuf;
455#else
54195d23 456 bmpData->m_pixbuf = pixbuf;
9dc44eff 457#endif
54195d23
PC
458 }
459}
460
23656673 461wxBitmap::~wxBitmap()
c801d85f 462{
c826213d
RR
463}
464
465bool wxBitmap::Create( int width, int height, int depth )
466{
467 UnRef();
06497cba
PC
468 wxCHECK_MSG(width >= 0 && height >= 0, false, "invalid bitmap size");
469 m_refData = new wxBitmapRefData(width, height, depth);
470 return true;
ff7b1510 471}
b5f01ae0 472
9dc44eff
PC
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
c0521644 519
9dc44eff
PC
520#if wxUSE_IMAGE
521#ifdef __WXGTK3__
522wxBitmap::wxBitmap(const wxImage& image, int depth)
b5f01ae0 523{
9dc44eff
PC
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();
a11672a4 539
9dc44eff
PC
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);
b5f01ae0 543
9dc44eff
PC
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");
902725ee 571
e503ef07 572 if (depth == 32 || (depth == -1 && image.HasAlpha()))
9dc44eff
PC
573 CreateFromImageAsPixbuf(image);
574 else
575 // otherwise create pixmap, if alpha is present it will be converted to mask
576 CreateFromImageAsPixmap(image, depth);
feac7937 577}
3ebcd89d 578
5ac1d44a 579bool wxBitmap::CreateFromImageAsPixmap(const wxImage& image, int depth)
feac7937 580{
5ac1d44a
PC
581 const int w = image.GetWidth();
582 const int h = image.GetHeight();
583 if (depth == 1)
feac7937 584 {
5ac1d44a
PC
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];
d0e79755 590 // set bits are black
5ac1d44a
PC
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;
3d37a968
FM
604
605 if (!M_BMPDATA) // SetPixmap may have failed
606 return false;
5ac1d44a
PC
607 }
608 else
609 {
610 SetPixmap(gdk_pixmap_new(wxGetRootWindow()->window, w, h, depth));
3d37a968
FM
611 if (!M_BMPDATA)
612 return false;
613
f3d74739 614 wxGtkObject<GdkGC> gc(gdk_gc_new(M_BMPDATA->m_pixmap));
5ac1d44a
PC
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);
feac7937 619 }
b5f01ae0 620
5ac1d44a
PC
621 const wxByte* alpha = image.GetAlpha();
622 if (alpha != NULL || image.HasMask())
feac7937 623 {
5ac1d44a
PC
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)
b5f01ae0 631 {
5ac1d44a 632 for (int y = 0; y < h; y++)
b5f01ae0 633 {
5ac1d44a
PC
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;
feac7937 638 }
5ac1d44a
PC
639 }
640 else
b5f01ae0 641 {
5ac1d44a
PC
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 }
2d13e22f 654 SetMask(new wxMask(gdk_bitmap_create_from_data(M_BMPDATA->m_pixmap, (char*)out, w, h)));
5ac1d44a
PC
655 delete[] out;
656 }
3d37a968 657 return IsOk();
b5f01ae0
VS
658}
659
feac7937 660bool wxBitmap::CreateFromImageAsPixbuf(const wxImage& image)
b5f01ae0 661{
feac7937
VS
662 int width = image.GetWidth();
663 int height = image.GetHeight();
2eefae6e 664
23656673 665 Create(width, height, 32);
06497cba 666 GdkPixbuf* pixbuf = GetPixbuf();
feac7937
VS
667 if (!pixbuf)
668 return false;
c2fa61e8 669
feac7937 670 // Copy the data:
23656673 671 const unsigned char* in = image.GetData();
feac7937
VS
672 unsigned char *out = gdk_pixbuf_get_pixels(pixbuf);
673 unsigned char *alpha = image.GetAlpha();
902725ee 674
23656673 675 int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - 4 * width;
b5f01ae0 676
23656673 677 for (int y = 0; y < height; y++, out += rowpad)
b5f01ae0 678 {
e503ef07 679 for (int x = 0; x < width; x++, out += 4, in += 3)
feac7937
VS
680 {
681 out[0] = in[0];
682 out[1] = in[1];
683 out[2] = in[2];
e503ef07
PC
684 if (alpha)
685 out[3] = *alpha++;
feac7937 686 }
b5f01ae0 687 }
902725ee 688
feac7937
VS
689 return true;
690}
9dc44eff 691#endif
b5f01ae0 692
feac7937
VS
693wxImage wxBitmap::ConvertToImage() const
694{
9dc44eff
PC
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
3d37a968 757 wxCHECK_MSG( IsOk(), wxNullImage, wxT("invalid bitmap") );
902725ee 758
afbfbfdf
PC
759 const int w = GetWidth();
760 const int h = GetHeight();
23656673 761 wxImage image(w, h, false);
feac7937 762 unsigned char *data = image.GetData();
2eefae6e 763
5588ce92 764 wxCHECK_MSG(data != NULL, wxNullImage, wxT("couldn't create image") );
b5f01ae0 765
70029506 766 // prefer pixbuf if available, it will preserve alpha and should be quicker
feac7937 767 if (HasPixbuf())
b5f01ae0 768 {
feac7937 769 GdkPixbuf *pixbuf = GetPixbuf();
70029506
PC
770 unsigned char* alpha = NULL;
771 if (gdk_pixbuf_get_has_alpha(pixbuf))
772 {
773 image.SetAlpha();
774 alpha = image.GetAlpha();
775 }
23656673 776 const unsigned char* in = gdk_pixbuf_get_pixels(pixbuf);
feac7937 777 unsigned char *out = data;
70029506 778 const int inc = 3 + int(alpha != NULL);
23656673 779 const int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - inc * w;
b5f01ae0 780
23656673 781 for (int y = 0; y < h; y++, in += rowpad)
feac7937 782 {
70029506 783 for (int x = 0; x < w; x++, in += inc, out += 3)
feac7937
VS
784 {
785 out[0] = in[0];
786 out[1] = in[1];
787 out[2] = in[2];
70029506
PC
788 if (alpha != NULL)
789 *alpha++ = in[3];
feac7937
VS
790 }
791 }
b5f01ae0 792 }
feac7937 793 else
b5f01ae0 794 {
afbfbfdf
PC
795 GdkPixmap* pixmap = GetPixmap();
796 GdkPixmap* pixmap_invert = NULL;
797 if (GetDepth() == 1)
b5f01ae0 798 {
d0e79755 799 // mono bitmaps are inverted, i.e. 0 is white
afbfbfdf 800 pixmap_invert = gdk_pixmap_new(pixmap, w, h, 1);
f3d74739 801 wxGtkObject<GdkGC> gc(gdk_gc_new(pixmap_invert));
afbfbfdf
PC
802 gdk_gc_set_function(gc, GDK_COPY_INVERT);
803 gdk_draw_drawable(pixmap_invert, gc, pixmap, 0, 0, 0, 0, w, h);
afbfbfdf 804 pixmap = pixmap_invert;
feac7937 805 }
afbfbfdf
PC
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);
feac7937 809
afbfbfdf 810 gdk_pixbuf_get_from_drawable(pixbuf, pixmap, NULL, 0, 0, 0, 0, w, h);
feac7937 811
afbfbfdf
PC
812 g_object_unref(pixbuf);
813 if (pixmap_invert != NULL)
814 g_object_unref(pixmap_invert);
70029506
PC
815 }
816 // convert mask, unless there is already alpha
817 if (GetMask() && !image.HasAlpha())
818 {
23656673
PC
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
70029506
PC
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;
feac7937 827
70029506
PC
828 image.SetMaskColour(MASK_RED, MASK_GREEN, MASK_BLUE);
829 GdkImage* image_mask = gdk_drawable_get_image(GetMask()->GetBitmap(), 0, 0, w, h);
feac7937 830
70029506
PC
831 for (int y = 0; y < h; y++)
832 {
833 for (int x = 0; x < w; x++, data += 3)
8ab696e0 834 {
70029506 835 if (gdk_image_get_pixel(image_mask, x, y) == 0)
8ab696e0 836 {
70029506
PC
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 {
23656673
PC
843 // we have to fudge the colour a bit to prevent
844 // this pixel from appearing transparent
70029506 845 data[2] = MASK_BLUE_REPLACEMENT;
feac7937 846 }
feac7937 847 }
b5f01ae0 848 }
70029506 849 g_object_unref(image_mask);
feac7937 850 }
9dc44eff 851#endif
b5f01ae0
VS
852
853 return image;
854}
855
c0521644
VZ
856#endif // wxUSE_IMAGE
857
91b8de8d 858int wxBitmap::GetHeight() const
c801d85f 859{
3d37a968 860 wxCHECK_MSG( IsOk(), -1, wxT("invalid bitmap") );
e55ad60e 861
fd0eed64 862 return M_BMPDATA->m_height;
ff7b1510 863}
c801d85f 864
91b8de8d 865int wxBitmap::GetWidth() const
c801d85f 866{
3d37a968 867 wxCHECK_MSG( IsOk(), -1, wxT("invalid bitmap") );
8bbe427f 868
fd0eed64 869 return M_BMPDATA->m_width;
ff7b1510 870}
c801d85f 871
91b8de8d 872int wxBitmap::GetDepth() const
c801d85f 873{
3d37a968 874 wxCHECK_MSG( IsOk(), -1, wxT("invalid bitmap") );
8bbe427f 875
fd0eed64 876 return M_BMPDATA->m_bpp;
ff7b1510 877}
c801d85f 878
91b8de8d 879wxMask *wxBitmap::GetMask() const
c801d85f 880{
d3b9f782 881 wxCHECK_MSG( IsOk(), NULL, wxT("invalid bitmap") );
8bbe427f 882
fd0eed64 883 return M_BMPDATA->m_mask;
ff7b1510 884}
c801d85f
KB
885
886void wxBitmap::SetMask( wxMask *mask )
887{
3d37a968 888 wxCHECK_RET( IsOk(), wxT("invalid bitmap") );
8bbe427f 889
e3e89a93 890 AllocExclusive();
23656673 891 delete M_BMPDATA->m_mask;
fd0eed64 892 M_BMPDATA->m_mask = mask;
ff7b1510 893}
c801d85f 894
9dc44eff
PC
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
db0aec83
VS
940bool wxBitmap::CopyFromIcon(const wxIcon& icon)
941{
942 *this = icon;
3d37a968 943 return IsOk();
db0aec83
VS
944}
945
9dc44eff
PC
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
17bec151
RR
966wxBitmap wxBitmap::GetSubBitmap( const wxRect& rect) const
967{
5588ce92
PC
968 wxBitmap ret;
969
3d37a968 970 wxCHECK_MSG(IsOk(), ret, wxT("invalid bitmap"));
06497cba
PC
971
972 const int w = rect.width;
973 const int h = rect.height;
974 const wxBitmapRefData* bmpData = M_BMPDATA;
975
23656673 976 wxCHECK_MSG(rect.x >= 0 && rect.y >= 0 &&
06497cba
PC
977 rect.x + w <= bmpData->m_width &&
978 rect.y + h <= bmpData->m_height,
23656673 979 ret, wxT("invalid bitmap region"));
13111b2a 980
fc684792 981 wxBitmapRefData * const newRef = new wxBitmapRefData(w, h, bmpData->m_bpp);
06497cba 982 ret.m_refData = newRef;
06497cba 983
9dc44eff
PC
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
06497cba 1003 if (bmpData->m_pixbuf)
17bec151 1004 {
06497cba
PC
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);
17bec151 1009 }
06497cba 1010 if (bmpData->m_pixmap)
17bec151 1011 {
06497cba
PC
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);
17bec151 1017 }
2d13e22f
PC
1018 GdkPixmap* mask = NULL;
1019 if (bmpData->m_mask)
1020 mask = bmpData->m_mask->GetBitmap();
1021 if (mask)
17bec151 1022 {
2d13e22f
PC
1023 GdkPixmap* sub_mask = gdk_pixmap_new(mask, w, h, 1);
1024 newRef->m_mask = new wxMask(sub_mask);
06497cba
PC
1025 GdkGC* gc = gdk_gc_new(sub_mask);
1026 gdk_draw_drawable(
2d13e22f 1027 sub_mask, gc, mask, rect.x, rect.y, 0, 0, w, h);
06497cba 1028 g_object_unref(gc);
17bec151 1029 }
9dc44eff 1030#endif
13111b2a 1031
17bec151
RR
1032 return ret;
1033}
1034
4611dd06 1035bool wxBitmap::SaveFile( const wxString &name, wxBitmapType type, const wxPalette *WXUNUSED(palette) ) const
c801d85f 1036{
3d37a968 1037 wxCHECK_MSG( IsOk(), false, wxT("invalid bitmap") );
8bbe427f 1038
c0521644 1039#if wxUSE_IMAGE
5588ce92 1040 wxImage image = ConvertToImage();
8362e67c
PC
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 &&
c282e47d 1054 gdk_pixbuf_save(GetPixbuf(), wxGTK_CONV_FN(name), type_name, NULL, NULL);
ff7b1510 1055}
c801d85f 1056
4611dd06 1057bool wxBitmap::LoadFile( const wxString &name, wxBitmapType type )
c801d85f 1058{
c0521644 1059#if wxUSE_IMAGE
8362e67c
PC
1060 wxImage image;
1061 if (image.LoadFile(name, type) && image.IsOk())
1062 *this = wxBitmap(image);
1063 else
1064#endif
fd0eed64 1065 {
357f4c81
VZ
1066 wxUnusedVar(type); // The type is detected automatically by GDK.
1067
c282e47d 1068 *this = wxBitmap(gdk_pixbuf_new_from_file(wxGTK_CONV_FN(name), NULL));
fd0eed64 1069 }
8bbe427f 1070
3d37a968 1071 return IsOk();
ff7b1510 1072}
8bbe427f 1073
0b04c4e0 1074#if wxUSE_PALETTE
91b8de8d 1075wxPalette *wxBitmap::GetPalette() const
c801d85f 1076{
b2bf3ac6 1077 return NULL;
ff7b1510 1078}
c801d85f 1079
4611dd06
RR
1080void wxBitmap::SetPalette(const wxPalette& WXUNUSED(palette))
1081{
1082 // TODO
1083}
0b04c4e0 1084#endif // wxUSE_PALETTE
4611dd06 1085
4bc67cc5
RR
1086void wxBitmap::SetHeight( int height )
1087{
e3e89a93 1088 AllocExclusive();
4bc67cc5
RR
1089 M_BMPDATA->m_height = height;
1090}
1091
1092void wxBitmap::SetWidth( int width )
1093{
e3e89a93 1094 AllocExclusive();
4bc67cc5
RR
1095 M_BMPDATA->m_width = width;
1096}
1097
1098void wxBitmap::SetDepth( int depth )
1099{
e3e89a93 1100 AllocExclusive();
4bc67cc5
RR
1101 M_BMPDATA->m_bpp = depth;
1102}
1103
9dc44eff 1104#ifndef __WXGTK3__
4bc67cc5
RR
1105void wxBitmap::SetPixmap( GdkPixmap *pixmap )
1106{
06497cba
PC
1107 UnRef();
1108
3d37a968
FM
1109 if (!pixmap)
1110 return;
1111
06497cba
PC
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);
4bc67cc5
RR
1118}
1119
91b8de8d 1120GdkPixmap *wxBitmap::GetPixmap() const
c801d85f 1121{
d3b9f782 1122 wxCHECK_MSG( IsOk(), NULL, wxT("invalid bitmap") );
8bbe427f 1123
06497cba
PC
1124 wxBitmapRefData* bmpData = M_BMPDATA;
1125 if (bmpData->m_pixmap)
1126 return bmpData->m_pixmap;
1127
1128 if (bmpData->m_pixbuf)
feac7937 1129 {
2d13e22f 1130 GdkPixmap* pixmap = NULL;
06497cba
PC
1131 GdkPixmap** mask_pixmap = NULL;
1132 if (gdk_pixbuf_get_has_alpha(bmpData->m_pixbuf))
70029506
PC
1133 {
1134 // make new mask from alpha
2d13e22f 1135 mask_pixmap = &pixmap;
70029506 1136 }
06497cba
PC
1137 gdk_pixbuf_render_pixmap_and_mask(
1138 bmpData->m_pixbuf, &bmpData->m_pixmap, mask_pixmap, 128);
2d13e22f
PC
1139 if (pixmap)
1140 {
1141 delete bmpData->m_mask;
1142 bmpData->m_mask = new wxMask(pixmap);
1143 }
feac7937 1144 }
06497cba
PC
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;
ff7b1510 1151}
8bbe427f 1152
feac7937
VS
1153bool wxBitmap::HasPixmap() const
1154{
3d37a968 1155 wxCHECK_MSG( IsOk(), false, wxT("invalid bitmap") );
feac7937
VS
1156
1157 return M_BMPDATA->m_pixmap != NULL;
1158}
9dc44eff
PC
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
feac7937 1299
feac7937
VS
1300GdkPixbuf *wxBitmap::GetPixbuf() const
1301{
3d37a968 1302 wxCHECK_MSG( IsOk(), NULL, wxT("invalid bitmap") );
feac7937 1303
06497cba 1304 wxBitmapRefData* bmpData = M_BMPDATA;
9dc44eff
PC
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
06497cba
PC
1338 if (bmpData->m_pixbuf)
1339 return bmpData->m_pixbuf;
6db34764 1340
06497cba
PC
1341 const int w = bmpData->m_width;
1342 const int h = bmpData->m_height;
1343 GdkPixmap* mask = NULL;
1344 if (bmpData->m_mask)
2d13e22f 1345 mask = bmpData->m_mask->GetBitmap();
06497cba
PC
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;
9dc44eff 1353#endif
feac7937
VS
1354}
1355
9dc44eff 1356#ifndef __WXGTK3__
feac7937
VS
1357bool wxBitmap::HasPixbuf() const
1358{
3d37a968 1359 wxCHECK_MSG( IsOk(), false, wxT("invalid bitmap") );
feac7937
VS
1360
1361 return M_BMPDATA->m_pixbuf != NULL;
1362}
1363
feac7937
VS
1364void wxBitmap::PurgeOtherRepresentations(wxBitmap::Representation keep)
1365{
1366 if (keep == Pixmap && HasPixbuf())
1367 {
3fe39b0c 1368 g_object_unref (M_BMPDATA->m_pixbuf);
feac7937
VS
1369 M_BMPDATA->m_pixbuf = NULL;
1370 }
1371 if (keep == Pixbuf && HasPixmap())
1372 {
3fe39b0c 1373 g_object_unref (M_BMPDATA->m_pixmap);
feac7937
VS
1374 M_BMPDATA->m_pixmap = NULL;
1375 }
1376}
9dc44eff 1377#endif
feac7937 1378
ce7c8a97 1379#ifdef wxHAS_RAW_BITMAP
284f2b59
RR
1380void *wxBitmap::GetRawData(wxPixelDataBase& data, int bpp)
1381{
70029506 1382 void* bits = NULL;
9dc44eff
PC
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
284f2b59 1404 GdkPixbuf *pixbuf = GetPixbuf();
70029506 1405 const bool hasAlpha = HasAlpha();
3d37a968 1406
70029506 1407 // allow access if bpp is valid and matches existence of alpha
c74fc1e1 1408 if ( pixbuf && ((bpp == 24 && !hasAlpha) || (bpp == 32 && hasAlpha)) )
70029506
PC
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 }
9dc44eff 1415#endif
70029506 1416 return bits;
284f2b59
RR
1417}
1418
17a1ebd1 1419void wxBitmap::UngetRawData(wxPixelDataBase& WXUNUSED(data))
284f2b59
RR
1420{
1421}
ce7c8a97 1422#endif // wxHAS_RAW_BITMAP
284f2b59 1423
902725ee 1424bool wxBitmap::HasAlpha() const
0ff2a74d 1425{
06497cba 1426 const wxBitmapRefData* bmpData = M_BMPDATA;
9dc44eff
PC
1427#ifdef __WXGTK3__
1428 return bmpData && bmpData->m_bpp == 32;
1429#else
06497cba
PC
1430 return bmpData && (bmpData->m_alphaRequested ||
1431 (bmpData->m_pixbuf && gdk_pixbuf_get_has_alpha(bmpData->m_pixbuf)));
9dc44eff 1432#endif
0ff2a74d
RR
1433}
1434
8f884a0d 1435wxGDIRefData* wxBitmap::CreateGDIRefData() const
e3e89a93 1436{
06497cba 1437 return new wxBitmapRefData(0, 0, 0);
e3e89a93
PC
1438}
1439
8f884a0d 1440wxGDIRefData* wxBitmap::CloneGDIRefData(const wxGDIRefData* data) const
e3e89a93 1441{
5c33522f 1442 const wxBitmapRefData* oldRef = static_cast<const wxBitmapRefData*>(data);
fc684792
VZ
1443 wxBitmapRefData * const newRef = new wxBitmapRefData(oldRef->m_width,
1444 oldRef->m_height,
1445 oldRef->m_bpp);
9dc44eff
PC
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
e3e89a93
PC
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));
f3d74739 1471 wxGtkObject<GdkGC> gc(gdk_gc_new(newRef->m_pixmap));
e3e89a93
PC
1472 gdk_draw_drawable(
1473 newRef->m_pixmap, gc, oldRef->m_pixmap, 0, 0, 0, 0, -1, -1);
e3e89a93
PC
1474 }
1475 if (oldRef->m_pixbuf != NULL)
1476 {
1477 newRef->m_pixbuf = gdk_pixbuf_copy(oldRef->m_pixbuf);
1478 }
9dc44eff 1479#endif
e3e89a93
PC
1480 if (oldRef->m_mask != NULL)
1481 {
27297c82 1482 newRef->m_mask = new wxMask(*oldRef->m_mask);
e3e89a93 1483 }
e3e89a93
PC
1484
1485 return newRef;
1486}
1487
4b61c88d
RR
1488/* static */ void wxBitmap::InitStandardHandlers()
1489{
1490 // TODO: Insert handler based on GdkPixbufs handler later
1491}