1
import type { File } from '@directus/types';
2
import { clamp } from 'lodash-es';
3
import type { Region } from 'sharp';
4
import type { Transformation, TransformationFormat, TransformationSet } from '../types/index.js';
6
export function resolvePreset({ transformationParams, acceptFormat }: TransformationSet, file: File): Transformation[] {
7
const transforms = transformationParams.transforms ? [...transformationParams.transforms] : [];
9
if (transformationParams.format || transformationParams.quality) {
12
getFormat(file, transformationParams.format, acceptFormat),
14
quality: transformationParams.quality ? Number(transformationParams.quality) : undefined,
19
if ((transformationParams.width || transformationParams.height) && file.width && file.height) {
20
const toWidth = transformationParams.width ? Number(transformationParams.width) : undefined;
21
const toHeight = transformationParams.height ? Number(transformationParams.height) : undefined;
23
const toFocalPointX = transformationParams.focal_point_x
24
? Number(transformationParams.focal_point_x)
27
const toFocalPointY = transformationParams.focal_point_y
28
? Number(transformationParams.focal_point_y)
32
* Focal point cropping only works with a fixed size (width x height) when `cover`ing,
33
* since the other modes show the whole image. Sharp by default also simply scales up/down
34
* when only supplied with one dimension, so we **must** check, else we break existing behaviour.
35
* See: https://sharp.pixelplumbing.com/api-resize#resize
36
* Also only crop to focal point when explicitly defined so that users can still `cover` with
37
* other parameters like `position` and `gravity` - Else fall back to regular behaviour
40
(transformationParams.fit === undefined || transformationParams.fit === 'cover') &&
43
toFocalPointX !== null &&
44
toFocalPointY !== null
46
const transformArgs = getResizeArguments(
47
{ w: file.width, h: file.height },
48
{ w: toWidth, h: toHeight },
49
{ x: toFocalPointX, y: toFocalPointY },
56
width: transformArgs.width,
57
height: transformArgs.height,
58
fit: transformationParams.fit,
59
withoutEnlargement: transformationParams.withoutEnlargement
60
? Boolean(transformationParams.withoutEnlargement)
64
['extract', transformArgs.region],
72
fit: transformationParams.fit,
73
withoutEnlargement: transformationParams.withoutEnlargement
74
? Boolean(transformationParams.withoutEnlargement)
86
format: TransformationSet['transformationParams']['format'],
87
acceptFormat: TransformationSet['acceptFormat'],
88
): TransformationFormat {
89
const fileType = file.type?.split('/')[1] as TransformationFormat | undefined;
92
if (format !== 'auto') {
100
if (fileType && ['avif', 'webp', 'tiff'].includes(fileType)) {
105
return fileType || 'jpg';
109
* Try to extract a file format from an array of `Transformation`'s.
111
export function maybeExtractFormat(transforms: Transformation[]): string | undefined {
112
const toFormats = transforms.filter((t) => t[0] === 'toFormat');
113
const lastToFormat = toFormats[toFormats.length - 1];
114
return lastToFormat ? lastToFormat[1]?.toString() : undefined;
117
type Dimensions = { w: number; h: number };
118
type FocalPoint = { x: number; y: number };
121
* Resize an image but keep it centered on the focal point.
122
* Based on the method outlined in https://github.com/lovell/sharp/issues/1198#issuecomment-384591756
124
function getResizeArguments(
125
original: Dimensions,
127
focalPoint?: FocalPoint | null,
128
): { width: number; height: number; region: Region } {
129
const { width, height, factor } = getIntermediateDimensions(original, target);
131
const region = getExtractionRegion(factor, focalPoint ?? { x: original.w / 2, y: original.h / 2 }, target, {
136
return { width, height, region };
140
* Calculates the dimensions of the intermediate (resized) image.
142
function getIntermediateDimensions(
143
original: Dimensions,
145
): { width: number; height: number; factor: number } {
146
const hRatio = original.h / target.h;
147
const wRatio = original.w / target.w;
153
if (hRatio < wRatio) {
155
height = Math.round(target.h);
156
width = Math.round(original.w / factor);
159
width = Math.round(target.w);
160
height = Math.round(original.h / factor);
163
return { width, height, factor };
167
* Calculates the Region to extract from the intermediate image.
169
function getExtractionRegion(
171
focalPoint: FocalPoint,
173
intermediate: Dimensions,
175
const newXCenter = focalPoint.x / factor;
176
const newYCenter = focalPoint.y / factor;
179
left: clamp(Math.round(newXCenter - target.w / 2), 0, intermediate.w - target.w),
180
top: clamp(Math.round(newYCenter - target.h / 2), 0, intermediate.h - target.h),