]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/bitmap.cpp
Use "<Application> Preferences" as generic wxPreferencesEditor dialog title.
[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
feabb2eb
PC
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
9dc44eff 316#ifdef __WXGTK3__
5ca21fe7 317wxMask::operator cairo_surface_t*() const
9dc44eff 318#else
5ca21fe7 319wxMask::operator GdkPixmap*() const
9dc44eff 320#endif
c801d85f 321{
fd0eed64 322 return m_bitmap;
ff7b1510 323}
8bbe427f 324
c801d85f 325//-----------------------------------------------------------------------------
732d8c74 326// wxBitmapRefData
c801d85f
KB
327//-----------------------------------------------------------------------------
328
8f884a0d 329class wxBitmapRefData: public wxGDIRefData
c801d85f 330{
fd0eed64 331public:
06497cba 332 wxBitmapRefData(int width, int height, int depth);
d3c7fc99 333 virtual ~wxBitmapRefData();
f2593d0d 334
06497cba 335 virtual bool IsOk() const;
8f884a0d 336
9dc44eff
PC
337#ifdef __WXGTK3__
338 GdkPixbuf* m_pixbufMask;
339 GdkPixbuf* m_pixbufNoMask;
340 cairo_surface_t* m_surface;
341#else
f2593d0d 342 GdkPixmap *m_pixmap;
feac7937 343 GdkPixbuf *m_pixbuf;
9dc44eff 344#endif
f2593d0d
RR
345 wxMask *m_mask;
346 int m_width;
347 int m_height;
348 int m_bpp;
9dc44eff 349#ifndef __WXGTK3__
06497cba 350 bool m_alphaRequested;
9dc44eff 351#endif
fc684792 352
fc684792
VZ
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);
c801d85f
KB
358};
359
06497cba 360wxBitmapRefData::wxBitmapRefData(int width, int height, int depth)
c801d85f 361{
9dc44eff
PC
362#ifdef __WXGTK3__
363 m_pixbufMask = NULL;
364 m_pixbufNoMask = NULL;
365 m_surface = NULL;
366#else
d3b9f782
VZ
367 m_pixmap = NULL;
368 m_pixbuf = NULL;
9dc44eff 369#endif
d3b9f782 370 m_mask = NULL;
06497cba
PC
371 m_width = width;
372 m_height = height;
373 m_bpp = depth;
9dc44eff
PC
374#ifdef __WXGTK3__
375 if (m_bpp != 1 && m_bpp != 32)
376 m_bpp = 24;
377#else
06497cba
PC
378 if (m_bpp < 0)
379 m_bpp = gdk_drawable_get_depth(wxGetRootWindow()->window);
380 m_alphaRequested = depth == 32;
9dc44eff 381#endif
ff7b1510 382}
c801d85f 383
8bbe427f 384wxBitmapRefData::~wxBitmapRefData()
c801d85f 385{
9dc44eff
PC
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
3ebcd89d 394 if (m_pixmap)
3fe39b0c 395 g_object_unref (m_pixmap);
feac7937 396 if (m_pixbuf)
3fe39b0c 397 g_object_unref (m_pixbuf);
9dc44eff 398#endif
3ebcd89d 399 delete m_mask;
ff7b1510 400}
c801d85f 401
06497cba
PC
402bool wxBitmapRefData::IsOk() const
403{
404 return m_bpp != 0;
405}
732d8c74
FM
406
407//-----------------------------------------------------------------------------
408// wxBitmap
c801d85f
KB
409//-----------------------------------------------------------------------------
410
5c33522f 411#define M_BMPDATA static_cast<wxBitmapRefData*>(m_refData)
c801d85f
KB
412
413IMPLEMENT_DYNAMIC_CLASS(wxBitmap,wxGDIObject)
414
23656673
PC
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)
c801d85f 421{
23656673
PC
422 wxASSERT(depth == 1);
423 if (width > 0 && height > 0 && depth == 1)
424 {
9dc44eff
PC
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
23656673 449 }
ff7b1510 450}
8bbe427f 451
452418c4
PC
452wxBitmap::wxBitmap(const char* const* bits)
453{
454 wxCHECK2_MSG(bits != NULL, return, wxT("invalid bitmap data"));
455
9dc44eff
PC
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
452418c4 469 GdkBitmap* mask = NULL;
9dc44eff
PC
470 GdkPixmap* pixmap = gdk_pixmap_create_from_xpm_d(wxGetRootWindow()->window, &mask, NULL, const_cast<char**>(bits));
471 if (pixmap)
452418c4 472 {
9dc44eff
PC
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 }
452418c4 481 }
9dc44eff 482#endif
452418c4
PC
483}
484
5ca21fe7 485wxBitmap::wxBitmap(GdkPixbuf* pixbuf, int depth)
54195d23
PC
486{
487 if (pixbuf)
488 {
5ca21fe7
PC
489 if (depth != 1)
490 depth = gdk_pixbuf_get_n_channels(pixbuf) * 8;
54195d23
PC
491 wxBitmapRefData* bmpData = new wxBitmapRefData(
492 gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf),
5ca21fe7 493 depth);
54195d23 494 m_refData = bmpData;
9dc44eff
PC
495#ifdef __WXGTK3__
496 bmpData->m_pixbufNoMask = pixbuf;
497#else
54195d23 498 bmpData->m_pixbuf = pixbuf;
9dc44eff 499#endif
54195d23
PC
500 }
501}
502
5ca21fe7
PC
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
23656673 518wxBitmap::~wxBitmap()
c801d85f 519{
c826213d
RR
520}
521
522bool wxBitmap::Create( int width, int height, int depth )
523{
524 UnRef();
06497cba
PC
525 wxCHECK_MSG(width >= 0 && height >= 0, false, "invalid bitmap size");
526 m_refData = new wxBitmapRefData(width, height, depth);
527 return true;
ff7b1510 528}
b5f01ae0 529
9dc44eff
PC
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
c0521644 576
9dc44eff
PC
577#if wxUSE_IMAGE
578#ifdef __WXGTK3__
579wxBitmap::wxBitmap(const wxImage& image, int depth)
b5f01ae0 580{
9dc44eff
PC
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();
a11672a4 596
9dc44eff
PC
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);
b5f01ae0 600
9dc44eff
PC
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");
902725ee 628
e503ef07 629 if (depth == 32 || (depth == -1 && image.HasAlpha()))
9dc44eff
PC
630 CreateFromImageAsPixbuf(image);
631 else
632 // otherwise create pixmap, if alpha is present it will be converted to mask
633 CreateFromImageAsPixmap(image, depth);
feac7937 634}
3ebcd89d 635
5ac1d44a 636bool wxBitmap::CreateFromImageAsPixmap(const wxImage& image, int depth)
feac7937 637{
5ac1d44a
PC
638 const int w = image.GetWidth();
639 const int h = image.GetHeight();
640 if (depth == 1)
feac7937 641 {
5ac1d44a
PC
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];
d0e79755 647 // set bits are black
5ac1d44a
PC
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;
3d37a968
FM
661
662 if (!M_BMPDATA) // SetPixmap may have failed
663 return false;
5ac1d44a
PC
664 }
665 else
666 {
667 SetPixmap(gdk_pixmap_new(wxGetRootWindow()->window, w, h, depth));
3d37a968
FM
668 if (!M_BMPDATA)
669 return false;
670
f3d74739 671 wxGtkObject<GdkGC> gc(gdk_gc_new(M_BMPDATA->m_pixmap));
5ac1d44a
PC
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);
feac7937 676 }
b5f01ae0 677
5ac1d44a
PC
678 const wxByte* alpha = image.GetAlpha();
679 if (alpha != NULL || image.HasMask())
feac7937 680 {
5ac1d44a
PC
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)
b5f01ae0 688 {
5ac1d44a 689 for (int y = 0; y < h; y++)
b5f01ae0 690 {
5ac1d44a
PC
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;
feac7937 695 }
5ac1d44a
PC
696 }
697 else
b5f01ae0 698 {
5ac1d44a
PC
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 }
2d13e22f 711 SetMask(new wxMask(gdk_bitmap_create_from_data(M_BMPDATA->m_pixmap, (char*)out, w, h)));
5ac1d44a
PC
712 delete[] out;
713 }
3d37a968 714 return IsOk();
b5f01ae0
VS
715}
716
feac7937 717bool wxBitmap::CreateFromImageAsPixbuf(const wxImage& image)
b5f01ae0 718{
feac7937
VS
719 int width = image.GetWidth();
720 int height = image.GetHeight();
2eefae6e 721
23656673 722 Create(width, height, 32);
06497cba 723 GdkPixbuf* pixbuf = GetPixbuf();
feac7937
VS
724 if (!pixbuf)
725 return false;
c2fa61e8 726
feac7937 727 // Copy the data:
23656673 728 const unsigned char* in = image.GetData();
feac7937
VS
729 unsigned char *out = gdk_pixbuf_get_pixels(pixbuf);
730 unsigned char *alpha = image.GetAlpha();
902725ee 731
23656673 732 int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - 4 * width;
b5f01ae0 733
23656673 734 for (int y = 0; y < height; y++, out += rowpad)
b5f01ae0 735 {
e503ef07 736 for (int x = 0; x < width; x++, out += 4, in += 3)
feac7937
VS
737 {
738 out[0] = in[0];
739 out[1] = in[1];
740 out[2] = in[2];
e503ef07
PC
741 if (alpha)
742 out[3] = *alpha++;
feac7937 743 }
b5f01ae0 744 }
902725ee 745
feac7937
VS
746 return true;
747}
9dc44eff 748#endif
b5f01ae0 749
feac7937
VS
750wxImage wxBitmap::ConvertToImage() const
751{
9dc44eff
PC
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)
5ca21fe7 790 maskSurf = *bmpData->m_mask;
9dc44eff
PC
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
3d37a968 814 wxCHECK_MSG( IsOk(), wxNullImage, wxT("invalid bitmap") );
902725ee 815
afbfbfdf
PC
816 const int w = GetWidth();
817 const int h = GetHeight();
23656673 818 wxImage image(w, h, false);
feac7937 819 unsigned char *data = image.GetData();
2eefae6e 820
5588ce92 821 wxCHECK_MSG(data != NULL, wxNullImage, wxT("couldn't create image") );
b5f01ae0 822
70029506 823 // prefer pixbuf if available, it will preserve alpha and should be quicker
feac7937 824 if (HasPixbuf())
b5f01ae0 825 {
feac7937 826 GdkPixbuf *pixbuf = GetPixbuf();
70029506
PC
827 unsigned char* alpha = NULL;
828 if (gdk_pixbuf_get_has_alpha(pixbuf))
829 {
830 image.SetAlpha();
831 alpha = image.GetAlpha();
832 }
23656673 833 const unsigned char* in = gdk_pixbuf_get_pixels(pixbuf);
feac7937 834 unsigned char *out = data;
70029506 835 const int inc = 3 + int(alpha != NULL);
23656673 836 const int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - inc * w;
b5f01ae0 837
23656673 838 for (int y = 0; y < h; y++, in += rowpad)
feac7937 839 {
70029506 840 for (int x = 0; x < w; x++, in += inc, out += 3)
feac7937
VS
841 {
842 out[0] = in[0];
843 out[1] = in[1];
844 out[2] = in[2];
70029506
PC
845 if (alpha != NULL)
846 *alpha++ = in[3];
feac7937
VS
847 }
848 }
b5f01ae0 849 }
feac7937 850 else
b5f01ae0 851 {
afbfbfdf
PC
852 GdkPixmap* pixmap = GetPixmap();
853 GdkPixmap* pixmap_invert = NULL;
854 if (GetDepth() == 1)
b5f01ae0 855 {
d0e79755 856 // mono bitmaps are inverted, i.e. 0 is white
afbfbfdf 857 pixmap_invert = gdk_pixmap_new(pixmap, w, h, 1);
f3d74739 858 wxGtkObject<GdkGC> gc(gdk_gc_new(pixmap_invert));
afbfbfdf
PC
859 gdk_gc_set_function(gc, GDK_COPY_INVERT);
860 gdk_draw_drawable(pixmap_invert, gc, pixmap, 0, 0, 0, 0, w, h);
afbfbfdf 861 pixmap = pixmap_invert;
feac7937 862 }
afbfbfdf
PC
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);
feac7937 866
afbfbfdf 867 gdk_pixbuf_get_from_drawable(pixbuf, pixmap, NULL, 0, 0, 0, 0, w, h);
feac7937 868
afbfbfdf
PC
869 g_object_unref(pixbuf);
870 if (pixmap_invert != NULL)
871 g_object_unref(pixmap_invert);
70029506
PC
872 }
873 // convert mask, unless there is already alpha
874 if (GetMask() && !image.HasAlpha())
875 {
23656673
PC
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
70029506
PC
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;
feac7937 884
70029506 885 image.SetMaskColour(MASK_RED, MASK_GREEN, MASK_BLUE);
5ca21fe7 886 GdkImage* image_mask = gdk_drawable_get_image(*GetMask(), 0, 0, w, h);
feac7937 887
70029506
PC
888 for (int y = 0; y < h; y++)
889 {
890 for (int x = 0; x < w; x++, data += 3)
8ab696e0 891 {
70029506 892 if (gdk_image_get_pixel(image_mask, x, y) == 0)
8ab696e0 893 {
70029506
PC
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 {
23656673
PC
900 // we have to fudge the colour a bit to prevent
901 // this pixel from appearing transparent
70029506 902 data[2] = MASK_BLUE_REPLACEMENT;
feac7937 903 }
feac7937 904 }
b5f01ae0 905 }
70029506 906 g_object_unref(image_mask);
feac7937 907 }
9dc44eff 908#endif
b5f01ae0
VS
909
910 return image;
911}
912
c0521644
VZ
913#endif // wxUSE_IMAGE
914
91b8de8d 915int wxBitmap::GetHeight() const
c801d85f 916{
3d37a968 917 wxCHECK_MSG( IsOk(), -1, wxT("invalid bitmap") );
e55ad60e 918
fd0eed64 919 return M_BMPDATA->m_height;
ff7b1510 920}
c801d85f 921
91b8de8d 922int wxBitmap::GetWidth() const
c801d85f 923{
3d37a968 924 wxCHECK_MSG( IsOk(), -1, wxT("invalid bitmap") );
8bbe427f 925
fd0eed64 926 return M_BMPDATA->m_width;
ff7b1510 927}
c801d85f 928
91b8de8d 929int wxBitmap::GetDepth() const
c801d85f 930{
3d37a968 931 wxCHECK_MSG( IsOk(), -1, wxT("invalid bitmap") );
8bbe427f 932
fd0eed64 933 return M_BMPDATA->m_bpp;
ff7b1510 934}
c801d85f 935
91b8de8d 936wxMask *wxBitmap::GetMask() const
c801d85f 937{
d3b9f782 938 wxCHECK_MSG( IsOk(), NULL, wxT("invalid bitmap") );
8bbe427f 939
fd0eed64 940 return M_BMPDATA->m_mask;
ff7b1510 941}
c801d85f
KB
942
943void wxBitmap::SetMask( wxMask *mask )
944{
3d37a968 945 wxCHECK_RET( IsOk(), wxT("invalid bitmap") );
8bbe427f 946
e3e89a93 947 AllocExclusive();
23656673 948 delete M_BMPDATA->m_mask;
fd0eed64 949 M_BMPDATA->m_mask = mask;
ff7b1510 950}
c801d85f 951
db0aec83
VS
952bool wxBitmap::CopyFromIcon(const wxIcon& icon)
953{
954 *this = icon;
3d37a968 955 return IsOk();
db0aec83
VS
956}
957
9dc44eff
PC
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
17bec151
RR
978wxBitmap wxBitmap::GetSubBitmap( const wxRect& rect) const
979{
5588ce92
PC
980 wxBitmap ret;
981
3d37a968 982 wxCHECK_MSG(IsOk(), ret, wxT("invalid bitmap"));
06497cba
PC
983
984 const int w = rect.width;
985 const int h = rect.height;
986 const wxBitmapRefData* bmpData = M_BMPDATA;
987
23656673 988 wxCHECK_MSG(rect.x >= 0 && rect.y >= 0 &&
06497cba
PC
989 rect.x + w <= bmpData->m_width &&
990 rect.y + h <= bmpData->m_height,
23656673 991 ret, wxT("invalid bitmap region"));
13111b2a 992
fc684792 993 wxBitmapRefData * const newRef = new wxBitmapRefData(w, h, bmpData->m_bpp);
06497cba 994 ret.m_refData = newRef;
06497cba 995
9dc44eff
PC
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)
5ca21fe7 1009 maskSurf = *bmpData->m_mask;
9dc44eff
PC
1010 if (maskSurf)
1011 {
1012 newRef->m_mask = new wxMask(GetSubSurface(maskSurf, rect));
1013 }
1014#else
06497cba 1015 if (bmpData->m_pixbuf)
17bec151 1016 {
06497cba
PC
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);
17bec151 1021 }
06497cba 1022 if (bmpData->m_pixmap)
17bec151 1023 {
06497cba
PC
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);
17bec151 1029 }
2d13e22f
PC
1030 GdkPixmap* mask = NULL;
1031 if (bmpData->m_mask)
5ca21fe7 1032 mask = *bmpData->m_mask;
2d13e22f 1033 if (mask)
17bec151 1034 {
2d13e22f
PC
1035 GdkPixmap* sub_mask = gdk_pixmap_new(mask, w, h, 1);
1036 newRef->m_mask = new wxMask(sub_mask);
06497cba
PC
1037 GdkGC* gc = gdk_gc_new(sub_mask);
1038 gdk_draw_drawable(
2d13e22f 1039 sub_mask, gc, mask, rect.x, rect.y, 0, 0, w, h);
06497cba 1040 g_object_unref(gc);
17bec151 1041 }
9dc44eff 1042#endif
13111b2a 1043
17bec151
RR
1044 return ret;
1045}
1046
4611dd06 1047bool wxBitmap::SaveFile( const wxString &name, wxBitmapType type, const wxPalette *WXUNUSED(palette) ) const
c801d85f 1048{
3d37a968 1049 wxCHECK_MSG( IsOk(), false, wxT("invalid bitmap") );
8bbe427f 1050
c0521644 1051#if wxUSE_IMAGE
5588ce92 1052 wxImage image = ConvertToImage();
8362e67c
PC
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 &&
c282e47d 1066 gdk_pixbuf_save(GetPixbuf(), wxGTK_CONV_FN(name), type_name, NULL, NULL);
ff7b1510 1067}
c801d85f 1068
4611dd06 1069bool wxBitmap::LoadFile( const wxString &name, wxBitmapType type )
c801d85f 1070{
c0521644 1071#if wxUSE_IMAGE
8362e67c
PC
1072 wxImage image;
1073 if (image.LoadFile(name, type) && image.IsOk())
1074 *this = wxBitmap(image);
1075 else
1076#endif
fd0eed64 1077 {
357f4c81
VZ
1078 wxUnusedVar(type); // The type is detected automatically by GDK.
1079
c282e47d 1080 *this = wxBitmap(gdk_pixbuf_new_from_file(wxGTK_CONV_FN(name), NULL));
fd0eed64 1081 }
8bbe427f 1082
3d37a968 1083 return IsOk();
ff7b1510 1084}
8bbe427f 1085
0b04c4e0 1086#if wxUSE_PALETTE
91b8de8d 1087wxPalette *wxBitmap::GetPalette() const
c801d85f 1088{
b2bf3ac6 1089 return NULL;
ff7b1510 1090}
c801d85f 1091
4611dd06
RR
1092void wxBitmap::SetPalette(const wxPalette& WXUNUSED(palette))
1093{
1094 // TODO
1095}
0b04c4e0 1096#endif // wxUSE_PALETTE
4611dd06 1097
4bc67cc5
RR
1098void wxBitmap::SetHeight( int height )
1099{
e3e89a93 1100 AllocExclusive();
4bc67cc5
RR
1101 M_BMPDATA->m_height = height;
1102}
1103
1104void wxBitmap::SetWidth( int width )
1105{
e3e89a93 1106 AllocExclusive();
4bc67cc5
RR
1107 M_BMPDATA->m_width = width;
1108}
1109
1110void wxBitmap::SetDepth( int depth )
1111{
e3e89a93 1112 AllocExclusive();
4bc67cc5
RR
1113 M_BMPDATA->m_bpp = depth;
1114}
1115
9dc44eff 1116#ifndef __WXGTK3__
4bc67cc5
RR
1117void wxBitmap::SetPixmap( GdkPixmap *pixmap )
1118{
06497cba
PC
1119 UnRef();
1120
3d37a968
FM
1121 if (!pixmap)
1122 return;
1123
06497cba
PC
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);
4bc67cc5
RR
1130}
1131
91b8de8d 1132GdkPixmap *wxBitmap::GetPixmap() const
c801d85f 1133{
d3b9f782 1134 wxCHECK_MSG( IsOk(), NULL, wxT("invalid bitmap") );
8bbe427f 1135
06497cba
PC
1136 wxBitmapRefData* bmpData = M_BMPDATA;
1137 if (bmpData->m_pixmap)
1138 return bmpData->m_pixmap;
1139
1140 if (bmpData->m_pixbuf)
feac7937 1141 {
2d13e22f 1142 GdkPixmap* pixmap = NULL;
06497cba
PC
1143 GdkPixmap** mask_pixmap = NULL;
1144 if (gdk_pixbuf_get_has_alpha(bmpData->m_pixbuf))
70029506
PC
1145 {
1146 // make new mask from alpha
2d13e22f 1147 mask_pixmap = &pixmap;
70029506 1148 }
06497cba
PC
1149 gdk_pixbuf_render_pixmap_and_mask(
1150 bmpData->m_pixbuf, &bmpData->m_pixmap, mask_pixmap, 128);
2d13e22f
PC
1151 if (pixmap)
1152 {
1153 delete bmpData->m_mask;
1154 bmpData->m_mask = new wxMask(pixmap);
1155 }
feac7937 1156 }
06497cba
PC
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;
ff7b1510 1163}
8bbe427f 1164
feac7937
VS
1165bool wxBitmap::HasPixmap() const
1166{
3d37a968 1167 wxCHECK_MSG( IsOk(), false, wxT("invalid bitmap") );
feac7937
VS
1168
1169 return M_BMPDATA->m_pixmap != NULL;
1170}
9dc44eff
PC
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)
5ca21fe7 1304 mask = *bmpData->m_mask;
9dc44eff
PC
1305 if (mask)
1306 cairo_mask_surface(cr, mask, x, y);
1307 else
1308 cairo_paint(cr);
1309}
1310#endif
feac7937 1311
feac7937
VS
1312GdkPixbuf *wxBitmap::GetPixbuf() const
1313{
3d37a968 1314 wxCHECK_MSG( IsOk(), NULL, wxT("invalid bitmap") );
feac7937 1315
06497cba 1316 wxBitmapRefData* bmpData = M_BMPDATA;
9dc44eff
PC
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)
5ca21fe7 1325 mask = *bmpData->m_mask;
9dc44eff
PC
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
06497cba
PC
1350 if (bmpData->m_pixbuf)
1351 return bmpData->m_pixbuf;
6db34764 1352
06497cba
PC
1353 const int w = bmpData->m_width;
1354 const int h = bmpData->m_height;
1355 GdkPixmap* mask = NULL;
1356 if (bmpData->m_mask)
5ca21fe7 1357 mask = *bmpData->m_mask;
06497cba
PC
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;
9dc44eff 1365#endif
feac7937
VS
1366}
1367
9dc44eff 1368#ifndef __WXGTK3__
feac7937
VS
1369bool wxBitmap::HasPixbuf() const
1370{
3d37a968 1371 wxCHECK_MSG( IsOk(), false, wxT("invalid bitmap") );
feac7937
VS
1372
1373 return M_BMPDATA->m_pixbuf != NULL;
1374}
1375
feac7937
VS
1376void wxBitmap::PurgeOtherRepresentations(wxBitmap::Representation keep)
1377{
1378 if (keep == Pixmap && HasPixbuf())
1379 {
3fe39b0c 1380 g_object_unref (M_BMPDATA->m_pixbuf);
feac7937
VS
1381 M_BMPDATA->m_pixbuf = NULL;
1382 }
1383 if (keep == Pixbuf && HasPixmap())
1384 {
3fe39b0c 1385 g_object_unref (M_BMPDATA->m_pixmap);
feac7937
VS
1386 M_BMPDATA->m_pixmap = NULL;
1387 }
1388}
9dc44eff 1389#endif
feac7937 1390
ce7c8a97 1391#ifdef wxHAS_RAW_BITMAP
284f2b59
RR
1392void *wxBitmap::GetRawData(wxPixelDataBase& data, int bpp)
1393{
70029506 1394 void* bits = NULL;
9dc44eff
PC
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
284f2b59 1416 GdkPixbuf *pixbuf = GetPixbuf();
70029506 1417 const bool hasAlpha = HasAlpha();
3d37a968 1418
70029506 1419 // allow access if bpp is valid and matches existence of alpha
c74fc1e1 1420 if ( pixbuf && ((bpp == 24 && !hasAlpha) || (bpp == 32 && hasAlpha)) )
70029506
PC
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 }
9dc44eff 1427#endif
70029506 1428 return bits;
284f2b59
RR
1429}
1430
17a1ebd1 1431void wxBitmap::UngetRawData(wxPixelDataBase& WXUNUSED(data))
284f2b59
RR
1432{
1433}
ce7c8a97 1434#endif // wxHAS_RAW_BITMAP
284f2b59 1435
902725ee 1436bool wxBitmap::HasAlpha() const
0ff2a74d 1437{
06497cba 1438 const wxBitmapRefData* bmpData = M_BMPDATA;
9dc44eff
PC
1439#ifdef __WXGTK3__
1440 return bmpData && bmpData->m_bpp == 32;
1441#else
06497cba
PC
1442 return bmpData && (bmpData->m_alphaRequested ||
1443 (bmpData->m_pixbuf && gdk_pixbuf_get_has_alpha(bmpData->m_pixbuf)));
9dc44eff 1444#endif
0ff2a74d
RR
1445}
1446
8f884a0d 1447wxGDIRefData* wxBitmap::CreateGDIRefData() const
e3e89a93 1448{
06497cba 1449 return new wxBitmapRefData(0, 0, 0);
e3e89a93
PC
1450}
1451
8f884a0d 1452wxGDIRefData* wxBitmap::CloneGDIRefData(const wxGDIRefData* data) const
e3e89a93 1453{
5c33522f 1454 const wxBitmapRefData* oldRef = static_cast<const wxBitmapRefData*>(data);
fc684792
VZ
1455 wxBitmapRefData * const newRef = new wxBitmapRefData(oldRef->m_width,
1456 oldRef->m_height,
1457 oldRef->m_bpp);
9dc44eff
PC
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
e3e89a93
PC
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));
f3d74739 1483 wxGtkObject<GdkGC> gc(gdk_gc_new(newRef->m_pixmap));
e3e89a93
PC
1484 gdk_draw_drawable(
1485 newRef->m_pixmap, gc, oldRef->m_pixmap, 0, 0, 0, 0, -1, -1);
e3e89a93
PC
1486 }
1487 if (oldRef->m_pixbuf != NULL)
1488 {
1489 newRef->m_pixbuf = gdk_pixbuf_copy(oldRef->m_pixbuf);
1490 }
9dc44eff 1491#endif
e3e89a93
PC
1492 if (oldRef->m_mask != NULL)
1493 {
27297c82 1494 newRef->m_mask = new wxMask(*oldRef->m_mask);
e3e89a93 1495 }
e3e89a93
PC
1496
1497 return newRef;
1498}
1499
4b61c88d
RR
1500/* static */ void wxBitmap::InitStandardHandlers()
1501{
1502 // TODO: Insert handler based on GdkPixbufs handler later
1503}