dolphin/Externals/wxWidgets3/src/gtk/bitmap.cpp
EmptyChaos 822326eea9 Update wxWidgets to 3.1.0
From wxWidgets master 81570ae070b35c9d52de47b1f14897f3ff1a66c7.

include/wx/defs.h -- __w64 warning disable patch by comex brought forward.

include/wx/msw/window.h -- added GetContentScaleFactor() which was not implemented on Windows but is necessary for wxBitmap scaling on Mac OS X so it needs to work to avoid #ifdef-ing the code.

src/gtk/window.cpp -- Modified DoSetClientSize() to direct call wxWindowGTK::DoSetSize() instead of using public wxWindowBase::SetSize() which now prevents derived classes (like wxAuiToolbar) intercepting the call and breaking it. This matches Windows which does NOT need to call DoSetSize internally. End result is this fixes Dolphin's debug tools toolbars on Linux.

src/osx/window_osx.cpp -- Same fix as for GTK since it has the same issue.

src/msw/radiobox.cpp -- Hacked to fix display in HiDPI (was clipping off end of text).

Updated CMakeLists for Linux and Mac OS X. Small code changes to Dolphin to fix debug error boxes, deprecation warnings, and retain previous UI behavior on Windows.
2016-06-26 15:25:29 +10:00

1565 lines
46 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/gtk/bitmap.cpp
// Purpose:
// Author: Robert Roebling
// Copyright: (c) 1998 Robert Roebling
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#include "wx/bitmap.h"
#ifndef WX_PRECOMP
#include "wx/icon.h"
#include "wx/image.h"
#include "wx/colour.h"
#include "wx/cursor.h"
#endif
#include "wx/rawbmp.h"
#include "wx/gtk/private/object.h"
#include "wx/gtk/private.h"
#include <gtk/gtk.h>
GdkWindow* wxGetTopLevelGDK();
#ifndef __WXGTK3__
static void PixmapToPixbuf(GdkPixmap* pixmap, GdkPixbuf* pixbuf, int w, int h)
{
gdk_pixbuf_get_from_drawable(pixbuf, pixmap, NULL, 0, 0, 0, 0, w, h);
if (gdk_drawable_get_depth(pixmap) == 1)
{
// invert to match XBM convention
guchar* p = gdk_pixbuf_get_pixels(pixbuf);
const int inc = 3 + int(gdk_pixbuf_get_has_alpha(pixbuf) != 0);
const int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - w * inc;
for (int y = h; y; y--, p += rowpad)
for (int x = w; x; x--, p += inc)
{
// pixels are either (0,0,0) or (0xff,0xff,0xff)
p[0] = ~p[0];
p[1] = ~p[1];
p[2] = ~p[2];
}
}
}
static void MaskToAlpha(GdkPixmap* mask, GdkPixbuf* pixbuf, int w, int h)
{
GdkPixbuf* mask_pixbuf = gdk_pixbuf_get_from_drawable(
NULL, mask, NULL, 0, 0, 0, 0, w, h);
guchar* p = gdk_pixbuf_get_pixels(pixbuf) + 3;
const guchar* mask_data = gdk_pixbuf_get_pixels(mask_pixbuf);
const int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - w * 4;
const int mask_rowpad = gdk_pixbuf_get_rowstride(mask_pixbuf) - w * 3;
for (int y = h; y; y--, p += rowpad, mask_data += mask_rowpad)
{
for (int x = w; x; x--, p += 4, mask_data += 3)
{
*p = 255;
// no need to test all 3 components,
// pixels are either (0,0,0) or (0xff,0xff,0xff)
if (mask_data[0] == 0)
*p = 0;
}
}
g_object_unref(mask_pixbuf);
}
#endif
//-----------------------------------------------------------------------------
// wxMask
//-----------------------------------------------------------------------------
wxIMPLEMENT_DYNAMIC_CLASS(wxMask, wxMaskBase);
wxMask::wxMask()
{
m_bitmap = NULL;
}
wxMask::wxMask(const wxMask& mask)
{
#ifdef __WXGTK3__
m_bitmap = NULL;
if (mask.m_bitmap)
{
const int w = cairo_image_surface_get_width(mask.m_bitmap);
const int h = cairo_image_surface_get_height(mask.m_bitmap);
m_bitmap = cairo_image_surface_create(CAIRO_FORMAT_A8, w, h);
const guchar* src = cairo_image_surface_get_data(mask.m_bitmap);
guchar* dst = cairo_image_surface_get_data(m_bitmap);
const int stride = cairo_image_surface_get_stride(m_bitmap);
wxASSERT(stride == cairo_image_surface_get_stride(mask.m_bitmap));
memcpy(dst, src, stride * h);
cairo_surface_mark_dirty(m_bitmap);
}
#else
if ( !mask.m_bitmap )
{
m_bitmap = NULL;
return;
}
// create a copy of an existing mask
gint w, h;
gdk_drawable_get_size(mask.m_bitmap, &w, &h);
m_bitmap = gdk_pixmap_new(mask.m_bitmap, w, h, 1);
wxGtkObject<GdkGC> gc(gdk_gc_new(m_bitmap));
gdk_draw_drawable(m_bitmap, gc, mask.m_bitmap, 0, 0, 0, 0, -1, -1);
#endif
}
wxMask::wxMask( const wxBitmap& bitmap, const wxColour& colour )
{
m_bitmap = NULL;
InitFromColour(bitmap, colour);
}
#if wxUSE_PALETTE
wxMask::wxMask( const wxBitmap& bitmap, int paletteIndex )
{
m_bitmap = NULL;
Create( bitmap, paletteIndex );
}
#endif // wxUSE_PALETTE
wxMask::wxMask( const wxBitmap& bitmap )
{
m_bitmap = NULL;
InitFromMonoBitmap(bitmap);
}
#ifdef __WXGTK3__
wxMask::wxMask(cairo_surface_t* bitmap)
#else
wxMask::wxMask(GdkPixmap* bitmap)
#endif
{
m_bitmap = bitmap;
}
wxMask::~wxMask()
{
if (m_bitmap)
{
#ifdef __WXGTK3__
cairo_surface_destroy(m_bitmap);
#else
g_object_unref (m_bitmap);
#endif
}
}
void wxMask::FreeData()
{
if (m_bitmap)
{
#ifdef __WXGTK3__
cairo_surface_destroy(m_bitmap);
#else
g_object_unref (m_bitmap);
#endif
m_bitmap = NULL;
}
}
bool wxMask::InitFromColour(const wxBitmap& bitmap, const wxColour& colour)
{
const int w = bitmap.GetWidth();
const int h = bitmap.GetHeight();
#ifdef __WXGTK3__
m_bitmap = cairo_image_surface_create(CAIRO_FORMAT_A8, w, h);
GdkPixbuf* pixbuf = bitmap.GetPixbufNoMask();
const guchar* src = gdk_pixbuf_get_pixels(pixbuf);
guchar* dst = cairo_image_surface_get_data(m_bitmap);
const int stride_src = gdk_pixbuf_get_rowstride(pixbuf);
const int stride_dst = cairo_image_surface_get_stride(m_bitmap);
const int src_inc = gdk_pixbuf_get_n_channels(pixbuf);
const guchar r = colour.Red();
const guchar g = colour.Green();
const guchar b = colour.Blue();
for (int j = 0; j < h; j++, src += stride_src, dst += stride_dst)
{
const guchar* s = src;
for (int i = 0; i < w; i++, s += src_inc)
{
dst[i] = 0xff;
if (s[0] == r && s[1] == g && s[2] == b)
dst[i] = 0;
}
}
cairo_surface_mark_dirty(m_bitmap);
#else
// create mask as XBM format bitmap
// one bit per pixel, each row starts on a byte boundary
const size_t out_size = size_t((w + 7) / 8) * unsigned(h);
wxByte* out = new wxByte[out_size];
// set bits are unmasked
memset(out, 0xff, out_size);
unsigned bit_index = 0;
if (bitmap.HasPixbuf())
{
const wxByte r_mask = colour.Red();
const wxByte g_mask = colour.Green();
const wxByte b_mask = colour.Blue();
GdkPixbuf* pixbuf = bitmap.GetPixbuf();
const wxByte* in = gdk_pixbuf_get_pixels(pixbuf);
const int inc = 3 + int(gdk_pixbuf_get_has_alpha(pixbuf) != 0);
const int rowpadding = gdk_pixbuf_get_rowstride(pixbuf) - inc * w;
for (int y = 0; y < h; y++, in += rowpadding)
{
for (int x = 0; x < w; x++, in += inc, bit_index++)
if (in[0] == r_mask && in[1] == g_mask && in[2] == b_mask)
out[bit_index >> 3] ^= 1 << (bit_index & 7);
// move index to next byte boundary
bit_index = (bit_index + 7) & ~7u;
}
}
else
{
GdkImage* image = gdk_drawable_get_image(bitmap.GetPixmap(), 0, 0, w, h);
GdkColormap* colormap = gdk_image_get_colormap(image);
guint32 mask_pixel;
if (colormap == NULL)
// mono bitmap, white is pixel value 0
mask_pixel = guint32(colour.Red() != 255 || colour.Green() != 255 || colour.Blue() != 255);
else
{
wxColor c(colour);
c.CalcPixel(colormap);
mask_pixel = c.GetPixel();
}
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++, bit_index++)
if (gdk_image_get_pixel(image, x, y) == mask_pixel)
out[bit_index >> 3] ^= 1 << (bit_index & 7);
bit_index = (bit_index + 7) & ~7u;
}
g_object_unref(image);
}
m_bitmap = gdk_bitmap_create_from_data(wxGetTopLevelGDK(), (char*)out, w, h);
delete[] out;
#endif
return true;
}
bool wxMask::InitFromMonoBitmap(const wxBitmap& bitmap)
{
if (!bitmap.IsOk()) return false;
wxCHECK_MSG( bitmap.GetDepth() == 1, false, wxT("Cannot create mask from colour bitmap") );
#ifdef __WXGTK3__
InitFromColour(bitmap, *wxBLACK);
#else
m_bitmap = gdk_pixmap_new(wxGetTopLevelGDK(), bitmap.GetWidth(), bitmap.GetHeight(), 1);
if (!m_bitmap) return false;
wxGtkObject<GdkGC> gc(gdk_gc_new( m_bitmap ));
gdk_gc_set_function(gc, GDK_COPY_INVERT);
gdk_draw_drawable(m_bitmap, gc, bitmap.GetPixmap(), 0, 0, 0, 0, bitmap.GetWidth(), bitmap.GetHeight());
#endif
return true;
}
wxBitmap wxMask::GetBitmap() const
{
wxBitmap bitmap;
if (m_bitmap)
{
#ifdef __WXGTK3__
cairo_surface_t* mask = m_bitmap;
const int w = cairo_image_surface_get_width(mask);
const int h = cairo_image_surface_get_height(mask);
GdkPixbuf* pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, false, 8, w, h);
const guchar* src = cairo_image_surface_get_data(mask);
guchar* dst = gdk_pixbuf_get_pixels(pixbuf);
const int stride_src = cairo_image_surface_get_stride(mask);
const int stride_dst = gdk_pixbuf_get_rowstride(pixbuf);
for (int j = 0; j < h; j++, src += stride_src, dst += stride_dst)
{
guchar* d = dst;
for (int i = 0; i < w; i++, d += 3)
{
d[0] = src[i];
d[1] = src[i];
d[2] = src[i];
}
}
bitmap = wxBitmap(pixbuf, 1);
#else
GdkPixmap* mask = m_bitmap;
int w, h;
gdk_drawable_get_size(mask, &w, &h);
GdkPixmap* pixmap = gdk_pixmap_new(mask, w, h, -1);
GdkGC* gc = gdk_gc_new(pixmap);
gdk_gc_set_function(gc, GDK_COPY_INVERT);
gdk_draw_drawable(pixmap, gc, mask, 0, 0, 0, 0, w, h);
g_object_unref(gc);
bitmap = wxBitmap(pixmap);
#endif
}
return bitmap;
}
#ifdef __WXGTK3__
wxMask::operator cairo_surface_t*() const
#else
wxMask::operator GdkPixmap*() const
#endif
{
return m_bitmap;
}
//-----------------------------------------------------------------------------
// wxBitmapRefData
//-----------------------------------------------------------------------------
class wxBitmapRefData: public wxGDIRefData
{
public:
wxBitmapRefData(int width, int height, int depth);
virtual ~wxBitmapRefData();
virtual bool IsOk() const wxOVERRIDE;
#ifdef __WXGTK3__
GdkPixbuf* m_pixbufMask;
GdkPixbuf* m_pixbufNoMask;
cairo_surface_t* m_surface;
double m_scaleFactor;
#else
GdkPixmap *m_pixmap;
GdkPixbuf *m_pixbuf;
#endif
wxMask *m_mask;
int m_width;
int m_height;
int m_bpp;
#ifndef __WXGTK3__
bool m_alphaRequested;
#endif
// We don't provide a copy ctor as copying m_pixmap and m_pixbuf properly
// is expensive and we don't want to do it implicitly (and possibly
// accidentally). wxBitmap::CloneGDIRefData() which does need to do it does
// it explicitly itself.
wxDECLARE_NO_COPY_CLASS(wxBitmapRefData);
};
wxBitmapRefData::wxBitmapRefData(int width, int height, int depth)
{
#ifdef __WXGTK3__
m_pixbufMask = NULL;
m_pixbufNoMask = NULL;
m_surface = NULL;
m_scaleFactor = 1;
#else
m_pixmap = NULL;
m_pixbuf = NULL;
#endif
m_mask = NULL;
m_width = width;
m_height = height;
m_bpp = depth;
#ifdef __WXGTK3__
if (m_bpp != 1 && m_bpp != 32)
m_bpp = 24;
#else
if (m_bpp < 0)
m_bpp = gdk_drawable_get_depth(wxGetTopLevelGDK());
m_alphaRequested = depth == 32;
#endif
}
wxBitmapRefData::~wxBitmapRefData()
{
#ifdef __WXGTK3__
if (m_pixbufMask)
g_object_unref(m_pixbufMask);
if (m_pixbufNoMask)
g_object_unref(m_pixbufNoMask);
if (m_surface)
cairo_surface_destroy(m_surface);
#else
if (m_pixmap)
g_object_unref (m_pixmap);
if (m_pixbuf)
g_object_unref (m_pixbuf);
#endif
delete m_mask;
}
bool wxBitmapRefData::IsOk() const
{
return m_bpp != 0;
}
//-----------------------------------------------------------------------------
// wxBitmap
//-----------------------------------------------------------------------------
#define M_BMPDATA static_cast<wxBitmapRefData*>(m_refData)
wxIMPLEMENT_DYNAMIC_CLASS(wxBitmap,wxGDIObject);
wxBitmap::wxBitmap(const wxString &filename, wxBitmapType type)
{
LoadFile(filename, type);
}
wxBitmap::wxBitmap(const char bits[], int width, int height, int depth)
{
wxASSERT(depth == 1);
if (width > 0 && height > 0 && depth == 1)
{
m_refData = new wxBitmapRefData(width, height, 1);
#ifdef __WXGTK3__
GdkPixbuf* pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, false, 8, width, height);
M_BMPDATA->m_pixbufNoMask = pixbuf;
const char* src = bits;
guchar* dst = gdk_pixbuf_get_pixels(pixbuf);
const int stride_src = (width + 7) / 8;
const int rowinc_dst = gdk_pixbuf_get_rowstride(pixbuf) - 3 * width;
for (int j = 0; j < width; j++, src += stride_src, dst += rowinc_dst)
{
for (int i = 0; i < height; i++)
{
guchar c = 0xff;
if (src[i >> 3] & (1 << (i & 7)))
c = 0;
*dst++ = c;
*dst++ = c;
*dst++ = c;
}
}
#else
M_BMPDATA->m_pixmap = gdk_bitmap_create_from_data(
wxGetTopLevelGDK(), bits, width, height);
#endif
}
}
wxBitmap::wxBitmap(const char* const* bits)
{
wxCHECK2_MSG(bits != NULL, return, wxT("invalid bitmap data"));
#if wxUSE_IMAGE
*this = wxBitmap(wxImage(bits));
#elif defined __WXGTK3__
GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(const_cast<const char**>(bits));
if (pixbuf)
{
m_refData = new wxBitmapRefData(
gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf),
gdk_pixbuf_get_n_channels(pixbuf) * 8);
M_BMPDATA->m_pixbufNoMask = pixbuf;
wxASSERT(M_BMPDATA->m_bpp == 32 || !gdk_pixbuf_get_has_alpha(M_BMPDATA->m_pixbufNoMask));
}
#else
GdkBitmap* mask = NULL;
GdkPixmap* pixmap = gdk_pixmap_create_from_xpm_d(
wxGetTopLevelGDK(), &mask, NULL, const_cast<char**>(bits));
if (pixmap)
{
int width, height;
gdk_drawable_get_size(pixmap, &width, &height);
m_refData = new wxBitmapRefData(width, height, -1);
M_BMPDATA->m_pixmap = pixmap;
if (mask)
{
M_BMPDATA->m_mask = new wxMask(mask);
}
}
#endif
}
wxBitmap::wxBitmap(GdkPixbuf* pixbuf, int depth)
{
if (pixbuf)
{
if (depth != 1)
depth = gdk_pixbuf_get_n_channels(pixbuf) * 8;
wxBitmapRefData* bmpData = new wxBitmapRefData(
gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf),
depth);
m_refData = bmpData;
#ifdef __WXGTK3__
bmpData->m_pixbufNoMask = pixbuf;
#else
bmpData->m_pixbuf = pixbuf;
#endif
}
}
#ifndef __WXGTK3__
wxBitmap::wxBitmap(GdkPixmap* pixmap)
{
if (pixmap)
{
int w, h;
gdk_drawable_get_size(pixmap, &w, &h);
wxBitmapRefData* bmpData =
new wxBitmapRefData(w, h, gdk_drawable_get_depth(pixmap));
m_refData = bmpData;
bmpData->m_pixmap = pixmap;
}
}
#endif
wxBitmap::wxBitmap(const wxCursor& cursor)
{
#if GTK_CHECK_VERSION(2,8,0)
if (gtk_check_version(2,8,0) == NULL)
{
GdkPixbuf *pixbuf = gdk_cursor_get_image(cursor.GetCursor());
*this = wxBitmap(pixbuf);
}
#else
wxUnusedVar(cursor);
#endif
}
wxBitmap::~wxBitmap()
{
}
bool wxBitmap::Create( int width, int height, int depth )
{
UnRef();
wxCHECK_MSG(width > 0 && height > 0, false, "invalid bitmap size");
m_refData = new wxBitmapRefData(width, height, depth);
return true;
}
#ifdef __WXGTK3__
static void CopyImageData(
guchar* dst, int dstChannels, int dstStride,
const guchar* src, int srcChannels, int srcStride,
int w, int h)
{
if (dstChannels == srcChannels)
{
if (dstStride == srcStride)
memcpy(dst, src, size_t(dstStride) * h);
else
{
const int stride = dstStride < srcStride ? dstStride : srcStride;
for (int j = 0; j < h; j++, src += srcStride, dst += dstStride)
memcpy(dst, src, stride);
}
}
else
{
for (int j = 0; j < h; j++, src += srcStride, dst += dstStride)
{
guchar* d = dst;
const guchar* s = src;
if (dstChannels == 4)
{
for (int i = 0; i < w; i++, d += 4, s += 3)
{
d[0] = s[0];
d[1] = s[1];
d[2] = s[2];
d[3] = 0xff;
}
}
else
{
for (int i = 0; i < w; i++, d += 3, s += 4)
{
d[0] = s[0];
d[1] = s[1];
d[2] = s[2];
}
}
}
}
}
#endif
#if wxUSE_IMAGE
#ifdef __WXGTK3__
wxBitmap::wxBitmap(const wxImage& image, int depth, double scale)
{
wxCHECK_RET(image.IsOk(), "invalid image");
const int w = image.GetWidth();
const int h = image.GetHeight();
const guchar* alpha = image.GetAlpha();
if (depth < 0)
depth = alpha ? 32 : 24;
else if (depth != 1 && depth != 32)
depth = 24;
wxBitmapRefData* bmpData = new wxBitmapRefData(w, h, depth);
bmpData->m_scaleFactor = scale;
m_refData = bmpData;
GdkPixbuf* pixbuf_dst = gdk_pixbuf_new(GDK_COLORSPACE_RGB, depth == 32, 8, w, h);
bmpData->m_pixbufNoMask = pixbuf_dst;
wxASSERT(bmpData->m_bpp == 32 || !gdk_pixbuf_get_has_alpha(bmpData->m_pixbufNoMask));
const guchar* src = image.GetData();
guchar* dst = gdk_pixbuf_get_pixels(pixbuf_dst);
const int dstStride = gdk_pixbuf_get_rowstride(pixbuf_dst);
CopyImageData(dst, gdk_pixbuf_get_n_channels(pixbuf_dst), dstStride, src, 3, 3 * w, w, h);
if (depth == 32 && alpha)
{
for (int j = 0; j < h; j++, dst += dstStride)
for (int i = 0; i < w; i++)
dst[i * 4 + 3] = *alpha++;
}
if (image.HasMask())
{
const guchar r = image.GetMaskRed();
const guchar g = image.GetMaskGreen();
const guchar b = image.GetMaskBlue();
cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_A8, w, h);
const int stride = cairo_image_surface_get_stride(surface);
dst = cairo_image_surface_get_data(surface);
memset(dst, 0xff, stride * h);
for (int j = 0; j < h; j++, dst += stride)
for (int i = 0; i < w; i++, src += 3)
if (src[0] == r && src[1] == g && src[2] == b)
dst[i] = 0;
cairo_surface_mark_dirty(surface);
bmpData->m_mask = new wxMask(surface);
}
}
#else
wxBitmap::wxBitmap(const wxImage& image, int depth, double WXUNUSED(scale))
{
wxCHECK_RET(image.IsOk(), "invalid image");
if (depth == 32 || (depth == -1 && image.HasAlpha()))
CreateFromImageAsPixbuf(image);
else
// otherwise create pixmap, if alpha is present it will be converted to mask
CreateFromImageAsPixmap(image, depth);
}
bool wxBitmap::CreateFromImageAsPixmap(const wxImage& image, int depth)
{
const int w = image.GetWidth();
const int h = image.GetHeight();
if (depth == 1)
{
// create XBM format bitmap
// one bit per pixel, each row starts on a byte boundary
const size_t out_size = size_t((w + 7) / 8) * unsigned(h);
wxByte* out = new wxByte[out_size];
// set bits are black
memset(out, 0xff, out_size);
const wxByte* in = image.GetData();
unsigned bit_index = 0;
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++, in += 3, bit_index++)
if (in[0] == 255 && in[1] == 255 && in[2] == 255)
out[bit_index >> 3] ^= 1 << (bit_index & 7);
// move index to next byte boundary
bit_index = (bit_index + 7) & ~7u;
}
SetPixmap(gdk_bitmap_create_from_data(wxGetTopLevelGDK(), (char*)out, w, h));
delete[] out;
if (!M_BMPDATA) // SetPixmap may have failed
return false;
}
else
{
SetPixmap(gdk_pixmap_new(wxGetTopLevelGDK(), w, h, depth));
if (!M_BMPDATA)
return false;
wxGtkObject<GdkGC> gc(gdk_gc_new(M_BMPDATA->m_pixmap));
gdk_draw_rgb_image(
M_BMPDATA->m_pixmap, gc,
0, 0, w, h,
GDK_RGB_DITHER_NONE, image.GetData(), w * 3);
}
const wxByte* alpha = image.GetAlpha();
if (alpha != NULL || image.HasMask())
{
// create mask as XBM format bitmap
const size_t out_size = size_t((w + 7) / 8) * unsigned(h);
wxByte* out = new wxByte[out_size];
memset(out, 0xff, out_size);
unsigned bit_index = 0;
if (alpha != NULL)
{
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++, bit_index++)
if (*alpha++ < wxIMAGE_ALPHA_THRESHOLD)
out[bit_index >> 3] ^= 1 << (bit_index & 7);
bit_index = (bit_index + 7) & ~7u;
}
}
else
{
const wxByte r_mask = image.GetMaskRed();
const wxByte g_mask = image.GetMaskGreen();
const wxByte b_mask = image.GetMaskBlue();
const wxByte* in = image.GetData();
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++, in += 3, bit_index++)
if (in[0] == r_mask && in[1] == g_mask && in[2] == b_mask)
out[bit_index >> 3] ^= 1 << (bit_index & 7);
bit_index = (bit_index + 7) & ~7u;
}
}
SetMask(new wxMask(gdk_bitmap_create_from_data(M_BMPDATA->m_pixmap, (char*)out, w, h)));
delete[] out;
}
return IsOk();
}
bool wxBitmap::CreateFromImageAsPixbuf(const wxImage& image)
{
int width = image.GetWidth();
int height = image.GetHeight();
Create(width, height, 32);
GdkPixbuf* pixbuf = GetPixbuf();
if (!pixbuf)
return false;
// Copy the data:
const unsigned char* in = image.GetData();
unsigned char *out = gdk_pixbuf_get_pixels(pixbuf);
unsigned char *alpha = image.GetAlpha();
int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - 4 * width;
for (int y = 0; y < height; y++, out += rowpad)
{
for (int x = 0; x < width; x++, out += 4, in += 3)
{
out[0] = in[0];
out[1] = in[1];
out[2] = in[2];
if (alpha)
out[3] = *alpha++;
}
}
return true;
}
#endif
wxImage wxBitmap::ConvertToImage() const
{
#ifdef __WXGTK3__
wxImage image;
wxCHECK_MSG(IsOk(), image, "invalid bitmap");
wxBitmapRefData* bmpData = M_BMPDATA;
const int w = bmpData->m_width;
const int h = bmpData->m_height;
image.Create(w, h, false);
guchar* dst = image.GetData();
GdkPixbuf* pixbuf_src = NULL;
if (bmpData->m_pixbufNoMask)
pixbuf_src = bmpData->m_pixbufNoMask;
else if (bmpData->m_surface)
{
pixbuf_src = gdk_pixbuf_get_from_surface(bmpData->m_surface, 0, 0, w, h);
bmpData->m_pixbufNoMask = pixbuf_src;
wxASSERT(bmpData->m_bpp == 32 || !gdk_pixbuf_get_has_alpha(bmpData->m_pixbufNoMask));
}
if (pixbuf_src)
{
const guchar* src = gdk_pixbuf_get_pixels(pixbuf_src);
const int srcStride = gdk_pixbuf_get_rowstride(pixbuf_src);
const int srcChannels = gdk_pixbuf_get_n_channels(pixbuf_src);
CopyImageData(dst, 3, 3 * w, src, srcChannels, srcStride, w, h);
if (srcChannels == 4)
{
image.SetAlpha();
guchar* alpha = image.GetAlpha();
for (int j = 0; j < h; j++, src += srcStride)
{
const guchar* s = src;
for (int i = 0; i < w; i++, s += 4)
*alpha++ = s[3];
}
}
}
cairo_surface_t* maskSurf = NULL;
if (bmpData->m_mask)
maskSurf = *bmpData->m_mask;
if (maskSurf)
{
const guchar r = 1;
const guchar g = 2;
const guchar b = 3;
image.SetMaskColour(r, g, b);
wxASSERT(cairo_image_surface_get_format(maskSurf) == CAIRO_FORMAT_A8);
const int stride = cairo_image_surface_get_stride(maskSurf);
const guchar* src = cairo_image_surface_get_data(maskSurf);
for (int j = 0; j < h; j++, src += stride)
{
for (int i = 0; i < w; i++, dst += 3)
if (src[i] == 0)
{
dst[0] = r;
dst[1] = g;
dst[2] = b;
}
else if (dst[0] == r && dst[1] == g && dst[2] == b)
dst[2]--;
}
}
#else
wxCHECK_MSG( IsOk(), wxNullImage, wxT("invalid bitmap") );
const int w = GetWidth();
const int h = GetHeight();
wxImage image(w, h, false);
unsigned char *data = image.GetData();
wxCHECK_MSG(data != NULL, wxNullImage, wxT("couldn't create image") );
// prefer pixbuf if available, it will preserve alpha and should be quicker
if (HasPixbuf())
{
GdkPixbuf *pixbuf = GetPixbuf();
unsigned char* alpha = NULL;
if (gdk_pixbuf_get_has_alpha(pixbuf))
{
image.SetAlpha();
alpha = image.GetAlpha();
}
const unsigned char* in = gdk_pixbuf_get_pixels(pixbuf);
unsigned char *out = data;
const int inc = 3 + int(alpha != NULL);
const int rowpad = gdk_pixbuf_get_rowstride(pixbuf) - inc * w;
for (int y = 0; y < h; y++, in += rowpad)
{
for (int x = 0; x < w; x++, in += inc, out += 3)
{
out[0] = in[0];
out[1] = in[1];
out[2] = in[2];
if (alpha != NULL)
*alpha++ = in[3];
}
}
}
else
{
GdkPixmap* pixmap = GetPixmap();
GdkPixmap* pixmap_invert = NULL;
if (GetDepth() == 1)
{
// mono bitmaps are inverted, i.e. 0 is white
pixmap_invert = gdk_pixmap_new(pixmap, w, h, 1);
wxGtkObject<GdkGC> gc(gdk_gc_new(pixmap_invert));
gdk_gc_set_function(gc, GDK_COPY_INVERT);
gdk_draw_drawable(pixmap_invert, gc, pixmap, 0, 0, 0, 0, w, h);
pixmap = pixmap_invert;
}
// create a pixbuf which shares data with the wxImage
GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data(
data, GDK_COLORSPACE_RGB, false, 8, w, h, 3 * w, NULL, NULL);
gdk_pixbuf_get_from_drawable(pixbuf, pixmap, NULL, 0, 0, 0, 0, w, h);
g_object_unref(pixbuf);
if (pixmap_invert != NULL)
g_object_unref(pixmap_invert);
}
// convert mask, unless there is already alpha
if (GetMask() && !image.HasAlpha())
{
// we hard code the mask colour for now but we could also make an
// effort (and waste time) to choose a colour not present in the
// image already to avoid having to fudge the pixels below --
// whether it's worth to do it is unclear however
const int MASK_RED = 1;
const int MASK_GREEN = 2;
const int MASK_BLUE = 3;
const int MASK_BLUE_REPLACEMENT = 2;
image.SetMaskColour(MASK_RED, MASK_GREEN, MASK_BLUE);
GdkImage* image_mask = gdk_drawable_get_image(*GetMask(), 0, 0, w, h);
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++, data += 3)
{
if (gdk_image_get_pixel(image_mask, x, y) == 0)
{
data[0] = MASK_RED;
data[1] = MASK_GREEN;
data[2] = MASK_BLUE;
}
else if (data[0] == MASK_RED && data[1] == MASK_GREEN && data[2] == MASK_BLUE)
{
// we have to fudge the colour a bit to prevent
// this pixel from appearing transparent
data[2] = MASK_BLUE_REPLACEMENT;
}
}
}
g_object_unref(image_mask);
}
#endif
return image;
}
#endif // wxUSE_IMAGE
int wxBitmap::GetHeight() const
{
wxCHECK_MSG( IsOk(), -1, wxT("invalid bitmap") );
return M_BMPDATA->m_height;
}
int wxBitmap::GetWidth() const
{
wxCHECK_MSG( IsOk(), -1, wxT("invalid bitmap") );
return M_BMPDATA->m_width;
}
int wxBitmap::GetDepth() const
{
wxCHECK_MSG( IsOk(), -1, wxT("invalid bitmap") );
return M_BMPDATA->m_bpp;
}
wxMask *wxBitmap::GetMask() const
{
wxCHECK_MSG( IsOk(), NULL, wxT("invalid bitmap") );
return M_BMPDATA->m_mask;
}
void wxBitmap::SetMask( wxMask *mask )
{
wxCHECK_RET( IsOk(), wxT("invalid bitmap") );
AllocExclusive();
delete M_BMPDATA->m_mask;
M_BMPDATA->m_mask = mask;
}
bool wxBitmap::CopyFromIcon(const wxIcon& icon)
{
*this = icon;
return IsOk();
}
#ifdef __WXGTK3__
bool wxBitmap::CreateScaled(int w, int h, int depth, double scale)
{
Create(int(w * scale), int(h * scale), depth);
M_BMPDATA->m_scaleFactor = scale;
return true;
}
double wxBitmap::GetScaleFactor() const
{
return M_BMPDATA->m_scaleFactor;
}
static cairo_surface_t* GetSubSurface(cairo_surface_t* surface, const wxRect& rect)
{
cairo_surface_flush(surface);
const cairo_format_t format = cairo_image_surface_get_format(surface);
int x = rect.x;
if (format != CAIRO_FORMAT_A8)
x *= 4;
cairo_surface_t* subSurface = cairo_image_surface_create(format, rect.width, rect.height);
const int srcStride = cairo_image_surface_get_stride(surface);
const int dstStride = cairo_image_surface_get_stride(subSurface);
const guchar* src = cairo_image_surface_get_data(surface) + rect.y * srcStride + x;
guchar* dst = cairo_image_surface_get_data(subSurface);
for (int j = 0; j < rect.height; j++, src += srcStride, dst += dstStride)
memcpy(dst, src, dstStride);
cairo_surface_mark_dirty(subSurface);
return subSurface;
}
#endif
wxBitmap wxBitmap::GetSubBitmap( const wxRect& rect) const
{
wxBitmap ret;
wxCHECK_MSG(IsOk(), ret, wxT("invalid bitmap"));
const int w = rect.width;
const int h = rect.height;
const wxBitmapRefData* bmpData = M_BMPDATA;
wxCHECK_MSG(rect.x >= 0 && rect.y >= 0 &&
rect.x + w <= bmpData->m_width &&
rect.y + h <= bmpData->m_height,
ret, wxT("invalid bitmap region"));
wxBitmapRefData * const newRef = new wxBitmapRefData(w, h, bmpData->m_bpp);
ret.m_refData = newRef;
#ifdef __WXGTK3__
newRef->m_scaleFactor = bmpData->m_scaleFactor;
if (bmpData->m_pixbufNoMask)
{
GdkPixbuf* pixbuf = gdk_pixbuf_new_subpixbuf(bmpData->m_pixbufNoMask, rect.x, rect.y, w, h);
newRef->m_pixbufNoMask = gdk_pixbuf_copy(pixbuf);
wxASSERT(newRef->m_bpp == 32 || !gdk_pixbuf_get_has_alpha(newRef->m_pixbufNoMask));
g_object_unref(pixbuf);
}
else if (bmpData->m_surface)
newRef->m_surface = GetSubSurface(bmpData->m_surface, rect);
cairo_surface_t* maskSurf = NULL;
if (bmpData->m_mask)
maskSurf = *bmpData->m_mask;
if (maskSurf)
{
newRef->m_mask = new wxMask(GetSubSurface(maskSurf, rect));
}
#else
if (bmpData->m_pixbuf)
{
GdkPixbuf* pixbuf =
gdk_pixbuf_new_subpixbuf(bmpData->m_pixbuf, rect.x, rect.y, w, h);
newRef->m_pixbuf = gdk_pixbuf_copy(pixbuf);
g_object_unref(pixbuf);
}
if (bmpData->m_pixmap)
{
newRef->m_pixmap = gdk_pixmap_new(bmpData->m_pixmap, w, h, -1);
GdkGC* gc = gdk_gc_new(newRef->m_pixmap);
gdk_draw_drawable(
newRef->m_pixmap, gc, bmpData->m_pixmap, rect.x, rect.y, 0, 0, w, h);
g_object_unref(gc);
}
GdkPixmap* mask = NULL;
if (bmpData->m_mask)
mask = *bmpData->m_mask;
if (mask)
{
GdkPixmap* sub_mask = gdk_pixmap_new(mask, w, h, 1);
newRef->m_mask = new wxMask(sub_mask);
GdkGC* gc = gdk_gc_new(sub_mask);
gdk_draw_drawable(
sub_mask, gc, mask, rect.x, rect.y, 0, 0, w, h);
g_object_unref(gc);
}
#endif
return ret;
}
bool wxBitmap::SaveFile( const wxString &name, wxBitmapType type, const wxPalette *WXUNUSED(palette) ) const
{
wxCHECK_MSG( IsOk(), false, wxT("invalid bitmap") );
const char* type_name = NULL;
switch (type)
{
case wxBITMAP_TYPE_ANI: type_name = "ani"; break;
case wxBITMAP_TYPE_BMP: type_name = "bmp"; break;
case wxBITMAP_TYPE_GIF: type_name = "gif"; break;
case wxBITMAP_TYPE_ICO: type_name = "ico"; break;
case wxBITMAP_TYPE_JPEG: type_name = "jpeg"; break;
case wxBITMAP_TYPE_PCX: type_name = "pcx"; break;
case wxBITMAP_TYPE_PNG: type_name = "png"; break;
case wxBITMAP_TYPE_PNM: type_name = "pnm"; break;
case wxBITMAP_TYPE_TGA: type_name = "tga"; break;
case wxBITMAP_TYPE_TIFF: type_name = "tiff"; break;
case wxBITMAP_TYPE_XBM: type_name = "xbm"; break;
case wxBITMAP_TYPE_XPM: type_name = "xpm"; break;
default: break;
}
if (type_name &&
gdk_pixbuf_save(GetPixbuf(), wxGTK_CONV_FN(name), type_name, NULL, NULL))
{
return true;
}
#if wxUSE_IMAGE
return ConvertToImage().SaveFile(name, type);
#else
return false;
#endif
}
bool wxBitmap::LoadFile( const wxString &name, wxBitmapType type )
{
GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file(wxGTK_CONV_FN(name), NULL);
if (pixbuf)
{
*this = wxBitmap(pixbuf);
return true;
}
#if wxUSE_IMAGE
wxImage image;
if (image.LoadFile(name, type) && image.IsOk())
{
*this = wxBitmap(image);
return true;
}
#else
wxUnusedVar(type);
#endif
return false;
}
#if wxUSE_PALETTE
wxPalette *wxBitmap::GetPalette() const
{
return NULL;
}
void wxBitmap::SetPalette(const wxPalette& WXUNUSED(palette))
{
// TODO
}
#endif // wxUSE_PALETTE
void wxBitmap::SetHeight( int height )
{
AllocExclusive();
M_BMPDATA->m_height = height;
}
void wxBitmap::SetWidth( int width )
{
AllocExclusive();
M_BMPDATA->m_width = width;
}
void wxBitmap::SetDepth( int depth )
{
AllocExclusive();
M_BMPDATA->m_bpp = depth;
}
#ifndef __WXGTK3__
void wxBitmap::SetPixmap( GdkPixmap *pixmap )
{
UnRef();
if (!pixmap)
return;
int w, h;
gdk_drawable_get_size(pixmap, &w, &h);
wxBitmapRefData* bmpData = new wxBitmapRefData(w, h, 0);
m_refData = bmpData;
bmpData->m_pixmap = pixmap;
bmpData->m_bpp = gdk_drawable_get_depth(pixmap);
}
GdkPixmap *wxBitmap::GetPixmap() const
{
wxCHECK_MSG( IsOk(), NULL, wxT("invalid bitmap") );
wxBitmapRefData* bmpData = M_BMPDATA;
if (bmpData->m_pixmap)
return bmpData->m_pixmap;
if (bmpData->m_pixbuf)
{
GdkPixmap* pixmap = NULL;
GdkPixmap** mask_pixmap = NULL;
if (gdk_pixbuf_get_has_alpha(bmpData->m_pixbuf))
{
// make new mask from alpha
mask_pixmap = &pixmap;
}
gdk_pixbuf_render_pixmap_and_mask(
bmpData->m_pixbuf, &bmpData->m_pixmap, mask_pixmap, 128);
if (pixmap)
{
delete bmpData->m_mask;
bmpData->m_mask = new wxMask(pixmap);
}
}
else
{
bmpData->m_pixmap = gdk_pixmap_new(wxGetTopLevelGDK(),
bmpData->m_width, bmpData->m_height, bmpData->m_bpp == 1 ? 1 : -1);
}
return bmpData->m_pixmap;
}
bool wxBitmap::HasPixmap() const
{
wxCHECK_MSG( IsOk(), false, wxT("invalid bitmap") );
return M_BMPDATA->m_pixmap != NULL;
}
#endif
#ifdef __WXGTK3__
GdkPixbuf* wxBitmap::GetPixbufNoMask() const
{
wxCHECK_MSG(IsOk(), NULL, "invalid bitmap");
wxBitmapRefData* bmpData = M_BMPDATA;
GdkPixbuf* pixbuf = bmpData->m_pixbufNoMask;
if (pixbuf)
return pixbuf;
const int w = bmpData->m_width;
const int h = bmpData->m_height;
if (bmpData->m_surface)
pixbuf = gdk_pixbuf_get_from_surface(bmpData->m_surface, 0, 0, w, h);
else
pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, bmpData->m_bpp == 32, 8, w, h);
bmpData->m_pixbufNoMask = pixbuf;
wxASSERT(bmpData->m_bpp == 32 || !gdk_pixbuf_get_has_alpha(bmpData->m_pixbufNoMask));
return pixbuf;
}
// helper to set up a simulated depth 1 surface
static void SetSourceSurface1(const wxBitmapRefData* bmpData, cairo_t* cr, int x, int y, const wxColour* fg, const wxColour* bg)
{
GdkPixbuf* pixbuf = gdk_pixbuf_copy(bmpData->m_pixbufNoMask);
const int w = bmpData->m_width;
const int h = bmpData->m_height;
const int stride = gdk_pixbuf_get_rowstride(pixbuf);
const int channels = gdk_pixbuf_get_n_channels(pixbuf);
guchar* dst = gdk_pixbuf_get_pixels(pixbuf);
guchar fg_r = 0, fg_g = 0, fg_b = 0;
if (fg && fg->IsOk())
{
fg_r = fg->Red();
fg_g = fg->Green();
fg_b = fg->Blue();
}
guchar bg_r = 255, bg_g = 255, bg_b = 255;
if (bg && bg->IsOk())
{
bg_r = bg->Red();
bg_g = bg->Green();
bg_b = bg->Blue();
}
for (int j = 0; j < h; j++, dst += stride)
{
guchar* d = dst;
for (int i = 0; i < w; i++, d += channels)
if (d[0])
{
d[0] = bg_r;
d[1] = bg_g;
d[2] = bg_b;
}
else
{
d[0] = fg_r;
d[1] = fg_g;
d[2] = fg_b;
}
}
gdk_cairo_set_source_pixbuf(cr, pixbuf, x, y);
g_object_unref(pixbuf);
}
void wxBitmap::SetSourceSurface(cairo_t* cr, int x, int y, const wxColour* fg, const wxColour* bg) const
{
wxBitmapRefData* bmpData = M_BMPDATA;
if (bmpData->m_surface)
{
cairo_set_source_surface(cr, bmpData->m_surface, x, y);
return;
}
wxCHECK_RET(bmpData->m_pixbufNoMask, "no bitmap data");
if (bmpData->m_bpp == 1)
SetSourceSurface1(bmpData, cr, x, y, fg, bg);
else
{
gdk_cairo_set_source_pixbuf(cr, bmpData->m_pixbufNoMask, x, y);
cairo_pattern_get_surface(cairo_get_source(cr), &bmpData->m_surface);
cairo_surface_reference(bmpData->m_surface);
}
}
cairo_t* wxBitmap::CairoCreate() const
{
wxCHECK_MSG(IsOk(), NULL, "invalid bitmap");
wxBitmapRefData* bmpData = M_BMPDATA;
cairo_t* cr;
if (bmpData->m_surface)
cr = cairo_create(bmpData->m_surface);
else
{
GdkPixbuf* pixbuf = bmpData->m_pixbufNoMask;
const bool useAlpha = bmpData->m_bpp == 32 || (pixbuf && gdk_pixbuf_get_has_alpha(pixbuf));
bmpData->m_surface = cairo_image_surface_create(
useAlpha ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24,
bmpData->m_width, bmpData->m_height);
cr = cairo_create(bmpData->m_surface);
if (pixbuf)
{
gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
cairo_paint(cr);
cairo_set_source_rgb(cr, 0, 0, 0);
}
}
if (bmpData->m_pixbufNoMask)
{
g_object_unref(bmpData->m_pixbufNoMask);
bmpData->m_pixbufNoMask = NULL;
}
if (bmpData->m_pixbufMask)
{
g_object_unref(bmpData->m_pixbufMask);
bmpData->m_pixbufMask = NULL;
}
wxASSERT(cr && cairo_status(cr) == 0);
if (!wxIsSameDouble(bmpData->m_scaleFactor, 1))
cairo_scale(cr, bmpData->m_scaleFactor, bmpData->m_scaleFactor);
return cr;
}
void wxBitmap::Draw(cairo_t* cr, int x, int y, bool useMask, const wxColour* fg, const wxColour* bg) const
{
wxCHECK_RET(IsOk(), "invalid bitmap");
wxBitmapRefData* bmpData = M_BMPDATA;
if (!wxIsSameDouble(bmpData->m_scaleFactor, 1))
{
cairo_translate(cr, x, y);
const double scale = 1 / bmpData->m_scaleFactor;
cairo_scale(cr, scale, scale);
x = 0;
y = 0;
}
SetSourceSurface(cr, x, y, fg, bg);
cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
cairo_surface_t* mask = NULL;
if (useMask && bmpData->m_mask)
mask = *bmpData->m_mask;
if (mask)
cairo_mask_surface(cr, mask, x, y);
else
cairo_paint(cr);
}
#endif
GdkPixbuf *wxBitmap::GetPixbuf() const
{
wxCHECK_MSG( IsOk(), NULL, wxT("invalid bitmap") );
wxBitmapRefData* bmpData = M_BMPDATA;
#ifdef __WXGTK3__
if (bmpData->m_pixbufMask)
return bmpData->m_pixbufMask;
if (bmpData->m_pixbufNoMask == NULL)
GetPixbufNoMask();
cairo_surface_t* mask = NULL;
if (bmpData->m_mask)
mask = *bmpData->m_mask;
if (mask == NULL)
return bmpData->m_pixbufNoMask;
const int w = bmpData->m_width;
const int h = bmpData->m_height;
bmpData->m_pixbufMask = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, w, h);
guchar* dst = gdk_pixbuf_get_pixels(bmpData->m_pixbufMask);
const int dstStride = gdk_pixbuf_get_rowstride(bmpData->m_pixbufMask);
CopyImageData(dst, 4, dstStride,
gdk_pixbuf_get_pixels(bmpData->m_pixbufNoMask),
gdk_pixbuf_get_n_channels(bmpData->m_pixbufNoMask),
gdk_pixbuf_get_rowstride(bmpData->m_pixbufNoMask),
w, h);
const guchar* src = cairo_image_surface_get_data(mask);
const int srcStride = cairo_image_surface_get_stride(mask);
for (int j = 0; j < h; j++, src += srcStride, dst += dstStride)
for (int i = 0; i < w; i++)
if (src[i] == 0)
dst[i * 4 + 3] = 0;
return bmpData->m_pixbufMask;
#else
if (bmpData->m_pixbuf)
return bmpData->m_pixbuf;
const int w = bmpData->m_width;
const int h = bmpData->m_height;
GdkPixmap* mask = NULL;
if (bmpData->m_mask)
mask = *bmpData->m_mask;
const bool useAlpha = bmpData->m_alphaRequested || mask;
bmpData->m_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, useAlpha, 8, w, h);
if (bmpData->m_pixmap)
PixmapToPixbuf(bmpData->m_pixmap, bmpData->m_pixbuf, w, h);
if (mask)
MaskToAlpha(mask, bmpData->m_pixbuf, w, h);
return bmpData->m_pixbuf;
#endif
}
#ifndef __WXGTK3__
bool wxBitmap::HasPixbuf() const
{
wxCHECK_MSG( IsOk(), false, wxT("invalid bitmap") );
return M_BMPDATA->m_pixbuf != NULL;
}
void wxBitmap::PurgeOtherRepresentations(wxBitmap::Representation keep)
{
if (keep == Pixmap && HasPixbuf())
{
g_object_unref (M_BMPDATA->m_pixbuf);
M_BMPDATA->m_pixbuf = NULL;
}
if (keep == Pixbuf && HasPixmap())
{
g_object_unref (M_BMPDATA->m_pixmap);
M_BMPDATA->m_pixmap = NULL;
}
}
#endif
#ifdef wxHAS_RAW_BITMAP
void *wxBitmap::GetRawData(wxPixelDataBase& data, int bpp)
{
void* bits = NULL;
#ifdef __WXGTK3__
GdkPixbuf* pixbuf = GetPixbufNoMask();
if ((bpp == 32) == (gdk_pixbuf_get_has_alpha(pixbuf) != 0))
{
bits = gdk_pixbuf_get_pixels(pixbuf);
wxBitmapRefData* bmpData = M_BMPDATA;
data.m_width = bmpData->m_width;
data.m_height = bmpData->m_height;
data.m_stride = gdk_pixbuf_get_rowstride(pixbuf);
if (bmpData->m_pixbufMask)
{
g_object_unref(bmpData->m_pixbufMask);
bmpData->m_pixbufMask = NULL;
}
if (bmpData->m_surface)
{
cairo_surface_destroy(bmpData->m_surface);
bmpData->m_surface = NULL;
}
}
#else
GdkPixbuf *pixbuf = GetPixbuf();
// Pixmap will get out of date when our pixbuf is accessed directly, so
// ensure we don't keep the old data in it.
PurgeOtherRepresentations(Pixbuf);
const bool hasAlpha = HasAlpha();
// allow access if bpp is valid and matches existence of alpha
if ( pixbuf && ((bpp == 24 && !hasAlpha) || (bpp == 32 && hasAlpha)) )
{
data.m_height = gdk_pixbuf_get_height( pixbuf );
data.m_width = gdk_pixbuf_get_width( pixbuf );
data.m_stride = gdk_pixbuf_get_rowstride( pixbuf );
bits = gdk_pixbuf_get_pixels(pixbuf);
}
#endif
return bits;
}
void wxBitmap::UngetRawData(wxPixelDataBase& WXUNUSED(data))
{
}
#endif // wxHAS_RAW_BITMAP
bool wxBitmap::HasAlpha() const
{
const wxBitmapRefData* bmpData = M_BMPDATA;
#ifdef __WXGTK3__
return bmpData && bmpData->m_bpp == 32;
#else
return bmpData && (bmpData->m_alphaRequested ||
(bmpData->m_pixbuf && gdk_pixbuf_get_has_alpha(bmpData->m_pixbuf)));
#endif
}
wxGDIRefData* wxBitmap::CreateGDIRefData() const
{
return new wxBitmapRefData(0, 0, 0);
}
wxGDIRefData* wxBitmap::CloneGDIRefData(const wxGDIRefData* data) const
{
const wxBitmapRefData* oldRef = static_cast<const wxBitmapRefData*>(data);
wxBitmapRefData * const newRef = new wxBitmapRefData(oldRef->m_width,
oldRef->m_height,
oldRef->m_bpp);
#ifdef __WXGTK3__
newRef->m_scaleFactor = oldRef->m_scaleFactor;
if (oldRef->m_pixbufNoMask)
newRef->m_pixbufNoMask = gdk_pixbuf_copy(oldRef->m_pixbufNoMask);
if (oldRef->m_surface)
{
const int w = oldRef->m_width;
const int h = oldRef->m_height;
cairo_surface_t* surface = cairo_image_surface_create(
cairo_image_surface_get_format(oldRef->m_surface), w, h);
newRef->m_surface = surface;
cairo_surface_flush(oldRef->m_surface);
const guchar* src = cairo_image_surface_get_data(oldRef->m_surface);
guchar* dst = cairo_image_surface_get_data(surface);
const int stride = cairo_image_surface_get_stride(surface);
wxASSERT(stride == cairo_image_surface_get_stride(oldRef->m_surface));
memcpy(dst, src, stride * h);
cairo_surface_mark_dirty(surface);
}
#else
if (oldRef->m_pixmap != NULL)
{
newRef->m_pixmap = gdk_pixmap_new(
oldRef->m_pixmap, oldRef->m_width, oldRef->m_height,
// use pixmap depth, m_bpp may not match
gdk_drawable_get_depth(oldRef->m_pixmap));
wxGtkObject<GdkGC> gc(gdk_gc_new(newRef->m_pixmap));
gdk_draw_drawable(
newRef->m_pixmap, gc, oldRef->m_pixmap, 0, 0, 0, 0, -1, -1);
}
if (oldRef->m_pixbuf != NULL)
{
newRef->m_pixbuf = gdk_pixbuf_copy(oldRef->m_pixbuf);
}
#endif
if (oldRef->m_mask != NULL)
{
newRef->m_mask = new wxMask(*oldRef->m_mask);
}
return newRef;
}
/* static */ void wxBitmap::InitStandardHandlers()
{
// TODO: Insert handler based on GdkPixbufs handler later
}