Coverage for lib/lottie/exporters/gif.py: 5%

68 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-03-20 16:17 +0100

1import io 

2from PIL import Image 

3from PIL import features 

4 

5from .cairo import PngRenderer 

6from .base import exporter, io_progress 

7from ..parsers.baseporter import ExtraOption 

8 

9 

10def _png_gif_prepare(image): 

11 if image.mode not in ["RGBA", "RGBa"]: 

12 image = image.convert("RGBA") 

13 alpha = image.getchannel("A") 

14 image = image.convert("RGB").convert('P', palette=Image.ADAPTIVE, colors=255) 

15 mask = Image.eval(alpha, lambda a: 255 if a <= 128 else 0) 

16 image.paste(255, mask=mask) 

17 return image 

18 

19 

20def _log_frame(fmt, frame_no=None, end=None): 

21 if frame_no is None: 

22 io_progress().report_message("%s frame rendering completed" % (fmt)) 

23 else: 

24 io_progress().report_progress("%s rendering frame" % fmt, frame_no, end) 

25 

26 

27@exporter("GIF", ["gif"], [ 

28 ExtraOption("skip_frames", type=int, default=1, help="Only renderer 1 out of these many frames"), 

29]) 

30def export_gif(animation, fp, dpi=96, skip_frames=1): 

31 """ 

32 Gif export 

33 

34 Note that it's a bit slow. 

35 """ 

36 start = int(animation.in_point) 

37 end = int(animation.out_point) 

38 frames = [] 

39 with PngRenderer(animation, dpi) as renderer: 

40 for i in range(start, end+1, skip_frames): 

41 _log_frame("GIF", i, end) 

42 file = io.BytesIO() 

43 renderer.serialize(i, file) 

44 file.seek(0) 

45 frames.append(_png_gif_prepare(Image.open(file))) 

46 _log_frame("GIF") 

47 

48 io_progress().report_message("GIF Writing to file...") 

49 duration = int(round(1000 / animation.frame_rate * skip_frames / 10)) * 10 

50 frames[0].save( 

51 fp, 

52 format='GIF', 

53 append_images=frames[1:], 

54 save_all=True, 

55 duration=duration, 

56 loop=0, 

57 transparency=255, 

58 disposal=2, 

59 ) 

60 

61 

62@exporter("WebP", ["webp"], [ 

63 ExtraOption("lossless", action="store_true", help="If present, use lossless compression"), 

64 ExtraOption("quality", type=int, default=80, 

65 help="Compression effort between 0 and 100\n" + 

66 "for lossy 0 gives the smallest size\n" + 

67 "for lossless 0 gives the largest file"), 

68 ExtraOption("method", type=int, default=0, help="Quality/speed trade-off (0=fast, 6=slower-better)"), 

69 ExtraOption("skip_frames", type=int, default=1, help="Only renderer 1 out of these many frames"), 

70]) 

71def export_webp(animation, fp, dpi=96, lossless=False, quality=80, method=0, skip_frames=1): 

72 """ 

73 Export WebP 

74 

75 See https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#webp 

76 """ 

77 if not features.check("webp_anim"): 

78 raise Exception("WebP animations not supported in this system") 

79 

80 start = int(animation.in_point) 

81 end = int(animation.out_point) 

82 frames = [] 

83 with PngRenderer(animation, dpi) as renderer: 

84 for i in range(start, end+1, skip_frames): 

85 _log_frame("WebP", i, end) 

86 file = io.BytesIO() 

87 renderer.serialize(i, file) 

88 file.seek(0) 

89 frames.append(Image.open(file)) 

90 

91 _log_frame("WebP") 

92 

93 io_progress().report_message("WebP Writing to file...") 

94 duration = int(round(1000 / animation.frame_rate * skip_frames)) 

95 frames[0].save( 

96 fp, 

97 format='WebP', 

98 append_images=frames[1:], 

99 save_all=True, 

100 duration=duration, 

101 loop=0, 

102 background=(0, 0, 0, 0), 

103 lossless=lossless, 

104 quality=quality, 

105 method=method 

106 ) 

107 

108 

109@exporter("TIFF", ["tiff"]) 

110def export_tiff(animation, fp, dpi=96): 

111 """ 

112 Export TIFF 

113 """ 

114 start = int(animation.in_point) 

115 end = int(animation.out_point) 

116 frames = [] 

117 with PngRenderer(animation, dpi) as renderer: 

118 for i in range(start, end+1): 

119 _log_frame("TIFF", i, end) 

120 file = io.BytesIO() 

121 renderer.serialize(i, file) 

122 file.seek(0) 

123 frames.append(Image.open(file)) 

124 _log_frame("TIFF") 

125 

126 io_progress().report_message("TIFF Writing to file...") 

127 duration = int(round(1000 / animation.frame_rate)) 

128 frames[0].save( 

129 fp, 

130 format='TIFF', 

131 append_images=frames[1:], 

132 save_all=True, 

133 duration=duration, 

134 loop=0, 

135 dpi=(dpi, dpi), 

136 )