// 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 { 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((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; }