Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/progressive-blur.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@imgproxy/imgproxy-js-core": minor
---

Add support for [progressive_blur](https://docs.imgproxy.net/usage/processing#progressive-blur) option (imgproxy Pro). When `sigma` is greater than `0`, imgproxy applies a progressive Gaussian blur filter that transitions from no blur at the `start` position to the specified `sigma` at the `stop` position. The option accepts `sigma`, an optional `direction` (`down`, `up`, `right`, `left`, or an angle in degrees), and optional `start` and `stop` floats between `0` and `1`. The short form `pbl` is also supported.
1 change: 1 addition & 0 deletions src/options/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export * as pages from "./pages";
export * as pixelate from "./pixelate";
export * as pngOptions from "./pngOptions";
export * as preset from "../optionsShared/preset";
export * as progressiveBlur from "./progressiveBlur";
export * as quality from "./quality";
export * as raw from "./raw";
export * as resize from "./resize";
Expand Down
65 changes: 65 additions & 0 deletions src/options/progressiveBlur.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import type {
ProgressiveBlur,
ProgressiveBlurOptionsPartial,
} from "../types/progressiveBlur";
import {
guardIsUndef,
guardIsNotNum,
guardIsNotStr,
guardIsValidVal,
} from "../utils";

const correctDirection = {
down: true,
up: true,
right: true,
left: true,
};

const getOpt = (
options: ProgressiveBlurOptionsPartial
): ProgressiveBlur | undefined => options.progressive_blur || options.pbl;

const test = (options: ProgressiveBlurOptionsPartial): boolean =>
Boolean(getOpt(options));

const build = (options: ProgressiveBlurOptionsPartial): string => {
const progressiveBlurOpts = getOpt(options);

guardIsUndef(progressiveBlurOpts, "progressive_blur");
const { sigma, direction, start, stop } = progressiveBlurOpts;

guardIsUndef(sigma, "progressive_blur.sigma");
guardIsNotNum(sigma, "progressive_blur.sigma", {
addParam: { min: 0 },
});

if (direction !== undefined) {
if (typeof direction === "number") {
guardIsNotNum(direction, "progressive_blur.direction");
} else {
guardIsNotStr(direction, "progressive_blur.direction");
guardIsValidVal(
correctDirection,
direction,
"progressive_blur.direction"
);
}
}
if (start !== undefined)
guardIsNotNum(start, "progressive_blur.start", {
addParam: { min: 0, max: 1 },
});
if (stop !== undefined)
guardIsNotNum(stop, "progressive_blur.stop", {
addParam: { min: 0, max: 1 },
});

const dir = direction ?? "";
const from = start ?? "";
const to = stop ?? "";

return `pbl:${sigma}:${dir}:${from}:${to}`.replace(/:+$/, "");
};

export { test, build };
2 changes: 2 additions & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import type { PagesOptionsPartial } from "./pages";
import type { PixelateOptionsPartial } from "./pixelate";
import type { PNGOptionsPartial } from "./pngOptions";
import type { PresetOptionsPartial } from "../typesShared/preset";
import type { ProgressiveBlurOptionsPartial } from "./progressiveBlur";
import type { QualityOptionsPartial } from "./quality";
import type { RawOptionsPartial } from "./raw";
import type { ResizeOptionsPartial } from "./resize";
Expand Down Expand Up @@ -132,6 +133,7 @@ export type Options = AdjustOptionsPartial &
PixelateOptionsPartial &
PNGOptionsPartial &
PresetOptionsPartial &
ProgressiveBlurOptionsPartial &
RawOptionsPartial &
QualityOptionsPartial &
ResizeOptionsPartial &
Expand Down
51 changes: 51 additions & 0 deletions src/types/progressiveBlur.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* *Progressive blur option*. **PRO feature**
*
* When `sigma` is greater than `0`, imgproxy will apply a progressive Gaussian blur filter
* to the resulting image. The placed progressive blur transitions from no blur at the `start`
* position to the specified `sigma` at the `stop` position.
*
* @param {number} sigma - defines the size of the mask imgproxy will use for the maximum blur. When set to `0`, the progressive blur is not applied.
* @param {"down" | "up" | "right" | "left" | number} [direction="down"] - (optional) specifies the direction of the progressive blur. Default `"down"`.
*
* Available values:
* - `down` - the top side of the progressive blur is not blurred, the bottom side is blurred
* - `up` - the bottom side of the progressive blur is not blurred, the top side is blurred
* - `right` - the left side of the progressive blur is not blurred, the right side is blurred
* - `left` - the right side of the progressive blur is not blurred, the left side is blurred
* - `number` - angle in degrees (clockwise). `0` creates a progressive blur from top to bottom; `90` creates a progressive blur from right to left.
* @param {number} [start=0.0] - (optional) specifies the relative position where the progressive blur starts. Value range: `0.0` - `1.0`. Default `0.0`.
* @param {number} [stop=1.0] - (optional) specifies the relative position where the progressive blur ends. Value range: `0.0` - `1.0`. Default `1.0`.
*
* @example
* // Apply a progressive blur from the top to the bottom of the image
* {progressive_blur: {sigma: 10}}
*
* // Apply a progressive blur from the left to the right of the image
* {progressive_blur: {sigma: 10, direction: "right"}}
*
* // Apply a progressive blur at a 45 degree angle with the start point at 0.2 and the end point at 0.8
* {progressive_blur: {sigma: 10, direction: 45, start: 0.2, stop: 0.8}}
*
* @see {@link https://docs.imgproxy.net/usage/processing#progressive-blur | progressive blur option imgproxy docs}
*/
interface ProgressiveBlur {
sigma: number;
direction?: "down" | "up" | "right" | "left" | number;
start?: number;
stop?: number;
}

/**
* *Progressive blur option*. **PRO feature**
*
* To describe the Progressive Blur option, you can use the keyword `progressive_blur` or `pbl`.
*
* @see https://docs.imgproxy.net/usage/processing#progressive-blur
*/
interface ProgressiveBlurOptionsPartial {
progressive_blur?: ProgressiveBlur;
pbl?: ProgressiveBlur;
}

export { ProgressiveBlur, ProgressiveBlurOptionsPartial };
143 changes: 143 additions & 0 deletions tests/optionsBasic/progressiveBlur.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { describe, expect, it } from "vitest";
import { test, build } from "../../src/options/progressiveBlur";

describe("progressiveBlur", () => {
describe("test", () => {
it("should return true if progressive_blur option is defined", () => {
expect(test({ progressive_blur: { sigma: 10 } })).toEqual(true);
});

it("should return false if progressive_blur option is undefined", () => {
expect(test({})).toEqual(false);
});

it("should return true if pbl option is defined", () => {
expect(test({ pbl: { sigma: 5 } })).toEqual(true);
});
});

describe("build", () => {
it("should throw an error if progressive_blur option is undefined", () => {
expect(() => build({})).toThrow("progressive_blur option is undefined");
});

it("should throw an error if sigma is undefined", () => {
// @ts-expect-error: Let's ignore an error (check for users with vanilla js).
expect(() => build({ progressive_blur: { direction: "up" } })).toThrow(
"progressive_blur.sigma is undefined"
);
});

it("should throw an error if sigma is not a number", () => {
// @ts-expect-error: Let's ignore an error (check for users with vanilla js).
expect(() => build({ progressive_blur: { sigma: "10" } })).toThrow(
"progressive_blur.sigma is not a number"
);
});

it("should throw an error if sigma is less than 0", () => {
expect(() => build({ progressive_blur: { sigma: -1 } })).toThrow(
"progressive_blur.sigma value can't be less than 0"
);
});

it("should throw an error if direction is not a string or number", () => {
expect(() =>
// @ts-expect-error: Let's ignore an error (check for users with vanilla js).
build({ progressive_blur: { sigma: 10, direction: true } })
).toThrow("progressive_blur.direction is not a string");
});

it("should throw an error if direction is a string not among: down, up, right, left", () => {
expect(() =>
// @ts-expect-error: Let's ignore an error (check for users with vanilla js).
build({ progressive_blur: { sigma: 10, direction: "top" } })
).toThrow(
"progressive_blur.direction is invalid. Valid values are: down, up, right, left"
);
});

it("should accept numeric angle for direction", () => {
expect(build({ progressive_blur: { sigma: 10, direction: 45 } })).toEqual(
"pbl:10:45"
);
});

it("should throw an error if start is not a number", () => {
expect(() =>
// @ts-expect-error: Let's ignore an error (check for users with vanilla js).
build({ progressive_blur: { sigma: 10, start: "0.5" } })
).toThrow("progressive_blur.start is not a number");
});

it("should throw an error if start is less than 0", () => {
expect(() =>
build({ progressive_blur: { sigma: 10, start: -0.5 } })
).toThrow("progressive_blur.start value can't be less than 0");
});

it("should throw an error if start is bigger than 1", () => {
expect(() =>
build({ progressive_blur: { sigma: 10, start: 1.5 } })
).toThrow("progressive_blur.start value can't be more than 1");
});

it("should throw an error if stop is not a number", () => {
expect(() =>
// @ts-expect-error: Let's ignore an error (check for users with vanilla js).
build({ progressive_blur: { sigma: 10, stop: "0.5" } })
).toThrow("progressive_blur.stop is not a number");
});

it("should throw an error if stop is less than 0", () => {
expect(() =>
build({ progressive_blur: { sigma: 10, stop: -0.5 } })
).toThrow("progressive_blur.stop value can't be less than 0");
});

it("should throw an error if stop is bigger than 1", () => {
expect(() =>
build({ progressive_blur: { sigma: 10, stop: 1.5 } })
).toThrow("progressive_blur.stop value can't be more than 1");
});

it("should return pbl:10:down:0.2:0.8 if all progressive_blur parametres is defined", () => {
expect(
build({
progressive_blur: {
sigma: 10,
direction: "down",
start: 0.2,
stop: 0.8,
},
})
).toEqual("pbl:10:down:0.2:0.8");
});

it("should return pbl:10 if only sigma is defined", () => {
expect(build({ progressive_blur: { sigma: 10 } })).toEqual("pbl:10");
});

it("should return pbl:10:right if only sigma and direction is defined", () => {
expect(build({ pbl: { sigma: 10, direction: "right" } })).toEqual(
"pbl:10:right"
);
});

it("should return pbl:10:::0.85 if only sigma and stop is defined", () => {
expect(build({ progressive_blur: { sigma: 10, stop: 0.85 } })).toEqual(
"pbl:10:::0.85"
);
});

it("should correctly handle 0 start", () => {
expect(
build({ progressive_blur: { sigma: 10, start: 0, stop: 0.85 } })
).toEqual("pbl:10::0:0.85");
});

it("should correctly handle 0 sigma", () => {
expect(build({ progressive_blur: { sigma: 0 } })).toEqual("pbl:0");
});
});
});
Loading