/* * Copyright 2012-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.boot.ansi; import java.awt.Color; import java.awt.color.ColorSpace; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import org.springframework.util.Assert; /** * Utility for working with {@link AnsiColor} in the context of {@link Color AWT Colors}. * * @author Craig Burke * @author Ruben Dijkstra * @author Phillip Webb * @author Michael Simons * @since 1.4.0 */ public final class AnsiColors { private static final Map<AnsiColor, LabColor> ANSI_COLOR_MAP; static { Map<AnsiColor, LabColor> colorMap = new LinkedHashMap<>(); colorMap.put(AnsiColor.BLACK, new LabColor(0x000000)); colorMap.put(AnsiColor.RED, new LabColor(0xAA0000)); colorMap.put(AnsiColor.GREEN, new LabColor(0x00AA00)); colorMap.put(AnsiColor.YELLOW, new LabColor(0xAA5500)); colorMap.put(AnsiColor.BLUE, new LabColor(0x0000AA)); colorMap.put(AnsiColor.MAGENTA, new LabColor(0xAA00AA)); colorMap.put(AnsiColor.CYAN, new LabColor(0x00AAAA)); colorMap.put(AnsiColor.WHITE, new LabColor(0xAAAAAA)); colorMap.put(AnsiColor.BRIGHT_BLACK, new LabColor(0x555555)); colorMap.put(AnsiColor.BRIGHT_RED, new LabColor(0xFF5555)); colorMap.put(AnsiColor.BRIGHT_GREEN, new LabColor(0x55FF00)); colorMap.put(AnsiColor.BRIGHT_YELLOW, new LabColor(0xFFFF55)); colorMap.put(AnsiColor.BRIGHT_BLUE, new LabColor(0x5555FF)); colorMap.put(AnsiColor.BRIGHT_MAGENTA, new LabColor(0xFF55FF)); colorMap.put(AnsiColor.BRIGHT_CYAN, new LabColor(0x55FFFF)); colorMap.put(AnsiColor.BRIGHT_WHITE, new LabColor(0xFFFFFF)); ANSI_COLOR_MAP = Collections.unmodifiableMap(colorMap); } private AnsiColors() { } public static AnsiColor getClosest(Color color) { return getClosest(new LabColor(color)); } private static AnsiColor getClosest(LabColor color) { AnsiColor result = null; double resultDistance = Float.MAX_VALUE; for (Entry<AnsiColor, LabColor> entry : ANSI_COLOR_MAP.entrySet()) { double distance = color.getDistance(entry.getValue()); if (result == null || distance < resultDistance) { resultDistance = distance; result = entry.getKey(); } } return result; } /** * Represents a color stored in LAB form. */ private static final class LabColor { private static final ColorSpace XYZ_COLOR_SPACE = ColorSpace .getInstance(ColorSpace.CS_CIEXYZ); private final double l; private final double a; private final double b; LabColor(Integer rgb) { this(rgb == null ? (Color) null : new Color(rgb)); } LabColor(Color color) { Assert.notNull(color, "Color must not be null"); float[] lab = fromXyz(color.getColorComponents(XYZ_COLOR_SPACE, null)); this.l = lab[0]; this.a = lab[1]; this.b = lab[2]; } private float[] fromXyz(float[] xyz) { return fromXyz(xyz[0], xyz[1], xyz[2]); } private float[] fromXyz(float x, float y, float z) { double l = (f(y) - 16.0) * 116.0; double a = (f(x) - f(y)) * 500.0; double b = (f(y) - f(z)) * 200.0; return new float[] { (float) l, (float) a, (float) b }; } private double f(double t) { return (t > (216.0 / 24389.0) ? Math.cbrt(t) : (1.0 / 3.0) * Math.pow(29.0 / 6.0, 2) * t + (4.0 / 29.0)); } // See http://en.wikipedia.org/wiki/Color_difference#CIE94 public double getDistance(LabColor other) { double c1 = Math.sqrt(this.a * this.a + this.b * this.b); double deltaC = c1 - Math.sqrt(other.a * other.a + other.b * other.b); double deltaA = this.a - other.a; double deltaB = this.b - other.b; double deltaH = Math.sqrt( Math.max(0.0, deltaA * deltaA + deltaB * deltaB - deltaC * deltaC)); return Math.sqrt(Math.max(0.0, Math.pow((this.l - other.l) / (1.0), 2) + Math.pow(deltaC / (1 + 0.045 * c1), 2) + Math.pow(deltaH / (1 + 0.015 * c1), 2.0))); } } }