Remove this line
[kdeaccessibility.git] / kmag / colorsim.cpp
blobe7a5faee905cea352992805c91be5e20ccb6c9a9
1 /***************************************************************************
2 colorsim.cpp - description
3 -------------------
4 begin : Mon Jan 21 14:54:37 CST 2008
5 copyright : (C) 2008 by Matthew Woehlke
6 email : mw_triad@users.sourceforge.net
7 ***************************************************************************/
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
18 /***************************************************************************
20 Citations:
22 [1] H. Brettel, F. Viénot and J. D. Mollon (1997)
23 "Computerized simulation of color appearance for dichromats."
24 J. Opt. Soc. Am. A 14, 2647-2655.
25 [2] F. Viénot, H. Brettel and J. D. Mollon (1999)
26 "Digital video colourmaps for checking the legibility of displays by
27 dichromats."
28 Color Research and Application 24, 243-252.
30 ***************************************************************************/
32 // application specific includes
33 #include "colorsim.h"
35 // include files for Qt
36 #include <QtGui/QColor>
38 #include <math.h>
40 typedef qreal matrix[3][3];
42 #define SIMPLE_ALGORITHM
44 #ifndef SIMPLE_ALGORITHM
45 typedef qreal vector[3];
47 struct fcoef {
48 vector k[2];
49 // vector e;
50 matrix s[2];
52 #endif
54 class xyza {
55 private:
56 qreal x, y, z, a;
58 public:
59 xyza() {}
60 xyza(const QColor &c);
61 QRgb rgba() const;
62 xyza gamma(qreal q) const;
63 xyza operator*(const matrix m) const;
64 #ifndef SIMPLE_ALGORITHM
65 xyza(const vector v);
66 qreal operator*(const vector m) const;
67 xyza flatten(fcoef c) const;
68 #endif
71 xyza::xyza(const QColor &c) :
72 x(c.redF()), y(c.greenF()), z(c.blueF()), a(c.alphaF())
76 inline qreal clamp(qreal n)
78 return qMin(qreal(1.0), qMax(qreal(0.0), n));
81 QRgb xyza::rgba() const
83 return QColor::fromRgbF(clamp(x), clamp(y), clamp(z), a).rgba();
86 xyza xyza::operator*(const matrix m) const
88 xyza r;
89 r.x = (x * m[0][0]) + (y * m[0][1]) + (z * m[0][2]);
90 r.y = (x * m[1][0]) + (y * m[1][1]) + (z * m[1][2]);
91 r.z = (x * m[2][0]) + (y * m[2][1]) + (z * m[2][2]);
92 r.a = a;
93 return r;
96 xyza xyza::gamma(qreal q) const
98 xyza r;
99 r.x = pow(x, q);
100 r.y = pow(y, q);
101 r.z = pow(z, q);
102 r.a = a;
103 return r;
106 #if !defined(SIMPLE_ALGORITHM) || !defined(STANFORD_ALGORITHM)
108 /***************************************************************************
110 These RGB<->LMS transformation matrices are from [1].
112 ***************************************************************************/
114 static const matrix rgb2lms = {
115 {0.1992, 0.4112, 0.0742},
116 {0.0353, 0.2226, 0.0574},
117 {0.0185, 0.1231, 1.3550}
120 static const matrix lms2rgb = {
121 { 7.4645, -13.8882, 0.1796},
122 {-1.1852, 6.8053, -0.2234},
123 { 0.0058, -0.4286, 0.7558}
126 #endif
128 #ifdef SIMPLE_ALGORITHM
130 # ifndef STANFORD_ALGORITHM // from "Computerized simulation of color appearance for dichromats"
132 # ifdef CRA_ALGORITHM
134 /***************************************************************************
136 These matrices are derived from Table II in [2], for the code based on the
137 Onur/Poliang algorithm below, using the LMS transformation from [1].
138 No tritanopia data were provided, so that simulation does not work correctly.
140 ***************************************************************************/
142 static const matrix coef[3] = {
143 { {0.0, 2.39238646, -0.04658523}, {0.0, 1.0, 0.0 }, {0.0, 0.0, 1.0} },
144 { {1.0, 0.0, 0.0 }, {0.41799267, 0.0, 0.01947228}, {0.0, 0.0, 1.0} },
145 { {1.0, 0.0, 0.0 }, {0.0, 1.0, 0.0 }, {0.0, 0.0, 0.0} }
148 # else
150 /***************************************************************************
152 These matrices are derived from the "Color Blindness Simulation" sample
153 palettes from Visolve, Ryobi System Solutions, using the LMS transformations
154 from [1].
155 http://www.ryobi-sol.co.jp/visolve/en/simulation.html
157 ***************************************************************************/
159 static const matrix coef[3] = {
160 { {0.0, 2.60493696, -0.08742194}, {0.0, 1.0, 0.0 }, {0.0, 0.0, 1.0} },
161 { {1.0, 0.0, 0.0 }, {0.38395980, 0.0, 0.03370622}, {0.0, 0.0, 1.0} },
162 { {1.0, 0.0, 0.0 }, {0.0, 1.0, 0.0 }, {-3.11932916, 12.18076308, 0.0} }
165 # endif
167 # else // from the "Analysis of Color Blindness" project
169 /***************************************************************************
171 This code is based on the matrices from [2], as presented by Onur and Poliang.
172 The tritanopia simulation uses different representative wavelengths (yellow
173 and blue) than those reccommended by [1] and found in most other simulations
174 (red and cyan).
175 http://www.stanford.edu/~ofidaner/psych221_proj/colorblindness_project.htm
177 ***************************************************************************/
179 static const matrix coef[3] = {
180 { { 0.0, 2.02344, -2.52581}, {0.0, 1.0, 0.0 }, { 0.0, 0.0, 1.0} },
181 { { 1.0, 0.0, 0.0 }, {0.494207, 0.0, 1.24827}, { 0.0, 0.0, 1.0} },
182 { { 1.0, 0.0, 0.0 }, {0.0, 1.0, 0.0 }, {-0.395913, 0.801109, 0.0} }
185 static const matrix rgb2lms = {
186 {17.8824, 43.5161, 4.11935},
187 { 3.45565, 27.1554, 3.86714},
188 { 0.0299566, 0.184309, 1.46709}
191 static const matrix lms2rgb = {
192 { 0.080944447905, -0.130504409160, 0.116721066440},
193 {-0.010248533515, 0.054019326636, -0.113614708214},
194 {-0.000365296938, -0.004121614686, 0.693511404861}
197 # endif
199 inline QRgb recolor(QRgb c, int mode, qreal g)
201 if (mode > 0 && mode < 4) {
202 xyza n = QColor(c);
203 if (g != 1.0) {
204 xyza r = n.gamma(g) * rgb2lms * coef[mode-1] * lms2rgb;
205 return r.gamma(qreal(1.0) / g).rgba();
207 else {
208 xyza r = n * rgb2lms * coef[mode-1] * lms2rgb;
209 return r.rgba();
212 else {
213 return qRgb(qGray(c), qGray(c), qGray(c));
217 #else // from "Computerized simulation of color appearance for dichromats"
219 /***************************************************************************
221 This code is based on [1]. The RGB<->LMS transformation matrices are declared
222 above.
224 ***************************************************************************/
226 static const fcoef coef[3] = {
228 { {0.0, 0.0, 1.0}, {0.0, 1.0, 0.0} }, // k
229 // { }, // e
230 // a { }
231 { // s
232 { {0.0, 2.39238646, -0.04658523}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0} },
233 { {0.0, 0.37421464, -0.02034378}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0} }
237 { {0.0, 0.0, 1.0}, {1.0, 0.0, 0.0} }, // k
238 // { }, // e
239 // a { }
240 { // s
241 { {1.0, 0.0, 0.0}, {0.41799267, 0.0, 0.01947228}, {0.0, 0.0, 1.0} },
242 { {1.0, 0.0, 0.0}, {0.41799267, 0.0, 0.01947228}, {0.0, 0.0, 1.0} }
246 { {0.0, 1.0, 0.0}, {1.0, 0.0, 0.0} }, // k
247 // { }, // e
248 // a { }
249 { // s
250 { {1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 0.0} },
251 { {1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 0.0} }
254 /* The 's' matrices are calculated thusly:
255 u = E.y*A.z - E.z*A.y;
256 v = E.z*A.x - E.x*A.z;
257 w = E.x*A.y - E.y*A.x;
258 r.x = (mode != 1) ? x : (-v/u) * y + (-w/u) * z;
259 r.y = (mode != 2) ? y : (-u/v) * x + (-w/v) * z;
260 r.z = (mode != 3) ? z : (-u/w) * x + (-v/w) * y;
264 xyza::xyza(const vector v) :
265 x(v[0]), y(v[1]), z(v[2]), a(0.0)
269 qreal xyza::operator*(const vector v) const
271 return (x * v[0]) + (y * v[1]) + (z * v[2]);
274 xyza xyza::flatten(fcoef c) const
276 // xyza e(c.e);
277 // qreal u = (*this * c.k[0]) / (*this * c.k[1]);
278 // qreal v = (e * c.k[0]) / (e * c.k[1]);
279 // int i = (u < v ? 0 : 1);
280 int i = 0;
281 return *this * c.s[i];
284 inline QRgb recolor(QRgb c, int mode, qreal g)
286 if (mode > 0 && mode < 4) {
287 xyza n = QColor(c);
288 xyza r = n.gamma(g) * rgb2lms;
289 r = r.flatten(coef[mode-1]);
290 r = r * lms2rgb;
291 return r.gamma(qreal(1.0) / g).rgba();
293 else {
294 return qRgb(qGray(c), qGray(c), qGray(c));
298 #endif
300 QPixmap ColorSim::recolor(const QPixmap &pm, int mode, qreal gamma)
302 // nothing to do if either paraneter is "bad"
303 if (pm.isNull() || mode > 4 || mode < 1)
304 return pm;
306 // get raw data in a format we can manipulate
307 QImage i = pm.toImage();
308 if (i.format() != QImage::Format_RGB32 && i.format() != QImage::Format_ARGB32)
309 i = i.convertToFormat(QImage::Format_ARGB32);
311 int n = i.width() * i.height();
312 QRgb *d = (QRgb*)i.bits();
313 for (int k = 0; k < n; ++k)
314 d[k] = ::recolor(d[k], mode, gamma);
316 return QPixmap::fromImage(i);
318 // kate: indent-width 2;