You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

216 lines
6.2 KiB

4 years ago
  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. import array
  19. from . import ImageColor, GimpPaletteFile, GimpGradientFile, PaletteFile
  20. class ImagePalette(object):
  21. """
  22. Color palette for palette mapped images
  23. :param mode: The mode to use for the Palette. See:
  24. :ref:`concept-modes`. Defaults to "RGB"
  25. :param palette: An optional palette. If given, it must be a bytearray,
  26. an array or a list of ints between 0-255 and of length ``size``
  27. times the number of colors in ``mode``. The list must be aligned
  28. by channel (All R values must be contiguous in the list before G
  29. and B values.) Defaults to 0 through 255 per channel.
  30. :param size: An optional palette size. If given, it cannot be equal to
  31. or greater than 256. Defaults to 0.
  32. """
  33. def __init__(self, mode="RGB", palette=None, size=0):
  34. self.mode = mode
  35. self.rawmode = None # if set, palette contains raw data
  36. self.palette = palette or bytearray(range(256))*len(self.mode)
  37. self.colors = {}
  38. self.dirty = None
  39. if ((size == 0 and len(self.mode)*256 != len(self.palette)) or
  40. (size != 0 and size != len(self.palette))):
  41. raise ValueError("wrong palette size")
  42. def copy(self):
  43. new = ImagePalette()
  44. new.mode = self.mode
  45. new.rawmode = self.rawmode
  46. if self.palette is not None:
  47. new.palette = self.palette[:]
  48. new.colors = self.colors.copy()
  49. new.dirty = self.dirty
  50. return new
  51. def getdata(self):
  52. """
  53. Get palette contents in format suitable for the low-level
  54. ``im.putpalette`` primitive.
  55. .. warning:: This method is experimental.
  56. """
  57. if self.rawmode:
  58. return self.rawmode, self.palette
  59. return self.mode + ";L", self.tobytes()
  60. def tobytes(self):
  61. """Convert palette to bytes.
  62. .. warning:: This method is experimental.
  63. """
  64. if self.rawmode:
  65. raise ValueError("palette contains raw palette data")
  66. if isinstance(self.palette, bytes):
  67. return self.palette
  68. arr = array.array("B", self.palette)
  69. if hasattr(arr, 'tobytes'):
  70. return arr.tobytes()
  71. return arr.tostring()
  72. # Declare tostring as an alias for tobytes
  73. tostring = tobytes
  74. def getcolor(self, color):
  75. """Given an rgb tuple, allocate palette entry.
  76. .. warning:: This method is experimental.
  77. """
  78. if self.rawmode:
  79. raise ValueError("palette contains raw palette data")
  80. if isinstance(color, tuple):
  81. try:
  82. return self.colors[color]
  83. except KeyError:
  84. # allocate new color slot
  85. if isinstance(self.palette, bytes):
  86. self.palette = bytearray(self.palette)
  87. index = len(self.colors)
  88. if index >= 256:
  89. raise ValueError("cannot allocate more than 256 colors")
  90. self.colors[color] = index
  91. self.palette[index] = color[0]
  92. self.palette[index+256] = color[1]
  93. self.palette[index+512] = color[2]
  94. self.dirty = 1
  95. return index
  96. else:
  97. raise ValueError("unknown color specifier: %r" % color)
  98. def save(self, fp):
  99. """Save palette to text file.
  100. .. warning:: This method is experimental.
  101. """
  102. if self.rawmode:
  103. raise ValueError("palette contains raw palette data")
  104. if isinstance(fp, str):
  105. fp = open(fp, "w")
  106. fp.write("# Palette\n")
  107. fp.write("# Mode: %s\n" % self.mode)
  108. for i in range(256):
  109. fp.write("%d" % i)
  110. for j in range(i*len(self.mode), (i+1)*len(self.mode)):
  111. try:
  112. fp.write(" %d" % self.palette[j])
  113. except IndexError:
  114. fp.write(" 0")
  115. fp.write("\n")
  116. fp.close()
  117. # --------------------------------------------------------------------
  118. # Internal
  119. def raw(rawmode, data):
  120. palette = ImagePalette()
  121. palette.rawmode = rawmode
  122. palette.palette = data
  123. palette.dirty = 1
  124. return palette
  125. # --------------------------------------------------------------------
  126. # Factories
  127. def make_linear_lut(black, white):
  128. lut = []
  129. if black == 0:
  130. for i in range(256):
  131. lut.append(white*i//255)
  132. else:
  133. raise NotImplementedError # FIXME
  134. return lut
  135. def make_gamma_lut(exp):
  136. lut = []
  137. for i in range(256):
  138. lut.append(int(((i / 255.0) ** exp) * 255.0 + 0.5))
  139. return lut
  140. def negative(mode="RGB"):
  141. palette = list(range(256))
  142. palette.reverse()
  143. return ImagePalette(mode, palette * len(mode))
  144. def random(mode="RGB"):
  145. from random import randint
  146. palette = []
  147. for i in range(256*len(mode)):
  148. palette.append(randint(0, 255))
  149. return ImagePalette(mode, palette)
  150. def sepia(white="#fff0c0"):
  151. r, g, b = ImageColor.getrgb(white)
  152. r = make_linear_lut(0, r)
  153. g = make_linear_lut(0, g)
  154. b = make_linear_lut(0, b)
  155. return ImagePalette("RGB", r + g + b)
  156. def wedge(mode="RGB"):
  157. return ImagePalette(mode, list(range(256)) * len(mode))
  158. def load(filename):
  159. # FIXME: supports GIMP gradients only
  160. with open(filename, "rb") as fp:
  161. for paletteHandler in [
  162. GimpPaletteFile.GimpPaletteFile,
  163. GimpGradientFile.GimpGradientFile,
  164. PaletteFile.PaletteFile
  165. ]:
  166. try:
  167. fp.seek(0)
  168. lut = paletteHandler(fp).getpalette()
  169. if lut:
  170. break
  171. except (SyntaxError, ValueError):
  172. # import traceback
  173. # traceback.print_exc()
  174. pass
  175. else:
  176. raise IOError("cannot load palette")
  177. return lut # data, rawmode