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 )