polished-vs-canvas

JavaScript performance comparison

Test case created by ricokahler

Preparation code


      
      <script>
Benchmark.prototype.setup = function() {
  const namedColorMap = {
      aliceblue: 'f0f8ff',
      antiquewhite: 'faebd7',
      aqua: '00ffff',
      aquamarine: '7fffd4',
      azure: 'f0ffff',
      beige: 'f5f5dc',
      bisque: 'ffe4c4',
      black: '000',
      blanchedalmond: 'ffebcd',
      blue: '0000ff',
      blueviolet: '8a2be2',
      brown: 'a52a2a',
      burlywood: 'deb887',
      cadetblue: '5f9ea0',
      chartreuse: '7fff00',
      chocolate: 'd2691e',
      coral: 'ff7f50',
      cornflowerblue: '6495ed',
      cornsilk: 'fff8dc',
      crimson: 'dc143c',
      cyan: '00ffff',
      darkblue: '00008b',
      darkcyan: '008b8b',
      darkgoldenrod: 'b8860b',
      darkgray: 'a9a9a9',
      darkgreen: '006400',
      darkgrey: 'a9a9a9',
      darkkhaki: 'bdb76b',
      darkmagenta: '8b008b',
      darkolivegreen: '556b2f',
      darkorange: 'ff8c00',
      darkorchid: '9932cc',
      darkred: '8b0000',
      darksalmon: 'e9967a',
      darkseagreen: '8fbc8f',
      darkslateblue: '483d8b',
      darkslategray: '2f4f4f',
      darkslategrey: '2f4f4f',
      darkturquoise: '00ced1',
      darkviolet: '9400d3',
      deeppink: 'ff1493',
      deepskyblue: '00bfff',
      dimgray: '696969',
      dimgrey: '696969',
      dodgerblue: '1e90ff',
      firebrick: 'b22222',
      floralwhite: 'fffaf0',
      forestgreen: '228b22',
      fuchsia: 'ff00ff',
      gainsboro: 'dcdcdc',
      ghostwhite: 'f8f8ff',
      gold: 'ffd700',
      goldenrod: 'daa520',
      gray: '808080',
      green: '008000',
      greenyellow: 'adff2f',
      grey: '808080',
      honeydew: 'f0fff0',
      hotpink: 'ff69b4',
      indianred: 'cd5c5c',
      indigo: '4b0082',
      ivory: 'fffff0',
      khaki: 'f0e68c',
      lavender: 'e6e6fa',
      lavenderblush: 'fff0f5',
      lawngreen: '7cfc00',
      lemonchiffon: 'fffacd',
      lightblue: 'add8e6',
      lightcoral: 'f08080',
      lightcyan: 'e0ffff',
      lightgoldenrodyellow: 'fafad2',
      lightgray: 'd3d3d3',
      lightgreen: '90ee90',
      lightgrey: 'd3d3d3',
      lightpink: 'ffb6c1',
      lightsalmon: 'ffa07a',
      lightseagreen: '20b2aa',
      lightskyblue: '87cefa',
      lightslategray: '789',
      lightslategrey: '789',
      lightsteelblue: 'b0c4de',
      lightyellow: 'ffffe0',
      lime: '0f0',
      limegreen: '32cd32',
      linen: 'faf0e6',
      magenta: 'f0f',
      maroon: '800000',
      mediumaquamarine: '66cdaa',
      mediumblue: '0000cd',
      mediumorchid: 'ba55d3',
      mediumpurple: '9370db',
      mediumseagreen: '3cb371',
      mediumslateblue: '7b68ee',
      mediumspringgreen: '00fa9a',
      mediumturquoise: '48d1cc',
      mediumvioletred: 'c71585',
      midnightblue: '191970',
      mintcream: 'f5fffa',
      mistyrose: 'ffe4e1',
      moccasin: 'ffe4b5',
      navajowhite: 'ffdead',
      navy: '000080',
      oldlace: 'fdf5e6',
      olive: '808000',
      olivedrab: '6b8e23',
      orange: 'ffa500',
      orangered: 'ff4500',
      orchid: 'da70d6',
      palegoldenrod: 'eee8aa',
      palegreen: '98fb98',
      paleturquoise: 'afeeee',
      palevioletred: 'db7093',
      papayawhip: 'ffefd5',
      peachpuff: 'ffdab9',
      peru: 'cd853f',
      pink: 'ffc0cb',
      plum: 'dda0dd',
      powderblue: 'b0e0e6',
      purple: '800080',
      rebeccapurple: '639',
      red: 'f00',
      rosybrown: 'bc8f8f',
      royalblue: '4169e1',
      saddlebrown: '8b4513',
      salmon: 'fa8072',
      sandybrown: 'f4a460',
      seagreen: '2e8b57',
      seashell: 'fff5ee',
      sienna: 'a0522d',
      silver: 'c0c0c0',
      skyblue: '87ceeb',
      slateblue: '6a5acd',
      slategray: '708090',
      slategrey: '708090',
      snow: 'fffafa',
      springgreen: '00ff7f',
      steelblue: '4682b4',
      tan: 'd2b48c',
      teal: '008080',
      thistle: 'd8bfd8',
      tomato: 'ff6347',
      turquoise: '40e0d0',
      violet: 'ee82ee',
      wheat: 'f5deb3',
      white: 'fff',
      whitesmoke: 'f5f5f5',
      yellow: 'ff0',
      yellowgreen: '9acd32',
  };
  /**
   * Checks if a string is a CSS named color and returns its equivalent hex value, otherwise returns the original color.
   * @private
   */
  function nameToHex(color) {
      if (typeof color !== 'string')
          return color;
      const normalizedColorName = color.toLowerCase();
      return namedColorMap[normalizedColorName]
          ? `#${namedColorMap[normalizedColorName]}`
          : color;
  }
  function colorToInt(color) {
      return Math.round(color * 255);
  }
  function convertToInt(red, green, blue) {
      return `${colorToInt(red)},${colorToInt(green)},${colorToInt(blue)}`;
  }
  function hslToRgb(hue, saturation, lightness, convert = convertToInt) {
      if (saturation === 0) {
          // achromatic
          return convert(lightness, lightness, lightness);
      }
      // formulae from https://en.wikipedia.org/wiki/HSL_and_HSV
      const huePrime = (((hue % 360) + 360) % 360) / 60;
      const chroma = (1 - Math.abs(2 * lightness - 1)) * saturation;
      const secondComponent = chroma * (1 - Math.abs((huePrime % 2) - 1));
      let red = 0;
      let green = 0;
      let blue = 0;
      if (huePrime >= 0 && huePrime < 1) {
          red = chroma;
          green = secondComponent;
      }
      else if (huePrime >= 1 && huePrime < 2) {
          red = secondComponent;
          green = chroma;
      }
      else if (huePrime >= 2 && huePrime < 3) {
          green = chroma;
          blue = secondComponent;
      }
      else if (huePrime >= 3 && huePrime < 4) {
          green = secondComponent;
          blue = chroma;
      }
      else if (huePrime >= 4 && huePrime < 5) {
          red = secondComponent;
          blue = chroma;
      }
      else if (huePrime >= 5 && huePrime < 6) {
          red = chroma;
          blue = secondComponent;
      }
      const lightnessModification = lightness - chroma / 2;
      const finalRed = red + lightnessModification;
      const finalGreen = green + lightnessModification;
      const finalBlue = blue + lightnessModification;
      return convert(finalRed, finalGreen, finalBlue);
  }
  const hexRegex = /^#[a-fA-F0-9]{6}$/;
  const hexRgbaRegex = /^#[a-fA-F0-9]{8}$/;
  const reducedHexRegex = /^#[a-fA-F0-9]{3}$/;
  const reducedRgbaHexRegex = /^#[a-fA-F0-9]{4}$/;
  const rgbRegex = /^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/i;
  const rgbaRegex = /^rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*([-+]?[0-9]*[.]?[0-9]+)\s*\)$/i;
  const hslRegex = /^hsl\(\s*(\d{0,3}[.]?[0-9]+)\s*,\s*(\d{1,3}[.]?[0-9]?)%\s*,\s*(\d{1,3}[.]?[0-9]?)%\s*\)$/i;
  const hslaRegex = /^hsla\(\s*(\d{0,3}[.]?[0-9]+)\s*,\s*(\d{1,3}[.]?[0-9]?)%\s*,\s*(\d{1,3}[.]?[0-9]?)%\s*,\s*([-+]?[0-9]*[.]?[0-9]+)\s*\)$/i;
  /**
   * Returns an RgbColor or RgbaColor object. This utility function is only useful
   * if want to extract a color component. With the color util `toColorString` you
   * can convert a RgbColor or RgbaColor object back to a string.
   *
   * @example
   * // Assigns `{ red: 255, green: 0, blue: 0 }` to color1
   * const color1 = parseToRgb('rgb(255, 0, 0)');
   * // Assigns `{ red: 92, green: 102, blue: 112, alpha: 0.75 }` to color2
   * const color2 = parseToRgb('hsla(210, 10%, 40%, 0.75)');
   */
  function viaPolished(color) {
      if (typeof color !== 'string') {
          throw new Error();
      }
      const normalizedColor = nameToHex(color);
      if (normalizedColor.match(hexRegex)) {
          return {
              red: parseInt(`${normalizedColor[1]}${normalizedColor[2]}`, 16),
              green: parseInt(`${normalizedColor[3]}${normalizedColor[4]}`, 16),
              blue: parseInt(`${normalizedColor[5]}${normalizedColor[6]}`, 16),
          };
      }
      if (normalizedColor.match(hexRgbaRegex)) {
          const alpha = parseFloat((parseInt(`${normalizedColor[7]}${normalizedColor[8]}`, 16) / 255).toFixed(2));
          return {
              red: parseInt(`${normalizedColor[1]}${normalizedColor[2]}`, 16),
              green: parseInt(`${normalizedColor[3]}${normalizedColor[4]}`, 16),
              blue: parseInt(`${normalizedColor[5]}${normalizedColor[6]}`, 16),
              alpha,
          };
      }
      if (normalizedColor.match(reducedHexRegex)) {
          return {
              red: parseInt(`${normalizedColor[1]}${normalizedColor[1]}`, 16),
              green: parseInt(`${normalizedColor[2]}${normalizedColor[2]}`, 16),
              blue: parseInt(`${normalizedColor[3]}${normalizedColor[3]}`, 16),
          };
      }
      if (normalizedColor.match(reducedRgbaHexRegex)) {
          const alpha = parseFloat((parseInt(`${normalizedColor[4]}${normalizedColor[4]}`, 16) / 255).toFixed(2));
          return {
              red: parseInt(`${normalizedColor[1]}${normalizedColor[1]}`, 16),
              green: parseInt(`${normalizedColor[2]}${normalizedColor[2]}`, 16),
              blue: parseInt(`${normalizedColor[3]}${normalizedColor[3]}`, 16),
              alpha,
          };
      }
      const rgbMatched = rgbRegex.exec(normalizedColor);
      if (rgbMatched) {
          return {
              red: parseInt(`${rgbMatched[1]}`, 10),
              green: parseInt(`${rgbMatched[2]}`, 10),
              blue: parseInt(`${rgbMatched[3]}`, 10),
          };
      }
      const rgbaMatched = rgbaRegex.exec(normalizedColor);
      if (rgbaMatched) {
          return {
              red: parseInt(`${rgbaMatched[1]}`, 10),
              green: parseInt(`${rgbaMatched[2]}`, 10),
              blue: parseInt(`${rgbaMatched[3]}`, 10),
              alpha: parseFloat(`${rgbaMatched[4]}`),
          };
      }
      const hslMatched = hslRegex.exec(normalizedColor);
      if (hslMatched) {
          const hue = parseInt(`${hslMatched[1]}`, 10);
          const saturation = parseInt(`${hslMatched[2]}`, 10) / 100;
          const lightness = parseInt(`${hslMatched[3]}`, 10) / 100;
          const rgbColorString = `rgb(${hslToRgb(hue, saturation, lightness)})`;
          const hslRgbMatched = rgbRegex.exec(rgbColorString);
          if (!hslRgbMatched) {
              throw new Error();
          }
          return {
              red: parseInt(`${hslRgbMatched[1]}`, 10),
              green: parseInt(`${hslRgbMatched[2]}`, 10),
              blue: parseInt(`${hslRgbMatched[3]}`, 10),
          };
      }
      const hslaMatched = hslaRegex.exec(normalizedColor);
      if (hslaMatched) {
          const hue = parseInt(`${hslaMatched[1]}`, 10);
          const saturation = parseInt(`${hslaMatched[2]}`, 10) / 100;
          const lightness = parseInt(`${hslaMatched[3]}`, 10) / 100;
          const rgbColorString = `rgb(${hslToRgb(hue, saturation, lightness)})`;
          const hslRgbMatched = rgbRegex.exec(rgbColorString);
          if (!hslRgbMatched) {
              throw new Error();
          }
          return {
              red: parseInt(`${hslRgbMatched[1]}`, 10),
              green: parseInt(`${hslRgbMatched[2]}`, 10),
              blue: parseInt(`${hslRgbMatched[3]}`, 10),
              alpha: parseFloat(`${hslaMatched[4]}`),
          };
      }
      throw new Error();
  }
  
  const canvas = document.createElement('canvas');
    canvas.width = 1;
    canvas.height = 1;
    const ctx = canvas.getContext('2d');
  
  function viaCanvas(_color) {
  // normalize the color
    const color = _color.toLowerCase().trim();
    ctx.clearRect(0, 0, 1, 1);
    // In order to detect invalid values,
    // we can't rely on col being in the same format as what fillStyle is computed as,
    // but we can ask it to implicitly compute a normalized value twice and compare.
    ctx.fillStyle = '#000';
    ctx.fillStyle = color;
    const computed = ctx.fillStyle;
    ctx.fillStyle = '#fff';
    ctx.fillStyle = color;
  
    if (computed !== ctx.fillStyle) {
      return undefined; // invalid color
    }
  
    ctx.fillRect(0, 0, 1, 1);
    return Array.from(ctx.getImageData(0, 0, 1, 1).data);
  }
  

};
</script>

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in CCBot 2.0.0 / Other 0.0.0
Test Ops/sec
Canvas
viaCanvas('#f00');
pending…
Polished
viaPolished('#f00');
pending…

Revisions

You can edit these tests or add even more tests to this page by appending /edit to the URL.

0 Comments