Repository: python-pillow/Pillow

Issue:
Recent change in TIFF rows per strip break saving with JPEG compression
<!--
Thank you for reporting an issue.

Follow these guidelines to ensure your issue is handled properly.

If you have a ...

1.  General question: consider asking the question on Stack Overflow
    with the python-imaging-library tag:

    * https://stackoverflow.com/questions/tagged/python-imaging-library

    Do not ask a question in both places.

    If you think you have found a bug or have an unexplained exception
    then file a bug report here.

2.  Bug report: include a self-contained, copy-pastable example that
    generates the issue if possible. Be concise with code posted.
    Guidelines on how to provide a good bug report:

    * https://stackoverflow.com/help/mcve

    Bug reports which follow these guidelines are easier to diagnose,
    and are often handled much more quickly.

3.  Feature request: do a quick search of existing issues
    to make sure this has not been asked before.

We know asking good questions takes effort, and we appreciate your time.
Thank you.
-->

### What did you do?

Tried to save an image with TIFF JPEG compression.

### What did you expect to happen?

It to create a file.

### What actually happened?

It threw `OSError: encoder error -2 when writing image file`.  More specifically, I see `JPEGSetupEncode: RowsPerStrip must be multiple of 8 for JPEG.`.

### What are your OS, Python and Pillow versions?

* OS: Ubuntu 20.04
* Python: 3.9
* Pillow: 8.3.0

<!--
Please include **code** that reproduces the issue and whenever possible, an **image** that demonstrates the issue. Please upload images to GitHub, not to third-party file hosting sites. If necessary, add the image to a zip or tar archive.

The best reproductions are self-contained scripts with minimal dependencies. If you are using a framework such as Plone, Django, or Buildout, try to replicate the issue just using Pillow.
-->

```python
import PIL.Image
image = PIL.Image.new('RGB', (1760, 1046))
image.save('/tmp/junk.tiff', compression='tiff_jpeg')
```

The error was specifically introduced in https://github.com/python-pillow/Pillow/pull/5514/files#diff-6ad43f85f1a075181d4d8cfcd97ae27bba1eccf5c3db5a3457160f98218eba6eR1562-R1565


Discussion:
~~I get the same error in 8.2.0 when writing as a single strip, 1046 is not a multiple of 8 either...~~

~~The problem was always there, however now more likely to pop up...~~

Sorry, works as single strip in 8.2.0, had local changes...

Resolving patch:
diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py
--- a/src/PIL/TiffImagePlugin.py
+++ b/src/PIL/TiffImagePlugin.py
@@ -1577,6 +1577,9 @@ def _save(im, fp, filename):
     # aim for 64 KB strips when using libtiff writer
     if libtiff:
         rows_per_strip = min((2 ** 16 + stride - 1) // stride, im.size[1])
+        # JPEG encoder expects multiple of 8 rows
+        if compression == "jpeg":
+            rows_per_strip = min(((rows_per_strip + 7) // 8) * 8, im.size[1])
     else:
         rows_per_strip = im.size[1]
     strip_byte_counts = stride * rows_per_strip


Test patch:
diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py
--- a/Tests/test_file_libtiff.py
+++ b/Tests/test_file_libtiff.py
@@ -968,10 +968,11 @@ def test_realloc_overflow(self):
             assert str(e.value) == "-9"
         TiffImagePlugin.READ_LIBTIFF = False
 
-    def test_save_multistrip(self, tmp_path):
+    @pytest.mark.parametrize("compression", ("tiff_adobe_deflate", "jpeg"))
+    def test_save_multistrip(self, compression, tmp_path):
         im = hopper("RGB").resize((256, 256))
         out = str(tmp_path / "temp.tif")
-        im.save(out, compression="tiff_adobe_deflate")
+        im.save(out, compression=compression)
 
         with Image.open(out) as im:
             # Assert that there are multiple strips