]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/bitmap.cpp
Make wxWindow::HasScrollbar() respect wxScrolled::ShowScrollbars().
[wxWidgets.git] / src / gtk / bitmap.cpp
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__
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
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
77 IMPLEMENT_DYNAMIC_CLASS(wxMask, wxMaskBase)
78
79 wxMask::wxMask()
80 {
81 m_bitmap = NULL;
82 }
83
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
117 wxMask::wxMask( const wxBitmap& bitmap, const wxColour& colour )
118 {
119 m_bitmap = NULL;
120 InitFromColour(bitmap, colour);
121 }
122
123 #if wxUSE_PALETTE
124 wxMask::wxMask( const wxBitmap& bitmap, int paletteIndex )
125 {
126 m_bitmap = NULL;
127 Create( bitmap, paletteIndex );
128 }
129 #endif // wxUSE_PALETTE
130
131 wxMask::wxMask( const wxBitmap& bitmap )
132 {
133 m_bitmap = NULL;
134 InitFromMonoBitmap(bitmap);
135 }
136
137 #ifdef __WXGTK3__
138 wxMask::wxMask(cairo_surface_t* bitmap)
139 #else
140 wxMask::wxMask(GdkPixmap* bitmap)
141 #endif
142 {
143 m_bitmap = bitmap;
144 }
145
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
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
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
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
275 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__
316 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
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
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
401 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
412 IMPLEMENT_DYNAMIC_CLASS(wxBitmap,wxGDIObject)
413
414 wxBitmap::wxBitmap(const wxString &filename, wxBitmapType type)
415 {
416 LoadFile(filename, type);
417 }
418
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 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
448 }
449 }
450
451 wxBitmap::wxBitmap(const char* const* bits)
452 {
453 wxCHECK2_MSG(bits != NULL, return, wxT("invalid bitmap data"));
454
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
468 GdkBitmap* mask = NULL;
469 GdkPixmap* pixmap = gdk_pixmap_create_from_xpm_d(wxGetRootWindow()->window, &mask, NULL, const_cast<char**>(bits));
470 if (pixmap)
471 {
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 }
480 }
481 #endif
482 }
483
484 wxBitmap::wxBitmap(GdkPixbuf* pixbuf, int depth)
485 {
486 if (pixbuf)
487 {
488 if (depth != 1)
489 depth = gdk_pixbuf_get_n_channels(pixbuf) * 8;
490 wxBitmapRefData* bmpData = new wxBitmapRefData(
491 gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf),
492 depth);
493 m_refData = bmpData;
494 #ifdef __WXGTK3__
495 bmpData->m_pixbufNoMask = pixbuf;
496 #else
497 bmpData->m_pixbuf = pixbuf;
498 #endif
499 }
500 }
501
502 #ifndef __WXGTK3__
503 wxBitmap::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
517 wxBitmap::~wxBitmap()
518 {
519 }
520
521 bool wxBitmap::Create( int width, int height, int depth )
522 {
523 UnRef();
524 wxCHECK_MSG(width >= 0 && height >= 0, false, "invalid bitmap size");
525 m_refData = new wxBitmapRefData(width, height, depth);
526 return true;
527 }
528
529 #ifdef __WXGTK3__
530 static 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
575
576 #if wxUSE_IMAGE
577 #ifdef __WXGTK3__
578 wxBitmap::wxBitmap(const wxImage& image, int depth)
579 {
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();
595
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);
599
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
624 wxBitmap::wxBitmap(const wxImage& image, int depth)
625 {
626 wxCHECK_RET(image.IsOk(), "invalid image");
627
628 if (depth == 32 || (depth == -1 && image.HasAlpha()))
629 CreateFromImageAsPixbuf(image);
630 else
631 // otherwise create pixmap, if alpha is present it will be converted to mask
632 CreateFromImageAsPixmap(image, depth);
633 }
634
635 bool wxBitmap::CreateFromImageAsPixmap(const wxImage& image, int depth)
636 {
637 const int w = image.GetWidth();
638 const int h = image.GetHeight();
639 if (depth == 1)
640 {
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];
646 // set bits are black
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;
660
661 if (!M_BMPDATA) // SetPixmap may have failed
662 return false;
663 }
664 else
665 {
666 SetPixmap(gdk_pixmap_new(wxGetRootWindow()->window, w, h, depth));
667 if (!M_BMPDATA)
668 return false;
669
670 wxGtkObject<GdkGC> gc(gdk_gc_new(M_BMPDATA->m_pixmap));
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);
675 }
676
677 const wxByte* alpha = image.GetAlpha();
678 if (alpha != NULL || image.HasMask())
679 {
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)
687 {
688 for (int y = 0; y < h; y++)
689 {
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;
694 }
695 }
696 else
697 {
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 }
710 SetMask(new wxMask(gdk_bitmap_create_from_data(M_BMPDATA->m_pixmap, (char*)out, w, h)));
711 delete[] out;
712 }
713 return IsOk();
714 }
715
716 bool wxBitmap::CreateFromImageAsPixbuf(const wxImage& image)
717 {
718 int width = image.GetWidth();
719 int height = image.GetHeight();
720
721 Create(width, height, 32);
722 GdkPixbuf* pixbuf = GetPixbuf();
723 if (!pixbuf)
724 return false;
725
726 // Copy the data:
727 const unsigned char* in = image.GetData();
728 unsigned char *out = gdk_pixbuf_get_pixels(pixbuf);
729 unsigned char *alpha = image.GetAlpha();
730
731 int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - 4 * width;
732
733 for (int y = 0; y < height; y++, out += rowpad)
734 {
735 for (int x = 0; x < width; x++, out += 4, in += 3)
736 {
737 out[0] = in[0];
738 out[1] = in[1];
739 out[2] = in[2];
740 if (alpha)
741 out[3] = *alpha++;
742 }
743 }
744
745 return true;
746 }
747 #endif
748
749 wxImage wxBitmap::ConvertToImage() const
750 {
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)
789 maskSurf = *bmpData->m_mask;
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
813 wxCHECK_MSG( IsOk(), wxNullImage, wxT("invalid bitmap") );
814
815 const int w = GetWidth();
816 const int h = GetHeight();
817 wxImage image(w, h, false);
818 unsigned char *data = image.GetData();
819
820 wxCHECK_MSG(data != NULL, wxNullImage, wxT("couldn't create image") );
821
822 // prefer pixbuf if available, it will preserve alpha and should be quicker
823 if (HasPixbuf())
824 {
825 GdkPixbuf *pixbuf = GetPixbuf();
826 unsigned char* alpha = NULL;
827 if (gdk_pixbuf_get_has_alpha(pixbuf))
828 {
829 image.SetAlpha();
830 alpha = image.GetAlpha();
831 }
832 const unsigned char* in = gdk_pixbuf_get_pixels(pixbuf);
833 unsigned char *out = data;
834 const int inc = 3 + int(alpha != NULL);
835 const int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - inc * w;
836
837 for (int y = 0; y < h; y++, in += rowpad)
838 {
839 for (int x = 0; x < w; x++, in += inc, out += 3)
840 {
841 out[0] = in[0];
842 out[1] = in[1];
843 out[2] = in[2];
844 if (alpha != NULL)
845 *alpha++ = in[3];
846 }
847 }
848 }
849 else
850 {
851 GdkPixmap* pixmap = GetPixmap();
852 GdkPixmap* pixmap_invert = NULL;
853 if (GetDepth() == 1)
854 {
855 // mono bitmaps are inverted, i.e. 0 is white
856 pixmap_invert = gdk_pixmap_new(pixmap, w, h, 1);
857 wxGtkObject<GdkGC> gc(gdk_gc_new(pixmap_invert));
858 gdk_gc_set_function(gc, GDK_COPY_INVERT);
859 gdk_draw_drawable(pixmap_invert, gc, pixmap, 0, 0, 0, 0, w, h);
860 pixmap = pixmap_invert;
861 }
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);
865
866 gdk_pixbuf_get_from_drawable(pixbuf, pixmap, NULL, 0, 0, 0, 0, w, h);
867
868 g_object_unref(pixbuf);
869 if (pixmap_invert != NULL)
870 g_object_unref(pixmap_invert);
871 }
872 // convert mask, unless there is already alpha
873 if (GetMask() && !image.HasAlpha())
874 {
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
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;
883
884 image.SetMaskColour(MASK_RED, MASK_GREEN, MASK_BLUE);
885 GdkImage* image_mask = gdk_drawable_get_image(*GetMask(), 0, 0, w, h);
886
887 for (int y = 0; y < h; y++)
888 {
889 for (int x = 0; x < w; x++, data += 3)
890 {
891 if (gdk_image_get_pixel(image_mask, x, y) == 0)
892 {
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 {
899 // we have to fudge the colour a bit to prevent
900 // this pixel from appearing transparent
901 data[2] = MASK_BLUE_REPLACEMENT;
902 }
903 }
904 }
905 g_object_unref(image_mask);
906 }
907 #endif
908
909 return image;
910 }
911
912 #endif // wxUSE_IMAGE
913
914 int wxBitmap::GetHeight() const
915 {
916 wxCHECK_MSG( IsOk(), -1, wxT("invalid bitmap") );
917
918 return M_BMPDATA->m_height;
919 }
920
921 int wxBitmap::GetWidth() const
922 {
923 wxCHECK_MSG( IsOk(), -1, wxT("invalid bitmap") );
924
925 return M_BMPDATA->m_width;
926 }
927
928 int wxBitmap::GetDepth() const
929 {
930 wxCHECK_MSG( IsOk(), -1, wxT("invalid bitmap") );
931
932 return M_BMPDATA->m_bpp;
933 }
934
935 wxMask *wxBitmap::GetMask() const
936 {
937 wxCHECK_MSG( IsOk(), NULL, wxT("invalid bitmap") );
938
939 return M_BMPDATA->m_mask;
940 }
941
942 void wxBitmap::SetMask( wxMask *mask )
943 {
944 wxCHECK_RET( IsOk(), wxT("invalid bitmap") );
945
946 AllocExclusive();
947 delete M_BMPDATA->m_mask;
948 M_BMPDATA->m_mask = mask;
949 }
950
951 bool wxBitmap::CopyFromIcon(const wxIcon& icon)
952 {
953 *this = icon;
954 return IsOk();
955 }
956
957 #ifdef __WXGTK3__
958 static 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
977 wxBitmap wxBitmap::GetSubBitmap( const wxRect& rect) const
978 {
979 wxBitmap ret;
980
981 wxCHECK_MSG(IsOk(), ret, wxT("invalid bitmap"));
982
983 const int w = rect.width;
984 const int h = rect.height;
985 const wxBitmapRefData* bmpData = M_BMPDATA;
986
987 wxCHECK_MSG(rect.x >= 0 && rect.y >= 0 &&
988 rect.x + w <= bmpData->m_width &&
989 rect.y + h <= bmpData->m_height,
990 ret, wxT("invalid bitmap region"));
991
992 wxBitmapRefData * const newRef = new wxBitmapRefData(w, h, bmpData->m_bpp);
993 ret.m_refData = newRef;
994
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)
1008 maskSurf = *bmpData->m_mask;
1009 if (maskSurf)
1010 {
1011 newRef->m_mask = new wxMask(GetSubSurface(maskSurf, rect));
1012 }
1013 #else
1014 if (bmpData->m_pixbuf)
1015 {
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);
1020 }
1021 if (bmpData->m_pixmap)
1022 {
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);
1028 }
1029 GdkPixmap* mask = NULL;
1030 if (bmpData->m_mask)
1031 mask = *bmpData->m_mask;
1032 if (mask)
1033 {
1034 GdkPixmap* sub_mask = gdk_pixmap_new(mask, w, h, 1);
1035 newRef->m_mask = new wxMask(sub_mask);
1036 GdkGC* gc = gdk_gc_new(sub_mask);
1037 gdk_draw_drawable(
1038 sub_mask, gc, mask, rect.x, rect.y, 0, 0, w, h);
1039 g_object_unref(gc);
1040 }
1041 #endif
1042
1043 return ret;
1044 }
1045
1046 bool wxBitmap::SaveFile( const wxString &name, wxBitmapType type, const wxPalette *WXUNUSED(palette) ) const
1047 {
1048 wxCHECK_MSG( IsOk(), false, wxT("invalid bitmap") );
1049
1050 const char* type_name = NULL;
1051 switch (type)
1052 {
1053 case wxBITMAP_TYPE_ANI: type_name = "ani"; break;
1054 case wxBITMAP_TYPE_BMP: type_name = "bmp"; break;
1055 case wxBITMAP_TYPE_GIF: type_name = "gif"; break;
1056 case wxBITMAP_TYPE_ICO: type_name = "ico"; break;
1057 case wxBITMAP_TYPE_JPEG: type_name = "jpeg"; break;
1058 case wxBITMAP_TYPE_PCX: type_name = "pcx"; break;
1059 case wxBITMAP_TYPE_PNG: type_name = "png"; break;
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;
1065 default: break;
1066 }
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
1077 }
1078
1079 bool wxBitmap::LoadFile( const wxString &name, wxBitmapType type )
1080 {
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 }
1087 #if wxUSE_IMAGE
1088 wxImage image;
1089 if (image.LoadFile(name, type) && image.IsOk())
1090 {
1091 *this = wxBitmap(image);
1092 return true;
1093 }
1094 #else
1095 wxUnusedVar(type);
1096 #endif
1097 return false;
1098 }
1099
1100 #if wxUSE_PALETTE
1101 wxPalette *wxBitmap::GetPalette() const
1102 {
1103 return NULL;
1104 }
1105
1106 void wxBitmap::SetPalette(const wxPalette& WXUNUSED(palette))
1107 {
1108 // TODO
1109 }
1110 #endif // wxUSE_PALETTE
1111
1112 void wxBitmap::SetHeight( int height )
1113 {
1114 AllocExclusive();
1115 M_BMPDATA->m_height = height;
1116 }
1117
1118 void wxBitmap::SetWidth( int width )
1119 {
1120 AllocExclusive();
1121 M_BMPDATA->m_width = width;
1122 }
1123
1124 void wxBitmap::SetDepth( int depth )
1125 {
1126 AllocExclusive();
1127 M_BMPDATA->m_bpp = depth;
1128 }
1129
1130 #ifndef __WXGTK3__
1131 void wxBitmap::SetPixmap( GdkPixmap *pixmap )
1132 {
1133 UnRef();
1134
1135 if (!pixmap)
1136 return;
1137
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);
1144 }
1145
1146 GdkPixmap *wxBitmap::GetPixmap() const
1147 {
1148 wxCHECK_MSG( IsOk(), NULL, wxT("invalid bitmap") );
1149
1150 wxBitmapRefData* bmpData = M_BMPDATA;
1151 if (bmpData->m_pixmap)
1152 return bmpData->m_pixmap;
1153
1154 if (bmpData->m_pixbuf)
1155 {
1156 GdkPixmap* pixmap = NULL;
1157 GdkPixmap** mask_pixmap = NULL;
1158 if (gdk_pixbuf_get_has_alpha(bmpData->m_pixbuf))
1159 {
1160 // make new mask from alpha
1161 mask_pixmap = &pixmap;
1162 }
1163 gdk_pixbuf_render_pixmap_and_mask(
1164 bmpData->m_pixbuf, &bmpData->m_pixmap, mask_pixmap, 128);
1165 if (pixmap)
1166 {
1167 delete bmpData->m_mask;
1168 bmpData->m_mask = new wxMask(pixmap);
1169 }
1170 }
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;
1177 }
1178
1179 bool wxBitmap::HasPixmap() const
1180 {
1181 wxCHECK_MSG( IsOk(), false, wxT("invalid bitmap") );
1182
1183 return M_BMPDATA->m_pixmap != NULL;
1184 }
1185 #endif
1186
1187 #ifdef __WXGTK3__
1188 GdkPixbuf* 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
1210 static 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
1253 void 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
1272 cairo_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
1309 void 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)
1318 mask = *bmpData->m_mask;
1319 if (mask)
1320 cairo_mask_surface(cr, mask, x, y);
1321 else
1322 cairo_paint(cr);
1323 }
1324 #endif
1325
1326 GdkPixbuf *wxBitmap::GetPixbuf() const
1327 {
1328 wxCHECK_MSG( IsOk(), NULL, wxT("invalid bitmap") );
1329
1330 wxBitmapRefData* bmpData = M_BMPDATA;
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)
1339 mask = *bmpData->m_mask;
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
1364 if (bmpData->m_pixbuf)
1365 return bmpData->m_pixbuf;
1366
1367 const int w = bmpData->m_width;
1368 const int h = bmpData->m_height;
1369 GdkPixmap* mask = NULL;
1370 if (bmpData->m_mask)
1371 mask = *bmpData->m_mask;
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;
1379 #endif
1380 }
1381
1382 #ifndef __WXGTK3__
1383 bool wxBitmap::HasPixbuf() const
1384 {
1385 wxCHECK_MSG( IsOk(), false, wxT("invalid bitmap") );
1386
1387 return M_BMPDATA->m_pixbuf != NULL;
1388 }
1389
1390 void wxBitmap::PurgeOtherRepresentations(wxBitmap::Representation keep)
1391 {
1392 if (keep == Pixmap && HasPixbuf())
1393 {
1394 g_object_unref (M_BMPDATA->m_pixbuf);
1395 M_BMPDATA->m_pixbuf = NULL;
1396 }
1397 if (keep == Pixbuf && HasPixmap())
1398 {
1399 g_object_unref (M_BMPDATA->m_pixmap);
1400 M_BMPDATA->m_pixmap = NULL;
1401 }
1402 }
1403 #endif
1404
1405 #ifdef wxHAS_RAW_BITMAP
1406 void *wxBitmap::GetRawData(wxPixelDataBase& data, int bpp)
1407 {
1408 void* bits = NULL;
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
1430 GdkPixbuf *pixbuf = GetPixbuf();
1431 const bool hasAlpha = HasAlpha();
1432
1433 // allow access if bpp is valid and matches existence of alpha
1434 if ( pixbuf && ((bpp == 24 && !hasAlpha) || (bpp == 32 && hasAlpha)) )
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 }
1441 #endif
1442 return bits;
1443 }
1444
1445 void wxBitmap::UngetRawData(wxPixelDataBase& WXUNUSED(data))
1446 {
1447 }
1448 #endif // wxHAS_RAW_BITMAP
1449
1450 bool wxBitmap::HasAlpha() const
1451 {
1452 const wxBitmapRefData* bmpData = M_BMPDATA;
1453 #ifdef __WXGTK3__
1454 return bmpData && bmpData->m_bpp == 32;
1455 #else
1456 return bmpData && (bmpData->m_alphaRequested ||
1457 (bmpData->m_pixbuf && gdk_pixbuf_get_has_alpha(bmpData->m_pixbuf)));
1458 #endif
1459 }
1460
1461 wxGDIRefData* wxBitmap::CreateGDIRefData() const
1462 {
1463 return new wxBitmapRefData(0, 0, 0);
1464 }
1465
1466 wxGDIRefData* wxBitmap::CloneGDIRefData(const wxGDIRefData* data) const
1467 {
1468 const wxBitmapRefData* oldRef = static_cast<const wxBitmapRefData*>(data);
1469 wxBitmapRefData * const newRef = new wxBitmapRefData(oldRef->m_width,
1470 oldRef->m_height,
1471 oldRef->m_bpp);
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
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));
1497 wxGtkObject<GdkGC> gc(gdk_gc_new(newRef->m_pixmap));
1498 gdk_draw_drawable(
1499 newRef->m_pixmap, gc, oldRef->m_pixmap, 0, 0, 0, 0, -1, -1);
1500 }
1501 if (oldRef->m_pixbuf != NULL)
1502 {
1503 newRef->m_pixbuf = gdk_pixbuf_copy(oldRef->m_pixbuf);
1504 }
1505 #endif
1506 if (oldRef->m_mask != NULL)
1507 {
1508 newRef->m_mask = new wxMask(*oldRef->m_mask);
1509 }
1510
1511 return newRef;
1512 }
1513
1514 /* static */ void wxBitmap::InitStandardHandlers()
1515 {
1516 // TODO: Insert handler based on GdkPixbufs handler later
1517 }