diff --git a/PIL/Image.py b/PIL/Image.py index 56a4b7093..58b85d5d2 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1309,6 +1309,9 @@ class Image: if self.mode in ("1", "P"): resample = NEAREST + if self.mode == 'RGBA': + return self.convert('RGBa').resize(size, resample).convert('RGBA') + if resample == ANTIALIAS: # requires stretch support (imToolkit & PIL 1.1.3) try: @@ -1606,6 +1609,9 @@ class Image: :returns: An Image object. """ + if self.mode == 'RGBA': + return self.convert('RGBa').transform(size, method, data, resample, fill).convert('RGBA') + if isinstance(method, ImageTransformHandler): return method.transform(size, self, resample=resample, fill=fill) if hasattr(method, "getdata"): @@ -1613,6 +1619,7 @@ class Image: method, data = method.getdata() if data is None: raise ValueError("missing method data") + im = new(self.mode, size, None) if method == MESH: # list of quads @@ -1620,7 +1627,7 @@ class Image: im.__transformer(box, self, QUAD, quad, resample, fill) else: im.__transformer((0, 0)+size, self, method, data, resample, fill) - + return im def __transformer(self, box, image, method, data, diff --git a/Tests/test_image_transform.py b/Tests/test_image_transform.py index 31516d978..fdee6072f 100644 --- a/Tests/test_image_transform.py +++ b/Tests/test_image_transform.py @@ -57,6 +57,40 @@ def test_mesh(): assert_image_equal(blank, transformed.crop((w//2,0,w,h//2))) assert_image_equal(blank, transformed.crop((0,h//2,w//2,h))) +def _test_alpha_premult(op): + # create image with half white, half black, with the black half transparent. + # do op, + # there should be no darkness in the white section. + im = Image.new('RGBA', (10,10), (0,0,0,0)); + im2 = Image.new('RGBA', (5,10), (255,255,255,255)); + im.paste(im2, (0,0)) + + im = op(im, (40,10)) + im_background = Image.new('RGB', (40,10), (255,255,255)) + im_background.paste(im, (0,0), im) + + hist = im_background.histogram() + assert_equal(40*10, hist[-1]) + + +def test_alpha_premult_resize(): + + def op (im, sz): + return im.resize(sz, Image.LINEAR) + + _test_alpha_premult(op) + +def test_alpha_premult_transform(): + + def op(im, sz): + (w,h) = im.size + return im.transform(sz, Image.EXTENT, + (0,0, + w,h), + Image.BILINEAR) + + _test_alpha_premult(op) + def test_blank_fill(): # attempting to hit diff --git a/libImaging/Convert.c b/libImaging/Convert.c index ab02910a7..2caf219ea 100644 --- a/libImaging/Convert.c +++ b/libImaging/Convert.c @@ -289,6 +289,31 @@ rgba2rgba(UINT8* out, const UINT8* in, int xsize) } } +/* RGBa -> RGBA conversion to remove premultiplication + Needed for correct transforms/resizing on RGBA images */ +static void +rgba2rgbA(UINT8* out, const UINT8* in, int xsize) +{ + int x; + 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 { + *out++ = in[0]; + *out++ = in[1]; + *out++ = in[2]; + } + *out++ = in[3]; + + } +} + + + /* ---------------- */ /* CMYK conversions */ /* ---------------- */ @@ -619,6 +644,8 @@ static struct { { "RGBA", "CMYK", rgb2cmyk }, { "RGBA", "YCbCr", ImagingConvertRGB2YCbCr }, + { "RGBa", "RGBA", rgba2rgbA }, + { "RGBX", "1", rgb2bit }, { "RGBX", "L", rgb2l }, { "RGBA", "I", rgb2i },