2
* Copyright (c) 2005-2010 Substance Kirill Grouchnikov. All Rights Reserved.
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions are met:
7
* o Redistributions of source code must retain the above copyright notice,
8
* this list of conditions and the following disclaimer.
10
* o Redistributions in binary form must reproduce the above copyright notice,
11
* this list of conditions and the following disclaimer in the documentation
12
* and/or other materials provided with the distribution.
14
* o Neither the name of Substance Kirill Grouchnikov nor the names of
15
* its contributors may be used to endorse or promote products derived
16
* from this software without specific prior written permission.
18
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
22
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
package tools.uidebug;
32
import java.awt.Color;
34
import org.pushingpixels.substance.api.SubstanceColorScheme;
35
import org.pushingpixels.substance.api.colorscheme.BaseColorScheme;
38
* Base class for color schemes simulating color-blind users.
40
* @author Kirill Grouchnikov
42
public class ColorBlindColorScheme extends BaseColorScheme {
44
* Matrix for converting RGB to LMS.
46
public double[][] _rgbToLms = { { 0.05059983, 0.08585369, 0.00952420 },
47
{ 0.01893033, 0.08925308, 0.01370054 },
48
{ 0.00292202, 0.00975732, 0.07145979 } };
51
* Matrix for converting LMS to RGB.
53
public double[][] _lmsToRgb = { { 30.830854, -29.832659, 1.610474 },
54
{ -6.481468, 17.715578, -2.532642 },
55
{ -0.375690, -1.199062, 14.273846 } };
58
* The main ultra-light color.
60
private Color mainUltraLightColor;
63
* The main extra-light color.
65
private Color mainExtraLightColor;
68
* The main light color.
70
private Color mainLightColor;
73
* The main medium color.
75
private Color mainMidColor;
78
* The main dark color.
80
private Color mainDarkColor;
83
* The main ultra-dark color.
85
private Color mainUltraDarkColor;
88
* The foreground color.
90
private Color foregroundColor;
93
* The original color scheme.
95
private SubstanceColorScheme origScheme;
100
* @author Kirill Grouchnikov
102
public enum BlindnessKind {
104
* Protanopia color blindness.
109
* Deuteranopia color blindness.
114
* Tritanopia color blindness.
120
* Creates a new color scheme that simulates color-blindness.
123
* Original color scheme.
125
* Color-blindness kind.
127
public ColorBlindColorScheme(SubstanceColorScheme origScheme,
128
BlindnessKind kind) {
129
super(kind.name() + " " + origScheme.getDisplayName(), origScheme
131
this.origScheme = origScheme;
132
this.foregroundColor = getColorBlindColor(origScheme
133
.getForegroundColor(), _rgbToLms, kind, _lmsToRgb);
134
this.mainUltraDarkColor = getColorBlindColor(origScheme
135
.getUltraDarkColor(), _rgbToLms, kind, _lmsToRgb);
136
this.mainDarkColor = getColorBlindColor(origScheme.getDarkColor(),
137
_rgbToLms, kind, _lmsToRgb);
138
this.mainMidColor = getColorBlindColor(origScheme.getMidColor(),
139
_rgbToLms, kind, _lmsToRgb);
140
this.mainLightColor = getColorBlindColor(origScheme.getLightColor(),
141
_rgbToLms, kind, _lmsToRgb);
142
this.mainExtraLightColor = getColorBlindColor(origScheme
143
.getExtraLightColor(), _rgbToLms, kind, _lmsToRgb);
144
this.mainUltraLightColor = getColorBlindColor(origScheme
145
.getUltraLightColor(), _rgbToLms, kind, _lmsToRgb);
151
* @see org.pushingpixels.substance.color.ColorScheme#getForegroundColor()
154
public Color getForegroundColor() {
155
return this.foregroundColor;
161
* @see org.pushingpixels.substance.color.ColorScheme#getUltraLightColor()
164
public Color getUltraLightColor() {
165
return this.mainUltraLightColor;
171
* @see org.pushingpixels.substance.color.ColorScheme#getExtraLightColor()
174
public Color getExtraLightColor() {
175
return this.mainExtraLightColor;
181
* @see org.pushingpixels.substance.color.ColorScheme#getLightColor()
184
public Color getLightColor() {
185
return this.mainLightColor;
191
* @see org.pushingpixels.substance.color.ColorScheme#getMidColor()
194
public Color getMidColor() {
195
return this.mainMidColor;
201
* @see org.pushingpixels.substance.color.ColorScheme#getDarkColor()
204
public Color getDarkColor() {
205
return this.mainDarkColor;
211
* @see org.pushingpixels.substance.color.ColorScheme#getUltraDarkColor()
214
public Color getUltraDarkColor() {
215
return this.mainUltraDarkColor;
219
* Returns the original color scheme.
221
* @return The original color scheme.
223
public SubstanceColorScheme getOrigScheme() {
224
return this.origScheme;
228
* Converts the specified color into color-blind version.
231
* The original color.
233
* RGB to LMS conversion matrix.
235
* Color-blindness kind.
237
* LMS to RGB conversion matrix.
238
* @return Color-blind version of the original color.
240
private static Color getColorBlindColor(Color orig, double[][] rgbToLms,
241
BlindnessKind kind, double[][] lmsToRgb) {
242
double r = orig.getRed();
243
double g = orig.getGreen();
244
double b = orig.getBlue();
246
double[] rgbOrig = new double[] { r, g, b };
247
double[] lms = mult3(rgbToLms, rgbOrig);
250
double[] anchor = { 0.08008, 0.1579, 0.5897, 0.1284, 0.2237, 0.3636,
251
0.9856, 0.7325, 0.001079, 0.0914, 0.007009, 0.0 };
253
double[] rgbAnchor = {
254
rgbToLms[0][0] + rgbToLms[0][1] + rgbToLms[0][2],
255
rgbToLms[1][0] + rgbToLms[1][1] + rgbToLms[1][2],
256
rgbToLms[2][0] + rgbToLms[2][1] + rgbToLms[2][2] };
258
double a1, a2, b1, b2, c1, c2, inflection;
262
a1 = rgbAnchor[1] * anchor[8] - rgbAnchor[2] * anchor[7];
263
b1 = rgbAnchor[2] * anchor[6] - rgbAnchor[0] * anchor[8];
264
c1 = rgbAnchor[0] * anchor[7] - rgbAnchor[1] * anchor[6];
265
a2 = rgbAnchor[1] * anchor[2] - rgbAnchor[2] * anchor[1];
266
b2 = rgbAnchor[2] * anchor[0] - rgbAnchor[0] * anchor[2];
267
c2 = rgbAnchor[0] * anchor[1] - rgbAnchor[1] * anchor[0];
268
inflection = rgbAnchor[2] / rgbAnchor[1];
269
tmp = lms[2] / lms[1];
270
if (tmp < inflection)
271
lms[0] = -(b1 * lms[1] + c1 * lms[2]) / a1;
273
lms[0] = -(b2 * lms[1] + c2 * lms[2]) / a2;
277
a1 = rgbAnchor[1] * anchor[8] - rgbAnchor[2] * anchor[7];
278
b1 = rgbAnchor[2] * anchor[6] - rgbAnchor[0] * anchor[8];
279
c1 = rgbAnchor[0] * anchor[7] - rgbAnchor[1] * anchor[6];
280
a2 = rgbAnchor[1] * anchor[2] - rgbAnchor[2] * anchor[1];
281
b2 = rgbAnchor[2] * anchor[0] - rgbAnchor[0] * anchor[2];
282
c2 = rgbAnchor[0] * anchor[1] - rgbAnchor[1] * anchor[0];
283
inflection = rgbAnchor[2] / rgbAnchor[0];
284
tmp = lms[2] / lms[0];
285
/* See which side of the inflection line we fall... */
286
if (tmp < inflection)
287
lms[1] = -(a1 * lms[0] + c1 * lms[2]) / b1;
289
lms[1] = -(a2 * lms[0] + c2 * lms[2]) / b2;
293
a1 = rgbAnchor[1] * anchor[11] - rgbAnchor[2] * anchor[10];
294
b1 = rgbAnchor[2] * anchor[9] - rgbAnchor[0] * anchor[11];
295
c1 = rgbAnchor[0] * anchor[10] - rgbAnchor[1] * anchor[9];
296
a2 = rgbAnchor[1] * anchor[5] - rgbAnchor[2] * anchor[4];
297
b2 = rgbAnchor[2] * anchor[3] - rgbAnchor[0] * anchor[5];
298
c2 = rgbAnchor[0] * anchor[4] - rgbAnchor[1] * anchor[3];
299
inflection = (rgbAnchor[1] / rgbAnchor[0]);
300
tmp = lms[1] / lms[0];
301
if (tmp < inflection)
302
lms[2] = -(a1 * lms[0] + b1 * lms[1]) / c1;
304
lms[2] = -(a2 * lms[0] + b2 * lms[1]) / c2;
310
double[] rgbCb = mult3(lmsToRgb, lms);
312
double nr = Math.min(255.0, Math.max(0.0, rgbCb[0]));
313
double ng = Math.min(255.0, Math.max(0.0, rgbCb[1]));
314
double nb = Math.min(255.0, Math.max(0.0, rgbCb[2]));
315
return new Color((int) nr, (int) ng, (int) nb);
319
* Multiplies the specified 3x3 matrix by the specified 3x1 vector.
325
* @return Vector multiplication.
327
private static double[] mult3(double[][] matrix, double[] vector) {
328
double[] res = new double[3];
329
res[0] = matrix[0][0] * vector[0] + matrix[0][1] * vector[1]
330
+ matrix[0][2] * vector[2];
331
res[1] = matrix[1][0] * vector[0] + matrix[1][1] * vector[1]
332
+ matrix[1][2] * vector[2];
333
res[2] = matrix[2][0] * vector[0] + matrix[2][1] * vector[1]
334
+ matrix[2][2] * vector[2];