Skip to content

Commit

Permalink
[js] using OffscreenCanvas when DOM is not available (microsoft#19033)
Browse files Browse the repository at this point in the history
### Description
when DOM API is not avaiable, using OffscreenCanvas


### Motivation and Context
In some environment like service worker or web worker, the DOM API is
not avaiable, we can use OffscreenCanvas API to replace
`document.createElement('canvas')`.
Most of the APIs of OffscreenCanvas and HTMLCanvasElement are the same,
except that `toDataUrl` is missing.

It fix this issues microsoft#19032
  • Loading branch information
jiangzhuo authored Jan 12, 2024
1 parent abbc3d9 commit b1f258e
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 12 deletions.
15 changes: 11 additions & 4 deletions common/lib/tensor-conversion-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import {Tensor} from './tensor.js';
* implementation of Tensor.toDataURL()
*/
export const tensorToDataURL = (tensor: Tensor, options?: TensorToDataUrlOptions): string => {
const canvas = document.createElement('canvas');
const canvas = typeof document !== 'undefined' ? document.createElement('canvas') : (new OffscreenCanvas(1, 1));
canvas.width = tensor.dims[3];
canvas.height = tensor.dims[2];
const pixels2DContext = canvas.getContext('2d');
const pixels2DContext =
canvas.getContext('2d') as (CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D | null);

if (pixels2DContext != null) {
// Default values for height and width & format
Expand Down Expand Up @@ -88,7 +89,11 @@ export const tensorToDataURL = (tensor: Tensor, options?: TensorToDataUrlOptions
pixels2DContext.fillRect(j, i, 1, 1);
}
}
return canvas.toDataURL();
if ('toDataURL' in canvas) {
return canvas.toDataURL();
} else {
throw new Error('toDataURL is not supported');
}
} else {
throw new Error('Can not access image data');
}
Expand All @@ -98,7 +103,9 @@ export const tensorToDataURL = (tensor: Tensor, options?: TensorToDataUrlOptions
* implementation of Tensor.toImageData()
*/
export const tensorToImageData = (tensor: Tensor, options?: TensorToImageDataOptions): ImageData => {
const pixels2DContext = document.createElement('canvas').getContext('2d');
const pixels2DContext = typeof document !== 'undefined' ?
document.createElement('canvas').getContext('2d') :
new OffscreenCanvas(1, 1).getContext('2d') as OffscreenCanvasRenderingContext2D;
let image: ImageData;
if (pixels2DContext != null) {
// Default values for height and width & format
Expand Down
34 changes: 26 additions & 8 deletions common/lib/tensor-factory-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,31 @@ export const tensorFromImage = async(
let data: Uint8ClampedArray|undefined;
let bufferToTensorOptions: BufferToTensorOptions = options ?? {};

const createCanvas = () => {
if (typeof document !== 'undefined') {
return document.createElement('canvas');
} else if (typeof OffscreenCanvas !== 'undefined') {
return new OffscreenCanvas(1, 1);
} else {
throw new Error('Canvas is not supported');
}
};
const createCanvasContext = (canvas: HTMLCanvasElement|OffscreenCanvas) => {
if (canvas instanceof HTMLCanvasElement) {
return canvas.getContext('2d');
} else if (canvas instanceof OffscreenCanvas) {
return canvas.getContext('2d') as OffscreenCanvasRenderingContext2D;
} else {
return null;
}
};
// filling and checking image configuration options
if (isHTMLImageEle) {
// HTMLImageElement - image object - format is RGBA by default
const canvas = document.createElement('canvas');
const canvas = createCanvas();
canvas.width = image.width;
canvas.height = image.height;
const pixels2DContext = canvas.getContext('2d');
const pixels2DContext = createCanvasContext(canvas);

if (pixels2DContext != null) {
let height = image.height;
Expand Down Expand Up @@ -166,12 +184,12 @@ export const tensorFromImage = async(
bufferToTensorOptions.width = width;

if (options !== undefined) {
const tempCanvas = document.createElement('canvas');
const tempCanvas = createCanvas();

tempCanvas.width = width;
tempCanvas.height = height;

const pixels2DContext = tempCanvas.getContext('2d');
const pixels2DContext = createCanvasContext(tempCanvas);

if (pixels2DContext != null) {
pixels2DContext.putImageData(image, 0, 0);
Expand All @@ -188,10 +206,10 @@ export const tensorFromImage = async(
throw new Error('Please provide image config with format for Imagebitmap');
}

const canvas = document.createElement('canvas');
const canvas = createCanvas();
canvas.width = image.width;
canvas.height = image.height;
const pixels2DContext = canvas.getContext('2d');
const pixels2DContext = createCanvasContext(canvas);

if (pixels2DContext != null) {
const height = image.height;
Expand All @@ -206,8 +224,8 @@ export const tensorFromImage = async(
}
} else if (isString) {
return new Promise((resolve, reject) => {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
const canvas = createCanvas();
const context = createCanvasContext(canvas);
if (!image || !context) {
return reject();
}
Expand Down

0 comments on commit b1f258e

Please sign in to comment.