1010 "COLOR" ]
1111
1212
13+ def _clip (val , _min , _max ):
14+ return max (min (val , _max ), _min )
15+
16+
1317class Color (object ):
1418 """A class for working with and converting RGBA colors.
1519
@@ -47,10 +51,10 @@ class Color(object):
4751 def __init__ (self , r = 255 , g = 255 , b = 255 , a = 255 ):
4852 for val in (r , g , b , a ):
4953 self ._verify_rgba_value (val )
50- self ._r = int (r )
51- self ._g = int (g )
52- self ._b = int (b )
53- self ._a = int (a )
54+ self ._r = float ( int (r ) )
55+ self ._g = float ( int (g ) )
56+ self ._b = float ( int (b ) )
57+ self ._a = float ( int (a ) )
5458
5559 def _verify_rgba_value (self , val ):
5660 """Verifies that the input is a valid uint8 RGBA value."""
@@ -59,7 +63,7 @@ def _verify_rgba_value(self, val):
5963 float (val )
6064 except (ValueError , TypeError ):
6165 raise TypeError (e .format (val ))
62- if int ( val ) != val or val < 0 or val > 255 :
66+ if val < 0 or val > 255 :
6367 raise ValueError (e .format (val ))
6468
6569 def __repr__ (self ):
@@ -108,31 +112,35 @@ def __mod__(self, color):
108112
109113 def __div__ (self , color ):
110114 vals = [0 , 0 , 0 , 0 ]
111- if color .r != 0 :
112- vals [0 ] = self .r / color .r
113- if color .g != 0 :
114- vals [1 ] = self .g / color .g
115- if color .b != 0 :
116- vals [2 ] = self .b / color .b
117- if color .a != 0 :
118- vals [3 ] = self .a / color .a
115+ if color ._r != 0 :
116+ vals [0 ] = ( self ._r / color ._r )
117+ if color ._g != 0 :
118+ vals [1 ] = ( self ._g / color ._g )
119+ if color ._b != 0 :
120+ vals [2 ] = ( self ._b / color ._b )
121+ if color ._a != 0 :
122+ vals [3 ] = ( self ._a / color ._a )
119123 return Color (vals [0 ], vals [1 ], vals [2 ], vals [3 ])
120124
121125 def __truediv__ (self , color ):
122126 vals = [0 , 0 , 0 , 0 ]
123- if color .r != 0 :
124- vals [0 ] = self .r / color .r
125- if color .g != 0 :
126- vals [1 ] = self .g / color .g
127- if color .b != 0 :
128- vals [2 ] = self .b / color .b
129- if color .a != 0 :
130- vals [3 ] = self .a / color .a
127+ if color ._r != 0 :
128+ vals [0 ] = ( self ._r / color ._r )
129+ if color ._g != 0 :
130+ vals [1 ] = ( self ._g / color ._g )
131+ if color ._b != 0 :
132+ vals [2 ] = ( self ._b / color ._b )
133+ if color ._a != 0 :
134+ vals [3 ] = ( self ._a / color ._a )
131135 return Color (vals [0 ], vals [1 ], vals [2 ], vals [3 ])
132136
133137 def __mul__ (self , color ):
134- vals = (min (self .r * color .r , 255 ), min (self .g * color .g , 255 ),
135- min (self .b * color .b , 255 ), min (self .a * color .a , 255 ))
138+ vals = (
139+ min (self ._r * color ._r , 255 ),
140+ min (self ._g * color ._g , 255 ),
141+ min (self ._b * color ._b , 255 ),
142+ min (self ._a * color ._a , 255 )
143+ )
136144 return Color (vals [0 ], vals [1 ], vals [2 ], vals [3 ])
137145
138146 def __sub__ (self , color ):
@@ -162,42 +170,42 @@ def __setitem__(self, index, val):
162170 @property
163171 def r (self ):
164172 """"int: The 8-bit RGBA red level for the color."""
165- return self ._r
173+ return int ( round ( self ._r ))
166174
167175 @r .setter
168176 def r (self , val ):
169177 self ._verify_rgba_value (val )
170- self ._r = int (val )
178+ self ._r = float (val )
171179
172180 @property
173181 def g (self ):
174182 """"int: The 8-bit RGBA green level for the color."""
175- return self ._g
183+ return int ( round ( self ._g ))
176184
177185 @g .setter
178186 def g (self , val ):
179187 self ._verify_rgba_value (val )
180- self ._g = int (val )
188+ self ._g = float (val )
181189
182190 @property
183191 def b (self ):
184192 """"int: The 8-bit RGBA blue level for the color."""
185- return self ._b
193+ return int ( round ( self ._b ))
186194
187195 @b .setter
188196 def b (self , val ):
189197 self ._verify_rgba_value (val )
190- self ._b = int (val )
198+ self ._b = float (val )
191199
192200 @property
193201 def a (self ):
194202 """"int: The 8-bit RGBA alpha transparency level for the color."""
195- return self ._a
203+ return int ( round ( self ._a ))
196204
197205 @a .setter
198206 def a (self , val ):
199207 self ._verify_rgba_value (val )
200- self ._a = int (val )
208+ self ._a = float (val )
201209
202210 @property
203211 def hsva (self ):
@@ -212,10 +220,10 @@ def hsva(self):
212220 values for the given color.
213221
214222 """
215- rn = self .r / 255.0
216- gn = self .g / 255.0
217- bn = self .b / 255.0
218- an = self .a / 255.0
223+ rn = self ._r / 255.0
224+ gn = self ._g / 255.0
225+ bn = self ._b / 255.0
226+ an = self ._a / 255.0
219227
220228 maxv = max (rn , gn , bn )
221229 minv = min (rn , gn , bn )
@@ -238,6 +246,7 @@ def hsva(self):
238246 h = (60 * (rn - gn ) / diff ) + 240.0
239247 if h < 0 :
240248 h += 360.0
249+
241250 return (h , s , v , a )
242251
243252 @hsva .setter
@@ -269,7 +278,7 @@ def hsva(self, value):
269278 (t , p , v ), # if hi == 4
270279 (v , p , q ) # if hi == 5
271280 ]
272- vals = [int (n * 255 ) for n in rgb_map [hi ]]
281+ vals = [_clip (n * 255.0 , 0.0 , 255.0 ) for n in rgb_map [hi ]]
273282 self .r , self .g , self .b = vals
274283
275284 @property
@@ -285,10 +294,10 @@ def hsla(self):
285294 values for the given color.
286295
287296 """
288- rn = self .r / 255.0
289- gn = self .g / 255.0
290- bn = self .b / 255.0
291- an = self .a / 255.0
297+ rn = self ._r / 255.0
298+ gn = self ._g / 255.0
299+ bn = self ._b / 255.0
300+ an = self ._a / 255.0
292301
293302 maxv = max (rn , gn , bn )
294303 minv = min (rn , gn , bn )
@@ -315,6 +324,7 @@ def hsla(self):
315324 h = (60 * (rn - gn ) / diff ) + 240.0
316325 if h < 0 :
317326 h += 360.0
327+
318328 return (h , s , l , a )
319329
320330 @hsla .setter
@@ -328,14 +338,12 @@ def hsla(self, value):
328338 raise ValueError ("invalid HSLA value" )
329339
330340 self .a = int ((a / 100.0 ) * 255 )
331-
332341 s /= 100.0
333342 l /= 100.0
334-
335343 if s == 0 :
336- self .r = int ( l * 255 )
337- self .g = int ( l * 255 )
338- self .b = int ( l * 255 )
344+ self .r = l * 255.0
345+ self .g = l * 255.0
346+ self .b = l * 255.0
339347 return
340348
341349 q = 0
@@ -346,54 +354,23 @@ def hsla(self, value):
346354 p = 2 * l - q
347355
348356 ht = h / 360.0
357+ vals = []
358+ for h in [ht + (1.0 / 3.0 ), ht , ht - (1.0 / 3.0 )]:
359+ if h < 0 :
360+ h += 1
361+ elif h > 1 :
362+ h -= 1
363+ if h < (1.0 / 6.0 ):
364+ val = (p + ((q - p ) * 6 * h ))
365+ elif h < 0.5 :
366+ val = q
367+ elif h < (2.0 / 3.0 ):
368+ val = (p + ((q - p ) * 6 * (2.0 / 3.0 - h )))
369+ else :
370+ val = p
371+ vals .append (_clip (val * 255.0 , 0.0 , 255.0 ))
349372
350- # r
351- h = ht + (1.0 / 3.0 )
352- if h < 0 :
353- h += 1
354- elif h > 1 :
355- h -= 1
356-
357- if h < (1.0 / 6.0 ):
358- self .r = int ((p + ((q - p ) * 6 * h )) * 255 )
359- elif h < 0.5 :
360- self .r = int (q * 255 )
361- elif h < (2.0 / 3.0 ):
362- self .r = int ((p + ((q - p ) * 6 * (2.0 / 3.0 - h ))) * 255 )
363- else :
364- self .r = int (p * 255 )
365-
366- # g
367- h = ht
368- if h < 0 :
369- h += 1
370- elif h > 1 :
371- h -= 1
372-
373- if h < (1.0 / 6.0 ):
374- self .g = int ((p + ((q - p ) * 6 * h )) * 255 )
375- elif h < 0.5 :
376- self .g = int (q * 255 )
377- elif h < (2.0 / 3.0 ):
378- self .g = int ((p + ((q - p ) * 6 * (2.0 / 3.0 - h ))) * 255 )
379- else :
380- self .g = int (p * 255 )
381-
382- # b
383- h = ht - (1.0 / 3.0 )
384- if h < 0 :
385- h += 1
386- elif h > 1 :
387- h -= 1
388-
389- if h < (1.0 / 6.0 ):
390- self .b = int ((p + ((q - p ) * 6 * h )) * 255 )
391- elif h < 0.5 :
392- self .b = int (q * 255 )
393- elif h < (2.0 / 3.0 ):
394- self .b = int ((p + ((q - p ) * 6 * (2.0 / 3.0 - h ))) * 255 )
395- else :
396- self .b = int (p * 255 )
373+ self .r , self .g , self .b = vals
397374
398375 @property
399376 def i1i2i3 (self ):
@@ -410,15 +387,15 @@ def i1i2i3(self):
410387 values for the given color.
411388
412389 """
413- rn = self .r / 255.0
414- gn = self .g / 255.0
415- bn = self .b / 255.0
390+ rn = self ._r / 255.0
391+ gn = self ._g / 255.0
392+ bn = self ._b / 255.0
416393
417- i1 = ( rn + gn + bn ) / 3.0
418- i2 = ( rn - bn ) / 2.0
419- i3 = ( 2 * gn - rn - bn ) / 4.0
394+ i1 = _clip (( rn + gn + bn ) / 3.0 , 0.0 , 1.0 )
395+ i2 = _clip (( rn - bn ) / 2.0 , - 0.5 , 0.5 )
396+ i3 = _clip (( 2 * gn - rn - bn ) / 4.0 , - 0.5 , 0.5 )
420397
421- return (i1 , i2 , i3 )
398+ return (i1 , i2 , i3 )
422399
423400 @i1i2i3 .setter
424401 def i1i2i3 (self , value ):
@@ -434,9 +411,9 @@ def i1i2i3(self, value):
434411 ar = 2 * i2 + ab
435412 ag = 3 * i1 - ar - ab
436413
437- self .r = int (ar * 255 )
438- self .g = int (ag * 255 )
439- self .b = int (ab * 255 )
414+ self .r = _clip (ar * 255 , 0.0 , 255.0 )
415+ self .g = _clip (ag * 255 , 0.0 , 255.0 )
416+ self .b = _clip (ab * 255 , 0.0 , 255.0 )
440417
441418 @property
442419 def cmy (self ):
@@ -447,18 +424,18 @@ def cmy(self):
447424 All three values are represented as floats between 0.0 and 1.0.
448425
449426 """
450- return (1.0 - self .r / 255.0 ,
451- 1.0 - self .g / 255.0 ,
452- 1.0 - self .b / 255.0 )
427+ return (1.0 - self ._r / 255.0 ,
428+ 1.0 - self ._g / 255.0 ,
429+ 1.0 - self ._b / 255.0 )
453430
454431 @cmy .setter
455432 def cmy (self , value ):
456433 c , m , y = value
457434 if (c < 0 or c > 1 ) or (m < 0 or m > 1 ) or (y < 0 or y > 1 ):
458435 raise ValueError ("invalid CMY value" )
459- self .r = int (( 1.0 - c ) * 255 )
460- self .g = int (( 1.0 - m ) * 255 )
461- self .b = int (( 1.0 - y ) * 255 )
436+ self ._r = ( 1.0 - c ) * 255
437+ self ._g = ( 1.0 - m ) * 255
438+ self ._b = ( 1.0 - y ) * 255
462439
463440 def normalize (self ):
464441 """Returns the RGBA values as floats between 0 and 1.
0 commit comments