Improved PA conversion

This commit is contained in:
Andrew Murray 2019-03-29 23:13:07 +11:00
parent 2adfea9b4e
commit 819b8acd26
2 changed files with 120 additions and 26 deletions

View File

@ -12,7 +12,8 @@ class TestImageConvert(PillowTestCase):
self.assertEqual(out.mode, mode) self.assertEqual(out.mode, mode)
self.assertEqual(out.size, im.size) self.assertEqual(out.size, im.size)
modes = "1", "L", "I", "F", "RGB", "RGBA", "RGBX", "CMYK", "YCbCr" modes = ("1", "L", "LA", "P", "PA", "I", "F",
"RGB", "RGBA", "RGBX", "CMYK", "YCbCr")
for mode in modes: for mode in modes:
im = hopper(mode) im = hopper(mode)

View File

@ -913,6 +913,15 @@ p2bit(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
*out++ = (L(&palette[in[x]*4]) >= 128000) ? 255 : 0; *out++ = (L(&palette[in[x]*4]) >= 128000) ? 255 : 0;
} }
static void
pa2bit(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
{
int x;
/* FIXME: precalculate greyscale palette? */
for (x = 0; x < xsize; x++, in += 4)
*out++ = (L(&palette[in[0]*4]) >= 128000) ? 255 : 0;
}
static void static void
p2l(UINT8* out, const UINT8* in, int xsize, const UINT8* palette) p2l(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
{ {
@ -922,6 +931,28 @@ p2l(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
*out++ = L(&palette[in[x]*4]) / 1000; *out++ = L(&palette[in[x]*4]) / 1000;
} }
static void
pa2l(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
{
int x;
/* FIXME: precalculate greyscale palette? */
for (x = 0; x < xsize; x++, in += 4)
*out++ = L(&palette[in[0]*4]) / 1000;
}
static void
p2pa(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
{
int x;
for (x = 0; x < xsize; x++, in+=4) {
const UINT8* rgba = &palette[in[0] * 4];
*out++ = in[0];
*out++ = in[0];
*out++ = in[0];
*out++ = rgba[3];
}
}
static void static void
p2la(UINT8* out, const UINT8* in, int xsize, const UINT8* palette) p2la(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
{ {
@ -939,9 +970,9 @@ pa2la(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
{ {
int x; int x;
/* FIXME: precalculate greyscale palette? */ /* FIXME: precalculate greyscale palette? */
for (x = 0; x < xsize; x++, in += 2) { for (x = 0; x < xsize; x++, in += 4, out += 4) {
*out++ = L(&palette[in[0]*4]) / 1000; out[0] = out[1] = out[2] = L(&palette[in[0]*4]) / 1000;
*out++ = in[1]; out[3] = in[3];
} }
} }
@ -954,6 +985,15 @@ p2i(UINT8* out_, const UINT8* in, int xsize, const UINT8* palette)
*out++ = L(&palette[in[x]*4]) / 1000; *out++ = L(&palette[in[x]*4]) / 1000;
} }
static void
pa2i(UINT8* out_, const UINT8* in, int xsize, const UINT8* palette)
{
int x;
INT32* out = (INT32*) out_;
for (x = 0; x < xsize; x++, in += 4)
*out++ = L(&palette[in[0]*4]) / 1000;
}
static void static void
p2f(UINT8* out_, const UINT8* in, int xsize, const UINT8* palette) p2f(UINT8* out_, const UINT8* in, int xsize, const UINT8* palette)
{ {
@ -963,6 +1003,15 @@ p2f(UINT8* out_, const UINT8* in, int xsize, const UINT8* palette)
*out++ = (float) L(&palette[in[x]*4]) / 1000.0F; *out++ = (float) L(&palette[in[x]*4]) / 1000.0F;
} }
static void
pa2f(UINT8* out_, const UINT8* in, int xsize, const UINT8* palette)
{
int x;
FLOAT32* out = (FLOAT32*) out_;
for (x = 0; x < xsize; x++, in += 4)
*out++ = (float) L(&palette[in[0]*4]) / 1000.0F;
}
static void static void
p2rgb(UINT8* out, const UINT8* in, int xsize, const UINT8* palette) p2rgb(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
{ {
@ -976,6 +1025,19 @@ p2rgb(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
} }
} }
static void
pa2rgb(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++ = 255;
}
}
static void static void
p2rgba(UINT8* out, const UINT8* in, int xsize, const UINT8* palette) p2rgba(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
{ {
@ -1009,6 +1071,13 @@ p2cmyk(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
rgb2cmyk(out, out, xsize); rgb2cmyk(out, out, xsize);
} }
static void
pa2cmyk(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
{
pa2rgb(out, in, xsize, palette);
rgb2cmyk(out, out, xsize);
}
static void static void
p2ycbcr(UINT8* out, const UINT8* in, int xsize, const UINT8* palette) p2ycbcr(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
{ {
@ -1016,6 +1085,13 @@ p2ycbcr(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
ImagingConvertRGB2YCbCr(out, out, xsize); ImagingConvertRGB2YCbCr(out, out, xsize);
} }
static void
pa2ycbcr(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
{
pa2rgb(out, in, xsize, palette);
ImagingConvertRGB2YCbCr(out, out, xsize);
}
static Imaging static Imaging
frompalette(Imaging imOut, Imaging imIn, const char *mode) frompalette(Imaging imOut, Imaging imIn, const char *mode)
{ {
@ -1032,27 +1108,27 @@ frompalette(Imaging imOut, Imaging imIn, const char *mode)
alpha = !strcmp(imIn->mode, "PA"); alpha = !strcmp(imIn->mode, "PA");
if (strcmp(mode, "1") == 0) if (strcmp(mode, "1") == 0)
convert = p2bit; convert = alpha ? pa2bit : p2bit;
else if (strcmp(mode, "L") == 0) else if (strcmp(mode, "L") == 0)
convert = p2l; convert = alpha ? pa2l : p2l;
else if (strcmp(mode, "LA") == 0) else if (strcmp(mode, "LA") == 0)
convert = (alpha) ? pa2la : p2la; convert = alpha ? pa2la : p2la;
else if (strcmp(mode, "PA") == 0) else if (strcmp(mode, "PA") == 0)
convert = l2la; convert = p2pa;
else if (strcmp(mode, "I") == 0) else if (strcmp(mode, "I") == 0)
convert = p2i; convert = alpha ? pa2i : p2i;
else if (strcmp(mode, "F") == 0) else if (strcmp(mode, "F") == 0)
convert = p2f; convert = alpha ? pa2f : p2f;
else if (strcmp(mode, "RGB") == 0) else if (strcmp(mode, "RGB") == 0)
convert = p2rgb; convert = alpha ? pa2rgb : p2rgb;
else if (strcmp(mode, "RGBA") == 0) else if (strcmp(mode, "RGBA") == 0)
convert = (alpha) ? pa2rgba : p2rgba; convert = alpha ? pa2rgba : p2rgba;
else if (strcmp(mode, "RGBX") == 0) else if (strcmp(mode, "RGBX") == 0)
convert = p2rgba; convert = alpha ? pa2rgba : p2rgba;
else if (strcmp(mode, "CMYK") == 0) else if (strcmp(mode, "CMYK") == 0)
convert = p2cmyk; convert = alpha ? pa2cmyk : p2cmyk;
else if (strcmp(mode, "YCbCr") == 0) else if (strcmp(mode, "YCbCr") == 0)
convert = p2ycbcr; convert = alpha ? pa2ycbcr : p2ycbcr;
else else
return (Imaging) ImagingError_ValueError("conversion not supported"); return (Imaging) ImagingError_ValueError("conversion not supported");
@ -1073,9 +1149,10 @@ frompalette(Imaging imOut, Imaging imIn, const char *mode)
#pragma optimize("", off) #pragma optimize("", off)
#endif #endif
static Imaging static Imaging
topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither) topalette(Imaging imOut, Imaging imIn, const char *mode, ImagingPalette inpalette, int dither)
{ {
ImagingSectionCookie cookie; ImagingSectionCookie cookie;
int alpha;
int x, y; int x, y;
ImagingPalette palette = inpalette;; ImagingPalette palette = inpalette;;
@ -1083,6 +1160,8 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
if (strcmp(imIn->mode, "L") != 0 && strncmp(imIn->mode, "RGB", 3) != 0) if (strcmp(imIn->mode, "L") != 0 && strncmp(imIn->mode, "RGB", 3) != 0)
return (Imaging) ImagingError_ValueError("conversion not supported"); return (Imaging) ImagingError_ValueError("conversion not supported");
alpha = !strcmp(mode, "PA");
if (palette == NULL) { if (palette == NULL) {
/* FIXME: make user configurable */ /* FIXME: make user configurable */
if (imIn->bands == 1) if (imIn->bands == 1)
@ -1094,7 +1173,7 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
if (!palette) if (!palette)
return (Imaging) ImagingError_ValueError("no palette"); return (Imaging) ImagingError_ValueError("no palette");
imOut = ImagingNew2Dirty("P", imOut, imIn); imOut = ImagingNew2Dirty(mode, imOut, imIn);
if (!imOut) { if (!imOut) {
if (palette != inpalette) if (palette != inpalette)
ImagingPaletteDelete(palette); ImagingPaletteDelete(palette);
@ -1109,8 +1188,13 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
/* Greyscale palette: copy data as is */ /* Greyscale palette: copy data as is */
ImagingSectionEnter(&cookie); ImagingSectionEnter(&cookie);
for (y = 0; y < imIn->ysize; y++) for (y = 0; y < imIn->ysize; y++) {
memcpy(imOut->image[y], imIn->image[y], imIn->linesize); if (alpha) {
l2la((UINT8*) imOut->image[y], (UINT8*) imIn->image[y], imIn->xsize);
} else {
memcpy(imOut->image[y], imIn->image[y], imIn->linesize);
}
}
ImagingSectionLeave(&cookie); ImagingSectionLeave(&cookie);
} else { } else {
@ -1141,7 +1225,7 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
int g, g0, g1, g2; int g, g0, g1, g2;
int b, b0, b1, b2; int b, b0, b1, b2;
UINT8* in = (UINT8*) imIn->image[y]; UINT8* in = (UINT8*) imIn->image[y];
UINT8* out = imOut->image8[y]; UINT8* out = alpha ? (UINT8*) imOut->image32[y] : imOut->image8[y];
int* e = errors; int* e = errors;
r = r0 = r1 = 0; r = r0 = r1 = 0;
@ -1160,7 +1244,12 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
cache = &ImagingPaletteCache(palette, r, g, b); cache = &ImagingPaletteCache(palette, r, g, b);
if (cache[0] == 0x100) if (cache[0] == 0x100)
ImagingPaletteCacheUpdate(palette, r, g, b); ImagingPaletteCacheUpdate(palette, r, g, b);
out[x] = (UINT8) cache[0]; if (alpha) {
out[x*4] = out[x*4+1] = out[x*4+2] = (UINT8) cache[0];
out[x*4+3] = 255;
} else {
out[x] = (UINT8) cache[0];
}
r -= (int) palette->palette[cache[0]*4]; r -= (int) palette->palette[cache[0]*4];
g -= (int) palette->palette[cache[0]*4+1]; g -= (int) palette->palette[cache[0]*4+1];
@ -1193,7 +1282,7 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
for (y = 0; y < imIn->ysize; y++) { for (y = 0; y < imIn->ysize; y++) {
int r, g, b; int r, g, b;
UINT8* in = (UINT8*) imIn->image[y]; UINT8* in = (UINT8*) imIn->image[y];
UINT8* out = imOut->image8[y]; UINT8* out = alpha ? (UINT8*) imOut->image32[y] : imOut->image8[y];
for (x = 0; x < imIn->xsize; x++, in += 4) { for (x = 0; x < imIn->xsize; x++, in += 4) {
INT16* cache; INT16* cache;
@ -1204,8 +1293,12 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
cache = &ImagingPaletteCache(palette, r, g, b); cache = &ImagingPaletteCache(palette, r, g, b);
if (cache[0] == 0x100) if (cache[0] == 0x100)
ImagingPaletteCacheUpdate(palette, r, g, b); ImagingPaletteCacheUpdate(palette, r, g, b);
out[x] = (UINT8) cache[0]; if (alpha) {
out[x*4] = out[x*4+1] = out[x*4+2] = (UINT8) cache[0];
out[x*4+3] = 255;
} else {
out[x] = (UINT8) cache[0];
}
} }
} }
ImagingSectionLeave(&cookie); ImagingSectionLeave(&cookie);
@ -1335,8 +1428,8 @@ convert(Imaging imOut, Imaging imIn, const char *mode,
if (strcmp(imIn->mode, "P") == 0 || strcmp(imIn->mode, "PA") == 0) if (strcmp(imIn->mode, "P") == 0 || strcmp(imIn->mode, "PA") == 0)
return frompalette(imOut, imIn, mode); return frompalette(imOut, imIn, mode);
if (strcmp(mode, "P") == 0) if (strcmp(mode, "P") == 0 || strcmp(mode, "PA") == 0)
return topalette(imOut, imIn, palette, dither); return topalette(imOut, imIn, mode, palette, dither);
if (dither && strcmp(mode, "1") == 0) if (dither && strcmp(mode, "1") == 0)
return tobilevel(imOut, imIn, dither); return tobilevel(imOut, imIn, dither);