Skip to content

Commit

Permalink
WIP: Web Share Target graphics are resized using OffscreenCanvas
Browse files Browse the repository at this point in the history
  • Loading branch information
DougReeder committed Feb 3, 2024
1 parent 6eb4e4c commit 4973872
Showing 1 changed file with 49 additions and 14 deletions.
63 changes: 49 additions & 14 deletions src/util/imageFileToDataUrl.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// imageFileToDataUrl.js - downscales an image & converts to data URL
// Copyright © 2017-2024 Doug Reeder

/* eslint-env browser, worker */

const MAX_SIZE = 200_000; // max content size / 3

Expand All @@ -16,20 +16,23 @@ async function imageFileToDataUrl(file) {
// }
}

let dataUrl;
// avoids converting vector to raster if reasonably possible
// URL.createObjectURL is not available in a Service Worker
if ((file.size < (MAX_SIZE * 1.4) && file.type === 'image/svg+xml') || 'function' !== typeof URL.createObjectURL) {
const dataUrl = await fileToDataUrl(file);
return {dataUrl, alt: texts.join('\n')};
if ((file.size < (MAX_SIZE * 1.4) && file.type === 'image/svg+xml')) {
dataUrl = await fileToDataUrl(file);
// } else if ('function' === typeof URL.createObjectURL) { // not available in a Service Worker
// const objectUrl = URL.createObjectURL(file);
// dataUrl = await evaluateImage(file, objectUrl);
// URL.revokeObjectURL(objectUrl);
} else {
dataUrl = await evaluateImageBitmap(file);
}

const objectUrl = URL.createObjectURL(file);
const dataUrl = await evaluateImage(file, objectUrl);
URL.revokeObjectURL(objectUrl);

return {dataUrl, alt: texts.join('\n')};
}

const NOT_CROSS_BROWSER = ['image/tiff', 'image/jp2', 'image/jxl', 'image/avci', 'image/heif', 'image/heic'];

function evaluateImage(blob, objectURL) {
return new Promise((resolve, reject) => {
const img = new Image();
Expand All @@ -39,8 +42,7 @@ function evaluateImage(blob, objectURL) {
// Modern browsers respect the orientation data in EXIF.
// console.log("img onload size:", this.width, this.height);

if (this.width > 1280 || this.height > 1280 || blob.size > MAX_SIZE ||
['image/tiff', 'image/jp2', 'image/jxl', 'image/avci', 'image/heif', 'image/heic'].includes(blob.type)) {
if (this.width > 1280 || this.height > 1280 || blob.size > MAX_SIZE || NOT_CROSS_BROWSER.includes(blob.type)) {
resolve(resize(img, blob.type));
} else {
resolve(await fileToDataUrl(blob));
Expand Down Expand Up @@ -76,12 +78,12 @@ function resize(img, fileType) {
const canvas = document.createElement('canvas');
if (img.width >= img.height) { // constrain width
canvas.width = Math.min(1280, img.width); // logical width
canvas.height = (img.height / img.width) * canvas.width;
canvas.height = Math.round((img.height / img.width) * canvas.width);
} else { // constrain height
canvas.height = Math.min(1280, img.height); // logical height
canvas.width = (img.width / img.height) * canvas.height;
canvas.width = Math.round((img.width / img.height) * canvas.height);
}
console.info(`resizing to ${canvas.width}×${canvas.height}`);
console.info(`resizing to ${canvas.width}×${canvas.height} using Canvas`);

const context = canvas.getContext('2d');
if (!(['image/jpeg', 'image/bmp'].includes(fileType))) { // might have transparent background
Expand All @@ -97,4 +99,37 @@ function resize(img, fileType) {
return dataUrl;
}

async function evaluateImageBitmap(file) {
let imageBitmap = await createImageBitmap(file);
if ((imageBitmap.width > 1280 || imageBitmap.height > 1280 || file.size > MAX_SIZE ||
NOT_CROSS_BROWSER.includes(file.type)) &&
!('image/jpeg' === file.type && imageBitmap.height > imageBitmap.width)) {
let canvasWidth, canvasHeight;
if (imageBitmap.width > imageBitmap.height) {
canvasWidth = Math.min(imageBitmap.width, 1280);
canvasHeight = Math.round(imageBitmap.height * canvasWidth / imageBitmap.width);
} else {
canvasHeight = Math.min(imageBitmap.height, 1280);
canvasWidth = Math.round(imageBitmap.width * canvasHeight / imageBitmap.height);
}
if (canvasWidth !== imageBitmap.width || canvasHeight !== imageBitmap.height) {
imageBitmap.close();
imageBitmap = await createImageBitmap(file, {resizeWidth: canvasWidth, resizeHeight: canvasHeight});
}
const canvas = new OffscreenCanvas(canvasWidth, canvasHeight);
console.info(`resizing “${file.name}” to ${canvas.width}×${canvas.height} using OffscreenCanvas`);
const context = canvas.getContext("bitmaprenderer");
context.transferFromImageBitmap(imageBitmap); // consumes bitmap
let blob = await canvas.convertToBlob({type: 'image/webp', quality: 0.4});
if ('image/png' === blob.type) {
blob = await canvas.convertToBlob({type: 'image/jpeg', quality: 0.4});
}
return await fileToDataUrl(blob);
} else {
console.info(`importing “${file.name}” at original resolution of ${imageBitmap.width}×${imageBitmap.height}`);
imageBitmap.close();
return await fileToDataUrl(file);
}
}

export {imageFileToDataUrl, fileToDataUrl, evaluateImage};

0 comments on commit 4973872

Please sign in to comment.