bug fix: Qt wants data aligned to 32 bits

Images in Qt show up incorrectly if each line is not aligned to 32 bits.

It is pretty common for an image's lines to be 32-bit alinged by chance.
Obviously any 32-bit image will not have any problem.
For the bug to manifest itself you'd need...
* a 1-bit image whose width is not a multiple of 32
* an 8-bit image who width is not a multiple of 4

Testing more images now and added a 7x13 png test image
This commit is contained in:
Eric L Frederich 2015-09-17 09:32:15 -04:00 committed by Eric L Frederich
parent 58af64e245
commit 86e775daa3
3 changed files with 45 additions and 7 deletions

View File

@ -80,6 +80,33 @@ def fromqpixmap(im):
# bytes_io.seek(0) # bytes_io.seek(0)
# return PIL.Image.open(bytes_io) # return PIL.Image.open(bytes_io)
def align8to32(bytes, width, mode):
"""
converts each scanline of data from 8 bit to 32 bit aligned
"""
bits_per_pixel = {
'1': 1,
'L': 8,
'P': 8,
}[mode]
# calculate bytes per line and the extra padding if needed
bits_per_line = bits_per_pixel * width
full_bytes_per_line, remaining_bits_per_line = divmod(bits_per_line, 8)
bytes_per_line = full_bytes_per_line + (1 if remaining_bits_per_line else 0)
extra_padding = -bytes_per_line % 4
# already 32 bit aligned by luck
if not extra_padding:
return bytes
new_data = []
for i in range(len(bytes) / bytes_per_line):
new_data.append(bytes[i*bytes_per_line:(i+1)*bytes_per_line] + '\x00' * extra_padding)
return ''.join(new_data)
def _toqclass_helper(im): def _toqclass_helper(im):
data = None data = None
@ -123,7 +150,7 @@ def _toqclass_helper(im):
raise ValueError("unsupported image mode %r" % im.mode) raise ValueError("unsupported image mode %r" % im.mode)
# must keep a reference, or Qt will crash! # must keep a reference, or Qt will crash!
__data = data or im.tobytes() __data = data or align8to32(im.tobytes(), im.size[0], im.mode)
return { return {
'data': __data, 'im': im, 'format': format, 'colortable': colortable 'data': __data, 'im': im, 'format': format, 'colortable': colortable
} }

BIN
Tests/images/7x13.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 B

View File

@ -1,30 +1,41 @@
from helper import unittest, PillowTestCase, hopper from helper import unittest, PillowTestCase, hopper
from test_imageqt import PillowQtTestCase from test_imageqt import PillowQtTestCase
from PIL import ImageQt from PIL import ImageQt, Image
class TestFromQImage(PillowQtTestCase, PillowTestCase): class TestFromQImage(PillowQtTestCase, PillowTestCase):
files_to_test = [
hopper(),
Image.open('Tests/images/transparent.png'),
Image.open('Tests/images/7x13.png'),
]
def roundtrip(self, expected): def roundtrip(self, expected):
result = ImageQt.fromqimage(expected.toqimage()) result = ImageQt.fromqimage(expected.toqimage())
# Qt saves all images as rgb # Qt saves all images as rgb
self.assert_image_equal(result, expected.convert('RGB')) self.assert_image_equal(result, expected.convert('RGB'))
def test_sanity_1(self): def test_sanity_1(self):
self.roundtrip(hopper('1')) for im in self.files_to_test:
self.roundtrip(im.convert('1'))
def test_sanity_rgb(self): def test_sanity_rgb(self):
self.roundtrip(hopper('RGB')) for im in self.files_to_test:
self.roundtrip(im.convert('RGB'))
def test_sanity_rgba(self): def test_sanity_rgba(self):
self.roundtrip(hopper('RGBA')) for im in self.files_to_test:
self.roundtrip(im.convert('RGBA'))
def test_sanity_l(self): def test_sanity_l(self):
self.roundtrip(hopper('L')) for im in self.files_to_test:
self.roundtrip(im.convert('L'))
def test_sanity_p(self): def test_sanity_p(self):
self.roundtrip(hopper('P')) for im in self.files_to_test:
self.roundtrip(im.convert('P'))
if __name__ == '__main__': if __name__ == '__main__':