124 lines
3.6 KiB
Python
124 lines
3.6 KiB
Python
'''
|
|
PIL: PIL image loader
|
|
'''
|
|
|
|
__all__ = ('ImageLoaderPIL', )
|
|
|
|
try:
|
|
import Image as PILImage
|
|
except ImportError:
|
|
# for python3
|
|
from PIL import Image as PILImage
|
|
|
|
from kivy.logger import Logger
|
|
from kivy.core.image import ImageLoaderBase, ImageData, ImageLoader
|
|
|
|
try:
|
|
# Pillow
|
|
PILImage.frombytes
|
|
PILImage.Image.tobytes
|
|
except AttributeError:
|
|
# PIL
|
|
# monkey patch frombytes and tobytes methods, refs:
|
|
# https://github.com/kivy/kivy/issues/5460
|
|
PILImage.frombytes = PILImage.frombuffer
|
|
PILImage.Image.tobytes = PILImage.Image.tostring
|
|
|
|
|
|
class ImageLoaderPIL(ImageLoaderBase):
|
|
'''Image loader based on the PIL library.
|
|
|
|
.. versionadded:: 1.0.8
|
|
|
|
Support for GIF animation added.
|
|
|
|
Gif animation has a lot of issues(transparency/color depths... etc).
|
|
In order to keep it simple, what is implemented here is what is
|
|
natively supported by the PIL library.
|
|
|
|
As a general rule, try to use gifs that have no transparency.
|
|
Gif's with transparency will work but be prepared for some
|
|
artifacts until transparency support is improved.
|
|
|
|
'''
|
|
|
|
@staticmethod
|
|
def can_save(fmt, is_bytesio):
|
|
if is_bytesio:
|
|
return False
|
|
return fmt in ImageLoaderPIL.extensions()
|
|
|
|
@staticmethod
|
|
def can_load_memory():
|
|
return True
|
|
|
|
@staticmethod
|
|
def extensions():
|
|
'''Return accepted extensions for this loader'''
|
|
PILImage.init()
|
|
return tuple((ext_with_dot[1:] for ext_with_dot in PILImage.EXTENSION))
|
|
|
|
def _img_correct(self, _img_tmp):
|
|
'''Convert image to the correct format and orientation.
|
|
'''
|
|
# image loader work only with rgb/rgba image
|
|
if _img_tmp.mode.lower() not in ('rgb', 'rgba'):
|
|
try:
|
|
imc = _img_tmp.convert('RGBA')
|
|
except:
|
|
Logger.warning(
|
|
'Image: Unable to convert image to rgba (was %s)' %
|
|
(_img_tmp.mode.lower()))
|
|
raise
|
|
_img_tmp = imc
|
|
|
|
return _img_tmp
|
|
|
|
def _img_read(self, im):
|
|
'''Read images from an animated file.
|
|
'''
|
|
im.seek(0)
|
|
|
|
# Read all images inside
|
|
try:
|
|
img_ol = None
|
|
while True:
|
|
img_tmp = im
|
|
img_tmp = self._img_correct(img_tmp)
|
|
if img_ol and (hasattr(im, 'dispose') and not im.dispose):
|
|
# paste new frame over old so as to handle
|
|
# transparency properly
|
|
img_ol.paste(img_tmp, (0, 0), img_tmp)
|
|
img_tmp = img_ol
|
|
img_ol = img_tmp
|
|
yield ImageData(img_tmp.size[0], img_tmp.size[1],
|
|
img_tmp.mode.lower(), img_tmp.tobytes())
|
|
im.seek(im.tell() + 1)
|
|
except EOFError:
|
|
pass
|
|
|
|
def load(self, filename):
|
|
try:
|
|
im = PILImage.open(filename)
|
|
except:
|
|
Logger.warning('Image: Unable to load image <%s>' % filename)
|
|
raise
|
|
# update internals
|
|
if not self._inline:
|
|
self.filename = filename
|
|
# returns an array of type ImageData len 1 if not a sequence image
|
|
return list(self._img_read(im))
|
|
|
|
@staticmethod
|
|
def save(filename, width, height, pixelfmt, pixels, flipped=False,
|
|
imagefmt=None):
|
|
image = PILImage.frombytes(pixelfmt.upper(), (width, height), pixels)
|
|
if flipped:
|
|
image = image.transpose(PILImage.FLIP_TOP_BOTTOM)
|
|
image.save(filename)
|
|
return True
|
|
|
|
|
|
# register
|
|
ImageLoader.register(ImageLoaderPIL)
|