| 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);
|
| +})();
|
|
|