Transform the calls to a new coordinate system. The arguments are those to drawImage().
function transformedCalls(calls, args) {
  var image = args[0],
      sx = 0,
      sy = 0,
      sWidth = image.width,
      sHeight = image.height,
      dx,
      dy,
      dWidth = image.width,
      dHeight = image.height;

  if (args.length == 3) {
    // void ctx.drawImage(image, dx, dy);
    dx = args[1];
    dy = args[2];
  } else if (args.length == 5) {
    // void ctx.drawImage(image, dx, dy, dWidth, dHeight);
    dx = args[1];
    dy = args[2];
    dWidth = args[3];
    dHeight = args[4];
  } else if (args.length == 9) {
    // void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
    sx = args[1];
    sy = args[2];
    sWidth = args[3];
    sHeight = args[4];
    dx = args[5];
    dy = args[6];
    dWidth = args[7];
    dHeight = args[8];
  }
  // Other arities will make the browser throw an error on ctx.drawImage.apply

  var xScaling = getScaleFactor(sx, sx + sWidth, dx, dx + dWidth),
      xScale   = makeScale( sx, sx + sWidth, dx, dx + dWidth),
      yScaling = getScaleFactor(sy, sy + sHeight, dy, dy + dHeight),
      yScale   = makeScale( sy, sy + sHeight, dy, dy + dHeight);

  // These calls are more complex:
  // arc
  // arcTo
  // ellipse

  // TODO: clip calls outside of the source rectangle.
  var transformCall = function(originalCall) {
    var call = originalCall.slice(),  // make a copy
        type = call[0];
    if (type in CALLS_XY) {
      var xys = CALLS_XY[type];
      if (typeof(xys) == 'number') xys = [xys];
      xys.forEach(function(pos) {
        call[1 + pos] = xScale(call[1 + pos]);
        call[2 + pos] = yScale(call[2 + pos]);
      });
    }
    if (type in CALLS_WH) {
      var whs = CALLS_WH[type];
      if (typeof(whs) == 'number') whs = [whs];
      whs.forEach(function(pos) {
        call[1 + pos] *= xScaling;
        call[2 + pos] *= yScaling;
      });
    }
    return call;
  };

  return calls.map(transformCall);
}