# 
 | 
# The Python Imaging Library. 
 | 
# $Id$ 
 | 
# 
 | 
# image palette object 
 | 
# 
 | 
# History: 
 | 
# 1996-03-11 fl   Rewritten. 
 | 
# 1997-01-03 fl   Up and running. 
 | 
# 1997-08-23 fl   Added load hack 
 | 
# 2001-04-16 fl   Fixed randint shadow bug in random() 
 | 
# 
 | 
# Copyright (c) 1997-2001 by Secret Labs AB 
 | 
# Copyright (c) 1996-1997 by Fredrik Lundh 
 | 
# 
 | 
# See the README file for information on usage and redistribution. 
 | 
# 
 | 
  
 | 
import array 
 | 
import Image, ImageColor 
 | 
  
 | 
## 
 | 
# Colour palette wrapper for palette mapped images. 
 | 
  
 | 
class ImagePalette: 
 | 
    "Colour palette for palette mapped images" 
 | 
  
 | 
    def __init__(self, mode = "RGB", palette = None): 
 | 
        self.mode = mode 
 | 
        self.rawmode = None # if set, palette contains raw data 
 | 
        self.palette = palette or range(256)*len(self.mode) 
 | 
        self.colors = {} 
 | 
        self.dirty = None 
 | 
        if len(self.mode)*256 != len(self.palette): 
 | 
            raise ValueError, "wrong palette size" 
 | 
  
 | 
    def getdata(self): 
 | 
        # experimental: get palette contents in format suitable 
 | 
        # for the low-level im.putpalette primitive 
 | 
        if self.rawmode: 
 | 
            return self.rawmode, self.palette 
 | 
        return self.mode + ";L", self.tostring() 
 | 
  
 | 
    def tostring(self): 
 | 
        # experimental: convert palette to string 
 | 
        if self.rawmode: 
 | 
            raise ValueError("palette contains raw palette data") 
 | 
        if Image.isStringType(self.palette): 
 | 
            return self.palette 
 | 
        return array.array("B", self.palette).tostring() 
 | 
  
 | 
    def getcolor(self, color): 
 | 
        # experimental: given an rgb tuple, allocate palette entry 
 | 
        if self.rawmode: 
 | 
            raise ValueError("palette contains raw palette data") 
 | 
        if Image.isTupleType(color): 
 | 
            try: 
 | 
                return self.colors[color] 
 | 
            except KeyError: 
 | 
                # allocate new color slot 
 | 
                if Image.isStringType(self.palette): 
 | 
                    self.palette = map(int, self.palette) 
 | 
                index = len(self.colors) 
 | 
                if index >= 256: 
 | 
                    raise ValueError("cannot allocate more than 256 colors") 
 | 
                self.colors[color] = index 
 | 
                self.palette[index] = color[0] 
 | 
                self.palette[index+256] = color[1] 
 | 
                self.palette[index+512] = color[2] 
 | 
                self.dirty = 1 
 | 
                return index 
 | 
        else: 
 | 
            raise ValueError("unknown color specifier: %r" % color) 
 | 
  
 | 
    def save(self, fp): 
 | 
        # (experimental) save palette to text file 
 | 
        if self.rawmode: 
 | 
            raise ValueError("palette contains raw palette data") 
 | 
        if type(fp) == type(""): 
 | 
            fp = open(fp, "w") 
 | 
        fp.write("# Palette\n") 
 | 
        fp.write("# Mode: %s\n" % self.mode) 
 | 
        for i in range(256): 
 | 
            fp.write("%d" % i) 
 | 
            for j in range(i, len(self.palette), 256): 
 | 
                fp.write(" %d" % self.palette[j]) 
 | 
            fp.write("\n") 
 | 
        fp.close() 
 | 
  
 | 
# -------------------------------------------------------------------- 
 | 
# Internal 
 | 
  
 | 
def raw(rawmode, data): 
 | 
    palette = ImagePalette() 
 | 
    palette.rawmode = rawmode 
 | 
    palette.palette = data 
 | 
    palette.dirty = 1 
 | 
    return palette 
 | 
  
 | 
# -------------------------------------------------------------------- 
 | 
# Factories 
 | 
  
 | 
def _make_linear_lut(black, white): 
 | 
    lut = [] 
 | 
    if black == 0: 
 | 
        for i in range(256): 
 | 
            lut.append(white*i/255) 
 | 
    else: 
 | 
        raise NotImplementedError # FIXME 
 | 
    return lut 
 | 
  
 | 
def _make_gamma_lut(exp, mode="RGB"): 
 | 
    lut = [] 
 | 
    for i in range(256): 
 | 
        lut.append(int(((i / 255.0) ** exp) * 255.0 + 0.5)) 
 | 
    return lut 
 | 
  
 | 
def new(mode, data): 
 | 
    return Image.core.new_palette(mode, data) 
 | 
  
 | 
def negative(mode="RGB"): 
 | 
    palette = range(256) 
 | 
    palette.reverse() 
 | 
    return ImagePalette(mode, palette * len(mode)) 
 | 
  
 | 
def random(mode="RGB"): 
 | 
    from random import randint 
 | 
    palette = [] 
 | 
    for i in range(256*len(mode)): 
 | 
        palette.append(randint(0, 255)) 
 | 
    return ImagePalette(mode, palette) 
 | 
  
 | 
def sepia(white="#fff0c0"): 
 | 
    r, g, b = ImageColor.getrgb(white) 
 | 
    r = _make_linear_lut(0, r) 
 | 
    g = _make_linear_lut(0, g) 
 | 
    b = _make_linear_lut(0, b) 
 | 
    return ImagePalette("RGB", r + g + b) 
 | 
  
 | 
def wedge(mode="RGB"): 
 | 
    return ImagePalette(mode, range(256) * len(mode)) 
 | 
  
 | 
def load(filename): 
 | 
  
 | 
    # FIXME: supports GIMP gradients only 
 | 
  
 | 
    fp = open(filename, "rb") 
 | 
  
 | 
    lut = None 
 | 
  
 | 
    if not lut: 
 | 
        try: 
 | 
            import GimpPaletteFile 
 | 
            fp.seek(0) 
 | 
            p = GimpPaletteFile.GimpPaletteFile(fp) 
 | 
            lut = p.getpalette() 
 | 
        except (SyntaxError, ValueError): 
 | 
            pass 
 | 
  
 | 
    if not lut: 
 | 
        try: 
 | 
            import GimpGradientFile 
 | 
            fp.seek(0) 
 | 
            p = GimpGradientFile.GimpGradientFile(fp) 
 | 
            lut = p.getpalette() 
 | 
        except (SyntaxError, ValueError): 
 | 
            pass 
 | 
  
 | 
    if not lut: 
 | 
        try: 
 | 
            import PaletteFile 
 | 
            fp.seek(0) 
 | 
            p = PaletteFile.PaletteFile(fp) 
 | 
            lut = p.getpalette() 
 | 
        except (SyntaxError, ValueError): 
 | 
            pass 
 | 
  
 | 
    if not lut: 
 | 
        raise IOError, "cannot load palette" 
 | 
  
 | 
    return lut # data, rawmode 
 | 
  
 | 
  
 | 
# add some psuedocolour palettes as well 
 |