Skip to content

Commit b7ffedd

Browse files
feat(imago): add blurhash output option, update deps
1 parent 0e927ad commit b7ffedd

File tree

4 files changed

+82
-3
lines changed

4 files changed

+82
-3
lines changed

packages/imago/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"dependencies": {
3838
"@thi.ng/api": "^8.9.26",
3939
"@thi.ng/associative": "^6.3.43",
40+
"@thi.ng/blurhash": "^0.1.10",
4041
"@thi.ng/checks": "^3.5.0",
4142
"@thi.ng/date": "^2.7.1",
4243
"@thi.ng/defmulti": "^3.0.26",

packages/imago/src/api.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,14 +189,54 @@ export interface OutputSpec extends ProcSpec {
189189
id: string;
190190
/**
191191
* Possibly templated output path. See {@link formatPath} for details.
192+
* Ignored if {@link OutputSpec.blurhash} is being used.
192193
*/
193194
path: string;
195+
/**
196+
* AVIF output options. See [Sharp docs](https://sharp.pixelplumbing.com/api-output#avif)
197+
*/
194198
avif?: AvifOptions;
199+
/**
200+
* If given, ONLY the blurhash of the image will be computed and stored in
201+
* the `outputs` object returned by {@link processImage}. The
202+
* {@link OutputSpec.path} will be ignored and no file will be written.
203+
*
204+
* @remarks
205+
* Important: Ensure the image has already been downsized to ~50-500 pixels.
206+
* Larger images are causing unnecessary & long processing...
207+
*/
208+
blurhash?: {
209+
/**
210+
* Blurhash detail setting in 1-9 range, possibly given separately for
211+
* X/Y axis.
212+
*
213+
* @defaultValue 4
214+
*/
215+
detail?: number | [number, number];
216+
};
217+
/**
218+
* GIF output options. See [Sharp docs](https://sharp.pixelplumbing.com/api-output#gif)
219+
*/
195220
gif?: GifOptions;
221+
/**
222+
* JPEG 2000 output options. See [Sharp docs](https://sharp.pixelplumbing.com/api-output#jp2)
223+
*/
196224
jp2?: Jp2Options;
225+
/**
226+
* JPEG output options. See [Sharp docs](https://sharp.pixelplumbing.com/api-output#jpeg)
227+
*/
197228
jpeg?: JpegOptions;
229+
/**
230+
* JPEG XL output options. See [Sharp docs](https://sharp.pixelplumbing.com/api-output#jxl)
231+
*/
198232
jxl?: JxlOptions;
233+
/**
234+
* PNG output options. See [Sharp docs](https://sharp.pixelplumbing.com/api-output#avif)
235+
*/
199236
png?: PngOptions;
237+
/**
238+
* Raw binary output options.
239+
*/
200240
raw?:
201241
| boolean
202242
| {
@@ -210,8 +250,17 @@ export interface OutputSpec extends ProcSpec {
210250
*/
211251
meta?: boolean;
212252
};
253+
/**
254+
* Tiled format output options. See [Sharp docs](https://sharp.pixelplumbing.com/api-output#tile)
255+
*/
213256
tile?: TileOptions;
257+
/**
258+
* TIFF output options. See [Sharp docs](https://sharp.pixelplumbing.com/api-output#tiff)
259+
*/
214260
tiff?: TiffOptions;
261+
/**
262+
* WebP output options. See [Sharp docs](https://sharp.pixelplumbing.com/api-output#webp)
263+
*/
215264
webp?: WebpOptions;
216265
}
217266

packages/imago/src/ops/output.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
// thing:no-export
2+
import { encode } from "@thi.ng/blurhash";
3+
import { isNumber, isPlainObject } from "@thi.ng/checks";
24
import { writeFile, writeJSON } from "@thi.ng/file-io";
35
import { join, resolve } from "node:path";
6+
import type { Sharp } from "sharp";
47
import type { ImgProcCtx, OutputSpec, Processor } from "../api.js";
58
import { formatPath } from "../path.js";
6-
import type { Sharp } from "sharp";
7-
import { isPlainObject } from "@thi.ng/checks";
89

910
export const outputProc: Processor = async (spec, input, ctx) => {
1011
const opts = <OutputSpec>spec;
1112
const outDir = resolve(ctx.opts.outDir || ".");
1213
let output = input.clone();
14+
if (opts.blurhash) {
15+
await outputBlurHash(opts, output, ctx);
16+
return [input, false];
17+
}
1318
if (opts.raw) {
1419
await outputRaw(opts, output, ctx, outDir);
1520
return [input, false];
@@ -78,6 +83,7 @@ const outputRaw = async (
7883
: {};
7984
if (alpha) output = output.ensureAlpha();
8085
const { data, info } = await output
86+
.ensureAlpha()
8187
.raw()
8288
.toBuffer({ resolveWithObject: true });
8389
const path = join(outDir, formatPath(opts.path, ctx, opts, data));
@@ -93,3 +99,25 @@ const outputRaw = async (
9399
);
94100
}
95101
};
102+
103+
const outputBlurHash = async (
104+
opts: OutputSpec,
105+
output: Sharp,
106+
ctx: ImgProcCtx
107+
) => {
108+
const { data, info } = await output
109+
.ensureAlpha()
110+
.raw()
111+
.toBuffer({ resolveWithObject: true });
112+
const detail = opts.blurhash!.detail || 4;
113+
const [dx, dy] = isNumber(detail) ? [detail, detail] : detail;
114+
const hash = encode(
115+
new Uint32Array(data.buffer),
116+
info.width,
117+
info.height,
118+
dx,
119+
dy
120+
);
121+
ctx.logger.debug("computed blurhash:", hash);
122+
ctx.outputs[opts.id] = hash;
123+
};

yarn.lock

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3689,7 +3689,7 @@ __metadata:
36893689
languageName: unknown
36903690
linkType: soft
36913691

3692-
"@thi.ng/blurhash@workspace:^, @thi.ng/blurhash@workspace:packages/blurhash":
3692+
"@thi.ng/blurhash@npm:^0.1.10, @thi.ng/blurhash@workspace:^, @thi.ng/blurhash@workspace:packages/blurhash":
36933693
version: 0.0.0-use.local
36943694
resolution: "@thi.ng/blurhash@workspace:packages/blurhash"
36953695
dependencies:
@@ -5068,6 +5068,7 @@ __metadata:
50685068
"@microsoft/api-extractor": "npm:^7.40.1"
50695069
"@thi.ng/api": "npm:^8.9.26"
50705070
"@thi.ng/associative": "npm:^6.3.43"
5071+
"@thi.ng/blurhash": "npm:^0.1.10"
50715072
"@thi.ng/checks": "npm:^3.5.0"
50725073
"@thi.ng/date": "npm:^2.7.1"
50735074
"@thi.ng/defmulti": "npm:^3.0.26"

0 commit comments

Comments
 (0)