From 25bc5c76ab96aed7c40cd14c536d59be053b6327 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 31 May 2016 01:10:01 -0700 Subject: [PATCH] Pure python decoder registry --- PIL/Image.py | 39 +++++++++++++++++++++++ PIL/ImageFile.py | 80 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+) diff --git a/PIL/Image.py b/PIL/Image.py index fd6bfecb6..cb0a19bad 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -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. diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index 8f3ee524c..f9b966221 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -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")