2010-07-31 06:52:47 +04:00
|
|
|
#
|
|
|
|
# The Python Imaging Library.
|
|
|
|
# $Id$
|
|
|
|
#
|
|
|
|
# base class for image file handlers
|
|
|
|
#
|
|
|
|
# history:
|
|
|
|
# 1995-09-09 fl Created
|
|
|
|
# 1996-03-11 fl Fixed load mechanism.
|
|
|
|
# 1996-04-15 fl Added pcx/xbm decoders.
|
|
|
|
# 1996-04-30 fl Added encoders.
|
|
|
|
# 1996-12-14 fl Added load helpers
|
|
|
|
# 1997-01-11 fl Use encode_to_file where possible
|
|
|
|
# 1997-08-27 fl Flush output in _save
|
|
|
|
# 1998-03-05 fl Use memory mapping for some modes
|
|
|
|
# 1999-02-04 fl Use memory mapping also for "I;16" and "I;16B"
|
|
|
|
# 1999-05-31 fl Added image parser
|
|
|
|
# 2000-10-12 fl Set readonly flag on memory-mapped images
|
|
|
|
# 2002-03-20 fl Use better messages for common decoder errors
|
|
|
|
# 2003-04-21 fl Fall back on mmap/map_buffer if map is not available
|
|
|
|
# 2003-10-30 fl Added StubImageFile class
|
|
|
|
# 2004-02-25 fl Made incremental parser more robust
|
|
|
|
#
|
|
|
|
# Copyright (c) 1997-2004 by Secret Labs AB
|
|
|
|
# Copyright (c) 1995-2004 by Fredrik Lundh
|
|
|
|
#
|
|
|
|
# See the README file for information on usage and redistribution.
|
|
|
|
#
|
|
|
|
|
2012-10-15 09:55:39 +04:00
|
|
|
import io
|
2022-01-01 06:54:23 +03:00
|
|
|
import itertools
|
2015-07-31 01:20:51 +03:00
|
|
|
import struct
|
2019-07-06 23:40:53 +03:00
|
|
|
import sys
|
|
|
|
|
|
|
|
from . import Image
|
|
|
|
from ._util import isPath
|
2019-03-12 02:27:43 +03:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
MAXBLOCK = 65536
|
|
|
|
|
2019-03-21 16:28:20 +03:00
|
|
|
SAFEBLOCK = 1024 * 1024
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2013-01-10 23:44:41 +04:00
|
|
|
LOAD_TRUNCATED_IMAGES = False
|
2020-09-01 20:16:46 +03:00
|
|
|
"""Whether or not to load truncated image files. User code may change this."""
|
2013-01-10 23:44:41 +04:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
ERRORS = {
|
|
|
|
-1: "image buffer overrun error",
|
|
|
|
-2: "decoding error",
|
|
|
|
-3: "unknown error",
|
|
|
|
-8: "bad configuration",
|
2019-03-21 16:28:20 +03:00
|
|
|
-9: "out of memory error",
|
2010-07-31 06:52:47 +04:00
|
|
|
}
|
2022-02-25 08:07:01 +03:00
|
|
|
"""
|
|
|
|
Dict of known error codes returned from :meth:`.PyDecoder.decode`,
|
|
|
|
:meth:`.PyEncoder.encode` :meth:`.PyEncoder.encode_to_pyfd` and
|
|
|
|
:meth:`.PyEncoder.encode_to_file`.
|
|
|
|
"""
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2014-08-26 17:47:10 +04:00
|
|
|
|
2020-04-07 09:58:21 +03:00
|
|
|
#
|
|
|
|
# --------------------------------------------------------------------
|
|
|
|
# Helpers
|
|
|
|
|
|
|
|
|
|
|
|
def raise_oserror(error):
|
2010-07-31 06:52:47 +04:00
|
|
|
try:
|
|
|
|
message = Image.core.getcodecstatus(error)
|
|
|
|
except AttributeError:
|
|
|
|
message = ERRORS.get(error)
|
|
|
|
if not message:
|
2020-07-16 12:43:29 +03:00
|
|
|
message = f"decoder error {error}"
|
2019-09-30 17:56:31 +03:00
|
|
|
raise OSError(message + " when reading image file")
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2014-08-26 17:47:10 +04:00
|
|
|
|
2012-10-17 07:58:14 +04:00
|
|
|
def _tilesort(t):
|
2010-07-31 06:52:47 +04:00
|
|
|
# sort on offset
|
2012-10-17 07:58:14 +04:00
|
|
|
return t[2]
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2014-08-26 17:47:10 +04:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
#
|
|
|
|
# --------------------------------------------------------------------
|
|
|
|
# ImageFile base class
|
|
|
|
|
2019-03-21 16:28:20 +03:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
class ImageFile(Image.Image):
|
2020-06-22 07:43:14 +03:00
|
|
|
"""Base class for image file format handlers."""
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
def __init__(self, fp=None, filename=None):
|
2016-11-05 20:31:11 +03:00
|
|
|
super().__init__()
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2017-09-06 06:23:50 +03:00
|
|
|
self._min_frame = 0
|
|
|
|
|
2019-01-02 06:13:10 +03:00
|
|
|
self.custom_mimetype = None
|
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
self.tile = None
|
2020-07-23 15:40:02 +03:00
|
|
|
""" A list of tile descriptors, or ``None`` """
|
|
|
|
|
2014-08-26 17:47:10 +04:00
|
|
|
self.readonly = 1 # until we know better
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
self.decoderconfig = ()
|
|
|
|
self.decodermaxblock = MAXBLOCK
|
|
|
|
|
2013-06-30 15:04:42 +04:00
|
|
|
if isPath(fp):
|
2010-07-31 06:52:47 +04:00
|
|
|
# filename
|
|
|
|
self.fp = open(fp, "rb")
|
|
|
|
self.filename = fp
|
2016-07-17 06:29:36 +03:00
|
|
|
self._exclusive_fp = True
|
2010-07-31 06:52:47 +04:00
|
|
|
else:
|
|
|
|
# stream
|
|
|
|
self.fp = fp
|
|
|
|
self.filename = filename
|
2016-07-17 09:22:50 +03:00
|
|
|
# can be overridden
|
|
|
|
self._exclusive_fp = None
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
try:
|
Improve handling of file resources
Follow Python's file object semantics. User code is responsible for
closing resources (usually through a context manager) in a deterministic
way.
To achieve this, remove __del__ functions. These functions used to
closed open file handlers in an attempt to silence Python
ResourceWarnings. However, using __del__ has the following drawbacks:
- __del__ isn't called until the object's reference count reaches 0.
Therefore, resource handlers remain open or in use longer than
necessary.
- The __del__ method isn't guaranteed to execute on system exit. See the
Python documentation:
https://docs.python.org/3/reference/datamodel.html#object.__del__
> It is not guaranteed that __del__() methods are called for objects
> that still exist when the interpreter exits.
- Exceptions that occur inside __del__ are ignored instead of raised.
This has the potential of hiding bugs. This is also in the Python
documentation:
> Warning: Due to the precarious circumstances under which __del__()
> methods are invoked, exceptions that occur during their execution
> are ignored, and a warning is printed to sys.stderr instead.
Instead, always close resource handlers when they are no longer in use.
This will close the file handler at a specified point in the user's code
and not wait until the interpreter chooses to. It is always guaranteed
to run. And, if an exception occurs while closing the file handler, the
bug will not be ignored.
Now, when code receives a ResourceWarning, it will highlight an area
that is mishandling resources. It should not simply be silenced, but
fixed by closing resources with a context manager.
All warnings that were emitted during tests have been cleaned up. To
enable warnings, I passed the `-Wa` CLI option to Python. This exposed
some mishandling of resources in ImageFile.__init__() and
SpiderImagePlugin.loadImageSeries(), they too were fixed.
2019-05-25 19:30:58 +03:00
|
|
|
try:
|
|
|
|
self._open()
|
|
|
|
except (
|
|
|
|
IndexError, # end of data
|
|
|
|
TypeError, # end of data (ord)
|
|
|
|
KeyError, # unsupported mode
|
|
|
|
EOFError, # got header but not the first frame
|
|
|
|
struct.error,
|
|
|
|
) as v:
|
2020-06-21 13:13:35 +03:00
|
|
|
raise SyntaxError(v) from v
|
Improve handling of file resources
Follow Python's file object semantics. User code is responsible for
closing resources (usually through a context manager) in a deterministic
way.
To achieve this, remove __del__ functions. These functions used to
closed open file handlers in an attempt to silence Python
ResourceWarnings. However, using __del__ has the following drawbacks:
- __del__ isn't called until the object's reference count reaches 0.
Therefore, resource handlers remain open or in use longer than
necessary.
- The __del__ method isn't guaranteed to execute on system exit. See the
Python documentation:
https://docs.python.org/3/reference/datamodel.html#object.__del__
> It is not guaranteed that __del__() methods are called for objects
> that still exist when the interpreter exits.
- Exceptions that occur inside __del__ are ignored instead of raised.
This has the potential of hiding bugs. This is also in the Python
documentation:
> Warning: Due to the precarious circumstances under which __del__()
> methods are invoked, exceptions that occur during their execution
> are ignored, and a warning is printed to sys.stderr instead.
Instead, always close resource handlers when they are no longer in use.
This will close the file handler at a specified point in the user's code
and not wait until the interpreter chooses to. It is always guaranteed
to run. And, if an exception occurs while closing the file handler, the
bug will not be ignored.
Now, when code receives a ResourceWarning, it will highlight an area
that is mishandling resources. It should not simply be silenced, but
fixed by closing resources with a context manager.
All warnings that were emitted during tests have been cleaned up. To
enable warnings, I passed the `-Wa` CLI option to Python. This exposed
some mishandling of resources in ImageFile.__init__() and
SpiderImagePlugin.loadImageSeries(), they too were fixed.
2019-05-25 19:30:58 +03:00
|
|
|
|
|
|
|
if not self.mode or self.size[0] <= 0:
|
|
|
|
raise SyntaxError("not identified by this driver")
|
|
|
|
except BaseException:
|
2016-07-17 06:29:36 +03:00
|
|
|
# close the file only if we have opened it this constructor
|
|
|
|
if self._exclusive_fp:
|
|
|
|
self.fp.close()
|
Improve handling of file resources
Follow Python's file object semantics. User code is responsible for
closing resources (usually through a context manager) in a deterministic
way.
To achieve this, remove __del__ functions. These functions used to
closed open file handlers in an attempt to silence Python
ResourceWarnings. However, using __del__ has the following drawbacks:
- __del__ isn't called until the object's reference count reaches 0.
Therefore, resource handlers remain open or in use longer than
necessary.
- The __del__ method isn't guaranteed to execute on system exit. See the
Python documentation:
https://docs.python.org/3/reference/datamodel.html#object.__del__
> It is not guaranteed that __del__() methods are called for objects
> that still exist when the interpreter exits.
- Exceptions that occur inside __del__ are ignored instead of raised.
This has the potential of hiding bugs. This is also in the Python
documentation:
> Warning: Due to the precarious circumstances under which __del__()
> methods are invoked, exceptions that occur during their execution
> are ignored, and a warning is printed to sys.stderr instead.
Instead, always close resource handlers when they are no longer in use.
This will close the file handler at a specified point in the user's code
and not wait until the interpreter chooses to. It is always guaranteed
to run. And, if an exception occurs while closing the file handler, the
bug will not be ignored.
Now, when code receives a ResourceWarning, it will highlight an area
that is mishandling resources. It should not simply be silenced, but
fixed by closing resources with a context manager.
All warnings that were emitted during tests have been cleaned up. To
enable warnings, I passed the `-Wa` CLI option to Python. This exposed
some mishandling of resources in ImageFile.__init__() and
SpiderImagePlugin.loadImageSeries(), they too were fixed.
2019-05-25 19:30:58 +03:00
|
|
|
raise
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2018-06-30 14:08:41 +03:00
|
|
|
def get_format_mimetype(self):
|
2019-01-05 23:00:00 +03:00
|
|
|
if self.custom_mimetype:
|
|
|
|
return self.custom_mimetype
|
|
|
|
if self.format is not None:
|
|
|
|
return Image.MIME.get(self.format.upper())
|
2018-06-30 14:08:41 +03:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
def verify(self):
|
2019-02-03 07:58:24 +03:00
|
|
|
"""Check file integrity"""
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
# raise exception if something's wrong. must be called
|
|
|
|
# directly after open, and closes file when finished.
|
2016-07-17 06:33:23 +03:00
|
|
|
if self._exclusive_fp:
|
|
|
|
self.fp.close()
|
2010-07-31 06:52:47 +04:00
|
|
|
self.fp = None
|
|
|
|
|
|
|
|
def load(self):
|
2019-02-03 07:58:24 +03:00
|
|
|
"""Load image data based on tile list"""
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
if self.tile is None:
|
2019-09-30 17:56:31 +03:00
|
|
|
raise OSError("cannot load this image")
|
2020-04-17 11:29:45 +03:00
|
|
|
|
|
|
|
pixel = Image.Image.load(self)
|
2010-07-31 06:52:47 +04:00
|
|
|
if not self.tile:
|
|
|
|
return pixel
|
|
|
|
|
|
|
|
self.map = None
|
2014-07-18 21:40:08 +04:00
|
|
|
use_mmap = self.filename and len(self.tile) == 1
|
|
|
|
# As of pypy 2.1.0, memory mapping was failing here.
|
2019-03-21 16:28:20 +03:00
|
|
|
use_mmap = use_mmap and not hasattr(sys, "pypy_version_info")
|
2014-08-26 17:47:10 +04:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
readonly = 0
|
|
|
|
|
2014-07-18 21:40:08 +04:00
|
|
|
# look for read/seek overrides
|
|
|
|
try:
|
|
|
|
read = self.load_read
|
|
|
|
# don't use mmap if there are custom read/seek functions
|
|
|
|
use_mmap = False
|
|
|
|
except AttributeError:
|
|
|
|
read = self.fp.read
|
|
|
|
|
|
|
|
try:
|
|
|
|
seek = self.load_seek
|
|
|
|
use_mmap = False
|
|
|
|
except AttributeError:
|
|
|
|
seek = self.fp.seek
|
|
|
|
|
|
|
|
if use_mmap:
|
2010-07-31 06:52:47 +04:00
|
|
|
# try memory mapping
|
2016-11-22 13:46:54 +03:00
|
|
|
decoder_name, extents, offset, args = self.tile[0]
|
2019-03-21 16:28:20 +03:00
|
|
|
if (
|
|
|
|
decoder_name == "raw"
|
|
|
|
and len(args) >= 3
|
|
|
|
and args[0] == self.mode
|
|
|
|
and args[0] in Image._MAPMODES
|
|
|
|
):
|
2010-07-31 06:52:47 +04:00
|
|
|
try:
|
2021-01-25 13:10:49 +03:00
|
|
|
# use mmap, if possible
|
|
|
|
import mmap
|
|
|
|
|
|
|
|
with open(self.filename) as fp:
|
|
|
|
self.map = mmap.mmap(fp.fileno(), 0, access=mmap.ACCESS_READ)
|
|
|
|
self.im = Image.core.map_buffer(
|
|
|
|
self.map, self.size, decoder_name, offset, args
|
|
|
|
)
|
2010-07-31 06:52:47 +04:00
|
|
|
readonly = 1
|
2018-06-24 15:32:25 +03:00
|
|
|
# After trashing self.im,
|
|
|
|
# we might need to reload the palette data.
|
2016-09-29 15:54:25 +03:00
|
|
|
if self.palette:
|
|
|
|
self.palette.dirty = 1
|
2019-09-30 17:56:31 +03:00
|
|
|
except (AttributeError, OSError, ImportError):
|
2010-07-31 06:52:47 +04:00
|
|
|
self.map = None
|
|
|
|
|
|
|
|
self.load_prepare()
|
2018-03-04 06:24:36 +03:00
|
|
|
err_code = -3 # initialize to unknown error
|
2010-07-31 06:52:47 +04:00
|
|
|
if not self.map:
|
|
|
|
# sort tiles in file order
|
2012-10-17 07:58:14 +04:00
|
|
|
self.tile.sort(key=_tilesort)
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
try:
|
|
|
|
# FIXME: This is a hack to handle TIFF's JpegTables tag.
|
|
|
|
prefix = self.tile_prefix
|
|
|
|
except AttributeError:
|
py3k: The big push
There are two main issues fixed with this commit:
* bytes vs. str: All file, image, and palette data are now handled as
bytes. A new _binary module consolidates the hacks needed to do this
across Python versions. tostring/fromstring methods have been renamed to
tobytes/frombytes, but the Python 2.6/2.7 versions alias them to the old
names for compatibility. Users should move to tobytes/frombytes.
One other potentially-breaking change is that text data in image files
(such as tags, comments) are now explicitly handled with a specific
character encoding in mind. This works well with the Unicode str in
Python 3, but may trip up old code expecting a straight byte-for-byte
translation to a Python string. This also required a change to Gohlke's
tags tests (in Tests/test_file_png.py) to expect Unicode strings from
the code.
* True div vs. floor div: Many division operations used the "/" operator
to do floor division, which is now the "//" operator in Python 3. These
were fixed.
As of this commit, on the first pass, I have one failing test (improper
handling of a slice object in a C module, test_imagepath.py) in Python 3,
and three that that I haven't tried running yet (test_imagegl,
test_imagegrab, and test_imageqt). I also haven't tested anything on
Windows. All but the three skipped tests run flawlessly against Pythons
2.6 and 2.7.
2012-10-21 01:01:53 +04:00
|
|
|
prefix = b""
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2022-01-01 06:54:23 +03:00
|
|
|
# Remove consecutive duplicates that only differ by their offset
|
|
|
|
self.tile = [
|
|
|
|
list(tiles)[-1]
|
|
|
|
for _, tiles in itertools.groupby(
|
|
|
|
self.tile, lambda tile: (tile[0], tile[1], tile[3])
|
|
|
|
)
|
|
|
|
]
|
2016-05-29 01:07:36 +03:00
|
|
|
for decoder_name, extents, offset, args in self.tile:
|
2019-03-21 16:28:20 +03:00
|
|
|
decoder = Image._getdecoder(
|
|
|
|
self.mode, decoder_name, args, self.decoderconfig
|
|
|
|
)
|
2018-03-01 09:15:58 +03:00
|
|
|
try:
|
|
|
|
seek(offset)
|
|
|
|
decoder.setimage(self.im, extents)
|
|
|
|
if decoder.pulls_fd:
|
|
|
|
decoder.setfd(self.fp)
|
|
|
|
status, err_code = decoder.decode(b"")
|
|
|
|
else:
|
|
|
|
b = prefix
|
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
s = read(self.decodermaxblock)
|
2020-06-21 13:13:35 +03:00
|
|
|
except (IndexError, struct.error) as e:
|
2018-06-24 15:32:25 +03:00
|
|
|
# truncated png/gif
|
2018-03-01 09:15:58 +03:00
|
|
|
if LOAD_TRUNCATED_IMAGES:
|
|
|
|
break
|
|
|
|
else:
|
2020-06-21 13:13:35 +03:00
|
|
|
raise OSError("image file is truncated") from e
|
2018-03-01 09:15:58 +03:00
|
|
|
|
|
|
|
if not s: # truncated jpeg
|
|
|
|
if LOAD_TRUNCATED_IMAGES:
|
2018-03-06 08:05:56 +03:00
|
|
|
break
|
2018-03-01 09:15:58 +03:00
|
|
|
else:
|
2019-09-30 17:56:31 +03:00
|
|
|
raise OSError(
|
2019-03-21 16:28:20 +03:00
|
|
|
"image file is truncated "
|
2020-07-16 12:43:29 +03:00
|
|
|
f"({len(b)} bytes not processed)"
|
2019-03-21 16:28:20 +03:00
|
|
|
)
|
2018-03-01 09:15:58 +03:00
|
|
|
|
|
|
|
b = b + s
|
|
|
|
n, err_code = decoder.decode(b)
|
|
|
|
if n < 0:
|
2016-05-29 01:07:36 +03:00
|
|
|
break
|
2018-03-01 09:15:58 +03:00
|
|
|
b = b[n:]
|
|
|
|
finally:
|
|
|
|
# Need to cleanup here to prevent leaks
|
|
|
|
decoder.cleanup()
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
self.tile = []
|
|
|
|
self.readonly = readonly
|
|
|
|
|
2017-03-15 02:16:38 +03:00
|
|
|
self.load_end()
|
|
|
|
|
|
|
|
if self._exclusive_fp and self._close_exclusive_fp_after_loading:
|
2016-07-17 06:33:23 +03:00
|
|
|
self.fp.close()
|
|
|
|
self.fp = None
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2016-05-29 01:07:36 +03:00
|
|
|
if not self.map and not LOAD_TRUNCATED_IMAGES and err_code < 0:
|
2013-01-11 17:40:02 +04:00
|
|
|
# still raised if decoder fails to return anything
|
2020-04-07 09:58:21 +03:00
|
|
|
raise_oserror(err_code)
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
return Image.Image.load(self)
|
|
|
|
|
|
|
|
def load_prepare(self):
|
|
|
|
# create image memory if necessary
|
2019-03-21 16:28:20 +03:00
|
|
|
if not self.im or self.im.mode != self.mode or self.im.size != self.size:
|
2010-07-31 06:52:47 +04:00
|
|
|
self.im = Image.core.new(self.mode, self.size)
|
|
|
|
# create palette (optional)
|
|
|
|
if self.mode == "P":
|
|
|
|
Image.Image.load(self)
|
|
|
|
|
|
|
|
def load_end(self):
|
|
|
|
# may be overridden
|
|
|
|
pass
|
|
|
|
|
|
|
|
# may be defined for contained formats
|
|
|
|
# def load_seek(self, pos):
|
|
|
|
# pass
|
|
|
|
|
|
|
|
# may be defined for blocked formats (e.g. PNG)
|
|
|
|
# def load_read(self, bytes):
|
|
|
|
# pass
|
|
|
|
|
2017-09-30 06:32:43 +03:00
|
|
|
def _seek_check(self, frame):
|
2019-03-21 16:28:20 +03:00
|
|
|
if (
|
|
|
|
frame < self._min_frame
|
2017-09-30 06:32:43 +03:00
|
|
|
# Only check upper limit on frames if additional seek operations
|
|
|
|
# are not required to do so
|
2019-03-21 16:28:20 +03:00
|
|
|
or (
|
|
|
|
not (hasattr(self, "_n_frames") and self._n_frames is None)
|
|
|
|
and frame >= self.n_frames + self._min_frame
|
|
|
|
)
|
|
|
|
):
|
2017-09-30 06:32:43 +03:00
|
|
|
raise EOFError("attempt to seek outside sequence")
|
|
|
|
|
|
|
|
return self.tell() != frame
|
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
class StubImageFile(ImageFile):
|
2013-07-19 19:11:26 +04:00
|
|
|
"""
|
|
|
|
Base class for stub image loaders.
|
|
|
|
|
|
|
|
A stub loader is an image loader that can identify files of a
|
|
|
|
certain format, but relies on external code to load the file.
|
|
|
|
"""
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
def _open(self):
|
2019-03-21 16:28:20 +03:00
|
|
|
raise NotImplementedError("StubImageFile subclass must implement _open")
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
def load(self):
|
|
|
|
loader = self._load()
|
|
|
|
if loader is None:
|
2020-07-16 12:43:29 +03:00
|
|
|
raise OSError(f"cannot find loader for this {self.format} file")
|
2010-07-31 06:52:47 +04:00
|
|
|
image = loader.load(self)
|
|
|
|
assert image is not None
|
|
|
|
# become the other object (!)
|
|
|
|
self.__class__ = image.__class__
|
|
|
|
self.__dict__ = image.__dict__
|
2022-02-02 03:49:31 +03:00
|
|
|
return image.load()
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
def _load(self):
|
2019-02-03 07:58:24 +03:00
|
|
|
"""(Hook) Find actual image loader."""
|
2019-03-21 16:28:20 +03:00
|
|
|
raise NotImplementedError("StubImageFile subclass must implement _load")
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
|
2019-09-30 17:56:31 +03:00
|
|
|
class Parser:
|
2013-07-19 19:11:26 +04:00
|
|
|
"""
|
|
|
|
Incremental image parser. This class implements the standard
|
|
|
|
feed/close consumer interface.
|
|
|
|
"""
|
2019-03-21 16:28:20 +03:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
incremental = None
|
|
|
|
image = None
|
|
|
|
data = None
|
|
|
|
decoder = None
|
2015-04-24 11:24:52 +03:00
|
|
|
offset = 0
|
2010-07-31 06:52:47 +04:00
|
|
|
finished = 0
|
|
|
|
|
|
|
|
def reset(self):
|
2013-07-19 19:11:26 +04:00
|
|
|
"""
|
|
|
|
(Consumer) Reset the parser. Note that you can only call this
|
|
|
|
method immediately after you've created a parser; parser
|
|
|
|
instances cannot be reused.
|
|
|
|
"""
|
2010-07-31 06:52:47 +04:00
|
|
|
assert self.data is None, "cannot reuse parsers"
|
|
|
|
|
|
|
|
def feed(self, data):
|
2013-07-19 19:11:26 +04:00
|
|
|
"""
|
|
|
|
(Consumer) Feed data to the parser.
|
|
|
|
|
2013-10-13 00:57:27 +04:00
|
|
|
:param data: A string buffer.
|
2020-04-07 09:58:21 +03:00
|
|
|
:exception OSError: If the parser failed to parse the image file.
|
2013-07-19 19:11:26 +04:00
|
|
|
"""
|
2010-07-31 06:52:47 +04:00
|
|
|
# collect data
|
|
|
|
|
|
|
|
if self.finished:
|
|
|
|
return
|
|
|
|
|
|
|
|
if self.data is None:
|
|
|
|
self.data = data
|
|
|
|
else:
|
|
|
|
self.data = self.data + data
|
|
|
|
|
|
|
|
# parse what we have
|
|
|
|
if self.decoder:
|
|
|
|
|
|
|
|
if self.offset > 0:
|
|
|
|
# skip header
|
|
|
|
skip = min(len(self.data), self.offset)
|
|
|
|
self.data = self.data[skip:]
|
|
|
|
self.offset = self.offset - skip
|
|
|
|
if self.offset > 0 or not self.data:
|
|
|
|
return
|
|
|
|
|
|
|
|
n, e = self.decoder.decode(self.data)
|
|
|
|
|
|
|
|
if n < 0:
|
|
|
|
# end of stream
|
|
|
|
self.data = None
|
|
|
|
self.finished = 1
|
|
|
|
if e < 0:
|
|
|
|
# decoding error
|
|
|
|
self.image = None
|
2020-04-07 09:58:21 +03:00
|
|
|
raise_oserror(e)
|
2010-07-31 06:52:47 +04:00
|
|
|
else:
|
|
|
|
# end of image
|
|
|
|
return
|
|
|
|
self.data = self.data[n:]
|
|
|
|
|
|
|
|
elif self.image:
|
|
|
|
|
|
|
|
# if we end up here with no decoder, this file cannot
|
|
|
|
# be incrementally parsed. wait until we've gotten all
|
|
|
|
# available data
|
|
|
|
pass
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
# attempt to open this file
|
|
|
|
try:
|
2017-05-13 01:48:03 +03:00
|
|
|
with io.BytesIO(self.data) as fp:
|
2010-07-31 06:52:47 +04:00
|
|
|
im = Image.open(fp)
|
2019-09-30 17:56:31 +03:00
|
|
|
except OSError:
|
py3k: The big push
There are two main issues fixed with this commit:
* bytes vs. str: All file, image, and palette data are now handled as
bytes. A new _binary module consolidates the hacks needed to do this
across Python versions. tostring/fromstring methods have been renamed to
tobytes/frombytes, but the Python 2.6/2.7 versions alias them to the old
names for compatibility. Users should move to tobytes/frombytes.
One other potentially-breaking change is that text data in image files
(such as tags, comments) are now explicitly handled with a specific
character encoding in mind. This works well with the Unicode str in
Python 3, but may trip up old code expecting a straight byte-for-byte
translation to a Python string. This also required a change to Gohlke's
tags tests (in Tests/test_file_png.py) to expect Unicode strings from
the code.
* True div vs. floor div: Many division operations used the "/" operator
to do floor division, which is now the "//" operator in Python 3. These
were fixed.
As of this commit, on the first pass, I have one failing test (improper
handling of a slice object in a C module, test_imagepath.py) in Python 3,
and three that that I haven't tried running yet (test_imagegl,
test_imagegrab, and test_imageqt). I also haven't tested anything on
Windows. All but the three skipped tests run flawlessly against Pythons
2.6 and 2.7.
2012-10-21 01:01:53 +04:00
|
|
|
# traceback.print_exc()
|
2014-08-26 17:47:10 +04:00
|
|
|
pass # not enough data
|
2010-07-31 06:52:47 +04:00
|
|
|
else:
|
|
|
|
flag = hasattr(im, "load_seek") or hasattr(im, "load_read")
|
|
|
|
if flag or len(im.tile) != 1:
|
|
|
|
# custom load code, or multiple tiles
|
|
|
|
self.decode = None
|
|
|
|
else:
|
|
|
|
# initialize decoder
|
|
|
|
im.load_prepare()
|
|
|
|
d, e, o, a = im.tile[0]
|
|
|
|
im.tile = []
|
2019-03-21 16:28:20 +03:00
|
|
|
self.decoder = Image._getdecoder(im.mode, d, a, im.decoderconfig)
|
2010-07-31 06:52:47 +04:00
|
|
|
self.decoder.setimage(im.im, e)
|
|
|
|
|
|
|
|
# calculate decoder offset
|
|
|
|
self.offset = o
|
|
|
|
if self.offset <= len(self.data):
|
2019-03-21 16:28:20 +03:00
|
|
|
self.data = self.data[self.offset :]
|
2010-07-31 06:52:47 +04:00
|
|
|
self.offset = 0
|
|
|
|
|
|
|
|
self.image = im
|
|
|
|
|
2017-10-07 15:18:23 +03:00
|
|
|
def __enter__(self):
|
|
|
|
return self
|
|
|
|
|
|
|
|
def __exit__(self, *args):
|
|
|
|
self.close()
|
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
def close(self):
|
2013-07-19 19:11:26 +04:00
|
|
|
"""
|
|
|
|
(Consumer) Close the stream.
|
|
|
|
|
|
|
|
:returns: An image object.
|
2020-04-07 09:58:21 +03:00
|
|
|
:exception OSError: If the parser failed to parse the image file either
|
2013-10-13 00:57:27 +04:00
|
|
|
because it cannot be identified or cannot be
|
|
|
|
decoded.
|
2013-07-19 19:11:26 +04:00
|
|
|
"""
|
2010-07-31 06:52:47 +04:00
|
|
|
# finish decoding
|
|
|
|
if self.decoder:
|
|
|
|
# get rid of what's left in the buffers
|
py3k: The big push
There are two main issues fixed with this commit:
* bytes vs. str: All file, image, and palette data are now handled as
bytes. A new _binary module consolidates the hacks needed to do this
across Python versions. tostring/fromstring methods have been renamed to
tobytes/frombytes, but the Python 2.6/2.7 versions alias them to the old
names for compatibility. Users should move to tobytes/frombytes.
One other potentially-breaking change is that text data in image files
(such as tags, comments) are now explicitly handled with a specific
character encoding in mind. This works well with the Unicode str in
Python 3, but may trip up old code expecting a straight byte-for-byte
translation to a Python string. This also required a change to Gohlke's
tags tests (in Tests/test_file_png.py) to expect Unicode strings from
the code.
* True div vs. floor div: Many division operations used the "/" operator
to do floor division, which is now the "//" operator in Python 3. These
were fixed.
As of this commit, on the first pass, I have one failing test (improper
handling of a slice object in a C module, test_imagepath.py) in Python 3,
and three that that I haven't tried running yet (test_imagegl,
test_imagegrab, and test_imageqt). I also haven't tested anything on
Windows. All but the three skipped tests run flawlessly against Pythons
2.6 and 2.7.
2012-10-21 01:01:53 +04:00
|
|
|
self.feed(b"")
|
2010-07-31 06:52:47 +04:00
|
|
|
self.data = self.decoder = None
|
|
|
|
if not self.finished:
|
2019-09-30 17:56:31 +03:00
|
|
|
raise OSError("image was incomplete")
|
2010-07-31 06:52:47 +04:00
|
|
|
if not self.image:
|
2019-09-30 17:56:31 +03:00
|
|
|
raise OSError("cannot parse this image")
|
2010-07-31 06:52:47 +04:00
|
|
|
if self.data:
|
|
|
|
# incremental parsing not possible; reopen the file
|
|
|
|
# not that we have all data
|
2017-05-13 01:48:03 +03:00
|
|
|
with io.BytesIO(self.data) as fp:
|
|
|
|
try:
|
|
|
|
self.image = Image.open(fp)
|
|
|
|
finally:
|
|
|
|
self.image.load()
|
2010-07-31 06:52:47 +04:00
|
|
|
return self.image
|
|
|
|
|
2014-08-26 17:47:10 +04:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
# --------------------------------------------------------------------
|
|
|
|
|
2019-03-21 16:28:20 +03:00
|
|
|
|
2013-03-23 09:27:12 +04:00
|
|
|
def _save(im, fp, tile, bufsize=0):
|
2013-07-19 19:11:26 +04:00
|
|
|
"""Helper to save image based on tile list
|
|
|
|
|
|
|
|
:param im: Image object.
|
|
|
|
:param fp: File object.
|
|
|
|
:param tile: Tile list.
|
|
|
|
:param bufsize: Optional buffer size
|
|
|
|
"""
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
im.load()
|
|
|
|
if not hasattr(im, "encoderconfig"):
|
|
|
|
im.encoderconfig = ()
|
2012-10-17 07:58:14 +04:00
|
|
|
tile.sort(key=_tilesort)
|
2010-07-31 06:52:47 +04:00
|
|
|
# FIXME: make MAXBLOCK a configuration parameter
|
2014-08-26 17:47:10 +04:00
|
|
|
# It would be great if we could have the encoder specify what it needs
|
2013-07-19 19:11:26 +04:00
|
|
|
# But, it would need at least the image size in most cases. RawEncode is
|
|
|
|
# a tricky case.
|
2014-08-26 17:47:10 +04:00
|
|
|
bufsize = max(MAXBLOCK, bufsize, im.size[0] * 4) # see RawEncode.c
|
2010-07-31 06:52:47 +04:00
|
|
|
try:
|
|
|
|
fh = fp.fileno()
|
|
|
|
fp.flush()
|
2020-09-01 20:16:46 +03:00
|
|
|
except (AttributeError, io.UnsupportedOperation) as exc:
|
2010-07-31 06:52:47 +04:00
|
|
|
# compress to Python file-compatible object
|
|
|
|
for e, b, o, a in tile:
|
|
|
|
e = Image._getencoder(im.mode, e, a, im.encoderconfig)
|
2022-02-28 06:12:56 +03:00
|
|
|
try:
|
|
|
|
if o > 0:
|
|
|
|
fp.seek(o)
|
|
|
|
e.setimage(im.im, b)
|
|
|
|
if e.pushes_fd:
|
|
|
|
e.setfd(fp)
|
|
|
|
l, s = e.encode_to_pyfd()
|
|
|
|
else:
|
|
|
|
while True:
|
|
|
|
l, s, d = e.encode(bufsize)
|
|
|
|
fp.write(d)
|
|
|
|
if s:
|
|
|
|
break
|
|
|
|
if s < 0:
|
|
|
|
raise OSError(f"encoder error {s} when writing image file") from exc
|
|
|
|
finally:
|
|
|
|
e.cleanup()
|
2010-07-31 06:52:47 +04:00
|
|
|
else:
|
|
|
|
# slight speedup: compress to real file object
|
|
|
|
for e, b, o, a in tile:
|
|
|
|
e = Image._getencoder(im.mode, e, a, im.encoderconfig)
|
2022-02-28 06:12:56 +03:00
|
|
|
try:
|
|
|
|
if o > 0:
|
|
|
|
fp.seek(o)
|
|
|
|
e.setimage(im.im, b)
|
|
|
|
if e.pushes_fd:
|
|
|
|
e.setfd(fp)
|
|
|
|
l, s = e.encode_to_pyfd()
|
|
|
|
else:
|
|
|
|
s = e.encode_to_file(fh, bufsize)
|
|
|
|
if s < 0:
|
|
|
|
raise OSError(f"encoder error {s} when writing image file")
|
|
|
|
finally:
|
|
|
|
e.cleanup()
|
2015-09-02 16:48:22 +03:00
|
|
|
if hasattr(fp, "flush"):
|
2010-07-31 06:52:47 +04:00
|
|
|
fp.flush()
|
|
|
|
|
|
|
|
|
|
|
|
def _safe_read(fp, size):
|
2013-07-19 19:11:26 +04:00
|
|
|
"""
|
|
|
|
Reads large blocks in a safe way. Unlike fp.read(n), this function
|
|
|
|
doesn't trust the user. If the requested size is larger than
|
|
|
|
SAFEBLOCK, the file is read block by block.
|
|
|
|
|
|
|
|
:param fp: File handle. Must implement a <b>read</b> method.
|
|
|
|
:param size: Number of bytes to read.
|
2021-03-07 21:04:25 +03:00
|
|
|
:returns: A string containing <i>size</i> bytes of data.
|
|
|
|
|
2021-04-01 17:41:46 +03:00
|
|
|
Raises an OSError if the file is truncated and the read cannot be completed
|
2021-03-07 21:04:25 +03:00
|
|
|
|
2013-07-19 19:11:26 +04:00
|
|
|
"""
|
2010-07-31 06:52:47 +04:00
|
|
|
if size <= 0:
|
py3k: The big push
There are two main issues fixed with this commit:
* bytes vs. str: All file, image, and palette data are now handled as
bytes. A new _binary module consolidates the hacks needed to do this
across Python versions. tostring/fromstring methods have been renamed to
tobytes/frombytes, but the Python 2.6/2.7 versions alias them to the old
names for compatibility. Users should move to tobytes/frombytes.
One other potentially-breaking change is that text data in image files
(such as tags, comments) are now explicitly handled with a specific
character encoding in mind. This works well with the Unicode str in
Python 3, but may trip up old code expecting a straight byte-for-byte
translation to a Python string. This also required a change to Gohlke's
tags tests (in Tests/test_file_png.py) to expect Unicode strings from
the code.
* True div vs. floor div: Many division operations used the "/" operator
to do floor division, which is now the "//" operator in Python 3. These
were fixed.
As of this commit, on the first pass, I have one failing test (improper
handling of a slice object in a C module, test_imagepath.py) in Python 3,
and three that that I haven't tried running yet (test_imagegl,
test_imagegrab, and test_imageqt). I also haven't tested anything on
Windows. All but the three skipped tests run flawlessly against Pythons
2.6 and 2.7.
2012-10-21 01:01:53 +04:00
|
|
|
return b""
|
2010-07-31 06:52:47 +04:00
|
|
|
if size <= SAFEBLOCK:
|
2021-03-07 21:04:25 +03:00
|
|
|
data = fp.read(size)
|
|
|
|
if len(data) < size:
|
|
|
|
raise OSError("Truncated File Read")
|
|
|
|
return data
|
2010-07-31 06:52:47 +04:00
|
|
|
data = []
|
2021-12-07 03:40:44 +03:00
|
|
|
remaining_size = size
|
|
|
|
while remaining_size > 0:
|
|
|
|
block = fp.read(min(remaining_size, SAFEBLOCK))
|
2010-07-31 06:52:47 +04:00
|
|
|
if not block:
|
|
|
|
break
|
|
|
|
data.append(block)
|
2021-12-07 03:40:44 +03:00
|
|
|
remaining_size -= len(block)
|
2021-03-07 21:04:25 +03:00
|
|
|
if sum(len(d) for d in data) < size:
|
|
|
|
raise OSError("Truncated File Read")
|
py3k: The big push
There are two main issues fixed with this commit:
* bytes vs. str: All file, image, and palette data are now handled as
bytes. A new _binary module consolidates the hacks needed to do this
across Python versions. tostring/fromstring methods have been renamed to
tobytes/frombytes, but the Python 2.6/2.7 versions alias them to the old
names for compatibility. Users should move to tobytes/frombytes.
One other potentially-breaking change is that text data in image files
(such as tags, comments) are now explicitly handled with a specific
character encoding in mind. This works well with the Unicode str in
Python 3, but may trip up old code expecting a straight byte-for-byte
translation to a Python string. This also required a change to Gohlke's
tags tests (in Tests/test_file_png.py) to expect Unicode strings from
the code.
* True div vs. floor div: Many division operations used the "/" operator
to do floor division, which is now the "//" operator in Python 3. These
were fixed.
As of this commit, on the first pass, I have one failing test (improper
handling of a slice object in a C module, test_imagepath.py) in Python 3,
and three that that I haven't tried running yet (test_imagegl,
test_imagegrab, and test_imageqt). I also haven't tested anything on
Windows. All but the three skipped tests run flawlessly against Pythons
2.6 and 2.7.
2012-10-21 01:01:53 +04:00
|
|
|
return b"".join(data)
|
2016-05-31 11:10:01 +03:00
|
|
|
|
|
|
|
|
2019-09-30 17:56:31 +03:00
|
|
|
class PyCodecState:
|
2016-05-31 11:10:01 +03:00
|
|
|
def __init__(self):
|
|
|
|
self.xsize = 0
|
|
|
|
self.ysize = 0
|
|
|
|
self.xoff = 0
|
|
|
|
self.yoff = 0
|
|
|
|
|
|
|
|
def extents(self):
|
2019-03-21 16:28:20 +03:00
|
|
|
return (self.xoff, self.yoff, self.xoff + self.xsize, self.yoff + self.ysize)
|
2016-05-31 11:10:01 +03:00
|
|
|
|
2017-04-20 14:14:23 +03:00
|
|
|
|
2022-02-25 08:07:01 +03:00
|
|
|
class PyCodec:
|
2016-05-31 11:10:01 +03:00
|
|
|
def __init__(self, mode, *args):
|
|
|
|
self.im = None
|
|
|
|
self.state = PyCodecState()
|
|
|
|
self.fd = None
|
|
|
|
self.mode = mode
|
|
|
|
self.init(args)
|
|
|
|
|
|
|
|
def init(self, args):
|
2016-07-08 12:52:30 +03:00
|
|
|
"""
|
2022-02-25 08:07:01 +03:00
|
|
|
Override to perform codec specific initialization
|
2016-07-08 12:52:30 +03:00
|
|
|
|
|
|
|
:param args: Array of args items from the tile entry
|
|
|
|
:returns: None
|
|
|
|
"""
|
2016-05-31 11:10:01 +03:00
|
|
|
self.args = args
|
2017-04-20 14:14:23 +03:00
|
|
|
|
2016-05-31 11:10:01 +03:00
|
|
|
def cleanup(self):
|
2016-07-08 12:52:30 +03:00
|
|
|
"""
|
2022-02-25 08:07:01 +03:00
|
|
|
Override to perform codec specific cleanup
|
2016-07-08 12:52:30 +03:00
|
|
|
|
|
|
|
:returns: None
|
|
|
|
"""
|
2016-05-31 11:10:01 +03:00
|
|
|
pass
|
|
|
|
|
|
|
|
def setfd(self, fd):
|
2016-07-08 12:52:30 +03:00
|
|
|
"""
|
2022-02-25 08:07:01 +03:00
|
|
|
Called from ImageFile to set the Python file-like object
|
2016-07-08 12:52:30 +03:00
|
|
|
|
2022-02-25 08:07:01 +03:00
|
|
|
:param fd: A Python file-like object
|
2016-07-08 12:52:30 +03:00
|
|
|
:returns: None
|
|
|
|
"""
|
2016-05-31 11:10:01 +03:00
|
|
|
self.fd = fd
|
2017-04-20 14:14:23 +03:00
|
|
|
|
2016-05-31 11:10:01 +03:00
|
|
|
def setimage(self, im, extents=None):
|
2016-07-08 12:52:30 +03:00
|
|
|
"""
|
2022-02-25 08:07:01 +03:00
|
|
|
Called from ImageFile to set the core output image for the codec
|
2016-07-08 12:52:30 +03:00
|
|
|
|
|
|
|
:param im: A core image object
|
|
|
|
:param extents: a 4 tuple of (x0, y0, x1, y1) defining the rectangle
|
|
|
|
for this tile
|
|
|
|
:returns: None
|
|
|
|
"""
|
2017-04-20 14:14:23 +03:00
|
|
|
|
2016-05-31 11:10:01 +03:00
|
|
|
# following c code
|
|
|
|
self.im = im
|
|
|
|
|
|
|
|
if extents:
|
|
|
|
(x0, y0, x1, y1) = extents
|
|
|
|
else:
|
|
|
|
(x0, y0, x1, y1) = (0, 0, 0, 0)
|
|
|
|
|
2017-02-23 13:20:25 +03:00
|
|
|
if x0 == 0 and x1 == 0:
|
2016-05-31 11:10:01 +03:00
|
|
|
self.state.xsize, self.state.ysize = self.im.size
|
|
|
|
else:
|
|
|
|
self.state.xoff = x0
|
|
|
|
self.state.yoff = y0
|
|
|
|
self.state.xsize = x1 - x0
|
|
|
|
self.state.ysize = y1 - y0
|
|
|
|
|
|
|
|
if self.state.xsize <= 0 or self.state.ysize <= 0:
|
2017-02-23 13:20:25 +03:00
|
|
|
raise ValueError("Size cannot be negative")
|
2017-04-20 14:14:23 +03:00
|
|
|
|
2019-03-21 16:28:20 +03:00
|
|
|
if (
|
|
|
|
self.state.xsize + self.state.xoff > self.im.size[0]
|
|
|
|
or self.state.ysize + self.state.yoff > self.im.size[1]
|
|
|
|
):
|
2016-05-31 11:10:01 +03:00
|
|
|
raise ValueError("Tile cannot extend outside image")
|
2017-04-20 14:14:23 +03:00
|
|
|
|
2022-02-25 08:07:01 +03:00
|
|
|
|
|
|
|
class PyDecoder(PyCodec):
|
|
|
|
"""
|
|
|
|
Python implementation of a format decoder. Override this class and
|
|
|
|
add the decoding logic in the :meth:`decode` method.
|
|
|
|
|
|
|
|
See :ref:`Writing Your Own File Codec in Python<file-codecs-py>`
|
|
|
|
"""
|
|
|
|
|
|
|
|
_pulls_fd = False
|
|
|
|
|
|
|
|
@property
|
|
|
|
def pulls_fd(self):
|
|
|
|
return self._pulls_fd
|
|
|
|
|
|
|
|
def decode(self, buffer):
|
|
|
|
"""
|
|
|
|
Override to perform the decoding process.
|
|
|
|
|
|
|
|
:param buffer: A bytes object with the data to be decoded.
|
|
|
|
:returns: A tuple of ``(bytes consumed, errcode)``.
|
|
|
|
If finished with decoding return 0 for the bytes consumed.
|
|
|
|
Err codes are from :data:`.ImageFile.ERRORS`.
|
|
|
|
"""
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
2016-05-31 11:10:01 +03:00
|
|
|
def set_as_raw(self, data, rawmode=None):
|
2016-07-08 12:52:30 +03:00
|
|
|
"""
|
|
|
|
Convenience method to set the internal image from a stream of raw data
|
|
|
|
|
|
|
|
:param data: Bytes to be set
|
2018-06-24 15:32:25 +03:00
|
|
|
:param rawmode: The rawmode to be used for the decoder.
|
|
|
|
If not specified, it will default to the mode of the image
|
2016-07-08 12:52:30 +03:00
|
|
|
:returns: None
|
|
|
|
"""
|
2017-04-20 14:14:23 +03:00
|
|
|
|
2016-05-31 11:10:01 +03:00
|
|
|
if not rawmode:
|
|
|
|
rawmode = self.mode
|
2019-03-21 16:28:20 +03:00
|
|
|
d = Image._getdecoder(self.mode, "raw", (rawmode))
|
2016-05-31 11:10:01 +03:00
|
|
|
d.setimage(self.im, self.state.extents())
|
|
|
|
s = d.decode(data)
|
2017-04-20 14:14:23 +03:00
|
|
|
|
2016-05-31 11:10:01 +03:00
|
|
|
if s[0] >= 0:
|
|
|
|
raise ValueError("not enough image data")
|
|
|
|
if s[1] != 0:
|
|
|
|
raise ValueError("cannot decode image data")
|
2022-02-25 08:07:01 +03:00
|
|
|
|
|
|
|
|
|
|
|
class PyEncoder(PyCodec):
|
|
|
|
"""
|
|
|
|
Python implementation of a format encoder. Override this class and
|
|
|
|
add the decoding logic in the :meth:`encode` method.
|
|
|
|
"""
|
|
|
|
|
|
|
|
_pushes_fd = False
|
|
|
|
|
|
|
|
@property
|
|
|
|
def pushes_fd(self):
|
|
|
|
return self._pushes_fd
|
|
|
|
|
|
|
|
def encode(self, bufsize):
|
|
|
|
"""
|
|
|
|
Override to perform the encoding process.
|
|
|
|
|
|
|
|
:param bufsize: Buffer size.
|
|
|
|
:returns: A tuple of ``(bytes encoded, errcode, bytes)``.
|
|
|
|
If finished with encoding return 1 for the error code.
|
|
|
|
Err codes are from :data:`.ImageFile.ERRORS`.
|
|
|
|
"""
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
|
|
|
def encode_to_pyfd(self):
|
|
|
|
"""
|
|
|
|
:returns: A tuple of ``(bytes consumed, errcode)``.
|
|
|
|
Err codes are from :data:`.ImageFile.ERRORS`.
|
|
|
|
"""
|
|
|
|
if not self.pushes_fd:
|
|
|
|
return 0, -8 # bad configuration
|
|
|
|
bytes_consumed, errcode, data = self.encode(0)
|
|
|
|
if data:
|
|
|
|
self.fd.write(data)
|
|
|
|
return bytes_consumed, errcode
|
|
|
|
|
|
|
|
def encode_to_file(self, fh, bufsize):
|
|
|
|
"""
|
|
|
|
:param fh: File handle.
|
|
|
|
:param bufsize: Buffer size.
|
|
|
|
|
|
|
|
:returns: If finished successfully, return 0.
|
|
|
|
Otherwise, return an error code. Err codes are from
|
|
|
|
:data:`.ImageFile.ERRORS`.
|
|
|
|
"""
|
|
|
|
errcode = 0
|
|
|
|
while errcode == 0:
|
|
|
|
status, errcode, buf = self.encode(bufsize)
|
|
|
|
if status > 0:
|
|
|
|
fh.write(buf[status:])
|
|
|
|
return errcode
|