Module: Fatty::Color
- Defined in:
- lib/fatty/colors/color.rb
Constant Summary collapse
- DEFAULT_INDEX =
-1
- RGB_TXT_PATH =
Expected location for a bundled X11 rgb.txt (you provide it in the repo). Recommended path:
lib/fatty/color/rgb.txt File.("rgb.txt", __dir__)
- ANSI_NAMES =
ANSI 0..15 names (de-facto standard names)
{ "black" => 0, "red" => 1, "green" => 2, "yellow" => 3, "blue" => 4, "magenta" => 5, "cyan" => 6, "white" => 7, "bright_black" => 8, "bright_red" => 9, "bright_green" => 10, "bright_yellow" => 11, "bright_blue" => 12, "bright_magenta" => 13, "bright_cyan" => 14, "bright_white" => 15, "gray" => 8, "grey" => 8, "bright_gray" => 15, "bright_grey" => 15, "default" => DEFAULT_INDEX, }.freeze
- CANONICAL_ANSI_COLORS =
{ "black" => 0, "red" => 1, "green" => 2, "yellow" => 3, "blue" => 4, "magenta" => 5, "cyan" => 6, "white" => 7, "bright_black" => 8, "bright_red" => 9, "bright_green" => 10, "bright_yellow" => 11, "bright_blue" => 12, "bright_magenta" => 13, "bright_cyan" => 14, "bright_white" => 15, }.freeze
- ALIASES_256 =
Small alias set (xterm-256 indices). Keep this small + opinionated. Users can always use integers/hex/X11 names.
{ "navy" => 17, "dark_blue" => 18, "orange" => 208, "pink" => 205, "violet" => 141, "sky" => 117, "teal" => 37, "lime" => 118, "dark_grey" => 238, "dark_gray" => 238, "grey" => 244, "gray" => 244, "light_grey" => 250, "light_gray" => 250, }.freeze
- ANSI_RGB =
Approximate RGB for xterm-style ANSI 0..15. Used only when down-mapping to <=16 colors.
{ 0 => [0, 0, 0], 1 => [205, 0, 0], 2 => [0, 205, 0], 3 => [205, 205, 0], 4 => [0, 0, 238], 5 => [205, 0, 205], 6 => [0, 205, 205], 7 => [229, 229, 229], 8 => [127, 127, 127], 9 => [255, 0, 0], 10 => [0, 255, 0], 11 => [255, 255, 0], 12 => [92, 92, 255], 13 => [255, 0, 255], 14 => [0, 255, 255], 15 => [255, 255, 255], }.freeze
Class Method Summary collapse
- .ansi_colors ⇒ Object
- .clamp_index(idx, available_colors:) ⇒ Object
- .contrasting_ansi_index(index) ⇒ Object
- .contrasting_ansi_index_for_rgb(rgb) ⇒ Object
- .contrasting_rgb(rgb) ⇒ Object
- .hex_for_index(index) ⇒ Object
- .hex_for_rgb(rgb) ⇒ Object
- .luminance_for_rgb(rgb) ⇒ Object
-
.resolve(spec, available_colors: 256) ⇒ Object
Resolve a color specification to an integer color index suitable for curses init_pair.
- .rgb(spec) ⇒ Object
- .x11_colors ⇒ Object
- .x11_colors_by_name ⇒ Object
- .xterm_color_name(index) ⇒ Object
- .xterm_colors ⇒ Object
-
.xterm_index_for_rgb(r, g, b) ⇒ Object
Convert any RGB to an xterm-256 index (16..255).
- .xterm_rgb_for_index(idx) ⇒ Object
Class Method Details
.ansi_colors ⇒ Object
97 98 99 |
# File 'lib/fatty/colors/color.rb', line 97 def ansi_colors CANONICAL_ANSI_COLORS.dup end |
.clamp_index(idx, available_colors:) ⇒ Object
229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
# File 'lib/fatty/colors/color.rb', line 229 def clamp_index(idx, available_colors:) i = idx.to_i result = if available_colors.to_i <= 0 0 elsif available_colors.to_i <= 16 downmap_to_ansi16(i) else i.clamp(0, 255) end result end |
.contrasting_ansi_index(index) ⇒ Object
269 270 271 272 273 274 275 276 277 278 |
# File 'lib/fatty/colors/color.rb', line 269 def contrasting_ansi_index(index) rgb = xterm_rgb_for_index(index) fg = contrasting_rgb(rgb) if fg == [0, 0, 0] 0 else 15 end end |
.contrasting_ansi_index_for_rgb(rgb) ⇒ Object
244 245 246 247 |
# File 'lib/fatty/colors/color.rb', line 244 def contrasting_ansi_index_for_rgb(rgb) luminance = ((rgb[0] * 299) + (rgb[1] * 587) + (rgb[2] * 114)) / 1000 luminance >= 140 ? 0 : 15 end |
.contrasting_rgb(rgb) ⇒ Object
261 262 263 264 265 266 267 |
# File 'lib/fatty/colors/color.rb', line 261 def contrasting_rgb(rgb) if luminance_for_rgb(rgb) >= 140 [0, 0, 0] else [255, 255, 255] end end |
.hex_for_index(index) ⇒ Object
253 254 255 |
# File 'lib/fatty/colors/color.rb', line 253 def hex_for_index(index) hex_for_rgb(xterm_rgb_for_index(index)) end |
.hex_for_rgb(rgb) ⇒ Object
249 250 251 |
# File 'lib/fatty/colors/color.rb', line 249 def hex_for_rgb(rgb) format("#%02x%02x%02x", *rgb) end |
.luminance_for_rgb(rgb) ⇒ Object
257 258 259 |
# File 'lib/fatty/colors/color.rb', line 257 def luminance_for_rgb(rgb) ((rgb[0] * 299) + (rgb[1] * 587) + (rgb[2] * 114)) / 1000 end |
.resolve(spec, available_colors: 256) ⇒ Object
Resolve a color specification to an integer color index suitable for curses init_pair.
Accepts:
- Integer (e.g. 17, 226, -1)
- ANSI name (e.g. "yellow", "bright_blue", "default")
- Alias (e.g. "navy")
- Hex string (#RRGGBB or #RGB)
- X11 name (e.g. "MidnightBlue") resolved via bundled rgb.txt
available_colors:
- If <= 16, any resolved 256-color index is down-mapped to nearest ANSI 0..15.
- If > 16, returns xterm-256 indices 0..255 (or -1 for default).
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'lib/fatty/colors/color.rb', line 141 def resolve(spec, available_colors: 256) spec_norm = spec idx = if spec_norm.is_a?(Integer) spec_norm elsif spec_norm.nil? DEFAULT_INDEX else resolve_stringish(spec_norm.to_s, available_colors: available_colors) end if idx == DEFAULT_INDEX idx else clamp_index(idx, available_colors: available_colors) end end |
.rgb(spec) ⇒ Object
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/fatty/colors/color.rb', line 160 def rgb(spec) if spec.is_a?(Integer) xterm_rgb_for_index(spec) elsif spec.nil? nil else s = normalize_name(spec.to_s) if s.start_with?("ansi:") s = s.delete_prefix("ansi:") elsif s.start_with?("x11:") s = s.delete_prefix("x11:") end idx = ALIASES_256[s] if idx xterm_rgb_for_index(idx) else parsed = parse_hex(s) parsed ||= x11_rgb_for_name(s) parsed ||= begin ansi = ANSI_NAMES[s] xterm_rgb_for_index(ansi) unless ansi == DEFAULT_INDEX || ansi.nil? end parsed end end end |
.x11_colors ⇒ Object
107 108 109 |
# File 'lib/fatty/colors/color.rb', line 107 def x11_colors x11_table.dup end |
.x11_colors_by_name ⇒ Object
111 112 113 114 115 |
# File 'lib/fatty/colors/color.rb', line 111 def x11_colors_by_name x11_colors.sort_by do |name, _rgb| color_name_sort_key(name) end.to_h end |
.xterm_color_name(index) ⇒ Object
117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/fatty/colors/color.rb', line 117 def xterm_color_name(index) if index.between?(0, 15) CANONICAL_ANSI_COLORS.key(index) elsif index.between?(16, 231) "xterm_#{index}" elsif index.between?(232, 255) "gray_#{index}" else "color_#{index}" end end |
.xterm_colors ⇒ Object
101 102 103 104 105 |
# File 'lib/fatty/colors/color.rb', line 101 def xterm_colors (0..255).to_h do |index| [xterm_color_name(index), index] end end |
.xterm_index_for_rgb(r, g, b) ⇒ Object
Convert any RGB to an xterm-256 index (16..255). We consider both the 6x6x6 cube and the grayscale ramp and pick the closer.
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
# File 'lib/fatty/colors/color.rb', line 191 def xterm_index_for_rgb(r, g, b) rr = clamp_byte(r) gg = clamp_byte(g) bb = clamp_byte(b) cube = xterm_cube_index(rr, gg, bb) gray = xterm_gray_index(rr, gg, bb) cube_rgb = xterm_rgb_for_index(cube) gray_rgb = xterm_rgb_for_index(gray) if dist2(rr, gg, bb, gray_rgb[0], gray_rgb[1], gray_rgb[2]) < dist2(rr, gg, bb, cube_rgb[0], cube_rgb[1], cube_rgb[2]) gray else cube end end |
.xterm_rgb_for_index(idx) ⇒ Object
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
# File 'lib/fatty/colors/color.rb', line 210 def xterm_rgb_for_index(idx) i = idx.to_i if i.between?(232, 255) v = 8 + (i - 232) * 10 [v, v, v] elsif i.between?(16, 231) j = i - 16 r = j / 36 g = (j % 36) / 6 b = j % 6 levels = [0, 95, 135, 175, 215, 255] [levels[r], levels[g], levels[b]] else # for 0..15 we use ANSI_RGB as an approximation ANSI_RGB[i] || [0, 0, 0] end end |