Module: ChunkyPNG::Color
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 componetns. 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 =
The maximum value of each color component.
0xff
- BLACK =
Black pixel/color
rgb( 0, 0, 0)
- WHITE =
White pixel/color
rgb(255, 255, 255)
- TRANSPARENT =
Fully transparent pixel/color
rgba(0, 0, 0, 0)
Instance Method Summary collapse
-
#a(value) ⇒ Integer
Returns the alpha channel value for the color value.
-
#alpha_decomposable?(color, mask, bg, tolerance = 1) ⇒ Integer
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.
-
#b(value) ⇒ Integer
Returns the blue-component from the color value.
-
#blend(fg, bg) ⇒ Integer
Blends the foreground and background color by taking the average of the components.
-
#compose_precise(fg, bg) ⇒ Integer
Composes two colors with an alpha channel using floating point math.
-
#compose_quick(fg, bg) ⇒ Integer
(also: #compose)
Composes two colors with an alpha channel using integer math.
-
#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.
-
#decompose_alpha_component(channel, color, mask, bg) ⇒ Object
Decomposes an alpha channel for either the r, g or b color channel.
-
#decompose_alpha_components(color, mask, bg) ⇒ Array<Integer>
Decomposes the alpha channels for the r, g and b color channel.
-
#decompose_color(color, mask, bg, tolerance = 1) ⇒ Integer
Decomposes a color, given a color, a mask color and a background color.
-
#fade(color, factor) ⇒ Integer
Lowers the intensity of a color, by lowering its alpha by a given factor.
-
#from_hex(str) ⇒ Object
Creates a color by converting it from a string in hex notation.
-
#from_rgb_stream(stream, pos = 0) ⇒ Integer
Creates a color by unpacking an rgb triple from a string.
-
#from_rgba_stream(stream, pos = 0) ⇒ Integer
Creates a color by unpacking an rgba triple from a string.
-
#fully_transparent?(value) ⇒ true, false
Returns true if this color is fully transparent.
-
#g(value) ⇒ Integer
Returns the green-component from the color value.
-
#grayscale(teint) ⇒ ChunkyPNG::Color
Creates a new color using a grayscale teint.
-
#grayscale?(value) ⇒ true, false
Returns true if this color is fully transparent.
-
#grayscale_alpha(teint, a) ⇒ Integer
Creates a new color using a grayscale teint and alpha value.
-
#int8_mult(a, b) ⇒ Integer
Multiplies two fractions using integer math, where the fractions are stored using an integer between 0 and 255.
-
#opaque!(value) ⇒ Integer
Returns the opaque value of this color by removing the alpha channel.
-
#opaque?(value) ⇒ true, false
Returns true if this color is fully opaque.
-
#pass_bytesize(color_mode, depth, width, height) ⇒ Integer
Returns the number of bytes used for an image pass.
-
#pixel_bitsize(color_mode, depth = 8) ⇒ Integer
Returns the size in bits of a pixel when it is stored using a given color mode.
-
#pixel_bytesize(color_mode, depth = 8) ⇒ Integer
Returns the size in bytes of a pixel when it is stored using a given color mode.
-
#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.
-
#samples_per_pixel(color_mode) ⇒ Integer
Returns the number of sample values per pixel.
-
#scanline_bytesize(color_mode, depth, width) ⇒ Integer
Returns the number of bytes used per scanline.
-
#to_grayscale_alpha_bytes(color) ⇒ Array<Integer>
Returns an array with the grayscale teint and alpha channel values for this color.
-
#to_grayscale_bytes(color) ⇒ Array<Integer>
Returns an array with the grayscale teint value for this color.
-
#to_hex(color, include_alpha = true) ⇒ String
Returns a string representing this color using hex notation (i.e. #rrggbbaa).
-
#to_truecolor_alpha_bytes(color) ⇒ Array<Integer>
Returns an array with the separate RGBA values for this color.
-
#to_truecolor_bytes(color) ⇒ Array<Integer>
Returns an array with the separate RGB values for this color.
Instance Method Details
#a(value) ⇒ Integer
Returns the alpha channel value for the color value.
121 122 123 |
# File 'lib/chunky_png/color.rb', line 121 def a(value) value & 0x000000ff end |
#alpha_decomposable?(color, mask, bg, tolerance = 1) ⇒ Integer
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.
273 274 275 276 277 278 |
# File 'lib/chunky_png/color.rb', line 273 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 return components.max <= 255 && components.min >= 0 && (sum + tolerance * 3) >= max end |
#b(value) ⇒ Integer
Returns the blue-component from the color value.
113 114 115 |
# File 'lib/chunky_png/color.rb', line 113 def b(value) (value & 0x0000ff00) >> 8 end |
#blend(fg, bg) ⇒ Integer
Blends the foreground and background color by taking the average of the components.
228 229 230 |
# File 'lib/chunky_png/color.rb', line 228 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.
205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
# File 'lib/chunky_png/color.rb', line 205 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.
183 184 185 186 187 188 189 190 191 192 193 |
# File 'lib/chunky_png/color.rb', line 183 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.
292 293 294 295 |
# File 'lib/chunky_png/color.rb', line 292 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) ⇒ Object
Decomposes an alpha channel for either the r, g or b color channel.
303 304 305 306 |
# File 'lib/chunky_png/color.rb', line 303 def decompose_alpha_component(channel, color, mask, bg) ((send(channel, bg) - send(channel, color)).to_f / (send(channel, bg) - send(channel, mask)).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.
313 314 315 316 317 318 319 |
# File 'lib/chunky_png/color.rb', line 313 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 transparentvariant of the mask color.
255 256 257 258 259 260 261 |
# File 'lib/chunky_png/color.rb', line 255 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 |
#fade(color, factor) ⇒ Integer
Lowers the intensity of a color, by lowering its alpha by a given factor.
236 237 238 239 |
# File 'lib/chunky_png/color.rb', line 236 def fade(color, factor) new_alpha = int8_mult(a(color), factor) (color & 0xffffff00) | new_alpha end |
#from_hex(str) ⇒ Object
Creates a color by converting it from a string in hex notation.
It supports colors with (#rrggbbaa) or without (#rrggbb) alpha channel. Color strings may include the prefix “0x” or “#”.
converted color value.
81 82 83 84 85 86 87 |
# File 'lib/chunky_png/color.rb', line 81 def from_hex(str) case str when /^(?:#|0x)?([0-9a-f]{6})$/i; ($1.hex << 8) | 0xff when /^(?:#|0x)?([0-9a-f]{8})$/i; $1.hex else raise ChunkyPNG::ExpectationFailed, "Not a valid hex color notation: #{str.inspect}!" end end |
#from_rgb_stream(stream, pos = 0) ⇒ Integer
Creates a color by unpacking an rgb triple from a string.
60 61 62 |
# File 'lib/chunky_png/color.rb', line 60 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
70 71 72 |
# File 'lib/chunky_png/color.rb', line 70 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.
152 153 154 |
# File 'lib/chunky_png/color.rb', line 152 def fully_transparent?(value) a(value) == 0x00000000 end |
#g(value) ⇒ Integer
Returns the green-component from the color value.
105 106 107 |
# File 'lib/chunky_png/color.rb', line 105 def g(value) (value & 0x00ff0000) >> 16 end |
#grayscale(teint) ⇒ ChunkyPNG::Color
Creates a new color using a grayscale teint.
40 41 42 |
# File 'lib/chunky_png/color.rb', line 40 def grayscale(teint) teint << 24 | teint << 16 | teint << 8 | 0xff end |
#grayscale?(value) ⇒ true, false
Returns true if this color is fully transparent.
144 145 146 |
# File 'lib/chunky_png/color.rb', line 144 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.
46 47 48 |
# File 'lib/chunky_png/color.rb', line 46 def grayscale_alpha(teint, a) teint << 24 | teint << 16 | teint << 8 | a 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.
169 170 171 172 |
# File 'lib/chunky_png/color.rb', line 169 def int8_mult(a, b) t = a * b + 0x80 ((t >> 8) + t) >> 8 end |
#opaque!(value) ⇒ Integer
Returns the opaque value of this color by removing the alpha channel.
136 137 138 |
# File 'lib/chunky_png/color.rb', line 136 def opaque!(value) value | 0x000000ff end |
#opaque?(value) ⇒ true, false
Returns true if this color is fully opaque.
129 130 131 |
# File 'lib/chunky_png/color.rb', line 129 def opaque?(value) a(value) == 0x000000ff end |
#pass_bytesize(color_mode, depth, width, height) ⇒ Integer
Returns the number of bytes used for an image pass
434 435 436 437 |
# File 'lib/chunky_png/color.rb', line 434 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.
415 416 417 |
# File 'lib/chunky_png/color.rb', line 415 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.
406 407 408 409 |
# File 'lib/chunky_png/color.rb', line 406 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.
97 98 99 |
# File 'lib/chunky_png/color.rb', line 97 def r(value) (value & 0xff000000) >> 24 end |
#rgb(r, g, b) ⇒ Integer
Creates a new color using an r, g, b triple.
34 35 36 |
# File 'lib/chunky_png/color.rb', line 34 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.
28 29 30 |
# File 'lib/chunky_png/color.rb', line 28 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.
392 393 394 395 396 397 398 399 400 401 |
# File 'lib/chunky_png/color.rb', line 392 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 numer of samples for this colormode: #{color_mode}!" end end |
#scanline_bytesize(color_mode, depth, width) ⇒ Integer
Returns the number of bytes used per scanline.
424 425 426 |
# File 'lib/chunky_png/color.rb', line 424 def scanline_bytesize(color_mode, depth, width) ((pixel_bitsize(color_mode, depth) * width) + 7) >> 3 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 r,g and b value to be equal.
368 369 370 |
# File 'lib/chunky_png/color.rb', line 368 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.
357 358 359 |
# File 'lib/chunky_png/color.rb', line 357 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).
329 330 331 |
# File 'lib/chunky_png/color.rb', line 329 def to_hex(color, include_alpha = true) include_alpha ? ('#%08x' % color) : ('#%06x' % [color >> 8]) end |
#to_truecolor_alpha_bytes(color) ⇒ Array<Integer>
Returns an array with the separate RGBA values for this color.
337 338 339 |
# File 'lib/chunky_png/color.rb', line 337 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.
346 347 348 |
# File 'lib/chunky_png/color.rb', line 346 def to_truecolor_bytes(color) [r(color), g(color), b(color)] end |