001/* 002 * Copyright (c) 2002-2018, the original author or authors. 003 * 004 * This software is distributable under the BSD license. See the terms of the 005 * BSD license in the documentation provided with this software. 006 * 007 * https://opensource.org/licenses/BSD-3-Clause 008 */ 009package org.fusesource.jansi.io; 010 011/** 012 * Helper class for dealing with color rounding. 013 * This is a simplified version of the JLine's one at 014 * https://github.com/jline/jline3/blob/a24636dc5de83baa6b65049e8215fb372433b3b1/terminal/src/main/java/org/jline/utils/Colors.java 015 */ 016public class Colors { 017 018 /** 019 * Default 256 colors palette 020 */ 021 public static final int[] DEFAULT_COLORS_256 = { 022 // 16 ansi 023 0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, 0x008080, 0xc0c0c0, 024 0x808080, 0xff0000, 0x00ff00, 0xffff00, 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff, 025 026 // 6x6x6 color cube 027 0x000000, 0x00005f, 0x000087, 0x0000af, 0x0000d7, 0x0000ff, 028 0x005f00, 0x005f5f, 0x005f87, 0x005faf, 0x005fd7, 0x005fff, 029 0x008700, 0x00875f, 0x008787, 0x0087af, 0x0087d7, 0x0087ff, 030 0x00af00, 0x00af5f, 0x00af87, 0x00afaf, 0x00afd7, 0x00afff, 031 0x00d700, 0x00d75f, 0x00d787, 0x00d7af, 0x00d7d7, 0x00d7ff, 032 0x00ff00, 0x00ff5f, 0x00ff87, 0x00ffaf, 0x00ffd7, 0x00ffff, 033 034 0x5f0000, 0x5f005f, 0x5f0087, 0x5f00af, 0x5f00d7, 0x5f00ff, 035 0x5f5f00, 0x5f5f5f, 0x5f5f87, 0x5f5faf, 0x5f5fd7, 0x5f5fff, 036 0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af, 0x5f87d7, 0x5f87ff, 037 0x5faf00, 0x5faf5f, 0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff, 038 0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af, 0x5fd7d7, 0x5fd7ff, 039 0x5fff00, 0x5fff5f, 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff, 040 041 0x870000, 0x87005f, 0x870087, 0x8700af, 0x8700d7, 0x8700ff, 042 0x875f00, 0x875f5f, 0x875f87, 0x875faf, 0x875fd7, 0x875fff, 043 0x878700, 0x87875f, 0x878787, 0x8787af, 0x8787d7, 0x8787ff, 044 0x87af00, 0x87af5f, 0x87af87, 0x87afaf, 0x87afd7, 0x87afff, 045 0x87d700, 0x87d75f, 0x87d787, 0x87d7af, 0x87d7d7, 0x87d7ff, 046 0x87ff00, 0x87ff5f, 0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, 047 048 0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af, 0xaf00d7, 0xaf00ff, 049 0xaf5f00, 0xaf5f5f, 0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff, 050 0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, 0xaf87d7, 0xaf87ff, 051 0xafaf00, 0xafaf5f, 0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, 052 0xafd700, 0xafd75f, 0xafd787, 0xafd7af, 0xafd7d7, 0xafd7ff, 053 0xafff00, 0xafff5f, 0xafff87, 0xafffaf, 0xafffd7, 0xafffff, 054 055 0xd70000, 0xd7005f, 0xd70087, 0xd700af, 0xd700d7, 0xd700ff, 056 0xd75f00, 0xd75f5f, 0xd75f87, 0xd75faf, 0xd75fd7, 0xd75fff, 057 0xd78700, 0xd7875f, 0xd78787, 0xd787af, 0xd787d7, 0xd787ff, 058 0xd7af00, 0xd7af5f, 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff, 059 0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af, 0xd7d7d7, 0xd7d7ff, 060 0xd7ff00, 0xd7ff5f, 0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, 061 062 0xff0000, 0xff005f, 0xff0087, 0xff00af, 0xff00d7, 0xff00ff, 063 0xff5f00, 0xff5f5f, 0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff, 064 0xff8700, 0xff875f, 0xff8787, 0xff87af, 0xff87d7, 0xff87ff, 065 0xffaf00, 0xffaf5f, 0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, 066 0xffd700, 0xffd75f, 0xffd787, 0xffd7af, 0xffd7d7, 0xffd7ff, 067 0xffff00, 0xffff5f, 0xffff87, 0xffffaf, 0xffffd7, 0xffffff, 068 069 // 24 grey ramp 070 0x080808, 0x121212, 0x1c1c1c, 0x262626, 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e, 071 0x585858, 0x626262, 0x6c6c6c, 0x767676, 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e, 072 0xa8a8a8, 0xb2b2b2, 0xbcbcbc, 0xc6c6c6, 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee, 073 }; 074 075 public static int roundColor(int col, int max) { 076 if (col >= max) { 077 int c = DEFAULT_COLORS_256[col]; 078 col = roundColor(c, DEFAULT_COLORS_256, max); 079 } 080 return col; 081 } 082 083 public static int roundRgbColor(int r, int g, int b, int max) { 084 return roundColor((r << 16) + (g << 8) + b, DEFAULT_COLORS_256, max); 085 } 086 087 private static int roundColor(int color, int[] colors, int max) { 088 double best_distance = Integer.MAX_VALUE; 089 int best_index = Integer.MAX_VALUE; 090 for (int idx = 0; idx < max; idx++) { 091 double d = cie76(color, colors[idx]); 092 if (d <= best_distance) { 093 best_index = idx; 094 best_distance = d; 095 } 096 } 097 return best_index; 098 } 099 100 private static double cie76(int c1, int c2) { 101 return scalar(rgb2cielab(c1), rgb2cielab(c2)); 102 } 103 104 private static double scalar(double[] c1, double[] c2) { 105 return sqr(c1[0] - c2[0]) 106 + sqr(c1[1] - c2[1]) 107 + sqr(c1[2] - c2[2]); 108 } 109 110 private static double[] rgb(int color) { 111 int r = (color >> 16) & 0xFF; 112 int g = (color >> 8) & 0xFF; 113 int b = (color >> 0) & 0xFF; 114 return new double[] { r / 255.0, g / 255.0, b / 255.0 }; 115 } 116 117 private static double[] rgb2cielab(int color) { 118 return rgb2cielab(rgb(color)); 119 } 120 121 private static double[] rgb2cielab(double[] rgb) { 122 return xyz2lab(rgb2xyz(rgb)); 123 } 124 125 private static double[] rgb2xyz(double[] rgb) { 126 double vr = pivotRgb(rgb[0]); 127 double vg = pivotRgb(rgb[1]); 128 double vb = pivotRgb(rgb[2]); 129 // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html 130 double x = vr * 0.4124564 + vg * 0.3575761 + vb * 0.1804375; 131 double y = vr * 0.2126729 + vg * 0.7151522 + vb * 0.0721750; 132 double z = vr * 0.0193339 + vg * 0.1191920 + vb * 0.9503041; 133 return new double[] { x, y, z }; 134 } 135 136 private static double pivotRgb(double n) { 137 return n > 0.04045 ? Math.pow((n + 0.055) / 1.055, 2.4) : n / 12.92; 138 } 139 140 private static double[] xyz2lab(double[] xyz) { 141 double fx = pivotXyz(xyz[0]); 142 double fy = pivotXyz(xyz[1]); 143 double fz = pivotXyz(xyz[2]); 144 double l = 116.0 * fy - 16.0; 145 double a = 500.0 * (fx - fy); 146 double b = 200.0 * (fy - fz); 147 return new double[] { l, a, b }; 148 } 149 150 private static final double epsilon = 216.0 / 24389.0; 151 private static final double kappa = 24389.0 / 27.0; 152 private static double pivotXyz(double n) { 153 return n > epsilon ? Math.cbrt(n) : (kappa * n + 16) / 116; 154 } 155 156 private static double sqr(double n) { 157 return n * n; 158 } 159 160}