color.py (7187B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\chroma\color.py 2 from __future__ import division 3 import collections 4 import mathext 5 from chroma.conversion import hsb_to_rgb, rgb_to_hsb 6 7 class Color(collections.Sequence): 8 9 def __init__(self, r, g, b, a = 1.0): 10 for arg in (r, 11 g, 12 b, 13 a): 14 if not isinstance(arg, (float, int)): 15 raise TypeError('Color components should be floats, not {}'.format(type(arg).__name__)) 16 17 self._r = float(r) 18 self._g = float(g) 19 self._b = float(b) 20 self._a = float(a) 21 22 @classmethod 23 def from_rgba(cls, r, g, b, a = 1.0): 24 return cls(r, g, b, a) 25 26 @classmethod 27 def from_rgba_8bit(cls, r, g, b, a = 255): 28 return cls(mathext.clamp(r, 0, 255) / 255, mathext.clamp(g, 0, 255) / 255, mathext.clamp(b, 0, 255) / 255, mathext.clamp(a, 0, 255) / 255) 29 30 @classmethod 31 def from_hex(cls, value): 32 if not isinstance(value, (str, unicode)): 33 raise ValueError('Hex color value must be a string') 34 if value.startswith('#'): 35 value = value[1:] 36 if len(value) == 8: 37 a = int(value[0:2], 16) / 255 38 r = int(value[2:4], 16) / 255 39 g = int(value[4:6], 16) / 255 40 b = int(value[6:8], 16) / 255 41 elif len(value) == 6: 42 a = 1.0 43 r = int(value[0:2], 16) / 255 44 g = int(value[2:4], 16) / 255 45 b = int(value[4:6], 16) / 255 46 elif len(value) == 3: 47 a = 1.0 48 r = int(value[0] * 2, 16) / 255 49 g = int(value[1] * 2, 16) / 255 50 b = int(value[2] * 2, 16) / 255 51 else: 52 raise ValueError('Invalid hex color value provided: {!r}'.format(value)) 53 return cls(r, g, b, a) 54 55 @classmethod 56 def from_hsb(cls, h, s, b, a = 1.0): 57 red, green, blue = hsb_to_rgb(h, s, b) 58 return cls(red, green, blue, a) 59 60 @classmethod 61 def from_any(cls, value): 62 if isinstance(value, Color) or hasattr(value, 'rgba'): 63 return cls(*value.rgba) 64 if isinstance(value, (str, unicode)): 65 return cls.from_hex(value) 66 try: 67 return cls(*value) 68 except Exception: 69 pass 70 71 raise ValueError('Unknown color value: {!r}'.format(value)) 72 73 @property 74 def r(self): 75 return self._r 76 77 @property 78 def red(self): 79 return self._r 80 81 @property 82 def g(self): 83 return self._g 84 85 @property 86 def green(self): 87 return self._g 88 89 @property 90 def b(self): 91 return self._b 92 93 @property 94 def blue(self): 95 return self._b 96 97 @property 98 def a(self): 99 return self._a 100 101 @property 102 def alpha(self): 103 return self._a 104 105 @property 106 def opacity(self): 107 return self._a 108 109 @property 110 def rgb(self): 111 return (self._r, self._g, self._b) 112 113 @property 114 def rgb_8bit(self): 115 return (int(round(self._r * 255.0)), int(round(self._g * 255.0)), int(round(self._b * 255.0))) 116 117 @property 118 def rgba(self): 119 return (self._r, 120 self._g, 121 self._b, 122 self._a) 123 124 @property 125 def rgba_8bit(self): 126 return (int(round(self._r * 255.0)), 127 int(round(self._g * 255.0)), 128 int(round(self._b * 255.0)), 129 int(round(self._a * 255.0))) 130 131 @property 132 def hex_argb(self): 133 r, g, b, a = self.rgba_8bit 134 return u'#{:02x}{:02x}{:02x}{:02x}'.format(a, r, g, b) 135 136 @property 137 def hex_rgb(self): 138 return u'#{:02x}{:02x}{:02x}'.format(*self.rgb_8bit) 139 140 @property 141 def hsb(self): 142 return rgb_to_hsb(self._r, self._g, self._b) 143 144 @property 145 def hue(self): 146 hue, _, _ = rgb_to_hsb(self._r, self._g, self._b) 147 return hue 148 149 @property 150 def saturation(self): 151 _, saturation, _ = rgb_to_hsb(self._r, self._g, self._b) 152 return saturation 153 154 @property 155 def brightness(self): 156 _, _, brightness = rgb_to_hsb(self._r, self._g, self._b) 157 return brightness 158 159 @property 160 def luminance(self): 161 return 0.2126 * self.r + 0.7152 * self.g + 0.0722 * self.b 162 163 @property 164 def is_grayscale(self): 165 _, saturation, _ = self.hsb 166 return mathext.is_almost_zero(saturation) 167 168 def as_argb_int32(self): 169 return int(round(mathext.clamp(self._a, 0.0, 1.0) * 255)) << 24 | int(round(mathext.clamp(self._r, 0.0, 1.0) * 255)) << 16 | int(round(mathext.clamp(self._g, 0.0, 1.0) * 255)) << 8 | int(round(mathext.clamp(self._b, 0.0, 1.0) * 255)) 170 171 def with_red(self, red): 172 return Color(red, self._g, self._b, self._a) 173 174 def with_green(self, green): 175 return Color(self._r, green, self._b, self._a) 176 177 def with_blue(self, blue): 178 return Color(self._r, self._g, blue, self._a) 179 180 def with_alpha(self, alpha): 181 return Color(self._r, self._g, self._b, alpha) 182 183 def with_hue(self, hue): 184 hue = mathext.clamp(hue, 0.0, 1.0) 185 _, saturation, brightness = rgb_to_hsb(self._r, self._g, self._b) 186 r, g, b = hsb_to_rgb(hue, saturation, brightness) 187 return Color(r, g, b, a=self._a) 188 189 def with_saturation(self, saturation): 190 saturation = mathext.clamp(saturation, 0.0, 1.0) 191 hue, _, brightness = rgb_to_hsb(self._r, self._g, self._b) 192 r, g, b = hsb_to_rgb(hue, saturation, brightness) 193 return Color(r, g, b, a=self._a) 194 195 def with_brightness(self, brightness): 196 brightness = mathext.clamp(brightness, 0.0, 1.0) 197 hue, saturation, _ = rgb_to_hsb(self._r, self._g, self._b) 198 r, g, b = hsb_to_rgb(hue, saturation, brightness) 199 return Color(r, g, b, a=self._a) 200 201 def with_capped_brightness(self, min_brightness = 0.0, max_brightness = 1.0): 202 if self.brightness < min_brightness: 203 return Color.from_hsb(self.hue, self.brightness, min_brightness) 204 elif self.brightness > max_brightness: 205 return Color.from_hsb(self.hue, self.brightness, max_brightness) 206 else: 207 return self 208 209 def __eq__(self, other): 210 if not isinstance(other, Color): 211 try: 212 other = Color.from_any(other) 213 except ValueError: 214 return NotImplemented 215 216 return mathext.is_close(self._r, other.r) and mathext.is_close(self._g, other.g) and mathext.is_close(self._b, other.b) and mathext.is_close(self._a, other.a) 217 218 def __ne__(self, other): 219 if not isinstance(other, Color): 220 try: 221 other = Color.from_any(other) 222 except ValueError: 223 return NotImplemented 224 225 return not mathext.is_close(self._r, other.r) or not mathext.is_close(self._g, other.g) or not mathext.is_close(self._b, other.b) or not mathext.is_close(self._a, other.a) 226 227 def __hash__(self): 228 return hash(self.rgba) 229 230 def __str__(self): 231 return self.hex_argb 232 233 def __repr__(self): 234 return u'Color(r={!r}, g={!r}, b={!r}, a={!r})'.format(*self.rgba) 235 236 def __getitem__(self, item): 237 return self.rgba[item] 238 239 def __len__(self): 240 return 4