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}