@thi.ng/imago 0.4.0 → 0.5.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/proc.d.ts CHANGED
@@ -1,6 +1,5 @@
1
- /// <reference types="node" />
2
1
  import sharp, { type Sharp } from "sharp";
3
- import type { ImgProcCtx, ImgProcOpts, ProcSpec } from "./api.js";
2
+ import type { BufferLike, ImgProcCtx, ImgProcOpts, ProcSpec } from "./api.js";
4
3
  export declare const LOGGER: import("@thi.ng/logger").ProxyLogger;
5
4
  /**
6
5
  * Main API function. Takes an image input (file path, buffer or existing Sharp
@@ -17,7 +16,7 @@ export declare const LOGGER: import("@thi.ng/logger").ProxyLogger;
17
16
  * @param opts
18
17
  * @param parentCtx
19
18
  */
20
- export declare const processImage: (src: string | Buffer | Sharp, specs: ProcSpec[], opts?: Partial<ImgProcOpts>, parentCtx?: ImgProcCtx) => Promise<{
19
+ export declare const processImage: (src: string | BufferLike | ArrayBuffer | Sharp, specs: ProcSpec[], opts?: Partial<ImgProcOpts>, parentCtx?: ImgProcCtx) => Promise<{
21
20
  img: sharp.Sharp;
22
21
  meta: sharp.Metadata;
23
22
  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 = {
package/units.d.ts CHANGED
@@ -1,6 +1,5 @@
1
- import type { Nullable } from "@thi.ng/api";
2
1
  import type { Metadata } from "sharp";
3
- import { type Color, type CompLayer, type Dim, type Gravity, type Sides, type Size, type SizeRef, type SizeUnit } from "./api.js";
2
+ import { type Color, type Dim, type Gravity, type Position, type Sides, type Size, type SizeRef, type SizeUnit } from "./api.js";
4
3
  export declare const ensureSize: (meta: Metadata) => false;
5
4
  export declare const coerceColor: (col: Color) => string | {
6
5
  r: number;
@@ -8,7 +7,23 @@ export declare const coerceColor: (col: Color) => string | {
8
7
  b: number;
9
8
  alpha?: number | undefined;
10
9
  };
11
- export declare const positionOrGravity: (pos: CompLayer["pos"], gravity: Nullable<Gravity>, [w, h]: Dim, [parentW, parentH]: Dim, unit?: SizeUnit) => {
10
+ /**
11
+ *
12
+ * @remarks
13
+ * The given `size` MUST already be resolved (in pixels), e.g. via an earlier
14
+ * call to {@link computeSize}. `parentSize` is also in pixels.
15
+ *
16
+ * @param size
17
+ * @param parentSize
18
+ * @param opts
19
+ */
20
+ export declare const positionOrGravity: ([w, h]: Dim, parentSize: Dim, { pos, gravity, origin, ref, unit, }: {
21
+ pos?: Position | undefined;
22
+ gravity?: Gravity | undefined;
23
+ origin?: Gravity | undefined;
24
+ ref?: SizeRef | undefined;
25
+ unit?: SizeUnit | undefined;
26
+ }) => {
12
27
  gravity: string;
13
28
  left?: undefined;
14
29
  top?: undefined;
@@ -17,8 +32,10 @@ export declare const positionOrGravity: (pos: CompLayer["pos"], gravity: Nullabl
17
32
  top: number | undefined;
18
33
  gravity?: undefined;
19
34
  } | undefined;
35
+ export declare const gravityFlags: (gravity: Gravity) => boolean[];
20
36
  export declare const gravityPosition: (gravity: Gravity, [w, h]: Dim, [parentW, parentH]: Dim) => number[];
21
- export declare const refSize: ([w, h]: Dim, ref?: SizeRef) => number;
22
- export declare const computeSize: (size: Size, curr: Dim, unit?: SizeUnit) => Dim;
37
+ export declare const refSize: ([w, h]: Dim, ref?: SizeRef) => Dim;
38
+ export declare const computeSize: (size: Size, curr: Dim, ref?: SizeRef, unit?: SizeUnit) => Dim;
39
+ export declare const computeSizeWithAspect: (size: number, [w, h]: Dim, aspect: number, unit?: SizeUnit, clamp?: boolean) => Dim;
23
40
  export declare const computeMargins: (size: Size | Sides, curr: Dim, ref?: SizeRef, unit?: SizeUnit) => Sides;
24
41
  //# sourceMappingURL=units.d.ts.map
package/units.js CHANGED
@@ -6,70 +6,119 @@ import {
6
6
  const round = Math.round;
7
7
  const ensureSize = (meta) => !(isNumber(meta.width) && isNumber(meta.height)) && illegalArgs("can't determine image size");
8
8
  const coerceColor = (col) => isString(col) ? col : isArrayLike(col) ? { r: col[0], g: col[1], b: col[2], alpha: col[3] ?? 1 } : col;
9
- const positionOrGravity = (pos, gravity, [w, h], [parentW, parentH], unit = "px") => {
9
+ const positionOrGravity = ([w, h], parentSize, {
10
+ pos,
11
+ gravity,
12
+ origin,
13
+ ref,
14
+ unit = "px"
15
+ }) => {
10
16
  if (!pos)
11
17
  return gravity ? { gravity: GRAVITY_MAP[gravity] } : void 0;
12
- const isPC = unit === "%";
18
+ const [parentW, parentH] = parentSize;
13
19
  let { l, r, t, b } = pos;
14
- if (l != null)
15
- l = round(isPC ? l * parentW / 100 : l);
16
- if (r != null)
17
- l = round(parentW - (isPC ? r * parentW / 100 : r) - w);
18
- if (t != null)
19
- t = round(isPC ? t * parentH / 100 : t);
20
- if (b != null)
21
- t = round(parentH - (isPC ? b * parentH / 100 : b) - h);
22
- return { left: l, top: t };
20
+ [l, r, t, b] = computeMargins(
21
+ [l || 0, r || 0, t || 0, b || 0],
22
+ parentSize,
23
+ ref,
24
+ unit
25
+ );
26
+ let left, top;
27
+ const [isE, isW, isN, isS] = origin ? gravityFlags(origin) : [];
28
+ const w2 = w >> 1;
29
+ const h2 = h >> 1;
30
+ if (pos.l != null)
31
+ left = round(l) + (origin ? isW ? 0 : isE ? -w : -w2 : 0);
32
+ if (pos.r != null)
33
+ left = round(parentW - r) + (origin ? isW ? 0 : isE ? -w : -w2 : -w);
34
+ if (pos.t != null)
35
+ top = round(t) + (origin ? isN ? 0 : isS ? -h : -h2 : 0);
36
+ if (pos.b != null)
37
+ top = round(parentH - b) + (origin ? isN ? 0 : isS ? -h : -h2 : -h);
38
+ return { left, top };
23
39
  };
40
+ const gravityFlags = (gravity) => ["e", "w", "n", "s"].map((x) => gravity.includes(x));
24
41
  const gravityPosition = (gravity, [w, h], [parentW, parentH]) => [
25
42
  gravity.includes("w") ? 0 : gravity.includes("e") ? parentW - w : parentW - w >> 1,
26
43
  gravity.includes("n") ? 0 : gravity.includes("s") ? parentH - h : parentH - h >> 1
27
44
  ];
28
45
  const refSize = ([w, h], ref) => {
46
+ let v;
29
47
  switch (ref) {
30
48
  case "w":
31
- return w;
49
+ return [w, w];
32
50
  case "h":
33
- return h;
51
+ return [h, h];
34
52
  case "max":
35
- return Math.max(w, h);
53
+ v = Math.max(w, h);
54
+ return [v, v];
36
55
  case "min":
56
+ v = Math.min(w, h);
57
+ return [v, v];
58
+ case "both":
37
59
  default:
38
- return Math.min(w, h);
60
+ return [w, h];
39
61
  }
40
62
  };
41
- const computeSize = (size, curr, unit = "px") => {
63
+ const computeSize = (size, curr, ref, unit = "px") => {
42
64
  const aspect = curr[0] / curr[1];
43
65
  let res;
44
66
  if (isNumber(size)) {
45
- res = aspect > 1 ? [size, size / aspect] : [size * aspect, size];
67
+ if (unit === "%") {
68
+ res = refSize(curr, ref);
69
+ res = [res[0] * size / 100, res[1] * size / 100];
70
+ } else {
71
+ res = [size, size];
72
+ }
46
73
  } else {
47
- const [w, h] = size;
48
- res = w >= 0 ? h >= 0 ? size : [w, w / aspect] : h > 0 ? [w * aspect, h] : illegalArgs(
74
+ let [w, h] = size;
75
+ if (unit === "%") {
76
+ const [rw, rh] = refSize(curr, ref);
77
+ w *= rw / 100;
78
+ h *= rh / 100;
79
+ size = [w, h];
80
+ }
81
+ res = w >= 0 ? h >= 0 ? size : [w, w / aspect] : h >= 0 ? [h * aspect, h] : illegalArgs(
49
82
  `require at least width or height, but got: ${JSON.stringify(
50
83
  size
51
84
  )}`
52
85
  );
53
86
  }
87
+ res[0] = round(res[0]);
88
+ res[1] = round(res[1]);
89
+ return res;
90
+ };
91
+ const computeSizeWithAspect = (size, [w, h], aspect, unit = "px", clamp = true) => {
92
+ const origAspect = w / h;
93
+ const min = Math.min(w, h);
94
+ const max = Math.max(w, h);
95
+ let res;
54
96
  if (unit === "%") {
55
- res[0] *= curr[0] / 100;
56
- res[1] *= curr[1] / 100;
97
+ size = size / 100 * max;
57
98
  }
99
+ if (clamp) {
100
+ size = Math.min(size, max);
101
+ if (size / aspect > min)
102
+ size = min * aspect;
103
+ }
104
+ res = origAspect > 1 ? [size, size / aspect] : [size / aspect, size];
58
105
  res[0] = round(res[0]);
59
106
  res[1] = round(res[1]);
60
107
  return res;
61
108
  };
62
- const computeMargins = (size, curr, ref = "min", unit = "px") => {
109
+ const computeMargins = (size, curr, ref, unit = "px") => {
63
110
  let res;
64
111
  const refSide = refSize(curr, ref);
65
112
  const isPC = unit === "%";
66
113
  if (isArray(size) && size.length === 4) {
67
- res = isPC ? size.map((x) => round(x * refSide / 100)) : size.map(round);
114
+ res = isPC ? size.map((x, i) => round(x * refSide[i >> 1] / 100)) : size.map(round);
68
115
  } else if (isNumber(size)) {
69
- const w = round(isPC ? refSide * size / 100 : size);
70
- res = [w, w, w, w];
116
+ const w = round(isPC ? refSide[0] * size / 100 : size);
117
+ const h = round(isPC ? refSide[1] * size / 100 : size);
118
+ res = [w, w, h, h];
71
119
  } else {
72
- const [w, h] = computeSize(size, curr, unit);
120
+ const w = round(isPC ? refSide[0] * size[0] / 100 : size[0]);
121
+ const h = round(isPC ? refSide[1] * size[1] / 100 : size[1]);
73
122
  res = [w, w, h, h];
74
123
  }
75
124
  return res;
@@ -78,7 +127,9 @@ export {
78
127
  coerceColor,
79
128
  computeMargins,
80
129
  computeSize,
130
+ computeSizeWithAspect,
81
131
  ensureSize,
132
+ gravityFlags,
82
133
  gravityPosition,
83
134
  positionOrGravity,
84
135
  refSize