module ChunkyPNG::Color

def a(value)

Returns:
  • (Integer) - A value between 0 and MAX.

Parameters:
  • value (Integer) -- The color value.
def a(value)
  value & 0x000000ff
end

def alpha_decomposable?(color, mask, bg, tolerance = 1)

Other tags:
    See: #decompose_alpha -

Returns:
  • (Boolean) - True if the alpha component can be decomposed

Parameters:
  • tolerance (Integer) -- The decomposition tolerance level, a value
  • bg (Integer) -- The background color on which the color was composed.
  • mask (Integer) -- The opaque variant of the color that was being
  • color (Integer) -- The color that was the result of compositing.
def alpha_decomposable?(color, mask, bg, tolerance = 1)
  components = decompose_alpha_components(color, mask, bg)
  sum = components.inject(0) { |a,b| a + b }
  max = components.max * 3
  components.max <= 255 && components.min >= 0 && (sum + tolerance * 3) >= max
end

def b(value)

Returns:
  • (Integer) - A value between 0 and MAX.

Parameters:
  • value (Integer) -- The color value.
def b(value)
  (value & 0x0000ff00) >> 8
end

def blend(fg, bg)

Returns:
  • (Integer) - The blended color.

Parameters:
  • bg (Integer) -- The foreground color.
  • fg (Integer) -- The foreground color.
def blend(fg, bg)
  (fg + bg) >> 1
end

def compose_precise(fg, bg)

Other tags:
    See: ChunkyPNG::Color#compose_quick -

Returns:
  • (Integer) - The composited color.

Parameters:
  • bg (Integer) -- The background color.
  • fg (Integer) -- The foreground color.
def compose_precise(fg, bg)
  return fg if opaque?(fg) || fully_transparent?(bg)
  return bg if fully_transparent?(fg)
  fg_a  = a(fg).to_f / MAX
  bg_a  = a(bg).to_f / MAX
  a_com = (1.0 - fg_a) * bg_a
  new_r = (fg_a * r(fg) + a_com * r(bg)).round
  new_g = (fg_a * g(fg) + a_com * g(bg)).round
  new_b = (fg_a * b(fg) + a_com * b(bg)).round
  new_a = ((fg_a + a_com) * MAX).round
  rgba(new_r, new_g, new_b, new_a)
end

def compose_quick(fg, bg)

Other tags:
    See: ChunkyPNG::Color#compose_precise -

Returns:
  • (Integer) - The composited color.

Parameters:
  • bg (Integer) -- The background color.
  • fg (Integer) -- The foreground color.
def compose_quick(fg, bg)
  return fg if opaque?(fg) || fully_transparent?(bg)
  return bg if fully_transparent?(fg)
  a_com = int8_mult(0xff - a(fg), a(bg))
  new_r = int8_mult(a(fg), r(fg)) + int8_mult(a_com, r(bg))
  new_g = int8_mult(a(fg), g(fg)) + int8_mult(a_com, g(bg))
  new_b = int8_mult(a(fg), b(fg)) + int8_mult(a_com, b(bg))
  new_a = a(fg) + a_com
  rgba(new_r, new_g, new_b, new_a)
end

def cylindrical_to_cubic(hue, saturation, y_component, chroma)

Other tags:
    Private: -

Other tags:
    See: http://www.tomjewett.com/colors/hsb.html -
    See: http://en.wikipedia.org/wiki/HSL_and_HSV -

Returns:
  • (Array) - A scaled r,g,b triple. Scheme-dependent

Parameters:
  • chroma (Fixnum) -- The associated chroma value.
  • y_component (Fixnum) -- The y_component can represent either lightness
  • saturation (Fixnum) -- The saturation-component (0-1)
  • hue (Fixnum) -- The hue-component (0-360)
def 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); [chroma, x, 0]
  when (1...2); [x, chroma, 0]
  when (2...3); [0, chroma, x]
  when (3...4); [0, x, chroma]
  when (4...5); [x, 0, chroma]
  when (5..6);  [chroma, 0, x]
  end
end

def decompose_alpha(color, mask, bg)

Other tags:
    See: #alpha_decomposable? -

Returns:
  • (Integer) - The best fitting alpha channel, a value between 0 and

Parameters:
  • bg (Integer) -- The background color on which the color was composed.
  • mask (Integer) -- The opaque variant of the color that was being
  • color (Integer) -- The color that was the result of compositing.
def decompose_alpha(color, mask, bg)
  components = decompose_alpha_components(color, mask, bg)
  (components.inject(0) { |a,b| a + b } / 3.0).round
end

def decompose_alpha_component(channel, color, mask, bg)

Returns:
  • (Integer) - The decomposed alpha value for the channel.

Parameters:
  • bg (Integer) -- The background color on which the color was composed.
  • mask (Integer) -- The opaque variant of the color that was being
  • color (Integer) -- The color that was the result of compositing.
  • channel (:r, :g, :b) -- The channel to decompose the alpha channel
def decompose_alpha_component(channel, color, mask, bg)
  cc, mc, bc = send(channel, color), send(channel, mask), send(channel, bg)
  return 0x00 if bc == cc
  return 0xff if bc == mc
  return 0xff if cc == mc
  (((bc - cc).to_f / (bc - mc).to_f) * MAX).round
end

def decompose_alpha_components(color, mask, bg)

Returns:
  • (Array) - The decomposed alpha values for the r, g and b

Parameters:
  • bg (Integer) -- The background color on which the color was composed.
  • mask (Integer) -- The opaque variant of the color that was being
  • color (Integer) -- The color that was the result of compositing.
def decompose_alpha_components(color, mask, bg)
  [
    decompose_alpha_component(:r, color, mask, bg),
    decompose_alpha_component(:g, color, mask, bg),
    decompose_alpha_component(:b, color, mask, bg)
  ]
end

def decompose_color(color, mask, bg, tolerance = 1)

Returns:
  • (Integer) - The decomposed color, a variant of the masked color

Parameters:
  • tolerance (Integer) -- The decomposition tolerance level, a value
  • bg (Integer) -- The background color on which the color was composed.
  • mask (Integer) -- The opaque variant of the color that was being
  • color (Integer) -- The color that was the result of compositing.
def decompose_color(color, mask, bg, tolerance = 1)
  if alpha_decomposable?(color, mask, bg, tolerance)
    mask & 0xffffff00 | decompose_alpha(color, mask, bg)
  else
    mask & 0xffffff00
  end
end

def euclidean_distance_rgba(pixel_after, pixel_before)

Returns:
  • (Float) -

Parameters:
  • pixel_before (Integer) --
  • pixel_after (Integer) --
def euclidean_distance_rgba(pixel_after, pixel_before)
  Math.sqrt(
    (r(pixel_after) - r(pixel_before))**2 +
    (g(pixel_after) - g(pixel_before))**2 +
    (b(pixel_after) - b(pixel_before))**2 +
    (a(pixel_after) - a(pixel_before))**2
  )
end

def fade(color, factor)

Returns:
  • (Integer) - The faded color.

Parameters:
  • factor (Integer) -- Fade factor as an integer between 0 and 255.
  • color (Integer) -- The color to adjust.
def fade(color, factor)
  new_alpha = int8_mult(a(color), factor)
  (color & 0xffffff00) | new_alpha
end

def from_hex(hex_value, opacity = nil)

Raises:
  • (ArgumentError) - if the value given is not a hex color notation.

Returns:
  • (Integer) - The color value.

Parameters:
  • opacity (Integer) -- The opacity value for the color. Overrides any
  • str (String) -- The color in hex notation. @return [Integer] The
def from_hex(hex_value, opacity = nil)
  base_color = case hex_value
               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_value.inspect}!"
               end
  opacity  ||= $2 ? $2.hex : 0xff
  base_color | opacity
end

def from_hsl(hue, saturation, lightness, alpha = 255)

Other tags:
    See: http://en.wikipedia.org/wiki/HSL_and_HSV -

Raises:
  • (ArgumentError) - if the hsl triple is invalid.

Returns:
  • (Integer) - The newly constructed color value.

Parameters:
  • alpha (Fixnum) -- Defaults to opaque (255).
  • lightness (Fixnum) -- The lightness component (0-1)
  • saturation (Fixnum) -- The saturation component (0-1)
  • hue (Fixnum) -- The hue component (0-360)
def from_hsl(hue, saturation, lightness, alpha = 255)
  raise ArgumentError, "Hue #{hue} was not between 0 and 360" unless (0..360).include?(hue)
  raise ArgumentError, "Saturation #{saturation} was not between 0 and 1" unless (0..1).include?(saturation)
  raise ArgumentError, "Lightness #{lightness} was not between 0 and 1" unless (0..1).include?(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
  self.rgba(*rgb)
end

def from_hsv(hue, saturation, value, alpha = 255)

Other tags:
    See: http://en.wikipedia.org/wiki/HSL_and_HSV -

Raises:
  • (ArgumentError) - if the hsv triple is invalid.

Returns:
  • (Integer) - The newly constructed color value.

Parameters:
  • alpha (Fixnum) -- Defaults to opaque (255).
  • value (Fixnum) -- The value (brightness) component (0-1)
  • saturation (Fixnum) -- The saturation component (0-1)
  • hue (Fixnum) -- The hue component (0-360)
def from_hsv(hue, saturation, value, alpha = 255)
  raise ArgumentError, "Hue must be between 0 and 360" unless (0..360).include?(hue)
  raise ArgumentError, "Saturation must be between 0 and 1" unless (0..1).include?(saturation)
  raise ArgumentError, "Value/brightness must be between 0 and 1" unless (0..1).include?(value)
  chroma = value * saturation
  rgb    = cylindrical_to_cubic(hue, saturation, value, chroma)
  rgb.map! { |component| ((component + value - chroma) * 255).to_i }
  rgb << alpha
  self.rgba(*rgb)
end

def from_rgb_stream(stream, pos = 0)

Returns:
  • (Integer) - The newly constructed color value.

Parameters:
  • pos (Integer) -- The position in the string to load the triple from.
  • stream (String) -- The string to load the color from. It should be
def from_rgb_stream(stream, pos = 0)
  rgb(*stream.unpack("@#{pos}C3"))
end

def from_rgba_stream(stream, pos = 0)

Returns:
  • (Integer) - The newly constructed color value.

Parameters:
  • pos (Integer) -- The position in the string to load the triple from.
  • stream (String) -- The string to load the color from. It should be
def from_rgba_stream(stream, pos = 0)
  rgba(*stream.unpack("@#{pos}C4"))
end

def fully_transparent?(value)

Returns:
  • (true, false) - True if the alpha channel equals 0.

Parameters:
  • value (Integer) -- The color to test.
def fully_transparent?(value)
  a(value) == 0x00000000
end

def g(value)

Returns:
  • (Integer) - A value between 0 and MAX.

Parameters:
  • value (Integer) -- The color value.
def g(value)
  (value & 0x00ff0000) >> 16
end

def grayscale(teint)

Returns:
  • (Integer) - The newly constructed color value.

Parameters:
  • teint (Integer) -- The grayscale teint (0-255), will be used as r, g,
def grayscale(teint)
  teint << 24 | teint << 16 | teint << 8 | 0xff
end

def grayscale?(value)

Returns:
  • (true, false) - True if the r, g and b component are equal.

Parameters:
  • value (Integer) -- The color to test.
def grayscale?(value)
  r(value) == b(value) && b(value) == g(value)
end

def grayscale_alpha(teint, a)

Returns:
  • (Integer) - The newly constructed color value.

Parameters:
  • a (Integer) -- The opacity (0-255)
  • teint (Integer) -- The grayscale teint (0-255), will be used as r, g,
def grayscale_alpha(teint, a)
  teint << 24 | teint << 16 | teint << 8 | a
end

def grayscale_teint(color)

Returns:
  • (Integer) - The grayscale teint of the input color, 0-255.

Parameters:
  • color (Integer) -- The color to convert.
def grayscale_teint(color)
  (r(color) * 0.3 + g(color) * 0.59 + b(color) * 0.11).round
end

def html_color(color_name, opacity = nil)

Raises:
  • (ChunkyPNG::Exception) - If the color name was not recognized.

Returns:
  • (Integer) - The color value.

Parameters:
  • opacity (Integer) -- The opacity value for the color between 0 and
  • color_name (Symbol, String) -- The color name. It may include an
def html_color(color_name, opacity = nil)
  if color_name.to_s =~ HTML_COLOR_REGEXP
    opacity ||= $2 ? ($2.to_f * 255.0).round : 0xff
    base_color_name = $1.gsub(/[^a-z]+/i, '').downcase.to_sym
    return PREDEFINED_COLORS[base_color_name] | opacity if PREDEFINED_COLORS.has_key?(base_color_name)
  end
  raise ArgumentError, "Unknown color name #{color_name}!"
end

def hue_and_chroma(color)

Other tags:
    Private: -

Returns:
  • (Fixnum) - min The magnitude of the smallest scaled rgb component (0-1)
  • (Fixnum) - max The magnitude of the largest scaled rgb component (0-1)
  • (Fixnum) - chroma The chroma of the color (0-1)
  • (Fixnum) - hue The hue of the color (0-360)

Parameters:
  • A (Integer) -- ChunkyPNG color.
def hue_and_chroma(color)
  scaled_rgb      = to_truecolor_bytes(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; (g - b).fdiv(chroma)
                                 when g; (b - r).fdiv(chroma) + 2
                                 when b; (r - g).fdiv(chroma) + 4
                                 else 0
                                 end
  hue = 60 * hue_prime
  return hue.round, chroma, max, min
end

def int8_mult(a, b)

Returns:
  • (Integer) - The result of the multiplication.

Parameters:
  • b (Integer) -- The second fraction.
  • a (Integer) -- The first fraction.
def int8_mult(a, b)
  t = a * b + 0x80
  ((t >> 8) + t) >> 8
end

def interpolate_quick(fg, bg, alpha)

Parameters:
  • The (Integer) -- interpolated color.
  • alpha (Integer) -- The blending factor (fixed 8bit)
  • bg (Integer) -- The background color.
  • fg (Integer) -- The foreground color.
def interpolate_quick(fg, bg, alpha)
  return fg if alpha >= 255
  return bg if alpha <= 0
  alpha_com = 255 - alpha
  new_r = int8_mult(alpha, r(fg)) + int8_mult(alpha_com, r(bg))
  new_g = int8_mult(alpha, g(fg)) + int8_mult(alpha_com, g(bg))
  new_b = int8_mult(alpha, b(fg)) + int8_mult(alpha_com, b(bg))
  new_a = int8_mult(alpha, a(fg)) + int8_mult(alpha_com, a(bg))
  rgba(new_r, new_g, new_b, new_a)
end

def opaque!(value)

Returns:
  • (Integer) - The opaque color

Parameters:
  • value (Integer) -- The color to transform.
def opaque!(value)
  value | 0x000000ff
end

def opaque?(value)

Returns:
  • (true, false) - True if the alpha channel equals MAX.

Parameters:
  • value (Integer) -- The color to test.
def opaque?(value)
  a(value) == 0x000000ff
end

def parse(source)

Returns:
  • (Integer) - The color value, with the opacity applied if one was

Parameters:
  • The (Integer, String) -- color value.
def parse(source)
  return source if source.kind_of?(Integer)
  case source.to_s
    when /^\d+$/; source.to_s.to_i
    when HEX3_COLOR_REGEXP, HEX6_COLOR_REGEXP; from_hex(source.to_s)
    when HTML_COLOR_REGEXP; html_color(source.to_s)
    else raise ArgumentError, "Don't know how to create a color from #{source.inspect}!"
  end
end

def pass_bytesize(color_mode, depth, width, height)

Returns:
  • (Integer) - The number of bytes used per scanline in a datastream.

Parameters:
  • width (Integer) -- The height of the image pass.
  • width (Integer) -- The width of the image pass.
  • depth (Integer) -- The color depth of the pixels.
  • color_mode (Integer) -- The color mode in which the pixels are
def pass_bytesize(color_mode, depth, width, height)
  return 0 if width == 0 || height == 0
  (scanline_bytesize(color_mode, depth, width) + 1) * height
end

def pixel_bitsize(color_mode, depth = 8)

Returns:
  • (Integer) - The number of bytes used per pixel in a datastream.

Parameters:
  • depth (Integer) -- The color depth of the pixels.
  • color_mode (Integer) -- The color mode in which the pixels are
def pixel_bitsize(color_mode, depth = 8)
  samples_per_pixel(color_mode) * depth
end

def pixel_bytesize(color_mode, depth = 8)

Returns:
  • (Integer) - The number of bytes used per pixel in a datastream.

Parameters:
  • color_mode (Integer) -- The color mode in which the pixels are
def pixel_bytesize(color_mode, depth = 8)
  return 1 if depth < 8
  (pixel_bitsize(color_mode, depth) + 7) >> 3
end

def r(value)

Returns:
  • (Integer) - A value between 0 and MAX.

Parameters:
  • value (Integer) -- The color value.
def r(value)
  (value & 0xff000000) >> 24
end

def rgb(r, g, b)

Returns:
  • (Integer) - The newly constructed color value.

Parameters:
  • b (Integer) -- The b-component (0-255)
  • g (Integer) -- The g-component (0-255)
  • r (Integer) -- The r-component (0-255)
def rgb(r, g, b)
  r << 24 | g << 16 | b << 8 | 0xff
end

def rgba(r, g, b, a)

Returns:
  • (Integer) - The newly constructed color value.

Parameters:
  • a (Integer) -- The opacity (0-255)
  • b (Integer) -- The b-component (0-255)
  • g (Integer) -- The g-component (0-255)
  • r (Integer) -- The r-component (0-255)
def rgba(r, g, b, a)
  r << 24 | g << 16 | b << 8 | a
end

def samples_per_pixel(color_mode)

Returns:
  • (Integer) - The number of sample values per pixel.

Parameters:
  • color_mode (Integer) -- The color mode being used.
def samples_per_pixel(color_mode)
  case color_mode
    when ChunkyPNG::COLOR_INDEXED;         1
    when ChunkyPNG::COLOR_TRUECOLOR;       3
    when ChunkyPNG::COLOR_TRUECOLOR_ALPHA; 4
    when ChunkyPNG::COLOR_GRAYSCALE;       1
    when ChunkyPNG::COLOR_GRAYSCALE_ALPHA; 2
    else raise ChunkyPNG::NotSupported, "Don't know the number of samples for this colormode: #{color_mode}!"
  end
end

def scanline_bytesize(color_mode, depth, width)

Returns:
  • (Integer) - The number of bytes used per scanline in a datastream.

Parameters:
  • width (Integer) -- The number of pixels per scanline.
  • depth (Integer) -- The color depth of the pixels.
  • color_mode (Integer) -- The color mode in which the pixels are
def scanline_bytesize(color_mode, depth, width)
  ((pixel_bitsize(color_mode, depth) * width) + 7) >> 3
end

def to_grayscale(color)

Other tags:
    See: #grayscale_teint -

Returns:
  • (Integer) - The input color, converted to the best fitting

Parameters:
  • color (Integer) -- The color to convert.
def to_grayscale(color)
  grayscale_alpha(grayscale_teint(color), a(color))
end

def to_grayscale_alpha_bytes(color)

Other tags:
    See: #to_grayscale -

Returns:
  • (Array) - An array with 2 Integer elements.

Parameters:
  • color (Integer) -- The grayscale color to convert.
def to_grayscale_alpha_bytes(color)
  [b(color), a(color)] # assumption r == g == b
end

def to_grayscale_bytes(color)

Returns:
  • (Array) - An array with 1 Integer element.

Parameters:
  • color (Integer) -- The grayscale color to convert.
def to_grayscale_bytes(color)
  [b(color)] # assumption r == g == b
end

def to_hex(color, include_alpha = true)

Returns:
  • (String) - The color in hex notation, starting with a pound sign.

Parameters:
  • value (Integer) -- The color to convert.
def to_hex(color, include_alpha = true)
  include_alpha ? ('#%08x' % color) : ('#%06x' % [color >> 8])
end

def to_hsl(color, include_alpha = false)

Other tags:
    See: http://en.wikipedia.org/wiki/HSL_and_HSV -

Returns:
  • (Array[3]) - Optional fourth element for alpha, included if
  • (Array[2]) - The lightness of the color (0-1)
  • (Array[1]) - The saturation of the color (0-1)
  • (Array[0]) - The hue of the color (0-360)

Parameters:
  • include_alpha (Boolean) -- Flag indicates whether a fourth element
  • color (Integer) -- The ChunkyPNG color to convert.
def to_hsl(color, include_alpha = false)
  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

def to_hsv(color, include_alpha = false)

Other tags:
    See: http://en.wikipedia.org/wiki/HSL_and_HSV -

Returns:
  • (Array[3]) - Optional fourth element for alpha, included if
  • (Array[2]) - The value of the color (0-1)
  • (Array[1]) - The saturation of the color (0-1)
  • (Array[0]) - The hue of the color (0-360)

Parameters:
  • include_alpha (Boolean) -- Flag indicates whether a fourth element
  • color (Integer) -- The ChunkyPNG color to convert.
def to_hsv(color, include_alpha = false)
  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

def to_truecolor_alpha_bytes(color)

Returns:
  • (Array) - An array with 4 Integer elements.

Parameters:
  • color (Integer) -- The color to convert.
def to_truecolor_alpha_bytes(color)
  [r(color), g(color), b(color), a(color)]
end

def to_truecolor_bytes(color)

Returns:
  • (Array) - An array with 3 Integer elements.

Parameters:
  • color (Integer) -- The color to convert.
def to_truecolor_bytes(color)
  [r(color), g(color), b(color)]
end