Skip to content

Instantly share code, notes, and snippets.

@pseudosavant
Forked from MonsieurV/createImageBitmap.js
Last active March 20, 2022 12:30
Show Gist options
  • Save pseudosavant/a6d970b945ae85ef4dbc43200da94faf to your computer and use it in GitHub Desktop.
Save pseudosavant/a6d970b945ae85ef4dbc43200da94faf to your computer and use it in GitHub Desktop.
createImageBitmap polyfill with support for CanvasImageSource (img, video, canvas) sources, Blobs, and ImageData. https://createimagebitmap-polyfill.glitch.me/
/*
* Polyfill for createImageBitmap
* https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/createImageBitmap
*
* Supports CanvasImageSource (img, video, canvas) sources, Blobs, and ImageData.
*
* From:
* - https://dev.to/nektro/createimagebitmap-polyfill-for-safari-and-edge-228
* - https://gist.github.com/MonsieurV/fb640c29084c171b4444184858a91bc7
* Updated by:
* - Yoan Tournade <yoan@ytotech.com>
* - diachedelic, https://gist.github.com/diachedelic
* - Paul Ellis, https://pseudosavant.com
*/
(function createImageBitmapIIFE(global){
function isCanvasImageSource(el) {
const validElements = ['img', 'video', 'canvas'];
return (el && el.tagName && validElements.includes(el.tagName.toLowerCase()));
}
function idealSize(currentValue, newValue, numerator, denominator){
if (typeof newValue === 'number') return newValue;
if (typeof numerator !== 'number' || typeof denominator !== 'number') return currentValue;
return (numerator / denominator) * currentValue;
}
if (!('createImageBitmap' in global) || test) {
global.createImageBitmap = async function polyfillCreateImageBitmap(data, opts) {
return new Promise((resolve, reject) => {
opts = opts || {};
let dataURL;
const canvas = document.createElement('canvas');
try {
const ctx = canvas.getContext('2d');
if (data instanceof Blob) {
dataURL = URL.createObjectURL(data);
} else if (isCanvasImageSource(data)) {
const width = data.naturalWidth || data.videoWidth || data.clientWidth || data.width
const height = data.naturalHeight || data.videoHeight || data.clientHeight || data.height
canvas.width = idealSize(width, opts.resizeWidth, opts.resizeHeight, height);
canvas.height = idealSize(height, opts.resizeHeight, opts.resizeWidth, width);
ctx.drawImage(data, 0, 0, canvas.width, canvas.height);
dataURL = canvas.toDataURL();
} else if (data instanceof ImageData) {
canvas.width = idealSize(data.width, opts.resizeWidth, opts.resizeHeight, data.height);;
canvas.height = idealSize(data.height, opts.resizeHeight, opts.resizeWidth, data.width);
ctx.putImageData(data,0,0);
dataURL = canvas.toDataURL();
} else {
reject('createImageBitmap does not handle the provided image source type');
}
const img = new Image();
img.onerror = reject;
img.onload = () => resolve(img);
img.src = dataURL;
} finally {
// avoid memory leaks on iOS Safari, see https://stackoverflow.com/a/52586606
canvas.width = 0;
canvas.height = 0;
}
});
};
}
})(this);
@diachedelic
Copy link

diachedelic commented Aug 20, 2020

iOS Safari 12+ has a memory leak where, unless you set the width and height of a canvas to zero, it does not release the resources, fairly quickly crashing your page/webview if you are working with photos. I added that in a finally clause:

...
      const canvas = document.createElement("canvas");

      try {
        const ctx = canvas.getContext("2d");
...
        const img = new Image();
        img.onload = () => resolve(img);
        img.onerror = reject;
        img.src = dataURL;
      } finally {
        // avoid memory leaks on iOS Safari, see https://stackoverflow.com/a/52586606
        canvas.width = 0;
        canvas.height = 0;
      }

Source: openlayers/openlayers#9291

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment