]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/bitmap.cpp
Fix wxHtmlHelpData::SetTempDir() to behave correctly without trailing slash.
[wxWidgets.git] / src / gtk / bitmap.cpp
CommitLineData
c801d85f 1/////////////////////////////////////////////////////////////////////////////
0b04c4e0 2// Name: src/gtk/bitmap.cpp
c801d85f
KB
3// Purpose:
4// Author: Robert Roebling
01111366 5// Copyright: (c) 1998 Robert Roebling
65571936 6// Licence: wxWindows licence
c801d85f
KB
7/////////////////////////////////////////////////////////////////////////////
8
14f355c2
VS
9// For compilers that support precompilation, includes "wx.h".
10#include "wx/wxprec.h"
11
559a723c
WS
12#include "wx/bitmap.h"
13
93763ad5 14#ifndef WX_PRECOMP
923d28da 15 #include "wx/icon.h"
155ecd4c 16 #include "wx/image.h"
5ff14574 17 #include "wx/colour.h"
93763ad5 18#endif
22bd9387 19
ce7c8a97 20#include "wx/rawbmp.h"
9e691f46 21
f3d74739 22#include "wx/gtk/private/object.h"
c282e47d 23#include "wx/gtk/private.h"
f3d74739 24
d76fe38b 25#include <gtk/gtk.h>
13111b2a 26
c2fa61e8 27extern GtkWidget *wxGetRootWindow();
c801d85f 28
9dc44eff 29#ifndef __WXGTK3__
06497cba
PC
30static void PixmapToPixbuf(GdkPixmap* pixmap, GdkPixbuf* pixbuf, int w, int h)
31{
32 gdk_pixbuf_get_from_drawable(pixbuf, pixmap, NULL, 0, 0, 0, 0, w, h);
33 if (gdk_drawable_get_depth(pixmap) == 1)
34 {
35 // invert to match XBM convention
36 guchar* p = gdk_pixbuf_get_pixels(pixbuf);
37 const int inc = 3 + int(gdk_pixbuf_get_has_alpha(pixbuf) != 0);
38 const int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - w * inc;
39 for (int y = h; y; y--, p += rowpad)
40 for (int x = w; x; x--, p += inc)
41 {
42 // pixels are either (0,0,0) or (0xff,0xff,0xff)
43 p[0] = ~p[0];
44 p[1] = ~p[1];
45 p[2] = ~p[2];
46 }
47 }
48}
49
50static void MaskToAlpha(GdkPixmap* mask, GdkPixbuf* pixbuf, int w, int h)
51{
52 GdkPixbuf* mask_pixbuf = gdk_pixbuf_get_from_drawable(
53 NULL, mask, NULL, 0, 0, 0, 0, w, h);
54 guchar* p = gdk_pixbuf_get_pixels(pixbuf) + 3;
55 const guchar* mask_data = gdk_pixbuf_get_pixels(mask_pixbuf);
56 const int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - w * 4;
57 const int mask_rowpad = gdk_pixbuf_get_rowstride(mask_pixbuf) - w * 3;
58 for (int y = h; y; y--, p += rowpad, mask_data += mask_rowpad)
59 {
60 for (int x = w; x; x--, p += 4, mask_data += 3)
61 {
62 *p = 255;
63 // no need to test all 3 components,
64 // pixels are either (0,0,0) or (0xff,0xff,0xff)
65 if (mask_data[0] == 0)
66 *p = 0;
67 }
68 }
69 g_object_unref(mask_pixbuf);
70}
9dc44eff 71#endif
06497cba 72
c801d85f
KB
73//-----------------------------------------------------------------------------
74// wxMask
75//-----------------------------------------------------------------------------
76
60a3d1c6 77IMPLEMENT_DYNAMIC_CLASS(wxMask, wxMaskBase)
c801d85f 78
8bbe427f 79wxMask::wxMask()
c801d85f 80{
d3b9f782 81 m_bitmap = NULL;
ff7b1510 82}
c801d85f 83
27297c82
VZ
84wxMask::wxMask(const wxMask& mask)
85{
9dc44eff
PC
86#ifdef __WXGTK3__
87 m_bitmap = NULL;
88 if (mask.m_bitmap)
89 {
90 const int w = cairo_image_surface_get_width(mask.m_bitmap);
91 const int h = cairo_image_surface_get_height(mask.m_bitmap);
92 m_bitmap = cairo_image_surface_create(CAIRO_FORMAT_A8, w, h);
93 const guchar* src = cairo_image_surface_get_data(mask.m_bitmap);
94 guchar* dst = cairo_image_surface_get_data(m_bitmap);
95 const int stride = cairo_image_surface_get_stride(m_bitmap);
96 wxASSERT(stride == cairo_image_surface_get_stride(mask.m_bitmap));
97 memcpy(dst, src, stride * h);
98 cairo_surface_mark_dirty(m_bitmap);
99 }
100#else
27297c82
VZ
101 if ( !mask.m_bitmap )
102 {
103 m_bitmap = NULL;
104 return;
105 }
106
107 // create a copy of an existing mask
108 gint w, h;
109 gdk_drawable_get_size(mask.m_bitmap, &w, &h);
110 m_bitmap = gdk_pixmap_new(mask.m_bitmap, w, h, 1);
111
112 wxGtkObject<GdkGC> gc(gdk_gc_new(m_bitmap));
113 gdk_draw_drawable(m_bitmap, gc, mask.m_bitmap, 0, 0, 0, 0, -1, -1);
9dc44eff 114#endif
27297c82
VZ
115}
116
91b8de8d 117wxMask::wxMask( const wxBitmap& bitmap, const wxColour& colour )
c801d85f 118{
d3b9f782 119 m_bitmap = NULL;
60a3d1c6 120 InitFromColour(bitmap, colour);
ff7b1510 121}
c801d85f 122
0b04c4e0 123#if wxUSE_PALETTE
91b8de8d 124wxMask::wxMask( const wxBitmap& bitmap, int paletteIndex )
c801d85f 125{
d3b9f782 126 m_bitmap = NULL;
91b8de8d 127 Create( bitmap, paletteIndex );
ff7b1510 128}
0b04c4e0 129#endif // wxUSE_PALETTE
c801d85f 130
91b8de8d 131wxMask::wxMask( const wxBitmap& bitmap )
c801d85f 132{
d3b9f782 133 m_bitmap = NULL;
60a3d1c6 134 InitFromMonoBitmap(bitmap);
ff7b1510 135}
c801d85f 136
9dc44eff
PC
137#ifdef __WXGTK3__
138wxMask::wxMask(cairo_surface_t* bitmap)
139#else
2d13e22f 140wxMask::wxMask(GdkPixmap* bitmap)
9dc44eff 141#endif
2d13e22f
PC
142{
143 m_bitmap = bitmap;
144}
145
8bbe427f 146wxMask::~wxMask()
c801d85f 147{
13111b2a 148 if (m_bitmap)
9dc44eff
PC
149 {
150#ifdef __WXGTK3__
151 cairo_surface_destroy(m_bitmap);
152#else
3fe39b0c 153 g_object_unref (m_bitmap);
9dc44eff
PC
154#endif
155 }
ff7b1510 156}
c801d85f 157
60a3d1c6 158void wxMask::FreeData()
91b8de8d
RR
159{
160 if (m_bitmap)
284b4c88 161 {
9dc44eff
PC
162#ifdef __WXGTK3__
163 cairo_surface_destroy(m_bitmap);
164#else
3fe39b0c 165 g_object_unref (m_bitmap);
9dc44eff 166#endif
d3b9f782 167 m_bitmap = NULL;
91b8de8d 168 }
60a3d1c6 169}
13111b2a 170
60a3d1c6
PC
171bool wxMask::InitFromColour(const wxBitmap& bitmap, const wxColour& colour)
172{
13785a4b
PC
173 const int w = bitmap.GetWidth();
174 const int h = bitmap.GetHeight();
13111b2a 175
9dc44eff
PC
176#ifdef __WXGTK3__
177 m_bitmap = cairo_image_surface_create(CAIRO_FORMAT_A8, w, h);
178 GdkPixbuf* pixbuf = bitmap.GetPixbufNoMask();
179 const guchar* src = gdk_pixbuf_get_pixels(pixbuf);
180 guchar* dst = cairo_image_surface_get_data(m_bitmap);
181 const int stride_src = gdk_pixbuf_get_rowstride(pixbuf);
182 const int stride_dst = cairo_image_surface_get_stride(m_bitmap);
183 const int src_inc = gdk_pixbuf_get_n_channels(pixbuf);
184 const guchar r = colour.Red();
185 const guchar g = colour.Green();
186 const guchar b = colour.Blue();
187 for (int j = 0; j < h; j++, src += stride_src, dst += stride_dst)
188 {
189 const guchar* s = src;
190 for (int i = 0; i < w; i++, s += src_inc)
191 {
192 dst[i] = 0xff;
193 if (s[0] == r && s[1] == g && s[2] == b)
194 dst[i] = 0;
195 }
196 }
197 cairo_surface_mark_dirty(m_bitmap);
198#else
5ac1d44a 199 // create mask as XBM format bitmap
13111b2a 200
5ac1d44a
PC
201 // one bit per pixel, each row starts on a byte boundary
202 const size_t out_size = size_t((w + 7) / 8) * unsigned(h);
203 wxByte* out = new wxByte[out_size];
d0e79755 204 // set bits are unmasked
5ac1d44a
PC
205 memset(out, 0xff, out_size);
206 unsigned bit_index = 0;
13785a4b 207 if (bitmap.HasPixbuf())
1fb4de31 208 {
5ac1d44a
PC
209 const wxByte r_mask = colour.Red();
210 const wxByte g_mask = colour.Green();
211 const wxByte b_mask = colour.Blue();
13785a4b 212 GdkPixbuf* pixbuf = bitmap.GetPixbuf();
5ac1d44a
PC
213 const wxByte* in = gdk_pixbuf_get_pixels(pixbuf);
214 const int inc = 3 + int(gdk_pixbuf_get_has_alpha(pixbuf) != 0);
215 const int rowpadding = gdk_pixbuf_get_rowstride(pixbuf) - inc * w;
216 for (int y = 0; y < h; y++, in += rowpadding)
217 {
218 for (int x = 0; x < w; x++, in += inc, bit_index++)
219 if (in[0] == r_mask && in[1] == g_mask && in[2] == b_mask)
220 out[bit_index >> 3] ^= 1 << (bit_index & 7);
221 // move index to next byte boundary
222 bit_index = (bit_index + 7) & ~7u;
223 }
2eefae6e 224 }
13785a4b 225 else
1fb4de31 226 {
5ac1d44a 227 GdkImage* image = gdk_drawable_get_image(bitmap.GetPixmap(), 0, 0, w, h);
13785a4b 228 GdkColormap* colormap = gdk_image_get_colormap(image);
a7cfe08a
PC
229 guint32 mask_pixel;
230 if (colormap == NULL)
231 // mono bitmap, white is pixel value 0
232 mask_pixel = guint32(colour.Red() != 255 || colour.Green() != 255 || colour.Blue() != 255);
233 else
13785a4b
PC
234 {
235 wxColor c(colour);
236 c.CalcPixel(colormap);
237 mask_pixel = c.GetPixel();
238 }
5ac1d44a 239 for (int y = 0; y < h; y++)
1fb4de31 240 {
5ac1d44a
PC
241 for (int x = 0; x < w; x++, bit_index++)
242 if (gdk_image_get_pixel(image, x, y) == mask_pixel)
243 out[bit_index >> 3] ^= 1 << (bit_index & 7);
244 bit_index = (bit_index + 7) & ~7u;
f9ee644e 245 }
13785a4b 246 g_object_unref(image);
5ac1d44a
PC
247 }
248 m_bitmap = gdk_bitmap_create_from_data(wxGetRootWindow()->window, (char*)out, w, h);
249 delete[] out;
9dc44eff 250#endif
902725ee 251 return true;
91b8de8d
RR
252}
253
60a3d1c6 254bool wxMask::InitFromMonoBitmap(const wxBitmap& bitmap)
91b8de8d 255{
3d37a968 256 if (!bitmap.IsOk()) return false;
284b4c88 257
b85229d1 258 wxCHECK_MSG( bitmap.GetDepth() == 1, false, wxT("Cannot create mask from colour bitmap") );
284b4c88 259
9dc44eff
PC
260#ifdef __WXGTK3__
261 InitFromColour(bitmap, *wxBLACK);
262#else
c2fa61e8 263 m_bitmap = gdk_pixmap_new( wxGetRootWindow()->window, bitmap.GetWidth(), bitmap.GetHeight(), 1 );
284b4c88 264
902725ee 265 if (!m_bitmap) return false;
284b4c88 266
f3d74739 267 wxGtkObject<GdkGC> gc(gdk_gc_new( m_bitmap ));
9be3f755
PC
268 gdk_gc_set_function(gc, GDK_COPY_INVERT);
269 gdk_draw_drawable(m_bitmap, gc, bitmap.GetPixmap(), 0, 0, 0, 0, bitmap.GetWidth(), bitmap.GetHeight());
9dc44eff 270#endif
284b4c88 271
902725ee 272 return true;
91b8de8d
RR
273}
274
feabb2eb
PC
275wxBitmap wxMask::GetBitmap() const
276{
277 wxBitmap bitmap;
278 if (m_bitmap)
279 {
280#ifdef __WXGTK3__
281 cairo_surface_t* mask = m_bitmap;
282 const int w = cairo_image_surface_get_width(mask);
283 const int h = cairo_image_surface_get_height(mask);
284 GdkPixbuf* pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, false, 8, w, h);
285 const guchar* src = cairo_image_surface_get_data(mask);
286 guchar* dst = gdk_pixbuf_get_pixels(pixbuf);
287 const int stride_src = cairo_image_surface_get_stride(mask);
288 const int stride_dst = gdk_pixbuf_get_rowstride(pixbuf);
289 for (int j = 0; j < h; j++, src += stride_src, dst += stride_dst)
290 {
291 guchar* d = dst;
292 for (int i = 0; i < w; i++, d += 3)
293 {
294 d[0] = src[i];
295 d[1] = src[i];
296 d[2] = src[i];
297 }
298 }
299 bitmap = wxBitmap(pixbuf, 1);
300#else
301 GdkPixmap* mask = m_bitmap;
302 int w, h;
303 gdk_drawable_get_size(mask, &w, &h);
304 GdkPixmap* pixmap = gdk_pixmap_new(mask, w, h, -1);
305 GdkGC* gc = gdk_gc_new(pixmap);
306 gdk_gc_set_function(gc, GDK_COPY_INVERT);
307 gdk_draw_drawable(pixmap, gc, mask, 0, 0, 0, 0, w, h);
308 g_object_unref(gc);
309 bitmap = wxBitmap(pixmap);
310#endif
311 }
312 return bitmap;
313}
314
9dc44eff 315#ifdef __WXGTK3__
5ca21fe7 316wxMask::operator cairo_surface_t*() const
9dc44eff 317#else
5ca21fe7 318wxMask::operator GdkPixmap*() const
9dc44eff 319#endif
c801d85f 320{
fd0eed64 321 return m_bitmap;
ff7b1510 322}
8bbe427f 323
c801d85f 324//-----------------------------------------------------------------------------
732d8c74 325// wxBitmapRefData
c801d85f
KB
326//-----------------------------------------------------------------------------
327
8f884a0d 328class wxBitmapRefData: public wxGDIRefData
c801d85f 329{
fd0eed64 330public:
06497cba 331 wxBitmapRefData(int width, int height, int depth);
d3c7fc99 332 virtual ~wxBitmapRefData();
f2593d0d 333
06497cba 334 virtual bool IsOk() const;
8f884a0d 335
9dc44eff
PC
336#ifdef __WXGTK3__
337 GdkPixbuf* m_pixbufMask;
338 GdkPixbuf* m_pixbufNoMask;
339 cairo_surface_t* m_surface;
340#else
f2593d0d 341 GdkPixmap *m_pixmap;
feac7937 342 GdkPixbuf *m_pixbuf;
9dc44eff 343#endif
f2593d0d
RR
344 wxMask *m_mask;
345 int m_width;
346 int m_height;
347 int m_bpp;
9dc44eff 348#ifndef __WXGTK3__
06497cba 349 bool m_alphaRequested;
9dc44eff 350#endif
fc684792 351
fc684792
VZ
352 // We don't provide a copy ctor as copying m_pixmap and m_pixbuf properly
353 // is expensive and we don't want to do it implicitly (and possibly
354 // accidentally). wxBitmap::CloneGDIRefData() which does need to do it does
355 // it explicitly itself.
356 wxDECLARE_NO_COPY_CLASS(wxBitmapRefData);
c801d85f
KB
357};
358
06497cba 359wxBitmapRefData::wxBitmapRefData(int width, int height, int depth)
c801d85f 360{
9dc44eff
PC
361#ifdef __WXGTK3__
362 m_pixbufMask = NULL;
363 m_pixbufNoMask = NULL;
364 m_surface = NULL;
365#else
d3b9f782
VZ
366 m_pixmap = NULL;
367 m_pixbuf = NULL;
9dc44eff 368#endif
d3b9f782 369 m_mask = NULL;
06497cba
PC
370 m_width = width;
371 m_height = height;
372 m_bpp = depth;
9dc44eff
PC
373#ifdef __WXGTK3__
374 if (m_bpp != 1 && m_bpp != 32)
375 m_bpp = 24;
376#else
06497cba
PC
377 if (m_bpp < 0)
378 m_bpp = gdk_drawable_get_depth(wxGetRootWindow()->window);
379 m_alphaRequested = depth == 32;
9dc44eff 380#endif
ff7b1510 381}
c801d85f 382
8bbe427f 383wxBitmapRefData::~wxBitmapRefData()
c801d85f 384{
9dc44eff
PC
385#ifdef __WXGTK3__
386 if (m_pixbufMask)
387 g_object_unref(m_pixbufMask);
388 if (m_pixbufNoMask)
389 g_object_unref(m_pixbufNoMask);
390 if (m_surface)
391 cairo_surface_destroy(m_surface);
392#else
3ebcd89d 393 if (m_pixmap)
3fe39b0c 394 g_object_unref (m_pixmap);
feac7937 395 if (m_pixbuf)
3fe39b0c 396 g_object_unref (m_pixbuf);
9dc44eff 397#endif
3ebcd89d 398 delete m_mask;
ff7b1510 399}
c801d85f 400
06497cba
PC
401bool wxBitmapRefData::IsOk() const
402{
403 return m_bpp != 0;
404}
732d8c74
FM
405
406//-----------------------------------------------------------------------------
407// wxBitmap
c801d85f
KB
408//-----------------------------------------------------------------------------
409
5c33522f 410#define M_BMPDATA static_cast<wxBitmapRefData*>(m_refData)
c801d85f
KB
411
412IMPLEMENT_DYNAMIC_CLASS(wxBitmap,wxGDIObject)
413
23656673
PC
414wxBitmap::wxBitmap(const wxString &filename, wxBitmapType type)
415{
416 LoadFile(filename, type);
417}
418
419wxBitmap::wxBitmap(const char bits[], int width, int height, int depth)
c801d85f 420{
23656673
PC
421 wxASSERT(depth == 1);
422 if (width > 0 && height > 0 && depth == 1)
423 {
9dc44eff
PC
424 m_refData = new wxBitmapRefData(width, height, 1);
425#ifdef __WXGTK3__
426 GdkPixbuf* pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, false, 8, width, height);
427 M_BMPDATA->m_pixbufNoMask = pixbuf;
428 const char* src = bits;
429 guchar* dst = gdk_pixbuf_get_pixels(pixbuf);
430 const int stride_src = (width + 7) / 8;
431 const int rowinc_dst = gdk_pixbuf_get_rowstride(pixbuf) - 3 * width;
432 for (int j = 0; j < width; j++, src += stride_src, dst += rowinc_dst)
433 {
434 for (int i = 0; i < height; i++)
435 {
436 guchar c = 0xff;
437 if (src[i >> 3] & (1 << (i & 7)))
438 c = 0;
439 *dst++ = c;
440 *dst++ = c;
441 *dst++ = c;
442 }
443 }
444#else
445 M_BMPDATA->m_pixmap = gdk_bitmap_create_from_data(
446 wxGetRootWindow()->window, bits, width, height);
447#endif
23656673 448 }
ff7b1510 449}
8bbe427f 450
452418c4
PC
451wxBitmap::wxBitmap(const char* const* bits)
452{
453 wxCHECK2_MSG(bits != NULL, return, wxT("invalid bitmap data"));
454
9dc44eff
PC
455#if wxUSE_IMAGE
456 *this = wxBitmap(wxImage(bits));
457#elif defined __WXGTK3__
458 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(const_cast<const char**>(bits));
459 if (pixbuf)
460 {
461 m_refData = new wxBitmapRefData(
462 gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf),
463 gdk_pixbuf_get_n_channels(pixbuf) * 8);
464 M_BMPDATA->m_pixbufNoMask = pixbuf;
465 wxASSERT(M_BMPDATA->m_bpp == 32 || !gdk_pixbuf_get_has_alpha(M_BMPDATA->m_pixbufNoMask));
466 }
467#else
452418c4 468 GdkBitmap* mask = NULL;
9dc44eff
PC
469 GdkPixmap* pixmap = gdk_pixmap_create_from_xpm_d(wxGetRootWindow()->window, &mask, NULL, const_cast<char**>(bits));
470 if (pixmap)
452418c4 471 {
9dc44eff
PC
472 int width, height;
473 gdk_drawable_get_size(pixmap, &width, &height);
474 m_refData = new wxBitmapRefData(width, height, -1);
475 M_BMPDATA->m_pixmap = pixmap;
476 if (mask)
477 {
478 M_BMPDATA->m_mask = new wxMask(mask);
479 }
452418c4 480 }
9dc44eff 481#endif
452418c4
PC
482}
483
5ca21fe7 484wxBitmap::wxBitmap(GdkPixbuf* pixbuf, int depth)
54195d23
PC
485{
486 if (pixbuf)
487 {
5ca21fe7
PC
488 if (depth != 1)
489 depth = gdk_pixbuf_get_n_channels(pixbuf) * 8;
54195d23
PC
490 wxBitmapRefData* bmpData = new wxBitmapRefData(
491 gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf),
5ca21fe7 492 depth);
54195d23 493 m_refData = bmpData;
9dc44eff
PC
494#ifdef __WXGTK3__
495 bmpData->m_pixbufNoMask = pixbuf;
496#else
54195d23 497 bmpData->m_pixbuf = pixbuf;
9dc44eff 498#endif
54195d23
PC
499 }
500}
501
5ca21fe7
PC
502#ifndef __WXGTK3__
503wxBitmap::wxBitmap(GdkPixmap* pixmap)
504{
505 if (pixmap)
506 {
507 int w, h;
508 gdk_drawable_get_size(pixmap, &w, &h);
509 wxBitmapRefData* bmpData =
510 new wxBitmapRefData(w, h, gdk_drawable_get_depth(pixmap));
511 m_refData = bmpData;
512 bmpData->m_pixmap = pixmap;
513 }
514}
515#endif
516
23656673 517wxBitmap::~wxBitmap()
c801d85f 518{
c826213d
RR
519}
520
521bool wxBitmap::Create( int width, int height, int depth )
522{
523 UnRef();
06497cba
PC
524 wxCHECK_MSG(width >= 0 && height >= 0, false, "invalid bitmap size");
525 m_refData = new wxBitmapRefData(width, height, depth);
526 return true;
ff7b1510 527}
b5f01ae0 528
9dc44eff
PC
529#ifdef __WXGTK3__
530static void CopyImageData(
531 guchar* dst, int dstChannels, int dstStride,
532 const guchar* src, int srcChannels, int srcStride,
533 int w, int h)
534{
535 if (dstChannels == srcChannels)
536 {
537 if (dstStride == srcStride)
538 memcpy(dst, src, size_t(dstStride) * h);
539 else
540 {
541 const int stride = dstStride < srcStride ? dstStride : srcStride;
542 for (int j = 0; j < h; j++, src += srcStride, dst += dstStride)
543 memcpy(dst, src, stride);
544 }
545 }
546 else
547 {
548 for (int j = 0; j < h; j++, src += srcStride, dst += dstStride)
549 {
550 guchar* d = dst;
551 const guchar* s = src;
552 if (dstChannels == 4)
553 {
554 for (int i = 0; i < w; i++, d += 4, s += 3)
555 {
556 d[0] = s[0];
557 d[1] = s[1];
558 d[2] = s[2];
559 d[3] = 0xff;
560 }
561 }
562 else
563 {
564 for (int i = 0; i < w; i++, d += 3, s += 4)
565 {
566 d[0] = s[0];
567 d[1] = s[1];
568 d[2] = s[2];
569 }
570 }
571 }
572 }
573}
574#endif
c0521644 575
9dc44eff
PC
576#if wxUSE_IMAGE
577#ifdef __WXGTK3__
578wxBitmap::wxBitmap(const wxImage& image, int depth)
b5f01ae0 579{
9dc44eff
PC
580 wxCHECK_RET(image.IsOk(), "invalid image");
581
582 const int w = image.GetWidth();
583 const int h = image.GetHeight();
584 const guchar* alpha = image.GetAlpha();
585 if (depth < 0)
586 depth = alpha ? 32 : 24;
587 else if (depth != 1 && depth != 32)
588 depth = 24;
589 wxBitmapRefData* bmpData = new wxBitmapRefData(w, h, depth);
590 m_refData = bmpData;
591 GdkPixbuf* pixbuf_dst = gdk_pixbuf_new(GDK_COLORSPACE_RGB, depth == 32, 8, w, h);
592 bmpData->m_pixbufNoMask = pixbuf_dst;
593 wxASSERT(bmpData->m_bpp == 32 || !gdk_pixbuf_get_has_alpha(bmpData->m_pixbufNoMask));
594 const guchar* src = image.GetData();
a11672a4 595
9dc44eff
PC
596 guchar* dst = gdk_pixbuf_get_pixels(pixbuf_dst);
597 const int dstStride = gdk_pixbuf_get_rowstride(pixbuf_dst);
598 CopyImageData(dst, gdk_pixbuf_get_n_channels(pixbuf_dst), dstStride, src, 3, 3 * w, w, h);
b5f01ae0 599
9dc44eff
PC
600 if (depth == 32 && alpha)
601 {
602 for (int j = 0; j < h; j++, dst += dstStride)
603 for (int i = 0; i < w; i++)
604 dst[i * 4 + 3] = *alpha++;
605 }
606 if (image.HasMask())
607 {
608 const guchar r = image.GetMaskRed();
609 const guchar g = image.GetMaskGreen();
610 const guchar b = image.GetMaskBlue();
611 cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_A8, w, h);
612 const int stride = cairo_image_surface_get_stride(surface);
613 dst = cairo_image_surface_get_data(surface);
614 memset(dst, 0xff, stride * h);
615 for (int j = 0; j < h; j++, dst += stride)
616 for (int i = 0; i < w; i++, src += 3)
617 if (src[0] == r && src[1] == g && src[2] == b)
618 dst[i] = 0;
619 cairo_surface_mark_dirty(surface);
620 bmpData->m_mask = new wxMask(surface);
621 }
622}
623#else
624wxBitmap::wxBitmap(const wxImage& image, int depth)
625{
626 wxCHECK_RET(image.IsOk(), "invalid image");
902725ee 627
e503ef07 628 if (depth == 32 || (depth == -1 && image.HasAlpha()))
9dc44eff
PC
629 CreateFromImageAsPixbuf(image);
630 else
631 // otherwise create pixmap, if alpha is present it will be converted to mask
632 CreateFromImageAsPixmap(image, depth);
feac7937 633}
3ebcd89d 634
5ac1d44a 635bool wxBitmap::CreateFromImageAsPixmap(const wxImage& image, int depth)
feac7937 636{
5ac1d44a
PC
637 const int w = image.GetWidth();
638 const int h = image.GetHeight();
639 if (depth == 1)
feac7937 640 {
5ac1d44a
PC
641 // create XBM format bitmap
642
643 // one bit per pixel, each row starts on a byte boundary
644 const size_t out_size = size_t((w + 7) / 8) * unsigned(h);
645 wxByte* out = new wxByte[out_size];
d0e79755 646 // set bits are black
5ac1d44a
PC
647 memset(out, 0xff, out_size);
648 const wxByte* in = image.GetData();
649 unsigned bit_index = 0;
650 for (int y = 0; y < h; y++)
651 {
652 for (int x = 0; x < w; x++, in += 3, bit_index++)
653 if (in[0] == 255 && in[1] == 255 && in[2] == 255)
654 out[bit_index >> 3] ^= 1 << (bit_index & 7);
655 // move index to next byte boundary
656 bit_index = (bit_index + 7) & ~7u;
657 }
658 SetPixmap(gdk_bitmap_create_from_data(wxGetRootWindow()->window, (char*)out, w, h));
659 delete[] out;
3d37a968
FM
660
661 if (!M_BMPDATA) // SetPixmap may have failed
662 return false;
5ac1d44a
PC
663 }
664 else
665 {
666 SetPixmap(gdk_pixmap_new(wxGetRootWindow()->window, w, h, depth));
3d37a968
FM
667 if (!M_BMPDATA)
668 return false;
669
f3d74739 670 wxGtkObject<GdkGC> gc(gdk_gc_new(M_BMPDATA->m_pixmap));
5ac1d44a
PC
671 gdk_draw_rgb_image(
672 M_BMPDATA->m_pixmap, gc,
673 0, 0, w, h,
674 GDK_RGB_DITHER_NONE, image.GetData(), w * 3);
feac7937 675 }
b5f01ae0 676
5ac1d44a
PC
677 const wxByte* alpha = image.GetAlpha();
678 if (alpha != NULL || image.HasMask())
feac7937 679 {
5ac1d44a
PC
680 // create mask as XBM format bitmap
681
682 const size_t out_size = size_t((w + 7) / 8) * unsigned(h);
683 wxByte* out = new wxByte[out_size];
684 memset(out, 0xff, out_size);
685 unsigned bit_index = 0;
686 if (alpha != NULL)
b5f01ae0 687 {
5ac1d44a 688 for (int y = 0; y < h; y++)
b5f01ae0 689 {
5ac1d44a
PC
690 for (int x = 0; x < w; x++, bit_index++)
691 if (*alpha++ < wxIMAGE_ALPHA_THRESHOLD)
692 out[bit_index >> 3] ^= 1 << (bit_index & 7);
693 bit_index = (bit_index + 7) & ~7u;
feac7937 694 }
5ac1d44a
PC
695 }
696 else
b5f01ae0 697 {
5ac1d44a
PC
698 const wxByte r_mask = image.GetMaskRed();
699 const wxByte g_mask = image.GetMaskGreen();
700 const wxByte b_mask = image.GetMaskBlue();
701 const wxByte* in = image.GetData();
702 for (int y = 0; y < h; y++)
703 {
704 for (int x = 0; x < w; x++, in += 3, bit_index++)
705 if (in[0] == r_mask && in[1] == g_mask && in[2] == b_mask)
706 out[bit_index >> 3] ^= 1 << (bit_index & 7);
707 bit_index = (bit_index + 7) & ~7u;
708 }
709 }
2d13e22f 710 SetMask(new wxMask(gdk_bitmap_create_from_data(M_BMPDATA->m_pixmap, (char*)out, w, h)));
5ac1d44a
PC
711 delete[] out;
712 }
3d37a968 713 return IsOk();
b5f01ae0
VS
714}
715
feac7937 716bool wxBitmap::CreateFromImageAsPixbuf(const wxImage& image)
b5f01ae0 717{
feac7937
VS
718 int width = image.GetWidth();
719 int height = image.GetHeight();
2eefae6e 720
23656673 721 Create(width, height, 32);
06497cba 722 GdkPixbuf* pixbuf = GetPixbuf();
feac7937
VS
723 if (!pixbuf)
724 return false;
c2fa61e8 725
feac7937 726 // Copy the data:
23656673 727 const unsigned char* in = image.GetData();
feac7937
VS
728 unsigned char *out = gdk_pixbuf_get_pixels(pixbuf);
729 unsigned char *alpha = image.GetAlpha();
902725ee 730
23656673 731 int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - 4 * width;
b5f01ae0 732
23656673 733 for (int y = 0; y < height; y++, out += rowpad)
b5f01ae0 734 {
e503ef07 735 for (int x = 0; x < width; x++, out += 4, in += 3)
feac7937
VS
736 {
737 out[0] = in[0];
738 out[1] = in[1];
739 out[2] = in[2];
e503ef07
PC
740 if (alpha)
741 out[3] = *alpha++;
feac7937 742 }
b5f01ae0 743 }
902725ee 744
feac7937
VS
745 return true;
746}
9dc44eff 747#endif
b5f01ae0 748
feac7937
VS
749wxImage wxBitmap::ConvertToImage() const
750{
9dc44eff
PC
751#ifdef __WXGTK3__
752 wxImage image;
753 wxCHECK_MSG(IsOk(), image, "invalid bitmap");
754 wxBitmapRefData* bmpData = M_BMPDATA;
755 const int w = bmpData->m_width;
756 const int h = bmpData->m_height;
757 image.Create(w, h, false);
758 guchar* dst = image.GetData();
759 GdkPixbuf* pixbuf_src = NULL;
760 if (bmpData->m_pixbufNoMask)
761 pixbuf_src = bmpData->m_pixbufNoMask;
762 else if (bmpData->m_surface)
763 {
764 pixbuf_src = gdk_pixbuf_get_from_surface(bmpData->m_surface, 0, 0, w, h);
765 bmpData->m_pixbufNoMask = pixbuf_src;
766 wxASSERT(bmpData->m_bpp == 32 || !gdk_pixbuf_get_has_alpha(bmpData->m_pixbufNoMask));
767 }
768 if (pixbuf_src)
769 {
770 const guchar* src = gdk_pixbuf_get_pixels(pixbuf_src);
771 const int srcStride = gdk_pixbuf_get_rowstride(pixbuf_src);
772 const int srcChannels = gdk_pixbuf_get_n_channels(pixbuf_src);
773 CopyImageData(dst, 3, 3 * w, src, srcChannels, srcStride, w, h);
774
775 if (srcChannels == 4)
776 {
777 image.SetAlpha();
778 guchar* alpha = image.GetAlpha();
779 for (int j = 0; j < h; j++, src += srcStride)
780 {
781 const guchar* s = src;
782 for (int i = 0; i < w; i++, s += 4)
783 *alpha++ = s[3];
784 }
785 }
786 }
787 cairo_surface_t* maskSurf = NULL;
788 if (bmpData->m_mask)
5ca21fe7 789 maskSurf = *bmpData->m_mask;
9dc44eff
PC
790 if (maskSurf)
791 {
792 const guchar r = 1;
793 const guchar g = 2;
794 const guchar b = 3;
795 image.SetMaskColour(r, g, b);
796 wxASSERT(cairo_image_surface_get_format(maskSurf) == CAIRO_FORMAT_A8);
797 const int stride = cairo_image_surface_get_stride(maskSurf);
798 const guchar* src = cairo_image_surface_get_data(maskSurf);
799 for (int j = 0; j < h; j++, src += stride)
800 {
801 for (int i = 0; i < w; i++, dst += 3)
802 if (src[i] == 0)
803 {
804 dst[0] = r;
805 dst[1] = g;
806 dst[2] = b;
807 }
808 else if (dst[0] == r && dst[1] == g && dst[2] == b)
809 dst[2]--;
810 }
811 }
812#else
3d37a968 813 wxCHECK_MSG( IsOk(), wxNullImage, wxT("invalid bitmap") );
902725ee 814
afbfbfdf
PC
815 const int w = GetWidth();
816 const int h = GetHeight();
23656673 817 wxImage image(w, h, false);
feac7937 818 unsigned char *data = image.GetData();
2eefae6e 819
5588ce92 820 wxCHECK_MSG(data != NULL, wxNullImage, wxT("couldn't create image") );
b5f01ae0 821
70029506 822 // prefer pixbuf if available, it will preserve alpha and should be quicker
feac7937 823 if (HasPixbuf())
b5f01ae0 824 {
feac7937 825 GdkPixbuf *pixbuf = GetPixbuf();
70029506
PC
826 unsigned char* alpha = NULL;
827 if (gdk_pixbuf_get_has_alpha(pixbuf))
828 {
829 image.SetAlpha();
830 alpha = image.GetAlpha();
831 }
23656673 832 const unsigned char* in = gdk_pixbuf_get_pixels(pixbuf);
feac7937 833 unsigned char *out = data;
70029506 834 const int inc = 3 + int(alpha != NULL);
23656673 835 const int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - inc * w;
b5f01ae0 836
23656673 837 for (int y = 0; y < h; y++, in += rowpad)
feac7937 838 {
70029506 839 for (int x = 0; x < w; x++, in += inc, out += 3)
feac7937
VS
840 {
841 out[0] = in[0];
842 out[1] = in[1];
843 out[2] = in[2];
70029506
PC
844 if (alpha != NULL)
845 *alpha++ = in[3];
feac7937
VS
846 }
847 }
b5f01ae0 848 }
feac7937 849 else
b5f01ae0 850 {
afbfbfdf
PC
851 GdkPixmap* pixmap = GetPixmap();
852 GdkPixmap* pixmap_invert = NULL;
853 if (GetDepth() == 1)
b5f01ae0 854 {
d0e79755 855 // mono bitmaps are inverted, i.e. 0 is white
afbfbfdf 856 pixmap_invert = gdk_pixmap_new(pixmap, w, h, 1);
f3d74739 857 wxGtkObject<GdkGC> gc(gdk_gc_new(pixmap_invert));
afbfbfdf
PC
858 gdk_gc_set_function(gc, GDK_COPY_INVERT);
859 gdk_draw_drawable(pixmap_invert, gc, pixmap, 0, 0, 0, 0, w, h);
afbfbfdf 860 pixmap = pixmap_invert;
feac7937 861 }
afbfbfdf
PC
862 // create a pixbuf which shares data with the wxImage
863 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data(
864 data, GDK_COLORSPACE_RGB, false, 8, w, h, 3 * w, NULL, NULL);
feac7937 865
afbfbfdf 866 gdk_pixbuf_get_from_drawable(pixbuf, pixmap, NULL, 0, 0, 0, 0, w, h);
feac7937 867
afbfbfdf
PC
868 g_object_unref(pixbuf);
869 if (pixmap_invert != NULL)
870 g_object_unref(pixmap_invert);
70029506
PC
871 }
872 // convert mask, unless there is already alpha
873 if (GetMask() && !image.HasAlpha())
874 {
23656673
PC
875 // we hard code the mask colour for now but we could also make an
876 // effort (and waste time) to choose a colour not present in the
877 // image already to avoid having to fudge the pixels below --
878 // whether it's worth to do it is unclear however
70029506
PC
879 const int MASK_RED = 1;
880 const int MASK_GREEN = 2;
881 const int MASK_BLUE = 3;
882 const int MASK_BLUE_REPLACEMENT = 2;
feac7937 883
70029506 884 image.SetMaskColour(MASK_RED, MASK_GREEN, MASK_BLUE);
5ca21fe7 885 GdkImage* image_mask = gdk_drawable_get_image(*GetMask(), 0, 0, w, h);
feac7937 886
70029506
PC
887 for (int y = 0; y < h; y++)
888 {
889 for (int x = 0; x < w; x++, data += 3)
8ab696e0 890 {
70029506 891 if (gdk_image_get_pixel(image_mask, x, y) == 0)
8ab696e0 892 {
70029506
PC
893 data[0] = MASK_RED;
894 data[1] = MASK_GREEN;
895 data[2] = MASK_BLUE;
896 }
897 else if (data[0] == MASK_RED && data[1] == MASK_GREEN && data[2] == MASK_BLUE)
898 {
23656673
PC
899 // we have to fudge the colour a bit to prevent
900 // this pixel from appearing transparent
70029506 901 data[2] = MASK_BLUE_REPLACEMENT;
feac7937 902 }
feac7937 903 }
b5f01ae0 904 }
70029506 905 g_object_unref(image_mask);
feac7937 906 }
9dc44eff 907#endif
b5f01ae0
VS
908
909 return image;
910}
911
c0521644
VZ
912#endif // wxUSE_IMAGE
913
91b8de8d 914int wxBitmap::GetHeight() const
c801d85f 915{
3d37a968 916 wxCHECK_MSG( IsOk(), -1, wxT("invalid bitmap") );
e55ad60e 917
fd0eed64 918 return M_BMPDATA->m_height;
ff7b1510 919}
c801d85f 920
91b8de8d 921int wxBitmap::GetWidth() const
c801d85f 922{
3d37a968 923 wxCHECK_MSG( IsOk(), -1, wxT("invalid bitmap") );
8bbe427f 924
fd0eed64 925 return M_BMPDATA->m_width;
ff7b1510 926}
c801d85f 927
91b8de8d 928int wxBitmap::GetDepth() const
c801d85f 929{
3d37a968 930 wxCHECK_MSG( IsOk(), -1, wxT("invalid bitmap") );
8bbe427f 931
fd0eed64 932 return M_BMPDATA->m_bpp;
ff7b1510 933}
c801d85f 934
91b8de8d 935wxMask *wxBitmap::GetMask() const
c801d85f 936{
d3b9f782 937 wxCHECK_MSG( IsOk(), NULL, wxT("invalid bitmap") );
8bbe427f 938
fd0eed64 939 return M_BMPDATA->m_mask;
ff7b1510 940}
c801d85f
KB
941
942void wxBitmap::SetMask( wxMask *mask )
943{
3d37a968 944 wxCHECK_RET( IsOk(), wxT("invalid bitmap") );
8bbe427f 945
e3e89a93 946 AllocExclusive();
23656673 947 delete M_BMPDATA->m_mask;
fd0eed64 948 M_BMPDATA->m_mask = mask;
ff7b1510 949}
c801d85f 950
db0aec83
VS
951bool wxBitmap::CopyFromIcon(const wxIcon& icon)
952{
953 *this = icon;
3d37a968 954 return IsOk();
db0aec83
VS
955}
956
9dc44eff
PC
957#ifdef __WXGTK3__
958static cairo_surface_t* GetSubSurface(cairo_surface_t* surface, const wxRect& rect)
959{
960 cairo_surface_flush(surface);
961 const cairo_format_t format = cairo_image_surface_get_format(surface);
962 int x = rect.x;
963 if (format != CAIRO_FORMAT_A8)
964 x *= 4;
965 cairo_surface_t* subSurface = cairo_image_surface_create(format, rect.width, rect.height);
966 const int srcStride = cairo_image_surface_get_stride(surface);
967 const int dstStride = cairo_image_surface_get_stride(subSurface);
968 const guchar* src = cairo_image_surface_get_data(surface) + rect.y * srcStride + x;
969 guchar* dst = cairo_image_surface_get_data(subSurface);
970 for (int j = 0; j < rect.height; j++, src += srcStride, dst += dstStride)
971 memcpy(dst, src, dstStride);
972 cairo_surface_mark_dirty(subSurface);
973 return subSurface;
974}
975#endif
976
17bec151
RR
977wxBitmap wxBitmap::GetSubBitmap( const wxRect& rect) const
978{
5588ce92
PC
979 wxBitmap ret;
980
3d37a968 981 wxCHECK_MSG(IsOk(), ret, wxT("invalid bitmap"));
06497cba
PC
982
983 const int w = rect.width;
984 const int h = rect.height;
985 const wxBitmapRefData* bmpData = M_BMPDATA;
986
23656673 987 wxCHECK_MSG(rect.x >= 0 && rect.y >= 0 &&
06497cba
PC
988 rect.x + w <= bmpData->m_width &&
989 rect.y + h <= bmpData->m_height,
23656673 990 ret, wxT("invalid bitmap region"));
13111b2a 991
fc684792 992 wxBitmapRefData * const newRef = new wxBitmapRefData(w, h, bmpData->m_bpp);
06497cba 993 ret.m_refData = newRef;
06497cba 994
9dc44eff
PC
995#ifdef __WXGTK3__
996 if (bmpData->m_pixbufNoMask)
997 {
998 GdkPixbuf* pixbuf = gdk_pixbuf_new_subpixbuf(bmpData->m_pixbufNoMask, rect.x, rect.y, w, h);
999 newRef->m_pixbufNoMask = gdk_pixbuf_copy(pixbuf);
1000 wxASSERT(newRef->m_bpp == 32 || !gdk_pixbuf_get_has_alpha(newRef->m_pixbufNoMask));
1001 g_object_unref(pixbuf);
1002 }
1003 else if (bmpData->m_surface)
1004 newRef->m_surface = GetSubSurface(bmpData->m_surface, rect);
1005
1006 cairo_surface_t* maskSurf = NULL;
1007 if (bmpData->m_mask)
5ca21fe7 1008 maskSurf = *bmpData->m_mask;
9dc44eff
PC
1009 if (maskSurf)
1010 {
1011 newRef->m_mask = new wxMask(GetSubSurface(maskSurf, rect));
1012 }
1013#else
06497cba 1014 if (bmpData->m_pixbuf)
17bec151 1015 {
06497cba
PC
1016 GdkPixbuf* pixbuf =
1017 gdk_pixbuf_new_subpixbuf(bmpData->m_pixbuf, rect.x, rect.y, w, h);
1018 newRef->m_pixbuf = gdk_pixbuf_copy(pixbuf);
1019 g_object_unref(pixbuf);
17bec151 1020 }
06497cba 1021 if (bmpData->m_pixmap)
17bec151 1022 {
06497cba
PC
1023 newRef->m_pixmap = gdk_pixmap_new(bmpData->m_pixmap, w, h, -1);
1024 GdkGC* gc = gdk_gc_new(newRef->m_pixmap);
1025 gdk_draw_drawable(
1026 newRef->m_pixmap, gc, bmpData->m_pixmap, rect.x, rect.y, 0, 0, w, h);
1027 g_object_unref(gc);
17bec151 1028 }
2d13e22f
PC
1029 GdkPixmap* mask = NULL;
1030 if (bmpData->m_mask)
5ca21fe7 1031 mask = *bmpData->m_mask;
2d13e22f 1032 if (mask)
17bec151 1033 {
2d13e22f
PC
1034 GdkPixmap* sub_mask = gdk_pixmap_new(mask, w, h, 1);
1035 newRef->m_mask = new wxMask(sub_mask);
06497cba
PC
1036 GdkGC* gc = gdk_gc_new(sub_mask);
1037 gdk_draw_drawable(
2d13e22f 1038 sub_mask, gc, mask, rect.x, rect.y, 0, 0, w, h);
06497cba 1039 g_object_unref(gc);
17bec151 1040 }
9dc44eff 1041#endif
13111b2a 1042
17bec151
RR
1043 return ret;
1044}
1045
4611dd06 1046bool wxBitmap::SaveFile( const wxString &name, wxBitmapType type, const wxPalette *WXUNUSED(palette) ) const
c801d85f 1047{
3d37a968 1048 wxCHECK_MSG( IsOk(), false, wxT("invalid bitmap") );
8bbe427f 1049
8362e67c
PC
1050 const char* type_name = NULL;
1051 switch (type)
1052 {
fd51987f 1053 case wxBITMAP_TYPE_ANI: type_name = "ani"; break;
8362e67c 1054 case wxBITMAP_TYPE_BMP: type_name = "bmp"; break;
fd51987f 1055 case wxBITMAP_TYPE_GIF: type_name = "gif"; break;
8362e67c
PC
1056 case wxBITMAP_TYPE_ICO: type_name = "ico"; break;
1057 case wxBITMAP_TYPE_JPEG: type_name = "jpeg"; break;
fd51987f 1058 case wxBITMAP_TYPE_PCX: type_name = "pcx"; break;
8362e67c 1059 case wxBITMAP_TYPE_PNG: type_name = "png"; break;
fd51987f
PC
1060 case wxBITMAP_TYPE_PNM: type_name = "pnm"; break;
1061 case wxBITMAP_TYPE_TGA: type_name = "tga"; break;
1062 case wxBITMAP_TYPE_TIFF: type_name = "tiff"; break;
1063 case wxBITMAP_TYPE_XBM: type_name = "xbm"; break;
1064 case wxBITMAP_TYPE_XPM: type_name = "xpm"; break;
8362e67c
PC
1065 default: break;
1066 }
c2f8c2b2
PC
1067 if (type_name &&
1068 gdk_pixbuf_save(GetPixbuf(), wxGTK_CONV_FN(name), type_name, NULL, NULL))
1069 {
1070 return true;
1071 }
1072#if wxUSE_IMAGE
1073 return ConvertToImage().SaveFile(name, type);
1074#else
1075 return false;
1076#endif
ff7b1510 1077}
c801d85f 1078
4611dd06 1079bool wxBitmap::LoadFile( const wxString &name, wxBitmapType type )
c801d85f 1080{
c2f8c2b2
PC
1081 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file(wxGTK_CONV_FN(name), NULL);
1082 if (pixbuf)
1083 {
1084 *this = wxBitmap(pixbuf);
1085 return true;
1086 }
c0521644 1087#if wxUSE_IMAGE
8362e67c
PC
1088 wxImage image;
1089 if (image.LoadFile(name, type) && image.IsOk())
fd0eed64 1090 {
c2f8c2b2
PC
1091 *this = wxBitmap(image);
1092 return true;
fd0eed64 1093 }
c2f8c2b2
PC
1094#else
1095 wxUnusedVar(type);
1096#endif
1097 return false;
ff7b1510 1098}
8bbe427f 1099
0b04c4e0 1100#if wxUSE_PALETTE
91b8de8d 1101wxPalette *wxBitmap::GetPalette() const
c801d85f 1102{
b2bf3ac6 1103 return NULL;
ff7b1510 1104}
c801d85f 1105
4611dd06
RR
1106void wxBitmap::SetPalette(const wxPalette& WXUNUSED(palette))
1107{
1108 // TODO
1109}
0b04c4e0 1110#endif // wxUSE_PALETTE
4611dd06 1111
4bc67cc5
RR
1112void wxBitmap::SetHeight( int height )
1113{
e3e89a93 1114 AllocExclusive();
4bc67cc5
RR
1115 M_BMPDATA->m_height = height;
1116}
1117
1118void wxBitmap::SetWidth( int width )
1119{
e3e89a93 1120 AllocExclusive();
4bc67cc5
RR
1121 M_BMPDATA->m_width = width;
1122}
1123
1124void wxBitmap::SetDepth( int depth )
1125{
e3e89a93 1126 AllocExclusive();
4bc67cc5
RR
1127 M_BMPDATA->m_bpp = depth;
1128}
1129
9dc44eff 1130#ifndef __WXGTK3__
4bc67cc5
RR
1131void wxBitmap::SetPixmap( GdkPixmap *pixmap )
1132{
06497cba
PC
1133 UnRef();
1134
3d37a968
FM
1135 if (!pixmap)
1136 return;
1137
06497cba
PC
1138 int w, h;
1139 gdk_drawable_get_size(pixmap, &w, &h);
1140 wxBitmapRefData* bmpData = new wxBitmapRefData(w, h, 0);
1141 m_refData = bmpData;
1142 bmpData->m_pixmap = pixmap;
1143 bmpData->m_bpp = gdk_drawable_get_depth(pixmap);
4bc67cc5
RR
1144}
1145
91b8de8d 1146GdkPixmap *wxBitmap::GetPixmap() const
c801d85f 1147{
d3b9f782 1148 wxCHECK_MSG( IsOk(), NULL, wxT("invalid bitmap") );
8bbe427f 1149
06497cba
PC
1150 wxBitmapRefData* bmpData = M_BMPDATA;
1151 if (bmpData->m_pixmap)
1152 return bmpData->m_pixmap;
1153
1154 if (bmpData->m_pixbuf)
feac7937 1155 {
2d13e22f 1156 GdkPixmap* pixmap = NULL;
06497cba
PC
1157 GdkPixmap** mask_pixmap = NULL;
1158 if (gdk_pixbuf_get_has_alpha(bmpData->m_pixbuf))
70029506
PC
1159 {
1160 // make new mask from alpha
2d13e22f 1161 mask_pixmap = &pixmap;
70029506 1162 }
06497cba
PC
1163 gdk_pixbuf_render_pixmap_and_mask(
1164 bmpData->m_pixbuf, &bmpData->m_pixmap, mask_pixmap, 128);
2d13e22f
PC
1165 if (pixmap)
1166 {
1167 delete bmpData->m_mask;
1168 bmpData->m_mask = new wxMask(pixmap);
1169 }
feac7937 1170 }
06497cba
PC
1171 else
1172 {
1173 bmpData->m_pixmap = gdk_pixmap_new(wxGetRootWindow()->window,
1174 bmpData->m_width, bmpData->m_height, bmpData->m_bpp == 1 ? 1 : -1);
1175 }
1176 return bmpData->m_pixmap;
ff7b1510 1177}
8bbe427f 1178
feac7937
VS
1179bool wxBitmap::HasPixmap() const
1180{
3d37a968 1181 wxCHECK_MSG( IsOk(), false, wxT("invalid bitmap") );
feac7937
VS
1182
1183 return M_BMPDATA->m_pixmap != NULL;
1184}
9dc44eff
PC
1185#endif
1186
1187#ifdef __WXGTK3__
1188GdkPixbuf* wxBitmap::GetPixbufNoMask() const
1189{
1190 wxCHECK_MSG(IsOk(), NULL, "invalid bitmap");
1191
1192 wxBitmapRefData* bmpData = M_BMPDATA;
1193 GdkPixbuf* pixbuf = bmpData->m_pixbufNoMask;
1194 if (pixbuf)
1195 return pixbuf;
1196
1197 const int w = bmpData->m_width;
1198 const int h = bmpData->m_height;
1199 if (bmpData->m_surface)
1200 pixbuf = gdk_pixbuf_get_from_surface(bmpData->m_surface, 0, 0, w, h);
1201 else
1202 pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, bmpData->m_bpp == 32, 8, w, h);
1203 bmpData->m_pixbufNoMask = pixbuf;
1204 wxASSERT(bmpData->m_bpp == 32 || !gdk_pixbuf_get_has_alpha(bmpData->m_pixbufNoMask));
1205
1206 return pixbuf;
1207}
1208
1209// helper to set up a simulated depth 1 surface
1210static void SetSourceSurface1(const wxBitmapRefData* bmpData, cairo_t* cr, int x, int y, const wxColour* fg, const wxColour* bg)
1211{
1212 GdkPixbuf* pixbuf = gdk_pixbuf_copy(bmpData->m_pixbufNoMask);
1213 const int w = bmpData->m_width;
1214 const int h = bmpData->m_height;
1215 const int stride = gdk_pixbuf_get_rowstride(pixbuf);
1216 const int channels = gdk_pixbuf_get_n_channels(pixbuf);
1217 guchar* dst = gdk_pixbuf_get_pixels(pixbuf);
1218 guchar fg_r = 0, fg_g = 0, fg_b = 0;
1219 if (fg && fg->IsOk())
1220 {
1221 fg_r = fg->Red();
1222 fg_g = fg->Green();
1223 fg_b = fg->Blue();
1224 }
1225 guchar bg_r = 255, bg_g = 255, bg_b = 255;
1226 if (bg && bg->IsOk())
1227 {
1228 bg_r = bg->Red();
1229 bg_g = bg->Green();
1230 bg_b = bg->Blue();
1231 }
1232 for (int j = 0; j < h; j++, dst += stride)
1233 {
1234 guchar* d = dst;
1235 for (int i = 0; i < w; i++, d += channels)
1236 if (d[0])
1237 {
1238 d[0] = bg_r;
1239 d[1] = bg_g;
1240 d[2] = bg_b;
1241 }
1242 else
1243 {
1244 d[0] = fg_r;
1245 d[1] = fg_g;
1246 d[2] = fg_b;
1247 }
1248 }
1249 gdk_cairo_set_source_pixbuf(cr, pixbuf, x, y);
1250 g_object_unref(pixbuf);
1251}
1252
1253void wxBitmap::SetSourceSurface(cairo_t* cr, int x, int y, const wxColour* fg, const wxColour* bg) const
1254{
1255 wxBitmapRefData* bmpData = M_BMPDATA;
1256 if (bmpData->m_surface)
1257 {
1258 cairo_set_source_surface(cr, bmpData->m_surface, x, y);
1259 return;
1260 }
1261 wxCHECK_RET(bmpData->m_pixbufNoMask, "no bitmap data");
1262 if (bmpData->m_bpp == 1)
1263 SetSourceSurface1(bmpData, cr, x, y, fg, bg);
1264 else
1265 {
1266 gdk_cairo_set_source_pixbuf(cr, bmpData->m_pixbufNoMask, x, y);
1267 cairo_pattern_get_surface(cairo_get_source(cr), &bmpData->m_surface);
1268 cairo_surface_reference(bmpData->m_surface);
1269 }
1270}
1271
1272cairo_t* wxBitmap::CairoCreate() const
1273{
1274 wxCHECK_MSG(IsOk(), NULL, "invalid bitmap");
1275
1276 wxBitmapRefData* bmpData = M_BMPDATA;
1277 cairo_t* cr;
1278 if (bmpData->m_surface)
1279 cr = cairo_create(bmpData->m_surface);
1280 else
1281 {
1282 GdkPixbuf* pixbuf = bmpData->m_pixbufNoMask;
1283 const bool useAlpha = bmpData->m_bpp == 32 || (pixbuf && gdk_pixbuf_get_has_alpha(pixbuf));
1284 bmpData->m_surface = cairo_image_surface_create(
1285 useAlpha ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24,
1286 bmpData->m_width, bmpData->m_height);
1287 cr = cairo_create(bmpData->m_surface);
1288 if (pixbuf)
1289 {
1290 gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
1291 cairo_paint(cr);
1292 cairo_set_source_rgb(cr, 0, 0, 0);
1293 }
1294 }
1295 if (bmpData->m_pixbufNoMask)
1296 {
1297 g_object_unref(bmpData->m_pixbufNoMask);
1298 bmpData->m_pixbufNoMask = NULL;
1299 }
1300 if (bmpData->m_pixbufMask)
1301 {
1302 g_object_unref(bmpData->m_pixbufMask);
1303 bmpData->m_pixbufMask = NULL;
1304 }
1305 wxASSERT(cr && cairo_status(cr) == 0);
1306 return cr;
1307}
1308
1309void wxBitmap::Draw(cairo_t* cr, int x, int y, bool useMask, const wxColour* fg, const wxColour* bg) const
1310{
1311 wxCHECK_RET(IsOk(), "invalid bitmap");
1312
1313 wxBitmapRefData* bmpData = M_BMPDATA;
1314 SetSourceSurface(cr, x, y, fg, bg);
1315 cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
1316 cairo_surface_t* mask = NULL;
1317 if (useMask && bmpData->m_mask)
5ca21fe7 1318 mask = *bmpData->m_mask;
9dc44eff
PC
1319 if (mask)
1320 cairo_mask_surface(cr, mask, x, y);
1321 else
1322 cairo_paint(cr);
1323}
1324#endif
feac7937 1325
feac7937
VS
1326GdkPixbuf *wxBitmap::GetPixbuf() const
1327{
3d37a968 1328 wxCHECK_MSG( IsOk(), NULL, wxT("invalid bitmap") );
feac7937 1329
06497cba 1330 wxBitmapRefData* bmpData = M_BMPDATA;
9dc44eff
PC
1331#ifdef __WXGTK3__
1332 if (bmpData->m_pixbufMask)
1333 return bmpData->m_pixbufMask;
1334
1335 if (bmpData->m_pixbufNoMask == NULL)
1336 GetPixbufNoMask();
1337 cairo_surface_t* mask = NULL;
1338 if (bmpData->m_mask)
5ca21fe7 1339 mask = *bmpData->m_mask;
9dc44eff
PC
1340 if (mask == NULL)
1341 return bmpData->m_pixbufNoMask;
1342
1343 const int w = bmpData->m_width;
1344 const int h = bmpData->m_height;
1345 bmpData->m_pixbufMask = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, w, h);
1346
1347 guchar* dst = gdk_pixbuf_get_pixels(bmpData->m_pixbufMask);
1348 const int dstStride = gdk_pixbuf_get_rowstride(bmpData->m_pixbufMask);
1349 CopyImageData(dst, 4, dstStride,
1350 gdk_pixbuf_get_pixels(bmpData->m_pixbufNoMask),
1351 gdk_pixbuf_get_n_channels(bmpData->m_pixbufNoMask),
1352 gdk_pixbuf_get_rowstride(bmpData->m_pixbufNoMask),
1353 w, h);
1354
1355 const guchar* src = cairo_image_surface_get_data(mask);
1356 const int srcStride = cairo_image_surface_get_stride(mask);
1357 for (int j = 0; j < h; j++, src += srcStride, dst += dstStride)
1358 for (int i = 0; i < w; i++)
1359 if (src[i] == 0)
1360 dst[i * 4 + 3] = 0;
1361
1362 return bmpData->m_pixbufMask;
1363#else
06497cba
PC
1364 if (bmpData->m_pixbuf)
1365 return bmpData->m_pixbuf;
6db34764 1366
06497cba
PC
1367 const int w = bmpData->m_width;
1368 const int h = bmpData->m_height;
1369 GdkPixmap* mask = NULL;
1370 if (bmpData->m_mask)
5ca21fe7 1371 mask = *bmpData->m_mask;
06497cba
PC
1372 const bool useAlpha = bmpData->m_alphaRequested || mask;
1373 bmpData->m_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, useAlpha, 8, w, h);
1374 if (bmpData->m_pixmap)
1375 PixmapToPixbuf(bmpData->m_pixmap, bmpData->m_pixbuf, w, h);
1376 if (mask)
1377 MaskToAlpha(mask, bmpData->m_pixbuf, w, h);
1378 return bmpData->m_pixbuf;
9dc44eff 1379#endif
feac7937
VS
1380}
1381
9dc44eff 1382#ifndef __WXGTK3__
feac7937
VS
1383bool wxBitmap::HasPixbuf() const
1384{
3d37a968 1385 wxCHECK_MSG( IsOk(), false, wxT("invalid bitmap") );
feac7937
VS
1386
1387 return M_BMPDATA->m_pixbuf != NULL;
1388}
1389
feac7937
VS
1390void wxBitmap::PurgeOtherRepresentations(wxBitmap::Representation keep)
1391{
1392 if (keep == Pixmap && HasPixbuf())
1393 {
3fe39b0c 1394 g_object_unref (M_BMPDATA->m_pixbuf);
feac7937
VS
1395 M_BMPDATA->m_pixbuf = NULL;
1396 }
1397 if (keep == Pixbuf && HasPixmap())
1398 {
3fe39b0c 1399 g_object_unref (M_BMPDATA->m_pixmap);
feac7937
VS
1400 M_BMPDATA->m_pixmap = NULL;
1401 }
1402}
9dc44eff 1403#endif
feac7937 1404
ce7c8a97 1405#ifdef wxHAS_RAW_BITMAP
284f2b59
RR
1406void *wxBitmap::GetRawData(wxPixelDataBase& data, int bpp)
1407{
70029506 1408 void* bits = NULL;
9dc44eff
PC
1409#ifdef __WXGTK3__
1410 GdkPixbuf* pixbuf = GetPixbufNoMask();
1411 if ((bpp == 32) == (gdk_pixbuf_get_has_alpha(pixbuf) != 0))
1412 {
1413 bits = gdk_pixbuf_get_pixels(pixbuf);
1414 wxBitmapRefData* bmpData = M_BMPDATA;
1415 data.m_width = bmpData->m_width;
1416 data.m_height = bmpData->m_height;
1417 data.m_stride = gdk_pixbuf_get_rowstride(pixbuf);
1418 if (bmpData->m_pixbufMask)
1419 {
1420 g_object_unref(bmpData->m_pixbufMask);
1421 bmpData->m_pixbufMask = NULL;
1422 }
1423 if (bmpData->m_surface)
1424 {
1425 cairo_surface_destroy(bmpData->m_surface);
1426 bmpData->m_surface = NULL;
1427 }
1428 }
1429#else
284f2b59 1430 GdkPixbuf *pixbuf = GetPixbuf();
70029506 1431 const bool hasAlpha = HasAlpha();
3d37a968 1432
70029506 1433 // allow access if bpp is valid and matches existence of alpha
c74fc1e1 1434 if ( pixbuf && ((bpp == 24 && !hasAlpha) || (bpp == 32 && hasAlpha)) )
70029506
PC
1435 {
1436 data.m_height = gdk_pixbuf_get_height( pixbuf );
1437 data.m_width = gdk_pixbuf_get_width( pixbuf );
1438 data.m_stride = gdk_pixbuf_get_rowstride( pixbuf );
1439 bits = gdk_pixbuf_get_pixels(pixbuf);
1440 }
9dc44eff 1441#endif
70029506 1442 return bits;
284f2b59
RR
1443}
1444
17a1ebd1 1445void wxBitmap::UngetRawData(wxPixelDataBase& WXUNUSED(data))
284f2b59
RR
1446{
1447}
ce7c8a97 1448#endif // wxHAS_RAW_BITMAP
284f2b59 1449
902725ee 1450bool wxBitmap::HasAlpha() const
0ff2a74d 1451{
06497cba 1452 const wxBitmapRefData* bmpData = M_BMPDATA;
9dc44eff
PC
1453#ifdef __WXGTK3__
1454 return bmpData && bmpData->m_bpp == 32;
1455#else
06497cba
PC
1456 return bmpData && (bmpData->m_alphaRequested ||
1457 (bmpData->m_pixbuf && gdk_pixbuf_get_has_alpha(bmpData->m_pixbuf)));
9dc44eff 1458#endif
0ff2a74d
RR
1459}
1460
8f884a0d 1461wxGDIRefData* wxBitmap::CreateGDIRefData() const
e3e89a93 1462{
06497cba 1463 return new wxBitmapRefData(0, 0, 0);
e3e89a93
PC
1464}
1465
8f884a0d 1466wxGDIRefData* wxBitmap::CloneGDIRefData(const wxGDIRefData* data) const
e3e89a93 1467{
5c33522f 1468 const wxBitmapRefData* oldRef = static_cast<const wxBitmapRefData*>(data);
fc684792
VZ
1469 wxBitmapRefData * const newRef = new wxBitmapRefData(oldRef->m_width,
1470 oldRef->m_height,
1471 oldRef->m_bpp);
9dc44eff
PC
1472#ifdef __WXGTK3__
1473 if (oldRef->m_pixbufNoMask)
1474 newRef->m_pixbufNoMask = gdk_pixbuf_copy(oldRef->m_pixbufNoMask);
1475 if (oldRef->m_surface)
1476 {
1477 const int w = oldRef->m_width;
1478 const int h = oldRef->m_height;
1479 cairo_surface_t* surface = cairo_image_surface_create(
1480 cairo_image_surface_get_format(oldRef->m_surface), w, h);
1481 newRef->m_surface = surface;
1482 cairo_surface_flush(oldRef->m_surface);
1483 const guchar* src = cairo_image_surface_get_data(oldRef->m_surface);
1484 guchar* dst = cairo_image_surface_get_data(surface);
1485 const int stride = cairo_image_surface_get_stride(surface);
1486 wxASSERT(stride == cairo_image_surface_get_stride(oldRef->m_surface));
1487 memcpy(dst, src, stride * h);
1488 cairo_surface_mark_dirty(surface);
1489 }
1490#else
e3e89a93
PC
1491 if (oldRef->m_pixmap != NULL)
1492 {
1493 newRef->m_pixmap = gdk_pixmap_new(
1494 oldRef->m_pixmap, oldRef->m_width, oldRef->m_height,
1495 // use pixmap depth, m_bpp may not match
1496 gdk_drawable_get_depth(oldRef->m_pixmap));
f3d74739 1497 wxGtkObject<GdkGC> gc(gdk_gc_new(newRef->m_pixmap));
e3e89a93
PC
1498 gdk_draw_drawable(
1499 newRef->m_pixmap, gc, oldRef->m_pixmap, 0, 0, 0, 0, -1, -1);
e3e89a93
PC
1500 }
1501 if (oldRef->m_pixbuf != NULL)
1502 {
1503 newRef->m_pixbuf = gdk_pixbuf_copy(oldRef->m_pixbuf);
1504 }
9dc44eff 1505#endif
e3e89a93
PC
1506 if (oldRef->m_mask != NULL)
1507 {
27297c82 1508 newRef->m_mask = new wxMask(*oldRef->m_mask);
e3e89a93 1509 }
e3e89a93
PC
1510
1511 return newRef;
1512}
1513
4b61c88d
RR
1514/* static */ void wxBitmap::InitStandardHandlers()
1515{
1516 // TODO: Insert handler based on GdkPixbufs handler later
1517}