ImageFile.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832
  1. #
  2. # The Python Imaging Library.
  3. # $Id$
  4. #
  5. # base class for image file handlers
  6. #
  7. # history:
  8. # 1995-09-09 fl Created
  9. # 1996-03-11 fl Fixed load mechanism.
  10. # 1996-04-15 fl Added pcx/xbm decoders.
  11. # 1996-04-30 fl Added encoders.
  12. # 1996-12-14 fl Added load helpers
  13. # 1997-01-11 fl Use encode_to_file where possible
  14. # 1997-08-27 fl Flush output in _save
  15. # 1998-03-05 fl Use memory mapping for some modes
  16. # 1999-02-04 fl Use memory mapping also for "I;16" and "I;16B"
  17. # 1999-05-31 fl Added image parser
  18. # 2000-10-12 fl Set readonly flag on memory-mapped images
  19. # 2002-03-20 fl Use better messages for common decoder errors
  20. # 2003-04-21 fl Fall back on mmap/map_buffer if map is not available
  21. # 2003-10-30 fl Added StubImageFile class
  22. # 2004-02-25 fl Made incremental parser more robust
  23. #
  24. # Copyright (c) 1997-2004 by Secret Labs AB
  25. # Copyright (c) 1995-2004 by Fredrik Lundh
  26. #
  27. # See the README file for information on usage and redistribution.
  28. #
  29. from __future__ import annotations
  30. import abc
  31. import io
  32. import itertools
  33. import os
  34. import struct
  35. import sys
  36. from typing import IO, TYPE_CHECKING, Any, NamedTuple, cast
  37. from . import Image
  38. from ._deprecate import deprecate
  39. from ._util import is_path
  40. if TYPE_CHECKING:
  41. from ._typing import StrOrBytesPath
  42. MAXBLOCK = 65536
  43. SAFEBLOCK = 1024 * 1024
  44. LOAD_TRUNCATED_IMAGES = False
  45. """Whether or not to load truncated image files. User code may change this."""
  46. ERRORS = {
  47. -1: "image buffer overrun error",
  48. -2: "decoding error",
  49. -3: "unknown error",
  50. -8: "bad configuration",
  51. -9: "out of memory error",
  52. }
  53. """
  54. Dict of known error codes returned from :meth:`.PyDecoder.decode`,
  55. :meth:`.PyEncoder.encode` :meth:`.PyEncoder.encode_to_pyfd` and
  56. :meth:`.PyEncoder.encode_to_file`.
  57. """
  58. #
  59. # --------------------------------------------------------------------
  60. # Helpers
  61. def _get_oserror(error: int, *, encoder: bool) -> OSError:
  62. try:
  63. msg = Image.core.getcodecstatus(error)
  64. except AttributeError:
  65. msg = ERRORS.get(error)
  66. if not msg:
  67. msg = f"{'encoder' if encoder else 'decoder'} error {error}"
  68. msg += f" when {'writing' if encoder else 'reading'} image file"
  69. return OSError(msg)
  70. def raise_oserror(error: int) -> OSError:
  71. deprecate(
  72. "raise_oserror",
  73. 12,
  74. action="It is only useful for translating error codes returned by a codec's "
  75. "decode() method, which ImageFile already does automatically.",
  76. )
  77. raise _get_oserror(error, encoder=False)
  78. def _tilesort(t: _Tile) -> int:
  79. # sort on offset
  80. return t[2]
  81. class _Tile(NamedTuple):
  82. codec_name: str
  83. extents: tuple[int, int, int, int] | None
  84. offset: int = 0
  85. args: tuple[Any, ...] | str | None = None
  86. #
  87. # --------------------------------------------------------------------
  88. # ImageFile base class
  89. class ImageFile(Image.Image):
  90. """Base class for image file format handlers."""
  91. def __init__(
  92. self, fp: StrOrBytesPath | IO[bytes], filename: str | bytes | None = None
  93. ) -> None:
  94. super().__init__()
  95. self._min_frame = 0
  96. self.custom_mimetype: str | None = None
  97. self.tile: list[_Tile] = []
  98. """ A list of tile descriptors """
  99. self.readonly = 1 # until we know better
  100. self.decoderconfig: tuple[Any, ...] = ()
  101. self.decodermaxblock = MAXBLOCK
  102. if is_path(fp):
  103. # filename
  104. self.fp = open(fp, "rb")
  105. self.filename = os.fspath(fp)
  106. self._exclusive_fp = True
  107. else:
  108. # stream
  109. self.fp = cast(IO[bytes], fp)
  110. self.filename = filename if filename is not None else ""
  111. # can be overridden
  112. self._exclusive_fp = False
  113. try:
  114. try:
  115. self._open()
  116. except (
  117. IndexError, # end of data
  118. TypeError, # end of data (ord)
  119. KeyError, # unsupported mode
  120. EOFError, # got header but not the first frame
  121. struct.error,
  122. ) as v:
  123. raise SyntaxError(v) from v
  124. if not self.mode or self.size[0] <= 0 or self.size[1] <= 0:
  125. msg = "not identified by this driver"
  126. raise SyntaxError(msg)
  127. except BaseException:
  128. # close the file only if we have opened it this constructor
  129. if self._exclusive_fp:
  130. self.fp.close()
  131. raise
  132. def _open(self) -> None:
  133. pass
  134. def get_format_mimetype(self) -> str | None:
  135. if self.custom_mimetype:
  136. return self.custom_mimetype
  137. if self.format is not None:
  138. return Image.MIME.get(self.format.upper())
  139. return None
  140. def __setstate__(self, state: list[Any]) -> None:
  141. self.tile = []
  142. super().__setstate__(state)
  143. def verify(self) -> None:
  144. """Check file integrity"""
  145. # raise exception if something's wrong. must be called
  146. # directly after open, and closes file when finished.
  147. if self._exclusive_fp:
  148. self.fp.close()
  149. self.fp = None
  150. def load(self) -> Image.core.PixelAccess | None:
  151. """Load image data based on tile list"""
  152. if not self.tile and self._im is None:
  153. msg = "cannot load this image"
  154. raise OSError(msg)
  155. pixel = Image.Image.load(self)
  156. if not self.tile:
  157. return pixel
  158. self.map: mmap.mmap | None = None
  159. use_mmap = self.filename and len(self.tile) == 1
  160. # As of pypy 2.1.0, memory mapping was failing here.
  161. use_mmap = use_mmap and not hasattr(sys, "pypy_version_info")
  162. readonly = 0
  163. # look for read/seek overrides
  164. if hasattr(self, "load_read"):
  165. read = self.load_read
  166. # don't use mmap if there are custom read/seek functions
  167. use_mmap = False
  168. else:
  169. read = self.fp.read
  170. if hasattr(self, "load_seek"):
  171. seek = self.load_seek
  172. use_mmap = False
  173. else:
  174. seek = self.fp.seek
  175. if use_mmap:
  176. # try memory mapping
  177. decoder_name, extents, offset, args = self.tile[0]
  178. if isinstance(args, str):
  179. args = (args, 0, 1)
  180. if (
  181. decoder_name == "raw"
  182. and isinstance(args, tuple)
  183. and len(args) >= 3
  184. and args[0] == self.mode
  185. and args[0] in Image._MAPMODES
  186. ):
  187. try:
  188. # use mmap, if possible
  189. import mmap
  190. with open(self.filename) as fp:
  191. self.map = mmap.mmap(fp.fileno(), 0, access=mmap.ACCESS_READ)
  192. if offset + self.size[1] * args[1] > self.map.size():
  193. msg = "buffer is not large enough"
  194. raise OSError(msg)
  195. self.im = Image.core.map_buffer(
  196. self.map, self.size, decoder_name, offset, args
  197. )
  198. readonly = 1
  199. # After trashing self.im,
  200. # we might need to reload the palette data.
  201. if self.palette:
  202. self.palette.dirty = 1
  203. except (AttributeError, OSError, ImportError):
  204. self.map = None
  205. self.load_prepare()
  206. err_code = -3 # initialize to unknown error
  207. if not self.map:
  208. # sort tiles in file order
  209. self.tile.sort(key=_tilesort)
  210. # FIXME: This is a hack to handle TIFF's JpegTables tag.
  211. prefix = getattr(self, "tile_prefix", b"")
  212. # Remove consecutive duplicates that only differ by their offset
  213. self.tile = [
  214. list(tiles)[-1]
  215. for _, tiles in itertools.groupby(
  216. self.tile, lambda tile: (tile[0], tile[1], tile[3])
  217. )
  218. ]
  219. for decoder_name, extents, offset, args in self.tile:
  220. seek(offset)
  221. decoder = Image._getdecoder(
  222. self.mode, decoder_name, args, self.decoderconfig
  223. )
  224. try:
  225. decoder.setimage(self.im, extents)
  226. if decoder.pulls_fd:
  227. decoder.setfd(self.fp)
  228. err_code = decoder.decode(b"")[1]
  229. else:
  230. b = prefix
  231. while True:
  232. try:
  233. s = read(self.decodermaxblock)
  234. except (IndexError, struct.error) as e:
  235. # truncated png/gif
  236. if LOAD_TRUNCATED_IMAGES:
  237. break
  238. else:
  239. msg = "image file is truncated"
  240. raise OSError(msg) from e
  241. if not s: # truncated jpeg
  242. if LOAD_TRUNCATED_IMAGES:
  243. break
  244. else:
  245. msg = (
  246. "image file is truncated "
  247. f"({len(b)} bytes not processed)"
  248. )
  249. raise OSError(msg)
  250. b = b + s
  251. n, err_code = decoder.decode(b)
  252. if n < 0:
  253. break
  254. b = b[n:]
  255. finally:
  256. # Need to cleanup here to prevent leaks
  257. decoder.cleanup()
  258. self.tile = []
  259. self.readonly = readonly
  260. self.load_end()
  261. if self._exclusive_fp and self._close_exclusive_fp_after_loading:
  262. self.fp.close()
  263. self.fp = None
  264. if not self.map and not LOAD_TRUNCATED_IMAGES and err_code < 0:
  265. # still raised if decoder fails to return anything
  266. raise _get_oserror(err_code, encoder=False)
  267. return Image.Image.load(self)
  268. def load_prepare(self) -> None:
  269. # create image memory if necessary
  270. if self._im is None:
  271. self.im = Image.core.new(self.mode, self.size)
  272. # create palette (optional)
  273. if self.mode == "P":
  274. Image.Image.load(self)
  275. def load_end(self) -> None:
  276. # may be overridden
  277. pass
  278. # may be defined for contained formats
  279. # def load_seek(self, pos: int) -> None:
  280. # pass
  281. # may be defined for blocked formats (e.g. PNG)
  282. # def load_read(self, read_bytes: int) -> bytes:
  283. # pass
  284. def _seek_check(self, frame: int) -> bool:
  285. if (
  286. frame < self._min_frame
  287. # Only check upper limit on frames if additional seek operations
  288. # are not required to do so
  289. or (
  290. not (hasattr(self, "_n_frames") and self._n_frames is None)
  291. and frame >= getattr(self, "n_frames") + self._min_frame
  292. )
  293. ):
  294. msg = "attempt to seek outside sequence"
  295. raise EOFError(msg)
  296. return self.tell() != frame
  297. class StubHandler:
  298. def open(self, im: StubImageFile) -> None:
  299. pass
  300. @abc.abstractmethod
  301. def load(self, im: StubImageFile) -> Image.Image:
  302. pass
  303. class StubImageFile(ImageFile):
  304. """
  305. Base class for stub image loaders.
  306. A stub loader is an image loader that can identify files of a
  307. certain format, but relies on external code to load the file.
  308. """
  309. def _open(self) -> None:
  310. msg = "StubImageFile subclass must implement _open"
  311. raise NotImplementedError(msg)
  312. def load(self) -> Image.core.PixelAccess | None:
  313. loader = self._load()
  314. if loader is None:
  315. msg = f"cannot find loader for this {self.format} file"
  316. raise OSError(msg)
  317. image = loader.load(self)
  318. assert image is not None
  319. # become the other object (!)
  320. self.__class__ = image.__class__ # type: ignore[assignment]
  321. self.__dict__ = image.__dict__
  322. return image.load()
  323. def _load(self) -> StubHandler | None:
  324. """(Hook) Find actual image loader."""
  325. msg = "StubImageFile subclass must implement _load"
  326. raise NotImplementedError(msg)
  327. class Parser:
  328. """
  329. Incremental image parser. This class implements the standard
  330. feed/close consumer interface.
  331. """
  332. incremental = None
  333. image: Image.Image | None = None
  334. data: bytes | None = None
  335. decoder: Image.core.ImagingDecoder | PyDecoder | None = None
  336. offset = 0
  337. finished = 0
  338. def reset(self) -> None:
  339. """
  340. (Consumer) Reset the parser. Note that you can only call this
  341. method immediately after you've created a parser; parser
  342. instances cannot be reused.
  343. """
  344. assert self.data is None, "cannot reuse parsers"
  345. def feed(self, data: bytes) -> None:
  346. """
  347. (Consumer) Feed data to the parser.
  348. :param data: A string buffer.
  349. :exception OSError: If the parser failed to parse the image file.
  350. """
  351. # collect data
  352. if self.finished:
  353. return
  354. if self.data is None:
  355. self.data = data
  356. else:
  357. self.data = self.data + data
  358. # parse what we have
  359. if self.decoder:
  360. if self.offset > 0:
  361. # skip header
  362. skip = min(len(self.data), self.offset)
  363. self.data = self.data[skip:]
  364. self.offset = self.offset - skip
  365. if self.offset > 0 or not self.data:
  366. return
  367. n, e = self.decoder.decode(self.data)
  368. if n < 0:
  369. # end of stream
  370. self.data = None
  371. self.finished = 1
  372. if e < 0:
  373. # decoding error
  374. self.image = None
  375. raise _get_oserror(e, encoder=False)
  376. else:
  377. # end of image
  378. return
  379. self.data = self.data[n:]
  380. elif self.image:
  381. # if we end up here with no decoder, this file cannot
  382. # be incrementally parsed. wait until we've gotten all
  383. # available data
  384. pass
  385. else:
  386. # attempt to open this file
  387. try:
  388. with io.BytesIO(self.data) as fp:
  389. im = Image.open(fp)
  390. except OSError:
  391. pass # not enough data
  392. else:
  393. flag = hasattr(im, "load_seek") or hasattr(im, "load_read")
  394. if flag or len(im.tile) != 1:
  395. # custom load code, or multiple tiles
  396. self.decode = None
  397. else:
  398. # initialize decoder
  399. im.load_prepare()
  400. d, e, o, a = im.tile[0]
  401. im.tile = []
  402. self.decoder = Image._getdecoder(im.mode, d, a, im.decoderconfig)
  403. self.decoder.setimage(im.im, e)
  404. # calculate decoder offset
  405. self.offset = o
  406. if self.offset <= len(self.data):
  407. self.data = self.data[self.offset :]
  408. self.offset = 0
  409. self.image = im
  410. def __enter__(self) -> Parser:
  411. return self
  412. def __exit__(self, *args: object) -> None:
  413. self.close()
  414. def close(self) -> Image.Image:
  415. """
  416. (Consumer) Close the stream.
  417. :returns: An image object.
  418. :exception OSError: If the parser failed to parse the image file either
  419. because it cannot be identified or cannot be
  420. decoded.
  421. """
  422. # finish decoding
  423. if self.decoder:
  424. # get rid of what's left in the buffers
  425. self.feed(b"")
  426. self.data = self.decoder = None
  427. if not self.finished:
  428. msg = "image was incomplete"
  429. raise OSError(msg)
  430. if not self.image:
  431. msg = "cannot parse this image"
  432. raise OSError(msg)
  433. if self.data:
  434. # incremental parsing not possible; reopen the file
  435. # not that we have all data
  436. with io.BytesIO(self.data) as fp:
  437. try:
  438. self.image = Image.open(fp)
  439. finally:
  440. self.image.load()
  441. return self.image
  442. # --------------------------------------------------------------------
  443. def _save(im: Image.Image, fp: IO[bytes], tile: list[_Tile], bufsize: int = 0) -> None:
  444. """Helper to save image based on tile list
  445. :param im: Image object.
  446. :param fp: File object.
  447. :param tile: Tile list.
  448. :param bufsize: Optional buffer size
  449. """
  450. im.load()
  451. if not hasattr(im, "encoderconfig"):
  452. im.encoderconfig = ()
  453. tile.sort(key=_tilesort)
  454. # FIXME: make MAXBLOCK a configuration parameter
  455. # It would be great if we could have the encoder specify what it needs
  456. # But, it would need at least the image size in most cases. RawEncode is
  457. # a tricky case.
  458. bufsize = max(MAXBLOCK, bufsize, im.size[0] * 4) # see RawEncode.c
  459. try:
  460. fh = fp.fileno()
  461. fp.flush()
  462. _encode_tile(im, fp, tile, bufsize, fh)
  463. except (AttributeError, io.UnsupportedOperation) as exc:
  464. _encode_tile(im, fp, tile, bufsize, None, exc)
  465. if hasattr(fp, "flush"):
  466. fp.flush()
  467. def _encode_tile(
  468. im: Image.Image,
  469. fp: IO[bytes],
  470. tile: list[_Tile],
  471. bufsize: int,
  472. fh: int | None,
  473. exc: BaseException | None = None,
  474. ) -> None:
  475. for encoder_name, extents, offset, args in tile:
  476. if offset > 0:
  477. fp.seek(offset)
  478. encoder = Image._getencoder(im.mode, encoder_name, args, im.encoderconfig)
  479. try:
  480. encoder.setimage(im.im, extents)
  481. if encoder.pushes_fd:
  482. encoder.setfd(fp)
  483. errcode = encoder.encode_to_pyfd()[1]
  484. else:
  485. if exc:
  486. # compress to Python file-compatible object
  487. while True:
  488. errcode, data = encoder.encode(bufsize)[1:]
  489. fp.write(data)
  490. if errcode:
  491. break
  492. else:
  493. # slight speedup: compress to real file object
  494. assert fh is not None
  495. errcode = encoder.encode_to_file(fh, bufsize)
  496. if errcode < 0:
  497. raise _get_oserror(errcode, encoder=True) from exc
  498. finally:
  499. encoder.cleanup()
  500. def _safe_read(fp: IO[bytes], size: int) -> bytes:
  501. """
  502. Reads large blocks in a safe way. Unlike fp.read(n), this function
  503. doesn't trust the user. If the requested size is larger than
  504. SAFEBLOCK, the file is read block by block.
  505. :param fp: File handle. Must implement a <b>read</b> method.
  506. :param size: Number of bytes to read.
  507. :returns: A string containing <i>size</i> bytes of data.
  508. Raises an OSError if the file is truncated and the read cannot be completed
  509. """
  510. if size <= 0:
  511. return b""
  512. if size <= SAFEBLOCK:
  513. data = fp.read(size)
  514. if len(data) < size:
  515. msg = "Truncated File Read"
  516. raise OSError(msg)
  517. return data
  518. blocks: list[bytes] = []
  519. remaining_size = size
  520. while remaining_size > 0:
  521. block = fp.read(min(remaining_size, SAFEBLOCK))
  522. if not block:
  523. break
  524. blocks.append(block)
  525. remaining_size -= len(block)
  526. if sum(len(block) for block in blocks) < size:
  527. msg = "Truncated File Read"
  528. raise OSError(msg)
  529. return b"".join(blocks)
  530. class PyCodecState:
  531. def __init__(self) -> None:
  532. self.xsize = 0
  533. self.ysize = 0
  534. self.xoff = 0
  535. self.yoff = 0
  536. def extents(self) -> tuple[int, int, int, int]:
  537. return self.xoff, self.yoff, self.xoff + self.xsize, self.yoff + self.ysize
  538. class PyCodec:
  539. fd: IO[bytes] | None
  540. def __init__(self, mode: str, *args: Any) -> None:
  541. self.im: Image.core.ImagingCore | None = None
  542. self.state = PyCodecState()
  543. self.fd = None
  544. self.mode = mode
  545. self.init(args)
  546. def init(self, args: tuple[Any, ...]) -> None:
  547. """
  548. Override to perform codec specific initialization
  549. :param args: Tuple of arg items from the tile entry
  550. :returns: None
  551. """
  552. self.args = args
  553. def cleanup(self) -> None:
  554. """
  555. Override to perform codec specific cleanup
  556. :returns: None
  557. """
  558. pass
  559. def setfd(self, fd: IO[bytes]) -> None:
  560. """
  561. Called from ImageFile to set the Python file-like object
  562. :param fd: A Python file-like object
  563. :returns: None
  564. """
  565. self.fd = fd
  566. def setimage(
  567. self,
  568. im: Image.core.ImagingCore,
  569. extents: tuple[int, int, int, int] | None = None,
  570. ) -> None:
  571. """
  572. Called from ImageFile to set the core output image for the codec
  573. :param im: A core image object
  574. :param extents: a 4 tuple of (x0, y0, x1, y1) defining the rectangle
  575. for this tile
  576. :returns: None
  577. """
  578. # following c code
  579. self.im = im
  580. if extents:
  581. (x0, y0, x1, y1) = extents
  582. else:
  583. (x0, y0, x1, y1) = (0, 0, 0, 0)
  584. if x0 == 0 and x1 == 0:
  585. self.state.xsize, self.state.ysize = self.im.size
  586. else:
  587. self.state.xoff = x0
  588. self.state.yoff = y0
  589. self.state.xsize = x1 - x0
  590. self.state.ysize = y1 - y0
  591. if self.state.xsize <= 0 or self.state.ysize <= 0:
  592. msg = "Size cannot be negative"
  593. raise ValueError(msg)
  594. if (
  595. self.state.xsize + self.state.xoff > self.im.size[0]
  596. or self.state.ysize + self.state.yoff > self.im.size[1]
  597. ):
  598. msg = "Tile cannot extend outside image"
  599. raise ValueError(msg)
  600. class PyDecoder(PyCodec):
  601. """
  602. Python implementation of a format decoder. Override this class and
  603. add the decoding logic in the :meth:`decode` method.
  604. See :ref:`Writing Your Own File Codec in Python<file-codecs-py>`
  605. """
  606. _pulls_fd = False
  607. @property
  608. def pulls_fd(self) -> bool:
  609. return self._pulls_fd
  610. def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]:
  611. """
  612. Override to perform the decoding process.
  613. :param buffer: A bytes object with the data to be decoded.
  614. :returns: A tuple of ``(bytes consumed, errcode)``.
  615. If finished with decoding return -1 for the bytes consumed.
  616. Err codes are from :data:`.ImageFile.ERRORS`.
  617. """
  618. msg = "unavailable in base decoder"
  619. raise NotImplementedError(msg)
  620. def set_as_raw(
  621. self, data: bytes, rawmode: str | None = None, extra: tuple[Any, ...] = ()
  622. ) -> None:
  623. """
  624. Convenience method to set the internal image from a stream of raw data
  625. :param data: Bytes to be set
  626. :param rawmode: The rawmode to be used for the decoder.
  627. If not specified, it will default to the mode of the image
  628. :param extra: Extra arguments for the decoder.
  629. :returns: None
  630. """
  631. if not rawmode:
  632. rawmode = self.mode
  633. d = Image._getdecoder(self.mode, "raw", rawmode, extra)
  634. assert self.im is not None
  635. d.setimage(self.im, self.state.extents())
  636. s = d.decode(data)
  637. if s[0] >= 0:
  638. msg = "not enough image data"
  639. raise ValueError(msg)
  640. if s[1] != 0:
  641. msg = "cannot decode image data"
  642. raise ValueError(msg)
  643. class PyEncoder(PyCodec):
  644. """
  645. Python implementation of a format encoder. Override this class and
  646. add the decoding logic in the :meth:`encode` method.
  647. See :ref:`Writing Your Own File Codec in Python<file-codecs-py>`
  648. """
  649. _pushes_fd = False
  650. @property
  651. def pushes_fd(self) -> bool:
  652. return self._pushes_fd
  653. def encode(self, bufsize: int) -> tuple[int, int, bytes]:
  654. """
  655. Override to perform the encoding process.
  656. :param bufsize: Buffer size.
  657. :returns: A tuple of ``(bytes encoded, errcode, bytes)``.
  658. If finished with encoding return 1 for the error code.
  659. Err codes are from :data:`.ImageFile.ERRORS`.
  660. """
  661. msg = "unavailable in base encoder"
  662. raise NotImplementedError(msg)
  663. def encode_to_pyfd(self) -> tuple[int, int]:
  664. """
  665. If ``pushes_fd`` is ``True``, then this method will be used,
  666. and ``encode()`` will only be called once.
  667. :returns: A tuple of ``(bytes consumed, errcode)``.
  668. Err codes are from :data:`.ImageFile.ERRORS`.
  669. """
  670. if not self.pushes_fd:
  671. return 0, -8 # bad configuration
  672. bytes_consumed, errcode, data = self.encode(0)
  673. if data:
  674. assert self.fd is not None
  675. self.fd.write(data)
  676. return bytes_consumed, errcode
  677. def encode_to_file(self, fh: int, bufsize: int) -> int:
  678. """
  679. :param fh: File handle.
  680. :param bufsize: Buffer size.
  681. :returns: If finished successfully, return 0.
  682. Otherwise, return an error code. Err codes are from
  683. :data:`.ImageFile.ERRORS`.
  684. """
  685. errcode = 0
  686. while errcode == 0:
  687. status, errcode, buf = self.encode(bufsize)
  688. if status > 0:
  689. os.write(fh, buf[status:])
  690. return errcode