@bitpatty/imgproxy-url-builder
Version:
A TypeScript helper library for building imgproxy URLs
1,421 lines (1,352 loc) • 78.3 kB
JavaScript
/**
* Masks the lowest 6 bits of the specified number
*
* @param num The number
* @returns The lowest 6 bits
*/
const low6 = (num) => num & 0b0011_1111;
/**
* UTF-8 encodes the specified message
*
* @param msg The message
* @returns The encoded message as a Uint8Array of bytes
*/
const utf8encode = (msg) => {
const encoder = new TextEncoder();
return encoder.encode(msg);
};
/**
* Parses a hex character and returns its numeric value
*
* @param char The hex char
* @returns The numeric value
*/
const parseHexChar = (char) => {
if (['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'].includes(char))
return +char;
switch (char.toUpperCase()) {
case 'A':
return 0xa;
case 'B':
return 0xb;
case 'C':
return 0xc;
case 'D':
return 0xd;
case 'E':
return 0xe;
case 'F':
return 0xf;
}
throw new Error(`Invalid hex char: ${char}`);
};
/**
* Parses a string containing hex characters
* into an array of bytes
*
* @param str The string
* @returns The Uint8Array of parsed bytes
*/
const parseHexString = (str) => {
const res = new Uint8Array(Math.ceil(str.length / 2));
for (let i = 0; i < str.length; i += 2) {
res[i / 2] = (parseHexChar(str[i]) << 4) | parseHexChar(str[i + 1] || '0');
}
return res;
};
/**
* Encodes the lowest 6 bits of the specified
* number to its base64url representation
*
* @param b The number
* @returns The base64url encoded character
*/
const base64urlChar = (b) => {
const r = low6(b);
// Uppercase letters
if (r <= 25)
return String.fromCharCode(65 + r);
// Lowercase letters
if (r <= 51)
return String.fromCharCode(97 + r - 26);
// Numbers
if (r <= 61)
return String.fromCharCode(48 + r - 52);
// Base64 URL replacements for '+' and '/'
return r === 62 ? '-' : '_';
};
/**
* Encodes the specified array of bytes to
* its base64url representation
*
* @param bytes The Uint8Array of bytes
* @returns The base64url string
*/
const base64urlEncode = (bytes) => {
let res = '';
for (let i = 0; i < bytes.length; i += 3) {
// First byte
res += base64urlChar(bytes[i] >>> 2);
// Second byte
res += base64urlChar(((bytes[i] & 0b11) << 4) | (bytes[i + 1] >>> 4));
// Handle cases where fewer than 3 bytes are available
if (i + 1 < bytes.length) {
res += base64urlChar(((bytes[i + 1] & 0b1111) << 2) | (bytes[i + 2] >>> 6));
if (i + 2 < bytes.length) {
res += base64urlChar(bytes[i + 2] & 0b0011_1111);
}
}
}
return res;
};
/**
* The block size used for hashing functions
*/
const BLOCK_SIZE = 512;
/**
* Converts a word array (Uint32Array) into a byte array (Uint8Array)
*
* @param wordArray The Uint32Array
* @returns The Uint8Array
*/
const wordArrayToByteArray = (wordArray) => {
const byteArray = new Uint8Array(wordArray.length * 4);
for (let i = 0; i < wordArray.length; i++) {
byteArray[i * 4] = (wordArray[i] >>> 24) & 0xff;
byteArray[i * 4 + 1] = (wordArray[i] >>> 16) & 0xff;
byteArray[i * 4 + 2] = (wordArray[i] >>> 8) & 0xff;
byteArray[i * 4 + 3] = wordArray[i] & 0xff;
}
return byteArray;
};
/**
* Converts a byte array (Uint8Array) into a word array (Uint32Array)
*
* @param byteArray The Uint8Array
* @returns The Uint32Array
*/
const byteArrayToWordArray = (byteArray) => {
const wordArray = new Uint32Array(byteArray.length / 4);
for (let i = 0; i < wordArray.length; i++) {
wordArray[i] =
(byteArray[i * 4] << 24) |
(byteArray[i * 4 + 1] << 16) |
(byteArray[i * 4 + 2] << 8) |
byteArray[i * 4 + 3];
}
return wordArray;
};
// Constants
const HASH_VALUES = new Uint32Array([
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c,
0x1f83d9ab, 0x5be0cd19,
]);
const ROUND_CONSTANTS = new Uint32Array([
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
]);
/**
* Rotates the word right by the specified number of bits
*
* @param word The word
* @param cnt The number of rotations
* @returns The rotated word
*/
const rotr = (word, cnt) => {
return (word >>> cnt) | (word << (32 - cnt));
};
/**
* Prepares the message for chunking
*
* @param bytes The message bytes
* @returns The padded message (32-bit word array)
*/
const prepareMessage = (bytes) => {
const msgLen = bytes.length * 8;
const totalLen = Math.ceil((msgLen + 65) / BLOCK_SIZE) * (BLOCK_SIZE / 8);
const paddedMessage = new Uint8Array(totalLen);
// Copy original bytes
paddedMessage.set(bytes);
// Append '1' bit
paddedMessage[bytes.length] = 0b1000_0000;
// Append the length of the message in bits (as 64-bit big-endian)
const lenBytes = new DataView(new ArrayBuffer(8));
lenBytes.setUint32(4, msgLen); // Only the lower 32 bits (since we're not hashing files > 4GB)
paddedMessage.set(new Uint8Array(lenBytes.buffer), totalLen - 8);
// Convert the byte array to 32-bit word array
return byteArrayToWordArray(paddedMessage);
};
/**
* Processes a single chunk (512-bit block) of the message
*
* @param chunk The chunk (32-bit word array)
* @param hv The current hash value
* @returns The updated hash value
*/
const processChunk = (chunk, hv) => {
const w = new Uint32Array(64);
w.set(chunk);
// Extend the message schedule array
for (let i = 16; i < 64; i++) {
const s0 = rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ (w[i - 15] >>> 3);
const s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ (w[i - 2] >>> 10);
w[i] = (w[i - 16] + s0 + w[i - 7] + s1) >>> 0;
}
// Initialize working variables
let [a, b, c, d, e, f, g, h] = hv;
// Compression function main loop
for (let i = 0; i < 64; i++) {
const s0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
const s1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
const ch = (e & f) ^ (~e & g);
const maj = (a & b) ^ (a & c) ^ (b & c);
const temp1 = (h + s1 + ch + ROUND_CONSTANTS[i] + w[i]) >>> 0;
const temp2 = (s0 + maj) >>> 0;
h = g;
g = f;
f = e;
e = (d + temp1) >>> 0;
d = c;
c = b;
b = a;
a = (temp1 + temp2) >>> 0;
}
// Add the compressed chunk to the current hash value
hv[0] = (hv[0] + a) >>> 0;
hv[1] = (hv[1] + b) >>> 0;
hv[2] = (hv[2] + c) >>> 0;
hv[3] = (hv[3] + d) >>> 0;
hv[4] = (hv[4] + e) >>> 0;
hv[5] = (hv[5] + f) >>> 0;
hv[6] = (hv[6] + g) >>> 0;
hv[7] = (hv[7] + h) >>> 0;
return hv;
};
/**
* Calculates the SHA256 of the specified byte array
*
* @param bytes The Uint8Array of the byte array
* @returns The SHA256 as a Uint32Array
*/
const sha256 = (bytes) => {
const message = prepareMessage(bytes);
let hash = new Uint32Array(HASH_VALUES); // Copy of initial hash values
// Process each 512-bit chunk
for (let i = 0; i < message.length; i += 16) {
const chunk = message.subarray(i, i + 16);
hash = processChunk(chunk, hash);
}
return hash;
};
/**
* Pads the specified byte-array to the block size.
*
* @param arr The Uint8Array
* @param val The value to pad the array with
* @returns The padded Uint8Array
*/
const blockPad = (arr, val = 0) => {
const paddingSize = BLOCK_SIZE / 8 - arr.length;
const paddedArray = new Uint8Array(arr.length + paddingSize);
paddedArray.set(arr);
if (val !== 0) {
paddedArray.fill(val, arr.length);
}
return paddedArray;
};
/**
* Gets the block-padded key.
* If the key is longer than the block size, it is hashed using SHA-256.
* If the key is shorter, it is padded to the block size.
*
* @param keyBytes The key as a Uint8Array
* @returns The block-padded key
*/
const getBlockKey = (keyBytes) => {
// If key is exactly BLOCK_SIZE (512 bits), return as is.
if (keyBytes.length === BLOCK_SIZE / 8)
return keyBytes;
// If key is longer than BLOCK_SIZE, hash it and use that as the key.
if (keyBytes.length > BLOCK_SIZE / 8)
return blockPad(wordArrayToByteArray(sha256(keyBytes)));
// Otherwise, pad the key to the block size.
return blockPad(keyBytes);
};
/**
* Creates the HMAC of the specified message.
* Combines the key and message using inner and outer padding and returns the final HMAC as a Uint32Array.
*
* @param key The key (Uint8Array)
* @param message The message (Uint8Array)
* @returns The HMAC as a Uint32Array
*/
const hmac = (key, message) => {
const bK = getBlockKey(key); // Get block-padded key
// Initialize inner and outer padding
const oPad = new Uint8Array(bK.length);
const iPad = new Uint8Array(bK.length);
// XOR the key with 0x36 for iPad and 0x5c for oPad
for (let i = 0; i < bK.length; i++) {
oPad[i] = bK[i] ^ 0x5c;
iPad[i] = bK[i] ^ 0x36;
}
// Perform inner hash: H( K ⊕ ipad || message )
const innerHash = sha256(new Uint8Array([...iPad, ...message]));
// Perform outer hash: H( K ⊕ opad || innerHash )
return sha256(new Uint8Array([...oPad, ...wordArrayToByteArray(innerHash)]));
};
/**
* Stringifies the imgproxy modifier for use within the
* imgproxy URL.
*
* @param opCode The operation key
* @param values The values
* @returns The stringified modifier
*/
const stringifyOptions = (opCode, values) => {
return [
opCode,
...values.map((v) => (v == null ? '' : encodeURIComponent(v))),
]
.join(':')
.replace(/:+$/, '');
};
/**
* Encodes the filepath to base64url.
*
* @param filePath The file path
* @returns The base64url encoded file path
*/
const encodeFilePath = (filePath) => {
return base64urlEncode(utf8encode(filePath));
};
/**
* Generates the URL signature for the specified imgproxy param string.
*
* See https://github.com/imgproxy/imgproxy/blob/947d65cf29fe26e5e4d38ca8a17e44c7402e437c/docs/configuration.md#url-signature
*
* @param paramString The param string
* @param key The hex-encoded key
* @param salt The hex-encoded salt
* @param length The number of bytes to use for the signature before encoding to Base64
* @returns The base64url encoded signature
*/
const generateSignature = (paramString, key, salt, length) => {
const path = paramString.startsWith('/') ? paramString : `/${paramString}`;
// Parse key and salt from hex and ensure they're Uint8Array
const keyBytes = parseHexString(key);
const saltBytes = parseHexString(salt);
// Create HMAC using the key and the combination of salt and path
const h = hmac(keyBytes, new Uint8Array([...saltBytes, ...utf8encode(path)]));
// Convert the HMAC output (Uint32Array) into a byte array (Uint8Array)
const truncated = wordArrayToByteArray(h).slice(0, length);
// Return the base64url-encoded signature
return base64urlEncode(truncated);
};
/**
* Defines the brightness, contrast, and saturation.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#adjust-idadjust for the imgproxy documentation
*
* @param options The adjustment options
* @returns The adjustment param string
*/
const adjust = (options) => stringifyOptions('a', [
options.brightness,
options.contrast,
options.saturation,
]);
/**
* Automatically rotates the image based on the EXIF orientation parameter.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#auto-rotate for the imgproxy documentation
*
* @returns The auto-rotate param string
*/
const autoRotate = () => stringifyOptions('ar', [true]);
/**
* Adds alpha channel to background.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#background-alpha-idbackground-alpha for the imgproxy documentation
*
* @param percentage A positive floating point number between 0 and 1
* @returns The background alpha param string
*/
const backgroundAlpha = (percentage) => stringifyOptions('bga', [percentage]);
/**
* Fills the image background with the specified color.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#background for the imgproxy documentation
*
* @param options The background color (hex encoded string or RGB object)
* @returns The background param string
*/
const background = (options) => stringifyOptions('bg', [
...(typeof options === 'string'
? [options]
: [options.r, options.g, options.b]),
]);
/**
* Detects objects of the provided classes and blurs them.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#blur-detections-idblur-detections for the imgproxy documentation
*
* @param options The detection options
* @returns The blur detection param string
*/
const blurDetections = (options) => stringifyOptions('bd', [options.sigma, ...(options.classNames ?? [])]);
/**
* Applies a gaussian blur filter to the image.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#blur for the imgproxy documentation
*
* @param sigma The size of the blur mask
* @returns The blur param string
*/
const blur = (sigma) => stringifyOptions('bl', [sigma]);
/**
* Adjusts the brightness of an image.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#brightness-idbrightness for the imgproxy documentation
*
* @param value An integer number ranging from -255 to 255.
* @returns The brightness param string
*/
const brightness = (value) => stringifyOptions('br', [value]);
/**
* Adds a cache buster to the imgproxy params.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#cache-buster for the imgproxy documentation
*
* @param buster The cache buster
* @returns The cache buster param string
*/
const cacheBuster = (buster) => stringifyOptions('cb', [buster]);
/**
* Adjust contrast of the resulting image.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#contrast-idcontrast for the imgproxy documentation
*
* @param percentage A positive floating point number, where 1
* keeps the contrast unchanged.
* @returns The contrast param string
*/
const contrast = (percentage) => stringifyOptions('co', [percentage]);
/**
* Crops the image.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#crop for the imgproxy documentation
*
* @param options The cropping options
* @returns The cropping param string
*/
const crop = (options) => stringifyOptions('c', [
options?.width ?? 0,
options?.height ?? 0,
options?.gravity?.type,
options?.gravity?.offset?.x,
options?.gravity?.offset?.y,
]);
/**
* Use a single frame of animated images.
*
* See https://github.com/imgproxy/imgproxy/blob/cfa4b596d1f31656f9116cc16f2a4ff7d15c2837/docs/generating_the_url.md#disable-animation-iddisable-animation for the imgproxy documentation
*
* @returns The disable animation param string
*/
const disableAnimation = () => stringifyOptions('da', [true]);
/**
* When set, imgproxy will replace the image's DPI metadata with the provided value.
*
* See https://github.com/imgproxy/imgproxy/blob/8629c5eca1e422908363f471513bfc887d778a85/docs/generating_the_url.md#dpi-iddpi for the imgproxy documentation
*
* @param value The DPI value
* @returns The DPI param string
*/
const dpi = (value) => stringifyOptions('dpi', [value]);
/**
* Multiplies the dimensions according to the specified factor.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#dpr for the imgproxy documentation
*
* @param value The DPR factor
* @returns The DPR param string
*/
const dpr = (value) => stringifyOptions('dpr', [value]);
/**
* Detects objects of the provided classes and draws their
* bounding boxes.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#draw-detections-iddraw-detections for the imgproxy documentation
*
* @param options The detection options
* @returns The draw detection param string
*/
const drawDetections = (options) => stringifyOptions('dd', [true, ...(options.classNames ?? [])]);
/**
* Converts the image to duotone with specified intensity and colors.
*
* See https://github.com/imgproxy/imgproxy-docs/blob/7d15484aea6a1fae5f1dfd1806b5551a4774658d/docs/usage/processing.mdx?plain=1#L429 for the imgproxy documentation
*
* @param options The duotone options
* @returns The duotone param string
*/
const duotone = (options) => stringifyOptions('dt', [options.intensity, options.color1, options.color2]);
/**
* If the source image has an embedded thumbnail, imgproxy will use the
* embedded thumbnail instead of the main image.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#enforce-thumbnail for the imgproxy documentation
*
* @returns The enforce thumbnail param string
*/
const enforceThumbnail = () => stringifyOptions('eth', [true]);
/**
* Enlarges the image if it is smaller than the given size.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#enlarge for the imgproxy documentation
*
* @returns The enlarge param string
*/
const enlarge = () => stringifyOptions('el', [true]);
/**
* Returns a 404 if the expiration date is reached.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#expires for the imgproxy documentation
*
* @param options The unix timestamp
* @returns The expiration param string
*/
const expires = (options) => stringifyOptions('exp', [
typeof options === 'number'
? options
: Math.floor(options.getTime() / 1000),
]);
/**
* Extends the image to the requested aspect ratio.
*
* See https://github.com/imgproxy/imgproxy/blob/1a9768a2c682e88820064aa3d9a05ea234ff3cc4/docs/generating_the_url.md#extend-aspect-ratio for the imgproxy documentation
*
* @returns The extend aspect ratio param string
*/
const extendAspectRatio = (options) => stringifyOptions('exar', [
true,
options?.gravity?.type,
options?.gravity?.offset?.x,
options?.gravity?.offset?.y,
]);
/**
* Extends the image if it is smaller than the given size.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#extend for the imgproxy documentation
*
* @param options The extend options
* @returns The extend param string
*/
const extend = (options) => stringifyOptions('ex', [
true,
options?.gravity.type,
options?.gravity.offset?.x,
options?.gravity.offset?.y,
]);
/**
* Sets a custom fallback image by specifying its URL.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#fallback-image-url-idfallback-image-url for the imgproxy documentation
*
* @param url The fallback image URL
* @returns The fallback image URL param string
*/
const fallbackImageUrl = (url) => stringifyOptions('fiu', [base64urlEncode(utf8encode(url))]);
/**
* Sets the filename for the Content-Disposition header.
*
* See https://github.com/imgproxy/imgproxy/blob/41b9ebe9277ef3e664e0a842fbc0e912b2640969/docs/generating_the_url.md#filename for the imgproxy documentation
*
* @param name The filename
* @param base64Encoded Whether the file name is base64 encoded
* @returns The filename param string
*/
const fileName = (name, base64Encoded = false) => stringifyOptions('fn', [name, base64Encoded ? true : undefined]);
/**
* Sets the desired quality for each format.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#format-quality for the imgproxy documentation
*
* @param options The format quality options
* @returns The format quality param string
*/
const formatQuality = (options) => stringifyOptions('fq', Object.entries(options).flatMap((e) => e));
/**
* Specifies the resulting image format.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#format for the imgproxy documentation
*
* @param imageFormat The target format
* @returns The format param string
*/
const format = (imageFormat) => stringifyOptions('f', [imageFormat]);
// Removed in version 3.0.0
// See: https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/CHANGELOG.md#removed
/**
* Allows redefining GIF saving options.
*
* @deprecated Automatically applied since version 3.0.0
* @param options The gif options
* @returns The gif option param string
*/
const gifOptions = (options) => stringifyOptions('gifo', [
options.optimizeFrames,
options.optimizeTransparency,
]);
var GradientDirection;
(function (GradientDirection) {
GradientDirection["UP"] = "up";
GradientDirection["DOWN"] = "down";
GradientDirection["LEFT"] = "left";
GradientDirection["RIGHT"] = "RIGHT";
})(GradientDirection || (GradientDirection = {}));
var GradientDirection$1 = GradientDirection;
/**
* Places a gradient on the processed image.
*
* See https://github.com/imgproxy/imgproxy/blob/cfa4b596d1f31656f9116cc16f2a4ff7d15c2837/docs/generating_the_url.md#gradient-idgradient for the imgproxy documentation
*
* @param options The gradient options
* @returns The gradient param string
*/
const gradient = (options) => stringifyOptions('gr', [
options.opacity,
options.color ?? '000',
options.direction ?? GradientDirection$1.DOWN,
options.start ?? '0.0',
options.stop ?? '1.0',
]);
/**
* Sets the gravity.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#gravity for the imgproxy documentation
*
* @param options The gravity options
* @returns The gravity param string
*/
const gravity = (options) => stringifyOptions('g', [options.type, options.offset?.x, options.offset?.y]);
/**
* Allows redefining JPEG saving options.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#jpeg-options-idjpeg-options for the imgproxy documentation
*
* @param options The jpeg options
* @returns The jpeg option param string
*/
const jpegOptions = (options) => stringifyOptions('jpgo', [
options.progressive,
options.noSubsample,
options.trellisQuant,
options.overshootDeringing,
options.optimizeScans,
options.quantizationTable,
]);
/**
* Preserve the copyright info while stripping metadata.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#keep-copyright for the imgproxy documentation
*
* @returns The keep copyright param string
*/
const keepCopyright = () => stringifyOptions('kcr', [true]);
/**
* Limits the file size to the specified number of bytes.
*
* Note: only applicable to jpg, webp, heic and tiff.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#max-bytes for the imgproxy documentation
*
* @param maxBytes The number of bytes
* @returns The max bytes param string
*/
const maxBytes = (bytes) => stringifyOptions('mb', [bytes]);
/**
* Defines the minimum height of the resulting image.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#min-height for the imgproxy documentation
*
* @param height The minimum height
* @returns The min height param string
*/
const minHeight = (height) => stringifyOptions('mh', [height]);
/**
* Defines the minimum width of the resulting image.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#min-width for the imgproxy documentation
*
* @param width The minimum width
* @returns The min width param string
*/
const minWidth = (width) => stringifyOptions('mw', [width]);
/**
* Converts the image to monochrome.
*
* See https://github.com/imgproxy/imgproxy-docs/blob/7d15484aea6a1fae5f1dfd1806b5551a4774658d/docs/usage/processing.mdx?plain=1#L415 for the imgproxy documentation
*
* @param options The monochrome options
* @returns The monochrome param string
*/
const monochrome = (options) => stringifyOptions('mc', [
options.intensity,
...(options.color ? [options.color] : []),
]);
/**
* Applies the specified padding to the image.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#padding for the imgproxy documentation
*
* @param options The padding options
* @returns The padding param string
*/
const pad = (options) => stringifyOptions('pd', [
options.top,
options.right,
options.bottom,
options.left,
]);
/**
* When source image supports pagination (PDF, TIFF) or animation (GIF, WebP), this option allows
* specifying the page to use.
*
* Pages numeration starts from zero.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#page-idpage for the imgproxy documentation
*
* @param pg The page to use
* @returns The page param string
*/
const page = (pg) => stringifyOptions('pg', [pg]);
/**
* Apply the pixelate filter to the resulting image.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#pixelate for the imgproxy documentation
*
* @param pixelSize The size of a pixel
* @returns The pixelate param string
*/
const pixelate = (pixelSize) => stringifyOptions('pix', [pixelSize]);
/**
* Allows redefining PNG saving options.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#png-options-idpng-options for the imgproxy documentation
*
* @param options The png options
* @returns The png option param string
*/
const pngOptions = (options) => stringifyOptions('pngo', [
options.interlaced,
options.quantize,
options.quantization_colors,
]);
/**
* Sets one or many presets to be used by the imgproxy.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#preset for the imgproxy documentation
*
* @param presets The preset(s)
* @returns The preset param string
*/
const preset = (presets) => stringifyOptions('pr', Array.isArray(presets) ? presets : [presets]);
/**
* Redefines the quality of the resulting image.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#quality for the imgproxy documentation
*
* @param percentage The percentage as floating point number from 0 to 1
* @returns The quality param string
*/
const quality = (percentage) => stringifyOptions('q', [percentage]);
/**
* Returns a raw unprocessed and unchecked source image
*
* See https://github.com/imgproxy/imgproxy/blob/f95f57bb4df35c69ae2257958006ef54b1c1d8c7/docs/generating_the_url.md#raw for the imgproxy documentation
*
* @returns The raw param string
*/
const raw = () => stringifyOptions('raw', [true]);
/**
* Resizes the image.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#resize for the imgproxy documentation
*
* @param options The resizing options
* @returns The resizing param string
*/
const resize = (options) => stringifyOptions('rs', [options.type, options.width, options.height]);
/**
* Defines the algorithm that imgproxy will use for resizing.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#resizing-algorithm-idresizing-algorithm for the imgproxy documentation
*
* @param algorithm The resizing algorithm
* @returns The resizing algorithm param string
*/
const resizingAlgorithm = (algorithm) => stringifyOptions('ra', [algorithm]);
/**
* Returns attachment in the Content-Disposition header.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#return-attachment for the imgproxy documentation
*
* @returns The attachment param string
*/
const returnAttachment = () => stringifyOptions('att', [true]);
/**
* Rotates the image by the specified angle.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#rotate for the imgproxy documentation
*
* @param angle The angle
* @returns The rotate param string
*/
const rotate = (angle) => stringifyOptions('rot', [angle]);
/**
* Adjust saturation of the resulting image.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#saturation-idsaturation for the imgproxy documentation
*
* @param percentage A positive floating point number, where 1
* keeps the saturation unchanged
* @returns The saturation param string
*/
const saturation = (percentage) => stringifyOptions('sa', [percentage]);
/**
* Applies a sharpen filter to the image.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#sharpen for the imgproxy documentation
*
* @param sigma The size of the sharpen mask
* @returns The sharpen param string
*/
const sharpen = (sigma) => stringifyOptions('sh', [sigma]);
/**
* Skip the processing of the listed formats.
*
* Note: Processing can only be skipped when the requested format is
* the same as the source format.
*
* Note: Video thumbnail processing can't be skipped
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#skip-processing for the imgproxy documentation
*
* @param extensions The list of file extensions
* @returns The skip processing param string
*/
const skipProcessing = (extensions) => stringifyOptions('skp', extensions);
/**
* Strips the color profile from the image.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#strip-color-profile for the imgproxy documentation
*
* @returns The strip color profile param string
*/
const stripColorProfile = () => stringifyOptions('scp', [true]);
/**
* Strips the metadata from the image.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#strip-metadata for the imgproxy documentation
*
* @returns The strip metadata param string
*/
const stripMetadata = () => stringifyOptions('sm', [true]);
/**
* Prepend a `<style>` node with the provided CSS styles to the
* `<svg>` node of a source SVG image.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#style-idstyle for the imgproxy documentation
*
* @param styles The styles to apply
* @returns The style param string
*/
const style = (styles) => {
const styleString = typeof styles === 'string'
? styles
: Object.entries(styles)
.flatMap((v) => `${v[0]}:${v[1]}`)
.join(';');
return stringifyOptions('st', [base64urlEncode(utf8encode(styleString))]);
};
/**
* Trims the image background.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#trim for the imgproxy documentation
*
* @param options The trimming options
* @returns The trim param string
*/
const trim = (options) => stringifyOptions('t', [
options.threshold,
options.color,
options.equal?.horizontal,
options.equal?.vertical,
]);
/**
* Allows redefining unsharpening options.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#unsharpening-idunsharpening for the imgproxy documentation
*
* @param options The unsharpening options
* @returns The unsharpening param string
*/
const unsharpen = (options) => stringifyOptions('ush', [options.mode, options.weight, options.dividor]);
/**
* Redefines the second used for the thumbnail.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#video-thumbnail-second-idvideo-thumbnail-second for the imgproxy documentation
*
* @param second The timestamp of the frame in seconds
* that will be used for a thumbnail.
* @returns The video thumbnail second param string
*/
const videoThumbnailSecond = (second) => stringifyOptions('vts', [second]);
/**
* Generates a tiled sprite using the source video frames
*
* See https://github.com/imgproxy/imgproxy-docs/blob/676c6d4b1f5d9fee79abfecf130fc7dda3f9124e/versioned_docs/version-3.24.x/usage/processing.mdx#video-thumbnail-tile-pro-video-thumbnail-tile for the imgproxy documentation
*
* @param second The video thumbnail tile options
* @returns The video thumbnail tile param string
*/
const videoThumbnailTile = (options) => stringifyOptions('vtt', [
options.step,
options.columns,
options.rows,
options.tileWidth,
options.tileHeight,
options.extendTile ?? false,
options.trim ?? false,
options.fill ?? false,
options.focusX ?? '0.5',
options.focusY ?? '0.5',
]);
/**
* Adds a shadow to the watermark.
*
* See https://github.com/imgproxy/imgproxy/blob/f95f57bb4df35c69ae2257958006ef54b1c1d8c7/docs/generating_the_url.md#watermark-shadow-idwatermark-shadow for the imgproxy documentation
*
* @param sigma The size of the shadow mask
* @returns The watermark shadow param string
*/
const watermarkShadow = (sigma) => stringifyOptions('wmsh', [sigma]);
/**
* Defines the desired width and height of the watermark. imgproxy always
* uses `fit` resizing type when resizing watermarks and enlarges them
* when needed.
*
* Note: this processing option only takes effect when the `scale` argument
* of the `watermark` option is set to zero.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#watermark-size-idwatermark-size for the imgproxy documentation
*
* @param options The size of the blur mask
* @returns The blur param string
*/
const watermarkSize = (options) => stringifyOptions('wms', [options.width ?? 0, options.height ?? 0]);
/**
* Generate an image from the provided text and use it as a watermark.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#watermark-text-idwatermark-text for the imgproxy documentation
*
* @param text The watermark text
* @returns The watermark text param string
*/
const watermarkText = (text) => stringifyOptions('wmt', [base64urlEncode(utf8encode(text))]);
/**
* Use the image from the specified URL as a watermark.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#watermark-url-idwatermark-url for the imgproxy documentation
*
* @param url The watermark URL
* @returns The watermark URL param string
*/
const watermarkUrl = (url) => stringifyOptions('wmu', [base64urlEncode(utf8encode(url))]);
/**
* Places a watermark on the processed image.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#watermark for the imgproxy documentation
*
* @param options The watermark options
* @returns The watermark param string
*/
const watermark = (options) => stringifyOptions('wm', [
options.opacity,
options.position,
options.offset?.x,
options.offset?.y,
options.scale,
]);
/**
* Multiply the image dimensions according to the specified factors.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#zoom for the imgproxy documentation
*
* The values must be greater than 0.
*
* @param options The zoom options
* @returns The zoom param string
*/
const zoom = (options) => stringifyOptions('z', [
typeof options === 'number' ? options : options.join(' '),
]);
var HashsumType;
(function (HashsumType) {
HashsumType["NONE"] = "none";
HashsumType["MD5"] = "md5";
HashsumType["SHA1"] = "sha1";
HashsumType["SHA256"] = "sha256";
HashsumType["SHA512"] = "sha512";
})(HashsumType || (HashsumType = {}));
var HashsumType$1 = HashsumType;
/**
* When `hashsum_type` is not `none`, imgproxy will calculate the hashsum of the source image
* and compare it with the provided hashsum.
*
* If they don't match, imgproxy will respond with 422.
*
* See https://github.com/imgproxy/imgproxy-docs/blob/f9d7908d253ec2b31425b988a48f8c28cb271c58/docs/usage/processing.mdx#L916 for the imgproxy documentation
*
* @returns The auto-rotate param string
*/
const hashsum = (options) => stringifyOptions('hashsum', [
options.type ?? HashsumType$1.NONE,
options.hashsum,
]);
/**
* Specifies whether the latest keyframe before the video thumbnail second
* should be used for thumbnail generation
*
* See https://github.com/imgproxy/imgproxy-docs/blob/676c6d4b1f5d9fee79abfecf130fc7dda3f9124e/versioned_docs/version-3.24.x/usage/processing.mdx#video-thumbnail-keyframes-pro-video-thumbnail-keyframes for the imgproxy documentation
*
* @param keyframes The keyframes to be used.
* @returns The video thumbnail keyframes param string
*/
const videoThumbnailKeyframes = (keyframes) => stringifyOptions('vtk', [keyframes]);
class ParamBuilder {
/**
* The currently applied imgproxy modifiers
*/
modifiers;
constructor(initialModifiers = new Map()) {
this.modifiers = initialModifiers;
}
/**
* Creates a new param builder instance with a copy of the
* current modifiers
*
* @returns A copy of this param builder
*/
clone() {
return new ParamBuilder(new Map(this.modifiers));
}
/**
* Removes the specified modifier from the currently applied
* modifiers
*
* @param modifier The modifier
*/
unset(modifier) {
this.modifiers.delete(modifier);
return this;
}
/**
* Builds the imgproxy URL
*
* If a path is supplied, the full URL path will be returned,
* else only the stringified modifiers will be returned.
*
* If a base URL is supplied, the full imgproxy URL will be returned.
*
* @param options The build options
* @returns The imgproxy URL
*/
build(options) {
const { baseUrl, path, plain, signature } = options ?? {};
const mods = Array.from(this.modifiers.values());
if (!path)
return mods.join('/');
if (path && plain)
mods.push('plain', path);
else
mods.push(encodeFilePath(path));
// Get the file extension if requested
const extension = options?.addExtension
? this.getExtension(plain ? '@' : '.')
: '';
const res = mods.join('/') + extension;
// If no signature is calculated add a - as placeholder
// See https://github.com/imgproxy/imgproxy/blob/b243a08254b9ca7da2c628429cd870c111ece5c9/docs/signing_the_url.md
const finalPath = signature
? `${generateSignature(res, signature.key, signature.salt, signature.size ?? 32)}/${res}`
: `-/${res}`;
return baseUrl ? `${baseUrl}/${finalPath}` : `/${finalPath}`;
}
/**
* Get the extension for the file. Checks the specified format and the target image.
*
* See https://github.com/imgproxy/imgproxy/blob/5ac79477dfa76ed3c014a1472e31e26a2d2257a0/docs/generating_the_url.md#source-url for the imgproxy documentation
*
* @param path The path to the target image, e.g. `https://example.com/foo.png`
* @returns An extension if found, or an empty string
*/
getExtension(separator) {
const formatString = this.modifiers.get('format');
if (!formatString)
return '';
return `${separator}${formatString.split(':').pop()}`;
}
/**
* Defines the brightness, contrast, and saturation.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#adjust-idadjust for the imgproxy documentation
*
* @example
* ```typescript
* pb().adjust({
* brightness: 100, // optional
* contrast: 0.8, // optional
* saturation: 0.9 // optional
* });
* ```
*/
adjust(...options) {
this.modifiers.set('adjust', adjust(...options));
return this;
}
/**
* Automatically rotates the image based on the EXIF orientation parameter.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#auto-rotate for the imgproxy documentation
*
* @example
* ```typescript
* pb().autoRotate();
* ```
*/
autoRotate() {
this.modifiers.set('autoRotate', autoRotate());
return this;
}
/**
* Fills the image background with the specified color.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#background for the imgproxy documentation
*
* @example
* ```typescript
* pb().background('ff0000');
*
* pb().background({
* r: 255,
* g: 0,
* b: 0
* });
* ```
*/
background(...options) {
this.modifiers.set('background', background(...options));
return this;
}
/**
* Adds alpha channel to background.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#background-alpha-idbackground-alpha for the imgproxy documentation
*
* @example
* ```typescript
* pb().backgroundAlpha(0.4);
* ```
*/
backgroundAlpha(...options) {
this.modifiers.set('backgroundAlpha', backgroundAlpha(...options));
return this;
}
/**
* Applies a gaussian blur filter to the image.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#blur for the imgproxy documentation
*
* @example
* ```typescript
* pb().blur(10);
* ```
*/
blur(...options) {
this.modifiers.set('blur', blur(...options));
return this;
}
/**
* Detects objects of the provided classes and blurs them.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#blur-detections-idblur-detections for the imgproxy documentation
*
* @example
* ```typescript
* pb().blurDetections({
* sigma: 10,
* classNames: ['face']
* });
* ```
*/
blurDetections(...options) {
this.modifiers.set('blurDetections', blurDetections(...options));
return this;
}
/**
* Adjusts the brightness of an image.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#brightness-idbrightness for the imgproxy documentation
*
* @example
* ```typescript
* pb().brightness(-100);
* ```
*/
brightness(...options) {
this.modifiers.set('brightness', brightness(...options));
return this;
}
/**
* Adds a cache buster to the imgproxy params.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#cache-buster for the imgproxy documentation
*
* @example
* ```typescript
* pb().cacheBuster("abcdef123");
* ```
*/
cacheBuster(...options) {
this.modifiers.set('cacheBuster', cacheBuster(...options));
return this;
}
/**
* Adjust contrast of the resulting image.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#contrast-idcontrast for the imgproxy documentation
*
* @example
* ```typescript
* pb().contrast(0.3);
* ```
*/
contrast(...options) {
this.modifiers.set('contrast', contrast(...options));
return this;
}
/**
* Crops the image.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#crop for the imgproxy documentation
*
* @example
* ```typescript
* pb().crop({
* width: 100, // optional
* height: 50, // optional
* gravity: { // optional
* type: GravityType.CENTER, // required
* offset: { // optional
* x: 20, // required
* y: 20 // required
* }
* }
* })
* ```
*/
crop(...options) {
this.modifiers.set('crop', crop(...options));
return this;
}
/**
* Use a single frame of animated images.
*
* See https://github.com/imgproxy/imgproxy/blob/cfa4b596d1f31656f9116cc16f2a4ff7d15c2837/docs/generating_the_url.md#disable-animation-iddisable-animation for the imgproxy documentation
*
* @example
* ```typescript
* pb().disableAnimation();
* ```
*/
disableAnimation(...options) {
this.modifiers.set('disableAnimation', disableAnimation(...options));
return this;
}
/**
* When set, imgproxy will replace the image's DPI metadata with the provided value.
*
* See https://github.com/imgproxy/imgproxy/blob/8629c5eca1e422908363f471513bfc887d778a85/docs/generating_the_url.md#dpi-iddpi for the imgproxy documentation
*
* @example
* ```typescript
* pb().dpi(300);
* ```
*/
dpi(...options) {
this.modifiers.set('dpi', dpi(...options));
return this;
}
/**
* Multiplies the dimensions according to the specified factor.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#dpr for the imgproxy documentation
*
* @example
* ```typescript
* pb().dpr(18);
* ```
*/
dpr(...options) {
this.modifiers.set('dpr', dpr(...options));
return this;
}
/**
* Detects objects of the provided classes and draws their
* bounding boxes.
*
* See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#draw-detections-iddraw-detections for the imgproxy documentation
*
* @example
* ```typescript
* pb().drawDetections({
* classNames: ["face"]
* });
* ```
*/
drawDetections(...options) {
this.modifiers.set('drawDetections', drawDetections(...options));
return this;
}
/**
* Converts the image to duotone with specified intensity and colors.
*
* See https://github.com/imgproxy/imgproxy-docs/blob/7d15484aea6a1fae5f1dfd1806b5551a4774658d/docs/usage/processing.mdx?plain=1#L429 for the imgproxy documentation
*
* @example
* ```typescript
* pb().duotone({
* intensity: 1.0, // required
* color1: 'ff0000', // required
* color2: '00ff00' // required
* });
* ```
*/
duotone(...options) {
this.modifiers.set('duotone', duotone(...options));
return this;
}
/**
* If the source image has an embedded thumb