Class: Pixelart::Color
- Inherits:
-
Object
- Object
- Pixelart::Color
- Defined in:
- lib/pixelart/colors/color.rb,
lib/pixelart/colors/format.rb
Overview
todo/check - change class to module Color - why? why not?
Constant Summary collapse
- TRANSPARENT =
rgba( 0, 0, 0, 0)
0
- BLACK =
rgba( 0, 0, 0, 255)
0xff
- WHITE =
rgba(255, 255, 255, 255)
0xffffffff
- HEX3_COLOR_REGEXP =
The regexp to parse 3-digit hex color values.
/\A(?:#|0x)?([0-9a-f]{3})\z/i
- HEX6_COLOR_REGEXP =
The regexp to parse 6- and 8-digit hex color values.
/\A(?:#|0x)?([0-9a-f]{6})([0-9a-f]{2})?\z/i
- NAMES =
build_names
Class Method Summary collapse
-
.a(value) ⇒ Integer
Returns the alpha channel value for the color value.
-
.b(value) ⇒ Integer
Returns the blue-component from the color value.
-
.build_names ⇒ Object
known built-in color names.
-
.cylindrical_to_cubic(hue, saturation, y_component, chroma) ⇒ Array<Fixnum>
Convert one HSL or HSV triple and associated chroma to a scaled rgb triple.
- .format(color) ⇒ Object (also: fmt)
- .from_hex(hex_str) ⇒ Object
-
.from_hsl(hue, saturation, lightness, alpha = 255) ⇒ Integer
Creates a new color from an HSL triple.
-
.from_hsv(hue, saturation, value, alpha = 255) ⇒ Integer
Creates a new color from an HSV triple.
-
.g(value) ⇒ Integer
Returns the green-component from the color value.
-
.grayscale?(value) ⇒ true, false
Returns true if this color is fully transparent.
-
.hue_and_chroma(color) ⇒ Fixnum
This method encapsulates the logic needed to extract hue and chroma from a color.
-
.opaque?(value) ⇒ true, false
Returns true if this color is fully opaque.
- .parse(color) ⇒ Object
-
.r(value) ⇒ Integer
Returns the red-component from the color value.
-
.rgb(r, g, b) ⇒ Integer
Creates a new color using an r, g, b triple.
-
.rgba(r, g, b, a) ⇒ Integer
Creates a new color using an r, g, b triple and an alpha value.
-
.to_hex(color, include_alpha: true) ⇒ String
Returns a string representing this color using hex notation (i.e. #rrggbbaa).
-
.to_hsl(color, include_alpha: true) ⇒ Array<Fixnum>[0], ...
Note: Because this code internally handles colors as Integers for performance reasons, some rounding occurs when importing or exporting HSL colors whose coordinates are float-based.
-
.to_hsv(color, include_alpha: true) ⇒ Array[0], ...
Returns an array with the separate HSV components of a color.
-
.transparent?(value) ⇒ true, false
Returns true if this color is fully transparent.
Class Method Details
.a(value) ⇒ Integer
Returns the alpha channel value for the color value.
77 78 79 |
# File 'lib/pixelart/colors/color.rb', line 77 def self.a(value) value & 0x000000ff end |
.b(value) ⇒ Integer
Returns the blue-component from the color value.
69 70 71 |
# File 'lib/pixelart/colors/color.rb', line 69 def self.b(value) (value & 0x0000ff00) >> 8 end |
.build_names ⇒ Object
known built-in color names
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# File 'lib/pixelart/colors/format.rb', line 7 def self.build_names names = { '#00000000' => 'TRANSPARENT', '#000000ff' => 'BLACK', '#ffffffff' => 'WHITE', } ## auto-add grayscale 1 to 254 (1..254).each do |n| hex = "#" + ('%02x' % n)*3 hex << "ff" ## add alpha channel (255) names[ hex ] = "8-BIT GRAYSCALE ##{n}" end names end |
.cylindrical_to_cubic(hue, saturation, y_component, chroma) ⇒ Array<Fixnum>
Convert one HSL or HSV triple and associated chroma to a scaled rgb triple
This method encapsulates the shared mathematical operations needed to convert coordinates from a cylindrical colorspace such as HSL or HSV into coordinates of the RGB colorspace.
Even though chroma values are derived from the other three coordinates, the formula for calculating chroma differs for each colorspace. Since it is calculated differently for each colorspace, it must be passed in as a parameter.
218 219 220 221 222 223 224 225 226 227 228 229 230 |
# File 'lib/pixelart/colors/color.rb', line 218 def self.cylindrical_to_cubic(hue, saturation, y_component, chroma) hue_prime = hue.fdiv(60) x = chroma * (1 - (hue_prime % 2 - 1).abs) case hue_prime when (0...1) then [chroma, x, 0] when (1...2) then [x, chroma, 0] when (2...3) then [0, chroma, x] when (3...4) then [0, x, chroma] when (4...5) then [x, 0, chroma] when (5..6) then [chroma, 0, x] end end |
.format(color) ⇒ Object Also known as: fmt
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/pixelart/colors/format.rb', line 28 def self.format( color ) rgb = [r(color), g(color), b(color)] # rgb in hex (string format) # note: do NOT include alpha channel for now - why? why not? hex = "#" + rgb.map{|num| '%02x' % num }.join hsl = to_hsl( color ) hsv = to_hsv( color ) buf = '' buf << hex buf << " / " buf << "rgb(" buf << "%3d " % rgb[0] buf << "%3d " % rgb[1] buf << "%3d)" % rgb[2] buf << " - " buf << "hsl(" buf << "%3d° " % (hsl[0] % 360) buf << "%3d%% " % (hsl[1]*100+0.5).to_i buf << "%3d%%)" % (hsl[2]*100+0.5).to_i buf << " - " buf << "hsv(" buf << "%3d° " % (hsv[0] % 360) buf << "%3d%% " % (hsv[1]*100+0.5).to_i buf << "%3d%%)" % (hsv[2]*100+0.5).to_i alpha = a(color) if alpha != 255 buf << " - α(%3d%%)" % (alpha*100/255+0.5).to_i else buf << " " ## add empty for 255 (full opacity) end ## note: add alpha channel to hex alpha_hex = '%02x' % alpha name = NAMES[ hex+alpha_hex ] buf << " - #{name}" if name buf end |
.from_hex(hex_str) ⇒ Object
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/pixelart/colors/color.rb', line 117 def self.from_hex( hex_str ) ## Creates a color by converting it from a string in hex notation. ## ## It supports colors with (#rrggbbaa) or without (#rrggbb) ## alpha channel as well as the 3-digit short format (#rgb) ## for those without. Color strings may include ## the prefix "0x" or "#"". base_color = case hex_str when HEX3_COLOR_REGEXP $1.gsub( /([0-9a-f])/i, '\1\1' ).hex << 8 when HEX6_COLOR_REGEXP $1.hex << 8 else raise ArgumentError, "Not a valid hex color notation: #{hex_str.inspect}!" end opacity = $2 ? $2.hex : 0xff base_color | opacity end |
.from_hsl(hue, saturation, lightness, alpha = 255) ⇒ Integer
Creates a new color from an HSL triple.
This implementation follows the modern convention of 0 degrees hue indicating red.
161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/pixelart/colors/color.rb', line 161 def self.from_hsl( hue, saturation, lightness, alpha=255) raise ArgumentError, "Hue #{hue} was not between 0 and 360" unless (0..360).cover?(hue) raise ArgumentError, "Saturation #{saturation} was not between 0 and 1" unless (0..1).cover?(saturation) raise ArgumentError, "Lightness #{lightness} was not between 0 and 1" unless (0..1).cover?(lightness) chroma = (1 - (2 * lightness - 1).abs) * saturation rgb = cylindrical_to_cubic(hue, saturation, lightness, chroma) rgb.map! { |component| ((component + lightness - 0.5 * chroma) * 255).to_i } rgb << alpha rgba(*rgb) end |
.from_hsv(hue, saturation, value, alpha = 255) ⇒ Integer
Creates a new color from an HSV triple.
Create a new color using an HSV (sometimes also called HSB) triple. The words ‘value` and `brightness` are used interchangeably and synonymously in descriptions of this colorspace. This implementation follows the modern convention of 0 degrees hue indicating red.
187 188 189 190 191 192 193 194 195 196 197 |
# File 'lib/pixelart/colors/color.rb', line 187 def self.from_hsv(hue, saturation, value, alpha=255) raise ArgumentError, "Hue must be between 0 and 360" unless (0..360).cover?(hue) raise ArgumentError, "Saturation must be between 0 and 1" unless (0..1).cover?(saturation) raise ArgumentError, "Value/brightness must be between 0 and 1" unless (0..1).cover?(value) chroma = value * saturation rgb = cylindrical_to_cubic(hue, saturation, value, chroma) rgb.map! { |component| ((component + value - chroma) * 255).to_i } rgb << alpha rgba(*rgb) end |
.g(value) ⇒ Integer
Returns the green-component from the color value.
61 62 63 |
# File 'lib/pixelart/colors/color.rb', line 61 def self.g(value) (value & 0x00ff0000) >> 16 end |
.grayscale?(value) ⇒ true, false
Returns true if this color is fully transparent.
96 97 98 |
# File 'lib/pixelart/colors/color.rb', line 96 def self.grayscale?(value) r(value) == b(value) && b(value) == g(value) end |
.hue_and_chroma(color) ⇒ Fixnum
This method encapsulates the logic needed to extract hue and chroma from a color. This logic is shared by the cylindrical HSV/HSB and HSL color space models.
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 |
# File 'lib/pixelart/colors/color.rb', line 301 def self.hue_and_chroma(color) scaled_rgb = [r(color), g(color), b(color)] scaled_rgb.map! { |component| component.fdiv(255) } min, max = scaled_rgb.minmax chroma = max - min r, g, b = scaled_rgb hue_prime = chroma.zero? ? 0 : case max when r then (g - b).fdiv(chroma) when g then (b - r).fdiv(chroma) + 2 when b then (r - g).fdiv(chroma) + 4 else 0 end hue = 60 * hue_prime [hue.round, chroma, max, min] end |
.opaque?(value) ⇒ true, false
Returns true if this color is fully opaque.
88 89 90 |
# File 'lib/pixelart/colors/color.rb', line 88 def self.opaque?(value) a(value) == 0x000000ff end |
.parse(color) ⇒ Object
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# File 'lib/pixelart/colors/color.rb', line 11 def self.parse( color ) if color.is_a?( Integer ) ## e.g. assumes rgba or such color ## pass through as is 1:1 elsif color.is_a?( Array ) ## assume array of hsl(a) e. g. [180, 0.86, 0.88] from_hsl( *color ) elsif color.is_a?( String ) if color.downcase == 'transparent' ## special case for builtin colors TRANSPARENT else ## note: return an Integer !!! (not a Color class or such!!! ) from_hex( color ) end else raise ArgumentError, "unknown color format; cannot parse - expected rgb hex string e.g. d3d3d3" end end |
.r(value) ⇒ Integer
Returns the red-component from the color value.
53 54 55 |
# File 'lib/pixelart/colors/color.rb', line 53 def self.r(value) (value & 0xff000000) >> 24 end |
.rgb(r, g, b) ⇒ Integer
Creates a new color using an r, g, b triple.
44 45 46 |
# File 'lib/pixelart/colors/color.rb', line 44 def self.rgb(r, g, b) r << 24 | g << 16 | b << 8 | 0xff end |
.rgba(r, g, b, a) ⇒ Integer
Creates a new color using an r, g, b triple and an alpha value.
35 36 37 |
# File 'lib/pixelart/colors/color.rb', line 35 def self.rgba(r, g, b, a) r << 24 | g << 16 | b << 8 | a end |
.to_hex(color, include_alpha: true) ⇒ String
Returns a string representing this color using hex notation (i.e. #rrggbbaa).
142 143 144 |
# File 'lib/pixelart/colors/color.rb', line 142 def self.to_hex( color, include_alpha: true ) include_alpha ? ("#%08x" % color) : ("#%06x" % [color >> 8]) end |
.to_hsl(color, include_alpha: true) ⇒ Array<Fixnum>[0], ...
Note: Because this code internally handles colors as Integers for performance reasons, some rounding occurs when importing or exporting HSL colors whose coordinates are float-based. Because of this rounding, #to_hsl and #from_hsl may not be perfect inverses.
This implementation follows the modern convention of 0 degrees hue indicating red.
252 253 254 255 256 257 258 259 |
# File 'lib/pixelart/colors/color.rb', line 252 def self.to_hsl(color, include_alpha: true ) hue, chroma, max, min = hue_and_chroma(color) lightness = 0.5 * (max + min) saturation = chroma.zero? ? 0.0 : chroma.fdiv(1 - (2 * lightness - 1).abs) include_alpha ? [hue, saturation, lightness, a(color)] : [hue, saturation, lightness] end |
.to_hsv(color, include_alpha: true) ⇒ Array[0], ...
Returns an array with the separate HSV components of a color.
Note: Because this code internally handles colors as Integers for performance reasons, some rounding occurs when importing or exporting HSV colors whose coordinates are float-based. Because of this rounding, #to_hsv and #from_hsv may not be perfect inverses.
This implementation follows the modern convention of 0 degrees hue indicating red.
280 281 282 283 284 285 286 287 |
# File 'lib/pixelart/colors/color.rb', line 280 def self.to_hsv(color, include_alpha: true ) hue, chroma, max, _ = hue_and_chroma(color) value = max saturation = chroma.zero? ? 0.0 : chroma.fdiv(value) include_alpha ? [hue, saturation, value, a(color)] : [hue, saturation, value] end |
.transparent?(value) ⇒ true, false
Returns true if this color is fully transparent.
104 105 106 |
# File 'lib/pixelart/colors/color.rb', line 104 def self.transparent?(value) a(value) == 0x00000000 end |