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