@loaders.gl/images
Version:
Framework-independent loaders and writers for images (PNG, JPG, ...)
96 lines (81 loc) • 3.08 kB
text/typescript
// Image loading/saving for browser and Node.js
import {ImageDataType} from '../../types';
import {getImageSize} from '../category-api/parsed-image-api';
// @ts-ignore TS2339: Property does not exist on type
const encodeImageNode = globalThis.loaders?.encodeImageNode;
/**
* Returns data bytes representing a compressed image in PNG or JPG format,
* This data can be saved using file system (f) methods or used in a request.
* @param image - ImageBitmap Image or Canvas
* @param options
* param opt.type='png' - png, jpg or image/png, image/jpg are valid
* param mimeType= - Whether to include a data URI header
*/
export async function encodeImage(
image: ImageDataType,
options?: {[key: string]: any}
): Promise<ArrayBuffer> {
options = options || {};
options.image = options.image || ({} as {[key: string]: any});
return encodeImageNode
? encodeImageNode(image, {type: options.image.mimeType})
: encodeImageInBrowser(image, options);
}
// In case we get exceptions from canvas.toBlob(resolve, type, quality)
let qualityParamSupported = true;
/**
*
* @param image
* @param options
* @note Based on canvas.toBlob
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob
*/
async function encodeImageInBrowser(image, options) {
const {mimeType, jpegQuality} = options.image;
const {width, height} = getImageSize(image);
// create a canvas and resize it to the size of our image
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
drawImageToCanvas(image, canvas);
// The actual encoding is done asynchronously with `canvas.toBlob()`
const blob = await new Promise<Blob | null>((resolve) => {
// get it back as a Blob
if (jpegQuality && qualityParamSupported) {
try {
canvas.toBlob(resolve, mimeType, jpegQuality);
return;
} catch (error) {
qualityParamSupported = false;
}
}
canvas.toBlob(resolve, mimeType);
});
if (!blob) {
throw new Error('image encoding failed');
}
return await blob.arrayBuffer();
}
function drawImageToCanvas(image, canvas, x = 0, y = 0) {
// Try optimized path for ImageBitmaps via bitmaprenderer context
if (x === 0 && y === 0 && typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap) {
const context = canvas.getContext('bitmaprenderer');
if (context) {
// transfer the ImageBitmap to it
context.transferFromImageBitmap(image);
return canvas;
}
}
// Available on most platforms, except IE11 and Andriod WebViews...
const context = canvas.getContext('2d');
if (image.data) {
// ImageData constructor expects clamped array even though getImageData does not return a clamped array...
const clampedArray = new Uint8ClampedArray(image.data);
const imageData = new ImageData(clampedArray, image.width, image.height);
context.putImageData(imageData, 0, 0);
return canvas;
}
// Fall back to generic image/image bitmap rendering path
context.drawImage(image, 0, 0);
return canvas;
}