module ChunkyPNG::Color
The Color
module defines methods for handling colors. Within the ChunkyPNG
library, the concepts of pixels and colors are both used, and they are both represented by a Integer.
Pixels/colors are represented in RGBA components. Each of the four components is stored with a depth of 8 bits (maximum value = 255 = {ChunkyPNG::Color::MAX}). Together, these components are stored in a 4-byte Integer.
A color will always be represented using these 4 components in memory. When the image is encoded, a more suitable representation can be used (e.g. rgb, grayscale, palette-based), for which several conversion methods are provided in this module.
Constants
- BLACK
@return [Integer] Black pixel/color
- HEX3_COLOR_REGEXP
@private @return [Regexp] The regexp to parse 3-digit hex color values.
- HEX6_COLOR_REGEXP
@private @return [Regexp] The regexp to parse 6- and 8-digit hex color values.
- HTML_COLOR_REGEXP
@private @return [Regexp] The regexp to parse named color values.
- MAX
@return [Integer] The maximum value of each color component.
- MAX_EUCLIDEAN_DISTANCE_RGBA
Could be simplified as
MAX
* 2, but this format mirrors the math in {#euclidean_distance_rgba} @return [Float] The maximum Euclidean distance of two RGBA colors.- PREDEFINED_COLORS
@return [Hash<Symbol, Integer>] All the predefined color names in HTML.
- TRANSPARENT
@return [Integer] Fully transparent pixel/color
- WHITE
@return [Integer] White pixel/color
Public Instance Methods
Returns the alpha channel value for the color value.
@param [Integer] value The color value. @return [Integer] A value between 0 and MAX
.
# File lib/chunky_png/color.rb 298 def a(value) 299 value & 0x000000ff 300 end
Checks whether an alpha channel value can successfully be composed given the resulting color, the mask color and a background color, all of which should be opaque.
@param [Integer] color The color that was the result of compositing. @param [Integer] mask The opaque variant of the color that was being
composed
@param [Integer] bg The background color on which the color was composed. @param [Integer] tolerance The decomposition tolerance level, a value
between 0 and 255
@return [Boolean] True if the alpha component can be decomposed
successfully.
@see decompose_alpha
# File lib/chunky_png/color.rb 502 def alpha_decomposable?(color, mask, bg, tolerance = 1) 503 components = decompose_alpha_components(color, mask, bg) 504 sum = components.inject(0) { |a, b| a + b } 505 max = components.max * 3 506 components.max <= 255 && components.min >= 0 && (sum + tolerance * 3) >= max 507 end
Returns the blue-component from the color value.
@param [Integer] value The color value. @return [Integer] A value between 0 and MAX
.
# File lib/chunky_png/color.rb 290 def b(value) 291 (value & 0x0000ff00) >> 8 292 end
Blends the foreground and background color by taking the average of the components.
@param [Integer] fg The foreground color. @param [Integer] bg The foreground color. @return [Integer] The blended color.
# File lib/chunky_png/color.rb 405 def blend(fg, bg) 406 (fg + bg) >> 1 407 end
Composes two colors with an alpha channel using floating point math.
This method uses more precise floating point math, but this precision is lost when the result is converted back to an integer. Because it is slower than the version based on integer math, that version is preferred.
@param [Integer] fg The foreground color. @param [Integer] bg The background color. @return [Integer] The composited color. @see ChunkyPNG::Color#compose_quick
# File lib/chunky_png/color.rb 382 def compose_precise(fg, bg) 383 return fg if opaque?(fg) || fully_transparent?(bg) 384 return bg if fully_transparent?(fg) 385 386 fg_a = a(fg).to_f / MAX 387 bg_a = a(bg).to_f / MAX 388 a_com = (1.0 - fg_a) * bg_a 389 390 new_r = (fg_a * r(fg) + a_com * r(bg)).round 391 new_g = (fg_a * g(fg) + a_com * g(bg)).round 392 new_b = (fg_a * b(fg) + a_com * b(bg)).round 393 new_a = ((fg_a + a_com) * MAX).round 394 rgba(new_r, new_g, new_b, new_a) 395 end
Composes two colors with an alpha channel using integer math.
This version is faster than the version based on floating point math, so this compositing function is used by default.
@param [Integer] fg The foreground color. @param [Integer] bg The background color. @return [Integer] The composited color. @see ChunkyPNG::Color#compose_precise
# File lib/chunky_png/color.rb 360 def compose_quick(fg, bg) 361 return fg if opaque?(fg) || fully_transparent?(bg) 362 return bg if fully_transparent?(fg) 363 364 a_com = int8_mult(0xff - a(fg), a(bg)) 365 new_r = int8_mult(a(fg), r(fg)) + int8_mult(a_com, r(bg)) 366 new_g = int8_mult(a(fg), g(fg)) + int8_mult(a_com, g(bg)) 367 new_b = int8_mult(a(fg), b(fg)) + int8_mult(a_com, b(bg)) 368 new_a = a(fg) + a_com 369 rgba(new_r, new_g, new_b, new_a) 370 end
Decomposes the alpha channel value given the resulting color, the mask color and a background color, all of which should be opaque.
Make sure to call {#alpha_decomposable?} first to see if the alpha channel value can successfully decomposed with a given tolerance, otherwise the return value of this method is undefined.
@param [Integer] color The color that was the result of compositing. @param [Integer] mask The opaque variant of the color that was being
composed
@param [Integer] bg The background color on which the color was composed. @return [Integer] The best fitting alpha channel, a value between 0 and
255
@see alpha_decomposable?
# File lib/chunky_png/color.rb 523 def decompose_alpha(color, mask, bg) 524 components = decompose_alpha_components(color, mask, bg) 525 (components.inject(0) { |a, b| a + b } / 3.0).round 526 end
Decomposes an alpha channel for either the r, g or b color channel. @param [:r, :g, :b] channel The channel to decompose the alpha channel
from.
@param [Integer] color The color that was the result of compositing. @param [Integer] mask The opaque variant of the color that was being
composed
@param [Integer] bg The background color on which the color was composed. @return [Integer] The decomposed alpha value for the channel.
# File lib/chunky_png/color.rb 536 def decompose_alpha_component(channel, color, mask, bg) 537 cc, mc, bc = send(channel, color), send(channel, mask), send(channel, bg) 538 539 return 0x00 if bc == cc 540 return 0xff if bc == mc 541 return 0xff if cc == mc 542 543 (((bc - cc).to_f / (bc - mc).to_f) * MAX).round 544 end
Decomposes the alpha channels for the r, g and b color channel. @param [Integer] color The color that was the result of compositing. @param [Integer] mask The opaque variant of the color that was being
composed
@param [Integer] bg The background color on which the color was composed. @return [Array<Integer>] The decomposed alpha values for the r, g and b
channels.
# File lib/chunky_png/color.rb 553 def decompose_alpha_components(color, mask, bg) 554 [ 555 decompose_alpha_component(:r, color, mask, bg), 556 decompose_alpha_component(:g, color, mask, bg), 557 decompose_alpha_component(:b, color, mask, bg), 558 ] 559 end
Decomposes a color, given a color, a mask color and a background color. The returned color will be a variant of the mask color, with the alpha channel set to the best fitting value. This basically is the reverse operation if alpha composition.
If the color cannot be decomposed, this method will return the fully transparent variant of the mask color.
@param [Integer] color The color that was the result of compositing. @param [Integer] mask The opaque variant of the color that was being
composed
@param [Integer] bg The background color on which the color was composed. @param [Integer] tolerance The decomposition tolerance level, a value
between 0 and 255
@return [Integer] The decomposed color, a variant of the masked color
with the alpha channel set to an appropriate value.
# File lib/chunky_png/color.rb 481 def decompose_color(color, mask, bg, tolerance = 1) 482 if alpha_decomposable?(color, mask, bg, tolerance) 483 mask & 0xffffff00 | decompose_alpha(color, mask, bg) 484 else 485 mask & 0xffffff00 486 end 487 end
Compute the Euclidean distance between 2 colors in RGBA
This method simply takes the Euclidean distance between the RGBA channels of 2 colors, which gives us a measure of how different the two colors are.
Although it would be more perceptually accurate to calculate a proper Delta E in Lab colorspace, this method should serve many use-cases while avoiding the overhead of converting RGBA to Lab.
@param pixel_after [Integer] @param pixel_before [Integer] @return [Float]
# File lib/chunky_png/color.rb 721 def euclidean_distance_rgba(pixel_after, pixel_before) 722 return 0.0 if pixel_after == pixel_before 723 724 Math.sqrt( 725 (r(pixel_after) - r(pixel_before))**2 + 726 (g(pixel_after) - g(pixel_before))**2 + 727 (b(pixel_after) - b(pixel_before))**2 + 728 (a(pixel_after) - a(pixel_before))**2 729 ) 730 end
Lowers the intensity of a color, by lowering its alpha by a given factor. @param [Integer] color The color to adjust. @param [Integer] factor Fade factor as an integer between 0 and 255. @return [Integer] The faded color.
# File lib/chunky_png/color.rb 460 def fade(color, factor) 461 new_alpha = int8_mult(a(color), factor) 462 (color & 0xffffff00) | new_alpha 463 end
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 “#”.
@param [String] hex_value The color in hex notation. @param [Integer] opacity The opacity value for the color. Overrides any
opacity value given in the hex value if given.
@return [Integer] The color value. @raise [ArgumentError] if the value given is not a hex color notation.
# File lib/chunky_png/color.rb 165 def from_hex(hex_value, opacity = nil) 166 base_color = case hex_value 167 when HEX3_COLOR_REGEXP 168 $1.gsub(/([0-9a-f])/i, '\1\1').hex << 8 169 when HEX6_COLOR_REGEXP 170 $1.hex << 8 171 else 172 raise ArgumentError, "Not a valid hex color notation: #{hex_value.inspect}!" 173 end 174 opacity ||= $2 ? $2.hex : 0xff 175 base_color | opacity 176 end
Creates a new color from an HSL triple.
This implementation follows the modern convention of 0 degrees hue indicating red.
@param [Fixnum] hue The hue component (0-360) @param [Fixnum] saturation The saturation component (0-1) @param [Fixnum] lightness The lightness component (0-1) @param [Fixnum] alpha Defaults to opaque (255). @return [Integer] The newly constructed color value. @raise [ArgumentError] if the hsl triple is invalid. @see en.wikipedia.org/wiki/HSL_and_HSV
# File lib/chunky_png/color.rb 218 def from_hsl(hue, saturation, lightness, alpha = 255) 219 raise ArgumentError, "Hue #{hue} was not between 0 and 360" unless (0..360).cover?(hue) 220 raise ArgumentError, "Saturation #{saturation} was not between 0 and 1" unless (0..1).cover?(saturation) 221 raise ArgumentError, "Lightness #{lightness} was not between 0 and 1" unless (0..1).cover?(lightness) 222 223 chroma = (1 - (2 * lightness - 1).abs) * saturation 224 rgb = cylindrical_to_cubic(hue, saturation, lightness, chroma) 225 rgb.map! { |component| ((component + lightness - 0.5 * chroma) * 255).to_i } 226 rgb << alpha 227 rgba(*rgb) 228 end
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.
@param [Fixnum] hue The hue component (0-360) @param [Fixnum] saturation The saturation component (0-1) @param [Fixnum] value The value (brightness) component (0-1) @param [Fixnum] alpha Defaults to opaque (255). @return [Integer] The newly constructed color value. @raise [ArgumentError] if the hsv triple is invalid. @see en.wikipedia.org/wiki/HSL_and_HSV
# File lib/chunky_png/color.rb 192 def from_hsv(hue, saturation, value, alpha = 255) 193 raise ArgumentError, "Hue must be between 0 and 360" unless (0..360).cover?(hue) 194 raise ArgumentError, "Saturation must be between 0 and 1" unless (0..1).cover?(saturation) 195 raise ArgumentError, "Value/brightness must be between 0 and 1" unless (0..1).cover?(value) 196 197 chroma = value * saturation 198 rgb = cylindrical_to_cubic(hue, saturation, value, chroma) 199 rgb.map! { |component| ((component + value - chroma) * 255).to_i } 200 rgb << alpha 201 rgba(*rgb) 202 end
Creates a color by unpacking an rgb triple from a string.
@param [String] stream The string to load the color from. It should be
at least 3 + pos bytes long.
@param [Integer] pos The position in the string to load the triple from. @return [Integer] The newly constructed color value.
# File lib/chunky_png/color.rb 140 def from_rgb_stream(stream, pos = 0) 141 rgb(*stream.unpack("@#{pos}C3")) 142 end
Creates a color by unpacking an rgba triple from a string
@param [String] stream The string to load the color from. It should be
at least 4 + pos bytes long.
@param [Integer] pos The position in the string to load the triple from. @return [Integer] The newly constructed color value.
# File lib/chunky_png/color.rb 150 def from_rgba_stream(stream, pos = 0) 151 rgba(*stream.unpack("@#{pos}C4")) 152 end
Returns true if this color is fully transparent.
@param [Integer] value The color to test. @return [true, false] True if the alpha channel equals 0.
# File lib/chunky_png/color.rb 329 def fully_transparent?(value) 330 a(value) == 0x00000000 331 end
Returns the green-component from the color value.
@param [Integer] value The color value. @return [Integer] A value between 0 and MAX
.
# File lib/chunky_png/color.rb 282 def g(value) 283 (value & 0x00ff0000) >> 16 284 end
Creates a new color using a grayscale teint. @param [Integer] teint The grayscale teint (0-255), will be used as r, g,
and b value.
@return [Integer] The newly constructed color value.
# File lib/chunky_png/color.rb 117 def grayscale(teint) 118 teint << 24 | teint << 16 | teint << 8 | 0xff 119 end
Returns true if this color is fully transparent.
@param [Integer] value The color to test. @return [true, false] True if the r, g and b component are equal.
# File lib/chunky_png/color.rb 321 def grayscale?(value) 322 r(value) == b(value) && b(value) == g(value) 323 end
Creates a new color using a grayscale teint and alpha value. @param [Integer] teint The grayscale teint (0-255), will be used as r, g,
and b value.
@param [Integer] a The opacity (0-255) @return [Integer] The newly constructed color value.
# File lib/chunky_png/color.rb 126 def grayscale_alpha(teint, a) 127 teint << 24 | teint << 16 | teint << 8 | a 128 end
Calculates the grayscale teint of an RGB color.
@param [Integer] color The color to convert. @return [Integer] The grayscale teint of the input color, 0-255.
# File lib/chunky_png/color.rb 437 def grayscale_teint(color) 438 (r(color) * 0.3 + g(color) * 0.59 + b(color) * 0.11).round 439 end
Gets a color value based on a HTML color name.
The color name is flexible. E.g. 'yellowgreen'
, 'Yellow green'
, 'YellowGreen'
, 'YELLOW_GREEN'
and :yellow_green
will all return the same color value.
You can include a opacity level in the color name (e.g. 'red @ 0.5'
) or give an explicit opacity value as second argument. If no opacity value is given, the color will be fully opaque.
@param [Symbol, String] color_name The color name. It may include an
opacity specifier like <tt>@ 0.8</tt> to set the color's opacity.
@param [Integer] opacity The opacity value for the color between 0 and
255. Overrides any opacity value given in the color name.
@return [Integer] The color value. @raise [ChunkyPNG::Exception] If the color name was not recognized.
# File lib/chunky_png/color.rb 908 def html_color(color_name, opacity = nil) 909 if color_name.to_s =~ HTML_COLOR_REGEXP 910 opacity ||= $2 ? ($2.to_f * 255.0).round : 0xff 911 base_color_name = $1.gsub(/[^a-z]+/i, "").downcase.to_sym 912 return PREDEFINED_COLORS[base_color_name] | opacity if PREDEFINED_COLORS.key?(base_color_name) 913 end 914 raise ArgumentError, "Unknown color name #{color_name}!" 915 end
Multiplies two fractions using integer math, where the fractions are stored using an integer between 0 and 255. This method is used as a helper method for compositing colors using integer math.
This is a quicker implementation of ((a * b) / 255.0).round.
@param [Integer] a The first fraction. @param [Integer] b The second fraction. @return [Integer] The result of the multiplication.
# File lib/chunky_png/color.rb 346 def int8_mult(a, b) 347 t = a * b + 0x80 348 ((t >> 8) + t) >> 8 349 end
Interpolates the foreground and background colors by the given alpha value. This also blends the alpha channels themselves.
A blending factor of 255 will give entirely the foreground, while a blending factor of 0 will give the background.
@param [Integer] fg The foreground color. @param [Integer] bg The background color. @param [Integer] alpha The blending factor (fixed 8bit) @return [Integer] The interpolated color.
# File lib/chunky_png/color.rb 419 def interpolate_quick(fg, bg, alpha) 420 return fg if alpha >= 255 421 return bg if alpha <= 0 422 423 alpha_com = 255 - alpha 424 425 new_r = int8_mult(alpha, r(fg)) + int8_mult(alpha_com, r(bg)) 426 new_g = int8_mult(alpha, g(fg)) + int8_mult(alpha_com, g(bg)) 427 new_b = int8_mult(alpha, b(fg)) + int8_mult(alpha_com, b(bg)) 428 new_a = int8_mult(alpha, a(fg)) + int8_mult(alpha_com, a(bg)) 429 430 rgba(new_r, new_g, new_b, new_a) 431 end
Returns the opaque value of this color by removing the alpha channel. @param [Integer] value The color to transform. @return [Integer] The opaque color
# File lib/chunky_png/color.rb 313 def opaque!(value) 314 value | 0x000000ff 315 end
Returns true if this color is fully opaque.
@param [Integer] value The color to test. @return [true, false] True if the alpha channel equals MAX
.
# File lib/chunky_png/color.rb 306 def opaque?(value) 307 a(value) == 0x000000ff 308 end
Parses a color value given a numeric or string argument.
It supports color numbers, colors in hex notation and named HTML colors.
@param [Integer, String] source The color value. @return [Integer] The color value, with the opacity applied if one was
given.
# File lib/chunky_png/color.rb 84 def parse(source) 85 return source if source.is_a?(Integer) 86 case source.to_s 87 when /^\d+$/ then source.to_s.to_i 88 when HEX3_COLOR_REGEXP, HEX6_COLOR_REGEXP then from_hex(source.to_s) 89 when HTML_COLOR_REGEXP then html_color(source.to_s) 90 else raise ArgumentError, "Don't know how to create a color from #{source.inspect}!" 91 end 92 end
Returns the number of bytes used for an image pass @param [Integer] color_mode The color mode in which the pixels are
stored.
@param [Integer] depth The color depth of the pixels. @param [Integer] width The width of the image pass. @param [Integer] height The height of the image pass. @return [Integer] The number of bytes used per scanline in a datastream.
# File lib/chunky_png/color.rb 983 def pass_bytesize(color_mode, depth, width, height) 984 return 0 if width == 0 || height == 0 985 (scanline_bytesize(color_mode, depth, width) + 1) * height 986 end
Returns the size in bits of a pixel when it is stored using a given color mode.
@param [Integer] color_mode The color mode in which the pixels are
stored.
@param [Integer] depth The color depth of the pixels. @return [Integer] The number of bytes used per pixel in a datastream.
# File lib/chunky_png/color.rb 962 def pixel_bitsize(color_mode, depth = 8) 963 samples_per_pixel(color_mode) * depth 964 end
Returns the size in bytes of a pixel when it is stored using a given color mode.
@param [Integer] color_mode The color mode in which the pixels are
stored.
@return [Integer] The number of bytes used per pixel in a datastream.
# File lib/chunky_png/color.rb 950 def pixel_bytesize(color_mode, depth = 8) 951 return 1 if depth < 8 952 (pixel_bitsize(color_mode, depth) + 7) >> 3 953 end
Returns the red-component from the color value.
@param [Integer] value The color value. @return [Integer] A value between 0 and MAX
.
# File lib/chunky_png/color.rb 274 def r(value) 275 (value & 0xff000000) >> 24 276 end
Creates a new color using an r, g, b triple. @param [Integer] r The r-component (0-255) @param [Integer] g The g-component (0-255) @param [Integer] b The b-component (0-255) @return [Integer] The newly constructed color value.
# File lib/chunky_png/color.rb 109 def rgb(r, g, b) 110 r << 24 | g << 16 | b << 8 | 0xff 111 end
Creates a new color using an r, g, b triple and an alpha value. @param [Integer] r The r-component (0-255) @param [Integer] g The g-component (0-255) @param [Integer] b The b-component (0-255) @param [Integer] a The opacity (0-255) @return [Integer] The newly constructed color value.
# File lib/chunky_png/color.rb 100 def rgba(r, g, b, a) 101 r << 24 | g << 16 | b << 8 | a 102 end
Returns the number of sample values per pixel. @param [Integer] color_mode The color mode being used. @return [Integer] The number of sample values per pixel.
# File lib/chunky_png/color.rb 933 def samples_per_pixel(color_mode) 934 case color_mode 935 when ChunkyPNG::COLOR_INDEXED then 1 936 when ChunkyPNG::COLOR_TRUECOLOR then 3 937 when ChunkyPNG::COLOR_TRUECOLOR_ALPHA then 4 938 when ChunkyPNG::COLOR_GRAYSCALE then 1 939 when ChunkyPNG::COLOR_GRAYSCALE_ALPHA then 2 940 else raise ChunkyPNG::NotSupported, "Don't know the number of samples for this colormode: #{color_mode}!" 941 end 942 end
Returns the number of bytes used per scanline. @param [Integer] color_mode The color mode in which the pixels are
stored.
@param [Integer] depth The color depth of the pixels. @param [Integer] width The number of pixels per scanline. @return [Integer] The number of bytes used per scanline in a datastream.
# File lib/chunky_png/color.rb 972 def scanline_bytesize(color_mode, depth, width) 973 ((pixel_bitsize(color_mode, depth) * width) + 7) >> 3 974 end
Converts a color to a fiting grayscale value. It will conserve the alpha channel.
This method will return a full color value, with the R, G, and B value set to the grayscale teint calcuated from the input color's R, G and B values.
@param [Integer] color The color to convert. @return [Integer] The input color, converted to the best fitting
grayscale.
@see grayscale_teint
# File lib/chunky_png/color.rb 452 def to_grayscale(color) 453 grayscale_alpha(grayscale_teint(color), a(color)) 454 end
Returns an array with the grayscale teint and alpha channel values for this color.
This method expects the color to be grayscale, i.e. r, g, and b value to be equal and uses only the B channel. If you need to convert a color to grayscale first, see {#to_grayscale}.
@param [Integer] color The grayscale color to convert. @return [Array<Integer>] An array with 2 Integer elements. @see to_grayscale
# File lib/chunky_png/color.rb 700 def to_grayscale_alpha_bytes(color) 701 [b(color), a(color)] # assumption r == g == b 702 end
Returns an array with the grayscale teint value for this color.
This method expects the r, g, and b value to be equal, and the alpha channel will be discarded.
@param [Integer] color The grayscale color to convert. @return [Array<Integer>] An array with 1 Integer element.
# File lib/chunky_png/color.rb 686 def to_grayscale_bytes(color) 687 [b(color)] # assumption r == g == b 688 end
Returns a string representing this color using hex notation (i.e. rrggbbaa).
@param [Integer] color The color to convert. @param [Boolean] include_alpha @return [String] The color in hex notation, starting with a pound sign.
# File lib/chunky_png/color.rb 571 def to_hex(color, include_alpha = true) 572 include_alpha ? ("#%08x" % color) : ("#%06x" % [color >> 8]) 573 end
Returns an array with the separate HSL components of a color.
Because ChunkyPNG
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.
@param [Integer] color The ChunkyPNG
color to convert. @param [Boolean] include_alpha Flag indicates whether a fourth element
representing alpha channel should be included in the returned array.
@return [Array
include_alpha=true (0-255)
@see en.wikipedia.org/wiki/HSL_and_HSV
# File lib/chunky_png/color.rb 623 def to_hsl(color, include_alpha = false) 624 hue, chroma, max, min = hue_and_chroma(color) 625 lightness = 0.5 * (max + min) 626 saturation = chroma.zero? ? 0.0 : chroma.fdiv(1 - (2 * lightness - 1).abs) 627 628 include_alpha ? [hue, saturation, lightness, a(color)] : 629 [hue, saturation, lightness] 630 end
Returns an array with the separate HSV components of a color.
Because ChunkyPNG
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.
@param [Integer] color The ChunkyPNG
color to convert. @param [Boolean] include_alpha Flag indicates whether a fourth element
representing alpha channel should be included in the returned array.
@return [Array] The hue of the color (0-360) @return [Array] The saturation of the color (0-1) @return [Array] The value of the color (0-1) @return [Array] Optional fourth element for alpha, included if
include_alpha=true (0-255)
@see en.wikipedia.org/wiki/HSL_and_HSV
# File lib/chunky_png/color.rb 594 def to_hsv(color, include_alpha = false) 595 hue, chroma, max, _ = hue_and_chroma(color) 596 value = max 597 saturation = chroma.zero? ? 0.0 : chroma.fdiv(value) 598 599 include_alpha ? [hue, saturation, value, a(color)] : 600 [hue, saturation, value] 601 end
Returns an array with the separate RGBA values for this color.
@param [Integer] color The color to convert. @return [Array<Integer>] An array with 4 Integer elements.
# File lib/chunky_png/color.rb 666 def to_truecolor_alpha_bytes(color) 667 [r(color), g(color), b(color), a(color)] 668 end
Returns an array with the separate RGB values for this color. The alpha channel will be discarded.
@param [Integer] color The color to convert. @return [Array<Integer>] An array with 3 Integer elements.
# File lib/chunky_png/color.rb 675 def to_truecolor_bytes(color) 676 [r(color), g(color), b(color)] 677 end
Private Instance Methods
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.
@param [Fixnum] hue The hue-component (0-360) @param [Fixnum] saturation The saturation-component (0-1) @param [Fixnum] y_component The y_component can represent either lightness
or brightness/value (0-1) depending on which scheme (HSV/HSL) is being used.
@param [Fixnum] chroma The associated chroma value. @return [Array<Fixnum>] A scaled r,g,b triple. Scheme-dependent
adjustments are still needed to reach the true r,g,b values.
@see en.wikipedia.org/wiki/HSL_and_HSV @see www.tomjewett.com/colors/hsb.html @private
# File lib/chunky_png/color.rb 251 def cylindrical_to_cubic(hue, saturation, y_component, chroma) 252 hue_prime = hue.fdiv(60) 253 x = chroma * (1 - (hue_prime % 2 - 1).abs) 254 255 case hue_prime 256 when (0...1) then [chroma, x, 0] 257 when (1...2) then [x, chroma, 0] 258 when (2...3) then [0, chroma, x] 259 when (3...4) then [0, x, chroma] 260 when (4...5) then [x, 0, chroma] 261 when (5..6) then [chroma, 0, x] 262 end 263 end
This method encapsulates the logic needed to extract hue and chroma from a ChunkPNG color. This logic is shared by the cylindrical HSV/HSB and HSL color space models.
@param [Integer] color A ChunkyPNG
color. @return [Fixnum] hue The hue of the color (0-360) @return [Fixnum] chroma The chroma of the color (0-1) @return [Fixnum] max The magnitude of the largest scaled rgb component (0-1) @return [Fixnum] min The magnitude of the smallest scaled rgb component (0-1) @private
# File lib/chunky_png/color.rb 642 def hue_and_chroma(color) 643 scaled_rgb = to_truecolor_bytes(color) 644 scaled_rgb.map! { |component| component.fdiv(255) } 645 min, max = scaled_rgb.minmax 646 chroma = max - min 647 648 r, g, b = scaled_rgb 649 hue_prime = chroma.zero? ? 0 : case max 650 when r then (g - b).fdiv(chroma) 651 when g then (b - r).fdiv(chroma) + 2 652 when b then (r - g).fdiv(chroma) + 4 653 else 0 654 end 655 hue = 60 * hue_prime 656 657 [hue.round, chroma, max, min] 658 end