Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/PIL/ImagePalette.py: 24%
144 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1#
2# The Python Imaging Library.
3# $Id$
4#
5# image palette object
6#
7# History:
8# 1996-03-11 fl Rewritten.
9# 1997-01-03 fl Up and running.
10# 1997-08-23 fl Added load hack
11# 2001-04-16 fl Fixed randint shadow bug in random()
12#
13# Copyright (c) 1997-2001 by Secret Labs AB
14# Copyright (c) 1996-1997 by Fredrik Lundh
15#
16# See the README file for information on usage and redistribution.
17#
19import array
21from . import GimpGradientFile, GimpPaletteFile, ImageColor, PaletteFile
22from ._deprecate import deprecate
25class ImagePalette:
26 """
27 Color palette for palette mapped images
29 :param mode: The mode to use for the palette. See:
30 :ref:`concept-modes`. Defaults to "RGB"
31 :param palette: An optional palette. If given, it must be a bytearray,
32 an array or a list of ints between 0-255. The list must consist of
33 all channels for one color followed by the next color (e.g. RGBRGBRGB).
34 Defaults to an empty palette.
35 """
37 def __init__(self, mode="RGB", palette=None, size=0):
38 self.mode = mode
39 self.rawmode = None # if set, palette contains raw data
40 self.palette = palette or bytearray()
41 self.dirty = None
42 if size != 0: 42 ↛ 43line 42 didn't jump to line 43, because the condition on line 42 was never true
43 deprecate("The size parameter", 10, None)
44 if size != len(self.palette):
45 raise ValueError("wrong palette size")
47 @property
48 def palette(self):
49 return self._palette
51 @palette.setter
52 def palette(self, palette):
53 self._palette = palette
55 mode_len = len(self.mode)
56 self.colors = {}
57 for i in range(0, len(self.palette), mode_len):
58 color = tuple(self.palette[i : i + mode_len])
59 if color in self.colors:
60 continue
61 self.colors[color] = i // mode_len
63 def copy(self):
64 new = ImagePalette()
66 new.mode = self.mode
67 new.rawmode = self.rawmode
68 if self.palette is not None:
69 new.palette = self.palette[:]
70 new.dirty = self.dirty
72 return new
74 def getdata(self):
75 """
76 Get palette contents in format suitable for the low-level
77 ``im.putpalette`` primitive.
79 .. warning:: This method is experimental.
80 """
81 if self.rawmode: 81 ↛ 83line 81 didn't jump to line 83, because the condition on line 81 was never false
82 return self.rawmode, self.palette
83 return self.mode, self.tobytes()
85 def tobytes(self):
86 """Convert palette to bytes.
88 .. warning:: This method is experimental.
89 """
90 if self.rawmode:
91 raise ValueError("palette contains raw palette data")
92 if isinstance(self.palette, bytes):
93 return self.palette
94 arr = array.array("B", self.palette)
95 return arr.tobytes()
97 # Declare tostring as an alias for tobytes
98 tostring = tobytes
100 def getcolor(self, color, image=None):
101 """Given an rgb tuple, allocate palette entry.
103 .. warning:: This method is experimental.
104 """
105 if self.rawmode:
106 raise ValueError("palette contains raw palette data")
107 if isinstance(color, tuple):
108 if self.mode == "RGB":
109 if len(color) == 4 and color[3] == 255:
110 color = color[:3]
111 elif self.mode == "RGBA":
112 if len(color) == 3:
113 color += (255,)
114 try:
115 return self.colors[color]
116 except KeyError as e:
117 # allocate new color slot
118 if not isinstance(self.palette, bytearray):
119 self._palette = bytearray(self.palette)
120 index = len(self.palette) // 3
121 special_colors = ()
122 if image:
123 special_colors = (
124 image.info.get("background"),
125 image.info.get("transparency"),
126 )
127 while index in special_colors:
128 index += 1
129 if index >= 256:
130 if image:
131 # Search for an unused index
132 for i, count in reversed(list(enumerate(image.histogram()))):
133 if count == 0 and i not in special_colors:
134 index = i
135 break
136 if index >= 256:
137 raise ValueError("cannot allocate more than 256 colors") from e
138 self.colors[color] = index
139 if index * 3 < len(self.palette):
140 self._palette = (
141 self.palette[: index * 3]
142 + bytes(color)
143 + self.palette[index * 3 + 3 :]
144 )
145 else:
146 self._palette += bytes(color)
147 self.dirty = 1
148 return index
149 else:
150 raise ValueError(f"unknown color specifier: {repr(color)}")
152 def save(self, fp):
153 """Save palette to text file.
155 .. warning:: This method is experimental.
156 """
157 if self.rawmode:
158 raise ValueError("palette contains raw palette data")
159 if isinstance(fp, str):
160 fp = open(fp, "w")
161 fp.write("# Palette\n")
162 fp.write(f"# Mode: {self.mode}\n")
163 for i in range(256):
164 fp.write(f"{i}")
165 for j in range(i * len(self.mode), (i + 1) * len(self.mode)):
166 try:
167 fp.write(f" {self.palette[j]}")
168 except IndexError:
169 fp.write(" 0")
170 fp.write("\n")
171 fp.close()
174# --------------------------------------------------------------------
175# Internal
178def raw(rawmode, data):
179 palette = ImagePalette()
180 palette.rawmode = rawmode
181 palette.palette = data
182 palette.dirty = 1
183 return palette
186# --------------------------------------------------------------------
187# Factories
190def make_linear_lut(black, white):
191 lut = []
192 if black == 0:
193 for i in range(256):
194 lut.append(white * i // 255)
195 else:
196 raise NotImplementedError # FIXME
197 return lut
200def make_gamma_lut(exp):
201 lut = []
202 for i in range(256):
203 lut.append(int(((i / 255.0) ** exp) * 255.0 + 0.5))
204 return lut
207def negative(mode="RGB"):
208 palette = list(range(256 * len(mode)))
209 palette.reverse()
210 return ImagePalette(mode, [i // len(mode) for i in palette])
213def random(mode="RGB"):
214 from random import randint
216 palette = []
217 for i in range(256 * len(mode)):
218 palette.append(randint(0, 255))
219 return ImagePalette(mode, palette)
222def sepia(white="#fff0c0"):
223 bands = [make_linear_lut(0, band) for band in ImageColor.getrgb(white)]
224 return ImagePalette("RGB", [bands[i % 3][i // 3] for i in range(256 * 3)])
227def wedge(mode="RGB"):
228 palette = list(range(256 * len(mode)))
229 return ImagePalette(mode, [i // len(mode) for i in palette])
232def load(filename):
234 # FIXME: supports GIMP gradients only
236 with open(filename, "rb") as fp:
238 for paletteHandler in [
239 GimpPaletteFile.GimpPaletteFile,
240 GimpGradientFile.GimpGradientFile,
241 PaletteFile.PaletteFile,
242 ]:
243 try:
244 fp.seek(0)
245 lut = paletteHandler(fp).getpalette()
246 if lut:
247 break
248 except (SyntaxError, ValueError):
249 # import traceback
250 # traceback.print_exc()
251 pass
252 else:
253 raise OSError("cannot load palette")
255 return lut # data, rawmode