Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/Imaging/libImaging/Convert.c
diff options
context:
space:
mode:
Diffstat (limited to 'Imaging/libImaging/Convert.c')
-rw-r--r--Imaging/libImaging/Convert.c1080
1 files changed, 1080 insertions, 0 deletions
diff --git a/Imaging/libImaging/Convert.c b/Imaging/libImaging/Convert.c
new file mode 100644
index 0000000..26bd01c
--- /dev/null
+++ b/Imaging/libImaging/Convert.c
@@ -0,0 +1,1080 @@
+/*
+ * The Python Imaging Library
+ * $Id: Convert.c 2591 2005-12-08 21:25:29Z fredrik $
+ *
+ * convert images
+ *
+ * history:
+ * 1995-06-15 fl created
+ * 1995-11-28 fl added some "RGBA" and "CMYK" conversions
+ * 1996-04-22 fl added "1" conversions (same as "L")
+ * 1996-05-05 fl added palette conversions (hack)
+ * 1996-07-23 fl fixed "1" conversions to zero/non-zero convention
+ * 1996-11-01 fl fixed "P" to "L" and "RGB" to "1" conversions
+ * 1996-12-29 fl set alpha byte in RGB converters
+ * 1997-05-12 fl added ImagingConvert2
+ * 1997-05-30 fl added floating point support
+ * 1997-08-27 fl added "P" to "1" and "P" to "F" conversions
+ * 1998-01-11 fl added integer support
+ * 1998-07-01 fl added "YCbCr" support
+ * 1998-07-02 fl added "RGBX" conversions (sort of)
+ * 1998-07-04 fl added floyd-steinberg dithering
+ * 1998-07-12 fl changed "YCrCb" to "YCbCr" (!)
+ * 1998-12-29 fl added basic "I;16" and "I;16B" conversions
+ * 1999-02-03 fl added "RGBa", and "BGR" conversions (experimental)
+ * 2003-09-26 fl added "LA" and "PA" conversions (experimental)
+ * 2005-05-05 fl fixed "P" to "1" threshold
+ * 2005-12-08 fl fixed palette memory leak in topalette
+ *
+ * Copyright (c) 1997-2005 by Secret Labs AB.
+ * Copyright (c) 1995-1997 by Fredrik Lundh.
+ *
+ * See the README file for details on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+#define CLIP(v) ((v) <= 0 ? 0 : (v) >= 255 ? 255 : (v))
+#define CLIP16(v) ((v) <= -32768 ? -32768 : (v) >= 32767 ? 32767 : (v))
+
+/* like (a * b + 127) / 255), but much faster on most platforms */
+#define MULDIV255(a, b, tmp)\
+ (tmp = (a) * (b) + 128, ((((tmp) >> 8) + (tmp)) >> 8))
+
+/* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
+#define L(rgb)\
+ ((INT32) (rgb)[0]*299 + (INT32) (rgb)[1]*587 + (INT32) (rgb)[2]*114)
+
+/* ------------------- */
+/* 1 (bit) conversions */
+/* ------------------- */
+
+static void
+bit2l(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++)
+ *out++ = (*in++ != 0) ? 255 : 0;
+}
+
+static void
+bit2rgb(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++) {
+ UINT8 v = (*in++ != 0) ? 255 : 0;
+ *out++ = v;
+ *out++ = v;
+ *out++ = v;
+ *out++ = 255;
+ }
+}
+
+static void
+bit2cmyk(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++) {
+ *out++ = 0;
+ *out++ = 0;
+ *out++ = 0;
+ *out++ = (*in++ != 0) ? 0 : 255;
+ }
+}
+
+static void
+bit2ycbcr(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++) {
+ *out++ = (*in++ != 0) ? 255 : 0;
+ *out++ = 128;
+ *out++ = 128;
+ *out++ = 255;
+ }
+}
+
+/* ----------------- */
+/* RGB/L conversions */
+/* ----------------- */
+
+static void
+l2bit(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++)
+ *out++ = (*in++ >= 128) ? 255 : 0;
+}
+
+static void
+l2la(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++) {
+ UINT8 v = *in++;
+ *out++ = v;
+ *out++ = v;
+ *out++ = v;
+ *out++ = 255;
+ }
+}
+
+static void
+l2rgb(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++) {
+ UINT8 v = *in++;
+ *out++ = v;
+ *out++ = v;
+ *out++ = v;
+ *out++ = 255;
+ }
+}
+
+static void
+la2l(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++, in += 4)
+ *out++ = in[0];
+}
+
+static void
+la2rgb(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++, in += 4) {
+ UINT8 v = in[0];
+ *out++ = v;
+ *out++ = v;
+ *out++ = v;
+ *out++ = in[3];
+ }
+}
+
+static void
+rgb2bit(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++, in += 4)
+ /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
+ *out++ = (L(in) >= 128000) ? 255 : 0;
+}
+
+static void
+rgb2l(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++, in += 4)
+ /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
+ *out++ = L(in) / 1000;
+}
+
+static void
+rgb2la(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++, in += 4, out += 4) {
+ /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
+ out[0] = out[1] = out[2] = L(in) / 1000;
+ out[3] = 255;
+ }
+}
+
+static void
+rgb2i(UINT8* out_, const UINT8* in, int xsize)
+{
+ int x;
+ INT32* out = (INT32*) out_;
+ for (x = 0; x < xsize; x++, in += 4)
+ *out++ = L(in) / 1000;
+}
+
+static void
+rgb2f(UINT8* out_, const UINT8* in, int xsize)
+{
+ int x;
+ FLOAT32* out = (FLOAT32*) out_;
+ for (x = 0; x < xsize; x++, in += 4)
+ *out++ = L(in) / 1000.0F;
+}
+
+static void
+rgb2bgr15(UINT8* out_, const UINT8* in, int xsize)
+{
+ int x;
+ UINT16* out = (UINT16*) out_;
+ for (x = 0; x < xsize; x++, in += 4)
+ *out++ =
+ ((((UINT16)in[0])<<8)&0x7c00) +
+ ((((UINT16)in[1])<<2)&0x03e0) +
+ ((((UINT16)in[2])>>3)&0x001f);
+}
+
+static void
+rgb2bgr16(UINT8* out_, const UINT8* in, int xsize)
+{
+ int x;
+ UINT16* out = (UINT16*) out_;
+ for (x = 0; x < xsize; x++, in += 4)
+ *out++ =
+ ((((UINT16)in[0])<<8)&0xf800) +
+ ((((UINT16)in[1])<<3)&0x07e0) +
+ ((((UINT16)in[2])>>3)&0x001f);
+}
+
+static void
+rgb2bgr24(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++, in += 4) {
+ *out++ = in[2];
+ *out++ = in[1];
+ *out++ = in[0];
+ }
+}
+
+/* ---------------- */
+/* RGBA conversions */
+/* ---------------- */
+
+static void
+rgb2rgba(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++) {
+ *out++ = *in++;
+ *out++ = *in++;
+ *out++ = *in++;
+ *out++ = 255; in++;
+ }
+}
+
+static void
+rgba2la(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++, in += 4, out += 4) {
+ /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
+ out[0] = out[1] = out[2] = L(in) / 1000;
+ out[3] = in[3];
+ }
+}
+
+static void
+rgba2rgb(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++) {
+ *out++ = *in++;
+ *out++ = *in++;
+ *out++ = *in++;
+ *out++ = 255; in++;
+ }
+}
+
+static void
+rgba2rgba(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ unsigned int alpha, tmp;
+ for (x = 0; x < xsize; x++) {
+ alpha = in[3];
+ *out++ = MULDIV255(*in++, alpha, tmp);
+ *out++ = MULDIV255(*in++, alpha, tmp);
+ *out++ = MULDIV255(*in++, alpha, tmp);
+ *out++ = *in++;
+ }
+}
+
+/* ---------------- */
+/* CMYK conversions */
+/* ---------------- */
+
+static void
+l2cmyk(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++) {
+ *out++ = 0;
+ *out++ = 0;
+ *out++ = 0;
+ *out++ = ~(*in++);
+ }
+}
+
+static void
+rgb2cmyk(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++) {
+ /* Note: no undercolour removal */
+ *out++ = ~(*in++);
+ *out++ = ~(*in++);
+ *out++ = ~(*in++);
+ *out++ = 0; in++;
+ }
+}
+
+static void
+cmyk2rgb(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++, in += 4) {
+ *out++ = CLIP(255 - (in[0] + in[3]));
+ *out++ = CLIP(255 - (in[1] + in[3]));
+ *out++ = CLIP(255 - (in[2] + in[3]));
+ *out++ = 255;
+ }
+}
+
+/* ------------- */
+/* I conversions */
+/* ------------- */
+
+static void
+bit2i(UINT8* out_, const UINT8* in, int xsize)
+{
+ int x;
+ INT32* out = (INT32*) out_;
+ for (x = 0; x < xsize; x++)
+ *out++ = (*in++ != 0) ? 255 : 0;
+}
+
+static void
+l2i(UINT8* out_, const UINT8* in, int xsize)
+{
+ int x;
+ INT32* out = (INT32*) out_;
+ for (x = 0; x < xsize; x++)
+ *out++ = (INT32) *in++;
+}
+
+static void
+i2l(UINT8* out, const UINT8* in_, int xsize)
+{
+ int x;
+ INT32* in = (INT32*) in_;
+ for (x = 0; x < xsize; x++, in++, out++) {
+ if (*in <= 0)
+ *out = 0;
+ else if (*in >= 255)
+ *out = 255;
+ else
+ *out = (UINT8) *in;
+ }
+}
+
+static void
+i2f(UINT8* out_, const UINT8* in_, int xsize)
+{
+ int x;
+ INT32* in = (INT32*) in_;
+ FLOAT32* out = (FLOAT32*) out_;
+ for (x = 0; x < xsize; x++)
+ *out++ = (FLOAT32) *in++;
+}
+
+/* ------------- */
+/* F conversions */
+/* ------------- */
+
+static void
+bit2f(UINT8* out_, const UINT8* in, int xsize)
+{
+ int x;
+ FLOAT32* out = (FLOAT32*) out_;
+ for (x = 0; x < xsize; x++)
+ *out++ = (*in++ != 0) ? 255.0F : 0.0F;
+}
+
+static void
+l2f(UINT8* out_, const UINT8* in, int xsize)
+{
+ int x;
+ FLOAT32* out = (FLOAT32*) out_;
+ for (x = 0; x < xsize; x++)
+ *out++ = (FLOAT32) *in++;
+}
+
+static void
+f2l(UINT8* out, const UINT8* in_, int xsize)
+{
+ int x;
+ FLOAT32* in = (FLOAT32*) in_;
+ for (x = 0; x < xsize; x++, in++, out++) {
+ if (*in <= 0.0)
+ *out = 0;
+ else if (*in >= 255.0)
+ *out = 255;
+ else
+ *out = (UINT8) *in;
+ }
+}
+
+static void
+f2i(UINT8* out_, const UINT8* in_, int xsize)
+{
+ int x;
+ FLOAT32* in = (FLOAT32*) in_;
+ INT32* out = (INT32*) out_;
+ for (x = 0; x < xsize; x++)
+ *out++ = (INT32) *in++;
+}
+
+/* ----------------- */
+/* YCbCr conversions */
+/* ----------------- */
+
+/* See ConvertYCbCr.c for RGB/YCbCr tables */
+
+static void
+l2ycbcr(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++) {
+ *out++ = *in++;
+ *out++ = 128;
+ *out++ = 128;
+ *out++ = 255;
+ }
+}
+
+static void
+ycbcr2l(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++, in += 4)
+ *out++ = in[0];
+}
+
+/* ------------------------- */
+/* I;16 (16-bit) conversions */
+/* ------------------------- */
+
+static void
+i2i16(UINT8* out, const UINT8* in_, int xsize)
+{
+ int x, v;
+ INT32* in = (INT32*) in_;
+ for (x = 0; x < xsize; x++, in++) {
+ v = CLIP16(*in);
+ *out++ = (UINT8) v;
+ *out++ = (UINT8) (v >> 8);
+ }
+}
+
+static void
+l2i16(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++, in++) {
+ *out++ = *in;
+ *out++ = 0;
+ }
+}
+
+static void
+i2i16b(UINT8* out, const UINT8* in_, int xsize)
+{
+ int x, v;
+ INT32* in = (INT32*) in_;
+ for (x = 0; x < xsize; x++, in++) {
+ v = CLIP16(*in);
+ *out++ = (UINT8) (v >> 8);
+ *out++ = (UINT8) v;
+ }
+}
+
+static void
+i162i(UINT8* out_, const UINT8* in, int xsize)
+{
+ int x;
+ INT32* out = (INT32*) out_;
+ for (x = 0; x < xsize; x++, in += 2)
+ *out++ = in[0] + ((int) in[1] << 8);
+}
+
+static void
+i162l(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++, in += 2)
+ if (in[1] != 0)
+ *out++ = 255;
+ else
+ *out++ = in[0];
+}
+
+static void
+i16b2i(UINT8* out_, const UINT8* in, int xsize)
+{
+ int x;
+ INT32* out = (INT32*) out_;
+ for (x = 0; x < xsize; x++, in += 2)
+ *out++ = ((int) in[0] << 8) + in[1];
+}
+
+
+static struct {
+ const char* from;
+ const char* to;
+ ImagingShuffler convert;
+} converters[] = {
+
+ { "1", "L", bit2l },
+ { "1", "I", bit2i },
+ { "1", "F", bit2f },
+ { "1", "RGB", bit2rgb },
+ { "1", "RGBA", bit2rgb },
+ { "1", "RGBX", bit2rgb },
+ { "1", "CMYK", bit2cmyk },
+ { "1", "YCbCr", bit2ycbcr },
+
+ { "L", "1", l2bit },
+ { "L", "LA", l2la },
+ { "L", "I", l2i },
+ { "L", "F", l2f },
+ { "L", "RGB", l2rgb },
+ { "L", "RGBA", l2rgb },
+ { "L", "RGBX", l2rgb },
+ { "L", "CMYK", l2cmyk },
+ { "L", "YCbCr", l2ycbcr },
+
+ { "LA", "L", la2l },
+ { "LA", "RGB", la2rgb },
+ { "LA", "RGBX", la2rgb },
+ { "LA", "RGBA", la2rgb },
+
+ { "I", "L", i2l },
+ { "I", "F", i2f },
+
+ { "F", "L", f2l },
+ { "F", "I", f2i },
+
+ { "RGB", "1", rgb2bit },
+ { "RGB", "L", rgb2l },
+ { "RGB", "LA", rgb2la },
+ { "RGB", "I", rgb2i },
+ { "RGB", "F", rgb2f },
+ { "RGB", "BGR;15", rgb2bgr15 },
+ { "RGB", "BGR;16", rgb2bgr16 },
+ { "RGB", "BGR;24", rgb2bgr24 },
+ { "RGB", "RGBA", rgb2rgba },
+ { "RGB", "RGBX", rgb2rgba },
+ { "RGB", "CMYK", rgb2cmyk },
+ { "RGB", "YCbCr", ImagingConvertRGB2YCbCr },
+
+ { "RGBA", "1", rgb2bit },
+ { "RGBA", "L", rgb2l },
+ { "RGBA", "LA", rgba2la },
+ { "RGBA", "I", rgb2i },
+ { "RGBA", "F", rgb2f },
+ { "RGBA", "RGB", rgba2rgb },
+ { "RGBA", "RGBa", rgba2rgba },
+ { "RGBA", "RGBX", rgb2rgba },
+ { "RGBA", "CMYK", rgb2cmyk },
+ { "RGBA", "YCbCr", ImagingConvertRGB2YCbCr },
+
+ { "RGBX", "1", rgb2bit },
+ { "RGBX", "L", rgb2l },
+ { "RGBA", "I", rgb2i },
+ { "RGBA", "F", rgb2f },
+ { "RGBX", "RGB", rgba2rgb },
+ { "RGBX", "CMYK", rgb2cmyk },
+ { "RGBX", "YCbCr", ImagingConvertRGB2YCbCr },
+
+ { "CMYK", "RGB", cmyk2rgb },
+ { "CMYK", "RGBA", cmyk2rgb },
+ { "CMYK", "RGBX", cmyk2rgb },
+
+ { "YCbCr", "L", ycbcr2l },
+ { "YCbCr", "RGB", ImagingConvertYCbCr2RGB },
+
+ { "I", "I;16", i2i16 },
+ { "I;16", "I", i162i },
+ { "I", "I;16B", i2i16b },
+ { "I;16B", "I", i16b2i },
+
+ { "L", "I;16", l2i16 },
+ { "I;16", "L", i162l },
+
+ { NULL }
+};
+
+/* FIXME: translate indexed versions to pointer versions below this line */
+
+/* ------------------- */
+/* Palette conversions */
+/* ------------------- */
+
+static void
+p2bit(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
+{
+ int x;
+ /* FIXME: precalculate greyscale palette? */
+ for (x = 0; x < xsize; x++)
+ *out++ = (L(&palette[in[x]*4]) >= 128000) ? 255 : 0;
+}
+
+static void
+p2l(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
+{
+ int x;
+ /* FIXME: precalculate greyscale palette? */
+ for (x = 0; x < xsize; x++)
+ *out++ = L(&palette[in[x]*4]) / 1000;
+}
+
+static void
+pa2la(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
+{
+ int x;
+ /* FIXME: precalculate greyscale palette? */
+ for (x = 0; x < xsize; x++, in += 2) {
+ *out++ = L(&palette[in[0]*4]) / 1000;
+ *out++ = in[1];
+ }
+}
+
+static void
+p2i(UINT8* out_, const UINT8* in, int xsize, const UINT8* palette)
+{
+ int x;
+ INT32* out = (INT32*) out_;
+ for (x = 0; x < xsize; x++)
+ *out++ = L(&palette[in[x]*4]) / 1000;
+}
+
+static void
+p2f(UINT8* out_, const UINT8* in, int xsize, const UINT8* palette)
+{
+ int x;
+ FLOAT32* out = (FLOAT32*) out_;
+ for (x = 0; x < xsize; x++)
+ *out++ = L(&palette[in[x]*4]) / 1000.0F;
+}
+
+static void
+p2rgb(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
+{
+ int x;
+ for (x = 0; x < xsize; x++) {
+ const UINT8* rgb = &palette[*in++ * 4];
+ *out++ = rgb[0];
+ *out++ = rgb[1];
+ *out++ = rgb[2];
+ *out++ = 255;
+ }
+}
+
+static void
+p2rgba(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
+{
+ int x;
+ for (x = 0; x < xsize; x++) {
+ const UINT8* rgba = &palette[*in++ * 4];
+ *out++ = rgba[0];
+ *out++ = rgba[1];
+ *out++ = rgba[2];
+ *out++ = rgba[3];
+ }
+}
+
+static void
+pa2rgba(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
+{
+ int x;
+ for (x = 0; x < xsize; x++, in += 4) {
+ const UINT8* rgb = &palette[in[0] * 4];
+ *out++ = rgb[0];
+ *out++ = rgb[1];
+ *out++ = rgb[2];
+ *out++ = in[3];
+ }
+}
+
+static void
+p2cmyk(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
+{
+ p2rgb(out, in, xsize, palette);
+ rgb2cmyk(out, out, xsize);
+}
+
+static void
+p2ycbcr(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
+{
+ p2rgb(out, in, xsize, palette);
+ ImagingConvertRGB2YCbCr(out, out, xsize);
+}
+
+static Imaging
+frompalette(Imaging imOut, Imaging imIn, const char *mode)
+{
+ ImagingSectionCookie cookie;
+ int alpha;
+ int y;
+ void (*convert)(UINT8*, const UINT8*, int, const UINT8*);
+
+ /* Map palette image to L, RGB, RGBA, or CMYK */
+
+ if (!imIn->palette)
+ return (Imaging) ImagingError_ValueError("no palette");
+
+ alpha = !strcmp(imIn->mode, "PA");
+
+ if (strcmp(mode, "1") == 0)
+ convert = p2bit;
+ else if (strcmp(mode, "L") == 0)
+ convert = p2l;
+ else if (strcmp(mode, "LA") == 0)
+ convert = (alpha) ? pa2la : p2l;
+ else if (strcmp(mode, "I") == 0)
+ convert = p2i;
+ else if (strcmp(mode, "F") == 0)
+ convert = p2f;
+ else if (strcmp(mode, "RGB") == 0)
+ convert = p2rgb;
+ else if (strcmp(mode, "RGBA") == 0)
+ convert = (alpha) ? pa2rgba : p2rgba;
+ else if (strcmp(mode, "RGBX") == 0)
+ convert = p2rgba;
+ else if (strcmp(mode, "CMYK") == 0)
+ convert = p2cmyk;
+ else if (strcmp(mode, "YCbCr") == 0)
+ convert = p2ycbcr;
+ else
+ return (Imaging) ImagingError_ValueError("conversion not supported");
+
+ imOut = ImagingNew2(mode, imOut, imIn);
+ if (!imOut)
+ return NULL;
+
+ ImagingSectionEnter(&cookie);
+ for (y = 0; y < imIn->ysize; y++)
+ (*convert)((UINT8*) imOut->image[y], (UINT8*) imIn->image[y],
+ imIn->xsize, imIn->palette->palette);
+ ImagingSectionLeave(&cookie);
+
+ return imOut;
+}
+
+static Imaging
+topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
+{
+ ImagingSectionCookie cookie;
+ int x, y;
+ ImagingPalette palette = inpalette;;
+
+ /* Map L or RGB/RGBX/RGBA to palette image */
+ if (strcmp(imIn->mode, "L") != 0 && strncmp(imIn->mode, "RGB", 3) != 0)
+ return (Imaging) ImagingError_ValueError("conversion not supported");
+
+ if (palette == NULL) {
+ /* FIXME: make user configurable */
+ if (imIn->bands == 1)
+ palette = ImagingPaletteNew("RGB"); /* Initialised to grey ramp */
+ else
+ palette = ImagingPaletteNewBrowser(); /* Standard colour cube */
+ }
+
+ if (!palette)
+ return (Imaging) ImagingError_ValueError("no palette");
+
+ imOut = ImagingNew2("P", imOut, imIn);
+ if (!imOut) {
+ if (palette != inpalette)
+ ImagingPaletteDelete(palette);
+ return NULL;
+ }
+
+ ImagingPaletteDelete(imOut->palette);
+ imOut->palette = ImagingPaletteDuplicate(palette);
+
+ if (imIn->bands == 1) {
+ /* greyscale image */
+
+ /* Greyscale palette: copy data as is */
+ ImagingSectionEnter(&cookie);
+ for (y = 0; y < imIn->ysize; y++)
+ memcpy(imOut->image[y], imIn->image[y], imIn->linesize);
+ ImagingSectionLeave(&cookie);
+
+ } else {
+ /* colour image */
+
+ /* Create mapping cache */
+ if (ImagingPaletteCachePrepare(palette) < 0) {
+ ImagingDelete(imOut);
+ if (palette != inpalette)
+ ImagingPaletteDelete(palette);
+ return NULL;
+ }
+
+ if (dither) {
+ /* floyd-steinberg dither */
+
+ int* errors;
+ errors = calloc(imIn->xsize + 1, sizeof(int) * 3);
+ if (!errors) {
+ ImagingDelete(imOut);
+ ImagingError_MemoryError();
+ return NULL;
+ }
+
+ /* Map each pixel to the nearest palette entry */
+ ImagingSectionEnter(&cookie);
+ for (y = 0; y < imIn->ysize; y++) {
+ int r, r0, r1, r2;
+ int g, g0, g1, g2;
+ int b, b0, b1, b2;
+ UINT8* in = (UINT8*) imIn->image[y];
+ UINT8* out = imOut->image8[y];
+ int* e = errors;
+
+ r = r0 = r1 = 0;
+ g = g0 = g1 = 0;
+ b = b0 = b1 = b2 = 0;
+
+ for (x = 0; x < imIn->xsize; x++, in += 4) {
+ int d2;
+ INT16* cache;
+
+ r = CLIP(in[0] + (r + e[3+0])/16);
+ g = CLIP(in[1] + (g + e[3+1])/16);
+ b = CLIP(in[2] + (b + e[3+2])/16);
+
+ /* get closest colour */
+ cache = &ImagingPaletteCache(palette, r, g, b);
+ if (cache[0] == 0x100)
+ ImagingPaletteCacheUpdate(palette, r, g, b);
+ out[x] = (UINT8) cache[0];
+
+ r -= (int) palette->palette[cache[0]*4];
+ g -= (int) palette->palette[cache[0]*4+1];
+ b -= (int) palette->palette[cache[0]*4+2];
+
+ /* propagate errors (don't ask ;-) */
+ r2 = r; d2 = r + r; r += d2; e[0] = r + r0;
+ r += d2; r0 = r + r1; r1 = r2; r += d2;
+ g2 = g; d2 = g + g; g += d2; e[1] = g + g0;
+ g += d2; g0 = g + g1; g1 = g2; g += d2;
+ b2 = b; d2 = b + b; b += d2; e[2] = b + b0;
+ b += d2; b0 = b + b1; b1 = b2; b += d2;
+
+ e += 3;
+
+ }
+
+ e[0] = b0;
+ e[1] = b1;
+ e[2] = b2;
+
+ }
+ ImagingSectionLeave(&cookie);
+ free(errors);
+
+ } else {
+
+ /* closest colour */
+ ImagingSectionEnter(&cookie);
+ for (y = 0; y < imIn->ysize; y++) {
+ int r, g, b;
+ UINT8* in = (UINT8*) imIn->image[y];
+ UINT8* out = imOut->image8[y];
+
+ for (x = 0; x < imIn->xsize; x++, in += 4) {
+ INT16* cache;
+
+ r = in[0]; g = in[1]; b = in[2];
+
+ /* get closest colour */
+ cache = &ImagingPaletteCache(palette, r, g, b);
+ if (cache[0] == 0x100)
+ ImagingPaletteCacheUpdate(palette, r, g, b);
+ out[x] = (UINT8) cache[0];
+
+ }
+ }
+ ImagingSectionLeave(&cookie);
+
+ }
+ if (inpalette != palette)
+ ImagingPaletteCacheDelete(palette);
+ }
+
+ if (inpalette != palette)
+ ImagingPaletteDelete(palette);
+
+ return imOut;
+}
+
+static Imaging
+tobilevel(Imaging imOut, Imaging imIn, int dither)
+{
+ ImagingSectionCookie cookie;
+ int x, y;
+ int* errors;
+
+ /* Map L or RGB to dithered 1 image */
+ if (strcmp(imIn->mode, "L") != 0 && strcmp(imIn->mode, "RGB") != 0)
+ return (Imaging) ImagingError_ValueError("conversion not supported");
+
+ imOut = ImagingNew2("1", imOut, imIn);
+ if (!imOut)
+ return NULL;
+
+ errors = calloc(imIn->xsize + 1, sizeof(int));
+ if (!errors) {
+ ImagingDelete(imOut);
+ ImagingError_MemoryError();
+ return NULL;
+ }
+
+ if (imIn->bands == 1) {
+
+ /* map each pixel to black or white, using error diffusion */
+ ImagingSectionEnter(&cookie);
+ for (y = 0; y < imIn->ysize; y++) {
+ int l, l0, l1, l2, d2;
+ UINT8* in = (UINT8*) imIn->image[y];
+ UINT8* out = imOut->image8[y];
+
+ l = l0 = l1 = 0;
+
+ for (x = 0; x < imIn->xsize; x++) {
+
+ /* pick closest colour */
+ l = CLIP(in[x] + (l + errors[x+1])/16);
+ out[x] = (l > 128) ? 255 : 0;
+
+ /* propagate errors */
+ l -= (int) out[x];
+ l2 = l; d2 = l + l; l += d2; errors[x] = l + l0;
+ l += d2; l0 = l + l1; l1 = l2; l += d2;
+ }
+
+ errors[x] = l0;
+
+ }
+ ImagingSectionLeave(&cookie);
+
+ } else {
+
+ /* map each pixel to black or white, using error diffusion */
+ ImagingSectionEnter(&cookie);
+ for (y = 0; y < imIn->ysize; y++) {
+ int l, l0, l1, l2, d2;
+ UINT8* in = (UINT8*) imIn->image[y];
+ UINT8* out = imOut->image8[y];
+
+ l = l0 = l1 = 0;
+
+ for (x = 0; x < imIn->xsize; x++, in += 4) {
+
+ /* pick closest colour */
+ l = CLIP(L(in)/1000 + (l + errors[x+1])/16);
+ out[x] = (l > 128) ? 255 : 0;
+
+ /* propagate errors */
+ l -= (int) out[x];
+ l2 = l; d2 = l + l; l += d2; errors[x] = l + l0;
+ l += d2; l0 = l + l1; l1 = l2; l += d2;
+
+ }
+
+ errors[x] = l0;
+
+ }
+ ImagingSectionLeave(&cookie);
+ }
+
+ free(errors);
+
+ return imOut;
+}
+
+
+static Imaging
+convert(Imaging imOut, Imaging imIn, const char *mode,
+ ImagingPalette palette, int dither)
+{
+ ImagingSectionCookie cookie;
+ ImagingShuffler convert;
+ int y;
+
+ if (!imIn)
+ return (Imaging) ImagingError_ModeError();
+
+ if (!mode) {
+ /* Map palette image to full depth */
+ if (!imIn->palette)
+ return (Imaging) ImagingError_ModeError();
+ mode = imIn->palette->mode;
+ } else
+ /* Same mode? */
+ if (!strcmp(imIn->mode, mode))
+ return ImagingCopy2(imOut, imIn);
+
+
+ /* test for special conversions */
+
+ if (strcmp(imIn->mode, "P") == 0 || strcmp(imIn->mode, "PA") == 0)
+ return frompalette(imOut, imIn, mode);
+
+ if (strcmp(mode, "P") == 0)
+ return topalette(imOut, imIn, palette, dither);
+
+ if (dither && strcmp(mode, "1") == 0)
+ return tobilevel(imOut, imIn, dither);
+
+
+ /* standard conversion machinery */
+
+ convert = NULL;
+
+ for (y = 0; converters[y].from; y++)
+ if (!strcmp(imIn->mode, converters[y].from) &&
+ !strcmp(mode, converters[y].to)) {
+ convert = converters[y].convert;
+ break;
+ }
+
+ if (!convert)
+#ifdef notdef
+ return (Imaging) ImagingError_ValueError("conversion not supported");
+#else
+ {
+ static char buf[256];
+ sprintf(buf, "conversion from %s to %s not supported", imIn->mode, mode);
+ return (Imaging) ImagingError_ValueError(buf);
+ }
+#endif
+
+ imOut = ImagingNew2(mode, imOut, imIn);
+ if (!imOut)
+ return NULL;
+
+ ImagingSectionEnter(&cookie);
+ for (y = 0; y < imIn->ysize; y++)
+ (*convert)((UINT8*) imOut->image[y], (UINT8*) imIn->image[y],
+ imIn->xsize);
+ ImagingSectionLeave(&cookie);
+
+ return imOut;
+}
+
+Imaging
+ImagingConvert(Imaging imIn, const char *mode,
+ ImagingPalette palette, int dither)
+{
+ return convert(NULL, imIn, mode, palette, dither);
+}
+
+Imaging
+ImagingConvert2(Imaging imOut, Imaging imIn)
+{
+ return convert(imOut, imIn, imOut->mode, NULL, 0);
+}