mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-11-13 13:16:52 +03:00
d50445ff30
Similar to the recent adoption of Black. isort is a Python utility to sort imports alphabetically and automatically separate into sections. By using isort, contributors can quickly and automatically conform to the projects style without thinking. Just let the tool do it. Uses the configuration recommended by the Black to avoid conflicts of style. Rewrite TestImageQt.test_deprecated to no rely on import order.
225 lines
6.7 KiB
Python
225 lines
6.7 KiB
Python
#
|
|
# The Python Imaging Library.
|
|
# $Id$
|
|
#
|
|
# a simple Qt image interface.
|
|
#
|
|
# history:
|
|
# 2006-06-03 fl: created
|
|
# 2006-06-04 fl: inherit from QImage instead of wrapping it
|
|
# 2006-06-05 fl: removed toimage helper; move string support to ImageQt
|
|
# 2013-11-13 fl: add support for Qt5 (aurelien.ballier@cyclonit.com)
|
|
#
|
|
# Copyright (c) 2006 by Secret Labs AB
|
|
# Copyright (c) 2006 by Fredrik Lundh
|
|
#
|
|
# See the README file for information on usage and redistribution.
|
|
#
|
|
|
|
import sys
|
|
import warnings
|
|
from io import BytesIO
|
|
|
|
from . import Image
|
|
from ._util import isPath, py3
|
|
|
|
qt_versions = [["5", "PyQt5"], ["side2", "PySide2"], ["4", "PyQt4"], ["side", "PySide"]]
|
|
|
|
WARNING_TEXT = (
|
|
"Support for EOL {} is deprecated and will be removed in a future version. "
|
|
"Please upgrade to PyQt5 or PySide2."
|
|
)
|
|
|
|
# If a version has already been imported, attempt it first
|
|
qt_versions.sort(key=lambda qt_version: qt_version[1] in sys.modules, reverse=True)
|
|
for qt_version, qt_module in qt_versions:
|
|
try:
|
|
if qt_module == "PyQt5":
|
|
from PyQt5.QtGui import QImage, qRgba, QPixmap
|
|
from PyQt5.QtCore import QBuffer, QIODevice
|
|
elif qt_module == "PySide2":
|
|
from PySide2.QtGui import QImage, qRgba, QPixmap
|
|
from PySide2.QtCore import QBuffer, QIODevice
|
|
elif qt_module == "PyQt4":
|
|
from PyQt4.QtGui import QImage, qRgba, QPixmap
|
|
from PyQt4.QtCore import QBuffer, QIODevice
|
|
|
|
warnings.warn(WARNING_TEXT.format(qt_module), DeprecationWarning)
|
|
elif qt_module == "PySide":
|
|
from PySide.QtGui import QImage, qRgba, QPixmap
|
|
from PySide.QtCore import QBuffer, QIODevice
|
|
|
|
warnings.warn(WARNING_TEXT.format(qt_module), DeprecationWarning)
|
|
except (ImportError, RuntimeError):
|
|
continue
|
|
qt_is_installed = True
|
|
break
|
|
else:
|
|
qt_is_installed = False
|
|
qt_version = None
|
|
|
|
|
|
def rgb(r, g, b, a=255):
|
|
"""(Internal) Turns an RGB color into a Qt compatible color integer."""
|
|
# use qRgb to pack the colors, and then turn the resulting long
|
|
# into a negative integer with the same bitpattern.
|
|
return qRgba(r, g, b, a) & 0xFFFFFFFF
|
|
|
|
|
|
def fromqimage(im):
|
|
"""
|
|
:param im: A PIL Image object, or a file name
|
|
(given either as Python string or a PyQt string object)
|
|
"""
|
|
buffer = QBuffer()
|
|
buffer.open(QIODevice.ReadWrite)
|
|
# preserve alpha channel with png
|
|
# otherwise ppm is more friendly with Image.open
|
|
if im.hasAlphaChannel():
|
|
im.save(buffer, "png")
|
|
else:
|
|
im.save(buffer, "ppm")
|
|
|
|
b = BytesIO()
|
|
try:
|
|
b.write(buffer.data())
|
|
except TypeError:
|
|
# workaround for Python 2
|
|
b.write(str(buffer.data()))
|
|
buffer.close()
|
|
b.seek(0)
|
|
|
|
return Image.open(b)
|
|
|
|
|
|
def fromqpixmap(im):
|
|
return fromqimage(im)
|
|
# buffer = QBuffer()
|
|
# buffer.open(QIODevice.ReadWrite)
|
|
# # im.save(buffer)
|
|
# # What if png doesn't support some image features like animation?
|
|
# im.save(buffer, 'ppm')
|
|
# bytes_io = BytesIO()
|
|
# bytes_io.write(buffer.data())
|
|
# buffer.close()
|
|
# bytes_io.seek(0)
|
|
# return 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]
|
|
+ b"\x00" * extra_padding
|
|
)
|
|
|
|
return b"".join(new_data)
|
|
|
|
|
|
def _toqclass_helper(im):
|
|
data = None
|
|
colortable = None
|
|
|
|
# handle filename, if given instead of image name
|
|
if hasattr(im, "toUtf8"):
|
|
# FIXME - is this really the best way to do this?
|
|
if py3:
|
|
im = str(im.toUtf8(), "utf-8")
|
|
else:
|
|
im = unicode(im.toUtf8(), "utf-8") # noqa: F821
|
|
if isPath(im):
|
|
im = Image.open(im)
|
|
|
|
if im.mode == "1":
|
|
format = QImage.Format_Mono
|
|
elif im.mode == "L":
|
|
format = QImage.Format_Indexed8
|
|
colortable = []
|
|
for i in range(256):
|
|
colortable.append(rgb(i, i, i))
|
|
elif im.mode == "P":
|
|
format = QImage.Format_Indexed8
|
|
colortable = []
|
|
palette = im.getpalette()
|
|
for i in range(0, len(palette), 3):
|
|
colortable.append(rgb(*palette[i : i + 3]))
|
|
elif im.mode == "RGB":
|
|
data = im.tobytes("raw", "BGRX")
|
|
format = QImage.Format_RGB32
|
|
elif im.mode == "RGBA":
|
|
try:
|
|
data = im.tobytes("raw", "BGRA")
|
|
except SystemError:
|
|
# workaround for earlier versions
|
|
r, g, b, a = im.split()
|
|
im = Image.merge("RGBA", (b, g, r, a))
|
|
format = QImage.Format_ARGB32
|
|
else:
|
|
raise ValueError("unsupported image mode %r" % im.mode)
|
|
|
|
__data = data or align8to32(im.tobytes(), im.size[0], im.mode)
|
|
return {"data": __data, "im": im, "format": format, "colortable": colortable}
|
|
|
|
|
|
if qt_is_installed:
|
|
|
|
class ImageQt(QImage):
|
|
def __init__(self, im):
|
|
"""
|
|
An PIL image wrapper for Qt. This is a subclass of PyQt's QImage
|
|
class.
|
|
|
|
:param im: A PIL Image object, or a file name (given either as
|
|
Python string or a PyQt string object).
|
|
"""
|
|
im_data = _toqclass_helper(im)
|
|
# must keep a reference, or Qt will crash!
|
|
# All QImage constructors that take data operate on an existing
|
|
# buffer, so this buffer has to hang on for the life of the image.
|
|
# Fixes https://github.com/python-pillow/Pillow/issues/1370
|
|
self.__data = im_data["data"]
|
|
QImage.__init__(
|
|
self,
|
|
self.__data,
|
|
im_data["im"].size[0],
|
|
im_data["im"].size[1],
|
|
im_data["format"],
|
|
)
|
|
if im_data["colortable"]:
|
|
self.setColorTable(im_data["colortable"])
|
|
|
|
|
|
def toqimage(im):
|
|
return ImageQt(im)
|
|
|
|
|
|
def toqpixmap(im):
|
|
# # This doesn't work. For now using a dumb approach.
|
|
# im_data = _toqclass_helper(im)
|
|
# result = QPixmap(im_data['im'].size[0], im_data['im'].size[1])
|
|
# result.loadFromData(im_data['data'])
|
|
# Fix some strange bug that causes
|
|
if im.mode == "RGB":
|
|
im = im.convert("RGBA")
|
|
|
|
qimage = toqimage(im)
|
|
return QPixmap.fromImage(qimage)
|