Pure python decoder registry

This commit is contained in:
wiredfool 2016-05-31 01:10:01 -07:00
parent c578237630
commit 25bc5c76ab
2 changed files with 119 additions and 0 deletions

View File

@ -208,6 +208,8 @@ MIME = {}
SAVE = {}
SAVE_ALL = {}
EXTENSION = {}
DECODERS = {}
ENCODERS = {}
# --------------------------------------------------------------------
# Modes supported by this version
@ -413,6 +415,11 @@ def _getdecoder(mode, decoder_name, args, extra=()):
elif not isinstance(args, tuple):
args = (args,)
try:
decoder = DECODERS[decoder_name]
return decoder(mode, *args + extra)
except KeyError:
pass
try:
# get decoder
decoder = getattr(core, decoder_name + "_decoder")
@ -430,6 +437,11 @@ def _getencoder(mode, encoder_name, args, extra=()):
elif not isinstance(args, tuple):
args = (args,)
try:
encoder = ENCODERS[encoder_name]
return encoder(mode, *args + extra)
except KeyError:
pass
try:
# get encoder
encoder = getattr(core, encoder_name + "_encoder")
@ -2526,6 +2538,33 @@ def registered_extensions():
init()
return EXTENSION
def register_decoder(name, decoder):
"""
Registers an image decoder. This function should not be
used in application code.
:param name: The name of the decoder
:param decoder: A callable(mode, args) that returns an
ImageFile.PyDecoder object
.. versionadded:: 3.3.0
"""
DECODERS[name] = decoder
def register_encoder(name, encoder):
"""
Registers an image encoder. This function should not be
used in application code.
:param name: The name of the encoder
:param encoder: A callable(mode, args) that returns an
ImageFile.PyEncoder object
.. versionadded:: 3.3.0
"""
ENCODERS[name] = encoder
# --------------------------------------------------------------------
# Simple display support. User code may override this.

View File

@ -520,3 +520,83 @@ def _safe_read(fp, size):
data.append(block)
size -= len(block)
return b"".join(data)
class PyCodecState(object):
def __init__(self):
self.xsize = 0
self.ysize = 0
self.xoff = 0
self.yoff = 0
def extents(self):
return (self.xoff, self.yoff,
self.xoff+self.xsize, self.yoff+self.ysize)
class PyDecoder(object):
_handles_eof = False
_pulls_fd = False
def __init__(self, mode, *args):
self.im = None
self.state = PyCodecState()
self.fd = None
self.mode = mode
self.init(args)
def init(self, args):
self.args = args
@property
def handles_eof(self):
return self._handles_eof
@property
def pulls_fd(self):
return self._pulls_fd
def decode(self, buffer):
raise NotImplementedError()
def cleanup(self):
pass
def setfd(self, fd):
self.fd = fd
def setimage(self, im, extents=None):
# following c code
self.im = im
if extents:
(x0, y0, x1, y1) = extents
else:
(x0, y0, x1, y1) = (0, 0, 0, 0)
if x0 ==0 and x1 ==0:
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:
raise ValueError("Size Cannot be Negative")
if (self.state.xsize + self.state.xoff > self.im.size[0] or
self.state.ysize + self.state.yoff > self.im.size[1]):
raise ValueError("Tile cannot extend outside image")
def set_as_raw(self, data, rawmode=None):
if not rawmode:
rawmode = self.mode
d = Image._getdecoder(self.mode, 'raw', (rawmode))
d.setimage(self.im, self.state.extents())
s = d.decode(data)
if s[0] >= 0:
raise ValueError("not enough image data")
if s[1] != 0:
raise ValueError("cannot decode image data")