UNPKG

@directus/api

Version:

Directus is a real-time API and App dashboard for managing SQL database content

133 lines (132 loc) 5.42 kB
import { clamp } from 'lodash-es'; export function resolvePreset({ transformationParams, acceptFormat }, file) { const transforms = transformationParams.transforms ? [...transformationParams.transforms] : []; if (transformationParams.format || transformationParams.quality) { transforms.push([ 'toFormat', getFormat(file, transformationParams.format, acceptFormat), { quality: transformationParams.quality ? Number(transformationParams.quality) : undefined, }, ]); } if ((transformationParams.width || transformationParams.height) && file.width && file.height) { const toWidth = transformationParams.width ? Number(transformationParams.width) : undefined; const toHeight = transformationParams.height ? Number(transformationParams.height) : undefined; const toFocalPointX = transformationParams.focal_point_x ? Number(transformationParams.focal_point_x) : file.focal_point_x; const toFocalPointY = transformationParams.focal_point_y ? Number(transformationParams.focal_point_y) : file.focal_point_y; /* * Focal point cropping only works with a fixed size (width x height) when `cover`ing, * since the other modes show the whole image. Sharp by default also simply scales up/down * when only supplied with one dimension, so we **must** check, else we break existing behaviour. * See: https://sharp.pixelplumbing.com/api-resize#resize * Also only crop to focal point when explicitly defined so that users can still `cover` with * other parameters like `position` and `gravity` - Else fall back to regular behaviour */ if ((transformationParams.fit === undefined || transformationParams.fit === 'cover') && toWidth && toHeight && toFocalPointX !== null && toFocalPointY !== null) { const transformArgs = getResizeArguments({ w: file.width, h: file.height }, { w: toWidth, h: toHeight }, { x: toFocalPointX, y: toFocalPointY }); transforms.push([ 'resize', { width: transformArgs.width, height: transformArgs.height, fit: transformationParams.fit, withoutEnlargement: transformationParams.withoutEnlargement ? Boolean(transformationParams.withoutEnlargement) : undefined, }, ], ['extract', transformArgs.region]); } else { transforms.push([ 'resize', { width: toWidth, height: toHeight, fit: transformationParams.fit, withoutEnlargement: transformationParams.withoutEnlargement ? Boolean(transformationParams.withoutEnlargement) : undefined, }, ]); } } return transforms; } function getFormat(file, format, acceptFormat) { const fileType = file.type?.split('/')[1]; if (format) { if (format !== 'auto') { return format; } if (acceptFormat) { return acceptFormat; } if (fileType && ['avif', 'webp', 'tiff'].includes(fileType)) { return 'png'; } } return fileType || 'jpg'; } /** * Try to extract a file format from an array of `Transformation`'s. */ export function maybeExtractFormat(transforms) { const toFormats = transforms.filter((t) => t[0] === 'toFormat'); const lastToFormat = toFormats[toFormats.length - 1]; return lastToFormat ? lastToFormat[1]?.toString() : undefined; } /** * Resize an image but keep it centered on the focal point. * Based on the method outlined in https://github.com/lovell/sharp/issues/1198#issuecomment-384591756 */ function getResizeArguments(original, target, focalPoint) { const { width, height, factor } = getIntermediateDimensions(original, target); const region = getExtractionRegion(factor, focalPoint ?? { x: original.w / 2, y: original.h / 2 }, target, { w: width, h: height, }); return { width, height, region }; } /** * Calculates the dimensions of the intermediate (resized) image. */ function getIntermediateDimensions(original, target) { const hRatio = original.h / target.h; const wRatio = original.w / target.w; let factor; let width; let height; if (hRatio < wRatio) { factor = hRatio; height = Math.round(target.h); width = Math.round(original.w / factor); } else { factor = wRatio; width = Math.round(target.w); height = Math.round(original.h / factor); } return { width, height, factor }; } /** * Calculates the Region to extract from the intermediate image. */ function getExtractionRegion(factor, focalPoint, target, intermediate) { const newXCenter = focalPoint.x / factor; const newYCenter = focalPoint.y / factor; return { left: clamp(Math.round(newXCenter - target.w / 2), 0, intermediate.w - target.w), top: clamp(Math.round(newYCenter - target.h / 2), 0, intermediate.h - target.h), width: target.w, height: target.h, }; }