chromium/chrome/test/data/image_decoding/image_decoding.js

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Run benchmark for at least this many milliseconds.
var minTotalTimeMs = 10 * 1000;
// Run benchmark at least this many times.
var minIterations = 5;
// Discard results for this many milliseconds after startup.
var warmUpTimeMs = 1000;
var benchmarkStartTime;
var loadStartTimeMs;
var loadingTimesMs = [];
var isDone = false;
var img;

var now = (function() {
  if (window.performance)
    return (window.performance.now ||
            window.performance.webkitNow).bind(window.performance);
  return Date.now.bind(window);
})();

getImageFormat = function() {
  if (document.location.search) {
    return document.location.search.substr(1);
  }
  return "jpg";
}

prepareImageElement = function() {
  img = document.createElement('img');
  // Scale the image down to the display size to make sure the entire image is
  // decoded. If the image is much larger than the display, some browsers might
  // only decode the visible portion of the image, which would skew the results
  // of this test.
  img.setAttribute('width', '100%');
  img.setAttribute('height', '100%');
  document.body.appendChild(img);
  console.log("Running benchmark for at least " + minTotalTimeMs +
              " ms and at least " + minIterations + " times.");
  document.getElementById('status').innerHTML = "Benchmark running.";
  setStatus(getImageFormat().toUpperCase() + " benchmark running.");
  benchmarkStartTimeMs = now();
}

setStatus = function(status) {
  document.getElementById('status').innerHTML = status;
}

runBenchmark = function() {
  setStatus("Preparing benchmark.");
  prepareImageElement();
  startLoadingImage();
}

var iteration = (new Date).getTime();

startLoadingImage = function() {
  img.style.display = 'none';
  img.setAttribute('onload', '');
  img.setAttribute('src', '');
  img.addEventListener('load', onImageLoaded);

  // Setting 'src' and 'display' above causes a repaint. Let it finish before
  // loading a new image. Ensures loading and painting don't overlap.
  requestAnimationFrame(function() {
    loadStartTimeMs = now();
    // Use a unique URL for each test iteration to work around image caching.
    img.setAttribute('src', 'droids.' + getImageFormat() + '?' + iteration);
    iteration += 1;
  });
}

var requestAnimationFrame = (function() {
  return window.requestAnimationFrame       ||
         window.webkitRequestAnimationFrame ||
         window.mozRequestAnimationFrame    ||
         window.oRequestAnimationFrame      ||
         window.msRequestAnimationFrame     ||
         function(callback) {
           window.setTimeout(callback, 1000 / 60);
         };
})().bind(window);

onImageLoaded = function() {
  var nowMs = now();
  var loadingTimeMs = nowMs - loadStartTimeMs;
  if (nowMs - benchmarkStartTimeMs >= warmUpTimeMs) {
    loadingTimesMs.push(loadingTimeMs);
  }
  if (nowMs - benchmarkStartTimeMs < minTotalTimeMs ||
      loadingTimesMs.length < minIterations) {
    // Repaint happens right after the image is loaded. Make sure painting
    // is completed before making the image visible. Setting the image to
    // visible will start decoding. After decoding and painting are completed
    // we'll start next test iteration.
    // Double rAF is needed here otherwise the image is barely visible.
    requestAnimationFrame(function() {
      img.style.display = '';
      requestAnimationFrame(startLoadingImage);
    });
  } else {
    isDone = true;
    console.log("loadingTimes: " + loadingTimesMs);
    setStatus(getImageFormat().toUpperCase() + " benchmark finished: " +
              loadingTimesMs);
  }
}

averageLoadingTimeMs = function() {
  if (!loadingTimesMs.length)
    return 0;
  var total = 0;
  for (var i = 0; i < loadingTimesMs.length; ++i) {
    total += loadingTimesMs[i];
  }
  return total / loadingTimesMs.length;
}