Repository: python-pillow/Pillow

Issue:
Add correct alpha pre-multiplication handling
See http://stackoverflow.com/questions/13027169/scale-images-with-pil-preserving-transparency-and-color

I will take a look into this when I've time. This issue should be fixed but is no block to a 2.0 release.



Discussion:
@d-schmidt Still interested in working on this?

Yep. Still annoys me.
Am 12.05.2013 00:39 schrieb "Alex Clark ☺" notifications@github.com:

> @d-schmidt https://github.com/d-schmidt Still interested in working on
> this?
> 
> —
> Reply to this email directly or view it on GitHubhttps://github.com/python-imaging/Pillow/issues/66#issuecomment-17768824
> .

Cool thanks, "deadline" is roughly end of next month.

End of June? I've got another bigger project this and probably next week. But I would like to fix this as I improve my converter.

Yes, July 1 is the rough target.

This would be really nice to have - we've hit this problem in glue (https://github.com/jorgebastida/glue/issues/76) The workaround was to use imagemagick to scale down the images... quite horrible :(


Resolving patch:
diff --git a/PIL/Image.py b/PIL/Image.py
--- a/PIL/Image.py
+++ b/PIL/Image.py
@@ -1309,6 +1309,9 @@ def resize(self, size, resample=NEAREST):
         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 @@ def transform(self, size, method, data=None, resample=NEAREST, fill=1):
         :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 @@ def transform(self, size, method, data=None, resample=NEAREST, fill=1):
             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 @@ def transform(self, size, method, data=None, resample=NEAREST, fill=1):
                 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,


Test patch:
diff --git a/Tests/test_image_transform.py b/Tests/test_image_transform.py
--- 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