Repository: python-pillow/Pillow

Issue:
Transparent pixels in specific PNG are read as black
### What did you do?
I have an image containing only white and transparent pixels.
I tried to read the pixels from greyscale image in RGBA format using Pillow library.

### What did you expect to happen?
I expect that transparent pixels are read as (0, 0, 0, 0) when image is opened as RGBA.
When I open the image in Preview/GIMP, I see transparency correctly.

### What actually happened?
Transparent pixels were read as black (0, 0, 0, 255).

### What are your OS, Python and Pillow versions?

* OS: macOS 10.13.6
* Python: 3.7.0
* Pillow: 5.2.0

### Code
```python
from PIL import Image
im = Image.open("test.png").convert("RGBA")
im.load()
print(im.getpixel((0, 0)))
```

### Image
https://user-images.githubusercontent.com/3826188/46113793-b1684c80-c1a4-11e8-98e9-38b1f0baa7fa.png

![test](https://user-images.githubusercontent.com/3826188/46113793-b1684c80-c1a4-11e8-98e9-38b1f0baa7fa.png)


Discussion:
This is because the image has mode '1', which isn't yet supported for PNG transparency. I've created PR #3744 to resolve this.

Resolving patch:
diff --git a/src/PIL/Image.py b/src/PIL/Image.py
--- a/src/PIL/Image.py
+++ b/src/PIL/Image.py
@@ -950,7 +950,7 @@ def convert_transparency(m, v):
         delete_trns = False
         # transparency handling
         if has_transparency:
-            if self.mode in ('L', 'RGB') and mode == 'RGBA':
+            if self.mode in ('1', 'L', 'I', 'RGB') and mode == 'RGBA':
                 # Use transparent conversion to promote from transparent
                 # color to an alpha channel.
                 new_im = self._new(self.im.convert_transparent(
diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py
--- a/src/PIL/PngImagePlugin.py
+++ b/src/PIL/PngImagePlugin.py
@@ -54,19 +54,24 @@
 
 _MODES = {
     # supported bits/color combinations, and corresponding modes/rawmodes
+    # Greyscale
     (1, 0):  ("1", "1"),
     (2, 0):  ("L", "L;2"),
     (4, 0):  ("L", "L;4"),
     (8, 0):  ("L", "L"),
     (16, 0): ("I", "I;16B"),
+    # Truecolour
     (8, 2):  ("RGB", "RGB"),
     (16, 2): ("RGB", "RGB;16B"),
+    # Indexed-colour
     (1, 3):  ("P", "P;1"),
     (2, 3):  ("P", "P;2"),
     (4, 3):  ("P", "P;4"),
     (8, 3):  ("P", "P"),
+    # Greyscale with alpha
     (8, 4):  ("LA", "LA"),
     (16, 4): ("RGBA", "LA;16B"),  # LA;16B->LA not yet available
+    # Truecolour with alpha
     (8, 6):  ("RGBA", "RGBA"),
     (16, 6): ("RGBA", "RGBA;16B"),
 }
@@ -386,7 +391,7 @@ def chunk_tRNS(self, pos, length):
                 # otherwise, we have a byte string with one alpha value
                 # for each palette entry
                 self.im_info["transparency"] = s
-        elif self.im_mode == "L":
+        elif self.im_mode in ("1", "L", "I"):
             self.im_info["transparency"] = i16(s)
         elif self.im_mode == "RGB":
             self.im_info["transparency"] = i16(s), i16(s[2:]), i16(s[4:])
@@ -841,7 +846,7 @@ def _save(im, fp, filename, chunk=putchunk):
                 transparency = max(0, min(255, transparency))
                 alpha = b'\xFF' * transparency + b'\0'
                 chunk(fp, b"tRNS", alpha[:alpha_bytes])
-        elif im.mode == "L":
+        elif im.mode in ("1", "L", "I"):
             transparency = max(0, min(65535, transparency))
             chunk(fp, b"tRNS", o16(transparency))
         elif im.mode == "RGB":


Test patch:
diff --git a/Tests/images/1_trns.png b/Tests/images/1_trns.png
new file mode 100644
Binary files /dev/null and b/Tests/images/1_trns.png differ
diff --git a/Tests/images/i_trns.png b/Tests/images/i_trns.png
new file mode 100644
Binary files /dev/null and b/Tests/images/i_trns.png differ
diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py
--- a/Tests/test_file_png.py
+++ b/Tests/test_file_png.py
@@ -291,30 +291,32 @@ def test_save_p_transparent_black(self):
         self.assert_image(im, "RGBA", (10, 10))
         self.assertEqual(im.getcolors(), [(100, (0, 0, 0, 0))])
 
-    def test_save_l_transparency(self):
-        # There are 559 transparent pixels in l_trns.png.
-        num_transparent = 559
-
-        in_file = "Tests/images/l_trns.png"
-        im = Image.open(in_file)
-        self.assertEqual(im.mode, "L")
-        self.assertEqual(im.info["transparency"], 255)
+    def test_save_greyscale_transparency(self):
+        for mode, num_transparent in {
+            "1": 1994,
+            "L": 559,
+            "I": 559,
+        }.items():
+            in_file = "Tests/images/"+mode.lower()+"_trns.png"
+            im = Image.open(in_file)
+            self.assertEqual(im.mode, mode)
+            self.assertEqual(im.info["transparency"], 255)
 
-        im_rgba = im.convert('RGBA')
-        self.assertEqual(
-            im_rgba.getchannel("A").getcolors()[0][0], num_transparent)
+            im_rgba = im.convert('RGBA')
+            self.assertEqual(
+                im_rgba.getchannel("A").getcolors()[0][0], num_transparent)
 
-        test_file = self.tempfile("temp.png")
-        im.save(test_file)
+            test_file = self.tempfile("temp.png")
+            im.save(test_file)
 
-        test_im = Image.open(test_file)
-        self.assertEqual(test_im.mode, "L")
-        self.assertEqual(test_im.info["transparency"], 255)
-        self.assert_image_equal(im, test_im)
+            test_im = Image.open(test_file)
+            self.assertEqual(test_im.mode, mode)
+            self.assertEqual(test_im.info["transparency"], 255)
+            self.assert_image_equal(im, test_im)
 
-        test_im_rgba = test_im.convert('RGBA')
-        self.assertEqual(
-            test_im_rgba.getchannel('A').getcolors()[0][0], num_transparent)
+            test_im_rgba = test_im.convert('RGBA')
+            self.assertEqual(
+                test_im_rgba.getchannel('A').getcolors()[0][0], num_transparent)
 
     def test_save_rgb_single_transparency(self):
         in_file = "Tests/images/caption_6_33_22.png"