1/////////////////////////////////////////////////////////////////////////////
2// Name:        src/gtk/bitmap.cpp
3// Purpose:
4// Author:      Robert Roebling
5// RCS-ID:      $Id: bitmap.cpp 57052 2008-12-01 02:03:38Z PC $
6// Copyright:   (c) 1998 Robert Roebling
7// Licence:     wxWindows licence
8/////////////////////////////////////////////////////////////////////////////
9
10// For compilers that support precompilation, includes "wx.h".
11#include "wx/wxprec.h"
12
13#include "wx/bitmap.h"
14
15#ifndef WX_PRECOMP
16    #include "wx/app.h"
17    #include "wx/palette.h"
18    #include "wx/icon.h"
19    #include "wx/math.h"
20    #include "wx/image.h"
21    #include "wx/colour.h"
22#endif
23
24#include "wx/rawbmp.h"
25
26#include <gtk/gtk.h>
27
28//-----------------------------------------------------------------------------
29// data
30//-----------------------------------------------------------------------------
31
32extern GtkWidget *wxGetRootWindow();
33
34//-----------------------------------------------------------------------------
35// wxMask
36//-----------------------------------------------------------------------------
37
38IMPLEMENT_DYNAMIC_CLASS(wxMask,wxObject)
39
40wxMask::wxMask()
41{
42    m_bitmap = (GdkBitmap *) NULL;
43}
44
45wxMask::wxMask( const wxBitmap& bitmap, const wxColour& colour )
46{
47    m_bitmap = (GdkBitmap *) NULL;
48    Create( bitmap, colour );
49}
50
51#if wxUSE_PALETTE
52wxMask::wxMask( const wxBitmap& bitmap, int paletteIndex )
53{
54    m_bitmap = (GdkBitmap *) NULL;
55    Create( bitmap, paletteIndex );
56}
57#endif // wxUSE_PALETTE
58
59wxMask::wxMask( const wxBitmap& bitmap )
60{
61    m_bitmap = (GdkBitmap *) NULL;
62    Create( bitmap );
63}
64
65wxMask::~wxMask()
66{
67    if (m_bitmap)
68        g_object_unref (m_bitmap);
69}
70
71bool wxMask::Create( const wxBitmap& bitmap,
72                     const wxColour& colour )
73{
74    if (m_bitmap)
75    {
76        g_object_unref (m_bitmap);
77        m_bitmap = (GdkBitmap*) NULL;
78    }
79
80    const int w = bitmap.GetWidth();
81    const int h = bitmap.GetHeight();
82
83    // create mask as XBM format bitmap
84
85    // one bit per pixel, each row starts on a byte boundary
86    const size_t out_size = size_t((w + 7) / 8) * unsigned(h);
87    wxByte* out = new wxByte[out_size];
88    // set bits are unmasked
89    memset(out, 0xff, out_size);
90    unsigned bit_index = 0;
91    if (bitmap.HasPixbuf())
92    {
93        const wxByte r_mask = colour.Red();
94        const wxByte g_mask = colour.Green();
95        const wxByte b_mask = colour.Blue();
96        GdkPixbuf* pixbuf = bitmap.GetPixbuf();
97        const wxByte* in = gdk_pixbuf_get_pixels(pixbuf);
98        const int inc = 3 + int(gdk_pixbuf_get_has_alpha(pixbuf) != 0);
99        const int rowpadding = gdk_pixbuf_get_rowstride(pixbuf) - inc * w;
100        for (int y = 0; y < h; y++, in += rowpadding)
101        {
102            for (int x = 0; x < w; x++, in += inc, bit_index++)
103                if (in[0] == r_mask && in[1] == g_mask && in[2] == b_mask)
104                    out[bit_index >> 3] ^= 1 << (bit_index & 7);
105            // move index to next byte boundary
106            bit_index = (bit_index + 7) & ~7u;
107        }
108    }
109    else
110    {
111        GdkImage* image = gdk_drawable_get_image(bitmap.GetPixmap(), 0, 0, w, h);
112        GdkColormap* colormap = gdk_image_get_colormap(image);
113        guint32 mask_pixel;
114        if (colormap == NULL)
115            // mono bitmap, white is pixel value 0
116            mask_pixel = guint32(colour.Red() != 255 || colour.Green() != 255 || colour.Blue() != 255);
117        else
118        {
119            wxColor c(colour);
120            c.CalcPixel(colormap);
121            mask_pixel = c.GetPixel();
122        }
123        for (int y = 0; y < h; y++)
124        {
125            for (int x = 0; x < w; x++, bit_index++)
126                if (gdk_image_get_pixel(image, x, y) == mask_pixel)
127                    out[bit_index >> 3] ^= 1 << (bit_index & 7);
128            bit_index = (bit_index + 7) & ~7u;
129        }
130        g_object_unref(image);
131    }
132    m_bitmap = gdk_bitmap_create_from_data(wxGetRootWindow()->window, (char*)out, w, h);
133    delete[] out;
134    return true;
135}
136
137#if wxUSE_PALETTE
138bool wxMask::Create( const wxBitmap& bitmap, int paletteIndex )
139{
140    unsigned char r,g,b;
141    wxPalette *pal = bitmap.GetPalette();
142
143    wxCHECK_MSG( pal, false, wxT("Cannot create mask from bitmap without palette") );
144
145    pal->GetRGB(paletteIndex, &r, &g, &b);
146
147    return Create(bitmap, wxColour(r, g, b));
148}
149#endif // wxUSE_PALETTE
150
151bool wxMask::Create( const wxBitmap& bitmap )
152{
153    if (m_bitmap)
154    {
155        g_object_unref (m_bitmap);
156        m_bitmap = (GdkBitmap*) NULL;
157    }
158
159    if (!bitmap.Ok()) return false;
160
161    wxCHECK_MSG( bitmap.GetDepth() == 1, false, wxT("Cannot create mask from colour bitmap") );
162
163    m_bitmap = gdk_pixmap_new( wxGetRootWindow()->window, bitmap.GetWidth(), bitmap.GetHeight(), 1 );
164
165    if (!m_bitmap) return false;
166
167    GdkGC *gc = gdk_gc_new( m_bitmap );
168    gdk_gc_set_function(gc, GDK_COPY_INVERT);
169    gdk_draw_drawable(m_bitmap, gc, bitmap.GetPixmap(), 0, 0, 0, 0, bitmap.GetWidth(), bitmap.GetHeight());
170    g_object_unref (gc);
171
172    return true;
173}
174
175GdkBitmap *wxMask::GetBitmap() const
176{
177    return m_bitmap;
178}
179
180//-----------------------------------------------------------------------------
181// wxBitmap
182//-----------------------------------------------------------------------------
183
184class wxBitmapRefData: public wxObjectRefData
185{
186public:
187    wxBitmapRefData();
188    virtual ~wxBitmapRefData();
189
190    GdkPixmap      *m_pixmap;
191    GdkPixbuf      *m_pixbuf;
192    wxMask         *m_mask;
193    int             m_width;
194    int             m_height;
195    int             m_bpp;
196#if wxUSE_PALETTE
197    wxPalette      *m_palette;
198#endif // wxUSE_PALETTE
199};
200
201wxBitmapRefData::wxBitmapRefData()
202{
203    m_pixmap = (GdkPixmap *) NULL;
204    m_pixbuf = (GdkPixbuf *) NULL;
205    m_mask = (wxMask *) NULL;
206    m_width = 0;
207    m_height = 0;
208    m_bpp = 0;
209#if wxUSE_PALETTE
210    m_palette = (wxPalette *) NULL;
211#endif // wxUSE_PALETTE
212}
213
214wxBitmapRefData::~wxBitmapRefData()
215{
216    if (m_pixmap)
217        g_object_unref (m_pixmap);
218    if (m_pixbuf)
219        g_object_unref (m_pixbuf);
220    delete m_mask;
221#if wxUSE_PALETTE
222    delete m_palette;
223#endif // wxUSE_PALETTE
224}
225
226//-----------------------------------------------------------------------------
227
228#define M_BMPDATA wx_static_cast(wxBitmapRefData*, m_refData)
229
230IMPLEMENT_DYNAMIC_CLASS(wxBitmap,wxGDIObject)
231
232wxBitmap::wxBitmap(int width, int height, int depth)
233{
234    Create(width, height, depth);
235}
236
237wxBitmap::wxBitmap(const wxString &filename, wxBitmapType type)
238{
239    LoadFile(filename, type);
240}
241
242wxBitmap::wxBitmap(const char bits[], int width, int height, int depth)
243{
244    wxASSERT(depth == 1);
245    if (width > 0 && height > 0 && depth == 1)
246    {
247        SetPixmap(gdk_bitmap_create_from_data(wxGetRootWindow()->window, bits, width, height));
248
249        wxASSERT_MSG( M_BMPDATA->m_pixmap, wxT("couldn't create bitmap") );
250    }
251}
252
253wxBitmap::wxBitmap(const char* const* bits)
254{
255    wxCHECK2_MSG(bits != NULL, return, wxT("invalid bitmap data"));
256
257    GdkBitmap* mask = NULL;
258    SetPixmap(gdk_pixmap_create_from_xpm_d(wxGetRootWindow()->window, &mask, NULL, wx_const_cast(char**, bits)));
259
260    if (M_BMPDATA->m_pixmap != NULL && mask != NULL)
261    {
262        M_BMPDATA->m_mask = new wxMask;
263        M_BMPDATA->m_mask->m_bitmap = mask;
264    }
265}
266
267wxBitmap::~wxBitmap()
268{
269}
270
271bool wxBitmap::Create( int width, int height, int depth )
272{
273    UnRef();
274
275    if ( width <= 0 || height <= 0 )
276    {
277        return false;
278    }
279
280    if (depth == 32)
281    {
282        SetPixbuf(gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, width, height), 32);
283        // must initialize alpha, otherwise GetPixmap()
284        // will create a mask out of garbage
285        gdk_pixbuf_fill(M_BMPDATA->m_pixbuf, 0x000000ff);
286    }
287    else
288    {
289        if (depth != 1)
290        {
291            const GdkVisual* visual = wxTheApp->GetGdkVisual();
292            if (depth == -1)
293                depth = visual->depth;
294
295            wxCHECK_MSG(depth == visual->depth, false, wxT("invalid bitmap depth"));
296        }
297
298        SetPixmap(gdk_pixmap_new(wxGetRootWindow()->window, width, height, depth));
299    }
300
301    return Ok();
302}
303
304wxBitmap wxBitmap::Rescale(int clipx, int clipy, int clipwidth, int clipheight, int newx, int newy) const
305{
306    wxBitmap bmp;
307
308    wxCHECK_MSG(Ok(), bmp, wxT("invalid bitmap"));
309
310    if (newx==M_BMPDATA->m_width && newy==M_BMPDATA->m_height)
311        return *this;
312
313    int width = wxMax(newx, 1);
314    int height = wxMax(newy, 1);
315    width = wxMin(width, clipwidth);
316    height = wxMin(height, clipheight);
317
318    // scale pixbuf if available and it has alpha or there is no mask
319    if (M_BMPDATA->m_pixbuf != NULL && (
320        M_BMPDATA->m_mask == NULL || gdk_pixbuf_get_has_alpha(M_BMPDATA->m_pixbuf)))
321    {
322        bmp.SetPixbuf(gdk_pixbuf_new(GDK_COLORSPACE_RGB,
323                                     gdk_pixbuf_get_has_alpha(M_BMPDATA->m_pixbuf),
324                                     8, width, height), M_BMPDATA->m_bpp);
325        gdk_pixbuf_scale(M_BMPDATA->m_pixbuf, bmp.GetPixbuf(),
326                         0, 0, width, height,
327                         -clipx, -clipy,
328                         (double)newx/GetWidth(), (double)newy/GetHeight(),
329                         GDK_INTERP_BILINEAR);
330    }
331    else
332    {
333        GdkImage* img = gdk_drawable_get_image(
334            M_BMPDATA->m_pixmap, 0, 0, M_BMPDATA->m_width, M_BMPDATA->m_height);
335
336        bmp.Create(width, height, gdk_drawable_get_depth(M_BMPDATA->m_pixmap));
337        GdkImage* img_scaled = gdk_drawable_get_image(bmp.GetPixmap(), 0, 0, width, height);
338
339        // be careful to use the right scaling factor
340        float scx = (float)M_BMPDATA->m_width/(float)newx;
341        float scy = (float)M_BMPDATA->m_height/(float)newy;
342        // prepare accel-tables
343        int *tablex = (int *)calloc(width,sizeof(int));
344        int *tabley = (int *)calloc(height,sizeof(int));
345
346        // accel table filled with clipped values
347        for (int x = 0; x < width; x++)
348            tablex[x] = (int) (scx * (x+clipx));
349        for (int y = 0; y < height; y++)
350            tabley[y] = (int) (scy * (y+clipy));
351
352        // Main rescaling routine starts here
353        for (int h = 0; h < height; h++)
354        {
355            int old_x = -1;
356            guint32 old_pixval = 0;
357
358            for (int w = 0; w < width; w++)
359            {
360                guint32 pixval;
361                int x = tablex[w];
362                if (x == old_x)
363                    pixval = old_pixval;
364                else
365                {
366                    pixval = gdk_image_get_pixel( img, x, tabley[h] );
367                    old_pixval = pixval;
368                    old_x = x;
369                }
370
371                gdk_image_put_pixel(img_scaled, w, h, pixval);
372            }
373        }
374
375        g_object_unref (img);
376
377        GdkGC* gc = gdk_gc_new(bmp.GetPixmap());
378        gdk_draw_image(bmp.GetPixmap(), gc, img_scaled, 0, 0, 0, 0, -1, -1);
379        g_object_unref(gc);
380        g_object_unref(img_scaled);
381
382        if (GetMask())
383        {
384            size_t dstbyteperline = (width + 7) / 8;
385            char* dst = (char*) malloc(dstbyteperline*height);
386            img = gdk_drawable_get_image(GetMask()->GetBitmap(), 0, 0, GetWidth(), GetHeight());
387
388            for (int h = 0; h < height; h++)
389            {
390                char outbyte = 0;
391                int old_x = -1;
392                guint32 old_pixval = 0;
393
394                for (int w = 0; w < width; w++)
395                {
396                    guint32 pixval;
397                    int x = tablex[w];
398                    if (x == old_x)
399                        pixval = old_pixval;
400                    else
401                    {
402                        pixval = gdk_image_get_pixel( img, x, tabley[h] );
403                        old_pixval = pixval;
404                        old_x = x;
405                    }
406
407                    if (pixval)
408                    {
409                        char bit=1;
410                        char shift = bit << (w % 8);
411                        outbyte |= shift;
412                    }
413
414                    if ((w+1)%8 == 0)
415                    {
416                        dst[h*dstbyteperline+w/8] = outbyte;
417                        outbyte = 0;
418                    }
419                }
420
421                // do not forget the last byte
422                if (width % 8 != 0)
423                    dst[h*dstbyteperline+width/8] = outbyte;
424            }
425            wxMask* mask = new wxMask;
426            mask->m_bitmap = gdk_bitmap_create_from_data( wxGetRootWindow()->window, (gchar *) dst, width, height );
427            bmp.SetMask(mask);
428
429            free( dst );
430            g_object_unref (img);
431        }
432
433        free( tablex );
434        free( tabley );
435    }
436
437    return bmp;
438}
439
440bool wxBitmap::CreateFromImage(const wxImage& image, int depth)
441{
442    UnRef();
443
444    wxCHECK_MSG( image.Ok(), false, wxT("invalid image") );
445    wxCHECK_MSG( depth == -1 || depth == 1, false, wxT("invalid bitmap depth") );
446
447    if (image.GetWidth() <= 0 || image.GetHeight() <= 0)
448        return false;
449
450    // create pixbuf if image has alpha and requested depth is compatible
451    if (image.HasAlpha() && (depth == -1 || depth == 32))
452        return CreateFromImageAsPixbuf(image);
453
454    // otherwise create pixmap, if alpha is present it will be converted to mask
455    return CreateFromImageAsPixmap(image, depth);
456}
457
458bool wxBitmap::CreateFromImageAsPixmap(const wxImage& image, int depth)
459{
460    const int w = image.GetWidth();
461    const int h = image.GetHeight();
462    if (depth == 1)
463    {
464        // create XBM format bitmap
465
466        // one bit per pixel, each row starts on a byte boundary
467        const size_t out_size = size_t((w + 7) / 8) * unsigned(h);
468        wxByte* out = new wxByte[out_size];
469        // set bits are black
470        memset(out, 0xff, out_size);
471        const wxByte* in = image.GetData();
472        unsigned bit_index = 0;
473        for (int y = 0; y < h; y++)
474        {
475            for (int x = 0; x < w; x++, in += 3, bit_index++)
476                if (in[0] == 255 && in[1] == 255 && in[2] == 255)
477                    out[bit_index >> 3] ^= 1 << (bit_index & 7);
478            // move index to next byte boundary
479            bit_index = (bit_index + 7) & ~7u;
480        }
481        SetPixmap(gdk_bitmap_create_from_data(wxGetRootWindow()->window, (char*)out, w, h));
482        delete[] out;
483    }
484    else
485    {
486        SetPixmap(gdk_pixmap_new(wxGetRootWindow()->window, w, h, depth));
487        GdkGC* gc = gdk_gc_new(M_BMPDATA->m_pixmap);
488        gdk_draw_rgb_image(
489            M_BMPDATA->m_pixmap, gc,
490            0, 0, w, h,
491            GDK_RGB_DITHER_NONE, image.GetData(), w * 3);
492        g_object_unref(gc);
493    }
494
495    const wxByte* alpha = image.GetAlpha();
496    if (alpha != NULL || image.HasMask())
497    {
498        // create mask as XBM format bitmap
499
500        const size_t out_size = size_t((w + 7) / 8) * unsigned(h);
501        wxByte* out = new wxByte[out_size];
502        memset(out, 0xff, out_size);
503        unsigned bit_index = 0;
504        if (alpha != NULL)
505        {
506            for (int y = 0; y < h; y++)
507            {
508                for (int x = 0; x < w; x++, bit_index++)
509                    if (*alpha++ < wxIMAGE_ALPHA_THRESHOLD)
510                        out[bit_index >> 3] ^= 1 << (bit_index & 7);
511                bit_index = (bit_index + 7) & ~7u;
512            }
513        }
514        else
515        {
516            const wxByte r_mask = image.GetMaskRed();
517            const wxByte g_mask = image.GetMaskGreen();
518            const wxByte b_mask = image.GetMaskBlue();
519            const wxByte* in = image.GetData();
520            for (int y = 0; y < h; y++)
521            {
522                for (int x = 0; x < w; x++, in += 3, bit_index++)
523                    if (in[0] == r_mask && in[1] == g_mask && in[2] == b_mask)
524                        out[bit_index >> 3] ^= 1 << (bit_index & 7);
525                bit_index = (bit_index + 7) & ~7u;
526            }
527        }
528        wxMask* mask = new wxMask;
529        mask->m_bitmap = gdk_bitmap_create_from_data(M_BMPDATA->m_pixmap, (char*)out, w, h);
530        SetMask(mask);
531        delete[] out;
532    }
533    return true;
534}
535
536bool wxBitmap::CreateFromImageAsPixbuf(const wxImage& image)
537{
538    wxASSERT(image.HasAlpha());
539
540    int width = image.GetWidth();
541    int height = image.GetHeight();
542
543    Create(width, height, 32);
544    GdkPixbuf* pixbuf = M_BMPDATA->m_pixbuf;
545    if (!pixbuf)
546        return false;
547
548    // Copy the data:
549    const unsigned char* in = image.GetData();
550    unsigned char *out = gdk_pixbuf_get_pixels(pixbuf);
551    unsigned char *alpha = image.GetAlpha();
552
553    int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - 4 * width;
554
555    for (int y = 0; y < height; y++, out += rowpad)
556    {
557        for (int x = 0; x < width; x++, alpha++, out += 4, in += 3)
558        {
559            out[0] = in[0];
560            out[1] = in[1];
561            out[2] = in[2];
562            out[3] = *alpha;
563        }
564    }
565
566    return true;
567}
568
569wxImage wxBitmap::ConvertToImage() const
570{
571    wxCHECK_MSG( Ok(), wxNullImage, wxT("invalid bitmap") );
572
573    const int w = GetWidth();
574    const int h = GetHeight();
575    wxImage image(w, h, false);
576    unsigned char *data = image.GetData();
577
578    wxCHECK_MSG(data != NULL, wxNullImage, wxT("couldn't create image") );
579
580    // prefer pixbuf if available, it will preserve alpha and should be quicker
581    if (HasPixbuf())
582    {
583        GdkPixbuf *pixbuf = GetPixbuf();
584        unsigned char* alpha = NULL;
585        if (gdk_pixbuf_get_has_alpha(pixbuf))
586        {
587            image.SetAlpha();
588            alpha = image.GetAlpha();
589        }
590        const unsigned char* in = gdk_pixbuf_get_pixels(pixbuf);
591        unsigned char *out = data;
592        const int inc = 3 + int(alpha != NULL);
593        const int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - inc * w;
594
595        for (int y = 0; y < h; y++, in += rowpad)
596        {
597            for (int x = 0; x < w; x++, in += inc, out += 3)
598            {
599                out[0] = in[0];
600                out[1] = in[1];
601                out[2] = in[2];
602                if (alpha != NULL)
603                    *alpha++ = in[3];
604            }
605        }
606    }
607    else
608    {
609        GdkPixmap* pixmap = GetPixmap();
610        GdkPixmap* pixmap_invert = NULL;
611        if (GetDepth() == 1)
612        {
613            // mono bitmaps are inverted, i.e. 0 is white
614            pixmap_invert = gdk_pixmap_new(pixmap, w, h, 1);
615            GdkGC* gc = gdk_gc_new(pixmap_invert);
616            gdk_gc_set_function(gc, GDK_COPY_INVERT);
617            gdk_draw_drawable(pixmap_invert, gc, pixmap, 0, 0, 0, 0, w, h);
618            g_object_unref(gc);
619            pixmap = pixmap_invert;
620        }
621        // create a pixbuf which shares data with the wxImage
622        GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data(
623            data, GDK_COLORSPACE_RGB, false, 8, w, h, 3 * w, NULL, NULL);
624
625        gdk_pixbuf_get_from_drawable(pixbuf, pixmap, NULL, 0, 0, 0, 0, w, h);
626
627        g_object_unref(pixbuf);
628        if (pixmap_invert != NULL)
629            g_object_unref(pixmap_invert);
630    }
631    // convert mask, unless there is already alpha
632    if (GetMask() && !image.HasAlpha())
633    {
634        // we hard code the mask colour for now but we could also make an
635        // effort (and waste time) to choose a colour not present in the
636        // image already to avoid having to fudge the pixels below --
637        // whether it's worth to do it is unclear however
638        const int MASK_RED = 1;
639        const int MASK_GREEN = 2;
640        const int MASK_BLUE = 3;
641        const int MASK_BLUE_REPLACEMENT = 2;
642
643        image.SetMaskColour(MASK_RED, MASK_GREEN, MASK_BLUE);
644        GdkImage* image_mask = gdk_drawable_get_image(GetMask()->GetBitmap(), 0, 0, w, h);
645
646        for (int y = 0; y < h; y++)
647        {
648            for (int x = 0; x < w; x++, data += 3)
649            {
650                if (gdk_image_get_pixel(image_mask, x, y) == 0)
651                {
652                    data[0] = MASK_RED;
653                    data[1] = MASK_GREEN;
654                    data[2] = MASK_BLUE;
655                }
656                else if (data[0] == MASK_RED && data[1] == MASK_GREEN && data[2] == MASK_BLUE)
657                {
658                    // we have to fudge the colour a bit to prevent
659                    // this pixel from appearing transparent
660                    data[2] = MASK_BLUE_REPLACEMENT;
661                }
662            }
663        }
664        g_object_unref(image_mask);
665    }
666
667    return image;
668}
669
670bool wxBitmap::IsOk() const
671{
672    return (m_refData != NULL) &&
673           (
674              M_BMPDATA->m_pixbuf ||
675              M_BMPDATA->m_pixmap
676           );
677}
678
679int wxBitmap::GetHeight() const
680{
681    wxCHECK_MSG( Ok(), -1, wxT("invalid bitmap") );
682
683    return M_BMPDATA->m_height;
684}
685
686int wxBitmap::GetWidth() const
687{
688    wxCHECK_MSG( Ok(), -1, wxT("invalid bitmap") );
689
690    return M_BMPDATA->m_width;
691}
692
693int wxBitmap::GetDepth() const
694{
695    wxCHECK_MSG( Ok(), -1, wxT("invalid bitmap") );
696
697    return M_BMPDATA->m_bpp;
698}
699
700wxMask *wxBitmap::GetMask() const
701{
702    wxCHECK_MSG( Ok(), (wxMask *) NULL, wxT("invalid bitmap") );
703
704    return M_BMPDATA->m_mask;
705}
706
707void wxBitmap::SetMask( wxMask *mask )
708{
709    wxCHECK_RET( Ok(), wxT("invalid bitmap") );
710
711    AllocExclusive();
712    delete M_BMPDATA->m_mask;
713    M_BMPDATA->m_mask = mask;
714}
715
716bool wxBitmap::CopyFromIcon(const wxIcon& icon)
717{
718    *this = icon;
719    return Ok();
720}
721
722wxBitmap wxBitmap::GetSubBitmap( const wxRect& rect) const
723{
724    wxBitmap ret;
725
726    wxCHECK_MSG(Ok(), ret, wxT("invalid bitmap"));
727    wxCHECK_MSG(rect.x >= 0 && rect.y >= 0 &&
728                rect.x + rect.width <= M_BMPDATA->m_width &&
729                rect.y + rect.height <= M_BMPDATA->m_height,
730                ret, wxT("invalid bitmap region"));
731
732    if (HasPixbuf() || M_BMPDATA->m_bpp == 32)
733    {
734        GdkPixbuf *pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
735                                           gdk_pixbuf_get_has_alpha(GetPixbuf()),
736                                           8, rect.width, rect.height);
737        ret.SetPixbuf(pixbuf, M_BMPDATA->m_bpp);
738        gdk_pixbuf_copy_area(GetPixbuf(),
739                             rect.x, rect.y, rect.width, rect.height,
740                             pixbuf, 0, 0);
741    }
742    else
743    {
744        ret.Create(rect.width, rect.height, M_BMPDATA->m_bpp);
745        GdkGC *gc = gdk_gc_new( ret.GetPixmap() );
746        gdk_draw_drawable( ret.GetPixmap(), gc, GetPixmap(), rect.x, rect.y, 0, 0, rect.width, rect.height );
747        g_object_unref (gc);
748    }
749    // make mask, unless there is already alpha
750    if (GetMask() && !HasAlpha())
751    {
752        wxMask *mask = new wxMask;
753        mask->m_bitmap = gdk_pixmap_new( wxGetRootWindow()->window, rect.width, rect.height, 1 );
754
755        GdkGC *gc = gdk_gc_new( mask->m_bitmap );
756        gdk_draw_drawable(mask->m_bitmap, gc, M_BMPDATA->m_mask->m_bitmap, rect.x, rect.y, 0, 0, rect.width, rect.height);
757        g_object_unref (gc);
758
759        ret.SetMask( mask );
760    }
761
762    return ret;
763}
764
765bool wxBitmap::SaveFile( const wxString &name, wxBitmapType type, const wxPalette *WXUNUSED(palette) ) const
766{
767    wxCHECK_MSG( Ok(), false, wxT("invalid bitmap") );
768
769    // Try to save the bitmap via wxImage handlers:
770    wxImage image = ConvertToImage();
771    return image.Ok() && image.SaveFile(name, type);
772}
773
774bool wxBitmap::LoadFile( const wxString &name, wxBitmapType type )
775{
776    UnRef();
777
778    if (type == wxBITMAP_TYPE_XPM)
779    {
780        GdkBitmap *mask = (GdkBitmap*) NULL;
781        SetPixmap(gdk_pixmap_create_from_xpm(wxGetRootWindow()->window, &mask, NULL, name.fn_str()));
782
783        if (mask)
784        {
785            M_BMPDATA->m_mask = new wxMask;
786            M_BMPDATA->m_mask->m_bitmap = mask;
787        }
788    }
789    else // try if wxImage can load it
790    {
791        wxImage image;
792        if (image.LoadFile(name, type) && image.Ok())
793            CreateFromImage(image, -1);
794    }
795
796    return Ok();
797}
798
799#if wxUSE_PALETTE
800wxPalette *wxBitmap::GetPalette() const
801{
802    wxCHECK_MSG(Ok(), NULL, wxT("invalid bitmap"));
803
804    return M_BMPDATA->m_palette;
805}
806
807void wxBitmap::SetPalette(const wxPalette& WXUNUSED(palette))
808{
809    // TODO
810}
811#endif // wxUSE_PALETTE
812
813void wxBitmap::SetHeight( int height )
814{
815    AllocExclusive();
816    M_BMPDATA->m_height = height;
817}
818
819void wxBitmap::SetWidth( int width )
820{
821    AllocExclusive();
822    M_BMPDATA->m_width = width;
823}
824
825void wxBitmap::SetDepth( int depth )
826{
827    AllocExclusive();
828    M_BMPDATA->m_bpp = depth;
829}
830
831void wxBitmap::SetPixmap( GdkPixmap *pixmap )
832{
833    if (!m_refData)
834        m_refData = new wxBitmapRefData;
835
836    // AllocExclusive should not be needed for this internal function
837    wxASSERT(m_refData->GetRefCount() == 1);
838    wxASSERT(M_BMPDATA->m_pixmap == NULL);
839    M_BMPDATA->m_pixmap = pixmap;
840    gdk_drawable_get_size(pixmap, &M_BMPDATA->m_width, &M_BMPDATA->m_height);
841    M_BMPDATA->m_bpp = gdk_drawable_get_depth(pixmap);
842    PurgeOtherRepresentations(Pixmap);
843}
844
845GdkPixmap *wxBitmap::GetPixmap() const
846{
847    wxCHECK_MSG( Ok(), (GdkPixmap *) NULL, wxT("invalid bitmap") );
848
849    // create the pixmap on the fly if we use Pixbuf representation:
850    if (M_BMPDATA->m_pixmap == NULL)
851    {
852        GdkPixmap** pmask = NULL;
853        if (gdk_pixbuf_get_has_alpha(M_BMPDATA->m_pixbuf))
854        {
855            // make new mask from alpha
856            delete M_BMPDATA->m_mask;
857            M_BMPDATA->m_mask = new wxMask;
858            pmask = &M_BMPDATA->m_mask->m_bitmap;
859        }
860        gdk_pixbuf_render_pixmap_and_mask(M_BMPDATA->m_pixbuf,
861                                          &M_BMPDATA->m_pixmap,
862                                          pmask,
863                                          wxIMAGE_ALPHA_THRESHOLD);
864    }
865
866    return M_BMPDATA->m_pixmap;
867}
868
869bool wxBitmap::HasPixmap() const
870{
871    wxCHECK_MSG( Ok(), false, wxT("invalid bitmap") );
872
873    return M_BMPDATA->m_pixmap != NULL;
874}
875
876GdkPixbuf *wxBitmap::GetPixbuf() const
877{
878    wxCHECK_MSG( Ok(), NULL, wxT("invalid bitmap") );
879
880    if (M_BMPDATA->m_pixbuf == NULL)
881    {
882        int width = GetWidth();
883        int height = GetHeight();
884
885        GdkPixbuf *pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
886                                           GetMask() != NULL,
887                                           8, width, height);
888        M_BMPDATA->m_pixbuf = pixbuf;
889        gdk_pixbuf_get_from_drawable(pixbuf, M_BMPDATA->m_pixmap, NULL,
890                                     0, 0, 0, 0, width, height);
891
892        // apply the mask to created pixbuf:
893        if (M_BMPDATA->m_pixbuf && M_BMPDATA->m_mask)
894        {
895            GdkPixbuf *pmask =
896                gdk_pixbuf_get_from_drawable(NULL,
897                                             M_BMPDATA->m_mask->GetBitmap(),
898                                             NULL,
899                                             0, 0, 0, 0, width, height);
900            if (pmask)
901            {
902                guchar *bmp = gdk_pixbuf_get_pixels(pixbuf);
903                guchar *mask = gdk_pixbuf_get_pixels(pmask);
904                int bmprowinc = gdk_pixbuf_get_rowstride(pixbuf) - 4 * width;
905                int maskrowinc = gdk_pixbuf_get_rowstride(pmask) - 3 * width;
906
907                for (int y = 0; y < height;
908                     y++, bmp += bmprowinc, mask += maskrowinc)
909                {
910                    for (int x = 0; x < width; x++, bmp += 4, mask += 3)
911                    {
912                        if (mask[0] == 0 /*black pixel*/)
913                            bmp[3] = 0;
914                    }
915                }
916
917                g_object_unref (pmask);
918            }
919        }
920    }
921
922    return M_BMPDATA->m_pixbuf;
923}
924
925bool wxBitmap::HasPixbuf() const
926{
927    wxCHECK_MSG( Ok(), false, wxT("invalid bitmap") );
928
929    return M_BMPDATA->m_pixbuf != NULL;
930}
931
932void wxBitmap::SetPixbuf(GdkPixbuf* pixbuf, int depth)
933{
934    if (!m_refData)
935        m_refData = new wxBitmapRefData;
936
937    // AllocExclusive should not be needed for this internal function
938    wxASSERT(m_refData->GetRefCount() == 1);
939    wxASSERT(M_BMPDATA->m_pixbuf == NULL);
940    M_BMPDATA->m_pixbuf = pixbuf;
941    M_BMPDATA->m_width = gdk_pixbuf_get_width(pixbuf);
942    M_BMPDATA->m_height = gdk_pixbuf_get_height(pixbuf);
943    // if depth specified
944    if (depth != 0)
945        M_BMPDATA->m_bpp = depth;
946    else if (M_BMPDATA->m_bpp == 0)
947        // use something reasonable
948        M_BMPDATA->m_bpp = wxTheApp->GetGdkVisual()->depth;
949    PurgeOtherRepresentations(Pixbuf);
950}
951
952void wxBitmap::PurgeOtherRepresentations(wxBitmap::Representation keep)
953{
954    if (keep == Pixmap && HasPixbuf())
955    {
956        g_object_unref (M_BMPDATA->m_pixbuf);
957        M_BMPDATA->m_pixbuf = NULL;
958    }
959    if (keep == Pixbuf && HasPixmap())
960    {
961        g_object_unref (M_BMPDATA->m_pixmap);
962        M_BMPDATA->m_pixmap = NULL;
963    }
964}
965
966void *wxBitmap::GetRawData(wxPixelDataBase& data, int bpp)
967{
968    void* bits = NULL;
969    GdkPixbuf *pixbuf = GetPixbuf();
970    const bool hasAlpha = HasAlpha();
971    // allow access if bpp is valid and matches existence of alpha
972    if ( pixbuf && (((bpp == 24) && !hasAlpha) || ((bpp == 32) && hasAlpha)) )
973    {
974        data.m_height = gdk_pixbuf_get_height( pixbuf );
975        data.m_width = gdk_pixbuf_get_width( pixbuf );
976        data.m_stride = gdk_pixbuf_get_rowstride( pixbuf );
977        bits = gdk_pixbuf_get_pixels(pixbuf);
978    }
979    return bits;
980}
981
982void wxBitmap::UngetRawData(wxPixelDataBase& WXUNUSED(data))
983{
984}
985
986bool wxBitmap::HasAlpha() const
987{
988    return m_refData != NULL && M_BMPDATA->m_pixbuf != NULL &&
989        gdk_pixbuf_get_has_alpha(M_BMPDATA->m_pixbuf);
990}
991
992void wxBitmap::UseAlpha()
993{
994    GdkPixbuf* pixbuf = GetPixbuf();
995    // add alpha if necessary
996    if (!gdk_pixbuf_get_has_alpha(pixbuf))
997    {
998        M_BMPDATA->m_pixbuf = NULL;
999        AllocExclusive();
1000        M_BMPDATA->m_pixbuf = gdk_pixbuf_add_alpha(pixbuf, false, 0, 0, 0);
1001        g_object_unref(pixbuf);
1002    }
1003}
1004
1005wxObjectRefData* wxBitmap::CreateRefData() const
1006{
1007    return new wxBitmapRefData;
1008}
1009
1010wxObjectRefData* wxBitmap::CloneRefData(const wxObjectRefData* data) const
1011{
1012    const wxBitmapRefData* oldRef = wx_static_cast(const wxBitmapRefData*, data);
1013    wxBitmapRefData* newRef = new wxBitmapRefData;
1014    newRef->m_width = oldRef->m_width;
1015    newRef->m_height = oldRef->m_height;
1016    newRef->m_bpp = oldRef->m_bpp;
1017    if (oldRef->m_pixmap != NULL)
1018    {
1019        newRef->m_pixmap = gdk_pixmap_new(
1020            oldRef->m_pixmap, oldRef->m_width, oldRef->m_height,
1021            // use pixmap depth, m_bpp may not match
1022            gdk_drawable_get_depth(oldRef->m_pixmap));
1023        GdkGC* gc = gdk_gc_new(newRef->m_pixmap);
1024        gdk_draw_drawable(
1025            newRef->m_pixmap, gc, oldRef->m_pixmap, 0, 0, 0, 0, -1, -1);
1026        g_object_unref(gc);
1027    }
1028    if (oldRef->m_pixbuf != NULL)
1029    {
1030        newRef->m_pixbuf = gdk_pixbuf_copy(oldRef->m_pixbuf);
1031    }
1032    if (oldRef->m_mask != NULL)
1033    {
1034        newRef->m_mask = new wxMask;
1035        newRef->m_mask->m_bitmap = gdk_pixmap_new(
1036            oldRef->m_mask->m_bitmap, oldRef->m_width, oldRef->m_height, 1);
1037        GdkGC* gc = gdk_gc_new(newRef->m_mask->m_bitmap);
1038        gdk_draw_drawable(newRef->m_mask->m_bitmap,
1039            gc, oldRef->m_mask->m_bitmap, 0, 0, 0, 0, -1, -1);
1040        g_object_unref(gc);
1041    }
1042#if wxUSE_PALETTE
1043    // implement this if SetPalette is ever implemented
1044    wxASSERT(oldRef->m_palette == NULL);
1045#endif
1046
1047    return newRef;
1048}
1049
1050//-----------------------------------------------------------------------------
1051// wxBitmapHandler
1052//-----------------------------------------------------------------------------
1053
1054IMPLEMENT_ABSTRACT_CLASS(wxBitmapHandler, wxBitmapHandlerBase)
1055
1056/* static */ void wxBitmap::InitStandardHandlers()
1057{
1058    // TODO: Insert handler based on GdkPixbufs handler later
1059}
1060