Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

* bitmap.c, image.c, gdip.h, bmpcodec.c, jpegcodec.c, tiffcodec.c,

	  gifcodec.c, gifcodec.h, pngcodec.c, pngcodec.h: Re-applied two 
	  previous changes, since #75254 is now fixed

2005-06-13  Peter Bartok  <pbartok@novell.com>


svn path=/trunk/libgdiplus/; revision=45904
  • Loading branch information...
commit f9635774150a211943f4d59e5f812267256a6599 1 parent 64cade6
Peter Dennis Bartok authored
View
6 src/ChangeLog
@@ -1,5 +1,11 @@
2005-06-13 Peter Bartok <pbartok@novell.com>
+ * bitmap.c, image.c, gdip.h, bmpcodec.c, jpegcodec.c, tiffcodec.c,
+ gifcodec.c, gifcodec.h, pngcodec.c, pngcodec.h: Re-applied two
+ previous changes, since #75254 is now fixed
+
+2005-06-13 Peter Bartok <pbartok@novell.com>
+
* Rolled back last two checkins, to r45601, pre palette patch, due
to the regression caused with Icons (bug #75254)
View
1,186 src/bitmap.c
@@ -22,6 +22,7 @@
* Alexandre Pigolkine (pigolkine@gmx.de)
* Vladimir Vukicevic (vladimir@pobox.com)
* Jordi Mas (jordi@ximian.com)
+ * Jonathan Gilbert (logic@deltaq.org)
*/
#include <glib.h>
@@ -43,6 +44,9 @@ gdip_is_a_supported_pixelformat (PixelFormat fmt)
{
switch (fmt) {
+ case Format1bppIndexed:
+ case Format4bppIndexed:
+ case Format8bppIndexed:
case Format24bppRgb:
case Format32bppArgb:
case Format32bppPArgb:
@@ -53,6 +57,15 @@ gdip_is_a_supported_pixelformat (PixelFormat fmt)
}
}
+/*
+ Returns TRUE if the Bitmap contains indexed (palettized) data.
+*/
+BOOL
+gdip_is_an_indexed_pixelformat (PixelFormat fmt)
+{
+ return ((fmt & PixelFormatIndexed) != 0);
+}
+
void
gdip_bitmap_init (GpBitmap *bitmap)
{
@@ -130,6 +143,37 @@ gdip_bitmap_dispose (GpBitmap *bitmap)
}
}
+/* The default indexed palettes. This code was generated by a tiny C# program.
+ */
+
+static const unsigned int default_Format1bppIndexed_palette[2] =
+ {
+ 0x000000, 0xFFFFFF
+ };
+static const unsigned int default_Format4bppIndexed_palette[16] =
+ {
+ 0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, 0x008080, 0x808080, 0xC0C0C0, 0xFF0000, 0x00FF00, 0xFFFF00, 0x0000FF, 0xFF00FF, 0x00FFFF, 0xFFFFFF
+ };
+static const unsigned int default_Format8bppIndexed_palette[256] =
+ {
+ 0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, 0x008080, 0x808080, 0xC0C0C0, 0xFF0000, 0x00FF00, 0xFFFF00, 0x0000FF, 0xFF00FF, 0x00FFFF, 0xFFFFFF,
+ 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
+ 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000033, 0x000066, 0x000099, 0x0000CC, 0x0000FF, 0x003300, 0x003333,
+ 0x003366, 0x003399, 0x0033CC, 0x0033FF, 0x006600, 0x006633, 0x006666, 0x006699, 0x0066CC, 0x0066FF, 0x009900, 0x009933, 0x009966, 0x009999, 0x0099CC, 0x0099FF,
+ 0x00CC00, 0x00CC33, 0x00CC66, 0x00CC99, 0x00CCCC, 0x00CCFF, 0x00FF00, 0x00FF33, 0x00FF66, 0x00FF99, 0x00FFCC, 0x00FFFF, 0x330000, 0x330033, 0x330066, 0x330099,
+ 0x3300CC, 0x3300FF, 0x333300, 0x333333, 0x333366, 0x333399, 0x3333CC, 0x3333FF, 0x336600, 0x336633, 0x336666, 0x336699, 0x3366CC, 0x3366FF, 0x339900, 0x339933,
+ 0x339966, 0x339999, 0x3399CC, 0x3399FF, 0x33CC00, 0x33CC33, 0x33CC66, 0x33CC99, 0x33CCCC, 0x33CCFF, 0x33FF00, 0x33FF33, 0x33FF66, 0x33FF99, 0x33FFCC, 0x33FFFF,
+ 0x660000, 0x660033, 0x660066, 0x660099, 0x6600CC, 0x6600FF, 0x663300, 0x663333, 0x663366, 0x663399, 0x6633CC, 0x6633FF, 0x666600, 0x666633, 0x666666, 0x666699,
+ 0x6666CC, 0x6666FF, 0x669900, 0x669933, 0x669966, 0x669999, 0x6699CC, 0x6699FF, 0x66CC00, 0x66CC33, 0x66CC66, 0x66CC99, 0x66CCCC, 0x66CCFF, 0x66FF00, 0x66FF33,
+ 0x66FF66, 0x66FF99, 0x66FFCC, 0x66FFFF, 0x990000, 0x990033, 0x990066, 0x990099, 0x9900CC, 0x9900FF, 0x993300, 0x993333, 0x993366, 0x993399, 0x9933CC, 0x9933FF,
+ 0x996600, 0x996633, 0x996666, 0x996699, 0x9966CC, 0x9966FF, 0x999900, 0x999933, 0x999966, 0x999999, 0x9999CC, 0x9999FF, 0x99CC00, 0x99CC33, 0x99CC66, 0x99CC99,
+ 0x99CCCC, 0x99CCFF, 0x99FF00, 0x99FF33, 0x99FF66, 0x99FF99, 0x99FFCC, 0x99FFFF, 0xCC0000, 0xCC0033, 0xCC0066, 0xCC0099, 0xCC00CC, 0xCC00FF, 0xCC3300, 0xCC3333,
+ 0xCC3366, 0xCC3399, 0xCC33CC, 0xCC33FF, 0xCC6600, 0xCC6633, 0xCC6666, 0xCC6699, 0xCC66CC, 0xCC66FF, 0xCC9900, 0xCC9933, 0xCC9966, 0xCC9999, 0xCC99CC, 0xCC99FF,
+ 0xCCCC00, 0xCCCC33, 0xCCCC66, 0xCCCC99, 0xCCCCCC, 0xCCCCFF, 0xCCFF00, 0xCCFF33, 0xCCFF66, 0xCCFF99, 0xCCFFCC, 0xCCFFFF, 0xFF0000, 0xFF0033, 0xFF0066, 0xFF0099,
+ 0xFF00CC, 0xFF00FF, 0xFF3300, 0xFF3333, 0xFF3366, 0xFF3399, 0xFF33CC, 0xFF33FF, 0xFF6600, 0xFF6633, 0xFF6666, 0xFF6699, 0xFF66CC, 0xFF66FF, 0xFF9900, 0xFF9933,
+ 0xFF9966, 0xFF9999, 0xFF99CC, 0xFF99FF, 0xFFCC00, 0xFFCC33, 0xFFCC66, 0xFFCC99, 0xFFCCCC, 0xFFCCFF, 0xFFFF00, 0xFFFF33, 0xFFFF66, 0xFFFF99, 0xFFFFCC, 0xFFFFFF
+ };
+
GpStatus
GdipCreateBitmapFromScan0 (int width, int height, int stride, int format, void *scan0, GpBitmap **bitmap)
@@ -148,6 +192,7 @@ GdipCreateBitmapFromScan0 (int width, int height, int stride, int format, void *
cairo_format = CAIRO_FORMAT_ARGB32;
break;
case Format8bppIndexed:
+ case Format4bppIndexed:
cairo_format = CAIRO_FORMAT_A8;
break;
case Format1bppIndexed:
@@ -168,10 +213,19 @@ GdipCreateBitmapFromScan0 (int width, int height, int stride, int format, void *
result->cairo_format = cairo_format;
if (stride == 0) {
- int bpp = gdip_get_pixel_format_components (format);
- bpp = bpp * gdip_get_pixel_format_depth (format);
- stride = (bpp * width) / 8;
- stride = (stride + (sizeof(pixman_bits_t)-1));
+ if (gdip_is_an_indexed_pixelformat (format)) {
+ int bpp = gdip_get_pixel_format_depth(format);
+ int bits_per_row = bpp * width;
+ stride = (bits_per_row + 7) / 8;
+ }
+ else {
+ int bpp = gdip_get_pixel_format_components (format);
+ bpp = bpp * gdip_get_pixel_format_depth (format);
+ stride = (bpp * width) / 8;
+ }
+
+ /* make sure the stride aligns the next row to a pixman_bits_t boundary */
+ stride += (sizeof(pixman_bits_t)-1);
stride &= ~(sizeof(pixman_bits_t)-1);
}
@@ -179,8 +233,31 @@ GdipCreateBitmapFromScan0 (int width, int height, int stride, int format, void *
scan0 = GdipAlloc (stride * height);
if (scan0 == NULL)
return OutOfMemory;
-
- memset (scan0, 0, stride * height);
+
+ if ((gdip_get_pixel_format_bpp (format) < 16) || gdip_is_an_alpha_pixelformat (format)) {
+ memset (scan0, 0, stride * height);
+ }
+ else {
+ /* Since the pixel format is not an alpha pixel format (i.e., it is
+ * either Format24bppRgb or Format32bppRgb), the image should be
+ * initially black, not initially transparent. Thus, we need to set
+ * the alpha channel, which the user code doesn't think exists but
+ * Cairo is still paying attention to, to 0xFF.
+ */
+ int x, y;
+ ARGB solid_black;
+
+ /* Make sure the alpha channel is at the right end of the word. */
+ set_pixel_bgra (&solid_black, 0,
+ 0, 0, 0, 0xFF);
+
+ for (y=0; y < height; y++) {
+ ARGB *scan = (ARGB *)((char *)scan0 + y * stride);
+ for (x=0; x < width; x++) {
+ scan[x] = solid_black;
+ }
+ }
+ }
own_scan0 = TRUE;
}
@@ -200,6 +277,45 @@ GdipCreateBitmapFromScan0 (int width, int height, int stride, int format, void *
result->image.frameDimensionList[0].frames = &(((GpBitmap *) result)->data);
}
+ /* Make sure indexed images have a palette */
+ if (gdip_is_an_indexed_pixelformat (format)) {
+ int palette_entries = 1 << gdip_get_pixel_format_depth(format);
+ int header_size = sizeof(ColorPalette) - sizeof(ARGB);
+ int bytes_needed = header_size + palette_entries * sizeof(ARGB);
+
+ int i;
+
+ result->image.palette = malloc (bytes_needed);
+
+ if (result->image.palette == NULL)
+ return OutOfMemory;
+
+ result->image.palette->Flags = 0;
+ result->image.palette->Count = palette_entries;
+
+ const unsigned int *default_palette;
+
+ switch (format)
+ {
+ case Format1bppIndexed: default_palette = default_Format1bppIndexed_palette; break;
+ case Format4bppIndexed: default_palette = default_Format4bppIndexed_palette; break;
+ case Format8bppIndexed: default_palette = default_Format8bppIndexed_palette; break;
+
+ /* The compiler doesn't know that these are the only possible
+ * indexed pixel formats This suppresses a warning:
+ */
+ default: default_palette = NULL;
+ }
+
+ for (i=0; i < palette_entries; i++) {
+ set_pixel_bgra (result->image.palette, i * 4,
+ 0xFF & (default_palette[i] >> 16),
+ 0xFF & (default_palette[i] >> 8),
+ 0xFF & default_palette[i] ,
+ 0xFF /* alpha */);
+ }
+ }
+
*bitmap = result;
return Ok;
@@ -298,7 +414,7 @@ GdipCloneBitmapAreaI (int x, int y, int width, int height, PixelFormat format,
result->cairo_format = original->cairo_format;
memcpy (&result->data, &bd, sizeof (GdipBitmapData));
- result->image.pixFormat = format; /* this is only used by image codecs */
+ result->image.pixFormat = format;
result->image.format = original->image.format;
result->image.height = result->data.Height;
result->image.width = result->data.Width;
@@ -328,8 +444,8 @@ gdip_copy_strides (void *dst, int dstStride, void *src, int srcStride, int realB
/*
* Copy srcRect region in srcData to destRect region in destData. No conversion is done. Assumes
- * BitmapData is straight from a GpBitmap, i.e. its format is always PixelFormat32bppArgb. src and
- * dest rects must be the same width/height.
+ * BitmapData is straight from a GpBitmap. src and dest rects must be the same width/height and
+ * bits must be of the same PixelFormat.
*/
GpStatus
@@ -370,9 +486,74 @@ gdip_bitmap_clone_data_rect (GdipBitmapData *srcData, Rect *srcRect, GdipBitmapD
destData->Reserved = GBD_OWN_SCAN0;
}
- gdip_copy_strides (destData->Scan0, destData->Stride,
- srcData->Scan0 + (srcData->Stride * srcRect->Y) + (gdip_get_pixel_format_components (srcData->PixelFormat)
- * srcRect->X), srcData->Stride, destRect->Width * dest_components, destRect->Height);
+ if (!gdip_is_an_indexed_pixelformat (srcData->PixelFormat)) {
+ gdip_copy_strides (destData->Scan0, destData->Stride,
+ srcData->Scan0 + (srcData->Stride * srcRect->Y) + (gdip_get_pixel_format_components (srcData->PixelFormat)
+ * srcRect->X), srcData->Stride, destRect->Width * dest_components, destRect->Height);
+ }
+ else {
+ int src_depth = gdip_get_pixel_format_depth (srcData->PixelFormat);
+
+ /* first, check if the bits are aligned onto a byte boundary */
+ int src_first_x_bit_index = srcRect->X * src_depth;
+ int width_bits = destRect->Width * src_depth;
+
+ int src_first_x_bit_offset_into_byte = src_first_x_bit_index & 7;
+ int width_bit_offset_into_byte = width_bits & 7;
+
+ if (src_first_x_bit_offset_into_byte == 0) {
+ /* the fast path: no mid-byte bit mangling required :-)
+ * this will always be the case for 8-bit images.
+ * basically, the source bits are aligned to the destination
+ * bytes, and it doesn't matter if the width isn't a multiple
+ * of 8 bits, because the remainder is guaranteed to be
+ * allocated by the stride, and will be ignored because the
+ * width will indicate fewer pixels than actually end up being
+ * copied.
+ */
+ gdip_copy_strides (
+ destData->Scan0, destData->Stride,
+ srcData->Scan0 + (src_first_x_bit_index / 8) + (srcData->Stride * srcRect->Y),
+ srcData->Stride, width_bits / 8, destRect->Height);
+ }
+ else {
+ /* the not-so-fast path: no bits are aligned, so the entire image requires bit juggling. */
+
+ unsigned char *src_scan0 = srcData->Scan0;
+ unsigned char *dest_scan0 = destData->Scan0;
+
+ int left_shift = src_first_x_bit_offset_into_byte;
+
+ int left_edge_src_mask = 255 >> src_first_x_bit_offset_into_byte;
+
+ int num_whole_dest_bytes = (width_bits / 8);
+
+ int x, y;
+
+ /* move the src_scan0 up to the first byte with pixel data involved in the copy */
+ src_scan0 += srcRect->Y * srcData->Stride;
+ src_scan0 += (src_first_x_bit_offset_into_byte / 8);
+
+ for (y=0; y < destRect->Height; y++) {
+ unsigned char *src_scan = src_scan0 + y * srcData->Stride;
+ unsigned char *dest_scan = dest_scan0 + y * destData->Stride;
+
+ unsigned short buffer;
+
+ /* jump-start the packing function. it avoids double-sampling the source bits by
+ * using buffer as a shift register; bits 8-15 are the current packed dest pixel,
+ * and some of bits 0-7 are not used, to permit alignment of the pixel data.
+ */
+ buffer = src_scan[0] << left_shift;
+
+ for (x=1; x < destRect->Width; x++) {
+ buffer <<= 8;
+ buffer |= src_scan[x] << left_shift;
+ dest_scan[0] = (buffer >> 8);
+ }
+ }
+ }
+ }
return Ok;
}
@@ -403,7 +584,7 @@ gdip_is_pixel_format_conversion_valid (PixelFormat src, PixelFormat dest)
if (!(src & PixelFormatGDI))
return 0;
- if (src & PixelFormatIndexed)
+ if ((src & PixelFormatIndexed) )// || (dest & PixelFormatIndexed))
return 0;
/* These are the RGB formats */
@@ -420,162 +601,6 @@ gdip_is_pixel_format_conversion_valid (PixelFormat src, PixelFormat dest)
return 0;
}
-/**
- * srcData - input data
- * srcRect - rectangle of input data to place in destData
- * destData - where to place output; only the PixelFormat field is needed,
- * which specifies the output type.
- * destRect - destination rectangle in output.
- */
-
-GpStatus
-gdip_bitmap_change_rect_pixel_format (GdipBitmapData *srcData, Rect *srcRect, GdipBitmapData *destData, Rect *destRect)
-{
- gpointer outBuffer = NULL;
- gint outStride = 0;
- PixelFormat srcFormat, destFormat;
-
- g_return_val_if_fail (srcData != NULL, InvalidParameter);
- g_return_val_if_fail (srcRect != NULL, InvalidParameter);
- g_return_val_if_fail (destData != NULL, InvalidParameter);
- g_return_val_if_fail (destRect != NULL, InvalidParameter);
-
- g_return_val_if_fail (srcRect->Width <= destRect->Width, InvalidParameter);
- g_return_val_if_fail (srcRect->Height <= destRect->Height, InvalidParameter);
-
- srcFormat = srcData->PixelFormat;
- destFormat = destData->PixelFormat;
-
- if (!gdip_is_pixel_format_conversion_valid (srcFormat, destFormat))
- return InvalidParameter;
-
- /* If the pixel formats are the same, our job is easy. */
- if (srcFormat == destFormat) {
- int bitsPerPixel = gdip_get_pixel_format_bpp (srcFormat);
- int bytesPerPixel = bitsPerPixel / 8;
-
- g_return_val_if_fail (bitsPerPixel == 16 || bitsPerPixel == 24 || bitsPerPixel == 32, InvalidParameter);
-
- if (destData->Scan0 == NULL) {
- outStride = bytesPerPixel * destRect->Width;
- while (outStride % sizeof(pixman_bits_t))
- outStride++; /* dword-align each row */
-
- /* Allocate the output buffer */
- outBuffer = GdipAlloc (outStride * destRect->Height);
- if (outBuffer == NULL)
- return OutOfMemory;
-
- destData->Width = destRect->Width;
- destData->Height = destRect->Height;
- destData->Stride = outStride;
- destData->Scan0 = outBuffer;
- destData->Reserved = GBD_OWN_SCAN0;
- } else {
- /* Don't touch the output data, just get the portion of the rect
- * that we're writing to
- */
- outStride = destData->Stride;
- outBuffer = destData->Scan0 + (destRect->Y * destData->Stride) + (destRect->X * bytesPerPixel);
- }
-
- /* Then let gdip_copy_strides do our work for us */
- gdip_copy_strides
- (outBuffer, outStride,
- srcData->Scan0 + (srcRect->Y * srcData->Stride) + (srcRect->X * bytesPerPixel), srcData->Stride,
- bytesPerPixel * destRect->Width,
- destRect->Height);
- } else {
- /* We need to convert pixel formats */
- gboolean convert_24_to_32 = 0;
- gboolean convert_32_to_24 = 0;
- gboolean add_alpha = 0;
-
- int dest_skip = 0;
- int src_skip = 0;
-
- int srcBitsPerPixel = gdip_get_pixel_format_bpp (srcFormat);
- int srcBytesPerPixel = srcBitsPerPixel / 8;
- int destBitsPerPixel = gdip_get_pixel_format_bpp (destFormat);
- int destBytesPerPixel = destBitsPerPixel / 8;
-
- if (destData->Scan0 == NULL) {
- outStride = destBytesPerPixel * destRect->Width;
- while (outStride % sizeof(pixman_bits_t))
- outStride++; /* dword-align each row */
-
- /* Allocate the output buffer */
- outBuffer = GdipAlloc (outStride * destRect->Height);
- if (outBuffer == NULL)
- return OutOfMemory;
-
- destData->Width = destRect->Width;
- destData->Height = destRect->Height;
- destData->Stride = outStride;
- destData->Scan0 = outBuffer;
- destData->Reserved = GBD_OWN_SCAN0;
- } else {
- /* Don't touch the output data, just get the portion of the rect
- * that we're writing to
- */
- outStride = destData->Stride;
- outBuffer = destData->Scan0 + (destRect->Y * destData->Stride) + (destRect->X * destBytesPerPixel);
- }
-
- /* First, figure out the conversion type */
- if (gdip_get_pixel_format_bpp (srcFormat) == 32 && gdip_get_pixel_format_bpp (destFormat) == 24)
- convert_32_to_24 = 1;
- else if (gdip_get_pixel_format_bpp (srcFormat) == 24 && gdip_get_pixel_format_bpp (destFormat) == 32)
- convert_24_to_32 = 1;
-
- if (!(srcFormat & PixelFormatAlpha) && (destFormat & PixelFormatAlpha))
- add_alpha = 1;
-
- if (convert_32_to_24) src_skip = 1;
- if (convert_24_to_32 && !add_alpha) dest_skip = 1;
-
- if (!convert_32_to_24 && !convert_24_to_32) {
- /* Okay, well. The formats aren't identical, but they're the same size --
- * i.e. we might be going 32bppRgb <-> 32bppArgb. We can always copy strides,
- * and then go through and add alpha...
- */
- gdip_copy_strides
- (outBuffer,
- outStride,
- srcData->Scan0 + (srcRect->Y * srcData->Stride) + (srcRect->X * srcBytesPerPixel),
- srcData->Stride,
- destBytesPerPixel * destRect->Width,
- destRect->Height);
- if (add_alpha) {
- unsigned int *ptr = (unsigned int *) outBuffer;
- int i;
- for (i = 0; i < destRect->Height * (outStride / sizeof(unsigned int)); i++)
- *ptr++ |= 0xff000000; /* XXX TODO: check what value windows sets alpha to */
- }
- } else {
- /* We need to do 24->32 or 32->24 conversion */
- unsigned char *srcPtr, *destPtr;
- int i, j;
-
- for (j = 0; j < destRect->Height; j++) {
- srcPtr = srcData->Scan0 + ((srcRect->Y + j) * srcData->Stride) + (srcRect->X * srcBytesPerPixel);
- destPtr = outBuffer + (j * outStride);
- for (i = 0; i < destRect->Width; i++) {
- *destPtr++ = *srcPtr++;
- *destPtr++ = *srcPtr++;
- *destPtr++ = *srcPtr++;
- srcPtr += src_skip;
- if (add_alpha) *destPtr++ = 0xff;
- else destPtr += dest_skip;
- }
- }
- }
- }
-
- return Ok;
-}
-
-
/* Format24bppRgb is internally stored by Cairo as a four bytes. Convert it to 3-byte (RGB) */
int
gdip_from_ARGB_to_RGB (BYTE *src, int width, int height, int stride, BYTE **dest, int* dest_stride)
@@ -609,7 +634,7 @@ gdip_from_ARGB_to_RGB (BYTE *src, int width, int height, int stride, BYTE **dest
}
-/* Format24bppRgb is internally stored by Cairo as a four bytes. Convert it to 3-byte (RGB) */
+/* Format24bppRgb is internally stored by Cairo as a three bytes. Convert it to 4-byte (ARGB) */
int
gdip_from_RGB_to_ARGB (BYTE *src, int width, int height, int stride, BYTE **dest, int* dest_stride)
{
@@ -642,6 +667,660 @@ gdip_from_RGB_to_ARGB (BYTE *src, int width, int height, int stride, BYTE **dest
return Ok;
}
+typedef struct
+{
+ Rect region;
+ int x, y; /* the offset of the next byte that will be loaded, once the buffer is depleted */
+ unsigned short buffer;
+ int p; /* index of pixel within 'buffer' that was returned by the last call to gdip_pixel_stream_get_next () */
+ int one_pixel_mask, one_pixel_shift, pixels_per_byte;
+
+ BitmapData *data;
+ unsigned char *scan;
+} StreamingState;
+
+GpStatus
+gdip_init_pixel_stream (StreamingState *state, BitmapData *data, int x, int y, int w, int h)
+{
+ if ((state == NULL) || (data == NULL) || (data->Scan0 == NULL))
+ return InvalidParameter;
+
+ /* Ensure that the rectangle requested is sensible. */
+ if ((x < 0) || (y < 0) || (x >= data->Width) || (y >= data->Height))
+ return InvalidParameter;
+
+ if ((x + w > data->Width) || (y + h > data->Height))
+ return InvalidParameter;
+
+ /* Initialize the StreamingState structure to point at the first pixel. */
+ state->region.X = x;
+ state->region.Y = y;
+ state->region.Width = w;
+ state->region.Height = h;
+
+ state->x = x;
+ state->y = y;
+
+ state->p = -1; /* ensure that the buffer will be preloaded on the first call, for indexed formats */
+
+ switch (data->PixelFormat)
+ {
+ case Format1bppIndexed: state->one_pixel_mask = 0x01; state->one_pixel_shift = 1; state->pixels_per_byte = 8; break;
+ case Format4bppIndexed: state->one_pixel_mask = 0x0F; state->one_pixel_shift = 4; state->pixels_per_byte = 2; break;
+ case Format8bppIndexed: state->one_pixel_mask = 0xFF; state->one_pixel_shift = 8; state->pixels_per_byte = 1; break;
+ default:
+ state->pixels_per_byte = 0; /* indicate full RGB processing */
+ break;
+ }
+
+ state->data = data;
+
+ /* The following computation will compute the byte pointer that _contains_ the first
+ * pixel; this doesn't necessarily mean that the pixel is aligned to the byte. This
+ * will be handled in gdip_pixel_stream_get_next () each time it starts a new row.
+ */
+ state->scan = (unsigned char *)(data->Scan0)
+ + y * data->Stride
+ + x * gdip_get_pixel_format_bpp (data->PixelFormat) / 8;
+
+ return Ok;
+}
+
+BOOL
+gdip_pixel_stream_has_next (StreamingState *state)
+{
+ if (state != NULL)
+ return (state->p >= 0)
+ || ((state->y < (state->region.Y + state->region.Height))
+ && (state->x < (state->region.X + state->region.Width)));
+ else
+ return FALSE;
+}
+
+unsigned int /* <-- can be an ARGB or a palette index */
+gdip_pixel_stream_get_next (StreamingState *state)
+{
+ unsigned int ret;
+
+ if (state == NULL) {
+ int bright_pink;
+ set_pixel_bgra(&bright_pink, 0,
+ 0xFF, 0x00, 0xFF, 0xFF); /* bright pink; hopefully this will get somebody's attention :-) */
+ return bright_pink;
+ }
+
+ /* Note: This function does not check whether the end of the region has been hit. This function can
+ * potentially overrun memory buffers! gdip_pixel_stream_has_next () must be used in conjunction
+ * with this function.
+ */
+
+ if (state->pixels_per_byte == 1) {
+ /* A fast path for 8-bit indexed data: pixels are byte-aligned, so no special unpacking is required. */
+ ret = *state->scan;
+
+ state->scan++;
+ state->x++;
+
+ if (state->x >= (state->region.X + state->region.Width)) {
+ state->x = state->region.X;
+ state->y++;
+
+ state->scan = (unsigned char *)(state->data->Scan0)
+ + state->y * state->data->Stride
+ + state->x;
+ }
+ }
+ else if (state->pixels_per_byte > 0) {
+ /* We have an indexed format (the RGB formats can't fit a whole pixel into a single byte). */
+ if (state->p < 0) {
+ state->buffer = *state->scan;
+ state->scan++;
+ state->p = 0;
+
+ if (state->x == state->region.X) {
+ /* First pixel of the row; check whether it is aligned to the byte or not. */
+ int index_into_byte = state->x & (state->pixels_per_byte - 1);
+
+ if (index_into_byte != 0) {
+ /* Not aligned; need to advance the buffer to the
+ * first pixel in the stream region.
+ */
+ state->buffer <<= (index_into_byte * state->one_pixel_shift);
+ state->p = index_into_byte;
+ }
+ }
+ }
+
+ state->buffer <<= state->one_pixel_shift;
+
+ ret = (state->buffer >> 8) & state->one_pixel_mask;
+
+ state->x++;
+ state->p++;
+
+ /* Have we hit the end of the buffer? */
+ if (state->p >= state->pixels_per_byte)
+ state->p = -1;
+
+ if (state->x >= (state->region.X + state->region.Width)) {
+ state->x = state->region.X;
+ state->y++;
+
+ state->scan = (unsigned char *)(state->data->Scan0)
+ + state->y * state->data->Stride
+ + state->x * gdip_get_pixel_format_bpp (state->data->PixelFormat) / 8;
+
+ state->p = -1;
+ }
+ }
+ else {
+ /* We have an RGB format. In the current implementation, these are always stored as
+ * CAIRO_FORMAT_ARGB. This makes this section very easy to implement. If native
+ * support for 15- and 16-bit pixel formats needs to be added in the future, though,
+ * then this is where it needs to be done.
+ */
+ ret = *(unsigned int *)state->scan;
+
+ /* Special case: 24-bit data needs to have the cairo format alpha component forced
+ * to 0xFF, or many operations will do nothing (or do strange things if the alpha
+ * channel contains garbage).
+ */
+ if (state->data->PixelFormat == Format24bppRgb) {
+ int force_alpha;
+ set_pixel_bgra(&force_alpha, 0,
+ 0, 0, 0, 0xFF);
+ ret |= force_alpha;
+ }
+
+ state->scan += 4;
+ state->x++;
+
+ if (state->x >= (state->region.X + state->region.Width)) {
+ state->x = state->region.X;
+ state->y++;
+
+ state->scan = (unsigned char *)(state->data->Scan0)
+ + state->y * state->data->Stride
+ + state->x * 4;
+ }
+ }
+
+ return ret;
+}
+
+void
+gdip_pixel_stream_set_next (StreamingState *state, unsigned int pixel_value)
+{
+ if (state == NULL)
+ return;
+
+ /* Note: This function does not check whether the end of the region has been hit. This function can
+ * potentially overrun memory buffers! gdip_pixel_stream_has_next () must be used in conjunction
+ * with this function.
+ */
+
+ if (state->pixels_per_byte == 1) {
+ /* A fast path for 8-bit indexed data: pixels are byte-aligned, so no special packing is required. */
+ *state->scan = pixel_value & 0xFF;
+
+ state->scan++;
+ state->x++;
+
+ if (state->x >= (state->region.X + state->region.Width)) {
+ state->x = state->region.X;
+ state->y++;
+
+ state->scan = (unsigned char *)(state->data->Scan0)
+ + state->y * state->data->Stride
+ + state->x;
+ }
+ }
+ else if (state->pixels_per_byte > 0) {
+ /* We have an indexed format (the RGB formats can't fit a whole pixel into a single byte). */
+ if (state->p < 0) {
+ state->p = 0;
+
+ if (state->x == state->region.X) {
+ /* First pixel of the row; check whether it is aligned to the byte or not. */
+ int index_into_byte = state->x & (state->pixels_per_byte - 1);
+
+ if (index_into_byte == 0) {
+ /* It is aligned; all we need to do is clear the buffer. */
+ state->buffer = 0;
+ }
+ else {
+ /* It is not aligned; the buffer needs to be pre-loaded with those
+ * pixels that are to the left of the first pixel to be set.
+ */
+ state->buffer = (*state->scan << (index_into_byte * state->one_pixel_shift));
+ state->p = index_into_byte;
+ }
+ }
+ }
+
+ state->buffer <<= state->one_pixel_shift;
+ state->buffer |= ((pixel_value & state->one_pixel_mask) << 8);
+
+ state->x++;
+ state->p++;
+
+ /* Have we hit the end of the buffer? */
+ if (state->p >= state->pixels_per_byte) {
+ *state->scan = (state->buffer >> 8);
+ state->scan++;
+ state->p = -1;
+ }
+
+ if (state->x >= (state->region.X + state->region.Width)) {
+ if (state->p >= 0) {
+ int existing_mask = 0;
+
+ while (state->p < state->pixels_per_byte) {
+ existing_mask <<= state->one_pixel_shift;
+ existing_mask |= state->one_pixel_mask;
+
+ state->buffer <<= state->one_pixel_shift;
+ state->p++;
+ }
+
+ *state->scan = (*state->scan & existing_mask) | (state->buffer >> 8);
+ }
+
+ state->x = state->region.X;
+ state->y++;
+
+ state->scan = (unsigned char *)(state->data->Scan0)
+ + state->y * state->data->Stride
+ + state->x * gdip_get_pixel_format_bpp (state->data->PixelFormat) / 8;
+
+ state->p = -1;
+ }
+ }
+ else {
+ /* We have an RGB format. In the current implementation, these are always stored as
+ * CAIRO_FORMAT_ARGB. This makes this section very easy to implement. If native
+ * support for 15- and 16-bit pixel formats needs to be added in the future, though,
+ * then this is where it needs to be done.
+ */
+ *(unsigned int *)state->scan = pixel_value;
+
+ state->scan += 4;
+ state->x++;
+
+ if (state->x >= (state->region.X + state->region.Width)) {
+ state->x = state->region.X;
+ state->y++;
+
+ state->scan = (unsigned char *)(state->data->Scan0)
+ + state->y * state->data->Stride
+ + state->x * 4;
+ }
+ }
+}
+
+/**
+ * srcData - input data
+ * srcRect - rectangle of input data to place in destData
+ * destData - where to place output; only the PixelFormat field is needed,
+ * which specifies the output type.
+ * destRect - destination rectangle in output.
+ *
+ * assumes that the pixel format conversion has already been validated.
+ */
+
+GpStatus
+gdip_bitmap_change_rect_pixel_format (GdipBitmapData *srcData, Rect *srcRect, GdipBitmapData *destData, Rect *destRect)
+{
+ PixelFormat srcFormat, destFormat;
+ StreamingState srcStream, destStream;
+ Rect effectiveDestRect;
+
+ GpStatus status;
+
+ g_return_val_if_fail (srcData != NULL, InvalidParameter);
+ g_return_val_if_fail (srcRect != NULL, InvalidParameter);
+ g_return_val_if_fail (destData != NULL, InvalidParameter);
+ g_return_val_if_fail (destRect != NULL, InvalidParameter);
+
+ if ((srcRect->X < 0) || (srcRect->Y < 0) || (srcRect->X >= srcData->Width) || (srcRect->Y >= srcData->Height))
+ return InvalidParameter;
+ if ((srcRect->X + srcRect->Width > srcData->Width) || (srcRect->Y + srcRect->Height > srcData->Height))
+ return InvalidParameter;
+
+ if ((destRect->X < 0) || (destRect->Y < 0))
+ return InvalidParameter;
+
+ g_return_val_if_fail (srcRect->Width <= destRect->Width, InvalidParameter);
+ g_return_val_if_fail (srcRect->Height <= destRect->Height, InvalidParameter);
+
+ srcFormat = srcData->PixelFormat;
+ destFormat = destData->PixelFormat;
+
+ if (!gdip_is_pixel_format_conversion_valid (srcFormat, destFormat))
+ return InvalidParameter;
+
+ if (destData->Scan0 == NULL) {
+ /* Allocate a buffer on behalf of the caller. */
+ int width = destRect->X + destRect->Width;
+ int scans = destRect->Y + destRect->Height;
+
+ int row_bits = destRect->Width * gdip_get_pixel_format_bpp (destFormat);
+ int row_bytes = (row_bits + 7) / 8;
+
+ int stride = (row_bytes + sizeof(pixman_bits_t) - 1) & ~(sizeof(pixman_bits_t) - 1);
+
+ void *dest_scan0 = malloc(stride * scans);
+
+ if (dest_scan0 == NULL)
+ return OutOfMemory;
+
+ destData->Width = destRect->X + destRect->Width;
+ destData->Height = destRect->Y + destRect->Height;
+ destData->Stride = stride;
+ destData->Scan0 = dest_scan0;
+ destData->Reserved = GBD_OWN_SCAN0;
+ }
+ else {
+ /* Check that the destRect lies fully within the destData buffer. */
+ if ((destRect->X + destRect->Width > destData->Width) || (destRect->Y + destRect->Height > destData->Height))
+ return InvalidParameter;
+ }
+
+ effectiveDestRect = *destRect;
+
+ if (effectiveDestRect.Width > srcRect->Width)
+ effectiveDestRect.Width = srcRect->Width;
+ if (effectiveDestRect.Height > srcRect->Height)
+ effectiveDestRect.Height = srcRect->Height;
+
+ /* Fire up the pixel streams. */
+ status = gdip_init_pixel_stream (&srcStream, srcData, srcRect->X, srcRect->Y, srcRect->Width, srcRect->Height);
+
+ if (status != Ok)
+ return status;
+
+ status = gdip_init_pixel_stream (&destStream, destData, effectiveDestRect.X, effectiveDestRect.Y, effectiveDestRect.Width, effectiveDestRect.Height);
+
+ if (status != Ok)
+ return status;
+
+ /* Move the data. */
+ while (gdip_pixel_stream_has_next (&srcStream))
+ gdip_pixel_stream_set_next (&destStream, gdip_pixel_stream_get_next (&srcStream));
+
+ return Ok;
+}
+
+
+#define NEW_LOCKBITS_IMPL
+
+#ifdef NEW_LOCKBITS_IMPL
+BOOL
+gdip_is_a_32bit_pixelformat (PixelFormat format)
+{
+ switch (format)
+ {
+ case Format32bppRgb:
+ case Format32bppArgb:
+ case Format32bppPArgb: /* all of these use CAIRO_FORMAT_ARGB, which is 4 bytes wide */
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+BOOL
+gdip_is_an_alpha_pixelformat (PixelFormat format)
+{
+ return ((format & PixelFormatAlpha) != 0);
+}
+
+BOOL
+gdip_can_window_without_copy (BitmapData *data, Rect *rect, int format)
+{
+ int bpp = gdip_get_pixel_format_bpp (format);
+
+ if (format != data->PixelFormat) {
+ /* can't possibly reinterpret bits from one indexed pixel
+ * format as being of another indexed pixel format...
+ */
+ if (gdip_is_an_indexed_pixelformat (format)
+ || gdip_is_an_indexed_pixelformat (data->PixelFormat))
+ return FALSE;
+
+ /* ...but we can probably handle 24-bit<->32-bit and
+ * 32-bit alpha<->32-bit opaque without copying data,
+ * since these are all stored as CAIRO_FORMAT_ARGB
+ * internally.
+ */
+ if (!gdip_is_a_32bit_pixelformat (format)
+ || !gdip_is_a_32bit_pixelformat (data->PixelFormat))
+ return FALSE;
+ }
+
+ /* okay, so the pixel formats are compatible. now, make sure
+ * the rectangle lies on byte boundaries; if it doesn't, then
+ * pixels will have to be shuffled. =/
+ */
+
+ /* 8bpp and above are guaranteed to be byte-aligned */
+ if (bpp >= 8)
+ return TRUE;
+ else {
+ int left_bit_offset = rect->X * bpp;
+ int width_bit_count = rect->Width * bpp;
+
+ /* check whether the values are byte-aligned */
+ return ((left_bit_offset & 7) | (width_bit_count & 7)) == 0;
+ }
+}
+
+void
+gdip_make_alpha_opaque (BitmapData *data)
+{
+ unsigned char *scan0 = (unsigned char *)data->Scan0;
+ int y, x, o, f;
+
+ /* sanity check; make sure we aren't mangling any image data */
+ if ((data->PixelFormat != Format32bppArgb)
+ && (data->PixelFormat != Format32bppRgb))
+ return;
+
+ f = data->Stride - 4 * data->Width;
+ for (y=0, o=0; y < data->Height; y++, o += f)
+ for (x=0; x < data->Width; x++, o += 4)
+ scan0[o + 3] = 0xff; /* set alpha to fully-opaque */
+}
+
+GpStatus
+GdipBitmapLockBits (GpBitmap *bitmap, Rect *srcRect, int flags, int format, GdipBitmapData *locked_data)
+{
+ BitmapData *root_data = &bitmap->data;
+
+ g_return_val_if_fail (bitmap != NULL, InvalidParameter);
+ g_return_val_if_fail (srcRect != NULL, InvalidParameter);
+ g_return_val_if_fail (flags != 0, InvalidParameter);
+ g_return_val_if_fail (locked_data != NULL, InvalidParameter);
+
+ /* There is no way to set ImageLockModeUserInputBuf with S.D, so we don't
+ * support it
+ */
+ if (flags & ImageLockModeUserInputBuf)
+ return NotImplemented;
+
+ /* Is this bitmap already locked? */
+ if (root_data->Reserved & GBD_LOCKED)
+ return InvalidParameter;
+
+ if (!bitmap || !srcRect || !locked_data)
+ return InvalidParameter;
+
+ /* Make sure the srcRect makes sense */
+ if ((srcRect->X < 0) || (srcRect->Y < 0) || (srcRect->Width < 0) || (srcRect->Height < 0))
+ return InvalidParameter;
+
+ if ((srcRect->X + srcRect->Width > root_data->Width) || (srcRect->Y + srcRect->Height > root_data->Height))
+ return InvalidParameter;
+
+ if (gdip_is_a_supported_pixelformat (format) == FALSE)
+ return NotImplemented;
+
+ /* Common stuff */
+ if (flags == ImageLockModeRead)
+ locked_data->Reserved |= GBD_READ_ONLY;
+ else
+ locked_data->Reserved &= ~GBD_READ_ONLY;
+
+ locked_data->Reserved |= GBD_LOCKED;
+ locked_data->Reserved |= GBD_OWN_SCAN0;
+ root_data->Reserved |= GBD_LOCKED;
+
+ if (gdip_can_window_without_copy (root_data, srcRect, format)) {
+ /* Compute the start of the window; it's guaranteed to be on
+ * a byte boundary since the preceding function call
+ * returned true
+ */
+ locked_data->Scan0 = root_data->Scan0
+ + srcRect->Y * root_data->Stride
+ + srcRect->X * gdip_get_pixel_format_bpp(root_data->PixelFormat) / 8;
+
+ /* Set up the window's dimensions */
+ locked_data->Width = srcRect->Width;
+ locked_data->Height = srcRect->Height;
+ locked_data->Stride = root_data->Stride; /* the stride hasn't changed */
+ locked_data->PixelFormat = root_data->PixelFormat;
+
+ /* Make sure the bits don't get deallocated, since Scan0 doesn't
+ * necessarily point at the start of a memory block, and
+ * the bits are owned by the root bitmap anyway
+ */
+ locked_data->Reserved &= ~GBD_OWN_SCAN0;
+
+ /* If the source pixel format doesn't have alpha and the dest pixel format
+ * wants it, then overwrite the alpha byte in the original bitmap. it is
+ * unused anyway :-)
+ */
+ if (!gdip_is_an_alpha_pixelformat (root_data->PixelFormat)
+ && gdip_is_an_alpha_pixelformat (locked_data->PixelFormat)) {
+ gdip_make_alpha_opaque (locked_data);
+ }
+
+ return Ok;
+ }
+ else {
+ /* If we get here, then something more drastic is needed. either the user's rectangle
+ * doesn't line up on byte boundaries, or the pixel format needs to be changed.
+ */
+ int dest_pixel_format_bpp = gdip_get_pixel_format_bpp (format);
+ int dest_stride = (srcRect->Width * dest_pixel_format_bpp + 7) / 8;
+ int dest_size = srcRect->Height * dest_stride;
+
+ unsigned char *dest_scan0 = malloc(dest_size);
+
+ Rect destRect = { 0, 0, srcRect->Width, srcRect->Height };
+
+ GpStatus status = Ok;
+
+ if (dest_scan0 == NULL)
+ return OutOfMemory;
+
+ locked_data->Scan0 = dest_scan0;
+ locked_data->Width = srcRect->Width;
+ locked_data->Height = srcRect->Height;
+ locked_data->Stride = dest_stride;
+ locked_data->PixelFormat = format;
+
+ /* If the user wants the original data to be readable, then convert the bits. */
+ if ((flags & ImageLockModeRead) != 0) {
+ status = gdip_bitmap_change_rect_pixel_format (root_data, srcRect, locked_data, &destRect);
+ if (status != Ok)
+ free(dest_scan0);
+ }
+
+ return status;
+ }
+}
+
+GpStatus
+GdipBitmapUnlockBits (GpBitmap *bitmap, GdipBitmapData *locked_data)
+{
+ BitmapData *root_data = &bitmap->data;
+
+ g_return_val_if_fail (bitmap != NULL, InvalidParameter);
+ g_return_val_if_fail (locked_data != NULL, InvalidParameter);
+
+ /* Make sure the bitmap is locked when the unlock happens */
+ if (!(bitmap->data.Reserved & GBD_LOCKED))
+ return InvalidParameter;
+
+ /* It is not safe to assume that the correct BitmapData has been passed in.
+ * Sanity check: Make sure the locked data is in fact locked.
+ */
+ if ((locked_data->Reserved & GBD_LOCKED) == 0)
+ return InvalidParameter;
+
+ /* Sanity check: Make sure the locked data's size is consistent with having
+ * been returned from LockBits ().
+ */
+ if ((locked_data->Width > root_data->Width) || (locked_data->Height > root_data->Height))
+ return InvalidParameter;
+
+ /* Check whether the locked data window was created without
+ * duplicating the bits to be locked.
+ */
+ if ((int)(((char *)locked_data->Scan0) - ((char *)root_data->Scan0)) < root_data->Height * root_data->Stride) {
+ /* Since the locked data's Scan0 is inside the root data's Scan0, it is
+ * likely that the data was locked with the fast path of LockBits. However,
+ * a couple more sanity checks are required: The locked data's stride must
+ * be the same as the root data's stride, and the end of the locked data's
+ * bits must also lie within the root data's bits.
+ */
+ char *byte_after_last_byte = (char *)locked_data->Scan0
+ + (locked_data->Height - 1) * locked_data->Stride
+ + (locked_data->Width * gdip_get_pixel_format_bpp (locked_data->PixelFormat) + 7) / 8;
+
+ if ((locked_data->Stride != root_data->Stride)
+ || ((int)(byte_after_last_byte - (char *)root_data->Scan0) > root_data->Height * root_data->Stride))
+ return InvalidParameter;
+
+ /* We can be reasonably sure by this point that the locked data is authentic,
+ * so now the only thing that needs to be done is alpha fixup.
+ */
+ if ((locked_data->Reserved & GBD_READ_ONLY) != 0) {
+ if (!gdip_is_an_alpha_pixelformat (locked_data->PixelFormat)
+ && gdip_is_an_alpha_pixelformat (root_data->PixelFormat)) {
+ gdip_make_alpha_opaque (locked_data);
+ }
+ }
+ }
+ else {
+ /* We need to copy the locked data back to the root data's Scan0
+ * (but of course only if the ImageLockMode specified writability)
+ */
+ if ((locked_data->Reserved & GBD_READ_ONLY) != 0) {
+ /* FIXME: destRect isn't necessarily equal to srcRect. this is a bug (the old implementation had it too). */
+ Rect srcRect = { 0, 0, locked_data->Width, locked_data->Height };
+ Rect destRect = srcRect;
+
+ GpStatus status = gdip_bitmap_change_rect_pixel_format (root_data, &srcRect, locked_data, &destRect);
+ if (status != Ok)
+ return status;
+ }
+ }
+
+ if ((locked_data->Reserved & GBD_OWN_SCAN0) != 0) {
+ free(locked_data->Scan0);
+ locked_data->Scan0 = NULL;
+ locked_data->Reserved &= ~GBD_OWN_SCAN0;
+ }
+
+ locked_data->Reserved &= ~GBD_LOCKED;
+ root_data->Reserved &= ~GBD_LOCKED;
+
+ return Ok;
+}
+
+#else
/* Microsoft GDI+ returns BitmapLock buffers in BGR (not RGB) */
GpStatus
@@ -809,6 +1488,7 @@ GdipBitmapUnlockBits (GpBitmap *bitmap, GdipBitmapData *bitmap_data)
return Ok;
}
+#endif // NEW_LOCKBITS_IMPL
GpStatus
GdipBitmapSetPixel (GpBitmap *bitmap, int x, int y, ARGB color)
@@ -824,7 +1504,10 @@ GdipBitmapSetPixel (GpBitmap *bitmap, int x, int y, ARGB color)
if (y < 0 || y > data->Height)
return InvalidParameter;
-
+
+ if (gdip_is_an_indexed_pixelformat (data->PixelFormat))
+ return InvalidParameter;
+
/* BMP Locked */
if (bitmap->data.Reserved & GBD_LOCKED)
return InvalidParameter;
@@ -832,53 +1515,21 @@ GdipBitmapSetPixel (GpBitmap *bitmap, int x, int y, ARGB color)
v = (unsigned char *)(data->Scan0) + y * data->Stride;
switch (data->PixelFormat) {
case Format24bppRgb:
- v += x * 4;
-#ifdef WORDS_BIGENDIAN
- v [3] = color & 0xff;
- v [2] = (color >> 8) & 0xff;
- v [1] = (color >> 16) & 0xff;
- v [0] = 0xff;
-#else
- v [0] = color & 0xff;
- v [1] = (color >> 8) & 0xff;
- v [2] = (color >> 16) & 0xff;
- v [3] = 0xff;
-#endif
- break;
-
+ case Format32bppRgb:
+ color |= 0xFF000000; /* force the alpha for Cairo */
+ /* fall through */
case Format32bppArgb:
case Format32bppPArgb:
- v += x * 4;
-#ifdef WORDS_BIGENDIAN
- v [3] = color & 0xff;
- v [2] = (color >> 8) & 0xff;
- v [1] = (color >> 16) & 0xff;
- v [0] = color >> 24;
-#else
- v [0] = color & 0xff;
- v [1] = (color >> 8) & 0xff;
- v [2] = (color >> 16) & 0xff;
- v [3] = color >> 24;
-#endif
- break;
-
- case Format32bppRgb:
- v += x * 4;
-#ifdef WORDS_BIGENDIAN
- v [3] = color & 0xff;
- v [2] = (color >> 8) & 0xff;
- v [1] = (color >> 16) & 0xff;
- v [0] = 0xff;
-#else
- v [0] = color & 0xff;
- v [1] = (color >> 8) & 0xff;
- v [2] = (color >> 16) & 0xff;
- v [3] = 0xff;
-#endif
+ {
+ ARGB *scan = (ARGB *)v;
+
+ scan[x] = color;
break;
+ }
default:
return NotImplemented;
}
+
return Ok;
}
@@ -896,26 +1547,27 @@ GdipBitmapGetPixel (GpBitmap *bitmap, int x, int y, ARGB *color)
if (y < 0 || y > data->Height)
return InvalidParameter;
+
+ if (gdip_is_an_indexed_pixelformat (data->PixelFormat))
+ return InvalidParameter;
/* BMP Locked */
if (bitmap->data.Reserved & GBD_LOCKED)
return InvalidParameter;
v = ((unsigned char *)data->Scan0) + y * data->Stride;
- switch (data->PixelFormat) {
+ switch (data->PixelFormat)
+ {
case Format24bppRgb:
- v += x * 4;
- *color = (v [0]) | (v [1] << 8) | (v [2] << 16) | (0xff << 24);
- break;
case Format32bppArgb:
case Format32bppPArgb:
- v += x * 4;
- *color = (v [0]) | (v [1] << 8) | (v [2] << 16) | (v [3] << 24);
- break;
case Format32bppRgb:
- v += x * 4;
- *color = (v [0]) | (v [1] << 8) | (v [2] << 16)| (0xff << 24);
+ {
+ ARGB *scan = (ARGB *)v;
+
+ *color = scan[x];
break;
+ }
default:
return NotImplemented;
}
@@ -970,3 +1622,95 @@ gdip_bitmap_ensure_surface (GpBitmap *bitmap)
return bitmap->image.surface;
}
+
+GpBitmap *
+gdip_convert_indexed_to_rgb (GpBitmap *indexed_bmp)
+{
+ BitmapData *data = &indexed_bmp->data;
+ ColorPalette *palette = indexed_bmp->image.palette;
+
+ int rgb_stride, rgb_bytes, force_alpha;
+ int one_pixel_mask, one_pixel_shift, pixels_per_byte;
+ int *rgb_scan0;
+ int p, x, y;
+
+ GpBitmap *ret;
+ GpStatus status;
+
+ if (!gdip_is_an_indexed_pixelformat (data->PixelFormat))
+ return NULL;
+
+ if (palette == NULL)
+ return NULL;
+
+ switch (data->PixelFormat)
+ {
+ case Format1bppIndexed: one_pixel_mask = 0x01; one_pixel_shift = 1; pixels_per_byte = 8; break;
+ case Format4bppIndexed: one_pixel_mask = 0x0F; one_pixel_shift = 4; pixels_per_byte = 2; break;
+ case Format8bppIndexed: one_pixel_mask = 0xFF; one_pixel_shift = 8; pixels_per_byte = 1; break;
+ default: /* something is wrong!! */
+ return NULL;
+ }
+
+ if ((palette->Flags & PaletteFlagsHasAlpha) == 0)
+ set_pixel_bgra (&force_alpha, 0,
+ 0, 0, 0, 0xFF); /* full alpha bits set */
+ else
+ force_alpha = 0;
+
+ rgb_stride = data->Width * 4;
+
+ /* ensure pixman_bits_t alignment */
+ rgb_stride += (sizeof(pixman_bits_t)-1);
+ rgb_stride &= ~(sizeof(pixman_bits_t)-1);
+
+ rgb_bytes = data->Height * rgb_stride;
+
+ /* allocate the RGB frame */
+ rgb_scan0 = malloc (rgb_bytes);
+
+ if (rgb_scan0 == NULL) /* out of memory?? */
+ return NULL;
+
+ /* convert the indexed pixels into RGB values and store them into the RGB frame */
+ for (y=0; y < data->Height; y++)
+ {
+ unsigned char *indexed_scan = (unsigned char *)(data->Scan0) + y * data->Stride;
+ int *rgb_scan = rgb_scan0 + (y * rgb_stride) / sizeof(int);
+
+ for (x=0; x < data->Width; x += pixels_per_byte)
+ {
+ int pixels_this_byte = pixels_per_byte;
+ unsigned short sample = *indexed_scan;
+
+ indexed_scan++;
+
+ if (x + pixels_this_byte >= data->Width)
+ pixels_this_byte = data->Width - x;
+
+ for (p=0; p < pixels_this_byte; p++)
+ {
+ int index;
+
+ sample <<= one_pixel_shift;
+ index = (sample >> 8) & one_pixel_mask;
+
+ rgb_scan[x + p] = palette->Entries[index] | force_alpha;
+ }
+ }
+ }
+
+ /* try to get a GpBitmap out of it :-) */
+ status = GdipCreateBitmapFromScan0 (data->Width, data->Height, rgb_stride, Format32bppRgb, rgb_scan0, &ret);
+
+ if ((status != Ok) || (ret == NULL)) {
+ free (ret);
+ free (rgb_scan0);
+ return NULL;
+ }
+ else {
+ ret->data.Reserved |= GBD_OWN_SCAN0;
+ return ret;
+ }
+}
+
View
1,040 src/bmpcodec.c
@@ -22,6 +22,7 @@
* Jordi Mas i Hernandez (jordi@ximian.com)
* Sanjay Gupta (gsanjay@novell.com)
* Mark Steele (ms@rapidsys.com)
+ * Jonathan Gilbert (logic@deltaq.org)
*
* Useful documentation about bitmaps
*
@@ -39,6 +40,7 @@
#include <stdio.h>
#include "bmpcodec.h"
#include "gdip.h"
+#include "gdipImage.h"
/* Codecinfo related data*/
@@ -81,9 +83,42 @@ gdip_get_pixelformat (WORD bitcount)
return Format8bppIndexed;
case 4:
return Format4bppIndexed;
+ case 1:
+ return Format1bppIndexed;
default:
return 0;
- }
+ }
+}
+
+GpStatus
+gdip_get_bmp_pixelformat (int bitCount, int compression, PixelFormat *dest)
+{
+ switch (compression)
+ {
+ case BI_RLE4:
+ {
+ if (bitCount != 4)
+ return InvalidParameter;
+ *dest = Format4bppIndexed;
+ break;
+ }
+ case BI_RLE8:
+ {
+ if (bitCount != 8)
+ return InvalidParameter;
+ *dest = Format8bppIndexed;
+ break;
+ }
+ default:
+ {
+ *dest = gdip_get_pixelformat (bitCount);
+ if (0 == *dest) /* invalid pixel format */
+ return InvalidParameter;
+ break;
+ }
+ }
+
+ return Ok;
}
void
@@ -96,7 +131,7 @@ gdip_bitmap_fill_info_header (GpBitmap *bitmap, PBITMAPINFOHEADER bmi)
bmi->biWidth = GULONG_FROM_LE (bitmap->data.Width);
bmi->biHeight = GULONG_FROM_LE (bitmap->data.Height);
bmi->biPlanes = GUINT16_FROM_LE (1);
- bmi->biBitCount = GUINT16_FROM_LE (32); /*PIXEL_FORMAT_BPP (bitmap->image.pixFormat); */
+ bmi->biBitCount = GUINT16_FROM_LE (gdip_get_pixel_format_bpp (bitmap->data.PixelFormat));
bmi->biCompression = GUINT32_FROM_LE (BI_RGB);
bmi->biSizeImage = GUINT32_FROM_LE (0); /* Many tools expect this may be set to zero for BI_RGB bitmaps */
bmi->biXPelsPerMeter = GULONG_FROM_LE ((int) (0.5f + ((gdip_get_display_dpi() * 3937) / 100)));
@@ -106,7 +141,7 @@ gdip_bitmap_fill_info_header (GpBitmap *bitmap, PBITMAPINFOHEADER bmi)
bmi->biWidth = bitmap->data.Width;
bmi->biHeight = bitmap->data.Height;
bmi->biPlanes = 1;
- bmi->biBitCount = 32; /*PIXEL_FORMAT_BPP (bitmap->image.pixFormat); */
+ bmi->biBitCount = gdip_get_pixel_format_bpp (bitmap->data.PixelFormat);
bmi->biCompression = BI_RGB;
bmi->biSizeImage = 0; /* Many tools expect this may be set to zero for BI_RGB bitmaps */
bmi->biXPelsPerMeter = (int) (0.5f + ((gdip_get_display_dpi() * 3937) / 100));
@@ -130,7 +165,36 @@ gdip_bitmap_save_bmp (const char *name, GpBitmap *bitmap)
fwrite (&bmfh, sizeof (bmfh), 1, fp);
gdip_bitmap_fill_info_header (bitmap, &bmi);
bmi.biHeight = -bmi.biHeight;
+
fwrite (&bmi, sizeof (bmi), 1, fp);
+
+ if (gdip_is_an_indexed_pixelformat (bitmap->data.PixelFormat)) {
+ int i;
+
+ int palette_entries = bitmap->image.palette->Count;
+ if (bitmap->data.PixelFormat == Format4bppIndexed)
+ palette_entries = 16;
+
+ for (i=0; i < palette_entries; i++)
+ {
+ unsigned char entry[4];
+
+ unsigned int packed = bitmap->image.palette->Entries[i];
+
+ /* Pixel format: 0xAARRGGBB
+ * Therefore, it can be split numerically :-)
+ * Output always expects little-endian pixels.
+ */
+
+ entry[0] = ( packed & 0xFF); /* B */
+ entry[1] = ((packed >> 8) & 0xFF); /* G */
+ entry[2] = ((packed >> 16) & 0xFF); /* R */
+ entry[3] = ((packed >> 24) & 0xFF); /* Alpha */
+
+ fwrite(entry, 4, 1, fp);
+ }
+ }
+
fwrite (bitmap->data.Scan0, bitmapLen, 1, fp);
fclose (fp);
}
@@ -151,29 +215,547 @@ gdip_load_bmp_image_from_stream_delegate (GetBytesDelegate getBytesFunc,
image, FALSE);
}
-#define palette_lookup(x) img->image.palette->Entries[(x)]
+GpStatus
+gdip_read_bmp_rle_8bit (void *pointer, byte *scan0, bool upsidedown, int stride, int scanWidth, int scanCount, bool useFile)
+{
+ byte code;
+ int bytes_read;
+
+ int col_offset = 0;
+ int row_offset = (upsidedown ? (scanCount - 1) * stride : 0);
+ int row_delta = (upsidedown ? -stride : +stride);
+ int rows_remaining = scanCount;
+ bool new_row = FALSE;
+
+ if (scanWidth > stride)
+ return InvalidParameter;
+
+ while ((rows_remaining > 0)
+ || ((row_offset == 0) && (col_offset < scanWidth))) {
+ bytes_read = gdip_read_bmp_data (pointer, &code, 1, useFile);
+
+ if (bytes_read < 1)
+ return GenericError; /* TODO?: Add an "unexpected end of file" error code */
+
+ if (code == 0) { /* RLE escape code */
+ bytes_read = gdip_read_bmp_data (pointer, &code, 1, useFile);
+
+ if (bytes_read < 1)
+ return GenericError; /* TODO?: Add an "unexpected end of file" error code */
+
+ switch (code)
+ {
+ case 0: /* skip remainder of scan */
+ {
+ if (new_row)
+ new_row = FALSE;
+ else {
+ row_offset += row_delta;
+ rows_remaining--;
+ col_offset = 0;
+ }
+ break;
+ }
+ case 1: /* skip remainder of image -- in other words, we're finished :-) */
+ {
+ return Ok;
+ }
+ case 2: /* jump forward (dx, dy) coordinates */
+ {
+ byte dx, dy;
+
+ bytes_read = gdip_read_bmp_data (pointer, &dx, 1, useFile);
+ bytes_read += gdip_read_bmp_data (pointer, &dy, 1, useFile);
+
+ if (bytes_read < 2)
+ return GenericError; /* TODO?: Add an "unexpected end of file" error code */
+
+ /* not really sure how to handle the case where the X delta goes
+ * past the end of the scan. in the interest of not crashing,
+ * let's wrap it back around.
+ */
+ col_offset = (col_offset + dx) % scanWidth;
+ row_offset -= dy * stride; /* BMPs go from bottom to top */
+
+ new_row = FALSE;
+ break;
+ }
+ default: /* an uncompressed section, 'code' pixels wide */
+ {
+ /* uncompressed sections must be an even number of bytes long,
+ * even if they are an odd number of *pixels* long.
+ */
+ bool pad_byte_present = ((code & 1) != 0);
+ int bytes_to_read = code;
+
+ /* wrap rows properly, even though they are inverted in memory */
+ while (bytes_to_read > 0) {
+ int bytes_to_read_this_scan = scanWidth - col_offset;
+
+ if (bytes_to_read_this_scan > bytes_to_read)
+ bytes_to_read_this_scan = bytes_to_read;
+
+ bytes_read = gdip_read_bmp_data (
+ pointer,
+ &scan0[row_offset + col_offset],
+ bytes_to_read_this_scan,
+ useFile);
+
+ if (bytes_read < bytes_to_read_this_scan)
+ return GenericError; /* TODO?: Add an "unexpected end of file" error code */
+
+ col_offset += bytes_read;
+ bytes_to_read -= bytes_read;
+
+ if (col_offset >= scanWidth)
+ {
+ col_offset = 0;
+ row_offset += row_delta;
+ rows_remaining--;
+
+ if (rows_remaining <= 0) /* more data than expected -- let's not make this a fatal error */
+ return Ok;
+
+ new_row = TRUE;
+ }
+ else
+ new_row = FALSE;
+ }
+
+ if (pad_byte_present) {
+ bytes_read = gdip_read_bmp_data(pointer, &code, 1, useFile);
+
+ if (bytes_read < 1)
+ return GenericError; /* TODO?: Add an "unexpected end of file" error code */
+ }
+
+ break;
+ }
+ }
+ }
+ else {
+ /* we have a run of length 'code'. the colour of the run is the next byte in the file. */
+ int run_length = code;
+ byte pixel_value;
+
+ bytes_read = gdip_read_bmp_data(pointer, &pixel_value, 1, useFile);
+
+ if (bytes_read < 1)
+ return GenericError; /* TODO?: Add an "unexpected end of file" error code */
+
+ while (run_length > 0) {
+ int bytes_to_run_this_scan = scanWidth - col_offset;
+
+ if (bytes_to_run_this_scan > run_length)
+ bytes_to_run_this_scan = run_length;
+
+ memset (scan0 + row_offset + col_offset, pixel_value, bytes_to_run_this_scan);
+
+ col_offset += bytes_to_run_this_scan;
+ run_length -= bytes_to_run_this_scan;
+
+ if (col_offset >= scanWidth)
+ {
+ col_offset = 0;
+ row_offset += row_delta;
+ rows_remaining--;
+
+ if (rows_remaining <= 0) /* more data than expected -- let's not make this a fatal error */
+ return Ok;
+
+ new_row = TRUE;
+ }
+ else
+ new_row = FALSE;
+ }
+ }
+ }
+
+ return Ok;
+}
+
+GpStatus
+gdip_read_bmp_rle_4bit (void *pointer, byte *scan0, bool upsidedown, int stride, int scanWidth, int scanCount, bool useFile)
+{
+ byte code;
+ int bytes_read;
+
+ int col_offset = 0;
+ int row_offset = (upsidedown ? (scanCount - 1) * stride : 0);
+ int row_delta = (upsidedown ? -stride : +stride);
+ int rows_remaining = scanCount;
+ bool new_row = FALSE;
+
+ if ((scanWidth & 1) != 0)
+ scanWidth++;
+
+ if (scanWidth > stride * 2)
+ return InvalidParameter;
+
+ while (rows_remaining > 0) {
+ bytes_read = gdip_read_bmp_data (pointer, &code, 1, useFile);
+
+ if (bytes_read < 1)
+ return GenericError; /* TODO?: Add an "unexpected end of file" error code */
+
+ if (code == 0) { /* RLE escape code */
+ bytes_read = gdip_read_bmp_data (pointer, &code, 1, useFile);
+
+ if (bytes_read < 1)
+ return GenericError; /* TODO?: Add an "unexpected end of file" error code */
+
+ switch (code)
+ {
+ case 0: /* skip remainder of scan */
+ {
+ if (new_row)
+ new_row = FALSE;
+ else {
+ row_offset += row_delta;
+ rows_remaining--;
+ col_offset = 0;
+ }
+ break;
+ }
+ case 1: /* skip remainder of image -- in other words, we're finished :-) */
+ {
+ return Ok;
+ }
+ case 2: /* jump forward (dx, dy) coordinates */
+ {
+ byte dx, dy;
+
+ bytes_read = gdip_read_bmp_data (pointer, &dx, 1, useFile);
+ bytes_read += gdip_read_bmp_data (pointer, &dy, 1, useFile);
+
+ if (bytes_read < 2)
+ return GenericError; /* TODO?: Add an "unexpected end of file" error code */
+
+ /* not really sure how to handle the case where the X delta goes
+ * past the end of the scan. in the interest of not crashing,
+ * let's wrap it back around.
+ */
+ col_offset = (col_offset + dx) % scanWidth;
+ row_offset -= dy * stride; /* BMPs go from bottom to top */
+
+ new_row = FALSE;
+
+ break;
+ }
+ default: /* an uncompressed section, 'code' pixels wide */
+ {
+ int pixels_to_read = code;
+ int bytes_of_data = (pixels_to_read + 1) / 2;
+
+ /* uncompressed sections must be an even number of bytes long,
+ * even if they are an odd number of *pixels* long.
+ */
+ bool pad_byte_present = ((bytes_of_data & 1) != 0);
+
+ int bytes_to_read = pixels_to_read / 2; /* leave off the last pixel for now */
+
+ /* wrap rows properly, even though they are inverted in memory */
+ while (bytes_to_read > 0) {
+ if ((scanWidth - col_offset) == 1) {
+ /* special case: a pair of pixels is split across two rows. */
+ byte pixels, same_row_pixel, next_row_pixel;
+
+ bytes_read = gdip_read_bmp_data (pointer, &pixels, 1, useFile);
+
+ if (bytes_read < 1)
+ return GenericError; /* TODO?: Add an "unexpected end of file" error code */
+
+ same_row_pixel = (pixels >> 4) & 0x0F;
+ next_row_pixel = pixels & 0x0F;
+
+ if ((col_offset & 1) != 0) {
+ byte old_pixel = 0xF0 & scan0[row_offset + col_offset / 2];
+ scan0[row_offset + col_offset / 2] = (old_pixel & 0xF0) | same_row_pixel;
+ }
+ else
+ scan0[row_offset + col_offset / 2] = same_row_pixel << 4;
+
+ col_offset = 1;
+ row_offset += row_delta;
+ rows_remaining--;
+
+ if (rows_remaining <= 0) /* more data than expected -- let's not make this a fatal error */
+ return Ok;
+
+ scan0[row_offset] = next_row_pixel << 4;
+
+ new_row = FALSE;
+ }
+ else if ((col_offset & 1) == 0) {
+ /* alignment is good; we can read pairs of pixels as bytes.
+ * if there are an odd number of pixels in a scan, though,
+ * then the last pixel will need to be special-cased. also,
+ * if the scan width is odd, then a byte will be split
+ * across a row ending. I don't know if this is in the spec,
+ * but it is the most resistant to crashing.
+ */
+ int bytes_to_read_this_scan = (scanWidth - col_offset) / 2;
+
+ if (bytes_to_read_this_scan > bytes_to_read)
+ bytes_to_read_this_scan = bytes_to_read;
+
+ bytes_read = gdip_read_bmp_data (
+ pointer,
+ &scan0[row_offset + col_offset / 2],
+ bytes_to_read_this_scan,
+ useFile);
+
+ if (bytes_read < bytes_to_read_this_scan)
+ return GenericError; /* TODO?: Add an "unexpected end of file" error code */
+
+ col_offset += bytes_read * 2;
+ bytes_to_read -= bytes_read;
+
+ new_row = FALSE;
+ }
+ else {
+ /* bad alignment; nybble-swapping will be required */
+ byte last_high_nybble = 0xF0 & scan0[row_offset + col_offset / 2];
+
+ int bytes_to_read_this_scan = (scanWidth - col_offset) / 2;
+
+ if (bytes_to_read_this_scan > bytes_to_read)
+ bytes_to_read_this_scan = bytes_to_read;
+
+ while (bytes_to_read_this_scan >= 0) {
+ byte pixels;
+
+ bytes_read = gdip_read_bmp_data (pointer, &pixels, 1, useFile);
+
+ if (bytes_read < 1)
+ return GenericError; /* TODO?: Add an "unexpected end of file" error code */
+
+ scan0[row_offset + col_offset / 2] = last_high_nybble | (pixels >> 4);
+
+ last_high_nybble = (pixels << 4) & 0xF0;
+
+ col_offset += 2; /* two pixels processed */
+ bytes_to_read_this_scan--;
+ }
+
+ new_row = FALSE;
+ }
+
+ if (col_offset >= scanWidth) {
+ col_offset = 0;
+ row_offset += row_delta;
+ rows_remaining--;
+
+ if (rows_remaining <= 0) /* more data than expected -- let's not make this a fatal error */
+ return Ok;
+
+ new_row = TRUE;
+ }
+ }
+
+ if ((pixels_to_read & 1) != 0) {
+ /* half of a byte remains to be inserted into the correct nybble */
+ byte pixel;
+
+ bytes_read = gdip_read_bmp_data (pointer, &pixel, 1, useFile);
+
+ if (bytes_read < 1)
+ return GenericError; /* TODO?: Add an "unexpected end of file" error code */
+
+ pixel >>= 4; /* the last pixel is in the high nybble */
+
+ if ((col_offset & 1) != 0) {
+ byte old_pixel = 0xF0 & scan0[row_offset + col_offset / 2];
+ scan0[row_offset + col_offset / 2] = (old_pixel & 0xF0) | pixel;
+ }
+ else
+ scan0[row_offset + col_offset / 2] = pixel << 4;
+
+ col_offset++;
+
+ if (col_offset >= scanWidth) {
+ col_offset = 0;
+ row_offset += row_delta;
+ rows_remaining--;
+
+ if (rows_remaining <= 0) /* more data than expected -- let's not make this a fatal error */
+ return Ok;
+
+ new_row = TRUE;
+ }
+ else
+ new_row = FALSE;
+ }
+
+ if (pad_byte_present) {
+ bytes_read = gdip_read_bmp_data(pointer, &code, 1, useFile);
+
+ if (bytes_read < 1)
+ return GenericError; /* TODO?: Add an "unexpected end of file" error code */
+ }
+
+ break;
+ }
+ }
+ }
+ else {
+ /* we have a run of length 'code'. the colour of the run is the next byte in the file.
+ * something weird is happening here in 4-bit land, though; a byte stores two pixels.
+ * what happens is rather odd: the run is actually of two alternating colours (which
+ * may, of course, be the same, but are not required to be). we need to make sure that
+ * the colours end up in the right nybbles of the output bytes.
+ */
+ int run_pixels = code;
+ int run_length = run_pixels / 2;
+
+ byte pixel_values;
+ byte inverted_pixel_values;
+
+ bytes_read = gdip_read_bmp_data(pointer, &pixel_values, 1, useFile);
+
+ if (bytes_read < 1)
+ return GenericError; /* TODO?: Add an "unexpected end of file" error code */
+
+ inverted_pixel_values = ((pixel_values & 0x0F) << 4) | ((pixel_values & 0xF0) >> 4);
+
+ if ((col_offset & 1) != 0) {
+ byte temp = inverted_pixel_values;
+ inverted_pixel_values = pixel_values;
+ pixel_values = temp;
+ }
+
+ while (run_length > 0) {
+ if ((scanWidth - col_offset) == 1) {
+ /* special case: a pair of pixels is split across two rows. */
+ byte same_row_pixel = (pixel_values >> 4) & 0x0F;
+ byte next_row_pixel = pixel_values & 0x0F;
+
+ if ((col_offset & 1) != 0) {
+ byte old_pixel = 0xF0 & scan0[row_offset + col_offset / 2];
+ scan0[row_offset + col_offset / 2] = (old_pixel & 0xF0) | same_row_pixel;
+ }
+ else
+ scan0[row_offset + col_offset / 2] = same_row_pixel << 4;
+
+ col_offset = 1;
+ row_offset += row_delta;
+ rows_remaining--;
+
+ if (rows_remaining <= 0) /* more data than expected -- let's not make this a fatal error */
+ return Ok;
+
+ scan0[row_offset] = next_row_pixel << 4;
+
+ new_row = FALSE;
+
+ if ((scanWidth & 1) != 0) {
+ /* if the width of the scan is odd, then the nybbles swap
+ * places each time they cross from one row to the next
+ */
+ byte temp = inverted_pixel_values;
+ inverted_pixel_values = pixel_values;
+ pixel_values = temp;
+ }
+ }
+ else {
+ int bytes_to_run_this_scan;
+
+ /* make sure we're byte-aligned; if we're not, we need to store a nybble first */
+ if ((col_offset & 1) != 0) {
+ byte old_pixel = 0xF0 & scan0[row_offset + col_offset / 2];
+ scan0[row_offset + col_offset / 2] = (old_pixel & 0xF0) | (pixel_values & 0x0F);
+
+ col_offset++;
+ }
+
+ bytes_to_run_this_scan = (scanWidth - col_offset) / 2;
+
+ if (bytes_to_run_this_scan > run_length)
+ bytes_to_run_this_scan = run_length;
+
+ memset (scan0 + row_offset + col_offset / 2, pixel_values, bytes_to_run_this_scan);
+
+ col_offset += bytes_to_run_this_scan * 2;
+ run_length -= bytes_to_run_this_scan;
+
+ if (col_offset >= scanWidth) {
+ col_offset = 0;
+ row_offset += row_delta;
+ rows_remaining--;
+
+ if (rows_remaining <= 0) /* more data than expected -- let's not make this a fatal error */
+ return Ok;
+
+ new_row = TRUE;
+
+ if ((scanWidth & 1) != 0) {
+ /* if the width of the scan is odd, then the nybbles swap
+ * places each time they cross from one row to the next
+ */
+ byte temp = inverted_pixel_values;
+ inverted_pixel_values = pixel_values;
+ pixel_values = temp;
+ }
+ }
+ else
+ new_row = FALSE;
+ }
+ }
+
+ if ((run_pixels & 1) != 0) {
+ /* half of a byte remains to be inserted into the correct nybble */
+ byte pixel = pixel_values >> 4; /* the last pixel is in the high nybble */
+
+ if ((col_offset & 1) != 0) {
+ byte old_pixel = 0xF0 & scan0[row_offset + col_offset / 2];
+ scan0[row_offset + col_offset / 2] = (old_pixel & 0xF0) | pixel;
+ }
+ else
+ scan0[row_offset + col_offset / 2] = pixel << 4;
+
+ col_offset++;
+
+ if (col_offset >= scanWidth) {
+ col_offset = 0;
+ row_offset += row_delta;
+ rows_remaining--;
+
+ if (rows_remaining <= 0) /* more data than expected -- let's not make this a fatal error */
+ return Ok;
+
+ new_row = TRUE;
+ }
+ else
+ new_row = FALSE;
+ }
+ }
+ }
+
+ return Ok;
+}
GpStatus
gdip_read_bmp_image_from_file_stream (void *pointer, GpImage **image, bool useFile)
{
BITMAPFILEHEADER bmfh;
- BITMAPINFOHEADER bmi;
- GpBitmap *img = NULL;
- guchar *pixels = NULL;
- guchar *linep = NULL;
- int i;
+ BITMAPINFOHEADER bmi;
+ GpBitmap *img = NULL;
+ guchar *pixels = NULL;
+ guchar *linep = NULL;
+ int i;
int j;
- int format;
+ PixelFormat format;
int colours;
- BOOL os2format = FALSE;
+ BOOL os2format = FALSE;
BOOL upsidedown = TRUE;
- int size;
+ int size;
int size_read;
byte *data_read;
int line;
int loop;
long pixel;
long index;
+ GpStatus status;
size = sizeof(bmfh);
data_read = (byte*) GdipAlloc(size);
@@ -224,10 +806,10 @@ gdip_read_bmp_image_from_file_stream (void *pointer, GpImage **image, bool useFi
}
bmi.biHeight = (data_read[3]<<24 | data_read[2]<<16 | data_read[1]<<8 | data_read[0]);
}
- else {
+ else {
if (bmi.biSize == BITMAPCOREHEADER_SIZE) {
/* Old OS/2 format. Width and Height fields are WORDs instead of DWORDS */
- memset (data_read, 0, size);
+ memset (data_read, 0, size);
size_read = gdip_read_bmp_data (pointer, data_read, size, useFile);
if (size_read < size) {
GdipFree(data_read);
@@ -235,13 +817,13 @@ gdip_read_bmp_image_from_file_stream (void *pointer, GpImage **image, bool useFi
}
bmi.biWidth = (data_read[1]<<8 | data_read[0]);
bmi.biHeight = (data_read[3]<<8 | data_read[2]);
- os2format = TRUE;
- }
- else {
+ os2format = TRUE;
+ }
+ else {
GdipFree(data_read);
- return UnknownImageFormat;
+ return UnknownImageFormat;
}
- }
+ }
memset (data_read, 0, size);
size_read = gdip_read_bmp_data (pointer, data_read, size, useFile);
@@ -260,16 +842,11 @@ gdip_read_bmp_image_from_file_stream (void *pointer, GpImage **image, bool useFi
}
bmi.biCompression = (data_read[3]<<24 | data_read[2]<<16 | data_read[1]<<8 | data_read[0]);
- if (bmi.biCompression == BI_RLE4 || bmi.biCompression == BI_RLE8) {
- GdipFree(data_read);
- return NotImplemented; /* We do not support RLE for now*/
+ if (bmi.biHeight < 0) { /* Negative height indicates that the bitmap is sideup*/
+ upsidedown = FALSE;
+ bmi.biHeight = -bmi.biHeight;
}
- if (bmi.biHeight < 0) { /* Negative height indicates that the bitmap is sideup*/
- upsidedown = FALSE;
- bmi.biHeight = -bmi.biHeight;
- }
-
memset (data_read, 0, size);
size_read = gdip_read_bmp_data (pointer, data_read, size, useFile);
if (size_read < size) {
@@ -312,238 +889,217 @@ gdip_read_bmp_image_from_file_stream (void *pointer, GpImage **image, bool useFi
bmi.biClrImportant = (data_read[3]<<24 | data_read[2]<<16 | data_read[1]<<8 | data_read[0]);
colours = (bmi.biClrUsed == 0 && bmi.biBitCount <= 8) ? (1 << bmi.biBitCount) : bmi.biClrUsed;
+
+ status = gdip_get_bmp_pixelformat (bmi.biBitCount, bmi.biCompression, &format);
+
+ if (status != Ok) /* bit count mismatch */
+ return status;
- format = gdip_get_pixelformat (bmi.biBitCount);
-
- img = gdip_bitmap_new ();
- img->image.type = imageBitmap;
- img->image.width = bmi.biWidth;
- img->image.height = bmi.biHeight;
+ img = gdip_bitmap_new ();
+ img->image.pixFormat = format;
+ img->image.type = imageBitmap;
+ img->image.width = bmi.biWidth;
+ img->image.height = bmi.biHeight;
- img->image.pixFormat = img->data.PixelFormat = Format32bppArgb;
- img->data.Width = img->image.width;
- img->data.Height = img->image.height;
+ img->data.PixelFormat = img->image.pixFormat;
+ img->data.Width = img->image.width;
+ img->data.Height = img->image.height;
- // We always assume 32 bit and translate into 32 bit from source format
- img->data.Stride = (32 * img->image.width) / 8;
- img->data.Stride = (img->data.Stride + 3) & ~3;
+ switch (img->data.PixelFormat)
+ {
+ case Format1bppIndexed: img->data.Stride = (img->image.width + 7) / 8; break;
+ case Format4bppIndexed: img->data.Stride = (img->image.width + 1) / 2; break;
+ case Format8bppIndexed: img->data.Stride = img->image.width; break;
+ default:
+ /* For other types, we assume 32 bit and translate into 32 bit from source format */
+ img->data.PixelFormat = Format32bppArgb;
+ img->data.Stride = img->image.width * 4;
+ break;
+ }
GdipFree(data_read);
-
- if (colours) {
- img->image.palette = GdipAlloc (sizeof(ColorPalette) + sizeof(ARGB) * colours);
- img->image.palette->Flags = 0;
- img->image.palette->Count = colours;
-
- /* Read optional colour table*/
- if (os2format) { /* RGBTRIPLE */
+
+ /* Ensure pixman_bits_t alignment */
+ img->data.Stride += (sizeof(pixman_bits_t) - 1);
+ img->data.Stride &= ~(sizeof(pixman_bits_t) - 1);
+
+ if (colours) {
+ int palette_entries = colours;
+ if (img->data.PixelFormat == Format4bppIndexed)
+ palette_entries = 256;
+
+ img->image.palette = g_malloc (sizeof(ColorPalette) + sizeof(ARGB) * palette_entries);
+ img->image.palette->Flags = 0;
+ img->image.palette->Count = palette_entries;
+
+ /* Read optional colour table*/
+ if (os2format) { /* RGBTRIPLE */
size = sizeof(byte)*3;
data_read = (byte*) GdipAlloc(size);
- for (i = 0; i < colours; i++) {
+ for (i = 0; i < colours; i++) {
memset(data_read, 0, size);
size_read = gdip_read_bmp_data (pointer, data_read, size, useFile);
if (size_read < size) {
GdipFree(data_read);
return InvalidParameter;
}
- img->image.palette->Entries[i] =
- (((data_read[0]&0xff)<<16) | // R
- ((data_read[1]&0xff)<<8) | // G
- (data_read[2]&0xff)); // B
- }
+
+ set_pixel_bgra (img->image.palette->Entries, i * 4,
+ (data_read[0] & 0xFF), /* B */
+ (data_read[1] & 0xFF), /* G */
+ (data_read[2] & 0xFF), /* R */
+ 0xFF); /* Alpha */
+ }
GdipFree(data_read);
- }
- else { /* RGBSquads */
+ }
+ else { /* RGBSquads */
size = sizeof(byte)*4;
data_read = (byte*) GdipAlloc(size);
- for (i = 0; i < colours; i++) {
- size_read = gdip_read_bmp_data (pointer, data_read, size, useFile);
+ for (i = 0; i < colours; i++) {
+ size_read = gdip_read_bmp_data (pointer, data_read, size, useFile);
if (size_read < size) {
+ GdipFree(data_read);
return InvalidParameter;
}
- img->image.palette->Entries[i] =
- (((data_read[0]&0xff)<<16) | // R
- ((data_read[1]&0xff)<<8) | // G
- ((data_read[2]&0xff)) | // B
- ((data_read[3]& 0xff)<<24)); // Alpha
- }
+ set_pixel_bgra (img->image.palette->Entries, i * 4,
+ (data_read[0] & 0xFF), /* B */
+ (data_read[1] & 0xFF), /* G */
+ (data_read[2] & 0xFF), /* R */
+ (data_read[3] & 0xFF)); /* Alpha */
+ }
GdipFree(data_read);
- }
- } else {
- img->image.palette = NULL;
- }
-
- pixels = GdipAlloc (img->data.Stride * img->data.Height);
-
- // Size contains the size of the lines on disk
- switch (bmi.biBitCount) {
- case 1: {
- size = ((img->image.width + 31) & ~31) / 8;
- loop = img->image.width / 8;
- // we assume 0s for 1 bit, only setting 1s
- memset(pixels, 0, img->data.Stride * img->data.Height);
- break;
- }
-
- case 4: {
- size = ((bmi.biBitCount * img->image.width + 31) & ~31) / 8;
- loop = ((img->image.width + 1) &~1) / 2;
- break;
- }
-
- case 8: {
- size = (((bmi.biBitCount * img->image.width) + 31) & ~31) / 8;
- loop = img->image.width;
- break;
- }
-
- case 24: {
- size = (((bmi.biBitCount * img->image.width) + 31) & ~31) / 8;
- loop = img->image.width * 3;
- break;
- }
-
- default: {
- size = (((bmi.biBitCount * img->image.width) + 31) & ~31) / 8;
- loop = size;
- break;
}
+ } else {
+ img->image.palette = NULL;
}
- data_read = (byte*) GdipAlloc(size);
-
- for (i = 0; i < img->data.Height; i++){
- if (upsidedown) {
- line = img->data.Height - i - 1;
- } else {
- line = i;
+ pixels = GdipAlloc (img->data.Stride * img->data.Height);
+
+ if ((bmi.biCompression == BI_RLE4)
+ || (bmi.biCompression == BI_RLE8)) {
+ switch (bmi.biCompression)
+ {
+ case BI_RLE4:
+ gdip_read_bmp_rle_4bit (pointer, pixels, upsidedown, img->data.Stride, img->data.Width, img->data.Height, useFile);
+ break;
+ case BI_RLE8:
+ gdip_read_bmp_rle_8bit (pointer, pixels, upsidedown, img->data.Stride, img->data.Width, img->data.Height, useFile);
+ break;
}
-
- size_read = gdip_read_bmp_data (pointer, data_read, size, useFile);
-
- if (size_read < size) {
- GdipFree(data_read);
- return InvalidParameter;
- }
-
- switch(bmi.biBitCount) {
+ }
+ else {
+ // Size contains the size of the lines on disk
+ switch (bmi.biBitCount) {
case 1: {
- int c;
- int bit;
-
- for (c = 0; c < loop; c++) {
- for (bit = 0; bit < 8; bit++) {
- index = (line * img->data.Stride) + (c*8 + bit) * 4;
-
- if (((data_read[c] << bit) & 0x80) != 0) {
- set_pixel_bgra(pixels, index, 0xff, 0xff, 0xff, 0xff);
- } else {
- set_pixel_bgra(pixels, index, 0x00, 0x00, 0x00, 0xff);
- }
- }
- }
-
- for (bit = 0; bit < img->image.width % 8; bit++) {
- index = (line * img->data.Stride) + (c*8 + bit) * 4;
-
- if (((data_read[c] << bit) & 0x80) != 0) {
- set_pixel_bgra(pixels, index, 0xff, 0xff, 0xff, 0xff);
- } else {
- set_pixel_bgra(pixels, index, 0x00, 0x00, 0x00, 0xff);
- }
- }
- continue;
+ size = ((img->image.width + 31) & ~31) / 8;
+ loop = img->image.width / 8;
+ // we assume 0s for 1 bit, only setting 1s
+ memset(pixels, 0, img->data.Stride * img->data.Height);
+ break;
}
case 4: {
- int c;
-
- for (c = 0; c < loop; c++) {
- pixel = palette_lookup((data_read[c] & 0xf0) >> 4);
+ size = ((bmi.biBitCount * img->image.width + 31) & ~31) / 8;
+ loop = ((img->image.width + 1) &~1) / 2;
+ break;
+ }
- index = (line * img->data.Stride) + c*8;
+ case 8: {
+ size = (((bmi.biBitCount * img->image.width) + 31) & ~31) / 8;
+ loop = img->image.width;
+ break;
+ }
- set_pixel_bgra(pixels, index, (pixel & 0xff0000) >> 16, (pixel & 0xff00) >> 8, pixel & 0xff, 0xff);
+ default: {
+ size = (((bmi.biBitCount * img->image.width) + 31) & ~31) / 8;
+ loop = size;
+ break;
+ }
+ }
- pixel = palette_lookup(data_read[c] & 0xf);
+ data_read = (byte*) GdipAlloc(size);
- set_pixel_bgra(pixels, index+4, (pixel & 0xff0000) >> 16, (pixel & 0xff00) >> 8, pixel & 0xff, 0xff);
- }
- continue;
+ //printf("Reading image data, upsidedown:%d, size: %d, stride:%d, loop:%d\n", upsidedown, size, img->data.Stride, loop);
+ for (i = 0; i < img->data.Height; i++){
+ if (upsidedown) {
+ line = img->data.Height - i - 1;
+ } else {
+ line = i;
}
- case 8: {
- int c;
+ size_read = gdip_read_bmp_data (pointer, data_read, size, useFile);
- for (c = 0; c < loop; c++) {
- pixel = palette_lookup(data_read[c]);
+ if (size_read < size) {
+ GdipFree(data_read);
+ return InvalidParameter;
+ }
- index = (line * img->data.Stride) + c*4;
+ switch(bmi.biBitCount) {
+ case 1:
+ case 4:
+ case 8: memcpy(pixels + line * img->data.Stride,
+ data_read, size);
+ continue;
- set_pixel_bgra(pixels, index, (pixel & 0xff0000) >> 16, (pixel & 0xff00) >> 8, pixel & 0xff, 0xff);
- }
- continue;
- }
+ case 24: {
+ int src;
+ int dest;
- case 24: {
- int src;
- int dest;
+ src = 0;
+ dest = 0;
- src = 0;
- dest = 0;
+ while (src < loop) {
+ index = (line * img->data.Stride);
+ set_pixel_bgra(pixels, index+dest, data_read[src+0], data_read[src+1], data_read[src+2], 0xff);
+ dest += 4;
- while (src < loop) {
- index = (line * img->data.Stride);
- set_pixel_bgra(pixels, index+dest, data_read[src+0], data_read[src+1], data_read[src+2], 0xff);
- dest += 4;
- src += 3;
+ src += 3;
+ }
+ continue;
}
- continue;
- }
- case 32: {
- int src;
- int dest;
+ case 32: {
+ int src;
+ int dest;
- src = 0;
- dest = 0;
+ src = 0;
+ dest = 0;
- while (src < loop) {
- index = (line * img->data.Stride);
- set_pixel_bgra(pixels, index+dest, data_read[src+0], data_read[src+1], data_read[src+2], data_read[src+3]);
- dest += 4;
- src += 4;
+ while (src < loop) {
+ index = (line * img->data.Stride);
+ set_pixel_bgra(pixels, index+dest, data_read[src+0], data_read[src+1], data_read[src+2], data_read[src+3]);
+ dest += 4;
+ src += 4;
+ }
+ continue;
}
- continue;
}
}
- }
-
- GdipFree(data_read);
- if (img->image.palette) {
- GdipFree(img->image.palette);
- img->image.palette = NULL;
+ GdipFree(data_read);
}
img->data.Scan0 = pixels;
- img->data.Reserved = GBD_OWN_SCAN0;
- img->image.surface = cairo_surface_create_for_image ((char *)pixels,
- img->cairo_format,
- img->image.width,
- img->image.height,
- img->data.Stride);
- img->image.imageFlags =
- ImageFlagsReadOnly |
- ImageFlagsHasRealPixelSize |
- ImageFlagsColorSpaceRGB;
- img->image.horizontalResolution = 0;
- img->image.verticalResolution = 0;
- img->image.propItems = NULL;
-
- *image = (GpImage *) img;
- return Ok;
+ img->data.Reserved = GBD_OWN_SCAN0;
+ if (!gdip_is_an_indexed_pixelformat (img->data.PixelFormat)) {
+ img->image.surface = cairo_surface_create_for_image ((char *)pixels,
+ img->cairo_format,
+ img->image.width,
+ img->image.height,
+ img->data.Stride);
+ }
+
+ img->image.imageFlags =
+ ImageFlagsReadOnly |
+ ImageFlagsHasRealPixelSize |
+ ImageFlagsColorSpaceRGB;
+ img->image.horizontalResolution = 0;
+ img->image.verticalResolution = 0;
+ img->image.propItems = NULL;
+
+ *image = (GpImage *) img;
+ return Ok;
}
int
@@ -560,10 +1116,10 @@ gdip_read_bmp_data (void *pointer, byte *data, int size, bool useFile)
int total;
total = 0;
-
+
do {
got = ((GetBytesDelegate)(pointer))(data + total, size - total, 0);
- if (got < 1) { // 0 = end of stream, -1 = error
+ if (got < 1) { // 0 = end of stream, -1 = error
return total;
}
total += got;
@@ -607,52 +1163,44 @@ gdip_save_bmp_image_to_file_stream (void *pointer,
bmfh.bfReserved1 = bmfh.bfReserved2 = GUINT16_FROM_LE (0);
bmfh.bfType = GUINT16_FROM_LE (BFT_BITMAP);
bmfh.bfOffBits = GUINT32_FROM_LE (14 + 40 + colours * 4);
- bmfh.bfSize = GUINT32_FROM_LE (bmfh.bfOffBits + bitmapLen);
+ bmfh.bfSize = GUINT32_FROM_LE (bmfh.bfOffBits + bitmapLen);
#else
- bmfh.bfReserved1 = bmfh.bfReserved2 = 0;
- bmfh.bfType = BFT_BITMAP;
- bmfh.bfOffBits = (14 + 40 + colours * 4);
- bmfh.bfSize = (bmfh.bfOffBits + bitmapLen);
-
+ bmfh.bfReserved1 = bmfh.bfReserved2 = 0;
+ bmfh.bfType = BFT_BITMAP;
+ bmfh.bfOffBits = (14 + 40 + colours * 4);