File indexing completed on 2025-01-19 03:59:51

0001 import io
0002 from PIL import Image
0003 from PIL import features
0004 
0005 from .cairo import export_png
0006 from .base import exporter, io_progress
0007 from ..parsers.baseporter import ExtraOption
0008 
0009 
0010 def _png_gif_prepare(image):
0011     if image.mode not in ["RGBA", "RGBa"]:
0012         image = image.convert("RGBA")
0013     alpha = image.getchannel("A")
0014     image = image.convert("RGB").convert('P', palette=Image.ADAPTIVE, colors=255)
0015     mask = Image.eval(alpha, lambda a: 255 if a <= 128 else 0)
0016     image.paste(255, mask=mask)
0017     return image
0018 
0019 
0020 def _log_frame(fmt, frame_no=None, end=None):
0021     if frame_no is None:
0022         io_progress().report_message("%s frame rendering completed" % (fmt))
0023     else:
0024         io_progress().report_progress("%s rendering frame" % fmt, frame_no, end)
0025 
0026 
0027 @exporter("GIF", ["gif"], [
0028     ExtraOption("skip_frames", type=int, default=1, help="Only renderer 1 out of these many frames"),
0029 ])
0030 def export_gif(animation, fp, dpi=96, skip_frames=1):
0031     """
0032     Gif export
0033 
0034     Note that it's a bit slow.
0035     """
0036     start = int(animation.in_point)
0037     end = int(animation.out_point)
0038     frames = []
0039     for i in range(start, end+1, skip_frames):
0040         _log_frame("GIF", i, end)
0041         file = io.BytesIO()
0042         export_png(animation, file, i, dpi)
0043         file.seek(0)
0044         frames.append(_png_gif_prepare(Image.open(file)))
0045     _log_frame("GIF")
0046 
0047     io_progress().report_message("GIF Writing to file...")
0048     duration = int(round(1000 / animation.frame_rate * skip_frames / 10)) * 10
0049     frames[0].save(
0050         fp,
0051         format='GIF',
0052         append_images=frames[1:],
0053         save_all=True,
0054         duration=duration,
0055         loop=0,
0056         transparency=255,
0057         disposal=2,
0058     )
0059 
0060 
0061 @exporter("WebP", ["webp"], [
0062     ExtraOption("lossless", action="store_true", help="If present, use lossless compression"),
0063     ExtraOption("quality", type=int, default=80,
0064                 help="Compression effort between 0 and 100\n" +
0065                      "for lossy 0 gives the smallest size\n" +
0066                      "for lossless 0 gives the largest file"),
0067     ExtraOption("method", type=int, default=0, help="Quality/speed trade-off (0=fast, 6=slower-better)"),
0068     ExtraOption("skip_frames", type=int, default=1, help="Only renderer 1 out of these many frames"),
0069 ])
0070 def export_webp(animation, fp, dpi=96, lossless=False, quality=80, method=0, skip_frames=1):
0071     """
0072     Export WebP
0073 
0074     See https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#webp
0075     """
0076     if not features.check("webp_anim"):
0077         raise Exception("WebP animations not supported in this system")
0078 
0079     start = int(animation.in_point)
0080     end = int(animation.out_point)
0081     frames = []
0082     for i in range(start, end+1, skip_frames):
0083         _log_frame("WebP", i, end)
0084         file = io.BytesIO()
0085         export_png(animation, file, i, dpi)
0086         file.seek(0)
0087         frames.append(Image.open(file))
0088 
0089     _log_frame("WebP")
0090 
0091     io_progress().report_message("WebP Writing to file...")
0092     duration = int(round(1000 / animation.frame_rate * skip_frames))
0093     frames[0].save(
0094         fp,
0095         format='WebP',
0096         append_images=frames[1:],
0097         save_all=True,
0098         duration=duration,
0099         loop=0,
0100         background=(0, 0, 0, 0),
0101         lossless=lossless,
0102         quality=quality,
0103         method=method
0104     )
0105 
0106 
0107 @exporter("TIFF", ["tiff"])
0108 def export_tiff(animation, fp, dpi=96):
0109     """
0110     Export TIFF
0111     """
0112     start = int(animation.in_point)
0113     end = int(animation.out_point)
0114     frames = []
0115     for i in range(start, end+1):
0116         _log_frame("TIFF", i, end)
0117         file = io.BytesIO()
0118         export_png(animation, file, i, dpi)
0119         file.seek(0)
0120         frames.append(Image.open(file))
0121     _log_frame("TIFF")
0122 
0123     io_progress().report_message("TIFF Writing to file...")
0124     duration = int(round(1000 / animation.frame_rate))
0125     frames[0].save(
0126         fp,
0127         format='TIFF',
0128         append_images=frames[1:],
0129         save_all=True,
0130         duration=duration,
0131         loop=0,
0132         dpi=(dpi, dpi),
0133     )