mirror of
https://github.com/welton89/RRBEC.git
synced 2026-04-05 21:45:41 +00:00
nem sei pq tantos arquivos
This commit is contained in:
@@ -259,21 +259,36 @@ class BlpImageFile(ImageFile.ImageFile):
|
||||
|
||||
def _open(self) -> None:
|
||||
self.magic = self.fp.read(4)
|
||||
|
||||
self.fp.seek(5, os.SEEK_CUR)
|
||||
(self._blp_alpha_depth,) = struct.unpack("<b", self.fp.read(1))
|
||||
|
||||
self.fp.seek(2, os.SEEK_CUR)
|
||||
self._size = struct.unpack("<II", self.fp.read(8))
|
||||
|
||||
if self.magic in (b"BLP1", b"BLP2"):
|
||||
decoder = self.magic.decode()
|
||||
else:
|
||||
if not _accept(self.magic):
|
||||
msg = f"Bad BLP magic {repr(self.magic)}"
|
||||
raise BLPFormatError(msg)
|
||||
|
||||
self._mode = "RGBA" if self._blp_alpha_depth else "RGB"
|
||||
self.tile = [ImageFile._Tile(decoder, (0, 0) + self.size, 0, (self.mode, 0, 1))]
|
||||
compression = struct.unpack("<i", self.fp.read(4))[0]
|
||||
if self.magic == b"BLP1":
|
||||
alpha = struct.unpack("<I", self.fp.read(4))[0] != 0
|
||||
else:
|
||||
encoding = struct.unpack("<b", self.fp.read(1))[0]
|
||||
alpha = struct.unpack("<b", self.fp.read(1))[0] != 0
|
||||
alpha_encoding = struct.unpack("<b", self.fp.read(1))[0]
|
||||
self.fp.seek(1, os.SEEK_CUR) # mips
|
||||
|
||||
self._size = struct.unpack("<II", self.fp.read(8))
|
||||
|
||||
args: tuple[int, int, bool] | tuple[int, int, bool, int]
|
||||
if self.magic == b"BLP1":
|
||||
encoding = struct.unpack("<i", self.fp.read(4))[0]
|
||||
self.fp.seek(4, os.SEEK_CUR) # subtype
|
||||
|
||||
args = (compression, encoding, alpha)
|
||||
offset = 28
|
||||
else:
|
||||
args = (compression, encoding, alpha, alpha_encoding)
|
||||
offset = 20
|
||||
|
||||
decoder = self.magic.decode()
|
||||
|
||||
self._mode = "RGBA" if alpha else "RGB"
|
||||
self.tile = [ImageFile._Tile(decoder, (0, 0) + self.size, offset, args)]
|
||||
|
||||
|
||||
class _BLPBaseDecoder(ImageFile.PyDecoder):
|
||||
@@ -281,7 +296,7 @@ class _BLPBaseDecoder(ImageFile.PyDecoder):
|
||||
|
||||
def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]:
|
||||
try:
|
||||
self._read_blp_header()
|
||||
self._read_header()
|
||||
self._load()
|
||||
except struct.error as e:
|
||||
msg = "Truncated BLP file"
|
||||
@@ -292,25 +307,9 @@ class _BLPBaseDecoder(ImageFile.PyDecoder):
|
||||
def _load(self) -> None:
|
||||
pass
|
||||
|
||||
def _read_blp_header(self) -> None:
|
||||
assert self.fd is not None
|
||||
self.fd.seek(4)
|
||||
(self._blp_compression,) = struct.unpack("<i", self._safe_read(4))
|
||||
|
||||
(self._blp_encoding,) = struct.unpack("<b", self._safe_read(1))
|
||||
(self._blp_alpha_depth,) = struct.unpack("<b", self._safe_read(1))
|
||||
(self._blp_alpha_encoding,) = struct.unpack("<b", self._safe_read(1))
|
||||
self.fd.seek(1, os.SEEK_CUR) # mips
|
||||
|
||||
self.size = struct.unpack("<II", self._safe_read(8))
|
||||
|
||||
if isinstance(self, BLP1Decoder):
|
||||
# Only present for BLP1
|
||||
(self._blp_encoding,) = struct.unpack("<i", self._safe_read(4))
|
||||
self.fd.seek(4, os.SEEK_CUR) # subtype
|
||||
|
||||
self._blp_offsets = struct.unpack("<16I", self._safe_read(16 * 4))
|
||||
self._blp_lengths = struct.unpack("<16I", self._safe_read(16 * 4))
|
||||
def _read_header(self) -> None:
|
||||
self._offsets = struct.unpack("<16I", self._safe_read(16 * 4))
|
||||
self._lengths = struct.unpack("<16I", self._safe_read(16 * 4))
|
||||
|
||||
def _safe_read(self, length: int) -> bytes:
|
||||
assert self.fd is not None
|
||||
@@ -326,9 +325,11 @@ class _BLPBaseDecoder(ImageFile.PyDecoder):
|
||||
ret.append((b, g, r, a))
|
||||
return ret
|
||||
|
||||
def _read_bgra(self, palette: list[tuple[int, int, int, int]]) -> bytearray:
|
||||
def _read_bgra(
|
||||
self, palette: list[tuple[int, int, int, int]], alpha: bool
|
||||
) -> bytearray:
|
||||
data = bytearray()
|
||||
_data = BytesIO(self._safe_read(self._blp_lengths[0]))
|
||||
_data = BytesIO(self._safe_read(self._lengths[0]))
|
||||
while True:
|
||||
try:
|
||||
(offset,) = struct.unpack("<B", _data.read(1))
|
||||
@@ -336,7 +337,7 @@ class _BLPBaseDecoder(ImageFile.PyDecoder):
|
||||
break
|
||||
b, g, r, a = palette[offset]
|
||||
d: tuple[int, ...] = (r, g, b)
|
||||
if self._blp_alpha_depth:
|
||||
if alpha:
|
||||
d += (a,)
|
||||
data.extend(d)
|
||||
return data
|
||||
@@ -344,19 +345,21 @@ class _BLPBaseDecoder(ImageFile.PyDecoder):
|
||||
|
||||
class BLP1Decoder(_BLPBaseDecoder):
|
||||
def _load(self) -> None:
|
||||
if self._blp_compression == Format.JPEG:
|
||||
self._compression, self._encoding, alpha = self.args
|
||||
|
||||
if self._compression == Format.JPEG:
|
||||
self._decode_jpeg_stream()
|
||||
|
||||
elif self._blp_compression == 1:
|
||||
if self._blp_encoding in (4, 5):
|
||||
elif self._compression == 1:
|
||||
if self._encoding in (4, 5):
|
||||
palette = self._read_palette()
|
||||
data = self._read_bgra(palette)
|
||||
data = self._read_bgra(palette, alpha)
|
||||
self.set_as_raw(data)
|
||||
else:
|
||||
msg = f"Unsupported BLP encoding {repr(self._blp_encoding)}"
|
||||
msg = f"Unsupported BLP encoding {repr(self._encoding)}"
|
||||
raise BLPFormatError(msg)
|
||||
else:
|
||||
msg = f"Unsupported BLP compression {repr(self._blp_encoding)}"
|
||||
msg = f"Unsupported BLP compression {repr(self._encoding)}"
|
||||
raise BLPFormatError(msg)
|
||||
|
||||
def _decode_jpeg_stream(self) -> None:
|
||||
@@ -365,8 +368,8 @@ class BLP1Decoder(_BLPBaseDecoder):
|
||||
(jpeg_header_size,) = struct.unpack("<I", self._safe_read(4))
|
||||
jpeg_header = self._safe_read(jpeg_header_size)
|
||||
assert self.fd is not None
|
||||
self._safe_read(self._blp_offsets[0] - self.fd.tell()) # What IS this?
|
||||
data = self._safe_read(self._blp_lengths[0])
|
||||
self._safe_read(self._offsets[0] - self.fd.tell()) # What IS this?
|
||||
data = self._safe_read(self._lengths[0])
|
||||
data = jpeg_header + data
|
||||
image = JpegImageFile(BytesIO(data))
|
||||
Image._decompression_bomb_check(image.size)
|
||||
@@ -383,47 +386,47 @@ class BLP1Decoder(_BLPBaseDecoder):
|
||||
|
||||
class BLP2Decoder(_BLPBaseDecoder):
|
||||
def _load(self) -> None:
|
||||
self._compression, self._encoding, alpha, self._alpha_encoding = self.args
|
||||
|
||||
palette = self._read_palette()
|
||||
|
||||
assert self.fd is not None
|
||||
self.fd.seek(self._blp_offsets[0])
|
||||
self.fd.seek(self._offsets[0])
|
||||
|
||||
if self._blp_compression == 1:
|
||||
if self._compression == 1:
|
||||
# Uncompressed or DirectX compression
|
||||
|
||||
if self._blp_encoding == Encoding.UNCOMPRESSED:
|
||||
data = self._read_bgra(palette)
|
||||
if self._encoding == Encoding.UNCOMPRESSED:
|
||||
data = self._read_bgra(palette, alpha)
|
||||
|
||||
elif self._blp_encoding == Encoding.DXT:
|
||||
elif self._encoding == Encoding.DXT:
|
||||
data = bytearray()
|
||||
if self._blp_alpha_encoding == AlphaEncoding.DXT1:
|
||||
linesize = (self.size[0] + 3) // 4 * 8
|
||||
for yb in range((self.size[1] + 3) // 4):
|
||||
for d in decode_dxt1(
|
||||
self._safe_read(linesize), alpha=bool(self._blp_alpha_depth)
|
||||
):
|
||||
if self._alpha_encoding == AlphaEncoding.DXT1:
|
||||
linesize = (self.state.xsize + 3) // 4 * 8
|
||||
for yb in range((self.state.ysize + 3) // 4):
|
||||
for d in decode_dxt1(self._safe_read(linesize), alpha):
|
||||
data += d
|
||||
|
||||
elif self._blp_alpha_encoding == AlphaEncoding.DXT3:
|
||||
linesize = (self.size[0] + 3) // 4 * 16
|
||||
for yb in range((self.size[1] + 3) // 4):
|
||||
elif self._alpha_encoding == AlphaEncoding.DXT3:
|
||||
linesize = (self.state.xsize + 3) // 4 * 16
|
||||
for yb in range((self.state.ysize + 3) // 4):
|
||||
for d in decode_dxt3(self._safe_read(linesize)):
|
||||
data += d
|
||||
|
||||
elif self._blp_alpha_encoding == AlphaEncoding.DXT5:
|
||||
linesize = (self.size[0] + 3) // 4 * 16
|
||||
for yb in range((self.size[1] + 3) // 4):
|
||||
elif self._alpha_encoding == AlphaEncoding.DXT5:
|
||||
linesize = (self.state.xsize + 3) // 4 * 16
|
||||
for yb in range((self.state.ysize + 3) // 4):
|
||||
for d in decode_dxt5(self._safe_read(linesize)):
|
||||
data += d
|
||||
else:
|
||||
msg = f"Unsupported alpha encoding {repr(self._blp_alpha_encoding)}"
|
||||
msg = f"Unsupported alpha encoding {repr(self._alpha_encoding)}"
|
||||
raise BLPFormatError(msg)
|
||||
else:
|
||||
msg = f"Unknown BLP encoding {repr(self._blp_encoding)}"
|
||||
msg = f"Unknown BLP encoding {repr(self._encoding)}"
|
||||
raise BLPFormatError(msg)
|
||||
|
||||
else:
|
||||
msg = f"Unknown BLP compression {repr(self._blp_compression)}"
|
||||
msg = f"Unknown BLP compression {repr(self._compression)}"
|
||||
raise BLPFormatError(msg)
|
||||
|
||||
self.set_as_raw(data)
|
||||
@@ -472,10 +475,15 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
||||
|
||||
assert im.palette is not None
|
||||
fp.write(struct.pack("<i", 1)) # Uncompressed or DirectX compression
|
||||
fp.write(struct.pack("<b", Encoding.UNCOMPRESSED))
|
||||
fp.write(struct.pack("<b", 1 if im.palette.mode == "RGBA" else 0))
|
||||
fp.write(struct.pack("<b", 0)) # alpha encoding
|
||||
fp.write(struct.pack("<b", 0)) # mips
|
||||
|
||||
alpha_depth = 1 if im.palette.mode == "RGBA" else 0
|
||||
if magic == b"BLP1":
|
||||
fp.write(struct.pack("<L", alpha_depth))
|
||||
else:
|
||||
fp.write(struct.pack("<b", Encoding.UNCOMPRESSED))
|
||||
fp.write(struct.pack("<b", alpha_depth))
|
||||
fp.write(struct.pack("<b", 0)) # alpha encoding
|
||||
fp.write(struct.pack("<b", 0)) # mips
|
||||
fp.write(struct.pack("<II", *im.size))
|
||||
if magic == b"BLP1":
|
||||
fp.write(struct.pack("<i", 5))
|
||||
|
||||
@@ -560,9 +560,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
||||
+ struct.pack("<4I", *rgba_mask) # dwRGBABitMask
|
||||
+ struct.pack("<5I", DDSCAPS.TEXTURE, 0, 0, 0, 0)
|
||||
)
|
||||
ImageFile._save(
|
||||
im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 0, (rawmode, 0, 1))]
|
||||
)
|
||||
ImageFile._save(im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 0, rawmode)])
|
||||
|
||||
|
||||
def _accept(prefix: bytes) -> bool:
|
||||
|
||||
@@ -454,7 +454,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes, eps: int = 1) -
|
||||
if hasattr(fp, "flush"):
|
||||
fp.flush()
|
||||
|
||||
ImageFile._save(im, fp, [ImageFile._Tile("eps", (0, 0) + im.size, 0, None)])
|
||||
ImageFile._save(im, fp, [ImageFile._Tile("eps", (0, 0) + im.size)])
|
||||
|
||||
fp.write(b"\n%%%%EndBinary\n")
|
||||
fp.write(b"grestore end\n")
|
||||
|
||||
@@ -303,38 +303,38 @@ TAGS = {
|
||||
|
||||
|
||||
class GPS(IntEnum):
|
||||
GPSVersionID = 0
|
||||
GPSLatitudeRef = 1
|
||||
GPSLatitude = 2
|
||||
GPSLongitudeRef = 3
|
||||
GPSLongitude = 4
|
||||
GPSAltitudeRef = 5
|
||||
GPSAltitude = 6
|
||||
GPSTimeStamp = 7
|
||||
GPSSatellites = 8
|
||||
GPSStatus = 9
|
||||
GPSMeasureMode = 10
|
||||
GPSDOP = 11
|
||||
GPSSpeedRef = 12
|
||||
GPSSpeed = 13
|
||||
GPSTrackRef = 14
|
||||
GPSTrack = 15
|
||||
GPSImgDirectionRef = 16
|
||||
GPSImgDirection = 17
|
||||
GPSMapDatum = 18
|
||||
GPSDestLatitudeRef = 19
|
||||
GPSDestLatitude = 20
|
||||
GPSDestLongitudeRef = 21
|
||||
GPSDestLongitude = 22
|
||||
GPSDestBearingRef = 23
|
||||
GPSDestBearing = 24
|
||||
GPSDestDistanceRef = 25
|
||||
GPSDestDistance = 26
|
||||
GPSProcessingMethod = 27
|
||||
GPSAreaInformation = 28
|
||||
GPSDateStamp = 29
|
||||
GPSDifferential = 30
|
||||
GPSHPositioningError = 31
|
||||
GPSVersionID = 0x00
|
||||
GPSLatitudeRef = 0x01
|
||||
GPSLatitude = 0x02
|
||||
GPSLongitudeRef = 0x03
|
||||
GPSLongitude = 0x04
|
||||
GPSAltitudeRef = 0x05
|
||||
GPSAltitude = 0x06
|
||||
GPSTimeStamp = 0x07
|
||||
GPSSatellites = 0x08
|
||||
GPSStatus = 0x09
|
||||
GPSMeasureMode = 0x0A
|
||||
GPSDOP = 0x0B
|
||||
GPSSpeedRef = 0x0C
|
||||
GPSSpeed = 0x0D
|
||||
GPSTrackRef = 0x0E
|
||||
GPSTrack = 0x0F
|
||||
GPSImgDirectionRef = 0x10
|
||||
GPSImgDirection = 0x11
|
||||
GPSMapDatum = 0x12
|
||||
GPSDestLatitudeRef = 0x13
|
||||
GPSDestLatitude = 0x14
|
||||
GPSDestLongitudeRef = 0x15
|
||||
GPSDestLongitude = 0x16
|
||||
GPSDestBearingRef = 0x17
|
||||
GPSDestBearing = 0x18
|
||||
GPSDestDistanceRef = 0x19
|
||||
GPSDestDistance = 0x1A
|
||||
GPSProcessingMethod = 0x1B
|
||||
GPSAreaInformation = 0x1C
|
||||
GPSDateStamp = 0x1D
|
||||
GPSDifferential = 0x1E
|
||||
GPSHPositioningError = 0x1F
|
||||
|
||||
|
||||
"""Maps EXIF GPS tags to tag names."""
|
||||
@@ -342,40 +342,41 @@ GPSTAGS = {i.value: i.name for i in GPS}
|
||||
|
||||
|
||||
class Interop(IntEnum):
|
||||
InteropIndex = 1
|
||||
InteropVersion = 2
|
||||
RelatedImageFileFormat = 4096
|
||||
RelatedImageWidth = 4097
|
||||
RelatedImageHeight = 4098
|
||||
InteropIndex = 0x0001
|
||||
InteropVersion = 0x0002
|
||||
RelatedImageFileFormat = 0x1000
|
||||
RelatedImageWidth = 0x1001
|
||||
RelatedImageHeight = 0x1002
|
||||
|
||||
|
||||
class IFD(IntEnum):
|
||||
Exif = 34665
|
||||
GPSInfo = 34853
|
||||
Makernote = 37500
|
||||
Interop = 40965
|
||||
Exif = 0x8769
|
||||
GPSInfo = 0x8825
|
||||
MakerNote = 0x927C
|
||||
Makernote = 0x927C # Deprecated
|
||||
Interop = 0xA005
|
||||
IFD1 = -1
|
||||
|
||||
|
||||
class LightSource(IntEnum):
|
||||
Unknown = 0
|
||||
Daylight = 1
|
||||
Fluorescent = 2
|
||||
Tungsten = 3
|
||||
Flash = 4
|
||||
Fine = 9
|
||||
Cloudy = 10
|
||||
Shade = 11
|
||||
DaylightFluorescent = 12
|
||||
DayWhiteFluorescent = 13
|
||||
CoolWhiteFluorescent = 14
|
||||
WhiteFluorescent = 15
|
||||
StandardLightA = 17
|
||||
StandardLightB = 18
|
||||
StandardLightC = 19
|
||||
D55 = 20
|
||||
D65 = 21
|
||||
D75 = 22
|
||||
D50 = 23
|
||||
ISO = 24
|
||||
Other = 255
|
||||
Unknown = 0x00
|
||||
Daylight = 0x01
|
||||
Fluorescent = 0x02
|
||||
Tungsten = 0x03
|
||||
Flash = 0x04
|
||||
Fine = 0x09
|
||||
Cloudy = 0x0A
|
||||
Shade = 0x0B
|
||||
DaylightFluorescent = 0x0C
|
||||
DayWhiteFluorescent = 0x0D
|
||||
CoolWhiteFluorescent = 0x0E
|
||||
WhiteFluorescent = 0x0F
|
||||
StandardLightA = 0x11
|
||||
StandardLightB = 0x12
|
||||
StandardLightC = 0x13
|
||||
D55 = 0x14
|
||||
D65 = 0x15
|
||||
D75 = 0x16
|
||||
D50 = 0x17
|
||||
ISO = 0x18
|
||||
Other = 0xFF
|
||||
|
||||
@@ -159,7 +159,7 @@ class FliImageFile(ImageFile.ImageFile):
|
||||
framesize = i32(s)
|
||||
|
||||
self.decodermaxblock = framesize
|
||||
self.tile = [ImageFile._Tile("fli", (0, 0) + self.size, self.__offset, None)]
|
||||
self.tile = [ImageFile._Tile("fli", (0, 0) + self.size, self.__offset)]
|
||||
|
||||
self.__offset += framesize
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ class FpxImageFile(ImageFile.ImageFile):
|
||||
"raw",
|
||||
(x, y, x1, y1),
|
||||
i32(s, i) + 28,
|
||||
(self.rawmode,),
|
||||
self.rawmode,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ class FtexImageFile(ImageFile.ImageFile):
|
||||
self._mode = "RGBA"
|
||||
self.tile = [ImageFile._Tile("bcn", (0, 0) + self.size, 0, (1,))]
|
||||
elif format == Format.UNCOMPRESSED:
|
||||
self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 0, ("RGB", 0, 1))]
|
||||
self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 0, "RGB")]
|
||||
else:
|
||||
msg = f"Invalid texture compression format: {repr(format)}"
|
||||
raise ValueError(msg)
|
||||
|
||||
@@ -76,7 +76,7 @@ class GdImageFile(ImageFile.ImageFile):
|
||||
"raw",
|
||||
(0, 0) + self.size,
|
||||
7 + true_color_offset + 4 + 256 * 4,
|
||||
("L", 0, 1),
|
||||
"L",
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
@@ -103,7 +103,6 @@ class GifImageFile(ImageFile.ImageFile):
|
||||
|
||||
self.info["version"] = s[:6]
|
||||
self._size = i16(s, 6), i16(s, 8)
|
||||
self.tile = []
|
||||
flags = s[10]
|
||||
bits = (flags & 7) + 1
|
||||
|
||||
@@ -696,8 +695,9 @@ def _write_multiple_frames(
|
||||
)
|
||||
background = _get_background(im_frame, color)
|
||||
background_im = Image.new("P", im_frame.size, background)
|
||||
assert im_frames[0].im.palette is not None
|
||||
background_im.putpalette(im_frames[0].im.palette)
|
||||
first_palette = im_frames[0].im.palette
|
||||
assert first_palette is not None
|
||||
background_im.putpalette(first_palette, first_palette.mode)
|
||||
bbox = _getbbox(background_im, im_frame)[1]
|
||||
elif encoderinfo.get("optimize") and im_frame.mode != "1":
|
||||
if "transparency" not in encoderinfo:
|
||||
|
||||
@@ -357,7 +357,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
||||
name = "".join([name[: 92 - len(ext)], ext])
|
||||
|
||||
fp.write(f"Name: {name}\r\n".encode("ascii"))
|
||||
fp.write(("Image size (x*y): %d*%d\r\n" % im.size).encode("ascii"))
|
||||
fp.write(f"Image size (x*y): {im.size[0]}*{im.size[1]}\r\n".encode("ascii"))
|
||||
fp.write(f"File size (no of images): {frames}\r\n".encode("ascii"))
|
||||
if im.mode in ["P", "PA"]:
|
||||
fp.write(b"Lut: 1\r\n")
|
||||
|
||||
@@ -692,13 +692,10 @@ class Image:
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "<%s.%s image mode=%s size=%dx%d at 0x%X>" % (
|
||||
self.__class__.__module__,
|
||||
self.__class__.__name__,
|
||||
self.mode,
|
||||
self.size[0],
|
||||
self.size[1],
|
||||
id(self),
|
||||
return (
|
||||
f"<{self.__class__.__module__}.{self.__class__.__name__} "
|
||||
f"image mode={self.mode} size={self.size[0]}x{self.size[1]} "
|
||||
f"at 0x{id(self):X}>"
|
||||
)
|
||||
|
||||
def _repr_pretty_(self, p: PrettyPrinter, cycle: bool) -> None:
|
||||
@@ -707,14 +704,8 @@ class Image:
|
||||
# Same as __repr__ but without unpredictable id(self),
|
||||
# to keep Jupyter notebook `text/plain` output stable.
|
||||
p.text(
|
||||
"<%s.%s image mode=%s size=%dx%d>"
|
||||
% (
|
||||
self.__class__.__module__,
|
||||
self.__class__.__name__,
|
||||
self.mode,
|
||||
self.size[0],
|
||||
self.size[1],
|
||||
)
|
||||
f"<{self.__class__.__module__}.{self.__class__.__name__} "
|
||||
f"image mode={self.mode} size={self.size[0]}x{self.size[1]}>"
|
||||
)
|
||||
|
||||
def _repr_image(self, image_format: str, **kwargs: Any) -> bytes | None:
|
||||
@@ -763,7 +754,7 @@ class Image:
|
||||
|
||||
def __setstate__(self, state: list[Any]) -> None:
|
||||
Image.__init__(self)
|
||||
info, mode, size, palette, data = state
|
||||
info, mode, size, palette, data = state[:5]
|
||||
self.info = info
|
||||
self._mode = mode
|
||||
self._size = size
|
||||
@@ -1574,7 +1565,7 @@ class Image:
|
||||
for subifd_offset in subifd_offsets:
|
||||
ifds.append((exif._get_ifd_dict(subifd_offset), subifd_offset))
|
||||
ifd1 = exif.get_ifd(ExifTags.IFD.IFD1)
|
||||
if ifd1 and ifd1.get(513):
|
||||
if ifd1 and ifd1.get(ExifTags.Base.JpegIFOffset):
|
||||
assert exif._info is not None
|
||||
ifds.append((ifd1, exif._info.next))
|
||||
|
||||
@@ -1586,11 +1577,11 @@ class Image:
|
||||
|
||||
fp = self.fp
|
||||
if ifd is not None:
|
||||
thumbnail_offset = ifd.get(513)
|
||||
thumbnail_offset = ifd.get(ExifTags.Base.JpegIFOffset)
|
||||
if thumbnail_offset is not None:
|
||||
thumbnail_offset += getattr(self, "_exif_offset", 0)
|
||||
self.fp.seek(thumbnail_offset)
|
||||
data = self.fp.read(ifd.get(514))
|
||||
data = self.fp.read(ifd.get(ExifTags.Base.JpegIFByteCount))
|
||||
fp = io.BytesIO(data)
|
||||
|
||||
with open(fp) as im:
|
||||
@@ -2550,7 +2541,7 @@ class Image:
|
||||
filename: str | bytes = ""
|
||||
open_fp = False
|
||||
if is_path(fp):
|
||||
filename = os.path.realpath(os.fspath(fp))
|
||||
filename = os.fspath(fp)
|
||||
open_fp = True
|
||||
elif fp == sys.stdout:
|
||||
try:
|
||||
@@ -2559,13 +2550,13 @@ class Image:
|
||||
pass
|
||||
if not filename and hasattr(fp, "name") and is_path(fp.name):
|
||||
# only set the name for metadata purposes
|
||||
filename = os.path.realpath(os.fspath(fp.name))
|
||||
filename = os.fspath(fp.name)
|
||||
|
||||
# may mutate self!
|
||||
self._ensure_mutable()
|
||||
|
||||
save_all = params.pop("save_all", False)
|
||||
self.encoderinfo = params
|
||||
self.encoderinfo = {**getattr(self, "encoderinfo", {}), **params}
|
||||
self.encoderconfig: tuple[Any, ...] = ()
|
||||
|
||||
preinit()
|
||||
@@ -2612,6 +2603,11 @@ class Image:
|
||||
except PermissionError:
|
||||
pass
|
||||
raise
|
||||
finally:
|
||||
try:
|
||||
del self.encoderinfo
|
||||
except AttributeError:
|
||||
pass
|
||||
if open_fp:
|
||||
fp.close()
|
||||
|
||||
@@ -3463,7 +3459,7 @@ def open(
|
||||
exclusive_fp = False
|
||||
filename: str | bytes = ""
|
||||
if is_path(fp):
|
||||
filename = os.path.realpath(os.fspath(fp))
|
||||
filename = os.fspath(fp)
|
||||
|
||||
if filename:
|
||||
fp = builtins.open(filename, "rb")
|
||||
@@ -3893,7 +3889,7 @@ class Exif(_ExifBase):
|
||||
gps_ifd = exif.get_ifd(ExifTags.IFD.GPSInfo)
|
||||
print(gps_ifd)
|
||||
|
||||
Other IFDs include ``ExifTags.IFD.Exif``, ``ExifTags.IFD.Makernote``,
|
||||
Other IFDs include ``ExifTags.IFD.Exif``, ``ExifTags.IFD.MakerNote``,
|
||||
``ExifTags.IFD.Interop`` and ``ExifTags.IFD.IFD1``.
|
||||
|
||||
:py:mod:`~PIL.ExifTags` also has enum classes to provide names for data::
|
||||
@@ -4027,6 +4023,9 @@ class Exif(_ExifBase):
|
||||
|
||||
head = self._get_head()
|
||||
ifd = TiffImagePlugin.ImageFileDirectory_v2(ifh=head)
|
||||
for tag, ifd_dict in self._ifds.items():
|
||||
if tag not in self:
|
||||
ifd[tag] = ifd_dict
|
||||
for tag, value in self.items():
|
||||
if tag in [
|
||||
ExifTags.IFD.Exif,
|
||||
@@ -4056,11 +4055,11 @@ class Exif(_ExifBase):
|
||||
ifd = self._get_ifd_dict(offset, tag)
|
||||
if ifd is not None:
|
||||
self._ifds[tag] = ifd
|
||||
elif tag in [ExifTags.IFD.Interop, ExifTags.IFD.Makernote]:
|
||||
elif tag in [ExifTags.IFD.Interop, ExifTags.IFD.MakerNote]:
|
||||
if ExifTags.IFD.Exif not in self._ifds:
|
||||
self.get_ifd(ExifTags.IFD.Exif)
|
||||
tag_data = self._ifds[ExifTags.IFD.Exif][tag]
|
||||
if tag == ExifTags.IFD.Makernote:
|
||||
if tag == ExifTags.IFD.MakerNote:
|
||||
from .TiffImagePlugin import ImageFileDirectory_v2
|
||||
|
||||
if tag_data[:8] == b"FUJIFILM":
|
||||
@@ -4147,7 +4146,7 @@ class Exif(_ExifBase):
|
||||
ifd = {
|
||||
k: v
|
||||
for (k, v) in ifd.items()
|
||||
if k not in (ExifTags.IFD.Interop, ExifTags.IFD.Makernote)
|
||||
if k not in (ExifTags.IFD.Interop, ExifTags.IFD.MakerNote)
|
||||
}
|
||||
return ifd
|
||||
|
||||
|
||||
@@ -98,8 +98,8 @@ def _tilesort(t: _Tile) -> int:
|
||||
class _Tile(NamedTuple):
|
||||
codec_name: str
|
||||
extents: tuple[int, int, int, int] | None
|
||||
offset: int
|
||||
args: tuple[Any, ...] | str | None
|
||||
offset: int = 0
|
||||
args: tuple[Any, ...] | str | None = None
|
||||
|
||||
|
||||
#
|
||||
@@ -120,7 +120,7 @@ class ImageFile(Image.Image):
|
||||
self.custom_mimetype: str | None = None
|
||||
|
||||
self.tile: list[_Tile] = []
|
||||
""" A list of tile descriptors, or ``None`` """
|
||||
""" A list of tile descriptors """
|
||||
|
||||
self.readonly = 1 # until we know better
|
||||
|
||||
@@ -130,7 +130,7 @@ class ImageFile(Image.Image):
|
||||
if is_path(fp):
|
||||
# filename
|
||||
self.fp = open(fp, "rb")
|
||||
self.filename = os.path.realpath(os.fspath(fp))
|
||||
self.filename = os.fspath(fp)
|
||||
self._exclusive_fp = True
|
||||
else:
|
||||
# stream
|
||||
|
||||
@@ -553,7 +553,7 @@ class Color3DLUT(MultibandFilter):
|
||||
ch_out = channels or ch_in
|
||||
size_1d, size_2d, size_3d = self.size
|
||||
|
||||
table = [0] * (size_1d * size_2d * size_3d * ch_out)
|
||||
table: list[float] = [0] * (size_1d * size_2d * size_3d * ch_out)
|
||||
idx_in = 0
|
||||
idx_out = 0
|
||||
for b in range(size_3d):
|
||||
|
||||
@@ -270,7 +270,7 @@ class FreeTypeFont:
|
||||
)
|
||||
|
||||
if is_path(font):
|
||||
font = os.path.realpath(os.fspath(font))
|
||||
font = os.fspath(font)
|
||||
if sys.platform == "win32":
|
||||
font_bytes_path = font if isinstance(font, bytes) else font.encode()
|
||||
try:
|
||||
|
||||
@@ -104,28 +104,17 @@ def grab(
|
||||
|
||||
def grabclipboard() -> Image.Image | list[str] | None:
|
||||
if sys.platform == "darwin":
|
||||
fh, filepath = tempfile.mkstemp(".png")
|
||||
os.close(fh)
|
||||
commands = [
|
||||
'set theFile to (open for access POSIX file "'
|
||||
+ filepath
|
||||
+ '" with write permission)',
|
||||
"try",
|
||||
" write (the clipboard as «class PNGf») to theFile",
|
||||
"end try",
|
||||
"close access theFile",
|
||||
]
|
||||
script = ["osascript"]
|
||||
for command in commands:
|
||||
script += ["-e", command]
|
||||
subprocess.call(script)
|
||||
p = subprocess.run(
|
||||
["osascript", "-e", "get the clipboard as «class PNGf»"],
|
||||
capture_output=True,
|
||||
)
|
||||
if p.returncode != 0:
|
||||
return None
|
||||
|
||||
im = None
|
||||
if os.stat(filepath).st_size != 0:
|
||||
im = Image.open(filepath)
|
||||
im.load()
|
||||
os.unlink(filepath)
|
||||
return im
|
||||
import binascii
|
||||
|
||||
data = io.BytesIO(binascii.unhexlify(p.stdout[11:-3]))
|
||||
return Image.open(data)
|
||||
elif sys.platform == "win32":
|
||||
fmt, data = Image.core.grabclipboard_win32()
|
||||
if fmt == "file": # CF_HDROP
|
||||
|
||||
@@ -698,10 +698,11 @@ def exif_transpose(image: Image.Image, *, in_place: bool = False) -> Image.Image
|
||||
8: Image.Transpose.ROTATE_90,
|
||||
}.get(orientation)
|
||||
if method is not None:
|
||||
transposed_image = image.transpose(method)
|
||||
if in_place:
|
||||
image.im = transposed_image.im
|
||||
image._size = transposed_image._size
|
||||
image.im = image.im.transpose(method)
|
||||
image._size = image.im.size
|
||||
else:
|
||||
transposed_image = image.transpose(method)
|
||||
exif_image = image if in_place else transposed_image
|
||||
|
||||
exif = exif_image.getexif()
|
||||
|
||||
@@ -213,4 +213,7 @@ def toqimage(im: Image.Image | str | QByteArray) -> ImageQt:
|
||||
|
||||
def toqpixmap(im: Image.Image | str | QByteArray) -> QPixmap:
|
||||
qimage = toqimage(im)
|
||||
return getattr(QPixmap, "fromImage")(qimage)
|
||||
pixmap = getattr(QPixmap, "fromImage")(qimage)
|
||||
if qt_version == "6":
|
||||
pixmap.detach()
|
||||
return pixmap
|
||||
|
||||
@@ -62,7 +62,7 @@ class ImtImageFile(ImageFile.ImageFile):
|
||||
"raw",
|
||||
(0, 0) + self.size,
|
||||
self.fp.tell() - len(buffer),
|
||||
(self.mode, 0, 1),
|
||||
self.mode,
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
@@ -252,6 +252,7 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
|
||||
if sig == b"\xff\x4f\xff\x51":
|
||||
self.codec = "j2k"
|
||||
self._size, self._mode = _parse_codestream(self.fp)
|
||||
self._parse_comment()
|
||||
else:
|
||||
sig = sig + self.fp.read(8)
|
||||
|
||||
@@ -262,6 +263,9 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
|
||||
if dpi is not None:
|
||||
self.info["dpi"] = dpi
|
||||
if self.fp.read(12).endswith(b"jp2c\xff\x4f\xff\x51"):
|
||||
hdr = self.fp.read(2)
|
||||
length = _binary.i16be(hdr)
|
||||
self.fp.seek(length - 2, os.SEEK_CUR)
|
||||
self._parse_comment()
|
||||
else:
|
||||
msg = "not a JPEG 2000 file"
|
||||
@@ -296,10 +300,6 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
|
||||
]
|
||||
|
||||
def _parse_comment(self) -> None:
|
||||
hdr = self.fp.read(2)
|
||||
length = _binary.i16be(hdr)
|
||||
self.fp.seek(length - 2, os.SEEK_CUR)
|
||||
|
||||
while True:
|
||||
marker = self.fp.read(2)
|
||||
if not marker:
|
||||
|
||||
@@ -72,7 +72,7 @@ def APP(self: JpegImageFile, marker: int) -> None:
|
||||
n = i16(self.fp.read(2)) - 2
|
||||
s = ImageFile._safe_read(self.fp, n)
|
||||
|
||||
app = "APP%d" % (marker & 15)
|
||||
app = f"APP{marker & 15}"
|
||||
|
||||
self.app[app] = s # compatibility
|
||||
self.applist.append((app, s))
|
||||
@@ -90,6 +90,9 @@ def APP(self: JpegImageFile, marker: int) -> None:
|
||||
else:
|
||||
if jfif_unit == 1:
|
||||
self.info["dpi"] = jfif_density
|
||||
elif jfif_unit == 2: # cm
|
||||
# 1 dpcm = 2.54 dpi
|
||||
self.info["dpi"] = tuple(d * 2.54 for d in jfif_density)
|
||||
self.info["jfif_unit"] = jfif_unit
|
||||
self.info["jfif_density"] = jfif_density
|
||||
elif marker == 0xFFE1 and s[:6] == b"Exif\0\0":
|
||||
@@ -395,6 +398,13 @@ class JpegImageFile(ImageFile.ImageFile):
|
||||
return getattr(self, "_" + name)
|
||||
raise AttributeError(name)
|
||||
|
||||
def __getstate__(self) -> list[Any]:
|
||||
return super().__getstate__() + [self.layers, self.layer]
|
||||
|
||||
def __setstate__(self, state: list[Any]) -> None:
|
||||
super().__setstate__(state)
|
||||
self.layers, self.layer = state[5:]
|
||||
|
||||
def load_read(self, read_bytes: int) -> bytes:
|
||||
"""
|
||||
internal: read more image data
|
||||
@@ -751,7 +761,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
||||
extra = info.get("extra", b"")
|
||||
|
||||
MAX_BYTES_IN_MARKER = 65533
|
||||
xmp = info.get("xmp", im.info.get("xmp"))
|
||||
xmp = info.get("xmp")
|
||||
if xmp:
|
||||
overhead_len = 29 # b"http://ns.adobe.com/xap/1.0/\x00"
|
||||
max_data_bytes_in_marker = MAX_BYTES_IN_MARKER - overhead_len
|
||||
|
||||
@@ -70,9 +70,9 @@ class MspImageFile(ImageFile.ImageFile):
|
||||
self._size = i16(s, 4), i16(s, 6)
|
||||
|
||||
if s[:4] == b"DanM":
|
||||
self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 32, ("1", 0, 1))]
|
||||
self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 32, "1")]
|
||||
else:
|
||||
self.tile = [ImageFile._Tile("MSP", (0, 0) + self.size, 32, None)]
|
||||
self.tile = [ImageFile._Tile("MSP", (0, 0) + self.size, 32)]
|
||||
|
||||
|
||||
class MspDecoder(ImageFile.PyDecoder):
|
||||
@@ -188,7 +188,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
||||
fp.write(o16(h))
|
||||
|
||||
# image body
|
||||
ImageFile._save(im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 32, ("1", 0, 1))])
|
||||
ImageFile._save(im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 32, "1")])
|
||||
|
||||
|
||||
#
|
||||
|
||||
@@ -47,7 +47,7 @@ class PcdImageFile(ImageFile.ImageFile):
|
||||
|
||||
self._mode = "RGB"
|
||||
self._size = 768, 512 # FIXME: not correct for rotated images!
|
||||
self.tile = [ImageFile._Tile("pcd", (0, 0) + self.size, 96 * 2048, None)]
|
||||
self.tile = [ImageFile._Tile("pcd", (0, 0) + self.size, 96 * 2048)]
|
||||
|
||||
def load_end(self) -> None:
|
||||
if self.tile_post_rotate:
|
||||
|
||||
@@ -86,7 +86,7 @@ class PcxImageFile(ImageFile.ImageFile):
|
||||
|
||||
elif bits == 1 and planes in (2, 4):
|
||||
mode = "P"
|
||||
rawmode = "P;%dL" % planes
|
||||
rawmode = f"P;{planes}L"
|
||||
self.palette = ImagePalette.raw("RGB", s[16:64])
|
||||
|
||||
elif version == 5 and bits == 8 and planes == 1:
|
||||
|
||||
@@ -61,9 +61,7 @@ class PixarImageFile(ImageFile.ImageFile):
|
||||
# FIXME: to be continued...
|
||||
|
||||
# create tile descriptor (assuming "dumped")
|
||||
self.tile = [
|
||||
ImageFile._Tile("raw", (0, 0) + self.size, 1024, (self.mode, 0, 1))
|
||||
]
|
||||
self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 1024, self.mode)]
|
||||
|
||||
|
||||
#
|
||||
|
||||
@@ -523,7 +523,7 @@ class PngStream(ChunkStream):
|
||||
|
||||
assert self.fp is not None
|
||||
s = ImageFile._safe_read(self.fp, length)
|
||||
raw_vals = struct.unpack(">%dI" % (len(s) // 4), s)
|
||||
raw_vals = struct.unpack(f">{len(s) // 4}I", s)
|
||||
self.im_info["chromaticity"] = tuple(elt / 100000.0 for elt in raw_vals)
|
||||
return s
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ class QoiImageFile(ImageFile.ImageFile):
|
||||
self._mode = "RGB" if channels == 3 else "RGBA"
|
||||
|
||||
self.fp.seek(1, os.SEEK_CUR) # colorspace
|
||||
self.tile = [ImageFile._Tile("qoi", (0, 0) + self._size, self.fp.tell(), None)]
|
||||
self.tile = [ImageFile._Tile("qoi", (0, 0) + self._size, self.fp.tell())]
|
||||
|
||||
|
||||
class QoiDecoder(ImageFile.PyDecoder):
|
||||
|
||||
@@ -154,9 +154,7 @@ class SpiderImageFile(ImageFile.ImageFile):
|
||||
self.rawmode = "F;32F"
|
||||
self._mode = "F"
|
||||
|
||||
self.tile = [
|
||||
ImageFile._Tile("raw", (0, 0) + self.size, offset, (self.rawmode, 0, 1))
|
||||
]
|
||||
self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, offset, self.rawmode)]
|
||||
self._fp = self.fp # FIXME: hack
|
||||
|
||||
@property
|
||||
@@ -211,26 +209,27 @@ class SpiderImageFile(ImageFile.ImageFile):
|
||||
|
||||
|
||||
# given a list of filenames, return a list of images
|
||||
def loadImageSeries(filelist: list[str] | None = None) -> list[SpiderImageFile] | None:
|
||||
def loadImageSeries(filelist: list[str] | None = None) -> list[Image.Image] | None:
|
||||
"""create a list of :py:class:`~PIL.Image.Image` objects for use in a montage"""
|
||||
if filelist is None or len(filelist) < 1:
|
||||
return None
|
||||
|
||||
imglist = []
|
||||
byte_imgs = []
|
||||
for img in filelist:
|
||||
if not os.path.exists(img):
|
||||
print(f"unable to find {img}")
|
||||
continue
|
||||
try:
|
||||
with Image.open(img) as im:
|
||||
im = im.convert2byte()
|
||||
assert isinstance(im, SpiderImageFile)
|
||||
byte_im = im.convert2byte()
|
||||
except Exception:
|
||||
if not isSpiderImage(img):
|
||||
print(f"{img} is not a Spider image file")
|
||||
continue
|
||||
im.info["filename"] = img
|
||||
imglist.append(im)
|
||||
return imglist
|
||||
byte_im.info["filename"] = img
|
||||
byte_imgs.append(byte_im)
|
||||
return byte_imgs
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
@@ -280,9 +279,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
||||
fp.writelines(hdr)
|
||||
|
||||
rawmode = "F;32NF" # 32-bit native floating point
|
||||
ImageFile._save(
|
||||
im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 0, (rawmode, 0, 1))]
|
||||
)
|
||||
ImageFile._save(im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 0, rawmode)])
|
||||
|
||||
|
||||
def _save_spider(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
||||
|
||||
@@ -294,7 +294,7 @@ def _accept(prefix: bytes) -> bool:
|
||||
def _limit_rational(
|
||||
val: float | Fraction | IFDRational, max_val: int
|
||||
) -> tuple[IntegralLike, IntegralLike]:
|
||||
inv = abs(float(val)) > 1
|
||||
inv = abs(val) > 1
|
||||
n_d = IFDRational(1 / val if inv else val).limit_rational(max_val)
|
||||
return n_d[::-1] if inv else n_d
|
||||
|
||||
@@ -582,7 +582,7 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
ifh: bytes = b"II\052\0\0\0\0\0",
|
||||
ifh: bytes = b"II\x2A\x00\x00\x00\x00\x00",
|
||||
prefix: bytes | None = None,
|
||||
group: int | None = None,
|
||||
) -> None:
|
||||
@@ -685,22 +685,33 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
||||
else:
|
||||
self.tagtype[tag] = TiffTags.UNDEFINED
|
||||
if all(isinstance(v, IFDRational) for v in values):
|
||||
self.tagtype[tag] = (
|
||||
TiffTags.RATIONAL
|
||||
if all(v >= 0 for v in values)
|
||||
else TiffTags.SIGNED_RATIONAL
|
||||
)
|
||||
elif all(isinstance(v, int) for v in values):
|
||||
if all(0 <= v < 2**16 for v in values):
|
||||
self.tagtype[tag] = TiffTags.SHORT
|
||||
elif all(-(2**15) < v < 2**15 for v in values):
|
||||
self.tagtype[tag] = TiffTags.SIGNED_SHORT
|
||||
for v in values:
|
||||
assert isinstance(v, IFDRational)
|
||||
if v < 0:
|
||||
self.tagtype[tag] = TiffTags.SIGNED_RATIONAL
|
||||
break
|
||||
else:
|
||||
self.tagtype[tag] = (
|
||||
TiffTags.LONG
|
||||
if all(v >= 0 for v in values)
|
||||
else TiffTags.SIGNED_LONG
|
||||
)
|
||||
self.tagtype[tag] = TiffTags.RATIONAL
|
||||
elif all(isinstance(v, int) for v in values):
|
||||
short = True
|
||||
signed_short = True
|
||||
long = True
|
||||
for v in values:
|
||||
assert isinstance(v, int)
|
||||
if short and not (0 <= v < 2**16):
|
||||
short = False
|
||||
if signed_short and not (-(2**15) < v < 2**15):
|
||||
signed_short = False
|
||||
if long and v < 0:
|
||||
long = False
|
||||
if short:
|
||||
self.tagtype[tag] = TiffTags.SHORT
|
||||
elif signed_short:
|
||||
self.tagtype[tag] = TiffTags.SIGNED_SHORT
|
||||
elif long:
|
||||
self.tagtype[tag] = TiffTags.LONG
|
||||
else:
|
||||
self.tagtype[tag] = TiffTags.SIGNED_LONG
|
||||
elif all(isinstance(v, float) for v in values):
|
||||
self.tagtype[tag] = TiffTags.DOUBLE
|
||||
elif all(isinstance(v, str) for v in values):
|
||||
@@ -718,7 +729,10 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
||||
|
||||
is_ifd = self.tagtype[tag] == TiffTags.LONG and isinstance(values, dict)
|
||||
if not is_ifd:
|
||||
values = tuple(info.cvt_enum(value) for value in values)
|
||||
values = tuple(
|
||||
info.cvt_enum(value) if isinstance(value, str) else value
|
||||
for value in values
|
||||
)
|
||||
|
||||
dest = self._tags_v1 if legacy_api else self._tags_v2
|
||||
|
||||
@@ -921,9 +935,9 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
||||
self._tagdata[tag] = data
|
||||
self.tagtype[tag] = typ
|
||||
|
||||
msg += " - value: " + (
|
||||
"<table: %d bytes>" % size if size > 32 else repr(data)
|
||||
)
|
||||
msg += " - value: "
|
||||
msg += f"<table: {size} bytes>" if size > 32 else repr(data)
|
||||
|
||||
logger.debug(msg)
|
||||
|
||||
(self.next,) = (
|
||||
@@ -935,16 +949,26 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
||||
warnings.warn(str(msg))
|
||||
return
|
||||
|
||||
def _get_ifh(self):
|
||||
ifh = self._prefix + self._pack("H", 43 if self._bigtiff else 42)
|
||||
if self._bigtiff:
|
||||
ifh += self._pack("HH", 8, 0)
|
||||
ifh += self._pack("Q", 16) if self._bigtiff else self._pack("L", 8)
|
||||
|
||||
return ifh
|
||||
|
||||
def tobytes(self, offset: int = 0) -> bytes:
|
||||
# FIXME What about tagdata?
|
||||
result = self._pack("H", len(self._tags_v2))
|
||||
result = self._pack("Q" if self._bigtiff else "H", len(self._tags_v2))
|
||||
|
||||
entries: list[tuple[int, int, int, bytes, bytes]] = []
|
||||
offset = offset + len(result) + len(self._tags_v2) * 12 + 4
|
||||
offset += len(result) + len(self._tags_v2) * (20 if self._bigtiff else 12) + 4
|
||||
stripoffsets = None
|
||||
|
||||
# pass 1: convert tags to binary format
|
||||
# always write tags in ascending order
|
||||
fmt = "Q" if self._bigtiff else "L"
|
||||
fmt_size = 8 if self._bigtiff else 4
|
||||
for tag, value in sorted(self._tags_v2.items()):
|
||||
if tag == STRIPOFFSETS:
|
||||
stripoffsets = len(entries)
|
||||
@@ -952,11 +976,7 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
||||
logger.debug("Tag %s, Type: %s, Value: %s", tag, typ, repr(value))
|
||||
is_ifd = typ == TiffTags.LONG and isinstance(value, dict)
|
||||
if is_ifd:
|
||||
if self._endian == "<":
|
||||
ifh = b"II\x2A\x00\x08\x00\x00\x00"
|
||||
else:
|
||||
ifh = b"MM\x00\x2A\x00\x00\x00\x08"
|
||||
ifd = ImageFileDirectory_v2(ifh, group=tag)
|
||||
ifd = ImageFileDirectory_v2(self._get_ifh(), group=tag)
|
||||
values = self._tags_v2[tag]
|
||||
for ifd_tag, ifd_value in values.items():
|
||||
ifd[ifd_tag] = ifd_value
|
||||
@@ -967,10 +987,8 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
||||
|
||||
tagname = TiffTags.lookup(tag, self.group).name
|
||||
typname = "ifd" if is_ifd else TYPES.get(typ, "unknown")
|
||||
msg = f"save: {tagname} ({tag}) - type: {typname} ({typ})"
|
||||
msg += " - value: " + (
|
||||
"<table: %d bytes>" % len(data) if len(data) >= 16 else str(values)
|
||||
)
|
||||
msg = f"save: {tagname} ({tag}) - type: {typname} ({typ}) - value: "
|
||||
msg += f"<table: {len(data)} bytes>" if len(data) >= 16 else str(values)
|
||||
logger.debug(msg)
|
||||
|
||||
# count is sum of lengths for string and arbitrary data
|
||||
@@ -981,10 +999,10 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
||||
else:
|
||||
count = len(values)
|
||||
# figure out if data fits into the entry
|
||||
if len(data) <= 4:
|
||||
entries.append((tag, typ, count, data.ljust(4, b"\0"), b""))
|
||||
if len(data) <= fmt_size:
|
||||
entries.append((tag, typ, count, data.ljust(fmt_size, b"\0"), b""))
|
||||
else:
|
||||
entries.append((tag, typ, count, self._pack("L", offset), data))
|
||||
entries.append((tag, typ, count, self._pack(fmt, offset), data))
|
||||
offset += (len(data) + 1) // 2 * 2 # pad to word
|
||||
|
||||
# update strip offset data to point beyond auxiliary data
|
||||
@@ -995,13 +1013,15 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
||||
values = [val + offset for val in handler(self, data, self.legacy_api)]
|
||||
data = self._write_dispatch[typ](self, *values)
|
||||
else:
|
||||
value = self._pack("L", self._unpack("L", value)[0] + offset)
|
||||
value = self._pack(fmt, self._unpack(fmt, value)[0] + offset)
|
||||
entries[stripoffsets] = tag, typ, count, value, data
|
||||
|
||||
# pass 2: write entries to file
|
||||
for tag, typ, count, value, data in entries:
|
||||
logger.debug("%s %s %s %s %s", tag, typ, count, repr(value), repr(data))
|
||||
result += self._pack("HHL4s", tag, typ, count, value)
|
||||
result += self._pack(
|
||||
"HHQ8s" if self._bigtiff else "HHL4s", tag, typ, count, value
|
||||
)
|
||||
|
||||
# -- overwrite here for multi-page --
|
||||
result += b"\0\0\0\0" # end of entries
|
||||
@@ -1016,8 +1036,7 @@ class ImageFileDirectory_v2(_IFDv2Base):
|
||||
|
||||
def save(self, fp: IO[bytes]) -> int:
|
||||
if fp.tell() == 0: # skip TIFF header on subsequent pages
|
||||
# tiff header -- PIL always starts the first IFD at offset 8
|
||||
fp.write(self._prefix + self._pack("HL", 42, 8))
|
||||
fp.write(self._get_ifh())
|
||||
|
||||
offset = fp.tell()
|
||||
result = self.tobytes(offset)
|
||||
@@ -1202,10 +1221,6 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||
def _seek(self, frame: int) -> None:
|
||||
self.fp = self._fp
|
||||
|
||||
# reset buffered io handle in case fp
|
||||
# was passed to libtiff, invalidating the buffer
|
||||
self.fp.tell()
|
||||
|
||||
while len(self._frame_pos) <= frame:
|
||||
if not self.__next:
|
||||
msg = "no more images in TIFF file"
|
||||
@@ -1289,10 +1304,6 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||
if not self.is_animated:
|
||||
self._close_exclusive_fp_after_loading = True
|
||||
|
||||
# reset buffered io handle in case fp
|
||||
# was passed to libtiff, invalidating the buffer
|
||||
self.fp.tell()
|
||||
|
||||
# load IFD data from fp before it is closed
|
||||
exif = self.getexif()
|
||||
for key in TiffTags.TAGS_V2_GROUPS:
|
||||
@@ -1367,8 +1378,17 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||
logger.debug("have fileno, calling fileno version of the decoder.")
|
||||
if not close_self_fp:
|
||||
self.fp.seek(0)
|
||||
# Save and restore the file position, because libtiff will move it
|
||||
# outside of the Python runtime, and that will confuse
|
||||
# io.BufferedReader and possible others.
|
||||
# NOTE: This must use os.lseek(), and not fp.tell()/fp.seek(),
|
||||
# because the buffer read head already may not equal the actual
|
||||
# file position, and fp.seek() may just adjust it's internal
|
||||
# pointer and not actually seek the OS file handle.
|
||||
pos = os.lseek(fp, 0, os.SEEK_CUR)
|
||||
# 4 bytes, otherwise the trace might error out
|
||||
n, err = decoder.decode(b"fpfp")
|
||||
os.lseek(fp, pos, os.SEEK_SET)
|
||||
else:
|
||||
# we have something else.
|
||||
logger.debug("don't have fileno or getvalue. just reading")
|
||||
@@ -1419,8 +1439,12 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||
logger.debug("- YCbCr subsampling: %s", self.tag_v2.get(YCBCRSUBSAMPLING))
|
||||
|
||||
# size
|
||||
xsize = self.tag_v2.get(IMAGEWIDTH)
|
||||
ysize = self.tag_v2.get(IMAGELENGTH)
|
||||
try:
|
||||
xsize = self.tag_v2[IMAGEWIDTH]
|
||||
ysize = self.tag_v2[IMAGELENGTH]
|
||||
except KeyError as e:
|
||||
msg = "Missing dimensions"
|
||||
raise TypeError(msg) from e
|
||||
if not isinstance(xsize, int) or not isinstance(ysize, int):
|
||||
msg = "Invalid dimensions"
|
||||
raise ValueError(msg)
|
||||
@@ -1542,17 +1566,6 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||
# fillorder==2 modes have a corresponding
|
||||
# fillorder=1 mode
|
||||
self._mode, rawmode = OPEN_INFO[key]
|
||||
# libtiff always returns the bytes in native order.
|
||||
# we're expecting image byte order. So, if the rawmode
|
||||
# contains I;16, we need to convert from native to image
|
||||
# byte order.
|
||||
if rawmode == "I;16":
|
||||
rawmode = "I;16N"
|
||||
if ";16B" in rawmode:
|
||||
rawmode = rawmode.replace(";16B", ";16N")
|
||||
if ";16L" in rawmode:
|
||||
rawmode = rawmode.replace(";16L", ";16N")
|
||||
|
||||
# YCbCr images with new jpeg compression with pixels in one plane
|
||||
# unpacked straight into RGB values
|
||||
if (
|
||||
@@ -1561,6 +1574,14 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||
and self._planar_configuration == 1
|
||||
):
|
||||
rawmode = "RGB"
|
||||
# libtiff always returns the bytes in native order.
|
||||
# we're expecting image byte order. So, if the rawmode
|
||||
# contains I;16, we need to convert from native to image
|
||||
# byte order.
|
||||
elif rawmode == "I;16":
|
||||
rawmode = "I;16N"
|
||||
elif rawmode.endswith(";16B") or rawmode.endswith(";16L"):
|
||||
rawmode = rawmode[:-1] + "N"
|
||||
|
||||
# Offset in the tile tuple is 0, we go from 0,0 to
|
||||
# w,h, and we only do this once -- eds
|
||||
@@ -1666,10 +1687,13 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
||||
msg = f"cannot write mode {im.mode} as TIFF"
|
||||
raise OSError(msg) from e
|
||||
|
||||
ifd = ImageFileDirectory_v2(prefix=prefix)
|
||||
|
||||
encoderinfo = im.encoderinfo
|
||||
encoderconfig = im.encoderconfig
|
||||
|
||||
ifd = ImageFileDirectory_v2(prefix=prefix)
|
||||
if encoderinfo.get("big_tiff"):
|
||||
ifd._bigtiff = True
|
||||
|
||||
try:
|
||||
compression = encoderinfo["compression"]
|
||||
except KeyError:
|
||||
@@ -1901,7 +1925,9 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
||||
if not getattr(Image.core, "libtiff_support_custom_tags", False):
|
||||
continue
|
||||
|
||||
if tag in ifd.tagtype:
|
||||
if tag in TiffTags.TAGS_V2_GROUPS:
|
||||
types[tag] = TiffTags.LONG8
|
||||
elif tag in ifd.tagtype:
|
||||
types[tag] = ifd.tagtype[tag]
|
||||
elif not (isinstance(value, (int, float, str, bytes))):
|
||||
continue
|
||||
|
||||
@@ -60,7 +60,6 @@ class WebPImageFile(ImageFile.ImageFile):
|
||||
self.is_animated = self.n_frames > 1
|
||||
self._mode = "RGB" if mode == "RGBX" else mode
|
||||
self.rawmode = mode
|
||||
self.tile = []
|
||||
|
||||
# Attempt to read ICC / EXIF / XMP chunks from file
|
||||
icc_profile = self._decoder.get_chunk("ICCP")
|
||||
|
||||
@@ -92,6 +92,9 @@ class WmfStubImageFile(ImageFile.StubImageFile):
|
||||
|
||||
# get units per inch
|
||||
self._inch = word(s, 14)
|
||||
if self._inch == 0:
|
||||
msg = "Invalid inch"
|
||||
raise ValueError(msg)
|
||||
|
||||
# get bounding box
|
||||
x0 = short(s, 6)
|
||||
@@ -128,7 +131,7 @@ class WmfStubImageFile(ImageFile.StubImageFile):
|
||||
size = x1 - x0, y1 - y0
|
||||
|
||||
# calculate dots per inch from bbox and frame
|
||||
xdpi = 2540.0 * (x1 - y0) / (frame[2] - frame[0])
|
||||
xdpi = 2540.0 * (x1 - x0) / (frame[2] - frame[0])
|
||||
ydpi = 2540.0 * (y1 - y0) / (frame[3] - frame[1])
|
||||
|
||||
self.info["wmf_bbox"] = x0, y0, x1, y1
|
||||
|
||||
@@ -74,9 +74,7 @@ class XVThumbImageFile(ImageFile.ImageFile):
|
||||
self.palette = ImagePalette.raw("RGB", PALETTE)
|
||||
|
||||
self.tile = [
|
||||
ImageFile._Tile(
|
||||
"raw", (0, 0) + self.size, self.fp.tell(), (self.mode, 0, 1)
|
||||
)
|
||||
ImageFile._Tile("raw", (0, 0) + self.size, self.fp.tell(), self.mode)
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ class XbmImageFile(ImageFile.ImageFile):
|
||||
self._mode = "1"
|
||||
self._size = xsize, ysize
|
||||
|
||||
self.tile = [ImageFile._Tile("xbm", (0, 0) + self.size, m.end(), None)]
|
||||
self.tile = [ImageFile._Tile("xbm", (0, 0) + self.size, m.end())]
|
||||
|
||||
|
||||
def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
||||
@@ -85,7 +85,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
||||
|
||||
fp.write(b"static char im_bits[] = {\n")
|
||||
|
||||
ImageFile._save(im, fp, [ImageFile._Tile("xbm", (0, 0) + im.size, 0, None)])
|
||||
ImageFile._save(im, fp, [ImageFile._Tile("xbm", (0, 0) + im.size)])
|
||||
|
||||
fp.write(b"};\n")
|
||||
|
||||
|
||||
@@ -101,9 +101,7 @@ class XpmImageFile(ImageFile.ImageFile):
|
||||
self._mode = "P"
|
||||
self.palette = ImagePalette.raw("RGB", b"".join(palette))
|
||||
|
||||
self.tile = [
|
||||
ImageFile._Tile("raw", (0, 0) + self.size, self.fp.tell(), ("P", 0, 1))
|
||||
]
|
||||
self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, self.fp.tell(), "P")]
|
||||
|
||||
def load_read(self, read_bytes: int) -> bytes:
|
||||
#
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user