Merge pull request #1912 from uploadcare/premultiplied-luminosity

Premultiplied luminosity
This commit is contained in:
wiredfool 2016-05-26 21:03:26 +01:00
commit 761f470b92
7 changed files with 127 additions and 9 deletions

View File

@ -1539,6 +1539,9 @@ class Image(object):
if self.mode in ("1", "P"):
resample = NEAREST
if self.mode == 'LA':
return self.convert('La').resize(size, resample).convert('LA')
if self.mode == 'RGBA':
return self.convert('RGBa').resize(size, resample).convert('RGBA')
@ -1829,6 +1832,10 @@ class Image(object):
:returns: An :py:class:`~PIL.Image.Image` object.
"""
if self.mode == 'LA':
return self.convert('La').transform(
size, method, data, resample, fill).convert('LA')
if self.mode == 'RGBA':
return self.convert('RGBa').transform(
size, method, data, resample, fill).convert('RGBA')

View File

@ -278,6 +278,7 @@ mode_map = {'1': _PyAccess8,
'L': _PyAccess8,
'P': _PyAccess8,
'LA': _PyAccess32_2,
'La': _PyAccess32_2,
'PA': _PyAccess32_2,
'RGB': _PyAccess32_3,
'LAB': _PyAccess32_3,

View File

@ -4,7 +4,7 @@ from __future__ import print_function
modes = [
"1",
"L", "LA",
"L", "LA", "La",
"I", "I;16", "I;16L", "I;16B", "I;32L", "I;32B",
"F",
"P", "PA",

View File

@ -186,5 +186,70 @@ class CoreResampleConsistencyTest(PillowTestCase):
self.run_case(self.make_case('F', 1.192093e-07))
class CoreResampleAlphaCorrectTest(PillowTestCase):
def make_levels_case(self, mode):
i = Image.new(mode, (256, 16))
px = i.load()
for y in range(i.size[1]):
for x in range(i.size[0]):
pix = [x] * len(mode)
pix[-1] = 255 - y * 16
px[x, y] = tuple(pix)
return i
def run_levels_case(self, i):
px = i.load()
for y in range(i.size[1]):
used_colors = set(px[x, y][0] for x in range(i.size[0]))
self.assertEqual(256, len(used_colors),
'All colors should present in resized image. '
'Only {0} on {1} line.'.format(len(used_colors), y))
@unittest.skip("current implementation isn't precise enough")
def test_levels_rgba(self):
case = self.make_levels_case('RGBA')
self.run_levels_case(case.resize((512, 32), Image.BILINEAR))
self.run_levels_case(case.resize((512, 32), Image.BICUBIC))
self.run_levels_case(case.resize((512, 32), Image.LANCZOS))
@unittest.skip("current implementation isn't precise enough")
def test_levels_la(self):
case = self.make_levels_case('LA')
self.run_levels_case(case.resize((512, 32), Image.BILINEAR))
self.run_levels_case(case.resize((512, 32), Image.BICUBIC))
self.run_levels_case(case.resize((512, 32), Image.LANCZOS))
def make_dity_case(self, mode, clean_pixel, dirty_pixel):
i = Image.new(mode, (64, 64), dirty_pixel)
px = i.load()
xdiv4 = i.size[0] // 4
ydiv4 = i.size[1] // 4
for y in range(ydiv4 * 2):
for x in range(xdiv4 * 2):
px[x + xdiv4, y + ydiv4] = clean_pixel
return i
def run_dity_case(self, i, clean_pixel):
px = i.load()
for y in range(i.size[1]):
for x in range(i.size[0]):
if px[x, y][-1] != 0 and px[x, y][:-1] != clean_pixel:
message = 'pixel at ({0}, {1}) is differ:\n{2}\n{3}'\
.format(x, y, px[x, y], clean_pixel)
self.assertEqual(px[x, y][:3], clean_pixel, message)
def test_dirty_pixels_rgba(self):
case = self.make_dity_case('RGBA', (255, 255, 0, 128), (0, 0, 255, 0))
self.run_dity_case(case.resize((20, 20), Image.BILINEAR), (255, 255, 0))
self.run_dity_case(case.resize((20, 20), Image.BICUBIC), (255, 255, 0))
self.run_dity_case(case.resize((20, 20), Image.LANCZOS), (255, 255, 0))
def test_dirty_pixels_la(self):
case = self.make_dity_case('LA', (255, 128), (0, 0))
self.run_dity_case(case.resize((20, 20), Image.BILINEAR), (255,))
self.run_dity_case(case.resize((20, 20), Image.BICUBIC), (255,))
self.run_dity_case(case.resize((20, 20), Image.LANCZOS), (255,))
if __name__ == '__main__':
unittest.main()

View File

@ -222,6 +222,7 @@ ImagingAccessInit()
ADD("1", line_8, get_pixel_8, put_pixel_8);
ADD("L", line_8, get_pixel_8, put_pixel_8);
ADD("LA", line_32, get_pixel, put_pixel);
ADD("La", line_32, get_pixel, put_pixel);
ADD("I", line_32, get_pixel_32, put_pixel_32);
ADD("I;16", line_16, get_pixel_16L, put_pixel_16L);
ADD("I;16L", line_16, get_pixel_16L, put_pixel_16L);

View File

@ -116,6 +116,42 @@ l2bit(UINT8* out, const UINT8* in, int xsize)
*out++ = (*in++ >= 128) ? 255 : 0;
}
static void
lA2la(UINT8* out, const UINT8* in, int xsize)
{
int x;
unsigned int alpha, pixel, tmp;
for (x = 0; x < xsize; x++, in += 4) {
alpha = in[3];
pixel = MULDIV255(in[0], alpha, tmp);
*out++ = (UINT8) pixel;
*out++ = (UINT8) pixel;
*out++ = (UINT8) pixel;
*out++ = (UINT8) alpha;
}
}
/* RGBa -> RGBA conversion to remove premultiplication
Needed for correct transforms/resizing on RGBA images */
static void
la2lA(UINT8* out, const UINT8* in, int xsize)
{
int x;
unsigned int alpha, pixel;
for (x = 0; x < xsize; x++, in+=4) {
alpha = in[3];
if (alpha == 255 || alpha == 0) {
pixel = in[0];
} else {
pixel = CLIP((255 * in[0]) / alpha);
}
*out++ = (UINT8) pixel;
*out++ = (UINT8) pixel;
*out++ = (UINT8) pixel;
*out++ = (UINT8) alpha;
}
}
static void
l2la(UINT8* out, const UINT8* in, int xsize)
{
@ -405,7 +441,7 @@ rgba2rgb(UINT8* out, const UINT8* in, int xsize)
}
static void
rgba2rgba(UINT8* out, const UINT8* in, int xsize)
rgbA2rgba(UINT8* out, const UINT8* in, int xsize)
{
int x;
unsigned int alpha, tmp;
@ -427,17 +463,16 @@ rgba2rgbA(UINT8* out, const UINT8* in, int xsize)
unsigned int alpha;
for (x = 0; x < xsize; x++, in+=4) {
alpha = in[3];
if (alpha) {
*out++ = CLIP((255 * in[0]) / alpha);
*out++ = CLIP((255 * in[1]) / alpha);
*out++ = CLIP((255 * in[2]) / alpha);
} else {
if (alpha == 255 || alpha == 0) {
*out++ = in[0];
*out++ = in[1];
*out++ = in[2];
} else {
*out++ = CLIP((255 * in[0]) / alpha);
*out++ = CLIP((255 * in[1]) / alpha);
*out++ = CLIP((255 * in[2]) / alpha);
}
*out++ = in[3];
}
}
@ -765,10 +800,13 @@ static struct {
{ "L", "YCbCr", l2ycbcr },
{ "LA", "L", la2l },
{ "LA", "La", lA2la },
{ "LA", "RGB", la2rgb },
{ "LA", "RGBX", la2rgb },
{ "LA", "RGBA", la2rgb },
{ "La", "LA", la2lA },
{ "I", "L", i2l },
{ "I", "F", i2f },
@ -795,7 +833,7 @@ static struct {
{ "RGBA", "I", rgb2i },
{ "RGBA", "F", rgb2f },
{ "RGBA", "RGB", rgba2rgb },
{ "RGBA", "RGBa", rgba2rgba },
{ "RGBA", "RGBa", rgbA2rgba },
{ "RGBA", "RGBX", rgb2rgba },
{ "RGBA", "CMYK", rgb2cmyk },
{ "RGBA", "YCbCr", ImagingConvertRGB2YCbCr },

View File

@ -91,6 +91,12 @@ ImagingNewPrologueSubtype(const char *mode, unsigned xsize, unsigned ysize,
im->pixelsize = 4; /* store in image32 memory */
im->linesize = xsize * 4;
} else if (strcmp(mode, "La") == 0) {
/* 8-bit greyscale (luminance) with premultiplied alpha */
im->bands = 2;
im->pixelsize = 4; /* store in image32 memory */
im->linesize = xsize * 4;
} else if (strcmp(mode, "F") == 0) {
/* 32-bit floating point images */
im->bands = 1;