001/* 002 * Copyright 2012-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.ansi; 018 019import java.awt.Color; 020import java.awt.color.ColorSpace; 021import java.util.Collections; 022import java.util.EnumMap; 023import java.util.Map; 024import java.util.Map.Entry; 025 026import org.springframework.util.Assert; 027 028/** 029 * Utility for working with {@link AnsiColor} in the context of {@link Color AWT Colors}. 030 * 031 * @author Craig Burke 032 * @author Ruben Dijkstra 033 * @author Phillip Webb 034 * @author Michael Simons 035 * @since 1.4.0 036 */ 037public final class AnsiColors { 038 039 private static final Map<AnsiColor, LabColor> ANSI_COLOR_MAP; 040 041 static { 042 Map<AnsiColor, LabColor> colorMap = new EnumMap<>(AnsiColor.class); 043 colorMap.put(AnsiColor.BLACK, new LabColor(0x000000)); 044 colorMap.put(AnsiColor.RED, new LabColor(0xAA0000)); 045 colorMap.put(AnsiColor.GREEN, new LabColor(0x00AA00)); 046 colorMap.put(AnsiColor.YELLOW, new LabColor(0xAA5500)); 047 colorMap.put(AnsiColor.BLUE, new LabColor(0x0000AA)); 048 colorMap.put(AnsiColor.MAGENTA, new LabColor(0xAA00AA)); 049 colorMap.put(AnsiColor.CYAN, new LabColor(0x00AAAA)); 050 colorMap.put(AnsiColor.WHITE, new LabColor(0xAAAAAA)); 051 colorMap.put(AnsiColor.BRIGHT_BLACK, new LabColor(0x555555)); 052 colorMap.put(AnsiColor.BRIGHT_RED, new LabColor(0xFF5555)); 053 colorMap.put(AnsiColor.BRIGHT_GREEN, new LabColor(0x55FF00)); 054 colorMap.put(AnsiColor.BRIGHT_YELLOW, new LabColor(0xFFFF55)); 055 colorMap.put(AnsiColor.BRIGHT_BLUE, new LabColor(0x5555FF)); 056 colorMap.put(AnsiColor.BRIGHT_MAGENTA, new LabColor(0xFF55FF)); 057 colorMap.put(AnsiColor.BRIGHT_CYAN, new LabColor(0x55FFFF)); 058 colorMap.put(AnsiColor.BRIGHT_WHITE, new LabColor(0xFFFFFF)); 059 ANSI_COLOR_MAP = Collections.unmodifiableMap(colorMap); 060 } 061 062 private AnsiColors() { 063 } 064 065 public static AnsiColor getClosest(Color color) { 066 return getClosest(new LabColor(color)); 067 } 068 069 private static AnsiColor getClosest(LabColor color) { 070 AnsiColor result = null; 071 double resultDistance = Float.MAX_VALUE; 072 for (Entry<AnsiColor, LabColor> entry : ANSI_COLOR_MAP.entrySet()) { 073 double distance = color.getDistance(entry.getValue()); 074 if (result == null || distance < resultDistance) { 075 resultDistance = distance; 076 result = entry.getKey(); 077 } 078 } 079 return result; 080 } 081 082 /** 083 * Represents a color stored in LAB form. 084 */ 085 private static final class LabColor { 086 087 private static final ColorSpace XYZ_COLOR_SPACE = ColorSpace 088 .getInstance(ColorSpace.CS_CIEXYZ); 089 090 private final double l; 091 092 private final double a; 093 094 private final double b; 095 096 LabColor(Integer rgb) { 097 this((rgb != null) ? new Color(rgb) : null); 098 } 099 100 LabColor(Color color) { 101 Assert.notNull(color, "Color must not be null"); 102 float[] lab = fromXyz(color.getColorComponents(XYZ_COLOR_SPACE, null)); 103 this.l = lab[0]; 104 this.a = lab[1]; 105 this.b = lab[2]; 106 } 107 108 private float[] fromXyz(float[] xyz) { 109 return fromXyz(xyz[0], xyz[1], xyz[2]); 110 } 111 112 private float[] fromXyz(float x, float y, float z) { 113 double l = (f(y) - 16.0) * 116.0; 114 double a = (f(x) - f(y)) * 500.0; 115 double b = (f(y) - f(z)) * 200.0; 116 return new float[] { (float) l, (float) a, (float) b }; 117 } 118 119 private double f(double t) { 120 return (t > (216.0 / 24389.0)) ? Math.cbrt(t) 121 : (1.0 / 3.0) * Math.pow(29.0 / 6.0, 2) * t + (4.0 / 29.0); 122 } 123 124 // See http://en.wikipedia.org/wiki/Color_difference#CIE94 125 public double getDistance(LabColor other) { 126 double c1 = Math.sqrt(this.a * this.a + this.b * this.b); 127 double deltaC = c1 - Math.sqrt(other.a * other.a + other.b * other.b); 128 double deltaA = this.a - other.a; 129 double deltaB = this.b - other.b; 130 double deltaH = Math.sqrt( 131 Math.max(0.0, deltaA * deltaA + deltaB * deltaB - deltaC * deltaC)); 132 return Math.sqrt(Math.max(0.0, 133 Math.pow((this.l - other.l) / (1.0), 2) 134 + Math.pow(deltaC / (1 + 0.045 * c1), 2) 135 + Math.pow(deltaH / (1 + 0.015 * c1), 2.0))); 136 } 137 138 } 139 140}