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

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# 

18 

19import array 

20 

21from . import GimpGradientFile, GimpPaletteFile, ImageColor, PaletteFile 

22from ._deprecate import deprecate 

23 

24 

25class ImagePalette: 

26 """ 

27 Color palette for palette mapped images 

28 

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 """ 

36 

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") 

46 

47 @property 

48 def palette(self): 

49 return self._palette 

50 

51 @palette.setter 

52 def palette(self, palette): 

53 self._palette = palette 

54 

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 

62 

63 def copy(self): 

64 new = ImagePalette() 

65 

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 

71 

72 return new 

73 

74 def getdata(self): 

75 """ 

76 Get palette contents in format suitable for the low-level 

77 ``im.putpalette`` primitive. 

78 

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() 

84 

85 def tobytes(self): 

86 """Convert palette to bytes. 

87 

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() 

96 

97 # Declare tostring as an alias for tobytes 

98 tostring = tobytes 

99 

100 def getcolor(self, color, image=None): 

101 """Given an rgb tuple, allocate palette entry. 

102 

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)}") 

151 

152 def save(self, fp): 

153 """Save palette to text file. 

154 

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() 

172 

173 

174# -------------------------------------------------------------------- 

175# Internal 

176 

177 

178def raw(rawmode, data): 

179 palette = ImagePalette() 

180 palette.rawmode = rawmode 

181 palette.palette = data 

182 palette.dirty = 1 

183 return palette 

184 

185 

186# -------------------------------------------------------------------- 

187# Factories 

188 

189 

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 

198 

199 

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 

205 

206 

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]) 

211 

212 

213def random(mode="RGB"): 

214 from random import randint 

215 

216 palette = [] 

217 for i in range(256 * len(mode)): 

218 palette.append(randint(0, 255)) 

219 return ImagePalette(mode, palette) 

220 

221 

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)]) 

225 

226 

227def wedge(mode="RGB"): 

228 palette = list(range(256 * len(mode))) 

229 return ImagePalette(mode, [i // len(mode) for i in palette]) 

230 

231 

232def load(filename): 

233 

234 # FIXME: supports GIMP gradients only 

235 

236 with open(filename, "rb") as fp: 

237 

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") 

254 

255 return lut # data, rawmode