Module: ChunkyPNG::Color

Extended by:
Color
Included in:
Color
Defined in:
lib/chunky_png/color.rb

Overview

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 = 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.

Constant Summary collapse

MAX =

Returns The maximum value of each color component.

Returns:

  • (Integer)

    The maximum value of each color component.

0xff
MAX_EUCLIDEAN_DISTANCE_RGBA =

Could be simplified as MAX * 2, but this format mirrors the math in #euclidean_distance_rgba

Returns:

  • (Float)

    The maximum Euclidean distance of two RGBA colors.

Math.sqrt(MAX**2 * 4)
PREDEFINED_COLORS =

Returns All the predefined color names in HTML.

Returns:

  • (Hash<Symbol, Integer>)

    All the predefined color names in HTML.

{
  aliceblue: 0xf0f8ff00,
  antiquewhite: 0xfaebd700,
  aqua: 0x00ffff00,
  aquamarine: 0x7fffd400,
  azure: 0xf0ffff00,
  beige: 0xf5f5dc00,
  bisque: 0xffe4c400,
  black: 0x00000000,
  blanchedalmond: 0xffebcd00,
  blue: 0x0000ff00,
  blueviolet: 0x8a2be200,
  brown: 0xa52a2a00,
  burlywood: 0xdeb88700,
  cadetblue: 0x5f9ea000,
  chartreuse: 0x7fff0000,
  chocolate: 0xd2691e00,
  coral: 0xff7f5000,
  cornflowerblue: 0x6495ed00,
  cornsilk: 0xfff8dc00,
  crimson: 0xdc143c00,
  cyan: 0x00ffff00,
  darkblue: 0x00008b00,
  darkcyan: 0x008b8b00,
  darkgoldenrod: 0xb8860b00,
  darkgray: 0xa9a9a900,
  darkgrey: 0xa9a9a900,
  darkgreen: 0x00640000,
  darkkhaki: 0xbdb76b00,
  darkmagenta: 0x8b008b00,
  darkolivegreen: 0x556b2f00,
  darkorange: 0xff8c0000,
  darkorchid: 0x9932cc00,
  darkred: 0x8b000000,
  darksalmon: 0xe9967a00,
  darkseagreen: 0x8fbc8f00,
  darkslateblue: 0x483d8b00,
  darkslategray: 0x2f4f4f00,
  darkslategrey: 0x2f4f4f00,
  darkturquoise: 0x00ced100,
  darkviolet: 0x9400d300,
  deeppink: 0xff149300,
  deepskyblue: 0x00bfff00,
  dimgray: 0x69696900,
  dimgrey: 0x69696900,
  dodgerblue: 0x1e90ff00,
  firebrick: 0xb2222200,
  floralwhite: 0xfffaf000,
  forestgreen: 0x228b2200,
  fuchsia: 0xff00ff00,
  gainsboro: 0xdcdcdc00,
  ghostwhite: 0xf8f8ff00,
  gold: 0xffd70000,
  goldenrod: 0xdaa52000,
  gray: 0x80808000,
  grey: 0x80808000,
  green: 0x00800000,
  greenyellow: 0xadff2f00,
  honeydew: 0xf0fff000,
  hotpink: 0xff69b400,
  indianred: 0xcd5c5c00,
  indigo: 0x4b008200,
  ivory: 0xfffff000,
  khaki: 0xf0e68c00,
  lavender: 0xe6e6fa00,
  lavenderblush: 0xfff0f500,
  lawngreen: 0x7cfc0000,
  lemonchiffon: 0xfffacd00,
  lightblue: 0xadd8e600,
  lightcoral: 0xf0808000,
  lightcyan: 0xe0ffff00,
  lightgoldenrodyellow: 0xfafad200,
  lightgray: 0xd3d3d300,
  lightgrey: 0xd3d3d300,
  lightgreen: 0x90ee9000,
  lightpink: 0xffb6c100,
  lightsalmon: 0xffa07a00,
  lightseagreen: 0x20b2aa00,
  lightskyblue: 0x87cefa00,
  lightslategray: 0x77889900,
  lightslategrey: 0x77889900,
  lightsteelblue: 0xb0c4de00,
  lightyellow: 0xffffe000,
  lime: 0x00ff0000,
  limegreen: 0x32cd3200,
  linen: 0xfaf0e600,
  magenta: 0xff00ff00,
  maroon: 0x80000000,
  mediumaquamarine: 0x66cdaa00,
  mediumblue: 0x0000cd00,
  mediumorchid: 0xba55d300,
  mediumpurple: 0x9370d800,
  mediumseagreen: 0x3cb37100,
  mediumslateblue: 0x7b68ee00,
  mediumspringgreen: 0x00fa9a00,
  mediumturquoise: 0x48d1cc00,
  mediumvioletred: 0xc7158500,
  midnightblue: 0x19197000,
  mintcream: 0xf5fffa00,
  mistyrose: 0xffe4e100,
  moccasin: 0xffe4b500,
  navajowhite: 0xffdead00,
  navy: 0x00008000,
  oldlace: 0xfdf5e600,
  olive: 0x80800000,
  olivedrab: 0x6b8e2300,
  orange: 0xffa50000,
  orangered: 0xff450000,
  orchid: 0xda70d600,
  palegoldenrod: 0xeee8aa00,
  palegreen: 0x98fb9800,
  paleturquoise: 0xafeeee00,
  palevioletred: 0xd8709300,
  papayawhip: 0xffefd500,
  peachpuff: 0xffdab900,
  peru: 0xcd853f00,
  pink: 0xffc0cb00,
  plum: 0xdda0dd00,
  powderblue: 0xb0e0e600,
  purple: 0x80008000,
  red: 0xff000000,
  rosybrown: 0xbc8f8f00,
  royalblue: 0x4169e100,
  saddlebrown: 0x8b451300,
  salmon: 0xfa807200,
  sandybrown: 0xf4a46000,
  seagreen: 0x2e8b5700,
  seashell: 0xfff5ee00,
  sienna: 0xa0522d00,
  silver: 0xc0c0c000,
  skyblue: 0x87ceeb00,
  slateblue: 0x6a5acd00,
  slategray: 0x70809000,
  slategrey: 0x70809000,
  snow: 0xfffafa00,
  springgreen: 0x00ff7f00,
  steelblue: 0x4682b400,
  tan: 0xd2b48c00,
  teal: 0x00808000,
  thistle: 0xd8bfd800,
  tomato: 0xff634700,
  turquoise: 0x40e0d000,
  violet: 0xee82ee00,
  wheat: 0xf5deb300,
  white: 0xffffff00,
  whitesmoke: 0xf5f5f500,
  yellow: 0xffff0000,
  yellowgreen: 0x9acd3200,
}
BLACK =

Returns Black pixel/color.

Returns:

  • (Integer)

    Black pixel/color

rgb(0, 0, 0)
WHITE =

Returns White pixel/color.

Returns:

  • (Integer)

    White pixel/color

rgb(255, 255, 255)
TRANSPARENT =

Returns Fully transparent pixel/color.

Returns:

  • (Integer)

    Fully transparent pixel/color

rgba(0, 0, 0, 0)

Instance Method Summary collapse

Instance Method Details

#a(value) ⇒ Integer

Returns the alpha channel value for the color value.

Parameters:

  • value (Integer)

    The color value.

Returns:

  • (Integer)

    A value between 0 and MAX.



300
301
302
# File 'lib/chunky_png/color.rb', line 300

def a(value)
  value & 0x000000ff
end

#alpha_decomposable?(color, mask, bg, tolerance = 1) ⇒ Boolean

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.

Parameters:

  • color (Integer)

    The color that was the result of compositing.

  • mask (Integer)

    The opaque variant of the color that was being composed

  • bg (Integer)

    The background color on which the color was composed.

  • tolerance (Integer) (defaults to: 1)

    The decomposition tolerance level, a value between 0 and 255.

Returns:

  • (Boolean)

    True if the alpha component can be decomposed successfully.

See Also:



504
505
506
507
508
509
# File 'lib/chunky_png/color.rb', line 504

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

#b(value) ⇒ Integer

Returns the blue-component from the color value.

Parameters:

  • value (Integer)

    The color value.

Returns:

  • (Integer)

    A value between 0 and MAX.



292
293
294
# File 'lib/chunky_png/color.rb', line 292

def b(value)
  (value & 0x0000ff00) >> 8
end

#blend(fg, bg) ⇒ Integer

Blends the foreground and background color by taking the average of the components.

Parameters:

  • fg (Integer)

    The foreground color.

  • bg (Integer)

    The foreground color.

Returns:

  • (Integer)

    The blended color.



407
408
409
# File 'lib/chunky_png/color.rb', line 407

def blend(fg, bg)
  (fg + bg) >> 1
end

#compose_precise(fg, bg) ⇒ Integer

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.

Parameters:

  • fg (Integer)

    The foreground color.

  • bg (Integer)

    The background color.

Returns:

  • (Integer)

    The composited color.

See Also:



384
385
386
387
388
389
390
391
392
393
394
395
396
397
# File 'lib/chunky_png/color.rb', line 384

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

#compose_quick(fg, bg) ⇒ Integer Also known as: compose

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.

Parameters:

  • fg (Integer)

    The foreground color.

  • bg (Integer)

    The background color.

Returns:

  • (Integer)

    The composited color.

See Also:



362
363
364
365
366
367
368
369
370
371
372
# File 'lib/chunky_png/color.rb', line 362

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

#decompose_alpha(color, mask, bg) ⇒ Integer

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.

Parameters:

  • color (Integer)

    The color that was the result of compositing.

  • mask (Integer)

    The opaque variant of the color that was being composed

  • bg (Integer)

    The background color on which the color was composed.

Returns:

  • (Integer)

    The best fitting alpha channel, a value between 0 and 255.

See Also:



525
526
527
528
# File 'lib/chunky_png/color.rb', line 525

def decompose_alpha(color, mask, bg)
  components = decompose_alpha_components(color, mask, bg)
  (components.inject(0) { |a, b| a + b } / 3.0).round
end

#decompose_alpha_component(channel, color, mask, bg) ⇒ Integer

Decomposes an alpha channel for either the r, g or b color channel.

Parameters:

  • channel (:r, :g, :b)

    The channel to decompose the alpha channel from.

  • color (Integer)

    The color that was the result of compositing.

  • mask (Integer)

    The opaque variant of the color that was being composed

  • bg (Integer)

    The background color on which the color was composed.

Returns:

  • (Integer)

    The decomposed alpha value for the channel.



538
539
540
541
542
543
544
545
546
# File 'lib/chunky_png/color.rb', line 538

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

#decompose_alpha_components(color, mask, bg) ⇒ Array<Integer>

Decomposes the alpha channels for the r, g and b color channel.

Parameters:

  • color (Integer)

    The color that was the result of compositing.

  • mask (Integer)

    The opaque variant of the color that was being composed

  • bg (Integer)

    The background color on which the color was composed.

Returns:

  • (Array<Integer>)

    The decomposed alpha values for the r, g and b channels.



555
556
557
558
559
560
561
# File 'lib/chunky_png/color.rb', line 555

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

#decompose_color(color, mask, bg, tolerance = 1) ⇒ Integer

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.

Parameters:

  • color (Integer)

    The color that was the result of compositing.

  • mask (Integer)

    The opaque variant of the color that was being composed

  • bg (Integer)

    The background color on which the color was composed.

  • tolerance (Integer) (defaults to: 1)

    The decomposition tolerance level, a value between 0 and 255.

Returns:

  • (Integer)

    The decomposed color, a variant of the masked color with the alpha channel set to an appropriate value.



483
484
485
486
487
488
489
# File 'lib/chunky_png/color.rb', line 483

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

#euclidean_distance_rgba(pixel_after, pixel_before) ⇒ Float

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.

Parameters:

  • pixel_after (Integer)
  • pixel_before (Integer)

Returns:

  • (Float)


723
724
725
726
727
728
729
730
731
732
# File 'lib/chunky_png/color.rb', line 723

def euclidean_distance_rgba(pixel_after, pixel_before)
  return 0.0 if 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

#fade(color, factor) ⇒ Integer

Lowers the intensity of a color, by lowering its alpha by a given factor.

Parameters:

  • color (Integer)

    The color to adjust.

  • factor (Integer)

    Fade factor as an integer between 0 and 255.

Returns:

  • (Integer)

    The faded color.



462
463
464
465
# File 'lib/chunky_png/color.rb', line 462

def fade(color, factor)
  new_alpha = int8_mult(a(color), factor)
  (color & 0xffffff00) | new_alpha
end

#from_hex(hex_value, opacity = nil) ⇒ Integer

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 “#”.

Parameters:

  • hex_value (String)

    The color in hex notation.

  • opacity (Integer) (defaults to: nil)

    The opacity value for the color. Overrides any opacity value given in the hex value if given.

Returns:

  • (Integer)

    The color value.

Raises:

  • (ArgumentError)

    if the value given is not a hex color notation.



167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/chunky_png/color.rb', line 167

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

#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.

Parameters:

  • hue (Fixnum)

    The hue component (0-360)

  • saturation (Fixnum)

    The saturation component (0-1)

  • lightness (Fixnum)

    The lightness component (0-1)

  • alpha (Fixnum) (defaults to: 255)

    Defaults to opaque (255).

Returns:

  • (Integer)

    The newly constructed color value.

Raises:

  • (ArgumentError)

    if the hsl triple is invalid.

See Also:



220
221
222
223
224
225
226
227
228
229
230
# File 'lib/chunky_png/color.rb', line 220

def 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 Also known as: from_hsb

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.

Parameters:

  • hue (Fixnum)

    The hue component (0-360)

  • saturation (Fixnum)

    The saturation component (0-1)

  • value (Fixnum)

    The value (brightness) component (0-1)

  • alpha (Fixnum) (defaults to: 255)

    Defaults to opaque (255).

Returns:

  • (Integer)

    The newly constructed color value.

Raises:

  • (ArgumentError)

    if the hsv triple is invalid.

See Also:



194
195
196
197
198
199
200
201
202
203
204
# File 'lib/chunky_png/color.rb', line 194

def 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

#from_rgb_stream(stream, pos = 0) ⇒ Integer

Creates a color by unpacking an rgb triple from a string.

Parameters:

  • stream (String)

    The string to load the color from. It should be at least 3 + pos bytes long.

  • pos (Integer) (defaults to: 0)

    The position in the string to load the triple from.

Returns:

  • (Integer)

    The newly constructed color value.



142
143
144
# File 'lib/chunky_png/color.rb', line 142

def from_rgb_stream(stream, pos = 0)
  rgb(*stream.unpack("@#{pos}C3"))
end

#from_rgba_stream(stream, pos = 0) ⇒ Integer

Creates a color by unpacking an rgba triple from a string

Parameters:

  • stream (String)

    The string to load the color from. It should be at least 4 + pos bytes long.

  • pos (Integer) (defaults to: 0)

    The position in the string to load the triple from.

Returns:

  • (Integer)

    The newly constructed color value.



152
153
154
# File 'lib/chunky_png/color.rb', line 152

def from_rgba_stream(stream, pos = 0)
  rgba(*stream.unpack("@#{pos}C4"))
end

#fully_transparent?(value) ⇒ true, false

Returns true if this color is fully transparent.

Parameters:

  • value (Integer)

    The color to test.

Returns:

  • (true, false)

    True if the alpha channel equals 0.



331
332
333
# File 'lib/chunky_png/color.rb', line 331

def fully_transparent?(value)
  a(value) == 0x00000000
end

#g(value) ⇒ Integer

Returns the green-component from the color value.

Parameters:

  • value (Integer)

    The color value.

Returns:

  • (Integer)

    A value between 0 and MAX.



284
285
286
# File 'lib/chunky_png/color.rb', line 284

def g(value)
  (value & 0x00ff0000) >> 16
end

#grayscale(teint) ⇒ Integer

Creates a new color using a grayscale teint.

Parameters:

  • teint (Integer)

    The grayscale teint (0-255), will be used as r, g, and b value.

Returns:

  • (Integer)

    The newly constructed color value.



119
120
121
# File 'lib/chunky_png/color.rb', line 119

def grayscale(teint)
  teint << 24 | teint << 16 | teint << 8 | 0xff
end

#grayscale?(value) ⇒ true, false

Returns true if this color is fully transparent.

Parameters:

  • value (Integer)

    The color to test.

Returns:

  • (true, false)

    True if the r, g and b component are equal.



323
324
325
# File 'lib/chunky_png/color.rb', line 323

def grayscale?(value)
  r(value) == b(value) && b(value) == g(value)
end

#grayscale_alpha(teint, a) ⇒ Integer

Creates a new color using a grayscale teint and alpha value.

Parameters:

  • teint (Integer)

    The grayscale teint (0-255), will be used as r, g, and b value.

  • a (Integer)

    The opacity (0-255)

Returns:

  • (Integer)

    The newly constructed color value.



128
129
130
# File 'lib/chunky_png/color.rb', line 128

def grayscale_alpha(teint, a)
  teint << 24 | teint << 16 | teint << 8 | a
end

#grayscale_teint(color) ⇒ Integer

Calculates the grayscale teint of an RGB color.

Parameters:

  • color (Integer)

    The color to convert.

Returns:

  • (Integer)

    The grayscale teint of the input color, 0-255.



439
440
441
# File 'lib/chunky_png/color.rb', line 439

def grayscale_teint(color)
  (r(color) * 0.3 + g(color) * 0.59 + b(color) * 0.11).round
end

#html_color(color_name, opacity = nil) ⇒ Integer

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.

Parameters:

  • color_name (Symbol, String)

    The color name. It may include an opacity specifier like @ 0.8 to set the color’s opacity.

  • opacity (Integer) (defaults to: nil)

    The opacity value for the color between 0 and

    1. Overrides any opacity value given in the color name.

Returns:

  • (Integer)

    The color value.

Raises:



910
911
912
913
914
915
916
917
# File 'lib/chunky_png/color.rb', line 910

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.key?(base_color_name)
  end
  raise ArgumentError, "Unknown color name #{color_name}!"
end

#int8_mult(a, b) ⇒ Integer

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.

Parameters:

  • a (Integer)

    The first fraction.

  • b (Integer)

    The second fraction.

Returns:

  • (Integer)

    The result of the multiplication.



348
349
350
351
# File 'lib/chunky_png/color.rb', line 348

def int8_mult(a, b)
  t = a * b + 0x80
  ((t >> 8) + t) >> 8
end

#interpolate_quick(fg, bg, alpha) ⇒ Integer

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.

Parameters:

  • fg (Integer)

    The foreground color.

  • bg (Integer)

    The background color.

  • alpha (Integer)

    The blending factor (fixed 8bit)

Returns:

  • (Integer)

    The interpolated color.



421
422
423
424
425
426
427
428
429
430
431
432
433
# File 'lib/chunky_png/color.rb', line 421

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

#opaque!(value) ⇒ Integer

Returns the opaque value of this color by removing the alpha channel.

Parameters:

  • value (Integer)

    The color to transform.

Returns:

  • (Integer)

    The opaque color



315
316
317
# File 'lib/chunky_png/color.rb', line 315

def opaque!(value)
  value | 0x000000ff
end

#opaque?(value) ⇒ true, false

Returns true if this color is fully opaque.

Parameters:

  • value (Integer)

    The color to test.

Returns:

  • (true, false)

    True if the alpha channel equals MAX.



308
309
310
# File 'lib/chunky_png/color.rb', line 308

def opaque?(value)
  a(value) == 0x000000ff
end

#parse(source) ⇒ Integer

Parses a color value given a numeric or string argument.

It supports color numbers, colors in hex notation and named HTML colors.

Parameters:

  • source (Integer, String)

    The color value.

Returns:

  • (Integer)

    The color value, with the opacity applied if one was given.



86
87
88
89
90
91
92
93
94
# File 'lib/chunky_png/color.rb', line 86

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

#pass_bytesize(color_mode, depth, width, height) ⇒ Integer

Returns the number of bytes used for an image pass

Parameters:

  • color_mode (Integer)

    The color mode in which the pixels are stored.

  • depth (Integer)

    The color depth of the pixels.

  • width (Integer)

    The width of the image pass.

  • height (Integer)

    The height of the image pass.

Returns:

  • (Integer)

    The number of bytes used per scanline in a datastream.



985
986
987
988
# File 'lib/chunky_png/color.rb', line 985

def pass_bytesize(color_mode, depth, width, height)
  return 0 if width == 0 || height == 0
  (scanline_bytesize(color_mode, depth, width) + 1) * height
end

#pixel_bitsize(color_mode, depth = 8) ⇒ Integer

Returns the size in bits of a pixel when it is stored using a given color mode.

Parameters:

  • color_mode (Integer)

    The color mode in which the pixels are stored.

  • depth (Integer) (defaults to: 8)

    The color depth of the pixels.

Returns:

  • (Integer)

    The number of bytes used per pixel in a datastream.



964
965
966
# File 'lib/chunky_png/color.rb', line 964

def pixel_bitsize(color_mode, depth = 8)
  samples_per_pixel(color_mode) * depth
end

#pixel_bytesize(color_mode, depth = 8) ⇒ Integer

Returns the size in bytes of a pixel when it is stored using a given color mode.

Parameters:

  • color_mode (Integer)

    The color mode in which the pixels are stored.

Returns:

  • (Integer)

    The number of bytes used per pixel in a datastream.



952
953
954
955
# File 'lib/chunky_png/color.rb', line 952

def pixel_bytesize(color_mode, depth = 8)
  return 1 if depth < 8
  (pixel_bitsize(color_mode, depth) + 7) >> 3
end

#r(value) ⇒ Integer

Returns the red-component from the color value.

Parameters:

  • value (Integer)

    The color value.

Returns:

  • (Integer)

    A value between 0 and MAX.



276
277
278
# File 'lib/chunky_png/color.rb', line 276

def r(value)
  (value & 0xff000000) >> 24
end

#rgb(r, g, b) ⇒ Integer

Creates a new color using an r, g, b triple.

Parameters:

  • r (Integer)

    The r-component (0-255)

  • g (Integer)

    The g-component (0-255)

  • b (Integer)

    The b-component (0-255)

Returns:

  • (Integer)

    The newly constructed color value.



111
112
113
# File 'lib/chunky_png/color.rb', line 111

def 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.

Parameters:

  • r (Integer)

    The r-component (0-255)

  • g (Integer)

    The g-component (0-255)

  • b (Integer)

    The b-component (0-255)

  • a (Integer)

    The opacity (0-255)

Returns:

  • (Integer)

    The newly constructed color value.



102
103
104
# File 'lib/chunky_png/color.rb', line 102

def rgba(r, g, b, a)
  r << 24 | g << 16 | b << 8 | a
end

#samples_per_pixel(color_mode) ⇒ Integer

Returns the number of sample values per pixel.

Parameters:

  • color_mode (Integer)

    The color mode being used.

Returns:

  • (Integer)

    The number of sample values per pixel.



935
936
937
938
939
940
941
942
943
944
# File 'lib/chunky_png/color.rb', line 935

def samples_per_pixel(color_mode)
  case color_mode
    when ChunkyPNG::COLOR_INDEXED         then 1
    when ChunkyPNG::COLOR_TRUECOLOR       then 3
    when ChunkyPNG::COLOR_TRUECOLOR_ALPHA then 4
    when ChunkyPNG::COLOR_GRAYSCALE       then 1
    when ChunkyPNG::COLOR_GRAYSCALE_ALPHA then 2
    else raise ChunkyPNG::NotSupported, "Don't know the number of samples for this colormode: #{color_mode}!"
  end
end

#scanline_bytesize(color_mode, depth, width) ⇒ Integer

Returns the number of bytes used per scanline.

Parameters:

  • color_mode (Integer)

    The color mode in which the pixels are stored.

  • depth (Integer)

    The color depth of the pixels.

  • width (Integer)

    The number of pixels per scanline.

Returns:

  • (Integer)

    The number of bytes used per scanline in a datastream.



974
975
976
# File 'lib/chunky_png/color.rb', line 974

def scanline_bytesize(color_mode, depth, width)
  ((pixel_bitsize(color_mode, depth) * width) + 7) >> 3
end

#to_grayscale(color) ⇒ Integer

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.

Parameters:

  • color (Integer)

    The color to convert.

Returns:

  • (Integer)

    The input color, converted to the best fitting grayscale.

See Also:



454
455
456
# File 'lib/chunky_png/color.rb', line 454

def to_grayscale(color)
  grayscale_alpha(grayscale_teint(color), a(color))
end

#to_grayscale_alpha_bytes(color) ⇒ Array<Integer>

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.

Parameters:

  • color (Integer)

    The grayscale color to convert.

Returns:

  • (Array<Integer>)

    An array with 2 Integer elements.

See Also:



702
703
704
# File 'lib/chunky_png/color.rb', line 702

def to_grayscale_alpha_bytes(color)
  [b(color), a(color)] # assumption r == g == b
end

#to_grayscale_bytes(color) ⇒ Array<Integer>

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.

Parameters:

  • color (Integer)

    The grayscale color to convert.

Returns:

  • (Array<Integer>)

    An array with 1 Integer element.



688
689
690
# File 'lib/chunky_png/color.rb', line 688

def to_grayscale_bytes(color)
  [b(color)] # assumption r == g == b
end

#to_hex(color, include_alpha = true) ⇒ String

Returns a string representing this color using hex notation (i.e. #rrggbbaa).

Parameters:

  • color (Integer)

    The color to convert.

  • include_alpha (Boolean) (defaults to: true)

Returns:

  • (String)

    The color in hex notation, starting with a pound sign.



573
574
575
# File 'lib/chunky_png/color.rb', line 573

def to_hex(color, include_alpha = true)
  include_alpha ? ("#%08x" % color) : ("#%06x" % [color >> 8])
end

#to_hsl(color, include_alpha = false) ⇒ Array<Fixnum>[0], ...

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.

Parameters:

  • color (Integer)

    The ChunkyPNG color to convert.

  • include_alpha (Boolean) (defaults to: false)

    Flag indicates whether a fourth element representing alpha channel should be included in the returned array.

Returns:

  • (Array<Fixnum>[0])

    The hue of the color (0-360)

  • (Array<Fixnum>[1])

    The saturation of the color (0-1)

  • (Array<Fixnum>[2])

    The lightness of the color (0-1)

  • (Array<Fixnum>[3])

    Optional fourth element for alpha, included if include_alpha=true (0-255)

See Also:



625
626
627
628
629
630
631
632
# File 'lib/chunky_png/color.rb', line 625

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

#to_hsv(color, include_alpha = false) ⇒ Array[0], ... Also known as: to_hsb

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.

Parameters:

  • color (Integer)

    The ChunkyPNG color to convert.

  • include_alpha (Boolean) (defaults to: false)

    Flag indicates whether a fourth element representing alpha channel should be included in the returned array.

Returns:

  • (Array[0])

    The hue of the color (0-360)

  • (Array[1])

    The saturation of the color (0-1)

  • (Array[2])

    The value of the color (0-1)

  • (Array[3])

    Optional fourth element for alpha, included if include_alpha=true (0-255)

See Also:



596
597
598
599
600
601
602
603
# File 'lib/chunky_png/color.rb', line 596

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

#to_truecolor_alpha_bytes(color) ⇒ Array<Integer>

Returns an array with the separate RGBA values for this color.

Parameters:

  • color (Integer)

    The color to convert.

Returns:

  • (Array<Integer>)

    An array with 4 Integer elements.



668
669
670
# File 'lib/chunky_png/color.rb', line 668

def to_truecolor_alpha_bytes(color)
  [r(color), g(color), b(color), a(color)]
end

#to_truecolor_bytes(color) ⇒ Array<Integer>

Returns an array with the separate RGB values for this color. The alpha channel will be discarded.

Parameters:

  • color (Integer)

    The color to convert.

Returns:

  • (Array<Integer>)

    An array with 3 Integer elements.



677
678
679
# File 'lib/chunky_png/color.rb', line 677

def to_truecolor_bytes(color)
  [r(color), g(color), b(color)]
end