@thi.ng/imago 0.2.0 → 0.3.0
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 +18 -1
- package/README.md +38 -16
- package/api.d.ts +63 -19
- package/layers/image.d.ts +3 -0
- package/layers/image.js +29 -0
- package/layers/svg.d.ts +3 -0
- package/layers/svg.js +17 -0
- package/layers/text.d.ts +3 -0
- package/layers/text.js +46 -0
- package/ops/blur.d.ts +3 -0
- package/ops/blur.js +7 -0
- package/ops/composite.d.ts +5 -0
- package/ops/composite.js +25 -0
- package/ops/crop.d.ts +3 -0
- package/ops/crop.js +44 -0
- package/ops/dither.d.ts +3 -0
- package/ops/dither.js +68 -0
- package/ops/exif.d.ts +3 -0
- package/ops/exif.js +9 -0
- package/ops/extend.d.ts +3 -0
- package/ops/extend.js +20 -0
- package/ops/gamma.d.ts +3 -0
- package/ops/gamma.js +7 -0
- package/ops/grayscale.d.ts +3 -0
- package/ops/grayscale.js +11 -0
- package/ops/hsbl.d.ts +3 -0
- package/ops/hsbl.js +15 -0
- package/ops/nest.d.ts +3 -0
- package/ops/nest.js +13 -0
- package/ops/output.d.ts +3 -0
- package/ops/output.js +95 -0
- package/ops/resize.d.ts +3 -0
- package/ops/resize.js +20 -0
- package/ops/rotate.d.ts +3 -0
- package/ops/rotate.js +12 -0
- package/ops.d.ts +14 -14
- package/ops.js +1 -1
- package/package.json +6 -3
- package/proc.d.ts +24 -6
- package/proc.js +64 -340
package/ops/extend.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { coerceColor, computeMargins } from "../units.js";
|
|
2
|
+
const extendProc = async (spec, input, ctx) => {
|
|
3
|
+
const { bg, border, mode, ref, unit } = spec;
|
|
4
|
+
const sides = computeMargins(border, ctx.size, ref, unit);
|
|
5
|
+
const [left, right, top, bottom] = sides;
|
|
6
|
+
return [
|
|
7
|
+
input.extend({
|
|
8
|
+
left,
|
|
9
|
+
right,
|
|
10
|
+
top,
|
|
11
|
+
bottom,
|
|
12
|
+
background: coerceColor(bg || "#000"),
|
|
13
|
+
extendWith: mode
|
|
14
|
+
}),
|
|
15
|
+
true
|
|
16
|
+
];
|
|
17
|
+
};
|
|
18
|
+
export {
|
|
19
|
+
extendProc
|
|
20
|
+
};
|
package/ops/gamma.d.ts
ADDED
package/ops/gamma.js
ADDED
package/ops/grayscale.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { isNumber } from "@thi.ng/checks";
|
|
2
|
+
const grayscaleProc = async (spec, input) => {
|
|
3
|
+
const { gamma } = spec;
|
|
4
|
+
if (gamma !== false) {
|
|
5
|
+
input = input.gamma(isNumber(gamma) ? gamma : void 0);
|
|
6
|
+
}
|
|
7
|
+
return [input.grayscale(), true];
|
|
8
|
+
};
|
|
9
|
+
export {
|
|
10
|
+
grayscaleProc
|
|
11
|
+
};
|
package/ops/hsbl.d.ts
ADDED
package/ops/hsbl.js
ADDED
package/ops/nest.d.ts
ADDED
package/ops/nest.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { processImage } from "../proc.js";
|
|
2
|
+
const nestProc = async (spec, input, ctx) => {
|
|
3
|
+
const { procs } = spec;
|
|
4
|
+
ctx.logger.debug("--- nest start ---");
|
|
5
|
+
await Promise.all(
|
|
6
|
+
procs.map((p) => processImage(input.clone(), p, ctx.opts, ctx))
|
|
7
|
+
);
|
|
8
|
+
ctx.logger.debug("--- nest end ---");
|
|
9
|
+
return [input, false];
|
|
10
|
+
};
|
|
11
|
+
export {
|
|
12
|
+
nestProc
|
|
13
|
+
};
|
package/ops/output.d.ts
ADDED
package/ops/output.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { writeFile, writeJSON } from "@thi.ng/file-io";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { formatPath } from "../path.js";
|
|
4
|
+
import { isPlainObject } from "@thi.ng/checks";
|
|
5
|
+
const outputProc = async (spec, input, ctx) => {
|
|
6
|
+
const opts = spec;
|
|
7
|
+
const outDir = resolve(ctx.opts.outDir || ".");
|
|
8
|
+
let output = input.clone();
|
|
9
|
+
if (opts.raw) {
|
|
10
|
+
await outputRaw(opts, output, ctx, outDir);
|
|
11
|
+
return [input, false];
|
|
12
|
+
}
|
|
13
|
+
if (ctx.meta.exif && ctx.opts.keepEXIF) {
|
|
14
|
+
ctx.logger.warn(
|
|
15
|
+
"TODO injecting & merging EXIF in output still not supported"
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
if (Object.keys(ctx.exif).length) {
|
|
19
|
+
ctx.logger.debug("setting custom EXIF", ctx.exif);
|
|
20
|
+
output = output.withExif(ctx.exif);
|
|
21
|
+
}
|
|
22
|
+
if (ctx.iccFile && ctx.opts.keepICC) {
|
|
23
|
+
ctx.logger.debug("using stored ICC profile:", ctx.iccFile);
|
|
24
|
+
output = output.withIccProfile(ctx.iccFile);
|
|
25
|
+
}
|
|
26
|
+
let format = /\.(\w+)$/.exec(opts.path)?.[1];
|
|
27
|
+
switch (format) {
|
|
28
|
+
case "avif":
|
|
29
|
+
if (opts.avif)
|
|
30
|
+
output = output.avif(opts.avif);
|
|
31
|
+
break;
|
|
32
|
+
case "gif":
|
|
33
|
+
if (opts.gif)
|
|
34
|
+
output = output.gif(opts.gif);
|
|
35
|
+
break;
|
|
36
|
+
case "jpg":
|
|
37
|
+
case "jpeg":
|
|
38
|
+
if (opts.jpeg)
|
|
39
|
+
output = output.jpeg(opts.jpeg);
|
|
40
|
+
break;
|
|
41
|
+
case "jp2":
|
|
42
|
+
if (opts.jp2)
|
|
43
|
+
output = output.jp2(opts.jp2);
|
|
44
|
+
break;
|
|
45
|
+
case "jxl":
|
|
46
|
+
if (opts.jxl)
|
|
47
|
+
output = output.jxl(opts.jxl);
|
|
48
|
+
break;
|
|
49
|
+
case "png":
|
|
50
|
+
if (opts.png)
|
|
51
|
+
output = output.png(opts.png);
|
|
52
|
+
break;
|
|
53
|
+
case "tiff":
|
|
54
|
+
if (opts.tiff)
|
|
55
|
+
output = output.tiff(opts.tiff);
|
|
56
|
+
break;
|
|
57
|
+
case "webp":
|
|
58
|
+
if (opts.webp)
|
|
59
|
+
output = output.webp(opts.webp);
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
if (opts.tile)
|
|
63
|
+
output = output.tile(opts.tile);
|
|
64
|
+
if (format)
|
|
65
|
+
output = output.toFormat(format);
|
|
66
|
+
const result = await output.toBuffer();
|
|
67
|
+
const path = join(
|
|
68
|
+
outDir,
|
|
69
|
+
formatPath(opts.path, ctx, spec, result)
|
|
70
|
+
);
|
|
71
|
+
writeFile(path, result, null, ctx.logger);
|
|
72
|
+
ctx.outputs[opts.id] = path;
|
|
73
|
+
return [input, false];
|
|
74
|
+
};
|
|
75
|
+
const outputRaw = async (opts, output, ctx, outDir) => {
|
|
76
|
+
const { alpha = false, meta = false } = isPlainObject(opts.raw) ? opts.raw : {};
|
|
77
|
+
if (alpha)
|
|
78
|
+
output = output.ensureAlpha();
|
|
79
|
+
const { data, info } = await output.raw().toBuffer({ resolveWithObject: true });
|
|
80
|
+
const path = join(outDir, formatPath(opts.path, ctx, opts, data));
|
|
81
|
+
writeFile(path, data, null, ctx.logger);
|
|
82
|
+
ctx.outputs[opts.id] = path;
|
|
83
|
+
if (meta) {
|
|
84
|
+
writeJSON(
|
|
85
|
+
path + ".meta.json",
|
|
86
|
+
{ ...info, exif: ctx.exif },
|
|
87
|
+
void 0,
|
|
88
|
+
void 0,
|
|
89
|
+
ctx.logger
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
export {
|
|
94
|
+
outputProc
|
|
95
|
+
};
|
package/ops/resize.d.ts
ADDED
package/ops/resize.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { GRAVITY_POSITION } from "../api.js";
|
|
2
|
+
import { coerceColor, computeSize } from "../units.js";
|
|
3
|
+
const resizeProc = async (spec, input, ctx) => {
|
|
4
|
+
const { bg, filter, fit, gravity, size, unit } = spec;
|
|
5
|
+
const [width, height] = computeSize(size, ctx.size, unit);
|
|
6
|
+
return [
|
|
7
|
+
input.resize({
|
|
8
|
+
width,
|
|
9
|
+
height,
|
|
10
|
+
fit,
|
|
11
|
+
kernel: filter,
|
|
12
|
+
position: gravity ? GRAVITY_POSITION[gravity] : void 0,
|
|
13
|
+
background: bg ? coerceColor(bg) : void 0
|
|
14
|
+
}),
|
|
15
|
+
true
|
|
16
|
+
];
|
|
17
|
+
};
|
|
18
|
+
export {
|
|
19
|
+
resizeProc
|
|
20
|
+
};
|
package/ops/rotate.d.ts
ADDED
package/ops/rotate.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { coerceColor } from "../units.js";
|
|
2
|
+
const rotateProc = async (spec, input, _) => {
|
|
3
|
+
const { angle, bg, flipX, flipY } = spec;
|
|
4
|
+
if (flipX)
|
|
5
|
+
input = input.flop();
|
|
6
|
+
if (flipY)
|
|
7
|
+
input = input.flip();
|
|
8
|
+
return [input.rotate(angle, { background: coerceColor(bg) }), true];
|
|
9
|
+
};
|
|
10
|
+
export {
|
|
11
|
+
rotateProc
|
|
12
|
+
};
|
package/ops.d.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import type { BlurSpec, CompSpec, CropSpec, DitherSpec, EXIFSpec, ExtendSpec, GammaSpec, GrayscaleSpec, HSBLSpec, NestSpec, OutputSpec, ProcSpec, ResizeSpec, RotateSpec } from "./api.js";
|
|
2
|
-
export declare const defSpec: <T extends ProcSpec>(
|
|
3
|
-
export declare const blur: (opts: Omit<BlurSpec, "
|
|
4
|
-
export declare const composite: (opts: Omit<CompSpec, "
|
|
5
|
-
export declare const crop: (opts: Omit<CropSpec, "
|
|
6
|
-
export declare const dither: (opts: Omit<DitherSpec, "
|
|
7
|
-
export declare const exif: (opts: Omit<EXIFSpec, "
|
|
8
|
-
export declare const extend: (opts: Omit<ExtendSpec, "
|
|
9
|
-
export declare const gamma: (opts: Omit<GammaSpec, "
|
|
10
|
-
export declare const grayscale: (opts: Omit<GrayscaleSpec, "
|
|
11
|
-
export declare const hsbl: (opts: Omit<HSBLSpec, "
|
|
12
|
-
export declare const nest: (opts: Omit<NestSpec, "
|
|
13
|
-
export declare const output: (opts: Omit<OutputSpec, "
|
|
14
|
-
export declare const resize: (opts: Omit<ResizeSpec, "
|
|
15
|
-
export declare const rotate: (opts: Omit<RotateSpec, "
|
|
2
|
+
export declare const defSpec: <T extends ProcSpec>(op: T["op"]) => (opts: Omit<T, "op">) => T;
|
|
3
|
+
export declare const blur: (opts: Omit<BlurSpec, "op">) => BlurSpec;
|
|
4
|
+
export declare const composite: (opts: Omit<CompSpec, "op">) => CompSpec;
|
|
5
|
+
export declare const crop: (opts: Omit<CropSpec, "op">) => CropSpec;
|
|
6
|
+
export declare const dither: (opts: Omit<DitherSpec, "op">) => DitherSpec;
|
|
7
|
+
export declare const exif: (opts: Omit<EXIFSpec, "op">) => EXIFSpec;
|
|
8
|
+
export declare const extend: (opts: Omit<ExtendSpec, "op">) => ExtendSpec;
|
|
9
|
+
export declare const gamma: (opts: Omit<GammaSpec, "op">) => GammaSpec;
|
|
10
|
+
export declare const grayscale: (opts: Omit<GrayscaleSpec, "op">) => GrayscaleSpec;
|
|
11
|
+
export declare const hsbl: (opts: Omit<HSBLSpec, "op">) => HSBLSpec;
|
|
12
|
+
export declare const nest: (opts: Omit<NestSpec, "op">) => NestSpec;
|
|
13
|
+
export declare const output: (opts: Omit<OutputSpec, "op">) => OutputSpec;
|
|
14
|
+
export declare const resize: (opts: Omit<ResizeSpec, "op">) => ResizeSpec;
|
|
15
|
+
export declare const rotate: (opts: Omit<RotateSpec, "op">) => RotateSpec;
|
|
16
16
|
//# sourceMappingURL=ops.d.ts.map
|
package/ops.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/imago",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "JSON & API-based declarative and extensible image processing trees/pipelines",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
"@thi.ng/logger": "^3.0.2",
|
|
45
45
|
"@thi.ng/pixel": "^6.1.11",
|
|
46
46
|
"@thi.ng/pixel-dither": "^1.1.109",
|
|
47
|
+
"exif-reader": "^2.0.1",
|
|
47
48
|
"sharp": "^0.33.2"
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
|
@@ -95,7 +96,9 @@
|
|
|
95
96
|
},
|
|
96
97
|
"files": [
|
|
97
98
|
"./*.js",
|
|
98
|
-
"./*.d.ts"
|
|
99
|
+
"./*.d.ts",
|
|
100
|
+
"layers",
|
|
101
|
+
"ops"
|
|
99
102
|
],
|
|
100
103
|
"exports": {
|
|
101
104
|
".": {
|
|
@@ -121,5 +124,5 @@
|
|
|
121
124
|
"status": "alpha",
|
|
122
125
|
"year": 2024
|
|
123
126
|
},
|
|
124
|
-
"gitHead": "
|
|
127
|
+
"gitHead": "c744c5f804ca763bc03163a41a0e150d8e7a9e54\n"
|
|
125
128
|
}
|
package/proc.d.ts
CHANGED
|
@@ -1,17 +1,32 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import sharp, { type Sharp } from "sharp";
|
|
3
|
-
import
|
|
3
|
+
import type { ImgProcCtx, ImgProcOpts, ProcSpec } from "./api.js";
|
|
4
4
|
export declare const LOGGER: import("@thi.ng/logger").ProxyLogger;
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Main API function. Takes an image input (file path, buffer or existing Sharp
|
|
7
|
+
* instance) and applies given processing pipeline specs in sequence. Returns a
|
|
8
|
+
* promise of final processed image, input metadata (if any) and an object of
|
|
9
|
+
* all written output paths (keyed by each output's {@link OutputSpec.id}). The
|
|
10
|
+
* process can be configured via provided options.
|
|
11
|
+
*
|
|
12
|
+
* @remarks
|
|
13
|
+
* The `parentCtx` arg is internal use only!
|
|
14
|
+
*
|
|
15
|
+
* @param src
|
|
16
|
+
* @param specs
|
|
17
|
+
* @param opts
|
|
18
|
+
* @param parentCtx
|
|
19
|
+
*/
|
|
20
|
+
export declare const processImage: (src: string | Buffer | Sharp, specs: ProcSpec[], opts?: Partial<ImgProcOpts>, parentCtx?: ImgProcCtx) => Promise<{
|
|
6
21
|
img: sharp.Sharp;
|
|
7
22
|
meta: sharp.Metadata;
|
|
8
|
-
outputs: string
|
|
23
|
+
outputs: Record<string, string>;
|
|
9
24
|
}>;
|
|
10
25
|
/**
|
|
11
26
|
* Extensible polymorphic function performing a single image processing step.
|
|
12
27
|
*
|
|
13
28
|
* @remarks
|
|
14
|
-
* The function returns a tuple of `[img,
|
|
29
|
+
* The function returns a tuple of `[img, bakeFlag]`. If the flag (2nd value)
|
|
15
30
|
* is true, the returned image will be serialized/baked to an internal buffer
|
|
16
31
|
* (also wiping any EXIF!) after the current processing step and then used as
|
|
17
32
|
* input for the next processing step.
|
|
@@ -22,10 +37,13 @@ export declare const processImage: (src: string | Buffer | Sharp, procs: ProcSpe
|
|
|
22
37
|
* function checks this flag after each processing step and its down to each
|
|
23
38
|
* processor to determine if baking is required or not...
|
|
24
39
|
*
|
|
40
|
+
* To add support for a custom operation/processor, call: `processor.add("myid",
|
|
41
|
+
* myProcessor)`. Note that registered IDs should be unique (but can also
|
|
42
|
+
* override existing impls).
|
|
43
|
+
*
|
|
25
44
|
* @param spec
|
|
26
45
|
* @param img
|
|
27
46
|
* @param ctx
|
|
28
47
|
**/
|
|
29
|
-
export declare const
|
|
30
|
-
export declare const defLayer: import("@thi.ng/defmulti").MultiFn3<CompLayer, sharp.Sharp, ImgProcCtx, Promise<sharp.OverlayOptions>>;
|
|
48
|
+
export declare const processor: import("@thi.ng/defmulti").MultiFn3<ProcSpec, sharp.Sharp, ImgProcCtx, Promise<[sharp.Sharp, boolean]>>;
|
|
31
49
|
//# sourceMappingURL=proc.d.ts.map
|