import shim from '@joplin/lib/shim'; import Logger from '@joplin/utils/Logger'; import { ImageManipulator, SaveFormat } from 'expo-image-manipulator'; import fileToImage from './fileToImage.web'; import FsDriverWeb from '../fs-driver/fs-driver-rn.web'; import getImageDimensions from './getImageDimensions'; const logger = Logger.create('resizeImage'); type OutputFormat = 'PNG' | 'JPEG'; interface Options { inputPath: string; outputPath: string; maxWidth: number; maxHeight: number; format: OutputFormat; // A number from 0 to 1, where 1 is least compressed. quality: number; } const resizeImage = async (options: Options) => { type Sized = { width: number; height: number }; const computeScale = (image: Sized) => { // Choose a scale factor such that the resized image fits within a // maxWidth x maxHeight box. const scale = Math.min( options.maxWidth / image.width, options.maxHeight / image.height, ); return scale; }; if (shim.mobilePlatform() === 'web') { const image = await fileToImage(options.inputPath); try { const canvas = document.createElement('canvas'); const scale = computeScale(image.image); canvas.width = image.image.width * scale; canvas.height = image.image.height * scale; const ctx = canvas.getContext('2d'); ctx.drawImage(image.image, 0, 0, canvas.width, canvas.height); const blob = await new Promise((resolve, reject) => { try { canvas.toBlob( (blob) => resolve(blob), `image/${options.format.toLowerCase()}`, options.quality, ); } catch (error) { reject(error); } }); await (shim.fsDriver() as FsDriverWeb).writeFile( options.outputPath, await blob.arrayBuffer(), 'Buffer', ); } finally { image.free(); } } else { const originalSize = await getImageDimensions(options.inputPath); logger.debug('Processing image with size', originalSize.width, 'x', originalSize.height); let context = ImageManipulator.manipulate(options.inputPath); // Only rescale the image if it's bigger than the maximum size: if (originalSize.width > options.maxWidth || originalSize.height > options.maxHeight) { const scale = computeScale(originalSize); context = context.resize({ width: originalSize.width * scale, height: originalSize.height * scale, }); } const final = await context.renderAsync(); const saved = await final.saveAsync({ format: options.format === 'PNG' ? SaveFormat.PNG : SaveFormat.JPEG, compress: options.quality, }); const resizedImagePath = saved.uri; logger.info('Resized image ', resizedImagePath); logger.info(`Moving ${resizedImagePath} => ${options.outputPath}`); await shim.fsDriver().copy(resizedImagePath, options.outputPath); try { await shim.fsDriver().unlink(resizedImagePath); } catch (error) { logger.warn('Error when unlinking cached file: ', error); } } }; export default resizeImage;