Repository: python-pillow/Pillow

Issue:
Affine transformation fill parameter seems to do nothing
I would like to use PIL to perform an affine warp on an image, but I want to specify a custom fill value for undefined pixels. The `transform` function seems to have a `fill=1` argument, but it is undocumented and looking further in the code it seems to be unused. 

Is there any way to get a custom fill value in an affine transform?


Discussion:
I am also interested in this!

Resolving patch:
diff --git a/PIL/Image.py b/PIL/Image.py
--- a/PIL/Image.py
+++ b/PIL/Image.py
@@ -2074,7 +2074,8 @@ def thumbnail(self, size, resample=BICUBIC):
 
     # FIXME: the different transform methods need further explanation
     # instead of bloating the method docs, add a separate chapter.
-    def transform(self, size, method, data=None, resample=NEAREST, fill=1):
+    def transform(self, size, method, data=None, resample=NEAREST,
+                  fill=1, fillcolor=None):
         """
         Transforms this image.  This method creates a new image with the
         given size, and the same mode as the original, and copies data
@@ -2095,9 +2096,11 @@ def transform(self, size, method, data=None, resample=NEAREST, fill=1):
            environment), or :py:attr:`PIL.Image.BICUBIC` (cubic spline
            interpolation in a 4x4 environment). If omitted, or if the image
            has mode "1" or "P", it is set to :py:attr:`PIL.Image.NEAREST`.
+        :param fillcolor: Optional fill color for the area outside the transform
+           in the output image.
         :returns: An :py:class:`~PIL.Image.Image` object.
         """
-
+        
         if self.mode == 'LA':
             return self.convert('La').transform(
                 size, method, data, resample, fill).convert('LA')
@@ -2116,13 +2119,15 @@ def transform(self, size, method, data=None, resample=NEAREST, fill=1):
         if data is None:
             raise ValueError("missing method data")
 
-        im = new(self.mode, size, None)
+        im = new(self.mode, size, fillcolor)
         if method == MESH:
             # list of quads
             for box, quad in data:
-                im.__transformer(box, self, QUAD, quad, resample, fill)
+                im.__transformer(box, self, QUAD, quad, resample,
+                                 fillcolor is None)
         else:
-            im.__transformer((0, 0)+size, self, method, data, resample, fill)
+            im.__transformer((0, 0)+size, self, method, data,
+                             resample, fillcolor is None)
 
         return im
 


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
@@ -52,6 +52,17 @@ def test_quad(self):
 
         self.assert_image_equal(transformed, scaled)
 
+    def test_fill(self):
+        im = hopper('RGB')
+        (w, h) = im.size
+        transformed = im.transform(im.size, Image.EXTENT,
+                                   (0, 0,
+                                    w*2, h*2),
+                                   Image.BILINEAR,
+                                   fillcolor = 'red')
+
+        self.assertEqual(transformed.getpixel((w-1,h-1)), (255,0,0))
+
     def test_mesh(self):
         # this should be a checkerboard of halfsized hoppers in ul, lr
         im = hopper('RGBA')
@@ -256,6 +267,5 @@ class TestImageTransformPerspective(TestImageTransformAffine):
     # Repeat all tests for AFFINE transformations with PERSPECTIVE
     transform = Image.PERSPECTIVE
 
-
 if __name__ == '__main__':
     unittest.main()