summaryrefslogtreecommitdiffstats
path: root/devtools/server/actors/accessibility/worker.js
blob: 75dc78e5b2d68500012cb6c15921bf37eb27d7fb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

/**
 * Import `createTask` to communicate with `devtools/shared/worker`.
 */
importScripts("resource://gre/modules/workers/require.js");
const { createTask } = require("resource://devtools/shared/worker/helper.js");

/**
 * @see LineGraphWidget.prototype.setDataFromTimestamps in Graphs.js
 * @param number id
 * @param array timestamps
 * @param number interval
 * @param number duration
 */
createTask(self, "getBgRGBA", ({ dataTextBuf, dataBackgroundBuf }) =>
  getBgRGBA(dataTextBuf, dataBackgroundBuf)
);

/**
 * Calculates the luminance of a rgba tuple based on the formula given in
 * https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
 *
 * @param {Array} rgba An array with [r,g,b,a] values.
 * @return {Number} The calculated luminance.
 */
function calculateLuminance(rgba) {
  for (let i = 0; i < 3; i++) {
    rgba[i] /= 255;
    rgba[i] =
      rgba[i] < 0.03928
        ? rgba[i] / 12.92
        : Math.pow((rgba[i] + 0.055) / 1.055, 2.4);
  }
  return 0.2126 * rgba[0] + 0.7152 * rgba[1] + 0.0722 * rgba[2];
}

/**
 * Get RGBA or a range of RGBAs for the background pixels under the text. If luminance is
 * uniform, only return one value of RGBA, otherwise return values that correspond to the
 * min and max luminances.
 * @param  {ImageData} dataTextBuf
 *         pixel data for the accessible object with text visible.
 * @param  {ImageData} dataBackgroundBuf
 *         pixel data for the accessible object with transparent text.
 * @return {Object}
 *         RGBA or a range of RGBAs with min and max values.
 */
function getBgRGBA(dataTextBuf, dataBackgroundBuf) {
  let min = [0, 0, 0, 1];
  let max = [255, 255, 255, 1];
  let minLuminance = 1;
  let maxLuminance = 0;
  const luminances = {};
  const dataText = new Uint8ClampedArray(dataTextBuf);
  const dataBackground = new Uint8ClampedArray(dataBackgroundBuf);

  let foundDistinctColor = false;
  for (let i = 0; i < dataText.length; i = i + 4) {
    const tR = dataText[i];
    const bgR = dataBackground[i];
    const tG = dataText[i + 1];
    const bgG = dataBackground[i + 1];
    const tB = dataText[i + 2];
    const bgB = dataBackground[i + 2];

    // Ignore pixels that are the same where pixels that are different between the two
    // images are assumed to belong to the text within the node.
    if (tR === bgR && tG === bgG && tB === bgB) {
      continue;
    }

    foundDistinctColor = true;

    const bgColor = `rgb(${bgR}, ${bgG}, ${bgB})`;
    let luminance = luminances[bgColor];

    if (!luminance) {
      // Calculate luminance for the RGB value and store it to only measure once.
      luminance = calculateLuminance([bgR, bgG, bgB]);
      luminances[bgColor] = luminance;
    }

    if (minLuminance >= luminance) {
      minLuminance = luminance;
      min = [bgR, bgG, bgB, 1];
    }

    if (maxLuminance <= luminance) {
      maxLuminance = luminance;
      max = [bgR, bgG, bgB, 1];
    }
  }

  if (!foundDistinctColor) {
    return null;
  }

  return minLuminance === maxLuminance ? { value: max } : { min, max };
}