blob: 359e2d8670cecdd77c7dfe639475c435e07e5cfe [file] [log] [blame]
Barry Warsaw2406b1d1998-01-31 00:29:41 +00001"""Color Database.
2
3To create a class that contains color lookup methods, use the module global
4function `get_colordb(file)'. This function will try to examine the file to
5figure out what the format of the file is. If it can't figure out the file
6format, or it has trouble reading the file, None is returned. You can pass
7get_colordb() an optional filetype argument.
8
9Supporte file types are:
10
11 X_RGB_TXT -- X Consortium rgb.txt format files. Three columns of numbers
12 from 0 .. 255 separated by whitespace. Arbitrary trailing
13 columns used as the color name.
14"""
15
16import sys
17import re
18
19
20# generic class
21class ColorDB:
22 def __init__(self, fp, lineno):
23 # Maintain several dictionaries for indexing into the color database.
24 # Note that while Tk supports RGB intensities of 4, 8, 12, or 16 bits,
25 # for now we only support 8 bit intensities. At least on OpenWindows,
26 # all intensities in the /usr/openwin/lib/rgb.txt file are 8-bit
27 #
28 # key is rrggbb, value is (name, [aliases])
29 self.__byrrggbb = {}
30 #
31 # key is name, value is (red, green, blue, rrggbb)
32 self.__byname = {}
33 #
34 while 1:
35 line = fp.readline()
36 if not line:
37 break
38 # get this compiled regular expression from derived class
39 mo = self._re.match(line)
40 if not mo:
41 sys.stderr.write('Error in %s, line %d\n' % (fp.name, lineno))
42 lineno = lineno + 1
43 continue
44 #
45 # extract the red, green, blue, and name
46 red, green, blue = map(int, mo.group('red', 'green', 'blue'))
47 name = mo.group('name')
48 #
49 # calculate the 24 bit representation of the color
50 rrggbb = (red << 16) + (blue << 8) + green
51 #
52 # TBD: for now the `name' is just the first named color with the
53 # rgb values we find. Later, we might want to make the two word
54 # version the `name', or the CapitalizedVersion, etc.
55 foundname, aliases = self.__byrrggbb.get(rrggbb, (name, []))
56 if foundname <> name and foundname not in aliases:
57 aliases.append(name)
58 #
59 # add to by 24bit value
60 self.__byrrggbb[rrggbb] = (foundname, aliases)
61 #
62 # add to byname lookup
63 point = (red, green, blue, rrggbb)
64 self.__byname[name] = point
65 lineno = lineno + 1
66
67 def find(self, red, green, blue):
68 rrggbb = (red << 16) + (blue << 8) + green
69 return self.__byrrggbb.get(rrggbb, (None, []))
70
71 def find_byname(self, name):
72 # TBD: is the unfound value right?
73 return self.__byname.get(name, (0, 0, 0, 0))
74
75 def nearest(self, red, green, blue):
76 # TBD: use Voronoi diagrams, Delaunay triangulation, or octree for
77 # speeding up the locating of nearest point. This is really
78 # inefficient!
79 nearest = -1
80 nearest_name = ''
81 for name, (r, g, b, rrggbb) in self.__byname.items():
82 rdelta = red - r
83 gdelta = green - g
84 bdelta = blue - b
85 distance = rdelta * rdelta + gdelta * gdelta + bdelta * bdelta
86 if nearest == -1 or distance < nearest:
87 nearest = distance
88 nearest_name = name
89 return nearest_name
90
91
92class RGBColorDB(ColorDB):
93 _re = re.compile(
94 '\s*(?P<red>\d+)\s+(?P<green>\d+)\s+(?P<blue>\d+)\s+(?P<name>.*)')
95
96
97
98# format is a tuple (RE, SCANLINES, CLASS) where RE is a compiled regular
99# expression, SCANLINES is the number of header lines to scan, and CLASS is
100# the class to instantiate if a match is found
101
102X_RGB_TXT = re.compile('XConsortium'), 1, RGBColorDB
103
104def get_colordb(file, filetype=X_RGB_TXT):
105 colordb = None
106 fp = None
107 typere, scanlines, class_ = filetype
108 try:
109 try:
110 lineno = 0
111 fp = open(file)
112 while lineno < scanlines:
113 line = fp.readline()
114 if not line:
115 break
116 mo = typere.search(line)
117 if mo:
118 colordb = class_(fp, lineno)
119 break
120 lineno = lineno + 1
121 except IOError:
122 pass
123 finally:
124 if fp:
125 fp.close()
126 return colordb
127
128
129if __name__ == '__main__':
130 import string
131
132 colordb = get_colordb('/usr/openwin/lib/rgb.txt')
133 if not colordb:
134 print 'No parseable color database found'
135 sys.exit(1)
136 # on my system, this color matches exactly
137 target = 'navy'
138 target = 'snow'
139 red, green, blue, rrggbb = colordb.find_byname(target)
140 print target, ':', red, green, blue, hex(rrggbb)
141 name, aliases = colordb.find(red, green, blue)
142 print 'name:', name, 'aliases:', string.join(aliases, ", ")
143 target = (1, 1, 128) # nearest to navy
144 target = (145, 238, 144) # nearest to lightgreen
145 target = (255, 251, 250) # snow
146 print 'finding nearest to', target, '...'
147 import time
148 t0 = time.time()
149 nearest = apply(colordb.nearest, target)
150 t1 = time.time()
151 print 'found nearest color', nearest, 'in', t1-t0, 'seconds'