function mirrorContextOperations(ctx, destCtx) {
  if (ctx._removeMirroring) {
    throw new Error("Context is already forwarding operations.");
  }
  ctx.__originalSave = ctx.save;
  ctx.__originalRestore = ctx.restore;
  ctx.__originalRotate = ctx.rotate;
  ctx.__originalScale = ctx.scale;
  ctx.__originalTranslate = ctx.translate;
  ctx.__originalTransform = ctx.transform;
  ctx.__originalSetTransform = ctx.setTransform;
  ctx.__originalResetTransform = ctx.resetTransform;
  ctx.__originalClip = ctx.clip;
  ctx.__originalMoveTo = ctx.moveTo;
  ctx.__originalLineTo = ctx.lineTo;
  ctx.__originalBezierCurveTo = ctx.bezierCurveTo;
  ctx.__originalRect = ctx.rect;
  ctx.__originalClosePath = ctx.closePath;
  ctx.__originalBeginPath = ctx.beginPath;

  ctx._removeMirroring = () => {
    ctx.save = ctx.__originalSave;
    ctx.restore = ctx.__originalRestore;
    ctx.rotate = ctx.__originalRotate;
    ctx.scale = ctx.__originalScale;
    ctx.translate = ctx.__originalTranslate;
    ctx.transform = ctx.__originalTransform;
    ctx.setTransform = ctx.__originalSetTransform;
    ctx.resetTransform = ctx.__originalResetTransform;

    ctx.clip = ctx.__originalClip;
    ctx.moveTo = ctx.__originalMoveTo;
    ctx.lineTo = ctx.__originalLineTo;
    ctx.bezierCurveTo = ctx.__originalBezierCurveTo;
    ctx.rect = ctx.__originalRect;
    ctx.closePath = ctx.__originalClosePath;
    ctx.beginPath = ctx.__originalBeginPath;
    delete ctx._removeMirroring;
  };

  ctx.save = function () {
    destCtx.save();
    this.__originalSave();
  };

  ctx.restore = function () {
    destCtx.restore();
    this.__originalRestore();
  };

  ctx.translate = function (x, y) {
    destCtx.translate(x, y);
    this.__originalTranslate(x, y);
  };

  ctx.scale = function (x, y) {
    destCtx.scale(x, y);
    this.__originalScale(x, y);
  };

  ctx.transform = function (a, b, c, d, e, f) {
    destCtx.transform(a, b, c, d, e, f);
    this.__originalTransform(a, b, c, d, e, f);
  };

  ctx.setTransform = function (a, b, c, d, e, f) {
    destCtx.setTransform(a, b, c, d, e, f);
    this.__originalSetTransform(a, b, c, d, e, f);
  };

  ctx.resetTransform = function () {
    destCtx.resetTransform();
    this.__originalResetTransform();
  };

  ctx.rotate = function (angle) {
    destCtx.rotate(angle);
    this.__originalRotate(angle);
  };

  ctx.clip = function (rule) {
    destCtx.clip(rule);
    this.__originalClip(rule);
  };

  ctx.moveTo = function (x, y) {
    destCtx.moveTo(x, y);
    this.__originalMoveTo(x, y);
  };

  ctx.lineTo = function (x, y) {
    destCtx.lineTo(x, y);
    this.__originalLineTo(x, y);
  };

  ctx.bezierCurveTo = function (cp1x, cp1y, cp2x, cp2y, x, y) {
    destCtx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
    this.__originalBezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
  };

  ctx.rect = function (x, y, width, height) {
    destCtx.rect(x, y, width, height);
    this.__originalRect(x, y, width, height);
  };

  ctx.closePath = function () {
    destCtx.closePath();
    this.__originalClosePath();
  };

  ctx.beginPath = function () {
    destCtx.beginPath();
    this.__originalBeginPath();
  };
}
Overrides certain methods on a 2d ctx so that when they are called they
will also call the same method on the destCtx. The methods that are
overridden are all the transformation state modifiers, path creation, and
save/restore. We only forward these specific methods because they are the
only state modifiers that we cannot copy over when we switch contexts.

To remove mirroring call `ctx._removeMirroring()`.

@param {Object} ctx - The 2d canvas context that will duplicate its calls on
  the destCtx.
@param {Object} destCtx - The 2d canvas context that will receive the
  forwarded calls.