From 3f59b8c31f4f2c0135c9094cf8dc39a547c89b26 Mon Sep 17 00:00:00 2001 From: nojhan Date: Sun, 26 May 2013 16:07:38 +0200 Subject: [PATCH] Add GIMP palettes support, refactoring You can now use GIMP palette file as a colormap, by using its filename as a color. Add the well known Matlab jet72 palette (jet72.gpl). Group functions in sections. --- colout/colout.py | 137 +++++++++++++++++++++++++++++++++++------------ colout/jet72.gpl | 77 ++++++++++++++++++++++++++ 2 files changed, 180 insertions(+), 34 deletions(-) create mode 100644 colout/jet72.gpl diff --git a/colout/colout.py b/colout/colout.py index 220645c..68aa367 100755 --- a/colout/colout.py +++ b/colout/colout.py @@ -13,9 +13,50 @@ import glob import math import importlib -########### -# Library # -########### +############################################################################### +# Ressource parsing helpers +############################################################################### + +def parse_gimp_palette( filename ): + """ + Parse the given filename as a GIMP palette (.gpl) + + Return the filename (without path and extension) and a list of ordered + colors. + Generally, the colors are RGB triplets, thus this function returns: + (name, [ [R0,G0,B0], [R1,G1,B1], ... , [RN,GN,BN] ]) + """ + + fd = open(filename) + # remove path and extension, only keep the file name itself + name = os.path.splitext( os.path.basename(filename ))[0] + + # The first .gpl line is a header + assert( fd.readline().strip() == "GIMP Palette" ) + + # Then the full name of the palette + long_name = fd.readline().strip() + + # Then the columns number. + # split on colon, take the second argument as an int + columns = int( fd.readline().strip().split(":")[1].strip() ) + + # Then the colors themselves. + palette = [] + for line in fd: + # skip lines with only a comment + if re.match("^\s*#.*$", line ): + continue + # decode the columns-ths codes. Generally [R G B] followed by a comment + colors = [ int(c) for c in line.split()[:columns] ] + palette.append( colors ) + + return name,palette + + +############################################################################### +# Global variables +############################################################################### # Available styles styles = { @@ -30,37 +71,6 @@ colors = { "magenta": 5, "cyan": 6, "white": 7, "none": -1 } -ansi_min = 16 -ansi_max = 232 - -def rgb_rainbow( x, freq = 1.0/(256.0/math.pi) ): - scope = (ansi_max - ansi_min)/2.0 - red = ansi_min + scope * (1+math.sin( 2*freq*x + math.pi/2 )) - green = ansi_min + scope * (1+math.sin( 2*freq*x - math.pi/2 )) - blue = ansi_min + scope * (1+math.sin( freq*x - math.pi/2 )) - return ( red, green, blue ) - - -def rgb_to_ansi( red, green, blue ): - - offset = 42.5 - is_gray = True - while is_gray: - if red < offset or green < offset or blue < offset: - all_gray = red < offset and green < offset and blue < offset - is_gray = False - offset += 42.5 - - if all_gray: - val = ansi_max + round( (red + green + blue)/33.0 ) - return int(val) - else: - val = ansi_min - for color,modulo in zip( [red, green, blue], [6*6, 6, 1] ): - val += round(6.0 * (color / 256.0)) * modulo - return int(val) - - rainbow = ["magenta", "blue", "cyan", "green", "yellow", "red"] colormap = rainbow # default colormap to rainbow colormap_idx = 0 @@ -70,15 +80,40 @@ scale = (0,100) # Escaped end markers for given color modes endmarks = {8: ";", 256: ";38;5;"} +ansi_min = 16 +ansi_max = 232 + +def rgb_rainbow( x, freq = 1.0/(256.0/math.pi) ): + """Analytical expression of a n-colors rainbow colormap""" + scope = (ansi_max - ansi_min)/2.0 + red = ansi_min + scope * (1+math.sin( 2*freq*x + math.pi/2 )) + green = ansi_min + scope * (1+math.sin( 2*freq*x - math.pi/2 )) + blue = ansi_min + scope * (1+math.sin( freq*x - math.pi/2 )) + return ( red, green, blue ) + + +############################################################################### +# Load available extern ressources +############################################################################### + # load available themes themes = {} themes_dir=os.path.dirname(os.path.realpath(__file__)) os.chdir( themes_dir ) + for f in glob.iglob("colout_*.py"): module = ".".join(f.split(".")[:-1]) # remove extension name = "_".join(module.split("_")[1:]) # remove the prefix themes[name] = importlib.import_module(module) +# load available colormaps (GIMP palettes format) +colormaps = {} +for p in glob.iglob("*.gpl"): + name,palette = parse_gimp_palette(p) + if name in colormaps: + raise Exception('Duplicated palette filename: %s' % name) + colormaps[name] = palette + # load available pygments lexers lexers = [] try: @@ -98,6 +133,30 @@ else: lexers.sort() +############################################################################### +# Library +############################################################################### + +def rgb_to_ansi( red, green, blue ): + """Convert a RGB color to its closest 256-colors ANSI index""" + offset = 42.5 + is_gray = True + while is_gray: + if red < offset or green < offset or blue < offset: + all_gray = red < offset and green < offset and blue < offset + is_gray = False + offset += 42.5 + + if all_gray: + val = ansi_max + round( (red + green + blue)/33.0 ) + return int(val) + else: + val = ansi_min + for color,modulo in zip( [red, green, blue], [6*6, 6, 1] ): + val += round(6.0 * (color / 256.0)) * modulo + return int(val) + + def colorin(text, color="red", style="normal"): """ Return the given text, surrounded by the given color ASCII markers. @@ -162,6 +221,16 @@ def colorin(text, color="red", style="normal"): else: colormap_idx = 0 + elif color in colormaps.keys(): + mode = 256 + color_nb = rgb_to_ansi( *colormaps[color][colormap_idx] ) + color_code = str( color_nb ) + + if colormap_idx < len(colormaps[color]): + colormap_idx += 1 + else: + colormap_idx = 0 + elif color == "scale": try: import babel.numbers as bn diff --git a/colout/jet72.gpl b/colout/jet72.gpl new file mode 100644 index 0000000..ac69f16 --- /dev/null +++ b/colout/jet72.gpl @@ -0,0 +1,77 @@ +GIMP Palette +Name: MATLAB Jet (72) +Columns: 3 +# + 0 0 127 0 + 0 0 127 #1 + 0 0 141 #2 + 0 0 155 #3 + 0 0 169 #4 + 0 0 183 #5 + 0 0 198 #6 + 0 0 212 #7 + 0 0 226 #8 + 0 0 240 #9 + 0 0 255 #10 + 0 14 255 #11 + 0 28 255 #12 + 0 42 255 #13 + 0 56 255 #14 + 0 70 255 #15 + 0 84 255 #16 + 0 98 255 #17 + 0 112 255 #18 + 0 127 255 #19 + 0 141 255 #20 + 0 155 255 #21 + 0 169 255 #22 + 0 183 255 #23 + 0 198 255 #24 + 0 212 255 #25 + 0 226 255 #26 + 0 240 255 #27 + 0 255 255 #28 + 14 255 240 #29 + 28 255 226 #30 + 42 255 212 #31 + 56 255 198 #32 + 70 255 183 #33 + 84 255 169 #34 + 98 255 155 #35 +112 255 141 #36 +127 255 127 #37 +141 255 112 #38 +155 255 98 #39 +169 255 84 #40 +183 255 70 #41 +198 255 56 #42 +212 255 42 #43 +226 255 28 #44 +240 255 14 #45 +255 255 0 #46 +255 240 0 #47 +255 226 0 #48 +255 212 0 #49 +255 198 0 #50 +255 183 0 #51 +255 169 0 #52 +255 155 0 #53 +255 141 0 #54 +255 127 0 #55 +255 112 0 #56 +255 98 0 #57 +255 84 0 #58 +255 70 0 #59 +255 56 0 #60 +255 42 0 #61 +255 28 0 #62 +255 14 0 #63 +255 0 0 #64 +240 0 0 #65 +226 0 0 #66 +212 0 0 #67 +198 0 0 #68 +183 0 0 #69 +169 0 0 #70 +155 0 0 #71 +141 0 0 #72