@thi.ng/imago 0.3.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2024-02-25T14:07:53Z
3
+ - **Last updated**: 2024-02-28T14:23:30Z
4
4
  - **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
5
5
 
6
6
  All notable changes to this project will be documented in this file.
@@ -9,6 +9,18 @@ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelin
9
9
  **Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
10
10
  and/or version bumps of transitive dependencies.
11
11
 
12
+ ### [0.4.1](https://github.com/thi-ng/umbrella/tree/@thi.ng/imago@0.4.1) (2024-02-28)
13
+
14
+ #### 🩹 Bug fixes
15
+
16
+ - fix typedarray input handling in processImage() ([075ecaa](https://github.com/thi-ng/umbrella/commit/075ecaa))
17
+
18
+ ## [0.4.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/imago@0.4.0) (2024-02-27)
19
+
20
+ #### 🚀 Features
21
+
22
+ - add blurhash output option, update deps ([b7ffedd](https://github.com/thi-ng/umbrella/commit/b7ffedd))
23
+
12
24
  ## [0.3.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/imago@0.3.0) (2024-02-23)
13
25
 
14
26
  #### 🚀 Features
package/README.md CHANGED
@@ -76,9 +76,10 @@ The following pipeline performs these steps (in sequence):
76
76
  - proportionally resize image to 1920px (longest side by default)
77
77
  - overlay bitmap logo layer, positioned at 45% left / 5% bottom
78
78
  - add custom EXIF metadata
79
- - output this current stage as high quality AVIF (using templated output path)
79
+ - output this current stage as high quality AVIF (and record expanded output path)
80
80
  - crop center square region
81
- - output as JPEG thumbnail
81
+ - output as JPEG thumbnail (and record in outputs)
82
+ - compute [blurhash](https://github.com/thi-ng/umbrella/blob/develop/packages/blurhash) (and record in outputs)
82
83
 
83
84
  ```json tangle:export/readme-example1.json
84
85
  [
@@ -113,7 +114,8 @@ The following pipeline performs these steps (in sequence):
113
114
  "avif": { "quality": 80 }
114
115
  },
115
116
  { "op": "crop", "size": [240, 240], "gravity": "c" },
116
- { "op": "output", "id": "thumb", "path": "{name}-thumb.jpg" }
117
+ { "op": "output", "id": "thumb", "path": "{name}-thumb.jpg" },
118
+ { "op": "output", "id": "hash", "path": "", blurhash: { detail: 4 } }
117
119
  ]
118
120
  ```
119
121
 
@@ -232,6 +234,11 @@ File output in any of these formats:
232
234
  - tiff
233
235
  - webp
234
236
 
237
+ Alternatively, a
238
+ [blurhash](https://github.com/thi-ng/umbrella/blob/develop/packages/blurhash) of
239
+ the image can be computed and stored in the outputs. In this case, no file will
240
+ be written.
241
+
235
242
  #### Templated output paths
236
243
 
237
244
  Output paths can contain `{id}`-templated parts which will be replaced/expanded.
@@ -302,12 +309,13 @@ For Node.js REPL:
302
309
  const imago = await import("@thi.ng/imago");
303
310
  ```
304
311
 
305
- Package sizes (brotli'd, pre-treeshake): ESM: 4.06 KB
312
+ Package sizes (brotli'd, pre-treeshake): ESM: 4.15 KB
306
313
 
307
314
  ## Dependencies
308
315
 
309
316
  - [@thi.ng/api](https://github.com/thi-ng/umbrella/tree/develop/packages/api)
310
317
  - [@thi.ng/associative](https://github.com/thi-ng/umbrella/tree/develop/packages/associative)
318
+ - [@thi.ng/blurhash](https://github.com/thi-ng/umbrella/tree/develop/packages/blurhash)
311
319
  - [@thi.ng/checks](https://github.com/thi-ng/umbrella/tree/develop/packages/checks)
312
320
  - [@thi.ng/date](https://github.com/thi-ng/umbrella/tree/develop/packages/date)
313
321
  - [@thi.ng/defmulti](https://github.com/thi-ng/umbrella/tree/develop/packages/defmulti)
package/api.d.ts CHANGED
@@ -126,14 +126,54 @@ export interface OutputSpec extends ProcSpec {
126
126
  id: string;
127
127
  /**
128
128
  * Possibly templated output path. See {@link formatPath} for details.
129
+ * Ignored if {@link OutputSpec.blurhash} is being used.
129
130
  */
130
131
  path: string;
132
+ /**
133
+ * AVIF output options. See [Sharp docs](https://sharp.pixelplumbing.com/api-output#avif)
134
+ */
131
135
  avif?: AvifOptions;
136
+ /**
137
+ * If given, ONLY the blurhash of the image will be computed and stored in
138
+ * the `outputs` object returned by {@link processImage}. The
139
+ * {@link OutputSpec.path} will be ignored and no file will be written.
140
+ *
141
+ * @remarks
142
+ * Important: Ensure the image has already been downsized to ~50-500 pixels.
143
+ * Larger images are causing unnecessary & long processing...
144
+ */
145
+ blurhash?: {
146
+ /**
147
+ * Blurhash detail setting in 1-9 range, possibly given separately for
148
+ * X/Y axis.
149
+ *
150
+ * @defaultValue 4
151
+ */
152
+ detail?: number | [number, number];
153
+ };
154
+ /**
155
+ * GIF output options. See [Sharp docs](https://sharp.pixelplumbing.com/api-output#gif)
156
+ */
132
157
  gif?: GifOptions;
158
+ /**
159
+ * JPEG 2000 output options. See [Sharp docs](https://sharp.pixelplumbing.com/api-output#jp2)
160
+ */
133
161
  jp2?: Jp2Options;
162
+ /**
163
+ * JPEG output options. See [Sharp docs](https://sharp.pixelplumbing.com/api-output#jpeg)
164
+ */
134
165
  jpeg?: JpegOptions;
166
+ /**
167
+ * JPEG XL output options. See [Sharp docs](https://sharp.pixelplumbing.com/api-output#jxl)
168
+ */
135
169
  jxl?: JxlOptions;
170
+ /**
171
+ * PNG output options. See [Sharp docs](https://sharp.pixelplumbing.com/api-output#avif)
172
+ */
136
173
  png?: PngOptions;
174
+ /**
175
+ * Raw binary output options.
176
+ */
137
177
  raw?: boolean | {
138
178
  /**
139
179
  * If true, ensures the buffer has an alpha channel
@@ -145,8 +185,17 @@ export interface OutputSpec extends ProcSpec {
145
185
  */
146
186
  meta?: boolean;
147
187
  };
188
+ /**
189
+ * Tiled format output options. See [Sharp docs](https://sharp.pixelplumbing.com/api-output#tile)
190
+ */
148
191
  tile?: TileOptions;
192
+ /**
193
+ * TIFF output options. See [Sharp docs](https://sharp.pixelplumbing.com/api-output#tiff)
194
+ */
149
195
  tiff?: TiffOptions;
196
+ /**
197
+ * WebP output options. See [Sharp docs](https://sharp.pixelplumbing.com/api-output#webp)
198
+ */
150
199
  webp?: WebpOptions;
151
200
  }
152
201
  export interface ResizeSpec extends ProcSpec {
package/ops/output.js CHANGED
@@ -1,11 +1,16 @@
1
+ import { encode } from "@thi.ng/blurhash";
2
+ import { isNumber, isPlainObject } from "@thi.ng/checks";
1
3
  import { writeFile, writeJSON } from "@thi.ng/file-io";
2
4
  import { join, resolve } from "node:path";
3
5
  import { formatPath } from "../path.js";
4
- import { isPlainObject } from "@thi.ng/checks";
5
6
  const outputProc = async (spec, input, ctx) => {
6
7
  const opts = spec;
7
8
  const outDir = resolve(ctx.opts.outDir || ".");
8
9
  let output = input.clone();
10
+ if (opts.blurhash) {
11
+ await outputBlurHash(opts, output, ctx);
12
+ return [input, false];
13
+ }
9
14
  if (opts.raw) {
10
15
  await outputRaw(opts, output, ctx, outDir);
11
16
  return [input, false];
@@ -76,7 +81,7 @@ const outputRaw = async (opts, output, ctx, outDir) => {
76
81
  const { alpha = false, meta = false } = isPlainObject(opts.raw) ? opts.raw : {};
77
82
  if (alpha)
78
83
  output = output.ensureAlpha();
79
- const { data, info } = await output.raw().toBuffer({ resolveWithObject: true });
84
+ const { data, info } = await output.ensureAlpha().raw().toBuffer({ resolveWithObject: true });
80
85
  const path = join(outDir, formatPath(opts.path, ctx, opts, data));
81
86
  writeFile(path, data, null, ctx.logger);
82
87
  ctx.outputs[opts.id] = path;
@@ -90,6 +95,20 @@ const outputRaw = async (opts, output, ctx, outDir) => {
90
95
  );
91
96
  }
92
97
  };
98
+ const outputBlurHash = async (opts, output, ctx) => {
99
+ const { data, info } = await output.ensureAlpha().raw().toBuffer({ resolveWithObject: true });
100
+ const detail = opts.blurhash.detail || 4;
101
+ const [dx, dy] = isNumber(detail) ? [detail, detail] : detail;
102
+ const hash = encode(
103
+ new Uint32Array(data.buffer),
104
+ info.width,
105
+ info.height,
106
+ dx,
107
+ dy
108
+ );
109
+ ctx.logger.debug("computed blurhash:", hash);
110
+ ctx.outputs[opts.id] = hash;
111
+ };
93
112
  export {
94
113
  outputProc
95
114
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/imago",
3
- "version": "0.3.1",
3
+ "version": "0.4.1",
4
4
  "description": "JSON & API-based declarative and extensible image processing trees/pipelines",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -37,20 +37,21 @@
37
37
  "dependencies": {
38
38
  "@thi.ng/api": "^8.9.26",
39
39
  "@thi.ng/associative": "^6.3.43",
40
+ "@thi.ng/blurhash": "^0.1.11",
40
41
  "@thi.ng/checks": "^3.5.0",
41
- "@thi.ng/date": "^2.7.1",
42
+ "@thi.ng/date": "^2.7.2",
42
43
  "@thi.ng/defmulti": "^3.0.26",
43
44
  "@thi.ng/errors": "^2.4.18",
44
45
  "@thi.ng/file-io": "^1.3.4",
45
46
  "@thi.ng/logger": "^3.0.3",
46
- "@thi.ng/pixel": "^6.1.12",
47
- "@thi.ng/pixel-dither": "^1.1.110",
47
+ "@thi.ng/pixel": "^6.1.13",
48
+ "@thi.ng/pixel-dither": "^1.1.111",
48
49
  "@thi.ng/prefixes": "^2.3.10",
49
50
  "sharp": "^0.33.2"
50
51
  },
51
52
  "devDependencies": {
52
53
  "@microsoft/api-extractor": "^7.40.1",
53
- "@thi.ng/vectors": "^7.10.12",
54
+ "@thi.ng/vectors": "^7.10.13",
54
55
  "esbuild": "^0.20.0",
55
56
  "rimraf": "^5.0.5",
56
57
  "typedoc": "^0.25.7",
@@ -125,5 +126,5 @@
125
126
  "status": "alpha",
126
127
  "year": 2024
127
128
  },
128
- "gitHead": "6e20f80dd9df1c8055ffa3c1e4d6f7598add0c0b\n"
129
+ "gitHead": "190d68e7f7524631b333cfdbf32c6a23be27c166\n"
129
130
  }
package/proc.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  /// <reference types="node" />
2
+ import type { TypedArray } from "@thi.ng/api";
2
3
  import sharp, { type Sharp } from "sharp";
3
4
  import type { ImgProcCtx, ImgProcOpts, ProcSpec } from "./api.js";
4
5
  export declare const LOGGER: import("@thi.ng/logger").ProxyLogger;
@@ -17,7 +18,7 @@ export declare const LOGGER: import("@thi.ng/logger").ProxyLogger;
17
18
  * @param opts
18
19
  * @param parentCtx
19
20
  */
20
- export declare const processImage: (src: string | Buffer | Sharp, specs: ProcSpec[], opts?: Partial<ImgProcOpts>, parentCtx?: ImgProcCtx) => Promise<{
21
+ export declare const processImage: (src: string | TypedArray | Buffer | ArrayBuffer | Sharp, specs: ProcSpec[], opts?: Partial<ImgProcOpts>, parentCtx?: ImgProcCtx) => Promise<{
21
22
  img: sharp.Sharp;
22
23
  meta: sharp.Metadata;
23
24
  outputs: Record<string, string>;
package/proc.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { isArrayBufferView, isString } from "@thi.ng/checks";
2
2
  import { defmulti } from "@thi.ng/defmulti";
3
+ import { createTempFile, deleteFile } from "@thi.ng/file-io";
3
4
  import { ROOT } from "@thi.ng/logger";
4
5
  import sharp, {} from "sharp";
5
6
  import { blurProc } from "./ops/blur.js";
@@ -16,10 +17,9 @@ import { outputProc } from "./ops/output.js";
16
17
  import { resizeProc } from "./ops/resize.js";
17
18
  import { rotateProc } from "./ops/rotate.js";
18
19
  import { ensureSize } from "./units.js";
19
- import { createTempFile, deleteFile } from "@thi.ng/file-io";
20
20
  const LOGGER = ROOT.childLogger("imgproc");
21
21
  const processImage = async (src, specs, opts = {}, parentCtx) => {
22
- let img = isString(src) || isArrayBufferView(src.buffer) ? sharp(src) : src;
22
+ let img = isString(src) || isArrayBufferView(src) ? sharp(src) : src;
23
23
  const meta = await img.metadata();
24
24
  ensureSize(meta);
25
25
  const ctx = {