Index: ui/accessibility/extensions/colorenhancer/src/cvd.js |
diff --git a/ui/accessibility/extensions/colorenhancer/src/cvd.js b/ui/accessibility/extensions/colorenhancer/src/cvd.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..12b2d904239372462088368f407b9819852142f1 |
--- /dev/null |
+++ b/ui/accessibility/extensions/colorenhancer/src/cvd.js |
@@ -0,0 +1,363 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+ |
+// ======= Global state ======= |
+ |
+var storedDelta = 0; |
+var storedSeverity = 0; |
+var storedType = 'PROTANOMALY'; |
+var storedSimulate = false; |
+var curFilter = 0; |
+ |
+ |
+// ======= 3x3 matrix ops ======= |
+ |
+var identityMatrix3x3 = [ |
+ [1, 0, 0], |
+ [0, 1, 0], |
+ [0, 0, 1] |
+]; |
+ |
+ |
+/** |
+ * TODO(mustaq): JsDoc |
+ */ |
+function add3x3(m1, m2) { |
+ var result = []; |
+ for (var i = 0; i < 3; i++) { |
+ result[i] = []; |
+ for (var j = 0; j < 3; j++) { |
+ result[i].push(m1[i][j] + m2[i][j]); |
+ } |
+ } |
+ return result; |
+} |
+ |
+ |
+/** |
+ * TODO(mustaq): JsDoc |
+ */ |
+function sub3x3(m1, m2) { |
+ var result = []; |
+ for (var i = 0; i < 3; i++) { |
+ result[i] = []; |
+ for (var j = 0; j < 3; j++) { |
+ result[i].push(m1[i][j] - m2[i][j]); |
+ } |
+ } |
+ return result; |
+} |
+ |
+ |
+/** |
+ * TODO(mustaq): JsDoc |
+ */ |
+function mul3x3(m1, m2) { |
+ var result = []; |
+ for (var i = 0; i < 3; i++) { |
+ result[i] = []; |
+ for (var j = 0; j < 3; j++) { |
+ var sum = 0; |
+ for (var k = 0; k < 3; k++) { |
+ sum += m1[i][k] * m2[k][j]; |
+ } |
+ result[i].push(sum); |
+ } |
+ } |
+ return result; |
+} |
+ |
+ |
+/** |
+ * TODO(mustaq): JsDoc |
+ */ |
+function mul3x3Scalar(m, k) { |
+ var result = []; |
+ for (var i = 0; i < 3; i++) { |
+ result[i] = []; |
+ for (var j = 0; j < 3; j++) { |
+ result[i].push(k * m[i][j]); |
+ } |
+ } |
+ return result; |
+} |
+ |
+ |
+// ======= CVD parameters ======= |
+/** |
+ * Parameters for simulating color vision deficiency. |
+ * Source: |
+ * http://www.inf.ufrgs.br/~oliveira/pubs_files/CVD_Simulation/CVD_Simulation.html |
+ * Original Research Paper: |
+ * http://www.inf.ufrgs.br/~oliveira/pubs_files/CVD_Simulation/Machado_Oliveira_Fernandes_CVD_Vis2009_final.pdf |
+ * |
+ * @enum {string} |
+ */ |
+var cvdSimulationParams = { |
+ PROTANOMALY: [ |
+ [0.4720, -1.2946, 0.9857], |
+ [-0.6128, 1.6326, 0.0187], |
+ [0.1407, -0.3380, -0.0044], |
+ [-0.1420, 0.2488, 0.0044], |
+ [0.1872, -0.3908, 0.9942], |
+ [-0.0451, 0.1420, 0.0013], |
+ [0.0222, -0.0253, -0.0004], |
+ [-0.0290, -0.0201, 0.0006], |
+ [0.0068, 0.0454, 0.9990] |
+ ], |
+ DEUTERANOMALY: [ |
+ [0.5442, -1.1454, 0.9818], |
+ [-0.7091, 1.5287, 0.0238], |
+ [0.1650, -0.3833, -0.0055], |
+ [-0.1664, 0.4368, 0.0056], |
+ [0.2178, -0.5327, 0.9927], |
+ [-0.0514, 0.0958, 0.0017], |
+ [0.0180, -0.0288, -0.0006], |
+ [-0.0232, -0.0649, 0.0007], |
+ [0.0052, 0.0360, 0.9998] |
+ ], |
+ TRITANOMALY: [ |
+ [0.4275, -0.0181, 0.9307], |
+ [-0.2454, 0.0013, 0.0827], |
+ [-0.1821, 0.0168, -0.0134], |
+ [-0.1280, 0.0047, 0.0202], |
+ [0.0233, -0.0398, 0.9728], |
+ [0.1048, 0.0352, 0.0070], |
+ [-0.0156, 0.0061, 0.0071], |
+ [0.3841, 0.2947, 0.0151], |
+ [-0.3685, -0.3008, 0.9778] |
+ ] |
+}; |
+ |
+ |
+// TODO(mustaq): A common matrix for all types? E.g. Kevin's experiment |
+// suggested: Mx[1][0] = 0.7+0.3*delta and Mx[2][0] = 0.7-0.3*delta |
+/** |
+ * TODO(mustaq): JsDoc |
+ * |
+ * @enum {string} |
+ */ |
+var cvdCorrectionParams = { |
+ PROTANOMALY: { |
+ addendum: [ |
+ [-1.0, 0.0, 0.0], |
+ [0.5, 1.0, 0.0], |
+ [1.5, 0.0, 1.0] |
+ ], |
+ delta_factor: [ |
+ [0.0, 0.0, 0.0], |
+ [1.0, 0.0, 0.0], |
+ [-1.0, 0.0, 0.0] |
+ ] |
+ }, |
+ DEUTERANOMALY: { |
+ addendum: [ |
+ [1.0, 0.5, 0.0], |
+ [0.5, -1.0, 0.0], |
+ [1.5, 1.5, 1.0] |
+ ], |
+ delta_factor: [ |
+ [0.0, 1.0, 0.0], |
+ [0.0, 0.0, 0.0], |
+ [0.0, -1.0, 0.0] |
+ ] |
+ }, |
+ TRITANOMALY: { |
+ addendum: [ |
+ [1.0, 0.0, 1.5], |
+ [0.0, 1.0, 0.5], |
+ [0.0, 0.0, -1.0] |
+ ], |
+ delta_factor: [ |
+ [0.0, 0.0, -1.0], |
+ [0.0, 0.0, 1.0], |
+ [0.0, 0.0, 0.0] |
+ ] |
+ } |
+}; |
+ |
+ |
+// ======= CVD matrix builders ======= |
+ |
+/** |
+ * TODO(mustaq): JsDoc |
+ */ |
+function getCvdSimulationMatrix(cvdType, severity) { |
+ var cvdSimulationParam = cvdSimulationParams[cvdType]; |
+ var severity2 = severity * severity; |
+ var matrix = []; |
+ for (var i = 0; i < 3; i++) { |
+ var row = []; |
+ for (var j = 0; j < 3; j++) { |
+ var paramRow = i*3+j; |
+ var val = cvdSimulationParam[paramRow][0] * severity2 |
+ + cvdSimulationParam[paramRow][1] * severity |
+ + cvdSimulationParam[paramRow][2]; |
+ row.push(val); |
+ } |
+ matrix.push(row); |
+ } |
+ return matrix; |
+} |
+ |
+ |
+/** |
+ * TODO(mustaq): JsDoc |
+ */ |
+function getCvdCorrectionMatrix(cvdType, delta) { |
+ cvdCorrectionParam = cvdCorrectionParams[cvdType]; |
+ // TODO(mustaq): Perhaps nuke full-matrix operations after experiment. |
+ return add3x3(cvdCorrectionParam['addendum'], |
+ mul3x3Scalar(cvdCorrectionParam['delta_factor'], delta)); |
+} |
+ |
+ |
+/** |
+ * TODO(mustaq): JsDoc |
+ */ |
+function getCvdMatrixAsString(cvdType, severity, delta, simulate) { |
+ var effectiveMatrix = getCvdSimulationMatrix(cvdType, severity); |
+ |
+ if (!simulate) { |
+ var cvdCorrectionMatrix = getCvdCorrectionMatrix(cvdType, delta); |
+ var tmpProduct = mul3x3(cvdCorrectionMatrix, effectiveMatrix); |
+ |
+ effectiveMatrix = sub3x3( |
+ add3x3(identityMatrix3x3, cvdCorrectionMatrix), |
+ tmpProduct); |
+ } |
+ |
+ var outputRows = []; |
+ for (var i = 0; i < 3; i++) { |
+ outputRows.push(effectiveMatrix[i].join(' ') + ' 0 0'); |
+ } |
+ // Add the alpha row |
+ outputRows.push('0 0 0 1 0'); |
+ return outputRows.join(' '); |
+} |
+ |
+ |
+// ======= Page linker ======= |
+ |
+var svgDefaultMatrix = |
+ '1 0 0 0 0 ' + |
+ '0 1 0 0 0 ' + |
+ '0 0 1 0 0 ' + |
+ '0 0 0 1 0'; |
+ |
+var svgContent = |
+ '<svg xmlns='http://www.w3.org/2000/svg' version='1.1'>' + |
+ ' <defs>' + |
+ ' <filter id='cvd_extension_0'>' + |
+ ' <feColorMatrix id='cvd_filter_matrix_0' type='matrix' values='' + |
+ svgDefaultMatrix + ''/>' + |
+ ' </filter>' + |
+ ' <filter id='cvd_extension_1'>' + |
+ ' <feColorMatrix id='cvd_filter_matrix_1' type='matrix' values='' + |
+ svgDefaultMatrix + ''/>' + |
+ ' </filter>' + |
+ ' </defs>' + |
+ '</svg>'; |
+ |
+/** |
+ * Checks for svg filter matrix presence and append to DOM if not present. |
+ */ |
+function addSvgIfMissing() { |
+ var wrap = document.getElementById('cvd_extension_svg_filter'); |
+ if (!wrap) { |
+ wrap = document.createElement('span'); |
+ wrap.id = 'cvd_extension_svg_filter'; |
+ wrap.setAttribute('hidden', ''); |
+ wrap.innerHTML = svgContent; |
+ document.body.appendChild(wrap); |
+ } |
+} |
+ |
+ |
+/** |
+ * Update matrix when config values change. |
+ */ |
+function update() { |
+ if (!document.body) { |
+ document.addEventListener('DOMContentLoaded', update); |
+ return; |
+ } |
+ addSvgIfMissing(); |
+ var next = 1 - curFilter; |
+ |
+ debugPrint( |
+ 'Setting matrix#' + next + ' to ' + |
+ getCvdMatrixAsString( |
+ storedType, storedSeverity, storedDelta, storedSimulate)); |
+ |
+ var matrix = document.getElementById('cvd_filter_matrix_' + next); |
+ matrix.setAttribute( |
+ 'values', getCvdMatrixAsString( |
+ storedType, storedSeverity, storedDelta, storedSimulate)); |
+ |
+ var html = document.documentElement; |
+ html.classList.remove('filter' + curFilter); |
+ html.offsetTop; |
+ html.classList.add('filter' + next); |
+ |
+ curFilter = next; |
+ |
+ // TODO(wnwen): Figure out whether this hack is still necessary. |
+ window.scrollBy(0, 1); |
+ window.scrollBy(0, -1); |
+} |
+ |
+ |
+/** |
+ * Process request from background page. |
+ */ |
+function onExtensionMessage(request) { |
+ var changed = false; |
+ |
+ if (request['delta'] !== undefined) { |
+ var delta = request.delta; |
+ if (storedDelta != delta) { |
+ storedDelta = delta; |
+ changed = true; |
+ } |
+ } |
+ |
+ if (request['severity'] !== undefined) { |
+ var severity = request.severity; |
+ if (storedSeverity != severity) { |
+ storedSeverity = severity; |
+ changed = true; |
+ } |
+ } |
+ |
+ if (request['type'] !== undefined) { |
+ var type = request.type; |
+ if (storedType != type) { |
+ storedType = type; |
+ changed = true; |
+ } |
+ } |
+ |
+ if (request['simulate'] !== undefined) { |
+ var simulate = request.simulate; |
+ if (storedSimulate != simulate) { |
+ storedSimulate = simulate; |
+ changed = true; |
+ } |
+ } |
+ |
+ if (changed) |
+ update(); |
+} |
+ |
+ |
+/** |
+ * Prepare to process background messages and let it know to send initial |
+ * values. |
+ */ |
+(function initialize() { |
+ chrome.extension.onRequest.addListener(onExtensionMessage); |
+ chrome.extension.sendRequest({'init': true}, onExtensionMessage); |
+})(); |