+wxBitmap::~wxBitmap()
+{
+}
+
+bool wxBitmap::Create( int width, int height, int depth )
+{
+ UnRef();
+
+ if ( width <= 0 || height <= 0 )
+ {
+ return false;
+ }
+
+ if (depth == 32)
+ {
+ SetPixbuf(gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, width, height), 32);
+ }
+ else
+ {
+ if (depth != 1)
+ {
+ const GdkVisual* visual = wxTheApp->GetGdkVisual();
+ if (depth == -1)
+ depth = visual->depth;
+
+ wxCHECK_MSG(depth == visual->depth, false, wxT("invalid bitmap depth"));
+ }
+
+ SetPixmap(gdk_pixmap_new(wxGetRootWindow()->window, width, height, depth));
+ }
+
+ return Ok();
+}
+
+wxBitmap wxBitmap::Rescale(int clipx, int clipy, int clipwidth, int clipheight, int newx, int newy) const
+{
+ wxBitmap bmp;
+
+ wxCHECK_MSG(Ok(), bmp, wxT("invalid bitmap"));
+
+ if (newy==M_BMPDATA->m_width && newy==M_BMPDATA->m_height)
+ return *this;
+
+ int width = wxMax(newx, 1);
+ int height = wxMax(newy, 1);
+ width = wxMin(width, clipwidth);
+ height = wxMin(height, clipheight);
+
+ // scale pixbuf if available and it has alpha or there is no mask
+ if (M_BMPDATA->m_pixbuf != NULL && (
+ M_BMPDATA->m_mask == NULL || gdk_pixbuf_get_has_alpha(M_BMPDATA->m_pixbuf)))
+ {
+ bmp.SetPixbuf(gdk_pixbuf_new(GDK_COLORSPACE_RGB,
+ gdk_pixbuf_get_has_alpha(M_BMPDATA->m_pixbuf),
+ 8, width, height), M_BMPDATA->m_bpp);
+ gdk_pixbuf_scale(M_BMPDATA->m_pixbuf, bmp.GetPixbuf(),
+ 0, 0, width, height,
+ clipx, clipy,
+ (double)newx/GetWidth(), (double)newy/GetHeight(),
+ GDK_INTERP_BILINEAR);
+ }
+ else
+ {
+ GdkImage* img = gdk_drawable_get_image(
+ M_BMPDATA->m_pixmap, 0, 0, M_BMPDATA->m_width, M_BMPDATA->m_height);
+
+ wxCHECK_MSG(img, bmp, wxT("couldn't create image"));
+
+ GdkGC *gc = NULL;
+ GdkPixmap *dstpix = NULL;
+ char *dst = NULL;
+ long dstbyteperline = 0;
+
+ if (GetDepth() != 1)
+ {
+ bmp.Create(width, height, gdk_drawable_get_depth(M_BMPDATA->m_pixmap));
+ dstpix = bmp.GetPixmap();
+ gc = gdk_gc_new( dstpix );
+ }
+ else
+ {
+ dstbyteperline = (width + 7) / 8;
+ dst = (char*) malloc(dstbyteperline*height);
+ }
+
+ // be careful to use the right scaling factor
+ float scx = (float)M_BMPDATA->m_width/(float)newx;
+ float scy = (float)M_BMPDATA->m_height/(float)newy;
+ // prepare accel-tables
+ int *tablex = (int *)calloc(width,sizeof(int));
+ int *tabley = (int *)calloc(height,sizeof(int));
+
+ // accel table filled with clipped values
+ for (int x = 0; x < width; x++)
+ tablex[x] = (int) (scx * (x+clipx));
+ for (int y = 0; y < height; y++)
+ tabley[y] = (int) (scy * (y+clipy));
+
+ // Main rescaling routine starts here
+ for (int h = 0; h < height; h++)
+ {
+ char outbyte = 0;
+ int old_x = -1;
+ guint32 old_pixval = 0;
+
+ for (int w = 0; w < width; w++)
+ {
+ guint32 pixval;
+ int x = tablex[w];
+ if (x == old_x)
+ pixval = old_pixval;
+ else
+ {
+ pixval = gdk_image_get_pixel( img, x, tabley[h] );
+ old_pixval = pixval;
+ old_x = x;
+ }
+
+ if ( dst )
+ {
+ if (pixval)
+ {
+ char bit=1;
+ char shift = bit << (w % 8);
+ outbyte |= shift;
+ }
+
+ if ((w+1)%8==0)
+ {
+ dst[h*dstbyteperline+w/8] = outbyte;
+ outbyte = 0;
+ }
+ }
+ else
+ {
+ GdkColor col;
+ col.pixel = pixval;
+ gdk_gc_set_foreground( gc, &col );
+ gdk_draw_point( dstpix, gc, w, h);
+ }
+ }
+
+ // do not forget the last byte
+ if ( dst && (width % 8 != 0) )
+ dst[h*dstbyteperline+width/8] = outbyte;
+ }
+
+ g_object_unref (img);
+ if (gc) g_object_unref (gc);
+
+ if ( dst )
+ {
+ bmp = wxBitmap(dst, width, height, 1);
+ free( dst );
+ }
+
+ if (GetMask())
+ {
+ dstbyteperline = (width + 7) / 8;
+ dst = (char*) malloc(dstbyteperline*height);
+ img = gdk_drawable_get_image(GetMask()->GetBitmap(), 0, 0, GetWidth(), GetHeight());
+
+ for (int h = 0; h < height; h++)
+ {
+ char outbyte = 0;
+ int old_x = -1;
+ guint32 old_pixval = 0;
+
+ for (int w = 0; w < width; w++)
+ {
+ guint32 pixval;
+ int x = tablex[w];
+ if (x == old_x)
+ pixval = old_pixval;
+ else
+ {
+ pixval = gdk_image_get_pixel( img, x, tabley[h] );
+ old_pixval = pixval;
+ old_x = x;
+ }
+
+ if (pixval)
+ {
+ char bit=1;
+ char shift = bit << (w % 8);
+ outbyte |= shift;
+ }
+
+ if ((w+1)%8 == 0)
+ {
+ dst[h*dstbyteperline+w/8] = outbyte;
+ outbyte = 0;
+ }
+ }
+
+ // do not forget the last byte
+ if (width % 8 != 0)
+ dst[h*dstbyteperline+width/8] = outbyte;
+ }
+ wxMask* mask = new wxMask;
+ mask->m_bitmap = gdk_bitmap_create_from_data( wxGetRootWindow()->window, (gchar *) dst, width, height );
+ bmp.SetMask(mask);
+
+ free( dst );
+ g_object_unref (img);
+ }
+
+ free( tablex );
+ free( tabley );
+ }
+
+ return bmp;
+}
+
+bool wxBitmap::CreateFromImage(const wxImage& image, int depth)
+{
+ UnRef();
+
+ wxCHECK_MSG( image.Ok(), false, wxT("invalid image") );
+ wxCHECK_MSG( depth == -1 || depth == 1, false, wxT("invalid bitmap depth") );
+
+ if (image.GetWidth() <= 0 || image.GetHeight() <= 0)
+ return false;
+
+ // create pixbuf if image has alpha and requested depth is compatible
+ if (image.HasAlpha() && (depth == -1 || depth == 32))
+ return CreateFromImageAsPixbuf(image);
+
+ // otherwise create pixmap, if alpha is present it will be converted to mask
+ return 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(wxGetRootWindow()->window, (char*)out, w, h));
+ delete[] out;
+ }
+ else
+ {
+ SetPixmap(gdk_pixmap_new(wxGetRootWindow()->window, w, h, depth));
+ 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);
+ g_object_unref(gc);
+ }
+
+ 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;
+ }
+ }
+ wxMask* mask = new wxMask;
+ mask->m_bitmap = gdk_bitmap_create_from_data(M_BMPDATA->m_pixmap, (char*)out, w, h);
+ SetMask(mask);
+ delete[] out;
+ }
+ return true;
+}
+
+bool wxBitmap::CreateFromImageAsPixbuf(const wxImage& image)
+{
+ wxASSERT(image.HasAlpha());
+
+ int width = image.GetWidth();
+ int height = image.GetHeight();
+
+ Create(width, height, 32);
+ GdkPixbuf* pixbuf = M_BMPDATA->m_pixbuf;
+ 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++, alpha++, out += 4, in += 3)
+ {
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ out[3] = *alpha;
+ }
+ }
+
+ return true;
+}
+
+wxImage wxBitmap::ConvertToImage() const
+{
+ wxCHECK_MSG( Ok(), 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);
+ 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);
+ g_object_unref(gc);
+ 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()->GetBitmap(), 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);
+ }
+
+ return image;