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.
This commit is contained in:
parent
02a79ff10e
commit
3f59b8c31f
2 changed files with 177 additions and 31 deletions
137
colout/colout.py
137
colout/colout.py
|
|
@ -13,9 +13,50 @@ import glob
|
||||||
import math
|
import math
|
||||||
import importlib
|
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
|
# Available styles
|
||||||
styles = {
|
styles = {
|
||||||
|
|
@ -30,37 +71,6 @@ colors = {
|
||||||
"magenta": 5, "cyan": 6, "white": 7, "none": -1
|
"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"]
|
rainbow = ["magenta", "blue", "cyan", "green", "yellow", "red"]
|
||||||
colormap = rainbow # default colormap to rainbow
|
colormap = rainbow # default colormap to rainbow
|
||||||
colormap_idx = 0
|
colormap_idx = 0
|
||||||
|
|
@ -70,15 +80,40 @@ scale = (0,100)
|
||||||
# Escaped end markers for given color modes
|
# Escaped end markers for given color modes
|
||||||
endmarks = {8: ";", 256: ";38;5;"}
|
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
|
# load available themes
|
||||||
themes = {}
|
themes = {}
|
||||||
themes_dir=os.path.dirname(os.path.realpath(__file__))
|
themes_dir=os.path.dirname(os.path.realpath(__file__))
|
||||||
os.chdir( themes_dir )
|
os.chdir( themes_dir )
|
||||||
|
|
||||||
for f in glob.iglob("colout_*.py"):
|
for f in glob.iglob("colout_*.py"):
|
||||||
module = ".".join(f.split(".")[:-1]) # remove extension
|
module = ".".join(f.split(".")[:-1]) # remove extension
|
||||||
name = "_".join(module.split("_")[1:]) # remove the prefix
|
name = "_".join(module.split("_")[1:]) # remove the prefix
|
||||||
themes[name] = importlib.import_module(module)
|
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
|
# load available pygments lexers
|
||||||
lexers = []
|
lexers = []
|
||||||
try:
|
try:
|
||||||
|
|
@ -98,6 +133,30 @@ else:
|
||||||
lexers.sort()
|
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"):
|
def colorin(text, color="red", style="normal"):
|
||||||
"""
|
"""
|
||||||
Return the given text, surrounded by the given color ASCII markers.
|
Return the given text, surrounded by the given color ASCII markers.
|
||||||
|
|
@ -162,6 +221,16 @@ def colorin(text, color="red", style="normal"):
|
||||||
else:
|
else:
|
||||||
colormap_idx = 0
|
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":
|
elif color == "scale":
|
||||||
try:
|
try:
|
||||||
import babel.numbers as bn
|
import babel.numbers as bn
|
||||||
|
|
|
||||||
77
colout/jet72.gpl
Normal file
77
colout/jet72.gpl
Normal file
|
|
@ -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
|
||||||
Loading…
Add table
Add a link
Reference in a new issue