You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
139 lines
4.3 KiB
139 lines
4.3 KiB
#!/usr/bin/env python3 |
|
|
|
import mmap |
|
|
|
from PIL import Image, ImageFile |
|
|
|
from .filebuf import FileBuffer |
|
from ._libflif import lib as libflif, ffi |
|
|
|
|
|
# --------------------------------------------------------- |
|
# Image file |
|
def _accept(prefix): |
|
return prefix[:4] == b"FLIF" |
|
|
|
|
|
class FLImageFile(ImageFile.ImageFile): |
|
format = "FLIF" |
|
format_description = "Free Lossless Image Format" |
|
|
|
def _open(self): |
|
header = self.fp.read(30) |
|
info = libflif.flif_read_info_from_memory(header, 30) |
|
try: |
|
# Mode/Channels |
|
channels = libflif.flif_info_get_nb_channels(info) |
|
if channels == 1: |
|
if libflif.flif_info_get_depth(info) == 1: |
|
self.mode = "1" |
|
else: |
|
self.mode = "L" |
|
elif channels == 3: |
|
self.mode = "RGB" |
|
elif channels == 4: |
|
self.mode = "RGBA" |
|
else: |
|
raise ValueError("Cannot open FLIF image with %i channels" % channels) |
|
|
|
# Size |
|
self.size = ( |
|
libflif.flif_info_get_width(info), |
|
libflif.flif_info_get_height(info) |
|
) |
|
finally: |
|
libflif.flif_destroy_info(info) |
|
|
|
self.tile = [("libflif", (0, 0) + self.size, 0, ())] |
|
|
|
|
|
Image.register_open(FLImageFile.format, FLImageFile, _accept) |
|
|
|
Image.register_extension(FLImageFile.format, ".flif") |
|
|
|
Image.register_mime(FLImageFile.format, "image/flif") |
|
Image.register_mime(FLImageFile.format, "application/x-flif") |
|
|
|
|
|
# --------------------------------------------------------- |
|
# Decoder |
|
class LibFLIFDecoder(ImageFile.PyDecoder): |
|
_pulls_fd = True |
|
|
|
def init(self, args): |
|
self.context = libflif.flif_create_decoder() |
|
|
|
def _import_image(self, image, bufsize, mode, readinto_fn): |
|
# FIXME: Don't copy shit around TWICE; Probably want to do it in C |
|
buf = bytearray(bufsize) |
|
readinto_fn(image, ffi.from_buffer(buf), bufsize) |
|
self.set_as_raw(bytes(buf), mode) |
|
|
|
def decode(self, _): |
|
# Figure out best way to get pointer to data in memory |
|
|
|
with FileBuffer(self.fd) as fb: |
|
# Decode |
|
libflif.flif_decoder_decode_memory(self.context, ffi.from_buffer(fb.buffer), fb.size) |
|
|
|
# Get image |
|
image = libflif.flif_decoder_get_image(self.context, 0) |
|
|
|
# Convert to PIL representation |
|
# flif_image_read_into_* defined in libflif_build.py |
|
if self.mode in ("L", "1"): |
|
self._import_image(image, self.state.xsize * self.state.ysize, "L", libflif.flif_image_read_into_GRAY8) |
|
elif self.mode in ("RGB",): |
|
self._import_image(image, self.state.xsize * self.state.ysize * 3 + self.state.xsize, "RGB", libflif.flif_image_read_into_RGB8) |
|
elif self.mode in ("RGBA",): |
|
self._import_image(image, self.state.xsize + self.state.ysize * 4, "RGBA", libflif.flif_image_read_into_RGBA8) |
|
|
|
return 0, 0 |
|
|
|
def cleanup(self): |
|
libflif.flif_destroy_decoder(self.context) |
|
|
|
|
|
Image.register_decoder("libflif", LibFLIFDecoder) |
|
|
|
|
|
# --------------------------------------------------------- |
|
# Writing |
|
def _save(image, fp, filename): |
|
lossy = image.encoderinfo.get("lossy", 0) |
|
|
|
# Create FLIF_IMAGE |
|
# FIXME: Probably should do it in C |
|
mode = image.mode |
|
if mode == "L": |
|
fimage = libflif.flif_import_image_GRAY(image.size[0], image.size[1], image.tobytes(), image.size[0]) |
|
elif mode == "RGB": |
|
fimage = libflif.flif_import_image_RGB(image.size[0], image.size[1], image.tobytes(), image.size[0] * 3) |
|
elif mode == "RGBA": |
|
fimage = libflif.flif_import_image_RGBA(image.size[0], image.size[1], image.tobytes(), image.size[0] * 4) |
|
else: |
|
raise IOError("cannot write mode %s as FLIF (yet)" % mode) |
|
|
|
if fimage == ffi.NULL: |
|
raise RuntimeError("Could not create FLIF_IMAGE") |
|
|
|
# Set up encoder |
|
context = libflif.flif_create_encoder() |
|
|
|
libflif.flif_encoder_set_lossy(context, lossy) |
|
|
|
libflif.flif_encoder_add_image_move(context, fimage) |
|
|
|
# Encode |
|
out_buf = ffi.new("void **") |
|
out_size = ffi.new("size_t *") |
|
|
|
res = libflif.flif_encoder_encode_memory(context, out_buf, out_size) |
|
|
|
fp.write(ffi.buffer(out_buf[0], out_size[0])) |
|
|
|
libflif.flif_destroy_encoder(context) |
|
libflif.flif_free_memory(out_buf[0]) |
|
|
|
|
|
Image.register_save(FLImageFile.format, _save)
|
|
|