Repository: python-pillow/Pillow

Issue:
Specific ICO file being shifted vertically
### What did you do?

Downloaded this ICO file and used PIL to resave as a PNG: https://lauren.vortex.com/favicon.ico

### What did you expect to happen?

PNG result should look the same as the icon does in the browser (and other applications) - an L with a black square around it.

### What actually happened?

PNG result seems to have been shifted vertically, with the top part of the image lost: 
![vortex](https://user-images.githubusercontent.com/9033/46760528-847d6480-cc8f-11e8-8cc2-51da680bfa29.png)

### What are your OS, Python and Pillow versions?

* OS: Ubuntu 16.04
* Python: 3.6.5
* Pillow: 5.2.0

### Code to reproduce

```python
from PIL import Image
i = Image.open("favicon.ico")  # downloaded from https://lauren.vortex.com/favicon.ico
i.ico.getimage((16, 16)).save("vortex.png")
```



Discussion:
I presume that you have no connection to the site's favicon, and so can't give us permission to use the image as part of our test suite.
That's right, I'm not associated. Lauren seems like a pretty contactable person though, so you might be able to ask for his permission if it would make a good test: https://www.vortex.com/lauren
A potential solution to this is to use the "offset" and "size" from `header` to work backwards and read the AND image from the end, instead of moving forward.

```diff
    Read AND image from end

diff --git a/src/PIL/IcoImagePlugin.py b/src/PIL/IcoImagePlugin.py
index 5634bf8e9..65c73cdb3 100644
--- a/src/PIL/IcoImagePlugin.py
+++ b/src/PIL/IcoImagePlugin.py
@@ -189,15 +189,8 @@ class IcoFile:
             d, e, o, a = im.tile[0]
             im.tile[0] = d, (0, 0) + im.size, o, a
 
-            # figure out where AND mask image starts
             mode = a[0]
-            bpp = 8
-            for k, v in BmpImagePlugin.BIT2MODE.items():
-                if mode == v[1]:
-                    bpp = k
-                    break
-
-            if 32 == bpp:
+            if mode == BmpImagePlugin.BIT2MODE[32][1]:
                 # 32-bit color depth icon image allows semitransparent areas
                 # PIL's DIB format ignores transparency bits, recover them.
                 # The DIB is packed in BGRX byte order where X is the alpha
@@ -226,8 +219,8 @@ class IcoFile:
                 # the total mask data is
                 # padded row size * height / bits per char
 
-                and_mask_offset = o + int(im.size[0] * im.size[1] * (bpp / 8.0))
                 total_bytes = int((w * im.size[1]) / 8)
+                and_mask_offset = header["offset"] + header["size"] - total_bytes
 
                 self.buf.seek(and_mask_offset)
                 mask_data = self.buf.read(total_bytes)
```

I thought it might be because this is a 1 bit ICO, but comparing it with https://github.com/lordelph/icofileloader/blob/master/tests/assets/1bit-32px-sample.ico, that isn't the case.

Maybe the encoder just decided to throw some padding in the middle?
@laurenweinstein1 hi. In the above posts, a user has found that your site's favicon is an unusual image. Are you happy if we take that image, include it in our test suite, and distribute it under Pillow's license?
Hi. Interesting that you're seeing that behavior. However, I'd prefer that this favicon not be included in the test suite and not be distributed under Pillow's license. Thanks. -L
@laurenweinstein1 Sure thing!

Do you happen to remember how the favicon was created? 
I was thinking about that. I probably designed it around 2003 when I started my blog. I remember creating it and tweaking it in a bitmap editor but unfortunately today I have no idea which one. 

Resolving patch:
diff --git a/src/PIL/IcoImagePlugin.py b/src/PIL/IcoImagePlugin.py
--- a/src/PIL/IcoImagePlugin.py
+++ b/src/PIL/IcoImagePlugin.py
@@ -235,8 +235,8 @@ def frame(self, idx):
                 # the total mask data is
                 # padded row size * height / bits per char
 
-                and_mask_offset = o + int(im.size[0] * im.size[1] * (bpp / 8.0))
                 total_bytes = int((w * im.size[1]) / 8)
+                and_mask_offset = header["offset"] + header["size"] - total_bytes
 
                 self.buf.seek(and_mask_offset)
                 mask_data = self.buf.read(total_bytes)


Test patch:
diff --git a/Tests/images/hopper_mask.ico b/Tests/images/hopper_mask.ico
new file mode 100644
Binary files /dev/null and b/Tests/images/hopper_mask.ico differ
diff --git a/Tests/images/hopper_mask.png b/Tests/images/hopper_mask.png
new file mode 100644
Binary files /dev/null and b/Tests/images/hopper_mask.png differ
diff --git a/Tests/test_file_ico.py b/Tests/test_file_ico.py
--- a/Tests/test_file_ico.py
+++ b/Tests/test_file_ico.py
@@ -18,6 +18,11 @@ def test_sanity():
     assert im.get_format_mimetype() == "image/x-icon"
 
 
+def test_mask():
+    with Image.open("Tests/images/hopper_mask.ico") as im:
+        assert_image_equal_tofile(im, "Tests/images/hopper_mask.png")
+
+
 def test_black_and_white():
     with Image.open("Tests/images/black_and_white.ico") as im:
         assert im.mode == "RGBA"