@thi.ng/text-canvas 2.3.6 → 2.4.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2022-06-09T16:14:01Z
3
+ - **Last updated**: 2022-07-08T18:20:19Z
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,24 @@ 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
+ ## [2.4.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/text-canvas@2.4.0) (2022-07-08)
13
+
14
+ #### 🚀 Features
15
+
16
+ - add canvasFromText(), update Canvas ([e8baa0b](https://github.com/thi-ng/umbrella/commit/e8baa0b))
17
+ - update deps
18
+ - add canvasFromText() factory fn
19
+ - add ICopy impl for Canvas
20
+ - add clearFormat() ([83f04cc](https://github.com/thi-ng/umbrella/commit/83f04cc))
21
+ - add blitMask() & docs ([a6cf74a](https://github.com/thi-ng/umbrella/commit/a6cf74a))
22
+
23
+ ### [2.3.8](https://github.com/thi-ng/umbrella/tree/@thi.ng/text-canvas@2.3.8) (2022-06-28)
24
+
25
+ #### ♻️ Refactoring
26
+
27
+ - update/simplify formatCanvas() ([e2f3ab9](https://github.com/thi-ng/umbrella/commit/e2f3ab9))
28
+ - re-use new single-line formatting fns from [@thi.ng/text-format](https://github.com/thi-ng/umbrella/tree/main/packages/text-format)
29
+
12
30
  ## [2.3.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/text-canvas@2.3.0) (2022-04-07)
13
31
 
14
32
  #### 🚀 Features
package/README.md CHANGED
@@ -70,7 +70,7 @@ node --experimental-repl-await
70
70
  > const textCanvas = await import("@thi.ng/text-canvas");
71
71
  ```
72
72
 
73
- Package sizes (gzipped, pre-treeshake): ESM: 5.36 KB
73
+ Package sizes (gzipped, pre-treeshake): ESM: 5.24 KB
74
74
 
75
75
  ## Dependencies
76
76
 
package/canvas.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { Fn0, IGrid2D, NumOrString } from "@thi.ng/api";
1
+ import { Fn0, ICopy, IGrid2D, NumOrString } from "@thi.ng/api";
2
2
  import { ClipRect, StrokeStyle } from "./api.js";
3
- export declare class Canvas implements IGrid2D<Uint32Array, number> {
3
+ export declare class Canvas implements ICopy<Canvas>, IGrid2D<Uint32Array, number> {
4
4
  data: Uint32Array;
5
5
  size: [number, number];
6
6
  stride: [number, number];
@@ -13,6 +13,7 @@ export declare class Canvas implements IGrid2D<Uint32Array, number> {
13
13
  get height(): number;
14
14
  get offset(): number;
15
15
  get dim(): 2;
16
+ copy(): Canvas;
16
17
  order(): number[];
17
18
  includes(d0: number, d1: number): boolean;
18
19
  indexAt(d0: number, d1: number): number;
@@ -23,6 +24,19 @@ export declare class Canvas implements IGrid2D<Uint32Array, number> {
23
24
  setAtUnsafe(x: number, y: number, col: number): boolean;
24
25
  }
25
26
  export declare const canvas: (width: number, height: number, format?: number, style?: StrokeStyle) => Canvas;
27
+ /**
28
+ * Creates and returns a new {@link Canvas} from given string/lines array and
29
+ * optional default `format` and/or initial `fill` value.
30
+ *
31
+ * @remarks
32
+ * The canvas will use the longest line width as its width and the length of the
33
+ * source array as height.
34
+ *
35
+ * @param lines
36
+ * @param format
37
+ * @param fill
38
+ */
39
+ export declare const canvasFromText: (lines: string[], format?: number, fill?: NumOrString) => Canvas;
26
40
  export declare const beginClip: (canvas: Canvas, x: number, y: number, w: number, h: number) => void;
27
41
  export declare const endClip: (canvas: Canvas) => any;
28
42
  export declare const withClip: (canvas: Canvas, x: number, y: number, w: number, h: number, fn: Fn0<any>) => void;
package/canvas.js CHANGED
@@ -1,12 +1,17 @@
1
+ var Canvas_1;
1
2
  import { __decorate } from "tslib";
2
3
  import { nomixin } from "@thi.ng/api";
3
4
  import { IGrid2DMixin } from "@thi.ng/api/mixins/igrid";
4
5
  import { peek } from "@thi.ng/arrays/peek";
6
+ import { assert } from "@thi.ng/errors/assert";
5
7
  import { clamp } from "@thi.ng/math/interval";
6
- import { NONE } from "@thi.ng/text-format/api";
8
+ import { NONE } from "@thi.ng/text-format";
9
+ import { map } from "@thi.ng/transducers/map";
10
+ import { max } from "@thi.ng/transducers/max";
11
+ import { transduce } from "@thi.ng/transducers/transduce";
7
12
  import { STYLE_ASCII } from "./api.js";
8
13
  import { charCode, intersectRect } from "./utils.js";
9
- let Canvas = class Canvas {
14
+ let Canvas = Canvas_1 = class Canvas {
10
15
  constructor(width, height, format = NONE, style = STYLE_ASCII) {
11
16
  this.size = [width, height];
12
17
  this.stride = [1, this.width];
@@ -29,6 +34,14 @@ let Canvas = class Canvas {
29
34
  get dim() {
30
35
  return 2;
31
36
  }
37
+ copy() {
38
+ const res = new Canvas_1(this.width, this.height, this.format);
39
+ res.data.set(this.data);
40
+ res.stride = this.stride.slice();
41
+ res.styles = this.styles.slice();
42
+ res.clipRects = this.clipRects.slice();
43
+ return res;
44
+ }
32
45
  // @ts-ignore mixin
33
46
  order() { }
34
47
  // @ts-ignore mixin
@@ -53,11 +66,38 @@ let Canvas = class Canvas {
53
66
  __decorate([
54
67
  nomixin
55
68
  ], Canvas.prototype, "setAt", null);
56
- Canvas = __decorate([
69
+ Canvas = Canvas_1 = __decorate([
57
70
  IGrid2DMixin
58
71
  ], Canvas);
59
72
  export { Canvas };
60
73
  export const canvas = (width, height, format, style) => new Canvas(width, height, format, style);
74
+ /**
75
+ * Creates and returns a new {@link Canvas} from given string/lines array and
76
+ * optional default `format` and/or initial `fill` value.
77
+ *
78
+ * @remarks
79
+ * The canvas will use the longest line width as its width and the length of the
80
+ * source array as height.
81
+ *
82
+ * @param lines
83
+ * @param format
84
+ * @param fill
85
+ */
86
+ export const canvasFromText = (lines, format = NONE, fill) => {
87
+ const height = lines.length;
88
+ assert(height > 0, "require at least 1 line of text");
89
+ const width = transduce(map((x) => x.length), max(), lines);
90
+ const res = canvas(width, height, format);
91
+ fill !== undefined && res.data.fill(charCode(fill, format));
92
+ format <<= 16;
93
+ for (let y = 0; y < height; y++) {
94
+ const line = lines[y];
95
+ for (let x = 0, i = y * width, n = Math.min(width, line.length); x < n; x++, i++) {
96
+ res.data[i] = line.charCodeAt(x) | format;
97
+ }
98
+ }
99
+ return res;
100
+ };
61
101
  export const beginClip = (canvas, x, y, w, h) => {
62
102
  x |= 0;
63
103
  y |= 0;
package/format.d.ts CHANGED
@@ -6,7 +6,7 @@ import type { Canvas } from "./canvas.js";
6
6
  * any character format data.
7
7
  *
8
8
  * @param canvas -
9
- * @param format -
9
+ * @param fmt -
10
10
  */
11
- export declare const formatCanvas: (canvas: Canvas, format?: StringFormat) => string;
11
+ export declare const formatCanvas: (canvas: Canvas, fmt?: StringFormat) => string;
12
12
  //# sourceMappingURL=format.d.ts.map
package/format.js CHANGED
@@ -1,43 +1,24 @@
1
+ import { format, formatNone } from "@thi.ng/text-format/format";
1
2
  /**
2
3
  * Returns string representation of canvas, optionally using given string
3
4
  * formatter. If none is given, returns plain string representation, ignoring
4
5
  * any character format data.
5
6
  *
6
7
  * @param canvas -
7
- * @param format -
8
+ * @param fmt -
8
9
  */
9
- export const formatCanvas = (canvas, format) => {
10
+ export const formatCanvas = (canvas, fmt) => {
10
11
  const { data, width, height } = canvas;
11
12
  const res = [];
12
- if (format) {
13
- const { start, end, prefix, suffix, zero } = format;
14
- let prevID, ch, id;
15
- const check = zero ? () => prevID !== -1 : () => prevID !== 0;
16
- for (let y = 0, i = 0; y < height; y++) {
17
- prevID = zero ? -1 : 0;
18
- res.push(prefix);
19
- for (let x = 0; x < width; x++, i++) {
20
- ch = data[i];
21
- id = ch >>> 16;
22
- if (id != prevID) {
23
- check() && res.push(end);
24
- (zero || id) && res.push(start(id));
25
- prevID = id;
26
- }
27
- res.push(String.fromCharCode(ch & 0xffff));
28
- }
29
- check() && res.push(end);
30
- res.push(suffix);
13
+ if (fmt) {
14
+ for (let y = 0; y < height; y++) {
15
+ res.push(format(fmt, data, width, y * width));
31
16
  }
32
- return res.join("");
33
17
  }
34
18
  else {
35
- for (let y = 0, i = 0; y < height; y++) {
36
- for (let x = 0; x < width; x++, i++) {
37
- res.push(String.fromCharCode(data[i] & 0xffff));
38
- }
39
- res.push("\n");
19
+ for (let y = 0; y < height; y++) {
20
+ res.push(formatNone(data, width, y * width));
40
21
  }
41
- return res.join("");
42
22
  }
23
+ return res.join("");
43
24
  };
package/image.d.ts CHANGED
@@ -1,7 +1,51 @@
1
- import type { UIntArray } from "@thi.ng/api";
1
+ import type { NumOrString, UIntArray } from "@thi.ng/api";
2
2
  import { ImageOpts } from "./api.js";
3
3
  import { Canvas } from "./canvas.js";
4
- export declare const blit: (canvas: Canvas, x: number, y: number, src: Canvas) => void;
4
+ export declare const blit: (dest: Canvas, x: number, y: number, src: Canvas) => void;
5
+ /**
6
+ * Similar to {@link blit}. Pastes `src` {@link Canvas} into `dest` at given
7
+ * position and uses `mask` to exclude pixels from being copied (and therefore
8
+ * achieve a form of 1bit transparency, similar to GIFs), i.e. only non-`mask`
9
+ * pixels/chars will be copied. Supports region clipping.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * // source canvas
14
+ * const a = canvasFromText([
15
+ * "###==###",
16
+ * "##====##",
17
+ * "#======#",
18
+ * "##====##",
19
+ * "###==###",
20
+ * ]);
21
+ *
22
+ * // destination canvas (filled w/ "-")
23
+ * const b = canvas(12,7);
24
+ * clear(b, true, "-");
25
+ *
26
+ * // paste `a` several times into `b` using "#" as mask
27
+ * blitMask(b, -4, -2, a, "#"); // top-left (partially outside)
28
+ * blitMask(b, 2, 1, a, "#"); // center
29
+ * blitMask(b, 8, 4, a, "#"); // bottom-right (part outside)
30
+ *
31
+ * // show result
32
+ * console.log(formatCanvas(b))
33
+ * // ===---------
34
+ * // ==---==-----
35
+ * // =---====----
36
+ * // ---======---
37
+ * // ----====---=
38
+ * // -----==---==
39
+ * // ---------===
40
+ * ```
41
+ *
42
+ * @param dest
43
+ * @param x
44
+ * @param y
45
+ * @param src
46
+ * @param mask
47
+ */
48
+ export declare const blitMask: (dest: Canvas, x: number, y: number, src: Canvas, mask?: NumOrString) => void;
5
49
  export declare const resize: (canvas: Canvas, newWidth: number, newHeight: number) => void;
6
50
  export declare const extract: (canvas: Canvas, x: number, y: number, w: number, h: number) => Canvas;
7
51
  /**
package/image.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { blit1d } from "@thi.ng/arrays/blit";
1
2
  import { peek } from "@thi.ng/arrays/peek";
2
3
  import { isNumber } from "@thi.ng/checks/is-number";
3
4
  import { clamp0 } from "@thi.ng/math/interval";
@@ -6,12 +7,12 @@ import { SHADES_BLOCK } from "./api.js";
6
7
  import { canvas, Canvas } from "./canvas.js";
7
8
  import { formatCanvas } from "./format.js";
8
9
  import { charCode, intersectRect } from "./utils.js";
9
- export const blit = (canvas, x, y, src) => {
10
+ export const blit = (dest, x, y, src) => {
10
11
  x |= 0;
11
12
  y |= 0;
12
13
  const { data: sbuf, width: sw, height: sh } = src;
13
- const { data: dbuf, width: dw } = canvas;
14
- const { x1, y1, y2, w: iw, h: ih, } = intersectRect({ x1: x, y1: y, x2: x + sw, y2: y + sh, w: sw, h: sh }, peek(canvas.clipRects));
14
+ const { data: dbuf, width: dw } = dest;
15
+ const { x1, y1, y2, w: iw, h: ih, } = intersectRect({ x1: x, y1: y, x2: x + sw, y2: y + sh, w: sw, h: sh }, peek(dest.clipRects));
15
16
  if (!iw || !ih)
16
17
  return;
17
18
  const sx = clamp0(x1 - x);
@@ -22,6 +23,66 @@ export const blit = (canvas, x, y, src) => {
22
23
  dbuf.set(sbuf.subarray(sidx, sidx + iw), didx);
23
24
  }
24
25
  };
26
+ /**
27
+ * Similar to {@link blit}. Pastes `src` {@link Canvas} into `dest` at given
28
+ * position and uses `mask` to exclude pixels from being copied (and therefore
29
+ * achieve a form of 1bit transparency, similar to GIFs), i.e. only non-`mask`
30
+ * pixels/chars will be copied. Supports region clipping.
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * // source canvas
35
+ * const a = canvasFromText([
36
+ * "###==###",
37
+ * "##====##",
38
+ * "#======#",
39
+ * "##====##",
40
+ * "###==###",
41
+ * ]);
42
+ *
43
+ * // destination canvas (filled w/ "-")
44
+ * const b = canvas(12,7);
45
+ * clear(b, true, "-");
46
+ *
47
+ * // paste `a` several times into `b` using "#" as mask
48
+ * blitMask(b, -4, -2, a, "#"); // top-left (partially outside)
49
+ * blitMask(b, 2, 1, a, "#"); // center
50
+ * blitMask(b, 8, 4, a, "#"); // bottom-right (part outside)
51
+ *
52
+ * // show result
53
+ * console.log(formatCanvas(b))
54
+ * // ===---------
55
+ * // ==---==-----
56
+ * // =---====----
57
+ * // ---======---
58
+ * // ----====---=
59
+ * // -----==---==
60
+ * // ---------===
61
+ * ```
62
+ *
63
+ * @param dest
64
+ * @param x
65
+ * @param y
66
+ * @param src
67
+ * @param mask
68
+ */
69
+ export const blitMask = (dest, x, y, src, mask = 0x20) => {
70
+ x |= 0;
71
+ y |= 0;
72
+ const { data: sbuf, width: sw, height: sh } = src;
73
+ const { data: dbuf, width: dw } = dest;
74
+ const { x1, y1, y2, w: iw, h: ih, } = intersectRect({ x1: x, y1: y, x2: x + sw, y2: y + sh, w: sw, h: sh }, peek(dest.clipRects));
75
+ if (!iw || !ih)
76
+ return;
77
+ const sx = clamp0(x1 - x);
78
+ const sy = clamp0(y1 - y);
79
+ mask = charCode(mask, 0);
80
+ for (let yy = sy, dy = y1; dy < y2; yy++, dy++) {
81
+ let sidx = sx + yy * sw;
82
+ let didx = x1 + dy * dw;
83
+ blit1d(dbuf, didx, sbuf.subarray(sidx, sidx + iw), mask);
84
+ }
85
+ };
25
86
  export const resize = (canvas, newWidth, newHeight) => {
26
87
  if (canvas.width === newWidth && canvas.height === newHeight)
27
88
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/text-canvas",
3
- "version": "2.3.6",
3
+ "version": "2.4.0",
4
4
  "description": "Text based canvas, drawing, tables with arbitrary formatting (incl. ANSI/HTML)",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -35,13 +35,14 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "@thi.ng/api": "^8.3.7",
38
- "@thi.ng/arrays": "^2.2.5",
38
+ "@thi.ng/arrays": "^2.3.0",
39
39
  "@thi.ng/checks": "^3.2.1",
40
- "@thi.ng/geom-clip-line": "^2.1.15",
40
+ "@thi.ng/errors": "^2.1.7",
41
+ "@thi.ng/geom-clip-line": "^2.1.18",
41
42
  "@thi.ng/math": "^5.3.3",
42
43
  "@thi.ng/strings": "^3.3.5",
43
- "@thi.ng/text-format": "^1.1.7",
44
- "@thi.ng/transducers": "^8.3.5"
44
+ "@thi.ng/text-format": "^1.2.0",
45
+ "@thi.ng/transducers": "^8.3.6"
45
46
  },
46
47
  "devDependencies": {
47
48
  "@microsoft/api-extractor": "^7.25.0",
@@ -138,5 +139,5 @@
138
139
  ],
139
140
  "year": 2020
140
141
  },
141
- "gitHead": "f50e292ecc7dc74a6705b07fc739bf827cc5efd7\n"
142
+ "gitHead": "e64b1ab39ae9bcc494ef006f6329e5182fa2a326\n"
142
143
  }
package/rect.d.ts CHANGED
@@ -10,6 +10,13 @@ import type { Canvas } from "./canvas.js";
10
10
  * @param code -
11
11
  */
12
12
  export declare const clear: (canvas: Canvas, reset?: boolean, code?: NumOrString) => void;
13
+ /**
14
+ * Clears or resets format of entire canvas.
15
+ *
16
+ * @param canvas -
17
+ * @param format -
18
+ */
19
+ export declare const clearFormat: ({ data }: Canvas, format?: number) => void;
13
20
  /**
14
21
  * Fills given rect with char, taking currect clip rect and format into
15
22
  * account.
package/rect.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { peek } from "@thi.ng/arrays/peek";
2
+ import { NONE } from "@thi.ng/text-format/api";
2
3
  import { hline, vline } from "./hvline.js";
3
4
  import { charCode } from "./utils.js";
4
5
  /**
@@ -25,6 +26,17 @@ export const clear = (canvas, reset = false, code = 0x20) => {
25
26
  canvas.data.fill(code);
26
27
  }
27
28
  };
29
+ /**
30
+ * Clears or resets format of entire canvas.
31
+ *
32
+ * @param canvas -
33
+ * @param format -
34
+ */
35
+ export const clearFormat = ({ data }, format = NONE) => {
36
+ format <<= 16;
37
+ for (let i = data.length; i-- > 0;)
38
+ data[i] = (data[i] & 0xffff) | format;
39
+ };
28
40
  /**
29
41
  * Fills given rect with char, taking currect clip rect and format into
30
42
  * account.