Coverage for pyguymer3/image/image2gif.py: 5%
22 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-08 18:47 +0000
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-08 18:47 +0000
1#!/usr/bin/env python3
3# Define function ...
4def image2gif(
5 img,
6 gif,
7 /,
8 *,
9 chunksize = 1048576,
10 debug = __debug__,
11 exiftoolPath = None,
12 gifsiclePath = None,
13 jpegtranPath = None,
14 mode = "RGB",
15 optimise = True,
16 optipngPath = None,
17 screenHeight = -1,
18 screenWidth = -1,
19 strip = False,
20 timeout = 60.0,
21):
22 """Save an image as a GIF
24 This function accepts either a PIL Image or a file path and saves the image
25 as a GIF.
27 Parameters
28 ----------
29 img : PIL.Image.Image or str
30 the input PIL Image or path to the input image
31 gif : str
32 the path to the output GIF
33 chunksize : int, optional
34 the size of the chunks of any files which are read in (in bytes)
35 debug : bool, optional
36 print debug messages (default False)
37 exiftoolPath : str, optional
38 the path to the "exiftool" binary (if not provided then Python will attempt to
39 find the binary itself)
40 gifsiclePath : str, optional
41 the path to the "gifsicle" binary (if not provided then Python will attempt to
42 find the binary itself)
43 jpegtranPath : str, optional
44 the path to the "jpegtran" binary (if not provided then Python will attempt to
45 find the binary itself)
46 mode : str, optional
47 the mode of the outout GIF (default "RGB")
48 optimise : bool, optional
49 optimise the output GIF (default True)
50 optipngPath : str, optional
51 the path to the "optipng" binary (if not provided then Python will attempt to
52 find the binary itself)
53 screenHeight : int, optional
54 the height of the screen to downscale the input image to fit within,
55 currently only implemented if "img" is a str (default -1; integers less
56 than 100 imply no downscaling)
57 screenWidth : int, optional
58 the width of the screen to downscale the input image to fit within,
59 currently only implemented if "img" is a str (default -1; integers less
60 than 100 imply no downscaling)
61 strip : bool, optional
62 strip metadata from the output GIF (default False)
63 timeout : float, optional
64 the timeout for any requests/subprocess calls
66 Notes
67 -----
68 Copyright 2017 Thomas Guymer [1]_
70 References
71 ----------
72 .. [1] PyGuymer3, https://github.com/Guymer/PyGuymer3
73 """
75 # Import special modules ...
76 try:
77 import PIL
78 import PIL.Image
79 PIL.Image.MAX_IMAGE_PIXELS = 1024 * 1024 * 1024 # [px]
80 except:
81 raise Exception("\"PIL\" is not installed; run \"pip install --user Pillow\"") from None
83 # Import sub-functions ...
84 from .optimise_image import optimise_image
86 # Find out what the user supplied ...
87 match img:
88 case str():
89 # Open image as RGB (even if it is paletted) ...
90 with PIL.Image.open(img) as iObj:
91 tmpImg = iObj.convert("RGB")
93 # Check if the user wants to scale the image down to fit within a
94 # screen size ...
95 if screenWidth >= 100 and screenHeight >= 100:
96 # Resize image in place ...
97 tmpImg.thumbnail(
98 (screenWidth, screenHeight),
99 resample = PIL.Image.Resampling.LANCZOS,
100 )
102 # Convert it to whatever mode the user asked for ...
103 tmpImg = tmpImg.convert(mode)
104 case PIL.Image.Image():
105 # Convert image to whatever mode the user asked for ...
106 tmpImg = img.convert(mode)
107 case _:
108 # Crash ...
109 raise TypeError(f"\"img\" is an unexpected type ({repr(type(img))})") from None
111 # Save it as a GIF ...
112 # NOTE: See https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#gif
113 tmpImg.save(
114 gif,
115 optimise = optimise,
116 )
118 # Optimise GIF ...
119 if optimise or strip:
120 optimise_image(
121 gif,
122 chunksize = chunksize,
123 debug = debug,
124 exiftoolPath = exiftoolPath,
125 gifsiclePath = gifsiclePath,
126 jpegtranPath = jpegtranPath,
127 optipngPath = optipngPath,
128 pool = None,
129 strip = strip,
130 timeout = timeout,
131 )