1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 * Global exports. Used by popup to show effect of filter during setup.
12 var curType
= 'PROTANOMALY';
13 var curSimulate
= false;
14 var curEnable
= false;
18 // ======= 3x3 matrix ops =======
21 * The 3x3 identity matrix.
24 var IDENTITY_MATRIX_3x3
= [
33 * @param {!object} m1 A 3x3 matrix.
34 * @param {!object} m2 A 3x3 matrix.
35 * @return {!object} The 3x3 matrix m1 + m2.
37 function add3x3(m1
, m2
) {
39 for (var i
= 0; i
< 3; i
++) {
41 for (var j
= 0; j
< 3; j
++) {
42 result
[i
].push(m1
[i
][j
] + m2
[i
][j
]);
50 * Subtracts one matrix from another.
51 * @param {!object} m1 A 3x3 matrix.
52 * @param {!object} m2 A 3x3 matrix.
53 * @return {!object} The 3x3 matrix m1 - m2.
55 function sub3x3(m1
, m2
) {
57 for (var i
= 0; i
< 3; i
++) {
59 for (var j
= 0; j
< 3; j
++) {
60 result
[i
].push(m1
[i
][j
] - m2
[i
][j
]);
68 * Multiplies one matrix with another.
69 * @param {!object} m1 A 3x3 matrix.
70 * @param {!object} m2 A 3x3 matrix.
71 * @return {!object} The 3x3 matrix m1 * m2.
73 function mul3x3(m1
, m2
) {
75 for (var i
= 0; i
< 3; i
++) {
77 for (var j
= 0; j
< 3; j
++) {
79 for (var k
= 0; k
< 3; k
++) {
80 sum
+= m1
[i
][k
] * m2
[k
][j
];
90 * Multiplies a matrix with a number.
91 * @param {!object} m A 3x3 matrix.
92 * @param {!number} k A scalar multiplier.
93 * @return {!object} The 3x3 matrix m * k.
95 function mul3x3Scalar(m
, k
) {
97 for (var i
= 0; i
< 3; i
++) {
99 for (var j
= 0; j
< 3; j
++) {
100 result
[i
].push(k
* m
[i
][j
]);
107 // ======= 3x3 matrix utils =======
110 * Makes the SVG matrix string (of 20 values) for a given matrix.
111 * @param {!object} m A 3x3 matrix.
112 * @return {!string} The SVG matrix string for m.
114 function svgMatrixStringFrom3x3(m
) {
116 for (var i
= 0; i
< 3; i
++) {
117 outputRows
.push(m
[i
].join(' ') + ' 0 0');
120 outputRows
.push('0 0 0 1 0');
121 return outputRows
.join(' ');
126 * Makes a human readable string for a given matrix.
127 * @param {!object} m A 3x3 matrix.
128 * @return {!string} A human-readable string for m.
130 function humanReadbleStringFrom3x3(m
) {
132 for (var i
= 0; i
< 3; i
++) {
133 result
+= (i
? ', ' : '') + '[';
134 for (var j
= 0; j
< 3; j
++) {
135 result
+= (j
? ', ' : '') + m
[i
][j
].toFixed(2);
143 // ======= CVD parameters =======
145 * Parameters for simulating color vision deficiency.
147 * http://www.inf.ufrgs.br/~oliveira/pubs_files/CVD_Simulation/CVD_Simulation.html
148 * Original Research Paper:
149 * http://www.inf.ufrgs.br/~oliveira/pubs_files/CVD_Simulation/Machado_Oliveira_Fernandes_CVD_Vis2009_final.pdf
153 var cvdSimulationParams
= {
155 [0.4720, -1.2946, 0.9857],
156 [-0.6128, 1.6326, 0.0187],
157 [0.1407, -0.3380, -0.0044],
158 [-0.1420, 0.2488, 0.0044],
159 [0.1872, -0.3908, 0.9942],
160 [-0.0451, 0.1420, 0.0013],
161 [0.0222, -0.0253, -0.0004],
162 [-0.0290, -0.0201, 0.0006],
163 [0.0068, 0.0454, 0.9990]
166 [0.5442, -1.1454, 0.9818],
167 [-0.7091, 1.5287, 0.0238],
168 [0.1650, -0.3833, -0.0055],
169 [-0.1664, 0.4368, 0.0056],
170 [0.2178, -0.5327, 0.9927],
171 [-0.0514, 0.0958, 0.0017],
172 [0.0180, -0.0288, -0.0006],
173 [-0.0232, -0.0649, 0.0007],
174 [0.0052, 0.0360, 0.9998]
177 [0.4275, -0.0181, 0.9307],
178 [-0.2454, 0.0013, 0.0827],
179 [-0.1821, 0.0168, -0.0134],
180 [-0.1280, 0.0047, 0.0202],
181 [0.0233, -0.0398, 0.9728],
182 [0.1048, 0.0352, 0.0070],
183 [-0.0156, 0.0061, 0.0071],
184 [0.3841, 0.2947, 0.0151],
185 [-0.3685, -0.3008, 0.9778]
190 // TODO(mustaq): This should be nuked, see getCvdCorrectionMatrix().
191 var cvdCorrectionParams
= {
231 // ======= CVD matrix builders =======
234 * Returns a 3x3 matrix for simulating the given type of CVD with the given
236 * @param {string} cvdType Type of CVD, either "PROTANOMALY" or
237 * "DEUTERANOMALY" or "TRITANOMALY".
238 * @param {number} severity A real number in [0,1] denoting severity.
240 function getCvdSimulationMatrix(cvdType
, severity
) {
241 var cvdSimulationParam
= cvdSimulationParams
[cvdType
];
242 var severity2
= severity
* severity
;
244 for (var i
= 0; i
< 3; i
++) {
246 for (var j
= 0; j
< 3; j
++) {
247 var paramRow
= i
*3+j
;
248 var val
= cvdSimulationParam
[paramRow
][0] * severity2
249 + cvdSimulationParam
[paramRow
][1] * severity
250 + cvdSimulationParam
[paramRow
][2];
260 * Returns a 3x3 matrix for correcting the given type of CVD using the given
262 * @param {string} cvdType Type of CVD, either "PROTANOMALY" or
263 * "DEUTERANOMALY" or "TRITANOMALY".
264 * @param {number} delta A real number in [0,1] denoting color adjustment.
266 function getCvdCorrectionMatrix(cvdType
, delta
) {
267 cvdCorrectionParam
= cvdCorrectionParams
[cvdType
];
268 // TODO(mustaq): Perhaps nuke full-matrix operations after experiment.
269 return add3x3(cvdCorrectionParam
['addendum'],
270 mul3x3Scalar(cvdCorrectionParam
['delta_factor'], delta
));
275 * Returns the 3x3 matrix to be used for the given settings.
276 * @param {string} cvdType Type of CVD, either "PROTANOMALY" or
277 * "DEUTERANOMALY" or "TRITANOMALY".
278 * @param {number} severity A real number in [0,1] denoting severity.
279 * @param {number} delta A real number in [0,1] denoting color adjustment.
280 * @param {boolean} simulate Whether to simulate the CVD type.
281 * @param {boolean} enable Whether to enable color filtering.
283 function getEffectiveCvdMatrix(cvdType
, severity
, delta
, simulate
, enable
) {
285 //TODO(mustaq): we should remove matrices at the svg level
286 return IDENTITY_MATRIX_3x3
;
289 var effectiveMatrix
= getCvdSimulationMatrix(cvdType
, severity
);
292 var cvdCorrectionMatrix
= getCvdCorrectionMatrix(cvdType
, delta
);
293 var tmpProduct
= mul3x3(cvdCorrectionMatrix
, effectiveMatrix
);
295 effectiveMatrix
= sub3x3(
296 add3x3(IDENTITY_MATRIX_3x3
, cvdCorrectionMatrix
),
300 return effectiveMatrix
;
304 // ======= Page linker =======
306 /** @const {string} */
307 var SVG_DEFAULT_MATRIX
=
314 '<svg xmlns="http://www.w3.org/2000/svg" version="1.1">' +
316 ' <filter id="cvd_extension_0">' +
317 ' <feColorMatrix id="cvd_matrix_0" type="matrix" values="' +
318 SVG_DEFAULT_MATRIX
+ '"/>' +
320 ' <filter id="cvd_extension_1">' +
321 ' <feColorMatrix id="cvd_matrix_1" type="matrix" values="' +
322 SVG_DEFAULT_MATRIX
+ '"/>' +
328 * Checks for svg filter matrix presence and append to DOM if not present.
330 function addSvgIfMissing() {
331 var wrap
= document
.getElementById('cvd_extension_svg_filter');
333 wrap
= document
.createElement('span');
334 wrap
.id
= 'cvd_extension_svg_filter';
335 wrap
.setAttribute('hidden', '');
336 wrap
.innerHTML
= svgContent
;
337 document
.body
.appendChild(wrap
);
342 * Updates the SVG filter based on the RGB correction/simulation matrix.
343 * @param {!Object} matrix 3x3 RGB transformation matrix.
345 function setFilter(matrix
) {
347 var next
= 1 - curFilter
;
349 debugPrint('update: matrix#' + next
+ '=' +
350 humanReadbleStringFrom3x3(matrix
));
352 var matrixElem
= document
.getElementById('cvd_matrix_' + next
);
353 matrixElem
.setAttribute('values', svgMatrixStringFrom3x3(matrix
));
355 var html
= document
.documentElement
;
356 html
.classList
.remove('filter' + curFilter
);
357 html
.classList
.add('filter' + next
);
363 * Updates the SVG matrix using the current settings.
366 if (!document
.body
) {
367 document
.addEventListener('DOMContentLoaded', update
);
371 var effectiveMatrix
= getEffectiveCvdMatrix(
372 curType
, curSeverity
, curDelta
* 2 - 1, curSimulate
, curEnable
);
374 setFilter(effectiveMatrix
);
376 // TODO(kevers): Check if a call to getComputedStyle is sufficient to force
378 window
.scrollBy(0, 1);
379 window
.scrollBy(0, -1);
384 * Process request from background page.
385 * @param {!object} request An object containing color filter parameters.
387 function onExtensionMessage(request
) {
388 debugPrint('onExtensionMessage: ' + JSON
.stringify(request
));
391 if (request
['type'] !== undefined) {
392 var type
= request
.type
;
393 if (curType
!= type
) {
399 if (request
['severity'] !== undefined) {
400 var severity
= request
.severity
;
401 if (curSeverity
!= severity
) {
402 curSeverity
= severity
;
407 if (request
['delta'] !== undefined) {
408 var delta
= request
.delta
;
409 if (curDelta
!= delta
) {
415 if (request
['simulate'] !== undefined) {
416 var simulate
= request
.simulate
;
417 if (curSimulate
!= simulate
) {
418 curSimulate
= simulate
;
423 if (request
['enable'] !== undefined) {
424 var enable
= request
.enable
;
425 if (curEnable
!= enable
) {
437 * Prepare to process background messages and let it know to send initial
440 exports
.initializeExtension = function () {
441 chrome
.extension
.onRequest
.addListener(onExtensionMessage
);
442 chrome
.extension
.sendRequest({'init': true}, onExtensionMessage
);
446 * Generate SVG filter for color enhancement based on type and severity using
447 * default color adjustment.
448 * @param {string} type Type type of color vision defficiency (CVD).
449 * @param {number} severity The degree of CVD ranging from 0 for normal
450 * vision to 1 for dichromats.
452 exports
.getDefaultCvdCorrectionFilter = function(type
, severity
) {
453 return getEffectiveCvdMatrix(type
, severity
, 0, false, true);
457 * Adds support for a color enhancement filter.
458 * @param {!Object} matrix 3x3 RGB transformation matrix.
460 exports
.injectColorEnhancementFilter = function(matrix
) {
465 * Clears color correction filter.
467 exports
.clearColorEnhancementFilter = function() {
468 var html
= document
.documentElement
;
469 html
.classList
.remove('filter0');
470 html
.classList
.remove('filter1');
474 this.initializeExtension();