[Mono-winforms-list] libgdiplus/System.Drawing patch: native support for indexed Bitmaps

Jonathan Gilbert 2a5gjx302@sneakemail.com
Sat, 05 Mar 2005 02:12:37 -0500


--=====================_1110024757==_
Content-Type: text/plain; charset="us-ascii"

Please find attached the following files:

* libgdiplus.diff (patch -p0 in ./libgdiplus)
* System.Drawing.diff (patch -p0 in ./mcs/class/System.Drawing)
* lockbits.tgz (tar zvfx in ./)

After several days of haxing and a hard drive crashing halfway in, causing
me to lose about a day's work, I have completed an initial draft of a patch
which adds support for 1-, 4- and 8-bit bitmaps natively within libgdiplus.
As these are no longer upsampled to 32-bpp RGB at load/create time, palette
support is more important as well, and the patch adds a little bit to
System.Drawing as well to support this.

This sort of change isn't something that can be done a little bit at a
time; everything must be changed at once to recognize the new assumptions
based on pixel format, or the majority of code will not work properly. As a
result, this is a rather large patch which introduces a number of new
methods. To avoid excessive special cases, a few existing methods were
rewritten from scratch.

Here are the ChangeLog entries for the patch:

libgdiplus changes:
-------------------
* bitmap.c: Added indexed pixel formats to
  gdip_is_a_supported_pixelformat().
* bitmap.c: Added gdip_is_an_indexed_pixelformat(). Added its
  prototype to gdip.h; not sure if this is the correct place.
* bitmap.c: Added support for PixelFormat.Format4bppIndexed to
  GdipCreateBitmapFromScan0 (maps to CAIRO_FORMAT_A8, like
  PixelFormat.Format8bppIndexed). Palettes will be created
  as needed. Bitmaps with a 4bpp indexed pixel format will be
  given 256-entry ColorPalette structures, as the cairo_format
  indicates 8-bit data.
* bitmap.c: GdipCreateBitmapFromScan0 now computes stride for
  indexed pixel formats differently, and does not convert
  indexed pixel formats to 32-bit RGB. It also makes images with
  32-bit pixel formats initially black instead of initially
  transparent.
* bitmap.c: Added support for indexed images to
  gdip_bitmap_clone_data_rect().
* bitmap.c: Rewrote GdipBitmapLockBits () to be maintainable
  while supporting indexed pixel formats. Added
  gdip_can_window_without_copy (), gdip_is_a_32bit_pixelformat (),
  gdip_make_alpha_opaque () and gdip_is_an_alpha_pixelformat ().
* bitmap.c: Moved gdip_from_ARGB_to_RGB () and
  gdip_from_RGB_to_ARGB () above gdip_bitmap_change_rect_pixel_format (),
  so that gdip_bitmap_change_rect_pixel_format () can call them.
* bitmap.c: Added struct StreamingState and methods
  gdip_init_pixel_stream (), gdip_pixel_stream_has_next (),
  gdip_pixel_stream_get_next () and gdip_pixel_stream_set_next () to
  work with Bitmaps of various formats in an abstract way.
  gdip_pixel_stream_get_next () returns and
  gdip_pixel_stream_set_next () accepts 32-bit ARGB values for 15,
  16, 24, 32, 48 and 64 bit formats and palette indices for 1, 4 and
  8 bit formats. gdip_pixel_stream_get_next () and
  gdip_pixel_stream_set_next () cannot be mixed; call either one or
  the other on a given stream, but never both.
* bitmap.c: Rewrote gdip_bitmap_change_rect_pixel_format to use
  pixel streams, which adds support for indexed pixel formats and
  increases maintainability.
* bitmap.c: Added check to GdipBitmapSetPixel ensuring that
  the bitmap is not indexed (despite what MSDN says, in
  practice Microsoft's implementation rejects SetPixel on
  indexed Bitmaps).
* bitmap.c: Added gdip_convert_indexed_to_rgb (), which will
  be used by the functions implementing DrawImage, since
  gdip_bitmap_ensure_surface () is not compatible with indexed
  image data.
* image.c: Added support for indexed pixel formats in
  GdipDrawImageRect and GdipDrawImageRectRect. The support is
  for the image being drawn to be indexed, not the image
  being drawn on. :-)
* image.c: Implemented GdipGetImagePalette (),
  GdipSetImagePalette () and GdipGetImagePaletteSize ().
* bmpcodec.c: Corrected gdip_bitmap_fill_info_header ()'s
  interpretation of pixel bit widths.
* bmpcodec.c: Added include directive for gdipImage.h, for
  utility function gdip_get_pixel_format_bpp ().
* bmpcodec.c: Updated gdip_bitmap_save_bmp () to save the
  palette for indexed bitmaps.
* bmpcodec.c: Updated gdip_read_bmp_image_from_file_stream ()
  to maintain 1, 4 and 8 bpp indexed data instead of converting
  it on-the-fly to 32 bpp RGB data.
* bmpcodec.c: Updated gdip_get_pixelformat () to translate
  a bit width of 1 into Format1bppIndexed.
* gifcodec.c: Updated gdip_save_gif_image () to reject
  indexed images as NotImplemented, for now at least.
* jpegcodec.c: Updated gdip_save_jpeg_image_internal () to
  reject 8bpp indexed images that are not grayscale (the
  previous behaviour was to assume all 8bpp indexed images
  are grayscale).
* tiffcodec.c: Updated gdip_save_tiff_image () to reject
  indexed images as NotImplemented, for now at least.
* pngcodec.c: Updated gdip_load_png_image_from_file_or_stream ()
  to handle 1-, 4- and 8-bit PNG files separately, without
  upsampling the data to 32-bit RBG.
* gdip.h: Fixed the set_pixel_bgra () macro; a lack of
  parentheses and no cast down to 'unsigned char' was making
  expressions like set_pixel_bgra (&array[i], ...) fail to
  compile.

System.Drawing changes:
-----------------------
* ColorPalette.cs: implemented the empty setFromGDIPalette
  method.

Things to note about these patches:

1. Before the patch, the JPEG codec will assume any 8-bit data is
grayscale; after the patch, the JPEG codec actually checks and will return
NotImplemented for non-grayscale 8-bit data. This doesn't really make any
difference at all, because before the patch, it isn't even possible to
create 8-bit GpBitmaps.

2. Before the patch, all Bitmaps were stored as 32-bit RGB internally.
LockBits() and UnlockBits() were haxed to make this transparent with
respect to the 24-bit RGB pixel format (which was officially supported),
but all of the image codecs could simply assume that the data was 32-bit
RGB for the purposes of interfacing with underlying libraries and saving.
This is now true only for non-indexed modes. The BMP and PNG codecs have
been made to support indexed data, but the JPEG, GIF and TIFF codecs will
no longer be able to save GpBitmaps with Format1bppIndexed,
Format4bppIndexed and Format8bppIndexed. This will, I suspect, be fixed in
the future. (There is a nice convenience function
gdip_convert_indexed_to_rgb () that I added to enable 1-, 4- and 8-bit
images to be passed into DrawImage; it translates the indexed image into a
32-bit RGB image for cairo. This function could be used to make a temporary
32-bit version of palettized images, at least for the JPEG codec. It
probably doesn't make as much sense to use it for GIF and TIFF saving :-)

3. A new facility has been added to bitmap.c called "pixel streams".
Basically, it's a generic mechanism for either getting or setting all of
the pixels in a given rectangle of a bitmap. Each call to
gdip_pixel_stream_get_next () returns exactly 1 pixel, and each call to
gdip_pixel_stream_set_next () sets exactly 1 pixel. The functions handle
things like pixels not falling on byte boundaries automatically. Note,
though, that the two functions can NOT be mixed for a single stream.

4. As mentioned earlier, to avoid adding a variety of special cases, which
decreases maintainability, the functions GdipBitmapLockBits and
GdipBitmapUnlockBits were rewritten, mostly from scratch. They now have
more sanity checks than before, and, of course, they support indexed
images. Checking whether the locked area can be merely a "window" directly
onto the image's bits is factored off into gdip_can_window_without_copy (),
and gdip_bitmap_change_rect_pixel_format () has been rewritten to use pixel
streams. This reduces all the complexity of handling different pixel
formats to:

        /* Move the data. */
        while (gdip_pixel_stream_has_next (&srcStream))
                gdip_pixel_stream_set_next (&destStream,
                        gdip_pixel_stream_get_next (&srcStream));

5. Cairo has native formats CAIRO_FORMAT_A1 and CAIRO_FORMAT_A8, but it
does not have a CAIRO_FORMAT_A4 to which 4-bit graphics would most
naturally map. So, as I was not really sure whether Cairo functions ever
access the palette, I decided to map 4-bit images onto CAIRO_FORMAT_A8. The
underlying data (which never gets passed to Cairo anyway) is still stored
using 4 bits, but the palette is extended to 256 entries, so that any
assumptions made as a result of having CAIRO_FORMAT_A8 will be satisfied by
the palette. Whenever the palette is passed to System.Drawing or written to
disk in the BMP and PNG codecs, it is truncated back to 16 entries; the
remaining "dummy" entries exist only as a preventative measure. I'm not
sure if this is necessary, but it adds less than 20 lines of code overall,
and it makes me feel safer inside :-)

6. In order to be sure that my new LockBits/UnlockBits functions would
behave as expected, I made a test case that rigorously stresses locking
various rectangles of 32x32 bitmaps at 1bpp, 4bpp and 8bpp. I have ensured
that the test works on both Microsoft's runtime and on mono with these
patches. The test case is added as a subdirectory 'lockbits' of the
'winforms' subdir, which seemed to contain a bunch of tests. This test case
is contained in lockbits.tgz. Note, also, that winforms/endian works
perfectly with this patch applied.

I welcome any and all comments on this set of patches. Please CC replies to
4lw0e0402@sneakemail.com as I do not trust the e-mail box to which my list
subscription is sent.

Thanks,

Jonathan Gilbert

--=====================_1110024757==_
Content-Type: application/octet-stream; name="lockbits.tgz"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="lockbits.tgz"

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

--=====================_1110024757==_
Content-Type: text/plain; charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment; filename="System.Drawing.diff"

Index: System.Drawing.Imaging/ColorPalette.cs
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- System.Drawing.Imaging/ColorPalette.cs	(revision 41384)
+++ System.Drawing.Imaging/ColorPalette.cs	(working copy)
@@ -112,7 +112,16 @@

 		internal void setFromGDIPalette(IntPtr palette)
 		{
-
+			GdiColorPalette header =3D (GdiColorPalette)Marshal.PtrToStructure (pal=
ette, typeof(GdiColorPalette));
+
+			int[] values =3D new int[header.Count];
+
+			Marshal.Copy((IntPtr) (palette.ToInt32() + Marshal.SizeOf (palette)), v=
alues, 0, values.Length);
+
+			this.flags =3D header.Flags;
+			this.entries =3D new Color[values.Length];
+			for (int i=3D0; i < values.Length; i++)
+				this.entries[i] =3D Color.FromArgb(values[i]);
 		}
 	}
 }
Index: System.Drawing.Imaging/ChangeLog
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- System.Drawing.Imaging/ChangeLog	(revision 41384)
+++ System.Drawing.Imaging/ChangeLog	(working copy)
@@ -1,5 +1,10 @@
-2004-02-25 Jordi Mas i Hernadez <jordi@ximian.com>
+2005-03-02 Jonathan Gilbert  <................>
+
+	* ColorPalette.cs: implemented the empty setFromGDIPalette
+	  method.

+2005-02-25 Jordi Mas i Hernadez <jordi@ximian.com>
+
 	* ColorMatrix.cs: rewritten to be able to marshall it properly

 2004-12-27  Zoltan Varga  <vargaz@freemail.hu>
Index: System.Drawing/Bitmap.cs
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- System.Drawing/Bitmap.cs	(revision 41384)
+++ System.Drawing/Bitmap.cs	(working copy)
@@ -54,6 +54,7 @@
 		internal Bitmap (IntPtr ptr)
 		{
 			nativeObject =3D ptr;
+			retrieveGDIPalette();
 		}

 		public Bitmap (int width, int height) : this (width, height, PixelFormat=
.Format32bppArgb)
@@ -66,7 +67,8 @@
 			IntPtr bmp;
 			Status s =3D GDIPlus.GdipCreateBitmapFromGraphics (width, height, g.nat=
iveObject, out bmp);
 			GDIPlus.CheckStatus (s);
-			nativeObject =3D (IntPtr)bmp;
+			nativeObject =3D (IntPtr)bmp;
+			retrieveGDIPalette();
 		}

 		public Bitmap (int width, int height, PixelFormat format)
@@ -75,7 +77,7 @@
 			Status s =3D GDIPlus.GdipCreateBitmapFromScan0 (width, height, 0, forma=
t, IntPtr.Zero, out bmp);
 			GDIPlus.CheckStatus (s);
 			nativeObject =3D (IntPtr) bmp;
-
+			retrieveGDIPalette();
 		}

 		public Bitmap (Image original) : this (original.Width, original.Height, =
PixelFormat.Format32bppArgb)
@@ -105,13 +107,14 @@

 		internal Bitmap (int width, int height, PixelFormat pixel, IntPtr bmp)
 		{
-			nativeObject =3D (IntPtr)bmp;
+			nativeObject =3D (IntPtr)bmp;
+			retrieveGDIPalette();
 		}

 		internal Bitmap (float width, float height, PixelFormat pixel, IntPtr bm=
p)
 		{
-			nativeObject =3D (IntPtr)bmp;
-
+			nativeObject =3D (IntPtr)bmp;
+			retrieveGDIPalette();
 		}

 		internal void BitmapFromImage(Image original, Size newSize){
@@ -127,7 +130,7 @@
 				Status s =3D GDIPlus.GdipCloneBitmapAreaI (0, 0, newSize.Width, newSiz=
e.Height, bmpOriginal.PixelFormat, bmpOriginal.nativeObject, out bmp);
 				GDIPlus.CheckStatus (s);
 				nativeObject =3D (IntPtr) bmp;
-
+				retrieveGDIPalette();
 			}
 			else {
 				throw new NotImplementedException ();
@@ -139,9 +142,42 @@
 			IntPtr imagePtr;
 			Status st =3D GDIPlus.GdipLoadImageFromFile (filename, out imagePtr);
 			GDIPlus.CheckStatus (st);
-			nativeObject =3D imagePtr;
+			nativeObject =3D imagePtr;
+			retrieveGDIPalette();
 		}

+		bool IsIndexedPixelFormat(PixelFormat pixfmt)
+		{
+			return ((pixfmt & PixelFormat.Indexed) !=3D 0);
+		}
+
+		void retrieveGDIPalette()
+		{
+			if (!IsIndexedPixelFormat (PixelFormat))
+				return;
+
+			Status st;
+
+			int bytes;
+
+			st =3D GDIPlus.GdipGetImagePaletteSize (nativeObject, out bytes);
+			GDIPlus.CheckStatus (st);
+
+			IntPtr palette_data =3D Marshal.AllocHGlobal (bytes);
+
+			try
+			{
+				st =3D GDIPlus.GdipGetImagePalette (nativeObject, palette_data, bytes);
+				GDIPlus.CheckStatus (st);
+
+				Palette.setFromGDIPalette(palette_data);
+			}
+			finally
+			{
+				Marshal.FreeHGlobal(palette_data);
+			}
+		}
+
 		public Bitmap (Stream stream, bool useIcm)
 		{
 			InitFromStream (stream);
@@ -177,7 +213,8 @@

 			Status status =3D GDIPlus.GdipCreateBitmapFromScan0 (width, height, str=
ide, format, scan0, out bmp);
 			GDIPlus.CheckStatus (status);
-			nativeObject =3D (IntPtr) bmp;
+			nativeObject =3D (IntPtr) bmp;
+			retrieveGDIPalette();
 		}

 		private Bitmap (SerializationInfo info, StreamingContext context)
Index: System.Drawing/gdipFunctions.cs
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- System.Drawing/gdipFunctions.cs	(revision 41384)
+++ System.Drawing/gdipFunctions.cs	(working copy)
@@ -935,7 +935,7 @@
 		internal static extern Status GdipGetImagePaletteSize ( IntPtr image, ou=
t int size );

 		[DllImport("gdiplus.dll")]
-		internal static extern Status GdipGetImagePalette (IntPtr image, out Int=
Ptr palette, int size);
+		internal static extern Status GdipGetImagePalette (IntPtr image, IntPtr =
palette, int size);

 		[DllImport("gdiplus.dll")]
 		internal static extern Status GdipSetImagePalette (IntPtr image, IntPtr =
palette);
Index: System.Drawing/ChangeLog
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- System.Drawing/ChangeLog	(revision 41384)
+++ System.Drawing/ChangeLog	(working copy)
@@ -1,3 +1,19 @@
+2050-03-02  Jonathan Gilbert  <................>
+
+	* Bitmap.cs: Added retrieveGDIPalette () and calls to it. The
+	  function checks that the image is indexed, and if it is,
+	  uses GDI+ functions to retrieve the palette data and then
+	  load it using ColorPalette.setFromGDIPalette.
+	* Bitmap.cs: Added IsIndexedPixelFormat () in the same vein as
+	  Image::IsAlphaPixelFormat and Image::IsCanonicalPixelFormat.
+	  It would really fit better alongside its "sibling" functions,
+	  but such a function is not listed in MSDN nor given in
+	  Microsoft's implementation, so I have made it a private
+	  function within the class that uses it.
+	* gdipFunctions.cs: Corrected the parameter list for
+	  GdiPlus::GdipGetImagePalette () (the palette should be a
+	  caller-allocated 'IntPtr', not an 'out IntPtr').
+
 2005-02-24  Geoff Norton  <gnorton@customerdna.com>

 	* gdipFunctions.cs: Cache the delegates in the GdiPlusStreamHelper

--=====================_1110024757==_
Content-Type: text/plain; charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment; filename="libgdiplus.diff"

Index: src/bmpcodec.c
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- src/bmpcodec.c	(revision 41384)
+++ src/bmpcodec.c	(working copy)
@@ -39,6 +39,7 @@
 #include <stdio.h>
 #include "bmpcodec.h"
 #include "gdip.h"
+#include "gdipImage.h"


 /* Codecinfo related data*/
@@ -81,6 +82,8 @@
                 return Format8bppIndexed;
         case 4:
                 return Format4bppIndexed;
+	case 1:
+		return Format1bppIndexed;
         default:
                 return 0;
         }
@@ -96,7 +99,7 @@
 	bmi->biWidth =3D GULONG_FROM_LE (bitmap->data.Width);
 	bmi->biHeight =3D GULONG_FROM_LE (bitmap->data.Height);
 	bmi->biPlanes =3D GUINT16_FROM_LE (1);
-	bmi->biBitCount =3D GUINT16_FROM_LE (32); /*PIXEL_FORMAT_BPP (bitmap->ima=
ge.pixFormat); */
+	bmi->biBitCount =3D GUINT16_FROM_LE (gdip_get_pixel_format_bpp (bitmap->d=
ata.PixelFormat));
 	bmi->biCompression =3D GUINT32_FROM_LE (BI_RGB);
 	bmi->biSizeImage =3D  GUINT32_FROM_LE (0); /* Many tools expect this may =
be set to zero for BI_RGB bitmaps */
 	bmi->biXPelsPerMeter =3D GULONG_FROM_LE ((int) (0.5f + ((gdip_get_display=
_dpi() * 3937) / 100)));
@@ -106,7 +109,7 @@
 	bmi->biWidth =3D bitmap->data.Width;
 	bmi->biHeight =3D bitmap->data.Height;
 	bmi->biPlanes =3D 1;
-	bmi->biBitCount =3D 32; /*PIXEL_FORMAT_BPP (bitmap->image.pixFormat); */
+	bmi->biBitCount =3D gdip_get_pixel_format_bpp (bitmap->data.PixelFormat);
 	bmi->biCompression =3D BI_RGB;
 	bmi->biSizeImage =3D  0; /* Many tools expect this may be set to zero for=
 BI_RGB bitmaps */
 	bmi->biXPelsPerMeter =3D (int) (0.5f + ((gdip_get_display_dpi() * 3937) /=
 100));
@@ -130,7 +133,31 @@
 	fwrite (&bmfh, sizeof (bmfh), 1, fp);
 	gdip_bitmap_fill_info_header (bitmap, &bmi);
 	bmi.biHeight =3D -bmi.biHeight;
+
 	fwrite (&bmi, sizeof (bmi), 1, fp);
+
+	if (gdip_is_an_indexed_pixelformat (bitmap->data.PixelFormat)) {
+		int i;
+
+		int palette_entries =3D bitmap->image.palette->Count;
+		if (bitmap->data.PixelFormat =3D=3D Format4bppIndexed)
+			palette_entries =3D 16;
+
+		for (i=3D0; i < palette_entries; i++)
+		{
+			unsigned char entry[4];
+
+			unsigned int packed =3D bitmap->image.palette->Entries[i];
+
+			entry[0] =3D ((packed >> 16) & 0xFF); // B
+			entry[1] =3D ((packed >>  8) & 0xFF); // G
+			entry[2] =3D ( packed        & 0xFF); // R
+			entry[3] =3D ((packed >> 24) & 0xFF); // A
+
+			fwrite(entry, 4, 1, fp);
+		}
+	}
+
 	fwrite (bitmap->data.Scan0, bitmapLen, 1, fp);
 	fclose (fp);
 }
@@ -289,19 +316,34 @@
         img->image.width =3D bmi.biWidth;
         img->image.height =3D bmi.biHeight;

-        //img->data.PixelFormat =3D img->image.pixFormat;
-        img->data.PixelFormat =3D Format32bppArgb;
+        img->data.PixelFormat =3D img->image.pixFormat;
         img->data.Width =3D img->image.width;
         img->data.Height =3D img->image.height;

-	// We always assume 32 bit and translate into 32 bit from source format
-        img->data.Stride =3D (32 * img->image.width) / 8;
-        img->data.Stride =3D (img->data.Stride + 3) & ~3;
-
+	switch (img->data.PixelFormat)
+	{
+		case Format1bppIndexed: img->data.Stride =3D (img->image.width + 7) / 8;=
 break;
+		case Format4bppIndexed: img->data.Stride =3D (img->image.width + 1) / 2;=
 break;
+		case Format8bppIndexed: img->data.Stride =3D  img->image.width;         =
 break;
+		default:
+			/* For other types, we assume 32 bit and translate into 32 bit from sou=
rce format */
+			img->data.PixelFormat =3D Format32bppRgb;
+			img->data.Stride =3D img->image.width * 4;
+			break;
+	}
+
+	/* Ensure pixman_bits_t alignment */
+	img->data.Stride +=3D (sizeof(pixman_bits_t) - 1);
+	img->data.Stride &=3D ~(sizeof(pixman_bits_t) - 1);
+
         if (colours) {
-                img->image.palette =3D g_malloc (sizeof(ColorPalette) + si=
zeof(ARGB) * colours);
+		int palette_entries =3D colours;
+		if (img->data.PixelFormat =3D=3D Format4bppIndexed)
+			palette_entries =3D 256;
+
+                img->image.palette =3D g_malloc (sizeof(ColorPalette) + si=
zeof(ARGB) * palette_entries);
                 img->image.palette->Flags =3D 0;
-                img->image.palette->Count =3D colours;
+                img->image.palette->Count =3D palette_entries;

                 /* Read optional colour table*/
                 if (os2format) {  /* RGBTRIPLE */
@@ -314,9 +356,9 @@
 					return InvalidParameter;
 				}
 				img->image.palette->Entries[i] =3D
-					(((data_read[0]&0xff)<<16) | 	// R
+					(((data_read[2]&0xff)<<16) | 	// R
 					((data_read[1]&0xff)<<8) | 	// G
-					(data_read[2]&0xff));		// B
+					(data_read[0]&0xff));		// B
                         }
 			GdipFree(data_read);
                 }
@@ -330,9 +372,9 @@
 				}

                                 img->image.palette->Entries[i] =3D
-					(((data_read[0]&0xff)<<16)  | 	// R
+					(((data_read[2]&0xff)<<16)  | 	// R
 					((data_read[1]&0xff)<<8) | 	// G
-					((data_read[2]&0xff)) | 	// B
+					((data_read[0]&0xff)) | 	// B
 					((data_read[3]& 0xff)<<24));	// Alpha
                         }
 			GdipFree(data_read);
@@ -389,64 +431,12 @@
 		}

 		switch(bmi.biBitCount) {
-			case 1: {
-				int	c;
-				int	bit;
-
-				for (c =3D 0; c < loop; c++) {
-					for (bit =3D 0; bit < 8; bit++) {
-						index =3D (line * img->data.Stride) + (c*8 + bit) * 4;
-
-						if (((data_read[c] << bit) & 0x80) !=3D 0) {
-							set_pixel_bgra(pixels, index, 0xff, 0xff, 0xff, 0xff);
-						} else {
-							set_pixel_bgra(pixels, index, 0x00, 0x00, 0x00, 0xff);
-						}
-					}
-				}
-
-				for (bit =3D 0; bit < img->image.width % 8; bit++) {
-					index =3D (line * img->data.Stride) + (c*8 + bit) * 4;
-
-					if (((data_read[c] << bit) & 0x80) !=3D 0) {
-						set_pixel_bgra(pixels, index, 0xff, 0xff, 0xff, 0xff);
-					} else {
-						set_pixel_bgra(pixels, index, 0x00, 0x00, 0x00, 0xff);
-					}
-				}
+			case 1:
+			case 4:
+			case 8: memcpy(pixels + line * img->data.Stride,
+					data_read, size);
 				continue;
-			}

-			case 4: {
-				int c;
-
-				for (c =3D 0; c < loop; c++) {
-					pixel =3D palette_lookup((data_read[c] & 0xf0) >> 4);
-
-					index =3D (line * img->data.Stride) + c*8;
-
-					set_pixel_bgra(pixels, index, (pixel & 0xff0000) >> 16, (pixel & 0xff=
00) >> 8, pixel & 0xff, 0xff);
-
-					pixel =3D palette_lookup(data_read[c] & 0xf);
-
-					set_pixel_bgra(pixels, index+4, (pixel & 0xff0000) >> 16, (pixel & 0x=
ff00) >> 8, pixel & 0xff, 0xff);
-				}
-				continue;
-			}
-
-			case 8: {
-				int	c;
-
-				for (c =3D 0; c < loop; c++) {
-					pixel =3D palette_lookup(data_read[c]);
-
-					index =3D (line * img->data.Stride) + c*4;
-
-					set_pixel_bgra(pixels, index, (pixel & 0xff0000) >> 16, (pixel & 0xff=
00) >> 8, pixel & 0xff, 0xff);
-				}
-				continue;
-			}
-
 			case 24: {
 				int	src;
 				int	dest;
@@ -483,16 +473,18 @@
 	}

 	GdipFree(data_read);
-	g_free(img->image.palette);
-	img->image.palette =3D NULL;

 	img->data.Scan0 =3D pixels;
         img->data.Reserved =3D GBD_OWN_SCAN0;
-        img->image.surface =3D cairo_surface_create_for_image (pixels,
+
+	if (!gdip_is_an_indexed_pixelformat (img->data.PixelFormat)) {
+        	img->image.surface =3D cairo_surface_create_for_image (pixels,
                                                          img->cairo_format,
                                                          img->image.width,
                                                          img->image.height,
                                                          img->data.Stride);
+	}
+
         img->image.imageFlags =3D
                 ImageFlagsReadOnly |
                 ImageFlagsHasRealPixelSize |
@@ -562,9 +554,12 @@
 	gdip_write_bmp_data (pointer, (byte *)&bmi, sizeof (bmi), useFile);

         if (colours) {
+		int palette_entries =3D bitmap->image.palette->Count;
+		if (bitmap->data.PixelFormat =3D=3D Format4bppIndexed)
+			palette_entries =3D 16;

                 /* Write palette on disk on BGRA*/
-                for (i =3D 0; bitmap->image.palette->Count; i++) {
+                for (i =3D 0; i < palette_entries; i++) {
                         color =3D bitmap->image.palette->Entries[i];
 #ifdef WORDS_BIGENDIAN
                         b =3D color >> 24;
Index: src/image.c
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- src/image.c	(revision 41384)
+++ src/image.c	(working copy)
@@ -167,8 +167,24 @@

 	cairo_new_path(graphics->ct);

-	/* Create a surface for this bitmap if one doesn't exist */=20=20=20
-	gdip_bitmap_ensure_surface ((GpBitmap*) image);
+	if (gdip_is_an_indexed_pixelformat (((GpBitmap*) image)->data.PixelFormat=
)) {
+		/* Unable to create a surface for the bitmap; it is an indexed image.
+		 * Instead, it will first be converted to 32-bit RGB.
+		 */
+		GpStatus status =3D OutOfMemory;
+
+		GpBitmap *rgb_bitmap =3D gdip_convert_indexed_to_rgb ((GpBitmap *) image=
);
+
+		if (rgb_bitmap !=3D NULL) {
+			status =3D GdipDrawImageRect(graphics, (GpImage *)rgb_bitmap, x, y, wid=
th, height);
+			GdipDisposeImage((GpImage *)rgb_bitmap);
+		}
+
+		return status;
+	}
+
+	/* Create a surface for this bitmap if one doesn't exist */
+	gdip_bitmap_ensure_surface ((GpBitmap *) image);
 	cairo_surface_set_filter (((GpBitmap*) image)->image.surface, gdip_get_ca=
iro_filter (graphics->interpolation));

 	cairo_translate (graphics->ct, x, y);
@@ -257,6 +273,25 @@
 	g_return_val_if_fail (image !=3D NULL, InvalidParameter);
 	g_return_val_if_fail (image->type =3D=3D imageBitmap, InvalidParameter);

+	if (gdip_is_an_indexed_pixelformat (((GpBitmap *)image)->data.PixelFormat=
)) {
+                /* Unable to create a surface for the bitmap; it is an ind=
exed image.
+                 * Instead, it will first be converted to 32-bit RGB.
+                 */
+		GpStatus status =3D OutOfMemory;
+
+		GpBitmap *rgb_bitmap =3D gdip_convert_indexed_to_rgb ((GpBitmap *)image);
+
+		if (rgb_bitmap !=3D NULL) {
+			status =3D GdipDrawImageRectRect (graphics, (GpImage *)rgb_bitmap,
+				dstx, dsty, dstwidth, dstheight,
+				srcx, srcy, srcwidth, srcheight,
+				srcUnit, imageAttributes, callback, callbackData);
+			GdipDisposeImage ((GpImage *)rgb_bitmap);
+		}
+
+		return status;
+	}
+
 	if (srcUnit !=3D UnitPixel && srcUnit !=3D UnitWorld) {
 		gdip_unitConversion(srcUnit, UnitPixel, dstx, &dstx);
 		gdip_unitConversion(srcUnit, UnitPixel, dsty, &dsty);
@@ -1070,19 +1105,67 @@
 GpStatus
 GdipGetImagePalette (GpImage *image, ColorPalette *palette, int size)
 {
-	return NotImplemented;
+	int palette_entries;
+	int bytes_needed;
+
+	if ((image =3D=3D NULL) || (palette =3D=3D NULL))
+		return InvalidParameter;
+
+	if (image->palette =3D=3D NULL)
+		return InvalidParameter;
+
+	palette_entries =3D image->palette->Count;
+
+	if ((image->type =3D=3D imageBitmap) && (((GpBitmap *)image)->data.PixelF=
ormat =3D=3D Format4bppIndexed))
+		palette_entries =3D 16;
+
+	bytes_needed =3D palette_entries * sizeof(ARGB) + sizeof(ColorPalette) - =
sizeof(ARGB);
+
+	if (bytes_needed > size)
+		return InvalidParameter;
+
+	memcpy(palette, image->palette, bytes_needed);
+	return Ok;
 }

 GpStatus
 GdipSetImagePalette (GpImage *image, GDIPCONST ColorPalette *palette)
 {
+	int entries_to_copy;
+
+	if ((image =3D=3D NULL) || (palette =3D=3D NULL))
+		return InvalidParameter;
+
+	if (image->palette =3D=3D NULL)
+		return InvalidParameter;
+
+	entries_to_copy =3D image->palette->Count;
+	if (entries_to_copy > palette->Count)
+		entries_to_copy =3D palette->Count;
+
+	memcpy(image->palette->Entries, palette->Entries, entries_to_copy * sizeo=
f(ARGB));
 	return Ok;
 }

 GpStatus
 GdipGetImagePaletteSize (GpImage *image, int* size)
 {
-	return NotImplemented;
+        int palette_entries;
+        int bytes_needed;
+
+        if ((image =3D=3D NULL) || (size =3D=3D NULL))
+                return InvalidParameter;
+
+        if (image->palette =3D=3D NULL)
+                return InvalidParameter;
+
+        palette_entries =3D image->palette->Count;
+
+        if ((image->type =3D=3D imageBitmap) && (((GpBitmap *)image)->data=
.PixelFormat =3D=3D Format4bppIndexed))
+                palette_entries =3D 16;
+
+        *size =3D palette_entries * sizeof(ARGB) + sizeof(ColorPalette) - =
sizeof(ARGB);
+	return Ok;
 }

 GpStatus
Index: src/jpegcodec.c
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- src/jpegcodec.c	(revision 41384)
+++ src/jpegcodec.c	(working copy)
@@ -567,7 +567,10 @@
         case Format32bppPArgb:
         case Format32bppRgb:
         case Format24bppRgb:
-        case Format8bppIndexed: /* assume this is grayscale */
+	    break;
+        case Format8bppIndexed: /* check that this is grayscale */
+	    if ((image->palette->Flags & PaletteFlagsGrayScale) =3D=3D 0)
+		return InvalidParameter;
             break;
         default:
             return InvalidParameter;
Index: src/tiffcodec.c
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- src/tiffcodec.c	(revision 41384)
+++ src/tiffcodec.c	(working copy)
@@ -206,6 +206,9 @@
 	if (!tiff)
 		return InvalidParameter;

+	if (gdip_is_an_indexed_pixelformat (bitmap->data.PixelFormat))
+		return NotImplemented; /* for now */
+
 	dimensionCount =3D image->frameDimensionCount;
 	for (j =3D 0; j < dimensionCount; j++)
 		totalPages +=3D image->frameDimensionList [j].count;
Index: src/gifcodec.c
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- src/gifcodec.c	(revision 41384)
+++ src/gifcodec.c	(working copy)
@@ -383,6 +383,9 @@
 	if (!stream)
 		return InvalidParameter;

+	if (gdip_is_an_indexed_pixelformat (bitmap->data.PixelFormat))
+		return NotImplemented; /* for now */
+
 	if (from_file)
 		fp =3D EGifOpenFileName ((unsigned char *) stream, 0);
 	else
Index: src/ChangeLog
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- src/ChangeLog	(revision 41384)
+++ src/ChangeLog	(working copy)
@@ -1,3 +1,83 @@
+2005-03-02  Jonathan Gilbert  <................>
+
+	* bitmap.c: Added indexed pixel formats to
+	  gdip_is_a_supported_pixelformat().
+	* bitmap.c: Added gdip_is_an_indexed_pixelformat(). Added its
+	  prototype to gdip.h; not sure if this is the correct place.
+	* bitmap.c: Added support for PixelFormat.Format4bppIndexed to
+	  GdipCreateBitmapFromScan0 (maps to CAIRO_FORMAT_A8, like
+	  PixelFormat.Format8bppIndexed). Palettes will be created
+	  as needed. Bitmaps with a 4bpp indexed pixel format will be
+	  given 256-entry ColorPalette structures, as the cairo_format
+	  indicates 8-bit data.
+	* bitmap.c: GdipCreateBitmapFromScan0 now computes stride for
+	  indexed pixel formats differently, and does not convert
+	  indexed pixel formats to 32-bit RGB. It also makes images with
+	  32-bit pixel formats initially black instead of initially
+	  transparent.
+	* bitmap.c: Added support for indexed images to
+	  gdip_bitmap_clone_data_rect().
+	* bitmap.c: Rewrote GdipBitmapLockBits () to be maintainable
+	  while supporting indexed pixel formats. Added
+	  gdip_can_window_without_copy (), gdip_is_a_32bit_pixelformat (),
+	  gdip_make_alpha_opaque () and gdip_is_an_alpha_pixelformat ().
+	* bitmap.c: Moved gdip_from_ARGB_to_RGB () and
+	  gdip_from_RGB_to_ARGB () above gdip_bitmap_change_rect_pixel_format (),
+	  so that gdip_bitmap_change_rect_pixel_format () can call them.
+	* bitmap.c: Added struct StreamingState and methods
+	  gdip_init_pixel_stream (), gdip_pixel_stream_has_next (),
+	  gdip_pixel_stream_get_next () and gdip_pixel_stream_set_next () to
+	  work with Bitmaps of various formats in an abstract way.
+	  gdip_pixel_stream_get_next () returns and
+	  gdip_pixel_stream_set_next () accepts 32-bit ARGB values for 15,
+	  16, 24, 32, 48 and 64 bit formats and palette indices for 1, 4 and
+	  8 bit formats. gdip_pixel_stream_get_next () and
+	  gdip_pixel_stream_set_next () cannot be mixed; call either one or
+	  the other on a given stream, but never both.
+	* bitmap.c: Rewrote gdip_bitmap_change_rect_pixel_format to use
+	  pixel streams, which adds support for indexed pixel formats and
+	  increases maintainability.
+	* bitmap.c: Added check to GdipBitmapSetPixel ensuring that
+	  the bitmap is not indexed (despite what MSDN says, in
+	  practice Microsoft's implementation rejects SetPixel on
+	  indexed Bitmaps).
+	* bitmap.c: Added gdip_convert_indexed_to_rgb (), which will
+	  be used by the functions implementing DrawImage, since
+	  gdip_bitmap_ensure_surface () is not compatible with indexed
+	  image data.
+	* image.c: Added support for indexed pixel formats in
+	  GdipDrawImageRect and GdipDrawImageRectRect. The support is
+	  for the image being drawn to be indexed, not the image
+	  being drawn on. :-)
+	* image.c: Implemented GdipGetImagePalette (),
+	  GdipSetImagePalette () and GdipGetImagePaletteSize ().
+	* bmpcodec.c: Corrected gdip_bitmap_fill_info_header ()'s
+	  interpretation of pixel bit widths.
+	* bmpcodec.c: Added include directive for gdipImage.h, for
+	  utility function gdip_get_pixel_format_bpp ().
+	* bmpcodec.c: Updated gdip_bitmap_save_bmp () to save the
+	  palette for indexed bitmaps.
+	* bmpcodec.c: Updated gdip_read_bmp_image_from_file_stream ()
+	  to maintain 1, 4 and 8 bpp indexed data instead of converting
+	  it on-the-fly to 32 bpp RGB data.
+	* bmpcodec.c: Updated gdip_get_pixelformat () to translate
+	  a bit width of 1 into Format1bppIndexed.
+	* gifcodec.c: Updated gdip_save_gif_image () to reject
+	  indexed images as NotImplemented, for now at least.
+	* jpegcodec.c: Updated gdip_save_jpeg_image_internal () to
+	  reject 8bpp indexed images that are not grayscale (the
+	  previous behaviour was to assume all 8bpp indexed images
+	  are grayscale).
+	* tiffcodec.c: Updated gdip_save_tiff_image () to reject
+	  indexed images as NotImplemented, for now at least.
+	* pngcodec.c: Updated gdip_load_png_image_from_file_or_stream ()
+	  to handle 1-, 4- and 8-bit PNG files separately, without
+	  upsampling the data to 32-bit RBG.
+	* gdip.h: Fixed the set_pixel_bgra () macro; a lack of
+	  parentheses and no cast down to 'unsigned char' was making
+	  expressions like set_pixel_bgra (&array[i], ...) fail to
+	  compile.
+
 2005-02-24  Jordi Mas i Hernandez  <jordi@ximian.com>

 	* region.c: Fixes IsVisible methods
Index: src/bitmap.c
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- src/bitmap.c	(revision 41384)
+++ src/bitmap.c	(working copy)
@@ -43,6 +43,9 @@
 {
 	switch (fmt) {

+	case Format1bppIndexed:
+	case Format4bppIndexed:
+	case Format8bppIndexed:
  	case Format24bppRgb:
 	case Format32bppArgb:
 	case Format32bppPArgb:
@@ -53,6 +56,15 @@
 	}
 }

+/*
+	Returns TRUE if the Bitmap contains indexed (palettized) data.
+*/
+BOOL
+gdip_is_an_indexed_pixelformat (PixelFormat fmt)
+{
+	return ((fmt & PixelFormatIndexed) !=3D 0);
+}
+
 void
 gdip_bitmap_init (GpBitmap *bitmap)
 {
@@ -149,6 +161,7 @@
 		cairo_format =3D CAIRO_FORMAT_ARGB32;
 		break;
 	case Format8bppIndexed:
+	case Format4bppIndexed:
 		cairo_format =3D CAIRO_FORMAT_A8;
 		break;
 	case Format1bppIndexed:
@@ -169,10 +182,19 @@
 	result->cairo_format =3D cairo_format;

 	if (stride =3D=3D 0) {
-		int bpp  =3D gdip_get_pixel_format_components (format);
-		bpp =3D bpp * gdip_get_pixel_format_depth (format);
-		stride =3D (bpp * width) / 8;
-		stride +=3D (stride + (sizeof(pixman_bits_t)-1));
+		if (gdip_is_an_indexed_pixelformat (format)) {
+			int bpp =3D gdip_get_pixel_format_depth(format);
+			int bits_per_row =3D bpp * width;
+			stride =3D (bits_per_row + 7) / 8;
+		}
+		else {
+			int bpp  =3D gdip_get_pixel_format_components (format);
+			bpp =3D bpp * gdip_get_pixel_format_depth (format);
+			stride =3D (bpp * width) / 8;
+		}
+
+		/* make sure the stride aligns the next row to a pixman_bits_t boundary =
*/
+		stride +=3D (sizeof(pixman_bits_t)-1);
 		stride &=3D ~(sizeof(pixman_bits_t)-1);
 	}

@@ -180,8 +202,21 @@
 		scan0 =3D GdipAlloc (stride * height);
 		if (scan0 =3D=3D NULL)
 			return OutOfMemory;
-
-		memset (scan0, 0, stride * height);
+
+		if (gdip_get_pixel_format_bpp (format) < 16) {
+			memset (scan0, 0, stride * height);
+		}
+		else {
+			/* The image should be initially black, not initially transparent. */
+			int x, y;
+
+			for (y=3D0; y < height; y++) {
+				ARGB *scan =3D (ARGB *)((char *)scan0 + y * stride);
+				for (x=3D0; x * 4 < stride; x++) {
+					scan[x] =3D 0xFF000000;
+				}
+			}
+		}
 		own_scan0 =3D TRUE;
 	}

@@ -201,6 +236,22 @@
 			result->image.frameDimensionList[0].frames =3D &(((GpBitmap *) result)-=
>data);
 		}

+	/* Make sure indexed images have a palette */
+	if (gdip_is_an_indexed_pixelformat (format)) {
+		int palette_entries =3D 1 << gdip_get_pixel_format_depth(format);
+		int header_size =3D sizeof(ColorPalette) - sizeof(ARGB);
+		int bytes_needed =3D header_size + palette_entries * sizeof(ARGB);
+
+		result->image.palette =3D malloc (bytes_needed);
+
+		if (result->image.palette =3D=3D NULL)
+			return OutOfMemory;
+
+		result->image.palette->Flags =3D 0;
+		result->image.palette->Count =3D palette_entries;
+		memset(result->image.palette->Entries, 0, palette_entries * sizeof(ARGB)=
);
+	}
+
 	*bitmap =3D result;

 	return Ok;
@@ -299,7 +350,7 @@

 	result->cairo_format =3D original->cairo_format;
 	memcpy (&result->data, &bd, sizeof (GdipBitmapData));
-	result->image.pixFormat =3D format;	/* this is only used by image codecs =
*/
+	result->image.pixFormat =3D format;
 	result->image.format =3D original->image.format;
 	result->image.height =3D result->data.Height;
 	result->image.width =3D result->data.Width;
@@ -329,8 +380,8 @@

 /*
  * Copy srcRect region in srcData to destRect region in destData.  No conv=
ersion is done.  Assumes
- * BitmapData is straight from a GpBitmap, i.e. its format is always Pixel=
Format32bppArgb.  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
@@ -371,10 +422,75 @@
 		destData->Reserved =3D 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,  de=
stRect->Height);
+	if (!gdip_is_an_indexed_pixelformat) {
+		gdip_copy_strides (destData->Scan0, destData->Stride,
+			srcData->Scan0 + (srcData->Stride * srcRect->Y) + (gdip_get_pixel_forma=
t_components (srcData->PixelFormat)
+			* srcRect->X), srcData->Stride,   destRect->Width * dest_components,  d=
estRect->Height);
+	}
+	else {
+		int src_depth =3D gdip_get_pixel_format_depth (srcData->PixelFormat);

+		/* first, check if the bits are aligned onto a byte boundary */
+		int src_first_x_bit_index =3D srcRect->X * src_depth;
+		int width_bits =3D destRect->Width * src_depth;
+
+		int src_first_x_bit_offset_into_byte =3D src_first_x_bit_index & 7;
+		int width_bit_offset_into_byte =3D width_bits & 7;
+
+		if (src_first_x_bit_offset_into_byte =3D=3D 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 * srcR=
ect->Y),
+				srcData->Stride, width_bits / 8, destRect->Height);
+		}
+		else {
+			/* the not-so-fast path: no bits are aligned, so the entire image requi=
res bit juggling. */
+
+			unsigned char *src_scan0 =3D srcData->Scan0;
+			unsigned char *dest_scan0 =3D destData->Scan0;
+
+			int left_shift =3D src_first_x_bit_offset_into_byte;
+
+			int left_edge_src_mask =3D 255 >> src_first_x_bit_offset_into_byte;
+
+			int num_whole_dest_bytes =3D (width_bits / 8);
+
+			int x, y;
+
+			/* move the src_scan0 up to the first byte with pixel data involved in =
the copy */
+			src_scan0 +=3D srcRect->Y * srcData->Stride;
+			src_scan0 +=3D (src_first_x_bit_offset_into_byte / 8);
+
+			for (y=3D0; y < destRect->Height; y++) {
+				unsigned char *src_scan =3D src_scan0 + y * srcData->Stride;
+				unsigned char *dest_scan =3D dest_scan0 + y * destData->Stride;
+
+				unsigned short buffer;
+
+				/* jump-start the packing function. it avoids double-sampling the sour=
ce 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 =3D src_scan[0] << left_shift;
+
+				for (x=3D1; x < destRect->Width; x++) {
+					buffer <<=3D 8;
+					buffer |=3D src_scan[x] << left_shift;
+					dest_scan[0] =3D (buffer >> 8);
+				}
+			}
+		}
+	}
+
 	return Ok;
 }

@@ -404,7 +520,7 @@
 	if (!(src & PixelFormatGDI))
 		return 0;

-	if (src & PixelFormatIndexed)
+	if ((src & PixelFormatIndexed)      )//     || (dest & PixelFormatIndexed=
))
 		return 0;

 	/* These are the RGB formats */
@@ -421,162 +537,6 @@
 	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 *srcRe=
ct, GdipBitmapData *destData, Rect *destRect)
-{
-	gpointer outBuffer =3D NULL;
-	gint outStride =3D 0;
-	PixelFormat srcFormat, destFormat;
-
-	g_return_val_if_fail (srcData !=3D NULL, InvalidParameter);
-	g_return_val_if_fail (srcRect !=3D NULL, InvalidParameter);
-	g_return_val_if_fail (destData !=3D NULL, InvalidParameter);
-	g_return_val_if_fail (destRect !=3D NULL, InvalidParameter);
-
-	g_return_val_if_fail (srcRect->Width <=3D destRect->Width, InvalidParamet=
er);
-	g_return_val_if_fail (srcRect->Height <=3D destRect->Height, InvalidParam=
eter);
-
-	srcFormat =3D srcData->PixelFormat;
-	destFormat =3D 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 =3D=3D destFormat) {
-		int bitsPerPixel =3D gdip_get_pixel_format_bpp (srcFormat);
-		int bytesPerPixel =3D bitsPerPixel / 8;
-
-		g_return_val_if_fail (bitsPerPixel =3D=3D 16 || bitsPerPixel =3D=3D 24 |=
| bitsPerPixel =3D=3D 32, InvalidParameter);
-
-		if (destData->Scan0 =3D=3D NULL) {
-			outStride =3D bytesPerPixel * destRect->Width;
-			while (outStride % sizeof(pixman_bits_t))
-				outStride++;		/* dword-align each row */
-
-			/* Allocate the output buffer */
-			outBuffer =3D GdipAlloc (outStride * destRect->Height);
-			if (outBuffer =3D=3D NULL)
-				return OutOfMemory;
-
-			destData->Width =3D destRect->Width;
-			destData->Height =3D destRect->Height;
-			destData->Stride =3D outStride;
-			destData->Scan0 =3D outBuffer;
-			destData->Reserved =3D GBD_OWN_SCAN0;
-		} else {
-			/* Don't touch the output data, just get the portion of the rect
-			 * that we're writing to
-			 */
-			outStride =3D destData->Stride;
-			outBuffer =3D destData->Scan0 + (destRect->Y * destData->Stride) + (des=
tRect->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 * bytesP=
erPixel), srcData->Stride,
-			 bytesPerPixel * destRect->Width,
-			 destRect->Height);
-	} else {
-		/* We need to convert pixel formats */
-		gboolean convert_24_to_32 =3D 0;
-		gboolean convert_32_to_24 =3D 0;
-		gboolean add_alpha =3D 0;
-
-		int dest_skip =3D 0;
-		int src_skip =3D 0;
-
-		int srcBitsPerPixel =3D gdip_get_pixel_format_bpp (srcFormat);
-		int srcBytesPerPixel =3D srcBitsPerPixel / 8;
-		int destBitsPerPixel =3D gdip_get_pixel_format_bpp  (destFormat);
-		int destBytesPerPixel =3D destBitsPerPixel / 8;
-
-		if (destData->Scan0 =3D=3D NULL) {
-			outStride =3D destBytesPerPixel * destRect->Width;
-			while (outStride % sizeof(pixman_bits_t))
-				outStride++;		/* dword-align each row */
-
-			/* Allocate the output buffer */
-			outBuffer =3D GdipAlloc (outStride * destRect->Height);
-			if (outBuffer =3D=3D NULL)
-				return OutOfMemory;
-
-			destData->Width =3D destRect->Width;
-			destData->Height =3D destRect->Height;
-			destData->Stride =3D outStride;
-			destData->Scan0 =3D outBuffer;
-			destData->Reserved =3D GBD_OWN_SCAN0;
-		} else {
-			/* Don't touch the output data, just get the portion of the rect
-			 * that we're writing to
-			 */
-			outStride =3D destData->Stride;
-			outBuffer =3D destData->Scan0 + (destRect->Y * destData->Stride) + (des=
tRect->X * destBytesPerPixel);
-		}
-
-		/* First, figure out the conversion type */
-		if (gdip_get_pixel_format_bpp (srcFormat) =3D=3D 32 && gdip_get_pixel_fo=
rmat_bpp (destFormat) =3D=3D 24)
-			convert_32_to_24 =3D 1;
-		else if (gdip_get_pixel_format_bpp (srcFormat) =3D=3D 24 && gdip_get_pix=
el_format_bpp (destFormat) =3D=3D 32)
-			convert_24_to_32 =3D 1;
-
-		if (!(srcFormat & PixelFormatAlpha) && (destFormat & PixelFormatAlpha))
-			add_alpha =3D 1;
-
-		if (convert_32_to_24) src_skip =3D 1;
-		if (convert_24_to_32 && !add_alpha) dest_skip =3D 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 s=
trides,
-			 * and then go through and add alpha...
-			 */
-			gdip_copy_strides
-				(outBuffer,
-				 outStride,
-				 srcData->Scan0 + (srcRect->Y * srcData->Stride) + (srcRect->X * srcBy=
tesPerPixel),
-				 srcData->Stride,
-				 destBytesPerPixel * destRect->Width,
-				 destRect->Height);
-			if (add_alpha) {
-				unsigned int *ptr =3D (unsigned int *) outBuffer;
-				int i;
-				for (i =3D 0; i < destRect->Height * (outStride / sizeof(unsigned int)=
); i++)
-					*ptr++ |=3D 0xff000000; /* XXX TODO: check what value windows sets al=
pha to */
-			}
-		} else {
-			/* We need to do 24->32 or 32->24 conversion */
-			unsigned char *srcPtr, *destPtr;
-			int i, j;
-
-			for (j =3D 0; j < destRect->Height; j++) {
-				srcPtr =3D srcData->Scan0 + ((srcRect->Y + j) * srcData->Stride) + (sr=
cRect->X * srcBytesPerPixel);
-				destPtr =3D outBuffer + (j * outStride);
-				for (i =3D 0; i < destRect->Width; i++) {
-					*destPtr++ =3D *srcPtr++;
-					*destPtr++ =3D *srcPtr++;
-					*destPtr++ =3D *srcPtr++;
-					srcPtr +=3D src_skip;
-					if (add_alpha) *destPtr++ =3D 0xff;
-					else destPtr +=3D dest_skip;
-				}
-			}
-		}
-	}
-
-	return Ok;
-}
-
-
 /* Format24bppRgb is internally stored by Cairo as a four bytes. Convert i=
t to 3-byte (RGB) */
 int
 gdip_from_ARGB_to_RGB (BYTE *src, int width, int height, int stride, BYTE =
**dest, int* dest_stride)
@@ -610,7 +570,7 @@
 }


-/* Format24bppRgb is internally stored by Cairo as a four bytes. Convert i=
t 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)
 {
@@ -643,7 +603,653 @@
 	return Ok;
 }

+typedef struct
+{
+	Rect region;
+	int x, y;               /* the offset of the next byte that will be loade=
d, once the buffer is depleted */
+	unsigned short buffer;
+	int p;                  /* index of pixel within 'buffer' that was return=
ed 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, in=
t y, int w, int h)
+{
+	if ((state =3D=3D NULL) || (data =3D=3D NULL) || (data->Scan0 =3D=3D NULL=
))
+		return InvalidParameter;
+
+	/* Ensure that the rectangle requested is sensible. */
+	if ((x < 0) || (y < 0) || (x >=3D data->Width) || (y >=3D 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 =3D x;
+	state->region.Y =3D y;
+	state->region.Width =3D w;
+	state->region.Height =3D h;
+
+	state->x =3D x;
+	state->y =3D y;
+
+	state->p =3D -1; /* ensure that the buffer will be preloaded on the first=
 call, for indexed formats */
+
+	switch (data->PixelFormat)
+	{
+		case Format1bppIndexed: state->one_pixel_mask =3D 0x01; state->one_pixel=
_shift =3D 1; state->pixels_per_byte =3D 8; break;
+		case Format4bppIndexed: state->one_pixel_mask =3D 0x0F; state->one_pixel=
_shift =3D 4; state->pixels_per_byte =3D 2; break;
+		case Format8bppIndexed: state->one_pixel_mask =3D 0xFF; state->one_pixel=
_shift =3D 8; state->pixels_per_byte =3D 1; break;
+		default:
+			state->pixels_per_byte =3D 0; /* indicate full RGB processing */
+			break;
+	}
+
+	state->data =3D 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 =3D (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 !=3D NULL)
+		return (state->p >=3D 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 =3D=3D NULL)
+		return 0xFFFF00FF; /* bright pink; hopefully this will get somebody's at=
tention :-) */
+
+	/* Note: This function does not check whether the end of the region has b=
een 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 =3D=3D 1) {
+		/* A fast path for 8-bit indexed data: pixels are byte-aligned, so no sp=
ecial unpacking is required. */
+		ret =3D *state->scan;
+
+		state->scan++;
+		state->x++;
+
+		if (state->x >=3D (state->region.X + state->region.Width)) {
+			state->x =3D state->region.X;
+			state->y++;
+
+			state->scan =3D (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 in=
to a single byte). */
+		if (state->p < 0) {
+			state->buffer =3D *state->scan;
+			state->scan++;
+			state->p =3D 0;
+
+			if (state->x =3D=3D state->region.X) {
+				/* First pixel of the row; check whether it is aligned to the byte or =
not. */
+				int index_into_byte =3D state->x & (state->pixels_per_byte - 1);
+
+				if (index_into_byte !=3D 0) {
+					/* Not aligned; need to advance the buffer to the
+					 * first pixel in the stream region.
+					 */
+					state->buffer <<=3D (index_into_byte * state->one_pixel_shift);
+					state->p =3D index_into_byte;
+				}
+			}
+		}
+
+		state->buffer <<=3D state->one_pixel_shift;
+
+		ret =3D (state->buffer >> 8) & state->one_pixel_mask;
+
+		state->x++;
+		state->p++;
+
+		/* Have we hit the end of the buffer? */
+		if (state->p >=3D state->pixels_per_byte)
+			state->p =3D -1;
+
+		if (state->x >=3D (state->region.X + state->region.Width)) {
+			state->x =3D state->region.X;
+			state->y++;
+
+			state->scan =3D (unsigned char *)(state->data->Scan0)
+			            + state->y * state->data->Stride
+			            + state->x * gdip_get_pixel_format_bpp (state->data->PixelF=
ormat) / 8;
+
+			state->p =3D -1;
+		}
+	}
+	else {
+		/* We have an RGB format. In the current implementation, these are alway=
s 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 fut=
ure, though,
+		 * then this is where it needs to be done.
+		 */
+		ret =3D *(unsigned int *)state->scan;
+
+		/* Special case: 24-bit data needs to have the cairo format alpha compon=
ent forced
+		 * to 0xFF, or many operations will do nothing (or do strange things if =
the alpha
+		 * channel contains garbage).
+		 */
+		if (state->data->PixelFormat =3D=3D Format24bppRgb)
+			ret |=3D 0xFF000000;
+
+		state->scan +=3D 4;
+		state->x++;
+
+		if (state->x >=3D (state->region.X + state->region.Width)) {
+			state->x =3D state->region.X;
+			state->y++;
+
+			state->scan =3D (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_valu=
e)
+{
+	if (state =3D=3D NULL)
+		return;
+
+	/* Note: This function does not check whether the end of the region has b=
een 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 =3D=3D 1) {
+		/* A fast path for 8-bit indexed data: pixels are byte-aligned, so no sp=
ecial packing is required. */
+		*state->scan =3D pixel_value & 0xFF;
+
+		state->scan++;
+		state->x++;
+
+		if (state->x >=3D (state->region.X + state->region.Width)) {
+			state->x =3D state->region.X;
+			state->y++;
+
+			state->scan =3D (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 in=
to a single byte). */
+		if (state->p < 0) {
+			state->p =3D 0;
+
+			if (state->x =3D=3D state->region.X) {
+				/* First pixel of the row; check whether it is aligned to the byte or =
not. */
+				int index_into_byte =3D state->x & (state->pixels_per_byte - 1);
+
+				if (index_into_byte =3D=3D 0) {
+					/* It is aligned; all we need to do is clear the buffer. */
+					state->buffer =3D 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 =3D (*state->scan << (index_into_byte * state->one_pixe=
l_shift));
+					state->p =3D index_into_byte;
+				}
+			}
+		}
+
+		state->buffer <<=3D state->one_pixel_shift;
+		state->buffer |=3D ((pixel_value & state->one_pixel_mask) << 8);
+
+		state->x++;
+		state->p++;
+
+		/* Have we hit the end of the buffer? */
+		if (state->p >=3D state->pixels_per_byte)	{
+			*state->scan =3D (state->buffer >> 8);
+			state->scan++;
+			state->p =3D -1;
+		}
+
+		if (state->x >=3D (state->region.X + state->region.Width)) {
+			if (state->p >=3D 0) {
+				int existing_mask =3D 0;
+
+				while (state->p < state->pixels_per_byte) {
+					existing_mask <<=3D state->one_pixel_shift;
+					existing_mask |=3D state->one_pixel_mask;
+
+					state->buffer <<=3D state->one_pixel_shift;
+					state->p++;
+				}
+
+				*state->scan =3D (*state->scan & existing_mask) | (state->buffer >> 8);
+			}
+
+			state->x =3D state->region.X;
+			state->y++;
+
+			state->scan =3D (unsigned char *)(state->data->Scan0)
+			            + state->y * state->data->Stride
+			            + state->x * gdip_get_pixel_format_bpp (state->data->PixelF=
ormat) / 8;
+
+			state->p =3D -1;
+		}
+	}
+	else {
+		/* We have an RGB format. In the current implementation, these are alway=
s 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 fut=
ure, though,
+		 * then this is where it needs to be done.
+		 */
+		*(unsigned int *)state->scan =3D pixel_value;
+
+		state->scan +=3D 4;
+		state->x++;
+
+		if (state->x >=3D (state->region.X + state->region.Width)) {
+			state->x =3D state->region.X;
+			state->y++;
+
+			state->scan =3D (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 *srcRe=
ct, GdipBitmapData *destData, Rect *destRect)
+{
+	PixelFormat srcFormat, destFormat;
+	StreamingState srcStream, destStream;
+	Rect effectiveDestRect;
+
+	GpStatus status;
+
+	g_return_val_if_fail (srcData !=3D NULL, InvalidParameter);
+	g_return_val_if_fail (srcRect !=3D NULL, InvalidParameter);
+	g_return_val_if_fail (destData !=3D NULL, InvalidParameter);
+	g_return_val_if_fail (destRect !=3D NULL, InvalidParameter);
+
+	if ((srcRect->X < 0) || (srcRect->Y < 0) || (srcRect->X >=3D srcData->Wid=
th) || (srcRect->Y >=3D srcData->Height))
+		return InvalidParameter;
+	if ((srcRect->X + srcRect->Width > srcData->Width) || (srcRect->Y + srcRe=
ct->Height > srcData->Height))
+		return InvalidParameter;
+
+	if ((destRect->X < 0) || (destRect->Y < 0))
+		return InvalidParameter;
+
+	g_return_val_if_fail (srcRect->Width <=3D destRect->Width, InvalidParamet=
er);
+	g_return_val_if_fail (srcRect->Height <=3D destRect->Height, InvalidParam=
eter);
+
+	srcFormat =3D srcData->PixelFormat;
+	destFormat =3D destData->PixelFormat;
+
+	if (!gdip_is_pixel_format_conversion_valid (srcFormat, destFormat))
+		return InvalidParameter;
+
+	if (destData->Scan0 =3D=3D NULL) {
+		/* Allocate a buffer on behalf of the caller. */
+		int width =3D destRect->X + destRect->Width;
+		int scans =3D destRect->Y + destRect->Height;
+
+		int row_bits =3D destRect->Width * gdip_get_pixel_format_bpp (destFormat=
);
+		int row_bytes =3D (row_bits + 7) / 8;
+
+		int stride =3D (row_bytes + sizeof(pixman_bits_t) - 1) & ~(sizeof(pixman=
_bits_t) - 1);
+
+		void *dest_scan0 =3D malloc(stride * scans);
+
+		if (dest_scan0 =3D=3D NULL)
+			return OutOfMemory;
+
+		destData->Width =3D destRect->X + destRect->Width;
+		destData->Height =3D destRect->Y + destRect->Height;
+		destData->Stride =3D stride;
+		destData->Scan0 =3D dest_scan0;
+		destData->Reserved =3D 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 =3D *destRect;
+
+	if (effectiveDestRect.Width > srcRect->Width)
+		effectiveDestRect.Width =3D srcRect->Width;
+	if (effectiveDestRect.Height > srcRect->Height)
+		effectiveDestRect.Height =3D srcRect->Height;
+
+	/* Fire up the pixel streams. */
+	status =3D gdip_init_pixel_stream (&srcStream, srcData, srcRect->X, srcRe=
ct->Y, srcRect->Width, srcRect->Height);
+
+	if (status !=3D Ok)
+		return status;
+
+	status =3D gdip_init_pixel_stream (&destStream, destData, effectiveDestRe=
ct.X, effectiveDestRect.Y, effectiveDestRect.Width, effectiveDestRect.Heigh=
t);
+
+	if (status !=3D Ok)
+		return status;
+
+	/* Move the data. */
+	while (gdip_pixel_stream_has_next (&srcStream))
+		gdip_pixel_stream_set_next (&destStream, gdip_pixel_stream_get_next (&sr=
cStream));
+
+	return Ok;
+}
+
+
+#define NEW_LOCKBITS_IMPL
+
+#ifdef NEW_LOCKBITS_IMPL
+BOOL
+gdip_is_a_32bit_pixelformat (int 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 (int format)
+{
+	return ((format & PixelFormatAlpha) !=3D 0);
+}
+
+BOOL
+gdip_can_window_without_copy (BitmapData *data, Rect *rect, int format)
+{
+	int bpp =3D gdip_get_pixel_format_bpp (format);
+
+	if (format !=3D 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. =3D/
+	 */
+
+	/* 8bpp and above are guaranteed to be byte-aligned */
+	if (bpp >=3D 8)
+		return TRUE;
+	else {
+		int left_bit_offset =3D rect->X * bpp;
+		int width_bit_count =3D rect->Width * bpp;
+
+		/* check whether the values are byte-aligned */
+		return ((left_bit_offset & 7) | (width_bit_count & 7)) =3D=3D 0;
+	}
+}
+
+void
+gdip_make_alpha_opaque (BitmapData *data)
+{
+	unsigned char *scan0 =3D (unsigned char *)data->Scan0;
+	int y, x, o, f;
+
+	/* sanity check; make sure we aren't mangling any image data */
+	if ((data->PixelFormat !=3D Format32bppArgb)
+	 && (data->PixelFormat !=3D Format32bppRgb))
+		return;
+
+	f =3D data->Stride - 4 * data->Width;
+	for (y=3D0, o=3D0; y < data->Height; y++, o +=3D f)
+		for (x=3D0; x < data->Width; x++, o +=3D 4)
+			scan0[o + 3] =3D 0xff; /* set alpha to fully-opaque */
+}
+
+GpStatus
+GdipBitmapLockBits (GpBitmap *bitmap, Rect *srcRect, int flags, int format=
, GdipBitmapData *locked_data)
+{
+	BitmapData *root_data =3D &bitmap->data;
+
+	g_return_val_if_fail (bitmap !=3D NULL, InvalidParameter);
+	g_return_val_if_fail (srcRect !=3D NULL, InvalidParameter);
+	g_return_val_if_fail (flags !=3D 0, InvalidParameter);
+	g_return_val_if_fail (locked_data !=3D 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) || (srcR=
ect->Height < 0))
+		return InvalidParameter;
+
+	if ((srcRect->X + srcRect->Width > root_data->Width) || (srcRect->Y + src=
Rect->Height > root_data->Height))
+		return InvalidParameter;
+
+	if (gdip_is_a_supported_pixelformat (format) =3D=3D FALSE)
+		return NotImplemented;
+
+	/* Common stuff */
+	if (flags =3D=3D ImageLockModeRead)
+		locked_data->Reserved |=3D GBD_READ_ONLY;
+	else
+		locked_data->Reserved &=3D ~GBD_READ_ONLY;
+
+	locked_data->Reserved |=3D GBD_LOCKED;
+	locked_data->Reserved |=3D GBD_OWN_SCAN0;
+	root_data->Reserved |=3D 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 =3D root_data->Scan0
+		                   + srcRect->Y * root_data->Stride
+		                   + srcRect->X * gdip_get_pixel_format_bpp(root_data->P=
ixelFormat) / 8;
+
+		/* Set up the window's dimensions */
+		locked_data->Width =3D srcRect->Width;
+		locked_data->Height =3D srcRect->Height;
+		locked_data->Stride =3D root_data->Stride; /* the stride hasn't changed =
*/
+		locked_data->PixelFormat =3D 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 &=3D ~GBD_OWN_SCAN0;
+
+		/* If the source pixel format doesn't have alpha and the dest pixel form=
at
+		 * 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 use=
r's rectangle
+		 * doesn't line up on byte boundaries, or the pixel format needs to be c=
hanged.
+		 */
+		int dest_pixel_format_bpp =3D gdip_get_pixel_format_bpp (format);
+		int dest_stride =3D (srcRect->Width * dest_pixel_format_bpp + 7) / 8;
+		int dest_size =3D srcRect->Height * dest_stride;
+
+		unsigned char *dest_scan0 =3D malloc(dest_size);
+
+		Rect destRect =3D { 0, 0, srcRect->Width, srcRect->Height };
+
+		GpStatus status =3D Ok;
+
+		if (dest_scan0 =3D=3D NULL)
+			return OutOfMemory;
+
+		locked_data->Scan0 =3D dest_scan0;
+		locked_data->Width =3D srcRect->Width;
+		locked_data->Height =3D srcRect->Height;
+		locked_data->Stride =3D dest_stride;
+		locked_data->PixelFormat =3D format;
+
+		/* If the user wants the original data to be readable, then convert the =
bits. */
+		if ((flags & ImageLockModeRead) !=3D 0) {
+			status =3D gdip_bitmap_change_rect_pixel_format (root_data, srcRect, lo=
cked_data, &destRect);
+			if (status !=3D Ok)
+				free(dest_scan0);
+		}
+
+		return status;
+	}
+}
+
+GpStatus
+GdipBitmapUnlockBits (GpBitmap *bitmap, GdipBitmapData *locked_data)
+{
+	BitmapData *root_data =3D &bitmap->data;
+
+	g_return_val_if_fail (bitmap !=3D NULL, InvalidParameter);
+	g_return_val_if_fail (locked_data !=3D 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 i=
n.
+	 * Sanity check: Make sure the locked data is in fact locked.
+	 */
+	if ((locked_data->Reserved & GBD_LOCKED) =3D=3D 0)
+		return InvalidParameter;
+
+	/* Sanity check: Make sure the locked data's size is consistent with havi=
ng
+	 * been returned from LockBits ().
+	 */
+	if ((locked_data->Width > root_data->Width) || (locked_data->Height > roo=
t_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)) < ro=
ot_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. Howev=
er,
+		 * a couple more sanity checks are required: The locked data's stride mu=
st
+		 * 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 =3D (char *)locked_data->Scan0
+		                           + (locked_data->Height - 1) * locked_data->St=
ride
+		                           + (locked_data->Width * gdip_get_pixel_format=
_bpp (locked_data->PixelFormat) + 7) / 8;
+
+		if ((locked_data->Stride !=3D 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 authe=
ntic,
+		 * so now the only thing that needs to be done is alpha fixup.
+		 */
+		if ((locked_data->Reserved & GBD_READ_ONLY) !=3D 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) !=3D 0) {
+			/* FIXME: destRect isn't necessarily equal to srcRect. this is a bug (t=
he old implementation had it too). */
+			Rect srcRect =3D { 0, 0, locked_data->Width, locked_data->Height };
+			Rect destRect =3D srcRect;
+
+			GpStatus status =3D gdip_bitmap_change_rect_pixel_format (root_data, &s=
rcRect, locked_data, &destRect);
+			if (status !=3D Ok)
+				return status;
+		}
+	}
+
+	if ((locked_data->Reserved & GBD_OWN_SCAN0) !=3D 0) {
+		free(locked_data->Scan0);
+		locked_data->Scan0 =3D NULL;
+		locked_data->Reserved &=3D ~GBD_OWN_SCAN0;
+	}
+
+	locked_data->Reserved &=3D ~GBD_LOCKED;
+	root_data->Reserved &=3D ~GBD_LOCKED;
+
+	return Ok;
+}
+
+#else
+
 /* Microsoft GDI+ returns BitmapLock buffers in BGR (not RGB) */
 GpStatus
 GdipBitmapLockBits (GpBitmap *bitmap, Rect *srcRect, int flags, int format=
, GdipBitmapData *result)
@@ -810,6 +1416,7 @@

 	return Ok;
 }
+#endif // NEW_LOCKBITS_IMPL

 GpStatus
 GdipBitmapSetPixel (GpBitmap *bitmap, int x, int y, ARGB color)
@@ -825,7 +1432,10 @@

 	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;
@@ -880,6 +1490,7 @@
 		default:
 			return NotImplemented;
 	}
+
 	return Ok;
 }

@@ -972,3 +1583,93 @@
 	return bitmap->image.surface;
 }

+GpBitmap *
+gdip_convert_indexed_to_rgb (GpBitmap *indexed_bmp)
+{
+	BitmapData *data =3D &indexed_bmp->data;
+	ColorPalette *palette =3D 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 =3D=3D NULL)
+		return NULL;
+
+	switch (data->PixelFormat)
+	{
+		case Format1bppIndexed: one_pixel_mask =3D 0x01; one_pixel_shift =3D 1; =
pixels_per_byte =3D 8; break;
+		case Format4bppIndexed: one_pixel_mask =3D 0x0F; one_pixel_shift =3D 4; =
pixels_per_byte =3D 2; break;
+		case Format8bppIndexed: one_pixel_mask =3D 0xFF; one_pixel_shift =3D 8; =
pixels_per_byte =3D 1; break;
+		default: /* something is wrong!! */
+			return NULL;
+	}
+
+	if ((palette->Flags & PaletteFlagsHasAlpha) =3D=3D 0)
+		force_alpha =3D 0xff000000;
+	else
+		force_alpha =3D 0;
+
+	rgb_stride =3D data->Width * 4;
+
+	/* ensure pixman_bits_t alignment */
+	rgb_stride +=3D (sizeof(pixman_bits_t)-1);
+	rgb_stride &=3D ~(sizeof(pixman_bits_t)-1);
+
+	rgb_bytes =3D data->Height * rgb_stride;
+
+	/* allocate the RGB frame */
+	rgb_scan0 =3D malloc (rgb_bytes);
+
+	if (rgb_scan0 =3D=3D NULL) /* out of memory?? */
+		return NULL;
+
+	/* convert the indexed pixels into RGB values and store them into the RGB=
 frame */
+	for (y=3D0; y < data->Height; y++)
+	{
+		unsigned char *indexed_scan =3D (unsigned char *)(data->Scan0) + y * dat=
a->Stride;
+		int *rgb_scan =3D rgb_scan0 + (y * rgb_stride) / sizeof(int);
+
+		for (x=3D0; x < data->Width; x +=3D pixels_per_byte)
+		{
+			int pixels_this_byte =3D pixels_per_byte;
+			unsigned short sample =3D *indexed_scan;
+
+			indexed_scan++;
+
+			if (x + pixels_this_byte >=3D data->Width)
+				pixels_this_byte =3D data->Width - x;
+
+			for (p=3D0; p < pixels_this_byte; p++)
+			{
+				int index;
+
+				sample <<=3D one_pixel_shift;
+				index =3D (sample >> 8) & one_pixel_mask;
+
+				rgb_scan[x + p] =3D palette->Entries[index] | force_alpha;
+			}
+		}
+	}
+
+	/* try to get a GpBitmap out of it :-) */
+	status =3D GdipCreateBitmapFromScan0 (data->Width, data->Height, rgb_stri=
de, Format32bppRgb, rgb_scan0, &ret);
+
+	if ((status !=3D Ok) || (ret =3D=3D NULL)) {
+		free (ret);
+		free (rgb_scan0);
+		return NULL;
+	}
+	else {
+		ret->data.Reserved |=3D GBD_OWN_SCAN0;
+		return ret;
+	}
+}
+
Index: src/pngcodec.c
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- src/pngcodec.c	(revision 41384)
+++ src/pngcodec.c	(working copy)
@@ -147,9 +147,120 @@
         png_set_read_fn (png_ptr, pngsrc, _gdip_png_stream_read_data);
     }

-    png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_BGR | PNG_TRANSFORM_EXPA=
ND, NULL);
+    png_read_png(png_ptr, info_ptr, 0, NULL);

-    {
+    if ((png_get_bit_depth (png_ptr, info_ptr) <=3D 8)
+     && (png_get_channels (png_ptr, info_ptr) =3D=3D 1)
+     && (png_get_color_type (png_ptr, info_ptr) =3D=3D PNG_COLOR_TYPE_PALE=
TTE)) {
+	int width;
+	int height;
+	int bit_depth;
+	int source_stride, dest_stride;
+	png_bytep *row_pointers;
+	guchar *rawptr;
+	int num_colours;
+	int palette_entries;
+	ColorPalette *palette;
+	int i, j;
+
+	width =3D png_get_image_width (png_ptr, info_ptr);
+	height =3D png_get_image_height (png_ptr, info_ptr);
+	bit_depth =3D png_get_bit_depth (png_ptr, info_ptr);
+
+	source_stride =3D (width * bit_depth + 7) / 8;
+	dest_stride =3D (source_stride + sizeof(pixman_bits_t) - 1) & ~(sizeof(pi=
xman_bits_t) - 1);
+
+	/* Copy image data. */
+	row_pointers =3D png_get_rows (png_ptr, info_ptr);
+
+	if (bit_depth =3D=3D 2) { /* upsample to 4bpp */
+		dest_stride =3D ((width + 1) / 2 + sizeof(pixman_bits_t) - 1) & ~(sizeof=
(pixman_bits_t) - 1);
+
+		rawdata =3D GdipAlloc(dest_stride * height);
+		for (i=3D0; i < height; i++) {
+			png_bytep row =3D row_pointers[i];
+			rawptr =3D rawdata + i * dest_stride;
+
+			for (j=3D0; j < source_stride; j++) {
+				int four_pixels =3D row[j];
+
+				int first_two =3D 0x0F & (four_pixels >> 4);
+				int second_two =3D 0x0F & four_pixels;
+
+				first_two =3D (first_two & 0x03) | ((first_two & 0x0C) << 2);
+				second_two =3D (second_two & 0x03) | ((second_two & 0x0C) << 2);
+
+				rawptr[j * 2 + 0] =3D first_two;
+				rawptr[j * 2 + 1] =3D second_two;
+			}
+		}
+	}
+	else {
+		rawdata =3D GdipAlloc(dest_stride * height);
+		for (i=3D0; i < height; i++)
+			memcpy(rawdata + i * dest_stride, row_pointers[i], source_stride);
+	}
+
+	/* Copy palette. */
+	num_colours =3D 1 << bit_depth;
+	if (bit_depth =3D=3D 4)
+		num_colours =3D 256;
+
+	palette =3D GdipAlloc (sizeof(ColorPalette) + num_colours * sizeof(ARGB));
+
+	palette->Flags =3D 0;
+	palette->Count =3D num_colours;
+
+	palette_entries =3D num_colours;
+	if (palette_entries > info_ptr->num_palette)
+		palette_entries =3D info_ptr->num_palette;
+
+	for (i=3D0; i < palette_entries; i++)
+		set_pixel_bgra (&palette->Entries[i], 0,
+			info_ptr->palette[i].red,
+			info_ptr->palette[i].green,
+			info_ptr->palette[i].blue,
+			0xFF); /* alpha */
+
+	/* Make sure transparency is respected. */
+	for (i=3D0; i < info_ptr->num_trans; i++) {
+		int transparent_index =3D info_ptr->trans[i];
+
+		if (transparent_index < num_colours)
+			palette->Entries[i] =3D 0; /* 0 has an alpha value of 0x00 */
+	}
+
+        png_destroy_read_struct (&png_ptr, &info_ptr, &end_info_ptr);
+
+        img =3D gdip_bitmap_new ();
+        img->image.type =3D imageBitmap;
+        img->image.width =3D width;
+        img->image.height =3D height;
+
+        img->cairo_format =3D CAIRO_FORMAT_ARGB32;
+        img->data.Stride =3D dest_stride;
+        img->data.Width =3D width;
+        img->data.Height =3D height;
+        img->data.Scan0 =3D rawdata;
+        img->data.Reserved =3D GBD_OWN_SCAN0;
+
+	switch (bit_depth)
+	{
+		case 1: img->image.pixFormat =3D img->data.PixelFormat =3D Format1bppInd=
exed; img->cairo_format =3D CAIRO_FORMAT_A1; break;
+		case 4: img->image.pixFormat =3D img->data.PixelFormat =3D Format4bppInd=
exed; img->cairo_format =3D CAIRO_FORMAT_A8; break;
+		case 8: img->image.pixFormat =3D img->data.PixelFormat =3D Format8bppInd=
exed; img->cairo_format =3D CAIRO_FORMAT_A8; break;
+	}
+
+        img->image.imageFlags =3D ImageFlagsColorSpaceGRAY;
+        img->image.imageFlags |=3D
+            ImageFlagsReadOnly |
+            ImageFlagsHasRealPixelSize;
+        img->image.horizontalResolution =3D 0;
+        img->image.verticalResolution =3D 0;
+        img->image.propItems =3D NULL;
+        img->image.palette =3D palette;
+    }
+    else {
         int width;
         int height;
         guchar bit_depth;
@@ -168,6 +279,16 @@
         channels =3D png_get_channels (png_ptr, info_ptr);
         interlace =3D png_get_interlace_type (png_ptr, info_ptr);

+	/* According to the libpng manual, this sequence is equivalent to
+	 * using the PNG_TRANSFORM_EXPAND flag in png_read_png.
+	 */
+	if (color_type =3D=3D PNG_COLOR_TYPE_PALETTE)
+		png_set_palette_to_rgb (png_ptr);
+	if ((color_type =3D=3D PNG_COLOR_TYPE_GRAY) && (bit_depth < 8))
+		png_set_gray_1_2_4_to_8(png_ptr);
+	if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
+		png_set_tRNS_to_alpha(png_ptr);
+
         stride =3D width * 4;

         while (stride % sizeof(pixman_bits_t))
@@ -184,7 +305,7 @@
 		for (i =3D 0; i < height; i++) {
 			png_bytep rowp =3D row_pointers[i];
 			for (j =3D 0; j < width; j++) {
-				set_pixel_bgra (rawptr, 0, rowp[0], rowp[1], rowp[2], rowp[3]);
+				set_pixel_bgra (rawptr, 0, rowp[2], rowp[1], rowp[0], rowp[3]);
 				rowp +=3D 4;
 				rawptr +=3D 4;
 			}
@@ -195,7 +316,7 @@
 		for (i =3D 0; i < height; i++) {
 			png_bytep rowp =3D row_pointers[i];
 			for (j =3D 0; j < width; j++) {
-				set_pixel_bgra (rawptr, 0, rowp[0], rowp[1], rowp[2], 0xff);
+				set_pixel_bgra (rawptr, 0, rowp[2], rowp[1], rowp[0], 0xff);
 				rowp +=3D 3;
 				rawptr +=3D 4;
 			}
@@ -340,6 +461,12 @@
         case Format8bppIndexed:
             bit_depth =3D 8;
             break;
+	case Format4bppIndexed:
+	    bit_depth =3D 4;
+	    break;
+	case Format1bppIndexed:
+	    bit_depth =3D 1;
+	    break;
         /* We're not going to even try to save these images, for now */
         case Format64bppArgb:
         case Format64bppPArgb:
@@ -348,8 +475,6 @@
         case Format16bppGrayScale:
         case Format16bppRgb555:
         case Format16bppRgb565:
-        case Format4bppIndexed:
-        case Format1bppIndexed:
         default:
             bit_depth =3D -1;
             break;
@@ -366,9 +491,13 @@
             break;
         case Format32bppRgb:
         case Format24bppRgb:
-        case Format8bppIndexed:
             color_type =3D PNG_COLOR_TYPE_RGB; /* XXX - we should be able =
to write grayscale PNGs */
             break;
+        case Format8bppIndexed:
+        case Format4bppIndexed:
+        case Format1bppIndexed:
+ 	    color_type =3D PNG_COLOR_TYPE_PALETTE;
+	    break;
         case Format64bppArgb:
         case Format64bppPArgb:
         case Format48bppRgb:
@@ -376,8 +505,6 @@
         case Format16bppGrayScale:
         case Format16bppRgb555:
         case Format16bppRgb565:
-        case Format4bppIndexed:
-        case Format1bppIndexed:
         default:
             color_type =3D -1;
             break;
@@ -394,6 +521,24 @@
                   PNG_COMPRESSION_TYPE_DEFAULT,
                   PNG_FILTER_TYPE_DEFAULT);

+    if (gdip_is_an_indexed_pixelformat (bitmap->data.PixelFormat)) {
+	png_color palette[256];
+
+	int palette_entries =3D image->palette->Count;
+	if (bitmap->data.PixelFormat =3D=3D Format4bppIndexed)
+		palette_entries =3D 16;
+
+	for (i=3D0; i < palette_entries; i++) {
+		ARGB entry =3D image->palette->Entries[i];
+
+		palette[i].red   =3D  entry        & 0xFF;
+		palette[i].green =3D (entry >>  8) & 0xFF;
+		palette[i].blue  =3D (entry >> 16) & 0xFF;
+	}
+
+	png_set_PLTE (png_ptr, info_ptr, palette, palette_entries);
+    }
+
     png_write_info (png_ptr, info_ptr);

     if (image->pixFormat !=3D bitmap->data.PixelFormat) {
@@ -409,11 +554,16 @@

     png_set_bgr(png_ptr);

+    if (gdip_is_an_indexed_pixelformat (bitmap->data.PixelFormat)) {
+	for (i =3D 0; i < image->height; i++) {
+		png_write_row (png_ptr, bitmap->data.Scan0 + i * bitmap->data.Stride);
+	}
+    }
+    else {
 #ifdef WORDS_BIGENDIAN
-    {
-	    guchar *row_pointer =3D GdipAlloc (image->width * 4);
+	guchar *row_pointer =3D GdipAlloc (image->width * 4);

-	    for (i =3D 0; i < image->height; i++) {
+	for (i =3D 0; i < image->height; i++) {
 		for (j =3D 0; j < image->width; j++) {
 			row_pointer[j*4] =3D *((guchar *)bitmap->data.Scan0 + (bitmap->data.Str=
ide * i) + (j*4) + 3);
 			row_pointer[j*4+1] =3D *((guchar *)bitmap->data.Scan0 + (bitmap->data.S=
tride * i) + (j*4) + 2);
@@ -421,14 +571,14 @@
 			row_pointer[j*4+3] =3D *((guchar *)bitmap->data.Scan0 + (bitmap->data.S=
tride * i) + (j*4) + 0);
 		}
 		png_write_row (png_ptr, row_pointer);
-	    }
-	    GdipFree (row_pointer);
-    }
+	}
+	GdipFree (row_pointer);
 #else
-    for (i =3D 0; i < image->height; i++) {
-    	png_write_row (png_ptr, bitmap->data.Scan0 + (bitmap->data.Stride * i=
));
+	for (i =3D 0; i < image->height; i++) {
+		png_write_row (png_ptr, bitmap->data.Scan0 + (bitmap->data.Stride * i));
+	}
+#endif
     }
-#endif

     png_write_end (png_ptr, NULL);

Index: src/gdip.h
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- src/gdip.h	(revision 41384)
+++ src/gdip.h	(working copy)
@@ -45,17 +45,17 @@

 #ifdef WORDS_BIGENDIAN
 #define set_pixel_bgra(pixel,index,b,g,r,a) do {\
-                pixel[index+0] =3D a; \
-                pixel[index+1] =3D r; \
-                pixel[index+2] =3D g; \
-                pixel[index+3] =3D b; \
+                ((unsigned char *)(pixel))[index+0] =3D a; \
+                ((unsigned char *)(pixel))[index+1] =3D r; \
+                ((unsigned char *)(pixel))[index+2] =3D g; \
+                ((unsigned char *)(pixel))[index+3] =3D b; \
         } while (0);
 #else
 #define set_pixel_bgra(pixel,index,b,g,r,a) do {\
-                pixel[index+0] =3D b; \
-                pixel[index+1] =3D g; \
-                pixel[index+2] =3D r; \
-                pixel[index+3] =3D a; \
+                ((unsigned char *)(pixel))[index+0] =3D b; \
+                ((unsigned char *)(pixel))[index+1] =3D g; \
+                ((unsigned char *)(pixel))[index+2] =3D r; \
+                ((unsigned char *)(pixel))[index+3] =3D a; \
         } while (0);
 #endif

@@ -1248,6 +1248,8 @@
 void gdip_rect_expand_by (GpRectF *rect, GpPointF *point);

 cairo_surface_t * gdip_bitmap_ensure_surface (GpBitmap *bitmap);
+BOOL gdip_is_an_indexed_pixelformat (PixelFormat pixfmt);
+GpBitmap * gdip_convert_indexed_to_rgb (GpBitmap *bitmap);

 const EncoderParameter *gdip_find_encoder_parameter (GDIPCONST EncoderPara=
meters *eps, const GUID *guid);


--=====================_1110024757==_--