Repository: python-pillow/Pillow

Issue:
Problem with saving image from clipboard
Hello,

I have Windows 8 x64 system and installed Pillow version 2.8.2
I'm trying to grab image from clipboard. I press "Alt+PrtSc" and then call grabclipboard function, and get such error:

``` python
ImageGrab.grabclipboard()
---------------------------------------------------------------------------
IOError                                   Traceback (most recent call last)
<ipython-input-2-c8274e888e6c> in <module>()
      1 from PIL import ImageGrab
      2 
----> 3 ImageGrab.grabclipboard()

C:\Anaconda\lib\site-packages\PIL\ImageGrab.pyc in grabclipboard()
     49         from PIL import BmpImagePlugin
     50         import io
---> 51         return BmpImagePlugin.DibImageFile(io.BytesIO(data))
     52     return data

C:\Anaconda\lib\site-packages\PIL\ImageFile.pyc in __init__(self, fp, filename)
     95 
     96         try:
---> 97             self._open()
     98         except IndexError as v:  # end of data
     99             if Image.DEBUG > 1:

C:\Anaconda\lib\site-packages\PIL\BmpImagePlugin.pyc in _open(self)
    204 
    205     def _open(self):
--> 206         self._bitmap()
    207 
    208 #

C:\Anaconda\lib\site-packages\PIL\BmpImagePlugin.pyc in _bitmap(self, header, offset)
    145                     raw_mode = MASK_MODES[(file_info['bits'], file_info['rgb_mask'])]
    146                 else:
--> 147                     raise IOError("Unsupported BMP bitfields layout")
    148             else:
    149                 raise IOError("Unsupported BMP bitfields layout")

IOError: Unsupported BMP bitfields layout
```

But if I call ImageGrab.grab function, screenshot(of whole screen) is saved properly. 



Discussion:
UPD It's version problem, when I installed Pillow version 2.6.0 everything worked ok

Reproducible with Pilllow 2.8.0, 2.8.1, 2.8.2. 
Not reproducible with Pillow 2.6.0, 2.7.0.

```
E:\stufftodelete>cat test.py
from PIL import ImageGrab
ImageGrab.grabclipboard()

E:\stufftodelete>pip freeze | grep illow
Pillow==2.8.2

E:\stufftodelete>python test.py
Traceback (most recent call last):
  File "test.py", line 2, in <module>
    ImageGrab.grabclipboard()
  File "C:\Python27\lib\site-packages\PIL\ImageGrab.py", line 51, in grabclipboard
    return BmpImagePlugin.DibImageFile(io.BytesIO(data))
  File "C:\Python27\lib\site-packages\PIL\ImageFile.py", line 97, in __init__
    self._open()
  File "C:\Python27\lib\site-packages\PIL\BmpImagePlugin.py", line 206, in _open
    self._bitmap()
  File "C:\Python27\lib\site-packages\PIL\BmpImagePlugin.py", line 147, in _bitmap
    raise IOError("Unsupported BMP bitfields layout")
IOError: Unsupported BMP bitfields layout

E:\stufftodelete>pip install pillow==2.8.1
Collecting pillow==2.8.1
  Using cached Pillow-2.8.1-cp27-none-win_amd64.whl
Installing collected packages: pillow
  Found existing installation: Pillow 2.8.2
    Uninstalling Pillow-2.8.2:
      Successfully uninstalled Pillow-2.8.2
Successfully installed pillow-2.8.1

E:\stufftodelete>python test.py
Traceback (most recent call last):
  File "test.py", line 2, in <module>
    ImageGrab.grabclipboard()
  File "C:\Python27\lib\site-packages\PIL\ImageGrab.py", line 51, in grabclipboard
    return BmpImagePlugin.DibImageFile(io.BytesIO(data))
  File "C:\Python27\lib\site-packages\PIL\ImageFile.py", line 97, in __init__
    self._open()
  File "C:\Python27\lib\site-packages\PIL\BmpImagePlugin.py", line 206, in _open
    self._bitmap()
  File "C:\Python27\lib\site-packages\PIL\BmpImagePlugin.py", line 147, in _bitmap
    raise IOError("Unsupported BMP bitfields layout")
IOError: Unsupported BMP bitfields layout

E:\stufftodelete>pip install pillow==2.8.0
Collecting pillow==2.8.0
  Downloading Pillow-2.8.0-cp27-none-win_amd64.whl (1.3MB)
    100% |################################| 1.3MB 386kB/s
Installing collected packages: pillow
  Found existing installation: Pillow 2.8.1
    Uninstalling Pillow-2.8.1:
      Successfully uninstalled Pillow-2.8.1
Successfully installed pillow-2.8.0

E:\stufftodelete>python test.py
Traceback (most recent call last):
  File "test.py", line 2, in <module>
    ImageGrab.grabclipboard()
  File "C:\Python27\lib\site-packages\PIL\ImageGrab.py", line 51, in grabclipboard
    return BmpImagePlugin.DibImageFile(io.BytesIO(data))
  File "C:\Python27\lib\site-packages\PIL\ImageFile.py", line 97, in __init__
    self._open()
  File "C:\Python27\lib\site-packages\PIL\BmpImagePlugin.py", line 206, in _open
    self._bitmap()
  File "C:\Python27\lib\site-packages\PIL\BmpImagePlugin.py", line 147, in _bitmap
    raise IOError("Unsupported BMP bitfields layout")
IOError: Unsupported BMP bitfields layout

E:\stufftodelete>pip install pillow==2.7.0
Collecting pillow==2.7.0
  Downloading Pillow-2.7.0-cp27-none-win_amd64.whl (1.3MB)
    100% |################################| 1.3MB 393kB/s
Installing collected packages: pillow
  Found existing installation: Pillow 2.8.0
    Uninstalling Pillow-2.8.0:
      Successfully uninstalled Pillow-2.8.0
Successfully installed pillow-2.7.0

E:\stufftodelete>python test.py

E:\stufftodelete>pip install pillow==2.6.0
Collecting pillow==2.6.0
  Downloading Pillow-2.6.0-cp27-none-win_amd64.whl (1.3MB)
    100% |################################| 1.3MB 405kB/s
Installing collected packages: pillow
  Found existing installation: Pillow 2.7.0
    Uninstalling Pillow-2.7.0:
      Successfully uninstalled Pillow-2.7.0
Successfully installed pillow-2.6.0

E:\stufftodelete>python test.py
```

Pillow 2.7.0 was released on 1st January this year, and 2.8.0 was released on 1st April.

PR https://github.com/python-pillow/Pillow/pull/1125 may have caused it. Ping @artscoop.

It'd be great to get this fixed for 2.9.0, due out on 1st July.

The good news is we now have a Windows-based CI to run unit tests, so if it's possible to simulate Alt+PrtSc (or just PrtSc, which also triggers it) on AppVeyor then this should definitely be unit-tested.

Running OS X, I can reproduce the error using one of the files in the test suite - [Tests/images/bmp/g/rgb32bf.bmp](https://github.com/python-pillow/Pillow/blob/master/Tests/images/bmp/g/rgb32bf.bmp)

Interestingly though, it's [listed as unsupported](https://github.com/python-pillow/Pillow/blob/master/Tests/test_bmp_reference.py#L82).

Unfortunately, the ImageGrab functions don't work on the windows CI, likely because there's something screwy with the graphics context on a headless vm.

Notes:

This can be partially fixed by adding this `elif` near [here](https://github.com/python-pillow/Pillow/blob/master/PIL/BmpImagePlugin.py#L146):

``` python
                elif file_info['bits'] == 32 and file_info['rgb_mask'] == (0xff0000, 0xff00, 0xff):
                    pass
                else:
                    raise IOError("Unsupported BMP bitfields layout")
            else:
                raise IOError("Unsupported BMP bitfields layout")
```

That will return an image, but the resulting image is truncated. It can be shown or saved only if `LOAD_TRUNCATED_IMAGES` is true, but the image is one pixel shifted left and missing the top line.

This is because, for example, `self.tile` is:
`[('raw', (0, 0, 3360, 1050), 56L, ('BGRX', 13440, -1))]` 
instead of:
`[('raw', (0, 0, 3360, 1050), 52L, ('BGRX', 13440, -1))]`

That third value in both cases is `offset or self.fp.tell()`, the problem with 2.8.0 is that an [extra read](https://github.com/python-pillow/Pillow/blob/master/PIL/BmpImagePlugin.py#L112) had been made to load in a `file_info['a_mask']`, which doesn't make sense for this format, and putting the file pointer at the wrong place.

We should only be reading that alpha value when needed.

The regression appears with 3.0.0 too. 2.7.0 works ok.

So, any news on that?

Ok I understand what is going on.
I rewrote part of the BMP loading process.
And I made sure it worked on supported bitmap files, fixed bogus behaviour from previous versions, but it did not have to work with unsupported files.
However @radarhere , the cause of the error you've been experiencing with this file may be... let's say, different.

Now I remember: the file rgb32bf.bmp is unsupported because it has a bitmask not supported by Pillow. Supported masks for 32-bit true color images have classic boundaries, with RGB channels in RGB or BGR order.
Here we have an esoteric RGB/A mask (the image is read correctly by XNView though), which has no matching dialect in PIL definitions for unpacking pixels.
Therefore I have been able to load this image, but with wrong colors (for instance, we have a problem with B and G channels)

This bug can be closed. This works with 3.2.0:

```
>>> from PIL import ImageGrab
>>> ImageGrab.grabclipboard()
>>>
```

It can be renamed "ImageGrab.grabclipboard() problem on Windows", because there are other issues there.

This bug is still present: I can reproduce it as described in the original report with Pillow 3.2.0 (Python 2.7, Windows 7).

@hugovk can you confirm it with the following actions?

1 open mspaint
2 alt+prtscr
3 run the code

``` python
from PIL import ImageGrab
im = ImageGrab.grabclipboard()
if im is None:
  print('Error: No image data in clipboard')
```

Python 2.7, 32-bit Vista, Pillow 3.2.0 works.

@techtonik 
- If I alt+prtscr the cmd.exe window, I get `IOError: Unsupported BMP bitfields layout` (like the original report).
- If I alt+prtscr the Paint window, I get an `im` (e.g. `<PIL.BmpImagePlugin.DibImageFile image mode=RGBA size=886x754 at 0x2A7FE10>`) but `im.show()` gives `IOError: image file is truncated (3540 bytes not processed)` (like #1810).

@hugovk yep, the same here, so looks like this issue is only about console window.

What is the bitfield layout? If it's sane, there's no reason that we can't support it.

@wiredfool any ideas where to look for it? Let me update the Traceback for 3.2.0 first.

``` python
Traceback (most recent call last):
  File "code.py", line 2, in <module>
    im = ImageGrab.grabclipboard()
  File "C:\Python27\lib\site-packages\PIL\ImageGrab.py", line 60, in grabclipboard
    return BmpImagePlugin.DibImageFile(io.BytesIO(data))
  File "C:\Python27\lib\site-packages\PIL\ImageFile.py", line 97, in __init__
    self._open()
  File "C:\Python27\lib\site-packages\PIL\BmpImagePlugin.py", line 211, in _open
    self._bitmap()
  File "C:\Python27\lib\site-packages\PIL\BmpImagePlugin.py", line 152, in _bitmap
    raise IOError("Unsupported BMP bitfields layout")
IOError: Unsupported BMP bitfields layout
```

Printing `file_info` contents just before `raise`:

``` python
{
    'direction': -1,
    'r_mask': 16711680,
    'compression': 3,
    'pixels_per_meter': (0, 0),
    'palette_padding': 4,
    'g_mask': 65280,
    'a_mask': 6908265,
    'rgba_mask': (16711680, 65280, 255, 6908265),
    'header_size': 40,
    'height': 736,
    'width': 1066,
    'colors': 4294967296L,
    'b_mask': 255,
    'rgb_mask': (16711680, 65280, 255),
    'planes': 1,
    'y_flip': False,
    'bits': 32,
    'data_size': 3138304,
}
```

ok, that's a WTF. 

The alpha mask is 0x696969, so it's a BRG[WTF] image??

Screenshot of a console window:

![image](https://cloud.githubusercontent.com/assets/515889/14314532/714f11ea-fc00-11e5-9c94-4c4b35f0968a.png)

Try this:

```
diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py
index e398445..f7cc203 100644
--- a/PIL/BmpImagePlugin.py
+++ b/PIL/BmpImagePlugin.py
@@ -142,6 +142,10 @@ class BmpImageFile(ImageFile.ImageFile):
                 (16, (0xf800, 0x7e0, 0x1f)): "BGR;16",
                 (16, (0x7c00, 0x3e0, 0x1f)): "BGR;15"
             }
+            # work around really strange alpha reported in windows screenshot bmps.
+            if file_info['bits'] == 32 and file_info['a_mask'] == 0x696969:
+                file_info['bits'] = 24
+                
             if file_info['bits'] in SUPPORTED:
                 if file_info['bits'] == 32 and file_info['rgba_mask'] in SUPPORTED[file_info['bits']]:
                     raw_mode = MASK_MODES[(file_info['bits'], file_info['rgba_mask'])]
```

The mask of the image is:

```
(0xff0000, 0xff00, 0xff, 0x696969)
```

while supported ones for 32 bits are:

```
(0xff0000, 0xff00, 0xff, 0x0)
(0xff0000, 0xff00, 0xff, 0xff000000)
(0x0, 0x0, 0x0, 0x0)
```

I can't find any references of that magic number. Maybe Pillow gets that number wrong?

That magic number makes no sense at all from a masking point of view. 

@wiredfool this is the result

![cmd](https://cloud.githubusercontent.com/assets/515889/14314777/018f4706-fc02-11e5-9001-a08461ebc2ee.png)

That looks like aliasing.  so, probably BGRX instead of BGR

Try this:

```
diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py
index e398445..e3b3399 100644
--- a/PIL/BmpImagePlugin.py
+++ b/PIL/BmpImagePlugin.py
@@ -130,13 +130,15 @@ class BmpImageFile(ImageFile.ImageFile):
         # ----------------- Process BMP with Bitfields compression (not palette)
         if file_info['compression'] == self.BITFIELDS:
             SUPPORTED = {
-                32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000), (0x0, 0x0, 0x0, 0x0)],
+                32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000),
+                     (0xff0000, 0xff00, 0xff, 0x696969), (0x0, 0x0, 0x0, 0x0)],
                 24: [(0xff0000, 0xff00, 0xff)],
                 16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)]
             }
             MASK_MODES = {
                 (32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX",
                 (32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA",
+                (32, (0xff0000, 0xff00, 0xff, 0x696969)): "BGRX",
                 (32, (0x0, 0x0, 0x0, 0x0)): "BGRA",
                 (24, (0xff0000, 0xff00, 0xff)): "BGR",
                 (16, (0xf800, 0x7e0, 0x1f)): "BGR;16",
```

@wiredfool yay, this works (with `ImageFile.LOAD_TRUNCATED_IMAGES = True`, which is another issue - #1810).

Great!.  Could you dump the data in ImageGrab.grabclipboard() to a file for use in a test?

@wiredfool how to dump?

just before the return BmpImagePlugin line:

```
with open('test_file.bmp', 'wb') as f:
    f.write(data)
```

I used `im._dump('console.dib')` after `grabclipboard` to get [console.dib.zip](https://github.com/python-pillow/Pillow/files/206348/console.dib.zip). Let me try with `f.write()`

Image._dump saves a PPM file, I want the raw bytes so that we can get a good test out of it. 

Here you go - [console.raw.zip](https://github.com/python-pillow/Pillow/files/206355/console.raw.zip)
.

Thanks. I think I've found the bug, In the DIB file, it looks like they're only writing in the three masks, and we're reading 4. The 0x696969 is actually the color of the first pixel. Which is why the image is truncated, since the first pixel is being read as an alpha mask.

That's something mentioned by @hugovk in https://github.com/python-pillow/Pillow/issues/1293#issuecomment-115237352 that I could not understand.

From https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx , in the section on the 40 byte headers, which is the type that we're using here:

> BI_BITFIELDS  Specifies that the bitmap is not compressed and that the color table consists of three DWORD color masks that specify the red, green, and blue components, respectively, of each pixel. This is valid when used with 16- and 32-bpp bitmaps.

So, when we have a 40 byte header, we can't read the 4 components of the mask. 

Ok, I think this patch should do it, where the previous one would only work if the first pixel color was 0x696969.

```
diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py
index e398445..a3e39c1 100644
--- a/PIL/BmpImagePlugin.py
+++ b/PIL/BmpImagePlugin.py
@@ -109,7 +109,11 @@ class BmpImageFile(ImageFile.ImageFile):
                         for idx, mask in enumerate(['r_mask', 'g_mask', 'b_mask', 'a_mask']):
                             file_info[mask] = i32(header_data[36+idx*4:40+idx*4])
                     else:
-                        for mask in ['r_mask', 'g_mask', 'b_mask', 'a_mask']:
+                        # 40 byte headers only have the three components in the bitfields masks,
+                        # ref: https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
+                        # See also https://github.com/python-pillow/Pillow/issues/1293
+                        file_info['a_mask'] = 0x0
+                        for mask in ['r_mask', 'g_mask', 'b_mask']:
                             file_info[mask] = i32(read(4))
                     file_info['rgb_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'])
                     file_info['rgba_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'], file_info['a_mask'])
```

Hmm. Looks like there is an alpha channel involved, and it's inverted -- e.g. 00 is solid and FF is transparent. 


Resolving patch:
diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py
--- a/PIL/BmpImagePlugin.py
+++ b/PIL/BmpImagePlugin.py
@@ -109,7 +109,12 @@ def _bitmap(self, header=0, offset=0):
                         for idx, mask in enumerate(['r_mask', 'g_mask', 'b_mask', 'a_mask']):
                             file_info[mask] = i32(header_data[36+idx*4:40+idx*4])
                     else:
-                        for mask in ['r_mask', 'g_mask', 'b_mask', 'a_mask']:
+                        # 40 byte headers only have the three components in the bitfields masks,
+                        # ref: https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
+                        # See also https://github.com/python-pillow/Pillow/issues/1293
+                        # Note below, None is a key in the SUPPORTED and MASK_MODES structures.
+                        file_info['a_mask'] = None
+                        for mask in ['r_mask', 'g_mask', 'b_mask']:
                             file_info[mask] = i32(read(4))
                     file_info['rgb_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'])
                     file_info['rgba_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'], file_info['a_mask'])
@@ -130,12 +135,22 @@ def _bitmap(self, header=0, offset=0):
         # ----------------- Process BMP with Bitfields compression (not palette)
         if file_info['compression'] == self.BITFIELDS:
             SUPPORTED = {
-                32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000), (0x0, 0x0, 0x0, 0x0)],
+                32: [(0xff0000, 0xff00, 0xff, 0x0),
+                     (0xff0000, 0xff00, 0xff, None),
+                     (0xff0000, 0xff00, 0xff, 0xff000000),
+                     (0x0, 0x0, 0x0, 0x0)],
                 24: [(0xff0000, 0xff00, 0xff)],
                 16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)]
             }
+            # From inspecting DIB files from Vista screenshots, the
+            # file format is a 32 bit color image, where the 'Alpha'
+            # channel for screenshots is actually a transparency mask
+            # (255-alpha) where 00 is solid and ff is transparent.  I
+            # can't find documentation of this, but it matches
+            # imagemagick's conversion of the .dib file into a .png.
             MASK_MODES = {
                 (32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX",
+                (32, (0xff0000, 0xff00, 0xff, None)): "BGRT",
                 (32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA",
                 (32, (0x0, 0x0, 0x0, 0x0)): "BGRA",
                 (24, (0xff0000, 0xff00, 0xff)): "BGR",
@@ -145,7 +160,7 @@ def _bitmap(self, header=0, offset=0):
             if file_info['bits'] in SUPPORTED:
                 if file_info['bits'] == 32 and file_info['rgba_mask'] in SUPPORTED[file_info['bits']]:
                     raw_mode = MASK_MODES[(file_info['bits'], file_info['rgba_mask'])]
-                    self.mode = "RGBA" if raw_mode in ("BGRA",) else self.mode
+                    self.mode = "RGBA" if raw_mode in ("BGRA", "BGRT") else self.mode
                 elif file_info['bits'] in (24, 16) and file_info['rgb_mask'] in SUPPORTED[file_info['bits']]:
                     raw_mode = MASK_MODES[(file_info['bits'], file_info['rgb_mask'])]
                 else:


Test patch:
diff --git a/Tests/images/clipboard.dib b/Tests/images/clipboard.dib
new file mode 100644
Binary files /dev/null and b/Tests/images/clipboard.dib differ
diff --git a/Tests/images/clipboard_target.png b/Tests/images/clipboard_target.png
new file mode 100644
Binary files /dev/null and b/Tests/images/clipboard_target.png differ
diff --git a/Tests/test_file_bmp.py b/Tests/test_file_bmp.py
--- a/Tests/test_file_bmp.py
+++ b/Tests/test_file_bmp.py
@@ -70,6 +70,13 @@ def test_save_bmp_with_dpi(self):
         self.assertEqual(im.size, reloaded.size)
         self.assertEqual(reloaded.format, "JPEG")
 
+    def test_load_dib(self):
+        # test for #1293, Imagegrab returning Unsupported Bitfields Format
+        im = BmpImagePlugin.DibImageFile('Tests/images/clipboard.dib')
+        target = Image.open('Tests/images/clipboard_target.png')
+        self.assert_image_equal(im.convert('RGB'), target.convert('RGB'))
+        self.assert_image_similar(im, target, 1)
+
 
 if __name__ == '__main__':
     unittest.main()