Skip to content

Commit

Permalink
feat(text-canvas): add imageRaw(), update image()
Browse files Browse the repository at this point in the history
- add imageRaw() for direct use of pixels as format data (e.g. for FMT_HTML_565)
- update ImageOpts.format to allow functions
- update Canvas ctor, initial clear value to include format
  • Loading branch information
postspectacular committed Dec 23, 2020
1 parent 1f2d35b commit 34037ad
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 12 deletions.
8 changes: 5 additions & 3 deletions packages/text-canvas/src/api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Fn, NumOrString } from "@thi.ng/api";
import type { Fn, FnN, NumOrString } from "@thi.ng/api";

export enum Align {
LEFT,
Expand Down Expand Up @@ -40,9 +40,11 @@ export interface ImageOpts {
*/
chars: NumOrString[];
/**
* Format to apply to all chars.
* Format to apply to each pixel. If a function is given, it will be called
* for each pixel value, normalized to [0..1] interval (and after gamma
* correction). The function MUST return a valid format ID.
*/
format: number;
format: number | FnN;
/**
* Gamma correction value / exponent. All source pixel values will
* be raised by this exponent.
Expand Down
2 changes: 1 addition & 1 deletion packages/text-canvas/src/canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ export class Canvas {
) {
this.width = width;
this.height = height;
this.buf = new Uint32Array(width * height).fill(0x20);
this.format = this.defaultFormat = format;
this.buf = new Uint32Array(width * height).fill(charCode(0x20, format));
this.styles = [style];
this.clipRects = [
{ x1: 0, y1: 0, x2: width, y2: height, w: width, h: height },
Expand Down
55 changes: 47 additions & 8 deletions packages/text-canvas/src/image.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { peek } from "@thi.ng/arrays";
import { isNumber } from "@thi.ng/checks";
import { ImageOpts, SHADES_BLOCK } from "./api";
import { Canvas } from "./canvas";
import { charCode, intersectRect } from "./utils";
Expand Down Expand Up @@ -115,6 +116,7 @@ export const image = (
bits: 8,
...opts,
};
const fmt = isNumber(format) ? () => format : format;
const max = (1 << bits) - 1;
const mask = invert ? max : 0;
const norm = 1 / max;
Expand All @@ -123,14 +125,51 @@ export const image = (
let sidx = sx + yy * w;
let didx = x1 + dy * width;
for (let xx = sx, dx = x1; dx < x2; xx++, dx++) {
buf[didx++] = charCode(
chars[
(Math.pow((pixels[sidx++] ^ mask) * norm, gamma) * num +
0.5) |
0
],
format
);
const col = Math.pow((pixels[sidx++] ^ mask) * norm, gamma);
buf[didx++] = charCode(chars[(col * num + 0.5) | 0], fmt(col));
}
}
};

/**
* Optimized version of {@link image}, which only uses a single char for all
* pixels and applies pixel values directly as formatting data (for each pixel).
*
* @param canvas
* @param x
* @param y
* @param w
* @param h
* @param pixels
* @param char
*/
export const imageRaw = (
canvas: Canvas,
x: number,
y: number,
w: number,
h: number,
pixels: ArrayLike<number>,
char = "█"
) => {
x |= 0;
y |= 0;
w |= 0;
h |= 0;
const { buf, width } = canvas;
const { x1, y1, x2, y2, w: iw, h: ih } = intersectRect(
{ x1: x, y1: y, x2: x + w, y2: y + h, w, h },
peek(canvas.clipRects)
);
if (!iw || !ih) return;
const sx = Math.max(0, x1 - x);
const sy = Math.max(0, y1 - y);
const code = char.charCodeAt(0);
for (let yy = sy, dy = y1; dy < y2; yy++, dy++) {
let sidx = sx + yy * w;
let didx = x1 + dy * width;
for (let xx = sx, dx = x1; dx < x2; xx++, dx++) {
buf[didx++] = code | ((pixels[sidx++] & 0xffff) << 16);
}
}
};

0 comments on commit 34037ad

Please sign in to comment.