/* * The Python Imaging Library * $Id$ * * 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++ = (float) 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])<<7)&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 I_I16L(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 I_I16B(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 I16L_I(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 I16B_I(UINT8* out_, const UINT8* in, int xsize) { int x; INT32* out = (INT32*) out_; for (x = 0; x < xsize; x++, in += 2) *out++ = in[1] + ((int) in[0] << 8); } static void L_I16L(UINT8* out, const UINT8* in, int xsize) { int x; for (x = 0; x < xsize; x++, in++) { *out++ = *in; *out++ = 0; } } static void L_I16B(UINT8* out, const UINT8* in, int xsize) { int x; for (x = 0; x < xsize; x++, in++) { *out++ = 0; *out++ = *in; } } static void I16L_L(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 I16B_L(UINT8* out, const UINT8* in, int xsize) { int x; for (x = 0; x < xsize; x++, in += 2) if (in[0] != 0) *out++ = 255; else *out++ = 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", I_I16L }, { "I;16", "I", I16L_I }, { "L", "I;16", L_I16L }, { "I;16", "L", I16L_L }, { "I", "I;16L", I_I16L }, { "I;16L", "I", I16L_I }, { "I", "I;16B", I_I16B }, { "I;16B", "I", I16B_I }, { "L", "I;16L", L_I16L }, { "I;16L", "L", I16L_L }, { "L", "I;16B", L_I16B }, { "I;16B", "L", I16B_L }, { 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++ = (float) 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); return ImagingError_MemoryError(); } /* 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); return ImagingError_MemoryError(); } 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]; /* FIXME: may overflow if mode is too large */ 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); } Imaging ImagingConvertInPlace(Imaging imIn, const char* mode) { ImagingSectionCookie cookie; ImagingShuffler convert; int y; /* limited support for inplace conversion */ if (strcmp(imIn->mode, "L") == 0 && strcmp(mode, "1") == 0) convert = l2bit; else if (strcmp(imIn->mode, "1") == 0 && strcmp(mode, "L") == 0) convert = bit2l; else return ImagingError_ModeError(); ImagingSectionEnter(&cookie); for (y = 0; y < imIn->ysize; y++) (*convert)((UINT8*) imIn->image[y], (UINT8*) imIn->image[y], imIn->xsize); ImagingSectionLeave(&cookie); return imIn; }