class Chromate::Color
The `Color` class is used to represent a terminal color in the range 0..255. Colors are powerful tools, and have a multitude of convenience methods to make working with them easier.
Color
Models
¶ ↑
All colors have a so-called 'color model' (accessible via the `color_model` attribute). The color model of a color determines how it will be escaped. The following color models are possible:
- Colors in the range 0..7 have a color model of 8. These colors are always escaped as single-integer values in the ranges 30..37 and 40..47 for foregrounds and backgrounds, respectively. - Colors in the range 8..15 have a color model of 16. These colors can be escaped in two ways, with single-integer values in the ranges 90..97 and 100..107, for foreground and background, respectively, or with additional bold sequences. Which route is taken depends on the compliancy setting; single-integer values are non-compliant, while bold sequences are. The compliancy setting may be specified in three ways, each overriding the last: via the class attribute on `Color`, via the instance attribute by the same name, and in the method call as a second (optional parameter). The default at all levels is non-compliancy. - Colors in the range 16..255 have a color model of 256. These colors are always escaped as xterm-256 escape sequences of the form 38;5;x for foreground and 48;5;x for background, where x is the actual color value.
Converting from Other Formats
¶ ↑
The `Color` class features a powerful approximation mechanism, allowing the creation of colors from arbitrary RGB
values, which may be specified either in RGB
using {::from_rgb} or in hex using {::from_hex}. When either of these methods are used, the closest matching terminal color code will be found automatically, using the CIE 1976 delta-E algorithm. This operation is quite a bit slower compared to using exact color values, but it must be done only once, as all values are cached after first use and kept until the end of program execution.
Making New Colors
¶ ↑
If you __really__ want to make a new color, you may do so by calling the provided constructor. However, this is wasteful – there is, after all, a finite set of possible colors. Moreover, all of these colors already exist as members of the `COLORS` constant. That's all well and good if you have the raw color code, which can be used as an index for the `COLORS` array, but what if you don't? Well, then {::[]} is your new best friend! This method accepts the full range of color representations, including raw color codes, names, hex strings, and RGB
values. The following example showcases all of the various types that {::[]} accepts:
“`ruby # The following all return the 'black' color Color Color Color Color Color[[0, 0, 0]] Color[0, 0, 0]
# Malformed inputs Color # => ArgumentError: bad color code: -1 Color # => ArgumentError: bad color code: 256 Color # => nil Color # => ArgumentError: invalid hex string: # Color # => nil “`
Note that when getting a color from a name, `nil` will be returned if no color with that name exists, rather than throwing an error.
Attributes
@return [Boolean] the global default compliancy setting
@return [Integer] the color model of the color (either 8, 16, or 256)
@return [Boolean] the default compliancy setting for {#to_fg} and {#to_bg}
Public Class Methods
Get an existing Color
object representing the specified color code, hex value, color name, or RGB
array. @param value [String,Symbol,Array]
@return [Color]
# File lib/chromate/color.rb, line 125 def self.[](value, g = nil, b = nil) case value when Fixnum if value > 255 or value < 0 raise ArgumentError, "bad color code: #{value}" else COLORS[value] end when String if value[0] == '#' Color.from_hex(value) elsif COLOR_NAMES.key?(value.intern) COLORS[COLOR_NAMES[value.intern]] else nil end when Symbol if COLOR_NAMES.key?(value) COLORS[COLOR_NAMES[value]] else nil end when Array Color.from_rgb(*value) else if g.nil? and b.nil? raise TypeError, "don't know what to do with a #{value.class}" else Color.from_rgb(value, g, b) end end end
# File lib/chromate/color.rb, line 87 def compliant=(what) @compliant = what COLORS.each { |c| c.compliant = what } end
Get a Color
object representing the specified hex string. @param hex [String] a string of the form #XXXXXX
@return [Color]
# File lib/chromate/color.rb, line 163 def self.from_hex(hex) if Hex::PATTERN =~ hex Color.from_rgb(*hex.scan(Hex::BYTE).map { |b| b.to_i(16) }) else raise ArgumentError, "invalid hex string: #{hex}" end end
Get a Color
object representing the specified color in RGB
space. @note The `g` and `b` arguments default to the value passed in for `r`, so
that if you have a monochromatic color (e.g., r, g, and b are the same), you may pass a single value.
@param r [Integer] the red component @param g [Integer] the green component @param b [Integer] the blue component
@example Getting a monochromatic color
Color.from_rgb(0) # <=> Color.from_rgb(0, 0, 0)
@return [Color]
# File lib/chromate/color.rb, line 184 def self.from_rgb(r, g = r, b = r) ary = [r, g, b] if RGB::COLORS.include?(ary) COLORS[RGB::COLORS.index(ary)] elsif CACHE.include?(ary) CACHE[ary] else lab = RGB.to_lab(ary) code = Lab::COLORS.index(Lab::COLORS.sort do |lab1, lab2| Lab.difference(lab, lab1) <=> Lab.difference(lab, lab2) end.first) CACHE[ary] = COLORS[code] COLORS[code] end end
Create a new Color
object representing the specified color code. The color model will be inferred automatically based on the range of `value` (8 if it's less than 8, 16 if it's less than 16, and 256 otherwise). @param value [Numeric] the xterm color code of the new color
@return [Color]
# File lib/chromate/color.rb, line 101 def initialize(value, compliant = Color.compliant) if COLOR_NAMES.key?(value) super(COLOR_NAMES[value]) else super(value) end @compliant = compliant @escape = :fg @color_model = if @value < 8 8 elsif @value < 16 16 else 256 end end
Public Instance Methods
Convert to a Color
that escapes to a background by default.
@return [Color]
# File lib/chromate/color.rb, line 279 def as_bg @escape = :bg self end
Convert to a Color
that escapes to a foreground by default.
@return [Color]
# File lib/chromate/color.rb, line 241 def as_fg @escape = :fg self end
Does this color represent a background?
@return [Boolean]
# File lib/chromate/color.rb, line 288 def bg? @escape == :bg end
Get the blue component of the color.
@return [Integer]
# File lib/chromate/color.rb, line 361 def blue RGB::COLORS[@value][2] end
Escape as an SGR escape sequence.
@return [String] an escape sequence of the form “e[1;2;3;…m”
# File lib/chromate/color.rb, line 296 def escape case @escape when :fg to_fg when :bg to_bg end end
Does this color represent a foreground?
@return [Boolean]
# File lib/chromate/color.rb, line 250 def fg? @escape == :fg end
Get the green component of the color.
@return [Integer]
# File lib/chromate/color.rb, line 352 def green RGB::COLORS[@value][1] end
Is this color greyscale?
@return [Boolean]
# File lib/chromate/color.rb, line 212 def greyscale? GREYSCALE_COLORS.include?(self) end
# File lib/chromate/color.rb, line 315 def inspect if name.nil? "#<Color: (#{to_rgb.join(',')})>" else "#<Color: #{name}>" end end
Get the name of the color, if it exists.
@return [Symbol]
# File lib/chromate/color.rb, line 309 def name if COLOR_NAMES.value?(@value) COLOR_NAMES.find { |k, v| v == @value }.first end end
Get the red component of the color.
@return [Integer]
# File lib/chromate/color.rb, line 343 def red RGB::COLORS[@value][0] end
Is this color supported by the host environment?
@return [Boolean]
# File lib/chromate/color.rb, line 204 def supported? SUPPORTED_COLORS.include?(self) end
Escape as a background SGR escape sequence. @param compliant [Boolean] whether to use standards-compliant bold
sequences or the non-standard 100-107 range
@return [String] an escape sequence setting the foreground to the color
# File lib/chromate/color.rb, line 260 def to_bg(compliant = @compliant) case @color_model when 8 "\e[#{40 + @value}m" when 16 if compliant "\e[#{40 + @value - 8};1m" else "\e[#{100 + @value - 8}m" end when 256 "\e[48;5;#{@value}m" end end
Escape as a foreground SGR escape sequence. @param compliant [Boolean] whether to use standards-compliant bold
sequences or the non-standard 90-97 range
@return [String] an escape sequence setting the foreground to the color
# File lib/chromate/color.rb, line 222 def to_fg(compliant = @compliant) case @color_model when 8 "\e[#{30 + @value}m" when 16 if compliant "\e[#{30 + @value - 8};1m" else "\e[#{90 + @value - 8}m" end when 256 "\e[38;5;#{@value}m" end end
Get the hex value represented by the color.
@return [String] a string of the form #XXXXXX
# File lib/chromate/color.rb, line 335 def to_hex '#' + Hex::COLORS[@value] end
Get the RGB
value represented by the color.
@return [<Integer>] an array containing r, g, and b values, in that order
# File lib/chromate/color.rb, line 327 def to_rgb RGB::COLORS[@value] end