Merge pull request #3728 from radarhere/pa_mode

Improvements to PA and LA conversion and palette operations
This commit is contained in:
Hugo 2019-03-30 12:00:06 +02:00 committed by GitHub
commit 69ed0aa5b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 173 additions and 38 deletions

View File

@ -12,7 +12,8 @@ class TestImageConvert(PillowTestCase):
self.assertEqual(out.mode, mode)
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:
im = hopper(mode)

View File

@ -14,8 +14,10 @@ class TestImagePutPalette(PillowTestCase):
return im.mode, p[:10]
return im.mode
self.assertRaises(ValueError, palette, "1")
self.assertEqual(palette("L"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
self.assertEqual(palette("P"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
for mode in ["L", "LA", "P", "PA"]:
self.assertEqual(palette(mode),
("PA" if "A" in mode else "P",
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
self.assertRaises(ValueError, palette, "I")
self.assertRaises(ValueError, palette, "F")
self.assertRaises(ValueError, palette, "RGB")

View File

@ -1619,10 +1619,10 @@ class Image(object):
def putpalette(self, data, rawmode="RGB"):
"""
Attaches a palette to this image. The image must be a "P" or
"L" image, and the palette sequence must contain 768 integer
values, where each group of three values represent the red,
green, and blue values for the corresponding pixel
Attaches a palette to this image. The image must be a "P",
"PA", "L" or "LA" image, and the palette sequence must contain
768 integer values, where each group of three values represent
the red, green, and blue values for the corresponding pixel
index. Instead of an integer sequence, you can use an 8-bit
string.
@ -1631,7 +1631,7 @@ class Image(object):
"""
from . import ImagePalette
if self.mode not in ("L", "P"):
if self.mode not in ("L", "LA", "P", "PA"):
raise ValueError("illegal image mode")
self.load()
if isinstance(data, ImagePalette.ImagePalette):
@ -1643,7 +1643,7 @@ class Image(object):
else:
data = "".join(chr(x) for x in data)
palette = ImagePalette.raw(rawmode, data)
self.mode = "P"
self.mode = "PA" if "A" in self.mode else "P"
self.palette = palette
self.palette.mode = "RGB"
self.load() # install new palette

View File

@ -1569,7 +1569,8 @@ _putpalette(ImagingObject* self, PyObject* args)
if (!PyArg_ParseTuple(args, "s"PY_ARG_BYTES_LENGTH, &rawmode, &palette, &palettesize))
return NULL;
if (strcmp(self->image->mode, "L") != 0 && strcmp(self->image->mode, "P")) {
if (strcmp(self->image->mode, "L") && strcmp(self->image->mode, "LA") &&
strcmp(self->image->mode, "P") && strcmp(self->image->mode, "PA")) {
PyErr_SetString(PyExc_ValueError, wrong_mode);
return NULL;
}

View File

@ -517,6 +517,18 @@ l2cmyk(UINT8* out, const UINT8* in, int xsize)
}
}
static void
la2cmyk(UINT8* out, const UINT8* in, int xsize)
{
int x;
for (x = 0; x < xsize; x++, in += 4) {
*out++ = 0;
*out++ = 0;
*out++ = 0;
*out++ = ~(in[0]);
}
}
static void
rgb2cmyk(UINT8* out, const UINT8* in, int xsize)
{
@ -673,6 +685,18 @@ l2ycbcr(UINT8* out, const UINT8* in, int xsize)
}
}
static void
la2ycbcr(UINT8* out, const UINT8* in, int xsize)
{
int x;
for (x = 0; x < xsize; x++, in += 4) {
*out++ = in[0];
*out++ = 128;
*out++ = 128;
*out++ = 255;
}
}
static void
ycbcr2l(UINT8* out, const UINT8* in, int xsize)
{
@ -681,6 +705,16 @@ ycbcr2l(UINT8* out, const UINT8* in, int xsize)
*out++ = in[0];
}
static void
ycbcr2la(UINT8* out, const UINT8* in, int xsize)
{
int x;
for (x = 0; x < xsize; x++, in += 4, out += 4) {
out[0] = out[1] = out[2] = in[0];
out[3] = 255;
}
}
/* ------------------------- */
/* I;16 (16-bit) conversions */
/* ------------------------- */
@ -818,8 +852,10 @@ static struct {
{ "LA", "L", la2l },
{ "LA", "La", lA2la },
{ "LA", "RGB", la2rgb },
{ "LA", "RGBX", la2rgb },
{ "LA", "RGBA", la2rgb },
{ "LA", "RGBX", la2rgb },
{ "LA", "CMYK", la2cmyk },
{ "LA", "YCbCr", la2ycbcr },
{ "La", "LA", la2lA },
@ -861,8 +897,9 @@ static struct {
{ "RGBX", "1", rgb2bit },
{ "RGBX", "L", rgb2l },
{ "RGBA", "I", rgb2i },
{ "RGBA", "F", rgb2f },
{ "RGBX", "LA", rgb2la },
{ "RGBX", "I", rgb2i },
{ "RGBX", "F", rgb2f },
{ "RGBX", "RGB", rgba2rgb },
{ "RGBX", "CMYK", rgb2cmyk },
{ "RGBX", "YCbCr", ImagingConvertRGB2YCbCr },
@ -872,6 +909,7 @@ static struct {
{ "CMYK", "RGBX", cmyk2rgb },
{ "YCbCr", "L", ycbcr2l },
{ "YCbCr", "LA", ycbcr2la },
{ "YCbCr", "RGB", ImagingConvertYCbCr2RGB },
{ "HSV", "RGB", hsv2rgb },
@ -913,6 +951,15 @@ p2bit(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
*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
p2l(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
{
@ -922,6 +969,28 @@ p2l(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
*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
p2la(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
{
@ -939,9 +1008,9 @@ 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];
for (x = 0; x < xsize; x++, in += 4, out += 4) {
out[0] = out[1] = out[2] = L(&palette[in[0]*4]) / 1000;
out[3] = in[3];
}
}
@ -954,6 +1023,15 @@ p2i(UINT8* out_, const UINT8* in, int xsize, const UINT8* palette)
*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
p2f(UINT8* out_, const UINT8* in, int xsize, const UINT8* palette)
{
@ -963,6 +1041,15 @@ p2f(UINT8* out_, const UINT8* in, int xsize, const UINT8* palette)
*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
p2rgb(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
{
@ -976,6 +1063,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
p2rgba(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
{
@ -1009,6 +1109,13 @@ p2cmyk(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
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
p2ycbcr(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
{
@ -1016,6 +1123,13 @@ p2ycbcr(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
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
frompalette(Imaging imOut, Imaging imIn, const char *mode)
{
@ -1032,27 +1146,27 @@ frompalette(Imaging imOut, Imaging imIn, const char *mode)
alpha = !strcmp(imIn->mode, "PA");
if (strcmp(mode, "1") == 0)
convert = p2bit;
convert = alpha ? pa2bit : p2bit;
else if (strcmp(mode, "L") == 0)
convert = p2l;
convert = alpha ? pa2l : p2l;
else if (strcmp(mode, "LA") == 0)
convert = (alpha) ? pa2la : p2la;
convert = alpha ? pa2la : p2la;
else if (strcmp(mode, "PA") == 0)
convert = l2la;
convert = p2pa;
else if (strcmp(mode, "I") == 0)
convert = p2i;
convert = alpha ? pa2i : p2i;
else if (strcmp(mode, "F") == 0)
convert = p2f;
convert = alpha ? pa2f : p2f;
else if (strcmp(mode, "RGB") == 0)
convert = p2rgb;
convert = alpha ? pa2rgb : p2rgb;
else if (strcmp(mode, "RGBA") == 0)
convert = (alpha) ? pa2rgba : p2rgba;
convert = alpha ? pa2rgba : p2rgba;
else if (strcmp(mode, "RGBX") == 0)
convert = p2rgba;
convert = alpha ? pa2rgba : p2rgba;
else if (strcmp(mode, "CMYK") == 0)
convert = p2cmyk;
convert = alpha ? pa2cmyk : p2cmyk;
else if (strcmp(mode, "YCbCr") == 0)
convert = p2ycbcr;
convert = alpha ? pa2ycbcr : p2ycbcr;
else
return (Imaging) ImagingError_ValueError("conversion not supported");
@ -1073,9 +1187,10 @@ frompalette(Imaging imOut, Imaging imIn, const char *mode)
#pragma optimize("", off)
#endif
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;
int alpha;
int x, y;
ImagingPalette palette = inpalette;;
@ -1083,6 +1198,8 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
if (strcmp(imIn->mode, "L") != 0 && strncmp(imIn->mode, "RGB", 3) != 0)
return (Imaging) ImagingError_ValueError("conversion not supported");
alpha = !strcmp(mode, "PA");
if (palette == NULL) {
/* FIXME: make user configurable */
if (imIn->bands == 1)
@ -1094,7 +1211,7 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
if (!palette)
return (Imaging) ImagingError_ValueError("no palette");
imOut = ImagingNew2Dirty("P", imOut, imIn);
imOut = ImagingNew2Dirty(mode, imOut, imIn);
if (!imOut) {
if (palette != inpalette)
ImagingPaletteDelete(palette);
@ -1109,8 +1226,13 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
/* Greyscale palette: copy data as is */
ImagingSectionEnter(&cookie);
for (y = 0; y < imIn->ysize; y++)
memcpy(imOut->image[y], imIn->image[y], imIn->linesize);
for (y = 0; y < imIn->ysize; y++) {
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);
} else {
@ -1141,7 +1263,7 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
int g, g0, g1, g2;
int b, b0, b1, b2;
UINT8* in = (UINT8*) imIn->image[y];
UINT8* out = imOut->image8[y];
UINT8* out = alpha ? (UINT8*) imOut->image32[y] : imOut->image8[y];
int* e = errors;
r = r0 = r1 = 0;
@ -1160,7 +1282,12 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
cache = &ImagingPaletteCache(palette, r, g, b);
if (cache[0] == 0x100)
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];
g -= (int) palette->palette[cache[0]*4+1];
@ -1193,7 +1320,7 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
for (y = 0; y < imIn->ysize; y++) {
int r, g, b;
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) {
INT16* cache;
@ -1204,8 +1331,12 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
cache = &ImagingPaletteCache(palette, r, g, b);
if (cache[0] == 0x100)
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);
@ -1335,8 +1466,8 @@ convert(Imaging imOut, Imaging imIn, const char *mode,
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 (strcmp(mode, "P") == 0 || strcmp(mode, "PA") == 0)
return topalette(imOut, imIn, mode, palette, dither);
if (dither && strcmp(mode, "1") == 0)
return tobilevel(imOut, imIn, dither);