2020import java .awt .color .ColorSpace ;
2121import java .util .Collections ;
2222import java .util .EnumMap ;
23+ import java .util .LinkedHashMap ;
2324import java .util .Map ;
24- import java .util .Map .Entry ;
2525
2626import org .springframework .util .Assert ;
2727
3636 */
3737public final class AnsiColors {
3838
39- private static final Map <AnsiColor , LabColor > ANSI_COLOR_MAP ;
39+ private static final Map <AnsiElement , LabColor > ANSI_COLOR_MAP ;
4040
4141 static {
4242 Map <AnsiColor , LabColor > colorMap = new EnumMap <>(AnsiColor .class );
@@ -59,24 +59,86 @@ public final class AnsiColors {
5959 ANSI_COLOR_MAP = Collections .unmodifiableMap (colorMap );
6060 }
6161
62- private AnsiColors () {
62+ private static final int [] ANSI_8BIT_COLOR_CODE_LOOKUP = new int [] { 0x000000 , 0x800000 , 0x008000 , 0x808000 ,
63+ 0x000080 , 0x800080 , 0x008080 , 0xc0c0c0 , 0x808080 , 0xff0000 , 0x00ff00 , 0xffff00 , 0x0000ff , 0xff00ff ,
64+ 0x00ffff , 0xffffff , 0x000000 , 0x00005f , 0x000087 , 0x0000af , 0x0000d7 , 0x0000ff , 0x005f00 , 0x005f5f ,
65+ 0x005f87 , 0x005faf , 0x005fd7 , 0x005fff , 0x008700 , 0x00875f , 0x008787 , 0x0087af , 0x0087d7 , 0x0087ff ,
66+ 0x00af00 , 0x00af5f , 0x00af87 , 0x00afaf , 0x00afd7 , 0x00afff , 0x00d700 , 0x00d75f , 0x00d787 , 0x00d7af ,
67+ 0x00d7d7 , 0x00d7ff , 0x00ff00 , 0x00ff5f , 0x00ff87 , 0x00ffaf , 0x00ffd7 , 0x00ffff , 0x5f0000 , 0x5f005f ,
68+ 0x5f0087 , 0x5f00af , 0x5f00d7 , 0x5f00ff , 0x5f5f00 , 0x5f5f5f , 0x5f5f87 , 0x5f5faf , 0x5f5fd7 , 0x5f5fff ,
69+ 0x5f8700 , 0x5f875f , 0x5f8787 , 0x5f87af , 0x5f87d7 , 0x5f87ff , 0x5faf00 , 0x5faf5f , 0x5faf87 , 0x5fafaf ,
70+ 0x5fafd7 , 0x5fafff , 0x5fd700 , 0x5fd75f , 0x5fd787 , 0x5fd7af , 0x5fd7d7 , 0x5fd7ff , 0x5fff00 , 0x5fff5f ,
71+ 0x5fff87 , 0x5fffaf , 0x5fffd7 , 0x5fffff , 0x870000 , 0x87005f , 0x870087 , 0x8700af , 0x8700d7 , 0x8700ff ,
72+ 0x875f00 , 0x875f5f , 0x875f87 , 0x875faf , 0x875fd7 , 0x875fff , 0x878700 , 0x87875f , 0x878787 , 0x8787af ,
73+ 0x8787d7 , 0x8787ff , 0x87af00 , 0x87af5f , 0x87af87 , 0x87afaf , 0x87afd7 , 0x87afff , 0x87d700 , 0x87d75f ,
74+ 0x87d787 , 0x87d7af , 0x87d7d7 , 0x87d7ff , 0x87ff00 , 0x87ff5f , 0x87ff87 , 0x87ffaf , 0x87ffd7 , 0x87ffff ,
75+ 0xaf0000 , 0xaf005f , 0xaf0087 , 0xaf00af , 0xaf00d7 , 0xaf00ff , 0xaf5f00 , 0xaf5f5f , 0xaf5f87 , 0xaf5faf ,
76+ 0xaf5fd7 , 0xaf5fff , 0xaf8700 , 0xaf875f , 0xaf8787 , 0xaf87af , 0xaf87d7 , 0xaf87ff , 0xafaf00 , 0xafaf5f ,
77+ 0xafaf87 , 0xafafaf , 0xafafd7 , 0xafafff , 0xafd700 , 0xafd75f , 0xafd787 , 0xafd7af , 0xafd7d7 , 0xafd7ff ,
78+ 0xafff00 , 0xafff5f , 0xafff87 , 0xafffaf , 0xafffd7 , 0xafffff , 0xd70000 , 0xd7005f , 0xd70087 , 0xd700af ,
79+ 0xd700d7 , 0xd700ff , 0xd75f00 , 0xd75f5f , 0xd75f87 , 0xd75faf , 0xd75fd7 , 0xd75fff , 0xd78700 , 0xd7875f ,
80+ 0xd78787 , 0xd787af , 0xd787d7 , 0xd787ff , 0xd7af00 , 0xd7af5f , 0xd7af87 , 0xd7afaf , 0xd7afd7 , 0xd7afff ,
81+ 0xd7d700 , 0xd7d75f , 0xd7d787 , 0xd7d7af , 0xd7d7d7 , 0xd7d7ff , 0xd7ff00 , 0xd7ff5f , 0xd7ff87 , 0xd7ffaf ,
82+ 0xd7ffd7 , 0xd7ffff , 0xff0000 , 0xff005f , 0xff0087 , 0xff00af , 0xff00d7 , 0xff00ff , 0xff5f00 , 0xff5f5f ,
83+ 0xff5f87 , 0xff5faf , 0xff5fd7 , 0xff5fff , 0xff8700 , 0xff875f , 0xff8787 , 0xff87af , 0xff87d7 , 0xff87ff ,
84+ 0xffaf00 , 0xffaf5f , 0xffaf87 , 0xffafaf , 0xffafd7 , 0xffafff , 0xffd700 , 0xffd75f , 0xffd787 , 0xffd7af ,
85+ 0xffd7d7 , 0xffd7ff , 0xffff00 , 0xffff5f , 0xffff87 , 0xffffaf , 0xffffd7 , 0xffffff , 0x080808 , 0x121212 ,
86+ 0x1c1c1c , 0x262626 , 0x303030 , 0x3a3a3a , 0x444444 , 0x4e4e4e , 0x585858 , 0x626262 , 0x6c6c6c , 0x767676 ,
87+ 0x808080 , 0x8a8a8a , 0x949494 , 0x9e9e9e , 0xa8a8a8 , 0xb2b2b2 , 0xbcbcbc , 0xc6c6c6 , 0xd0d0d0 , 0xdadada ,
88+ 0xe4e4e4 , 0xeeeeee };
89+
90+ private final Map <AnsiElement , LabColor > lookup ;
91+
92+ /**
93+ * Create a new {@link AnsiColors} instance with the specified bit depth.
94+ * @param bitDepth the required bit depth
95+ */
96+ public AnsiColors (BitDepth bitDepth ) {
97+ this .lookup = getLookup (bitDepth );
6398 }
6499
65- public static AnsiColor getClosest (Color color ) {
66- return getClosest (new LabColor (color ));
100+ private Map <AnsiElement , LabColor > getLookup (BitDepth bitDepth ) {
101+ if (bitDepth == BitDepth .EIGHT ) {
102+ Map <Ansi8BitColor , LabColor > lookup = new LinkedHashMap <>();
103+ for (int i = 0 ; i < ANSI_8BIT_COLOR_CODE_LOOKUP .length ; i ++) {
104+ lookup .put (Ansi8BitColor .foreground (i ), new LabColor (ANSI_8BIT_COLOR_CODE_LOOKUP [i ]));
105+ }
106+ return Collections .unmodifiableMap (lookup );
107+ }
108+ return ANSI_COLOR_MAP ;
109+ }
110+
111+ /**
112+ * Find the closest {@link AnsiElement ANSI color} to the given AWT {@link Color}.
113+ * @param color the AWT color
114+ * @return the closest ANSI color
115+ */
116+ public AnsiElement findClosest (Color color ) {
117+ return findClosest (new LabColor (color ));
67118 }
68119
69- private static AnsiColor getClosest (LabColor color ) {
70- AnsiColor result = null ;
71- double resultDistance = Float .MAX_VALUE ;
72- for (Entry <AnsiColor , LabColor > entry : ANSI_COLOR_MAP .entrySet ()) {
73- double distance = color .getDistance (entry .getValue ());
74- if (result == null || distance < resultDistance ) {
75- resultDistance = distance ;
76- result = entry .getKey ();
120+ private AnsiElement findClosest (LabColor color ) {
121+ AnsiElement closest = null ;
122+ double closestDistance = Float .MAX_VALUE ;
123+ for (Map . Entry <AnsiElement , LabColor > entry : this . lookup .entrySet ()) {
124+ double candidateDistance = color .getDistance (entry .getValue ());
125+ if (closest == null || candidateDistance < closestDistance ) {
126+ closestDistance = candidateDistance ;
127+ closest = entry .getKey ();
77128 }
78129 }
79- return result ;
130+ return closest ;
131+ }
132+
133+ /**
134+ * Get the closest {@link AnsiColor ANSI color} to the given AWT {@link Color}.
135+ * @param color the color to find
136+ * @return the closest color
137+ * @deprecated since 2.2.0 in favor of {@link #findClosest(Color)}
138+ */
139+ @ Deprecated
140+ public static AnsiColor getClosest (Color color ) {
141+ return (AnsiColor ) new AnsiColors (BitDepth .FOUR ).findClosest (color );
80142 }
81143
82144 /**
@@ -132,4 +194,38 @@ private double f(double t) {
132194
133195 }
134196
197+ /**
198+ * Bit depths supported by this class.
199+ */
200+ public enum BitDepth {
201+
202+ /**
203+ * 4 bits (16 color).
204+ * @see AnsiColor
205+ */
206+ FOUR (4 ),
207+
208+ /**
209+ * 8 bits (256 color).
210+ * @see Ansi8BitColor
211+ */
212+ EIGHT (8 );
213+
214+ private final int bits ;
215+
216+ BitDepth (int bits ) {
217+ this .bits = bits ;
218+ }
219+
220+ public static BitDepth of (int bits ) {
221+ for (BitDepth candidate : values ()) {
222+ if (candidate .bits == bits ) {
223+ return candidate ;
224+ }
225+ }
226+ throw new IllegalArgumentException ("Unsupported ANSI bit depth '" + bits + "'" );
227+ }
228+
229+ }
230+
135231}
0 commit comments