New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@thi.ng/pixel

Package Overview
Dependencies
Maintainers
1
Versions
231
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@thi.ng/pixel - npm Package Compare versions

Comparing version 0.6.1 to 0.7.1

convolve.d.ts

92

api.d.ts

@@ -1,2 +0,2 @@

import type { Fn, Fn2, IObjectOf, NumericArray, TypedArray, UintType } from "@thi.ng/api";
import type { FloatArray, Fn, Fn2, Fn3, FnN, IObjectOf, NumericArray, TypedArray, UintType } from "@thi.ng/api";
/**

@@ -299,2 +299,92 @@ * ABGR 8bit lane/channel IDs

}
export declare type PoolTemplate = Fn3<string[], number, number, string>;
export interface ConvolutionKernelSpec {
/**
* Kernel coefficients.
*/
spec: NumericArray;
/**
* Kernel size. If given as number, expands to `[size, size]`.
*/
size: number | [number, number];
}
export interface PoolKernelSpec {
/**
* Code template function for {@link defPoolKernel}.
*/
pool: PoolTemplate;
/**
* Kernel size. If given as number, expands to `[size, size]`.
*/
size: number | [number, number];
}
export interface KernelFnSpec {
/**
* Kernel factory.
*/
fn: Fn<IPixelBuffer<FloatArray, NumericArray>, FnN>;
/**
* Kernel size. If given as number, expands to `[size, size]`.
*/
size: number | [number, number];
}
export declare type KernelSpec = ConvolutionKernelSpec | PoolKernelSpec | KernelFnSpec;
export interface ConvolveOpts {
/**
* Convolution kernel details/implementation.
*/
kernel: KernelSpec;
/**
* Channel ID to convolve.
*
* @defaultValue 0
*/
channel?: number;
/**
* If true, the result image will be same size as source image with empty
* (padded) border pixels.
*
* @defaultValue true
*/
pad?: boolean;
/**
* Result scale factor
*
* @defaultValue 1
*/
scale?: number;
/**
* Step size to process only every nth pixel.
*
* @defaultValue 1
*/
stride?: number | [number, number];
}
export interface NormalMapOpts {
/**
* Channel ID to use for gradient extraction in source image.
*
* @defaultValue 0
*/
channel: number;
/**
* Step size (aka number of pixels) between left/right, top/bottom
* neighbors.
*
* @defaultValue 0
*/
step: number;
/**
* Result gradient scale factor(s).
*
* @defaultValue 1
*/
scale: number | [number, number];
/**
* Z-axis value to use in blue channel of normal map.
*
* @defaultValue 1
*/
z: number;
}
//# sourceMappingURL=api.d.ts.map

@@ -6,2 +6,38 @@ # Change Log

## [0.7.1](https://github.com/thi-ng/umbrella/compare/@thi.ng/pixel@0.7.0...@thi.ng/pixel@0.7.1) (2021-03-03)
**Note:** Version bump only for package @thi.ng/pixel
# [0.7.0](https://github.com/thi-ng/umbrella/compare/@thi.ng/pixel@0.6.1...@thi.ng/pixel@0.7.0) (2021-03-03)
### Bug Fixes
* **pixel:** add clamping for float->ABGR conversion ([41540e0](https://github.com/thi-ng/umbrella/commit/41540e085b2261208e44e6f25b327e3371eae2df))
* **pixel:** fix POOL_NEAREST index ([b98d05d](https://github.com/thi-ng/umbrella/commit/b98d05d7827d98d3971bdbcd562735b96fa9b7ec))
### Features
* **pixel:** add 5x5 kernel presets ([56f96f4](https://github.com/thi-ng/umbrella/commit/56f96f4842e6a57087a565a8e5ce82e590da7d66))
* **pixel:** add convolve() & preset kernels ([6a31dc3](https://github.com/thi-ng/umbrella/commit/6a31dc38f3f0ae48853d899420d0fbebcc6b8678))
* **pixel:** add defKernel() kernel fn codegen ([25b97a3](https://github.com/thi-ng/umbrella/commit/25b97a341fa54ee8a82e3083fcb85a8061db8b1f))
* **pixel:** add defLargeKernel(), conv presets ([9c71165](https://github.com/thi-ng/umbrella/commit/9c71165adb71103fa88a5486987f270fecd2f439))
* **pixel:** add gradientImage() & FLOAT_NORMAL format ([78683b7](https://github.com/thi-ng/umbrella/commit/78683b701418bf184b2a1327cfd5e50397d687e0))
* **pixel:** add IEmpty impls for Float/PackedBuffer ([46ac1a1](https://github.com/thi-ng/umbrella/commit/46ac1a1906b256eefab0934efea300c67db7ea28))
* **pixel:** add normalMap(), add more kernels ([f32686d](https://github.com/thi-ng/umbrella/commit/f32686d463ffcb49b37e9b1b811ff5de06b58fed))
* **pixel:** add POOL_THRESHOLD preset ([5f1c1de](https://github.com/thi-ng/umbrella/commit/5f1c1dea87251f8a584cbe94d83784e7e4cc31a5))
* **pixel:** add step size support for normalMap() ([ab72a79](https://github.com/thi-ng/umbrella/commit/ab72a79532baab3f07f53419cb5970e90e97e0dd))
* **pixel:** add/update buffer factory fns ([ba38e13](https://github.com/thi-ng/umbrella/commit/ba38e137c6913d048bb4d678137241ee179d160c))
* **pixel:** update PackedBuffer.fromCanvas() ([3bdb086](https://github.com/thi-ng/umbrella/commit/3bdb0860bcd35a0475e83ebe948847f1ecd42db6))
* **pixel:** update/extend/refactor convolveChannel/Image() ([6692865](https://github.com/thi-ng/umbrella/commit/6692865d5facb75bf667056afa9cfee93ade2da6))
## [0.6.1](https://github.com/thi-ng/umbrella/compare/@thi.ng/pixel@0.6.0...@thi.ng/pixel@0.6.1) (2021-02-20)

@@ -8,0 +44,0 @@

8

float.d.ts

@@ -1,2 +0,2 @@

import { Fn, NumericArray } from "@thi.ng/api";
import { Fn, ICopy, IEmpty, NumericArray } from "@thi.ng/api";
import type { BlendFnFloat, BlitOpts, FloatFormat, FloatFormatSpec, IPixelBuffer, PackedFormat } from "./api";

@@ -12,4 +12,5 @@ import { PackedBuffer } from "./packed";

*/
export declare const floatBuffer: (w: number, h: number, fmt: FloatFormat | FloatFormatSpec, pixels?: Float32Array | undefined) => FloatBuffer;
export declare class FloatBuffer implements IPixelBuffer<Float32Array, NumericArray> {
export declare function floatBuffer(w: number, h: number, fmt: FloatFormat | FloatFormatSpec, pixels?: Float32Array): FloatBuffer;
export declare function floatBuffer(src: PackedBuffer, fmt: FloatFormat | FloatFormatSpec): FloatBuffer;
export declare class FloatBuffer implements IPixelBuffer<Float32Array, NumericArray>, ICopy<FloatBuffer>, IEmpty<FloatBuffer> {
/**

@@ -36,2 +37,3 @@ * Creates a new `FloatBuffer` from given {@link PackedBuffer} and using

copy(): FloatBuffer;
empty(): FloatBuffer;
getAt(x: number, y: number): NumericArray;

@@ -38,0 +40,0 @@ setAt(x: number, y: number, col: NumericArray): this;

@@ -8,11 +8,9 @@ import { assert } from "@thi.ng/api";

import { clampRegion, ensureChannel, ensureSize, prepRegions } from "./utils";
/**
* Syntax sugar for {@link FloatBuffer} ctor.
*
* @param w -
* @param h -
* @param fmt -
* @param pixels -
*/
export const floatBuffer = (w, h, fmt, pixels) => new FloatBuffer(w, h, fmt, pixels);
export function floatBuffer(...args) {
return args[0] instanceof PackedBuffer
? // @ts-ignore
FloatBuffer.fromPacked(...args)
: // @ts-ignore
new FloatBuffer(...args);
}
export class FloatBuffer {

@@ -59,6 +57,9 @@ constructor(w, h, fmt, pixels) {

copy() {
const dest = new FloatBuffer(this.width, this.height, this.format);
const dest = this.empty();
dest.pixels.set(this.pixels);
return dest;
}
empty() {
return new FloatBuffer(this.width, this.height, this.format);
}
getAt(x, y) {

@@ -65,0 +66,0 @@ const { width, stride } = this;

@@ -0,1 +1,2 @@

import { clamp01 } from "@thi.ng/math";
import { Lane } from "../api";

@@ -11,3 +12,3 @@ import { luminanceABGR } from "../utils";

}
const to = (col, i) => ((col[i] * 0xff + 0.5) | 0) << chanShift[chan[i]];
const to = (col, i) => ((clamp01(col[i]) * 0xff + 0.5) | 0) << chanShift[chan[i]];
const from = (col, i) => ((col >>> chanShift[chan[i]]) & 0xff) / 0xff;

@@ -42,3 +43,3 @@ switch (chan.length) {

const defConvert1Gray = (res) => {
res.toABGR = (col) => ((((col[0] * 0xff + 0.5) | 0) * 0x010101) | 0xff000000) >>> 0;
res.toABGR = (col) => ((((clamp01(col[0]) * 0xff + 0.5) | 0) * 0x010101) | 0xff000000) >>> 0;
res.fromABGR = (col, out = []) => ((out[0] = luminanceABGR(col) / 0xff), out);

@@ -63,3 +64,3 @@ };

res.toABGR = (col) => {
let out = ((col[gray] * 0xff + 0.5) | 0) * 0x010101;
let out = ((clamp01(col[gray]) * 0xff + 0.5) | 0) * 0x010101;
out |= ((col[alpha] * 0xff + 0.5) | 0) << 24;

@@ -66,0 +67,0 @@ return out >>> 0;

export * from "./api";
export * from "./canvas";
export * from "./codegen";
export * from "./convolve";
export * from "./dither";
export * from "./float";
export * from "./normal-map";
export * from "./packed";

@@ -25,4 +27,5 @@ export * from "./utils";

export * from "./format/float-hsva";
export * from "./format/float-norm";
export * from "./format/float-rgb";
export * from "./format/float-rgba";
//# sourceMappingURL=index.d.ts.map
export * from "./api";
export * from "./canvas";
export * from "./codegen";
export * from "./convolve";
export * from "./dither";
export * from "./float";
export * from "./normal-map";
export * from "./packed";

@@ -25,3 +27,4 @@ export * from "./utils";

export * from "./format/float-hsva";
export * from "./format/float-norm";
export * from "./format/float-rgb";
export * from "./format/float-rgba";

@@ -126,2 +126,8 @@ 'use strict';

};
const asVec = (x) => checks.isNumber(x) ? [x, x] : x;
const asIntVec = (x) => {
const v = asVec(x);
return [v[0] | 0, v[1] | 0];
};
const range = (n) => new Uint8Array(n).map((_, i) => i);

@@ -178,39 +184,2 @@ const compileLShift = (x, shift) => shift > 0

const init = (x, y, size, val, step, mat) => {
if (size === 1) {
!mat[y] && (mat[y] = []);
mat[y][x] = val;
return mat;
}
size >>= 1;
const step4 = step << 2;
init(x, y, size, val, step4, mat);
init(x + size, y + size, size, val + step, step4, mat);
init(x + size, y, size, val + step * 2, step4, mat);
init(x, y + size, size, val + step * 3, step4, mat);
return mat;
};
const defBayer = (size) => ({
mat: init(0, 0, size, 0, 1, []),
invSize: 1 / (size * size),
mask: size - 1,
});
const orderedDither = ({ mat, mask, invSize }, dsteps, drange, srange, x, y, val) => {
val =
(dsteps * (val / srange) + mat[y & mask][x & mask] * invSize - 0.5) | 0;
dsteps--;
return math.clamp(val, 0, dsteps) * ((drange - 1) / dsteps);
};
const ditherPixels = (dest, src, width, height, mat, dsteps, drange, srange) => {
!dest && (dest = src.slice());
drange--;
for (let y = 0, i = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
dest[i] = orderedDither(mat, dsteps, drange, srange, x, y, src[i]);
i++;
}
}
return dest;
};
const defFloatFormat = (fmt) => {

@@ -224,3 +193,3 @@ const chan = fmt.channels;

}
const to = (col, i) => ((col[i] * 0xff + 0.5) | 0) << chanShift[chan[i]];
const to = (col, i) => ((math.clamp01(col[i]) * 0xff + 0.5) | 0) << chanShift[chan[i]];
const from = (col, i) => ((col >>> chanShift[chan[i]]) & 0xff) / 0xff;

@@ -255,3 +224,3 @@ switch (chan.length) {

const defConvert1Gray = (res) => {
res.toABGR = (col) => ((((col[0] * 0xff + 0.5) | 0) * 0x010101) | 0xff000000) >>> 0;
res.toABGR = (col) => ((((math.clamp01(col[0]) * 0xff + 0.5) | 0) * 0x010101) | 0xff000000) >>> 0;
res.fromABGR = (col, out = []) => ((out[0] = luminanceABGR(col) / 0xff), out);

@@ -276,3 +245,3 @@ };

res.toABGR = (col) => {
let out = ((col[gray] * 0xff + 0.5) | 0) * 0x010101;
let out = ((math.clamp01(col[gray]) * 0xff + 0.5) | 0) * 0x010101;
out |= ((col[alpha] * 0xff + 0.5) | 0) << 24;

@@ -325,2 +294,39 @@ return out >>> 0;

const init = (x, y, size, val, step, mat) => {
if (size === 1) {
!mat[y] && (mat[y] = []);
mat[y][x] = val;
return mat;
}
size >>= 1;
const step4 = step << 2;
init(x, y, size, val, step4, mat);
init(x + size, y + size, size, val + step, step4, mat);
init(x + size, y, size, val + step * 2, step4, mat);
init(x, y + size, size, val + step * 3, step4, mat);
return mat;
};
const defBayer = (size) => ({
mat: init(0, 0, size, 0, 1, []),
invSize: 1 / (size * size),
mask: size - 1,
});
const orderedDither = ({ mat, mask, invSize }, dsteps, drange, srange, x, y, val) => {
val =
(dsteps * (val / srange) + mat[y & mask][x & mask] * invSize - 0.5) | 0;
dsteps--;
return math.clamp(val, 0, dsteps) * ((drange - 1) / dsteps);
};
const ditherPixels = (dest, src, width, height, mat, dsteps, drange, srange) => {
!dest && (dest = src.slice());
drange--;
for (let y = 0, i = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
dest[i] = orderedDither(mat, dsteps, drange, srange, x, y, src[i]);
i++;
}
}
return dest;
};
const defChannel = (ch, idx, shift) => {

@@ -380,3 +386,8 @@ const num = 1 << ch.size;

const packedBuffer = (w, h, fmt, pixels) => new PackedBuffer(w, h, fmt, pixels);
function packedBuffer(...args) {
return args[0] instanceof PackedBuffer
? args[0].as(args[1])
:
new PackedBuffer(...args);
}
const buffer = packedBuffer;

@@ -393,16 +404,22 @@ class PackedBuffer {

static fromImage(img, fmt, width, height = width) {
const ctx = imageCanvas(img, width, height);
const w = ctx.canvas.width;
const h = ctx.canvas.height;
const src = new Uint32Array(ctx.ctx.getImageData(0, 0, w, h).data.buffer);
const dest = api.typedArray(fmt.type, w * h);
const from = fmt.fromABGR;
for (let i = dest.length; --i >= 0;) {
dest[i] = from(src[i]);
return PackedBuffer.fromCanvas(imageCanvas(img, width, height).canvas, fmt);
}
static fromCanvas(canvas, fmt = ABGR8888) {
const ctx = canvasPixels(canvas);
const w = canvas.width;
const h = canvas.height;
let dest;
if (fmt === ABGR8888) {
dest = ctx.pixels;
}
else {
dest = api.typedArray(fmt.type, w * h);
const src = ctx.pixels;
const from = fmt.fromABGR;
for (let i = dest.length; --i >= 0;) {
dest[i] = from(src[i]);
}
}
return new PackedBuffer(w, h, fmt, dest);
}
static fromCanvas(canvas) {
return new PackedBuffer(canvas.width, canvas.height, ABGR8888, canvasPixels(canvas).pixels);
}
get stride() {

@@ -415,6 +432,9 @@ return 1;

copy() {
const dest = new PackedBuffer(this.width, this.height, this.format);
const dest = this.empty();
dest.pixels.set(this.pixels);
return dest;
}
empty() {
return new PackedBuffer(this.width, this.height, this.format);
}
getAt(x, y) {

@@ -621,3 +641,9 @@ return x >= 0 && x < this.width && y >= 0 && y < this.height

const floatBuffer = (w, h, fmt, pixels) => new FloatBuffer(w, h, fmt, pixels);
function floatBuffer(...args) {
return args[0] instanceof PackedBuffer
?
FloatBuffer.fromPacked(...args)
:
new FloatBuffer(...args);
}
class FloatBuffer {

@@ -654,6 +680,9 @@ constructor(w, h, fmt, pixels) {

copy() {
const dest = new FloatBuffer(this.width, this.height, this.format);
const dest = this.empty();
dest.pixels.set(this.pixels);
return dest;
}
empty() {
return new FloatBuffer(this.width, this.height, this.format);
}
getAt(x, y) {

@@ -818,2 +847,228 @@ const { width, stride } = this;

const convolveChannel = (src, opts) => convolve(initConvolve(src, opts));
const convolveImage = (src, opts) => {
const state = initConvolve(src, opts);
const dest = new FloatBuffer(state.dwidth, state.dheight, src.format);
for (let channel of opts.channels || range(src.format.channels.length)) {
dest.setChannel(channel, convolve(Object.assign(Object.assign({}, state), { channel })));
}
return dest;
};
const convolve = ({ channel, dest, dwidth, kernel, kh2, kw2, pad, rowStride, scale, src, srcStride, strideX, strideY, }) => {
ensureChannel(src.format, channel);
const maxY = src.height - kh2;
const maxX = src.width - kw2;
const padX = pad && strideX === 1;
const padY = pad && strideY === 1;
const dpix = dest.pixels;
for (let sy = kh2, dy = padY ? kh2 : 0; sy < maxY; sy += strideY, dy++) {
for (let x = kw2, i = sy * rowStride + x * srcStride + channel, j = dy * dwidth + (padX ? x : 0); x < maxX; x += strideX, i += strideX * srcStride, j++) {
dpix[j] = kernel(i) * scale;
}
}
return dest;
};
const initKernel = (src, kernel, kw, kh) => (checks.isFunction(kernel.fn)
? kernel.fn
: defKernel(kernel.spec ||
kernel.pool, kw, kh))(src);
const initConvolve = (src, opts) => {
const { kernel, channel, stride: sampleStride, pad, scale } = Object.assign({ channel: 0, pad: true, scale: 1, stride: 1 }, opts);
const size = kernel.size;
const [kw, kh] = asIntVec(size);
const [strideX, strideY] = asIntVec(sampleStride);
api.assert(strideX >= 1 && strideY >= 1, `illegal stride: ${sampleStride}`);
const { width, height, stride: srcStride, rowStride } = src;
api.assert(kw >= 0 && kw <= width && kh >= 0 && kh <= height, `invalid kernel size: ${size}`);
const dwidth = destSize(width, strideX, kw, pad);
const dheight = destSize(height, strideY, kh, pad);
const dest = new FloatBuffer(dwidth, dheight, FLOAT_GRAY);
return {
channel,
dest,
dheight,
dwidth,
kernel: initKernel(src, kernel, kw, kh),
kh2: kh >> 1,
kw2: kw >> 1,
pad,
rowStride,
scale,
src,
srcStride,
strideX,
strideY,
};
};
const destSize = (orig, res, k, pad) => pad ? Math.floor(orig / res) : Math.ceil((orig - k + 1) / res);
const defKernel = (tpl, w, h) => {
if (w * h > 1024 && !checks.isFunction(tpl))
return defLargeKernel(tpl, w, h);
const isPool = checks.isFunction(tpl);
const prefix = [];
const prefixI = [];
const body = [];
const kvars = [];
const h2 = h >> 1;
const w2 = w >> 1;
for (let y = 0, i = 0; y < h; y++) {
const yy = y - h2;
const row = [];
for (let x = 0; x < w; x++, i++) {
const kv = `k${y}_${x}`;
kvars.push(kv);
const xx = x - w2;
const idx = (yy !== 0 ? `i${y}` : `i`) + (xx !== 0 ? `+x${x}` : "");
isPool
? row.push(`pix[${idx}]`)
: tpl[i] !== 0 && row.push(`${kv}*pix[${idx}]`);
y === 0 && xx !== 0 && prefix.push(`const x${x} = ${xx} * stride;`);
}
row.length && body.push(...row);
if (yy !== 0) {
prefix.push(`const y${y} = ${yy} * rowStride;`);
prefixI.push(`const i${y} = i + y${y};`);
}
}
const decls = isPool
? ""
: `const [${kvars.join(", ")}] = [${tpl.join(", ")}];`;
const inner = isPool ? tpl(body, w, h) : body.join(" + ");
const fnBody = [
decls,
"const { pixels: pix, stride, rowStride } = src;",
...prefix,
"return (i) => {",
...prefixI,
`return ${inner};`,
"}",
].join("\n");
return new Function("src", fnBody);
};
const defLargeKernel = (kernel, w, h) => {
const h2 = h >> 1;
const w2 = w >> 1;
return (src) => {
const { pixels, rowStride, stride } = src;
return (i) => {
let sum = 0;
for (let y = -h2, k = 0; y <= h2; y++) {
for (let x = -w2, ii = i + y * rowStride; x <= w2; x++, ii += stride, k++) {
sum += kernel[k] * pixels[ii];
}
}
return sum;
};
};
};
const POOL_NEAREST = (body, w, h) => body[(h >> 1) * w + (w >> 1)];
const POOL_MEAN = (body, w, h) => `(${body.join("+")})*${1 / (w * h)}`;
const POOL_MIN = (body) => `Math.min(${body.join(",")})`;
const POOL_MAX = (body) => `Math.max(${body.join(",")})`;
const POOL_THRESHOLD = (bias = 0) => (body, w, h) => {
const center = POOL_NEAREST(body, w, h);
const mean = `(${body.join("+")})/${w * h}`;
return `(${center} - ${mean} + ${bias}) < 0 ? 0 : 1`;
};
const SOBEL_X = {
spec: [-1, -2, -1, 0, 0, 0, 1, 2, 1],
size: 3,
};
const SOBEL_Y = {
spec: [-1, 0, 1, -2, 0, 2, -1, 0, 1],
size: 3,
};
const SHARPEN3 = {
spec: [0, -1, 0, -1, 5, -1, 0, -1, 0],
size: 3,
};
const HIGHPASS3 = {
spec: [-1, -1, -1, -1, 9, -1, -1, -1, -1],
size: 3,
};
const BOX_BLUR3 = {
pool: POOL_MEAN,
size: 3,
};
const BOX_BLUR5 = {
pool: POOL_MEAN,
size: 5,
};
const GAUSSIAN_BLUR3 = {
spec: [1 / 16, 1 / 8, 1 / 16, 1 / 8, 1 / 4, 1 / 8, 1 / 16, 1 / 8, 1 / 16],
size: 3,
};
const GAUSSIAN_BLUR5 = {
spec: [
1 / 256, 1 / 64, 3 / 128, 1 / 64, 1 / 256,
1 / 64, 1 / 16, 3 / 32, 1 / 16, 1 / 64,
3 / 128, 3 / 32, 9 / 64, 3 / 32, 3 / 128,
1 / 64, 1 / 16, 3 / 32, 1 / 16, 1 / 64,
1 / 256, 1 / 64, 3 / 128, 1 / 64, 1 / 256,
],
size: 5,
};
const GAUSSIAN = (r) => {
r |= 0;
api.assert(r > 0, `invalid kernel radius: ${r}`);
const sigma = -1 / (2 * (Math.hypot(r, r) / 3) ** 2);
const res = [];
let sum = 0;
for (let y = -r; y <= r; y++) {
for (let x = -r; x <= r; x++) {
const g = Math.exp((x * x + y * y) * sigma);
res.push(g);
sum += g;
}
}
return { spec: res.map((x) => x / sum), size: r * 2 + 1 };
};
const UNSHARP_MASK5 = {
spec: [
-1 / 256, -1 / 64, -3 / 128, -1 / 64, -1 / 256,
-1 / 64, -1 / 16, -3 / 32, -1 / 16, -1 / 64,
-3 / 128, -3 / 32, 119 / 64, -3 / 32, -3 / 128,
-1 / 64, -1 / 16, -3 / 32, -1 / 16, -1 / 64,
-1 / 256, -1 / 64, -3 / 128, -1 / 64, -1 / 256,
],
size: 5,
};
const from = (x) => x / 127.5 - 1;
const to = (x, shift) => math.clamp(x * 127.5 + 128, 0, 255) << shift;
const FLOAT_NORMAL = {
__float: true,
alpha: false,
gray: false,
channels: [exports.Lane.RED, exports.Lane.GREEN, exports.Lane.BLUE],
shift: { 3: 0, 2: 8, 1: 16 },
size: 3,
fromABGR: (src) => [
from(src & 0xff),
from((src >> 8) & 0xff),
from((src >> 16) & 0xff),
],
toABGR: (src) => to(src[0], 0) | to(src[1], 8) | to(src[2], 16) | 0xff000000,
};
const normalMap = (src, opts) => {
const { channel, step, scale, z } = Object.assign({ channel: 0, step: 0, scale: 1, z: 1 }, opts);
ensureChannel(src.format, channel);
const spec = [-1, ...new Array(step).fill(0), 1];
const [sx, sy] = asVec(scale);
const dest = new FloatBuffer(src.width, src.height, FLOAT_NORMAL);
dest.setChannel(0, convolveChannel(src, {
kernel: { spec, size: [step + 2, 1] },
scale: sx,
channel,
}));
dest.setChannel(1, convolveChannel(src, {
kernel: { spec, size: [1, step + 2] },
scale: sy,
channel,
}));
dest.setChannel(2, z);
return dest;
};
const ALPHA8 = defPackedFormat({

@@ -1015,8 +1270,14 @@ type: "u8",

exports.BGR888 = BGR888;
exports.BOX_BLUR3 = BOX_BLUR3;
exports.BOX_BLUR5 = BOX_BLUR5;
exports.FLOAT_GRAY = FLOAT_GRAY;
exports.FLOAT_GRAY_ALPHA = FLOAT_GRAY_ALPHA;
exports.FLOAT_HSVA = FLOAT_HSVA;
exports.FLOAT_NORMAL = FLOAT_NORMAL;
exports.FLOAT_RGB = FLOAT_RGB;
exports.FLOAT_RGBA = FLOAT_RGBA;
exports.FloatBuffer = FloatBuffer;
exports.GAUSSIAN = GAUSSIAN;
exports.GAUSSIAN_BLUR3 = GAUSSIAN_BLUR3;
exports.GAUSSIAN_BLUR5 = GAUSSIAN_BLUR5;
exports.GRAY16 = GRAY16;

@@ -1026,5 +1287,17 @@ exports.GRAY8 = GRAY8;

exports.GRAY_ALPHA8 = GRAY_ALPHA8;
exports.HIGHPASS3 = HIGHPASS3;
exports.POOL_MAX = POOL_MAX;
exports.POOL_MEAN = POOL_MEAN;
exports.POOL_MIN = POOL_MIN;
exports.POOL_NEAREST = POOL_NEAREST;
exports.POOL_THRESHOLD = POOL_THRESHOLD;
exports.PackedBuffer = PackedBuffer;
exports.RGB565 = RGB565;
exports.RGB888 = RGB888;
exports.SHARPEN3 = SHARPEN3;
exports.SOBEL_X = SOBEL_X;
exports.SOBEL_Y = SOBEL_Y;
exports.UNSHARP_MASK5 = UNSHARP_MASK5;
exports.asIntVec = asIntVec;
exports.asVec = asVec;
exports.buffer = buffer;

@@ -1038,4 +1311,8 @@ exports.canvas2d = canvas2d;

exports.compileToABGR = compileToABGR;
exports.convolveChannel = convolveChannel;
exports.convolveImage = convolveImage;
exports.defBayer = defBayer;
exports.defFloatFormat = defFloatFormat;
exports.defKernel = defKernel;
exports.defLargeKernel = defLargeKernel;
exports.defPackedFormat = defPackedFormat;

@@ -1049,5 +1326,7 @@ exports.ditherPixels = ditherPixels;

exports.luminanceABGR = luminanceABGR;
exports.normalMap = normalMap;
exports.orderedDither = orderedDither;
exports.packedBuffer = packedBuffer;
exports.prepRegions = prepRegions;
exports.range = range;
exports.setChannelConvert = setChannelConvert;

@@ -1054,0 +1333,0 @@ exports.setChannelSame = setChannelSame;

@@ -1,1 +0,1 @@

!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("@thi.ng/checks"),require("@thi.ng/api"),require("@thi.ng/math"),require("@thi.ng/porter-duff"),require("@thi.ng/binary")):"function"==typeof define&&define.amd?define(["exports","@thi.ng/checks","@thi.ng/api","@thi.ng/math","@thi.ng/porter-duff","@thi.ng/binary"],e):e(((t="undefined"!=typeof globalThis?globalThis:t||self).thi=t.thi||{},t.thi.ng=t.thi.ng||{},t.thi.ng.pixel={}),t.thi.ng.checks,t.thi.ng.api,t.thi.ng.math,t.thi.ng.porterDuff,t.thi.ng.binary)}(this,(function(t,e,s,i,n,r){"use strict";var a,h,l;t.Lane=void 0,(a=t.Lane||(t.Lane={}))[a.ALPHA=0]="ALPHA",a[a.BLUE=1]="BLUE",a[a.GREEN=2]="GREEN",a[a.RED=3]="RED",t.Wrap=void 0,(h=t.Wrap||(t.Wrap={}))[h.NONE=0]="NONE",h[h.U=1]="U",h[h.V=2]="V",h[h.UV=3]="UV",t.Filter=void 0,(l=t.Filter||(t.Filter={}))[l.NEAREST=0]="NEAREST",l[l.LINEAR=1]="LINEAR";const o=(t,e=t,s)=>{const i=document.createElement("canvas");return i.width=t,i.height=e,s&&s.appendChild(i),{canvas:i,ctx:i.getContext("2d")}};function c(t,s){let i,n;if(e.isNumber(t)){const e=o(t,s);i=e.canvas,n=e.ctx}else i=t,n=i.getContext("2d");const r=n.getImageData(0,0,i.width,i.height);return{canvas:i,ctx:n,img:r,pixels:new Uint32Array(r.data.buffer)}}const f=(t,s,i=s,n)=>{const r=e.isNumber(s)?o(s,i,n):o(t.width,t.height,n);return r.ctx.drawImage(t,0,0,r.canvas.width,r.canvas.height),r},p=(t,e,i,n=1)=>s.assert(t.length>=e*i*n,"pixel buffer too small"),u=(t,e)=>{const i=t.channels[e];return s.assert(null!=i,`invalid channel ID: ${e}`),i},m=t=>(29*(t>>>16&255)+150*(t>>>8&255)+76*(255&t))/255,d=(t,e,s,n,r,a,h=0,l=0)=>(s|=0,n|=0,(t|=0)<0&&(s+=t,h-=t,t=0),(e|=0)<0&&(n+=e,l-=e,e=0),[t,e,i.clamp(s,0,r-t),i.clamp(n,0,a-e),h,l]),A=(t,e,s={})=>{let i,n,r,a,h,l,o=t.width,c=e.width;return[i,n,h,l]=d(s.sx||0,s.sy||0,s.w||o,s.h||t.height,o,t.height),[r,a,h,l,i,n]=d(s.dx||0,s.dy||0,h,l,c,e.height,i,n),{sx:i,sy:n,dx:r,dy:a,rw:h,rh:l}},g=(t,e,s)=>{for(let i=t.length;--i>=0;)t[i]=s(t[i],e)},R=(t,e,s,i)=>{for(let n=t.length;--n>=0;)t[n]=i(t[n],s(e[n]))},w=(t,e,s,i,n)=>{const r=~n;for(let a=t.length;--a>=0;)t[a]=t[a]&r|s(i(e[a]))&n},x=(t,e,s)=>{const i=e.fromABGR,n=e.toABGR;for(let e=t.length;--e>=0;)t[e]=i(s(n(t[e])))},L=(t,e)=>e>0?`(${t} << ${e})`:e<0?`(${t} >>> ${-e})`:`${t}`,G=(t,e)=>L(t,-e),y=t=>`0x${t.toString(16)}`,B=t=>{const e=(1<<t)-1;return new Function("luma",`return (x) => ${G("luma(x)",8-t)} & ${e};`)(m)},z=t=>{let e;if(8!==t){const s=(1<<t)-1;e=`(((x & ${s}) * ${255/s}) | 0)`}else e="x";return new Function("x",`return 0xff000000 | (${e} * 0x010101);`)},E=t=>new Function("x","return ("+t.map((t=>{const e=t.abgrShift+(8-t.size);return`(${G("x",e)} & ${y(t.maskA)})`})).join(" | ")+") >>> 0;"),b=(t,e)=>{const s=t.map((t=>{if(8!==t.size){const e=t.mask0,s=255/e,i=G("x",t.shift);return L(`((${i} & ${e}) * ${s})`,24-8*t.lane)}return L(`(x & ${y(t.maskA)})`,t.abgrShift)})).join(" | ");return new Function("x",`return (${e?"":"0xff000000 | "}${s}) >>> 0;`)},v=(t,e,s,i,n,r)=>{if(1===s)return!r[e]&&(r[e]=[]),r[e][t]=i,r;const a=n<<2;return v(t,e,s>>=1,i,a,r),v(t+s,e+s,s,i+n,a,r),v(t+s,e,s,i+2*n,a,r),v(t,e+s,s,i+3*n,a,r),r},D=t=>({mat:v(0,0,t,0,1,[]),invSize:1/(t*t),mask:t-1}),F=({mat:t,mask:e,invSize:s},n,r,a,h,l,o)=>(o=n*(o/a)+t[l&e][h&e]*s-.5|0,n--,i.clamp(o,0,n)*((r-1)/n)),P=t=>{const e=t.channels,s=e.reduce(((t,e)=>(t[e]=3-e<<3,t)),{}),i=Object.assign(Object.assign({},t),{size:e.length,shift:s,__float:!0});if(t.convert)return Object.assign(i,t.convert),i;const n=(t,i)=>(255*t[i]+.5|0)<<s[e[i]],r=(t,i)=>(t>>>s[e[i]]&255)/255;switch(e.length){case 1:t.gray?C(i):N(i,r,n);break;case 2:t.gray?_(i,r):S(i,r,n);break;case 3:U(i,r,n);break;case 4:k(i,r,n)}return i},N=(t,e,s)=>{t.toABGR=e=>{let i=t.alpha?0:4278190080;return i|=s(e,0),i>>>0},t.fromABGR=(t,s=[])=>(s[0]=e(t,0),s)},C=t=>{t.toABGR=t=>(65793*(255*t[0]+.5|0)|4278190080)>>>0,t.fromABGR=(t,e=[])=>(e[0]=m(t)/255,e)},S=(t,e,s)=>{t.toABGR=e=>{let i=t.alpha?0:4278190080;return i|=s(e,0),i|=s(e,1),i>>>0},t.fromABGR=(t,s=[])=>(s[0]=e(t,0),s[1]=e(t,1),s)},_=(e,s)=>{const i=~~(e.channels[0]===t.Lane.ALPHA),n=1^i;e.toABGR=t=>{let e=65793*(255*t[i]+.5|0);return e|=(255*t[n]+.5|0)<<24,e>>>0},e.fromABGR=(t,e=[])=>(e[i]=m(t)/255,e[n]=s(t,n),e)},U=(t,e,s)=>{t.toABGR=e=>{let i=t.alpha?0:4278190080;return i|=s(e,0),i|=s(e,1),i|=s(e,2),i>>>0},t.fromABGR=(t,s=[])=>(s[0]=e(t,0),s[1]=e(t,1),s[2]=e(t,2),s)},k=(t,e,s)=>{t.toABGR=e=>{let i=t.alpha?0:4278190080;return i|=s(e,0),i|=s(e,1),i|=s(e,2),i|=s(e,3),i>>>0},t.fromABGR=(t,s=[])=>(s[0]=e(t,0),s[1]=e(t,1),s[2]=e(t,2),s[3]=e(t,3),s)},$=P({gray:!0,channels:[t.Lane.RED]}),I=t=>{s.assert(t.channels.length>0,"no channel specs given");const e=t.channels.reduce((([t,e],s,n)=>(e-=s.size,t.push(((t,e,s)=>{const n=1<<t.size,r=n-1,a=r<<s>>>0,h=~a>>>0,l=null!=t.lane?t.lane:e,o=t=>t>>>s&r,c=(t,e)=>t&h|(e&r)<<s;return{size:t.size,abgrShift:24-8*l-s,lane:l,shift:s,mask0:r,maskA:a,int:o,setInt:c,float:t=>o(t)/r,setFloat:(t,e)=>c(t,i.clamp01(e)*r),dither:(t,e,s,i,r)=>F(t,e,n,n,s,i,r)}})(s,n,e)),[t,e])),[[],t.size])[0];return{__packed:!0,type:t.type,size:t.size,alpha:t.alpha||0,channels:e,fromABGR:t.fromABGR||E(e),toABGR:t.toABGR||b(e,!!t.alpha)}},H=I({type:"u32",size:32,alpha:8,channels:[{size:8,lane:t.Lane.ALPHA},{size:8,lane:t.Lane.BLUE},{size:8,lane:t.Lane.GREEN},{size:8,lane:t.Lane.RED}],fromABGR:t=>t,toABGR:t=>t}),O=(t,e,s,i)=>new j(t,e,s,i),T=O;class j{constructor(t,e,i,n){this.width=t,this.height=e,this.format=i.__packed?i:I(i),this.pixels=n||s.typedArray(i.type,t*e)}static fromImage(t,e,i,n=i){const r=f(t,i,n),a=r.canvas.width,h=r.canvas.height,l=new Uint32Array(r.ctx.getImageData(0,0,a,h).data.buffer),o=s.typedArray(e.type,a*h),c=e.fromABGR;for(let t=o.length;--t>=0;)o[t]=c(l[t]);return new j(a,h,e,o)}static fromCanvas(t){return new j(t.width,t.height,H,c(t).pixels)}get stride(){return 1}as(t){return this.getRegion(0,0,this.width,this.height,t)}copy(){const t=new j(this.width,this.height,this.format);return t.pixels.set(this.pixels),t}getAt(t,e){return t>=0&&t<this.width&&e>=0&&e<this.height?this.pixels[(0|t)+(0|e)*this.width]:0}setAt(t,e,s){return t>=0&&t<this.width&&e>=0&&e<this.height&&(this.pixels[(0|t)+(0|e)*this.width]=s),this}getChannelAt(t,e,s,i=!1){const n=u(this.format,s),r=this.getAt(t,e);return i?n.float(r):n.int(r)}setChannelAt(t,e,s,i,n=!1){const r=u(this.format,s),a=this.getAt(t,e);return n?r.setFloat(a,i):r.setInt(a,i),this}blend(t,e,s){let i=this.width,n=e.width;const{sx:r,sy:a,dx:h,dy:l,rw:o,rh:c}=A(this,e,s);if(o<1||c<1)return e;const f=this.pixels,p=e.pixels,u=this.format.toABGR,m=e.format.toABGR,d=e.format.fromABGR;for(let e=(0|r)+(0|a)*i,s=(0|h)+(0|l)*n,A=0;A<c;A++,e+=i,s+=n)for(let i=0;i<o;i++)p[s+i]=d(t(u(f[e+i]),m(p[s+i])));return e}blit(t,e){let s=this.width,i=t.width;const{sx:n,sy:r,dx:a,dy:h,rw:l,rh:o}=A(this,t,e);if(l<1||o<1)return t;const c=this.pixels,f=t.pixels,p=this.format.toABGR,u=t.format.fromABGR,m=this.format!==t.format?(t,e)=>{for(let s=0;s<l;s++)f[e+s]=u(p(c[t+s]))}:(t,e)=>f.set(c.subarray(t,t+l),e);for(let t=(0|n)+(0|r)*s,e=(0|a)+(0|h)*i,l=0;l<o;l++,t+=s,e+=i)m(t,e);return t}blitCanvas(t,e=0,s=0){const i=t.getContext("2d"),n=new ImageData(this.width,this.height),r=new Uint32Array(n.data.buffer),a=this.pixels,h=this.format.toABGR;for(let t=r.length;--t>=0;)r[t]=h(a[t]);return i.putImageData(n,e,s),t}getRegion(t,e,s,i,n){const[r,a,h,l]=d(t,e,s,i,this.width,this.height);return this.blit(new j(h,l,n||this.format),{sx:r,sy:a,w:h,h:l})}getChannel(e){const i=u(this.format,e),n=new j(this.width,this.height,{type:s.uintTypeForBits(i.size),size:i.size,channels:[{size:i.size,lane:t.Lane.RED}],fromABGR:B(i.size),toABGR:z(i.size)}),r=this.pixels,a=n.pixels,h=i.int;for(let t=r.length;--t>=0;)a[t]=h(r[t]);return n}setChannel(t,s){const i=u(this.format,t),n=this.pixels,r=i.setInt;if(e.isNumber(s))g(n,s,r);else{const t=s.pixels,e=s.format.channels[0];p(t,this.width,this.height),i.size===e.size?R(n,t,e.int,r):w(n,t,this.format.fromABGR,s.format.toABGR,i.maskA)}return this}invert(){const{format:t,pixels:e}=this,s=Math.pow(2,t.size-t.alpha)-1;for(let t=e.length;--t>=0;)e[t]^=s;return this}premultiply(){return x(this.pixels,this.format,n.premultiplyInt),this}postmultiply(){return x(this.pixels,this.format,n.postmultiplyInt),this}isPremultiplied(){const t=this.pixels,e=this.format.toABGR;for(let s=t.length;--s>=0;)if(!n.isPremultipliedInt(e(t[s])))return!1;return!0}forEach(t){const e=this.pixels;for(let s=e.length;--s>=0;)e[s]=t(e[s]);return this}dither(t,s){const{pixels:i,format:n,width:r}=this,a=e.isNumber(s)?new Array(n.channels.length).fill(s):s,h=e.isNumber(t)?D(t):t;for(let t=0,e=i.length,s=n.channels.length,l=0,o=0;t<e;t++){let e=i[t];for(let t=0;t<s;t++){const s=n.channels[t],i=a[t];i>0&&(e=s.setInt(e,s.dither(h,i,l,o,s.int(e))))}i[t]=e,++l===r&&(l=0,o++)}return this}flipY(){const{pixels:t,width:e}=this,i=s.typedArray(this.format.type,e);for(let s=0,n=t.length-e;s<n;s+=e,n-=e)i.set(t.subarray(s,s+e)),t.copyWithin(s,n,n+e),t.set(i,n);return this}downsample(t){t|=0;const{width:e,height:s,pixels:i}=this,n=new j(e/t|0,s/t|0,this.format),{width:r,height:a,pixels:h}=n;for(let s=0,n=0;s<a;s++)for(let a=0,l=s*t*e;a<r;a++,n++,l+=t)h[n]=i[l];return n}}class Y{constructor(t,e,s,i){this.width=t,this.height=e,this.format=s.__float?s:P(s),this.stride=s.channels.length,this.rowStride=t*this.stride,this.pixels=i||new Float32Array(t*e*this.stride),this.__empty=Object.freeze(new Array(this.stride).fill(0))}static fromPacked(t,e){const s=new Y(t.width,t.height,e),{pixels:i,format:n,stride:r}=s,{pixels:a,format:h}=t;for(let t=a.length;--t>=0;)i.set(n.fromABGR(h.toABGR(a[t])),t*r);return s}as(t){const{width:e,height:s,stride:i,pixels:n,format:r}=this,a=new j(e,s,t),h=a.pixels;for(let e=0,s=0,a=n.length;e<a;e+=i,s++)h[s]=t.fromABGR(r.toABGR(n.subarray(e,e+i)));return a}copy(){const t=new Y(this.width,this.height,this.format);return t.pixels.set(this.pixels),t}getAt(t,e){const{width:s,stride:i}=this;if(t>=0&&t<s&&e>=0&&e<this.height){const s=(0|t)*i+(0|e)*this.rowStride;return this.pixels.subarray(s,s+i)}return this.__empty}setAt(t,e,s){return t>=0&&t<this.width&&e>=0&&e<this.height&&this.pixels.set(s,(0|t)*this.stride+(0|e)*this.rowStride),this}getChannelAt(t,e,s){u(this.format,s);const{width:i,stride:n}=this;if(t>=0&&t<i&&e>=0&&e<this.height)return this.pixels[(0|t)*n+(0|e)*this.rowStride+s]}setChannelAt(t,e,s,i){u(this.format,s);const{width:n,stride:r}=this;return t>=0&&t<n&&e>=0&&e<this.height&&(this.pixels[(0|t)*r+(0|e)*this.rowStride+s]=i),this}getChannel(t){u(this.format,t);const{pixels:e,stride:s}=this,n=new Float32Array(this.width*this.height);for(let r=t,a=0,h=e.length;r<h;r+=s,a++)n[a]=i.clamp01(e[r]);return new Y(this.width,this.height,$,n)}setChannel(t,s){u(this.format,t);const{pixels:i,stride:n}=this;if(e.isNumber(s))for(let e=t,r=i.length;e<r;e+=n)i[e]=s;else{const{pixels:e,stride:r}=s;p(e,this.width,this.height,r);for(let s=t,a=0,h=i.length;s<h;s+=n,a+=r)i[s]=e[a]}return this}blend(t,e,s){this.ensureFormat(e);const{sx:i,sy:n,dx:r,dy:a,rw:h,rh:l}=A(this,e,s);if(h<1||l<1)return e;const o=this.pixels,c=e.pixels,f=this.rowStride,p=e.rowStride,u=this.stride;for(let e=(0|i)*u+(0|n)*f,s=(0|r)*u+(0|a)*p,m=0;m<l;m++,e+=f,s+=p)for(let i=h,n=e,r=s;--i>=0;n+=u,r+=u){const e=c.subarray(r,r+u);t(e,o.subarray(n,n+u),e)}return e}blit(t,e){this.ensureFormat(t);const{sx:s,sy:i,dx:n,dy:r,rw:a,rh:h}=A(this,t,e);if(a<1||h<1)return t;const l=this.pixels,o=t.pixels,c=this.rowStride,f=t.rowStride,p=a*this.stride;for(let t=(0|s)*this.stride+(0|i)*c,e=(0|n)*this.stride+(0|r)*f,a=0;a<h;a++,t+=c,e+=f)o.set(l.subarray(t,t+p),e);return t}blitCanvas(t,e=0,s=0){const i=t.getContext("2d"),n=new ImageData(this.width,this.height),r=new Uint32Array(n.data.buffer),{stride:a,pixels:h,format:l}=this;for(let t=0,e=0,s=h.length;t<s;t+=a,e++)r[e]=l.toABGR(h.subarray(t,t+a));return i.putImageData(n,e,s),t}getRegion(t,e,s,i){const[n,r,a,h]=d(t,e,s,i,this.width,this.height);return this.blit(new Y(a,h,this.format),{sx:n,sy:r,w:a,h})}forEach(t){const{pixels:e,stride:s}=this;for(let i=0,n=e.length;i<n;i+s)t(e.subarray(i,i+s));return this}clamp(){const t=this.pixels;for(let e=t.length;--e>=0;)t[e]=i.clamp01(t[e]);return this}clampChannel(t){u(this.format,t);const{pixels:e,stride:s}=this;for(let n=t,r=e.length;n<r;n+=s)e[n]=i.clamp01(e[n])}flipY(){const{pixels:t,rowStride:e}=this,s=new Float32Array(e);for(let i=0,n=t.length-e;i<n;i+=e,n-=e)s.set(t.subarray(i,i+e)),t.copyWithin(i,n,n+e),t.set(s,n);return this}downsample(t){t|=0;const{width:e,height:s,stride:i,pixels:n}=this,r=new Y(e/t|0,s/t|0,this.format),{width:a,height:h,pixels:l}=r;t*=i;for(let s=0,r=0;s<h;s++)for(let h=0,o=s*t*e;h<a;h++,r+=i,o+=t)l.set(n.subarray(o,o+i),r);return r}ensureFormat(t){s.assert(t.format===this.format,"dest buffer format must be same as src")}}const q=I({type:"u8",size:8,alpha:8,channels:[{size:8,lane:0}]}),V=I({type:"u16",size:16,alpha:1,channels:[{size:1,lane:t.Lane.ALPHA},{size:5,lane:t.Lane.RED},{size:5,lane:t.Lane.GREEN},{size:5,lane:t.Lane.BLUE}]}),W=I({type:"u16",size:16,alpha:4,channels:[{size:4,lane:t.Lane.ALPHA},{size:4,lane:t.Lane.RED},{size:4,lane:t.Lane.GREEN},{size:4,lane:t.Lane.BLUE}]}),M=I({type:"u32",size:32,alpha:8,channels:[{size:8,lane:t.Lane.ALPHA},{size:8,lane:t.Lane.RED},{size:8,lane:t.Lane.GREEN},{size:8,lane:t.Lane.BLUE}],fromABGR:r.swapLane13,toABGR:r.swapLane13}),J=I({type:"u32",size:24,channels:[{size:8,lane:t.Lane.BLUE},{size:8,lane:t.Lane.GREEN},{size:8,lane:t.Lane.RED}],fromABGR:t=>16777215&t,toABGR:t=>4278190080|t}),K=I({type:"u16",size:16,channels:[{size:16,lane:t.Lane.RED}],fromABGR:t=>257*(m(t)+.5|0),toABGR:t=>(4278190080|65793*(t>>>8))>>>0}),Q=I({type:"u32",size:32,channels:[{size:8,lane:t.Lane.ALPHA},{size:16,lane:t.Lane.RED}],fromABGR:t=>257*(m(t)+.5|0)|257*(t>>>8&16711680),toABGR:t=>(4278190080&t|65793*(t>>>8&255))>>>0}),X=I({type:"u8",size:8,channels:[{size:8,lane:t.Lane.RED}],fromABGR:t=>m(t),toABGR:t=>(4278190080|65793*(255&t))>>>0}),Z=I({type:"u16",size:16,alpha:8,channels:[{size:8,lane:t.Lane.ALPHA},{size:8,lane:t.Lane.RED}],fromABGR:t=>m(t)|t>>>16&65280,toABGR:t=>((65280&t)<<16|65793*(255&t))>>>0}),tt=I({type:"u16",size:16,channels:[{size:5,lane:t.Lane.RED},{size:6,lane:t.Lane.GREEN},{size:5,lane:t.Lane.BLUE}]}),et=I({type:"u32",size:24,channels:[{size:8,lane:t.Lane.RED},{size:8,lane:t.Lane.GREEN},{size:8,lane:t.Lane.BLUE}]}),st=P({gray:!0,alpha:!0,channels:[t.Lane.RED,t.Lane.ALPHA]}),it=Math.abs,nt=Math.min,rt=P({alpha:!0,channels:[t.Lane.RED,t.Lane.GREEN,t.Lane.BLUE,t.Lane.ALPHA],convert:{fromABGR:(t,e=[])=>{const s=(t>>>24&255)/255,n=(t>>>16&255)/255,r=(t>>>8&255)/255,a=(255&t)/255;let h,l,o,c,f,p,u,m;r<n?(h=n,l=r,o=-1,c=2/3):(h=r,l=n,o=0,c=-1/3),a<h?(f=h,p=l,u=c,m=a):(f=a,p=l,u=o,m=h);const d=f-nt(p,m);return f=i.clamp01(f),e[0]=i.clamp01(it((m-p)/(6*d+i.EPS)+u)),e[1]=i.clamp01(d/(f+i.EPS)),e[2]=f,e[3]=s,e},toABGR:t=>{const e=6*t[0],s=t[1],n=255*t[2],r=255*t[3],a=((i.clamp01(it(e-3)-1)-1)*s+1)*n,h=((i.clamp01(2-it(e-2))-1)*s+1)*n;return(r<<24|((i.clamp01(2-it(e-4))-1)*s+1)*n<<16|h<<8|a)>>>0}}}),at=P({channels:[t.Lane.RED,t.Lane.GREEN,t.Lane.BLUE]}),ht=P({alpha:!0,channels:[t.Lane.RED,t.Lane.GREEN,t.Lane.BLUE,t.Lane.ALPHA]});t.ABGR8888=H,t.ALPHA8=q,t.ARGB1555=V,t.ARGB4444=W,t.ARGB8888=M,t.BGR888=J,t.FLOAT_GRAY=$,t.FLOAT_GRAY_ALPHA=st,t.FLOAT_HSVA=rt,t.FLOAT_RGB=at,t.FLOAT_RGBA=ht,t.FloatBuffer=Y,t.GRAY16=K,t.GRAY8=X,t.GRAY_ALPHA16=Q,t.GRAY_ALPHA8=Z,t.PackedBuffer=j,t.RGB565=tt,t.RGB888=et,t.buffer=T,t.canvas2d=o,t.canvasPixels=c,t.clampRegion=d,t.compileFromABGR=E,t.compileGrayFromABGR=B,t.compileGrayToABGR=z,t.compileToABGR=b,t.defBayer=D,t.defFloatFormat=P,t.defPackedFormat=I,t.ditherPixels=(t,e,s,i,n,r,a,h)=>{!t&&(t=e.slice()),a--;for(let l=0,o=0;l<i;l++)for(let i=0;i<s;i++)t[o]=F(n,r,a,h,i,l,e[o]),o++;return t},t.ensureChannel=u,t.ensureSize=p,t.floatBuffer=(t,e,s,i)=>new Y(t,e,s,i),t.imageCanvas=f,t.imagePromise=async t=>{const e=new Image;return e.src=t,await e.decode(),e},t.luminanceABGR=m,t.orderedDither=F,t.packedBuffer=O,t.prepRegions=A,t.setChannelConvert=w,t.setChannelSame=R,t.setChannelUni=g,t.transformABGR=x,Object.defineProperty(t,"__esModule",{value:!0})}));
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("@thi.ng/checks"),require("@thi.ng/api"),require("@thi.ng/math"),require("@thi.ng/porter-duff"),require("@thi.ng/binary")):"function"==typeof define&&define.amd?define(["exports","@thi.ng/checks","@thi.ng/api","@thi.ng/math","@thi.ng/porter-duff","@thi.ng/binary"],e):e(((t="undefined"!=typeof globalThis?globalThis:t||self).thi=t.thi||{},t.thi.ng=t.thi.ng||{},t.thi.ng.pixel={}),t.thi.ng.checks,t.thi.ng.api,t.thi.ng.math,t.thi.ng.porterDuff,t.thi.ng.binary)}(this,(function(t,e,s,n,i,r){"use strict";var a,h,l;t.Lane=void 0,(a=t.Lane||(t.Lane={}))[a.ALPHA=0]="ALPHA",a[a.BLUE=1]="BLUE",a[a.GREEN=2]="GREEN",a[a.RED=3]="RED",t.Wrap=void 0,(h=t.Wrap||(t.Wrap={}))[h.NONE=0]="NONE",h[h.U=1]="U",h[h.V=2]="V",h[h.UV=3]="UV",t.Filter=void 0,(l=t.Filter||(t.Filter={}))[l.NEAREST=0]="NEAREST",l[l.LINEAR=1]="LINEAR";const o=(t,e=t,s)=>{const n=document.createElement("canvas");return n.width=t,n.height=e,s&&s.appendChild(n),{canvas:n,ctx:n.getContext("2d")}};function c(t,s){let n,i;if(e.isNumber(t)){const e=o(t,s);n=e.canvas,i=e.ctx}else n=t,i=n.getContext("2d");const r=i.getImageData(0,0,n.width,n.height);return{canvas:n,ctx:i,img:r,pixels:new Uint32Array(r.data.buffer)}}const f=(t,s,n=s,i)=>{const r=e.isNumber(s)?o(s,n,i):o(t.width,t.height,i);return r.ctx.drawImage(t,0,0,r.canvas.width,r.canvas.height),r},p=(t,e,n,i=1)=>s.assert(t.length>=e*n*i,"pixel buffer too small"),u=(t,e)=>{const n=t.channels[e];return s.assert(null!=n,`invalid channel ID: ${e}`),n},d=t=>(29*(t>>>16&255)+150*(t>>>8&255)+76*(255&t))/255,m=(t,e,s,i,r,a,h=0,l=0)=>(s|=0,i|=0,(t|=0)<0&&(s+=t,h-=t,t=0),(e|=0)<0&&(i+=e,l-=e,e=0),[t,e,n.clamp(s,0,r-t),n.clamp(i,0,a-e),h,l]),A=(t,e,s={})=>{let n,i,r,a,h,l,o=t.width,c=e.width;return[n,i,h,l]=m(s.sx||0,s.sy||0,s.w||o,s.h||t.height,o,t.height),[r,a,h,l,n,i]=m(s.dx||0,s.dy||0,h,l,c,e.height,n,i),{sx:n,sy:i,dx:r,dy:a,rw:h,rh:l}},g=(t,e,s)=>{for(let n=t.length;--n>=0;)t[n]=s(t[n],e)},R=(t,e,s,n)=>{for(let i=t.length;--i>=0;)t[i]=n(t[i],s(e[i]))},w=(t,e,s,n,i)=>{const r=~i;for(let a=t.length;--a>=0;)t[a]=t[a]&r|s(n(e[a]))&i},L=(t,e,s)=>{const n=e.fromABGR,i=e.toABGR;for(let e=t.length;--e>=0;)t[e]=n(s(i(t[e])))},x=t=>e.isNumber(t)?[t,t]:t,B=t=>{const e=x(t);return[0|e[0],0|e[1]]},y=t=>new Uint8Array(t).map(((t,e)=>e)),G=(t,e)=>e>0?`(${t} << ${e})`:e<0?`(${t} >>> ${-e})`:`${t}`,z=(t,e)=>G(t,-e),E=t=>`0x${t.toString(16)}`,b=t=>{const e=(1<<t)-1;return new Function("luma",`return (x) => ${z("luma(x)",8-t)} & ${e};`)(d)},$=t=>{let e;if(8!==t){const s=(1<<t)-1;e=`(((x & ${s}) * ${255/s}) | 0)`}else e="x";return new Function("x",`return 0xff000000 | (${e} * 0x010101);`)},S=t=>new Function("x","return ("+t.map((t=>{const e=t.abgrShift+(8-t.size);return`(${z("x",e)} & ${E(t.maskA)})`})).join(" | ")+") >>> 0;"),_=(t,e)=>{const s=t.map((t=>{if(8!==t.size){const e=t.mask0,s=255/e,n=z("x",t.shift);return G(`((${n} & ${e}) * ${s})`,24-8*t.lane)}return G(`(x & ${E(t.maskA)})`,t.abgrShift)})).join(" | ");return new Function("x",`return (${e?"":"0xff000000 | "}${s}) >>> 0;`)},N=t=>{const e=t.channels,s=e.reduce(((t,e)=>(t[e]=3-e<<3,t)),{}),i=Object.assign(Object.assign({},t),{size:e.length,shift:s,__float:!0});if(t.convert)return Object.assign(i,t.convert),i;const r=(t,i)=>(255*n.clamp01(t[i])+.5|0)<<s[e[i]],a=(t,n)=>(t>>>s[e[n]]&255)/255;switch(e.length){case 1:t.gray?v(i):P(i,a,r);break;case 2:t.gray?O(i,a):k(i,a,r);break;case 3:F(i,a,r);break;case 4:U(i,a,r)}return i},P=(t,e,s)=>{t.toABGR=e=>{let n=t.alpha?0:4278190080;return n|=s(e,0),n>>>0},t.fromABGR=(t,s=[])=>(s[0]=e(t,0),s)},v=t=>{t.toABGR=t=>(65793*(255*n.clamp01(t[0])+.5|0)|4278190080)>>>0,t.fromABGR=(t,e=[])=>(e[0]=d(t)/255,e)},k=(t,e,s)=>{t.toABGR=e=>{let n=t.alpha?0:4278190080;return n|=s(e,0),n|=s(e,1),n>>>0},t.fromABGR=(t,s=[])=>(s[0]=e(t,0),s[1]=e(t,1),s)},O=(e,s)=>{const i=~~(e.channels[0]===t.Lane.ALPHA),r=1^i;e.toABGR=t=>{let e=65793*(255*n.clamp01(t[i])+.5|0);return e|=(255*t[r]+.5|0)<<24,e>>>0},e.fromABGR=(t,e=[])=>(e[i]=d(t)/255,e[r]=s(t,r),e)},F=(t,e,s)=>{t.toABGR=e=>{let n=t.alpha?0:4278190080;return n|=s(e,0),n|=s(e,1),n|=s(e,2),n>>>0},t.fromABGR=(t,s=[])=>(s[0]=e(t,0),s[1]=e(t,1),s[2]=e(t,2),s)},U=(t,e,s)=>{t.toABGR=e=>{let n=t.alpha?0:4278190080;return n|=s(e,0),n|=s(e,1),n|=s(e,2),n|=s(e,3),n>>>0},t.fromABGR=(t,s=[])=>(s[0]=e(t,0),s[1]=e(t,1),s[2]=e(t,2),s[3]=e(t,3),s)},C=N({gray:!0,channels:[t.Lane.RED]}),D=(t,e,s,n,i,r)=>{if(1===s)return!r[e]&&(r[e]=[]),r[e][t]=n,r;const a=i<<2;return D(t,e,s>>=1,n,a,r),D(t+s,e+s,s,n+i,a,r),D(t+s,e,s,n+2*i,a,r),D(t,e+s,s,n+3*i,a,r),r},I=t=>({mat:D(0,0,t,0,1,[]),invSize:1/(t*t),mask:t-1}),H=({mat:t,mask:e,invSize:s},i,r,a,h,l,o)=>(o=i*(o/a)+t[l&e][h&e]*s-.5|0,i--,n.clamp(o,0,i)*((r-1)/i)),j=t=>{s.assert(t.channels.length>0,"no channel specs given");const e=t.channels.reduce((([t,e],s,i)=>(e-=s.size,t.push(((t,e,s)=>{const i=1<<t.size,r=i-1,a=r<<s>>>0,h=~a>>>0,l=null!=t.lane?t.lane:e,o=t=>t>>>s&r,c=(t,e)=>t&h|(e&r)<<s;return{size:t.size,abgrShift:24-8*l-s,lane:l,shift:s,mask0:r,maskA:a,int:o,setInt:c,float:t=>o(t)/r,setFloat:(t,e)=>c(t,n.clamp01(e)*r),dither:(t,e,s,n,r)=>H(t,e,i,i,s,n,r)}})(s,i,e)),[t,e])),[[],t.size])[0];return{__packed:!0,type:t.type,size:t.size,alpha:t.alpha||0,channels:e,fromABGR:t.fromABGR||S(e),toABGR:t.toABGR||_(e,!!t.alpha)}},M=j({type:"u32",size:32,alpha:8,channels:[{size:8,lane:t.Lane.ALPHA},{size:8,lane:t.Lane.BLUE},{size:8,lane:t.Lane.GREEN},{size:8,lane:t.Lane.RED}],fromABGR:t=>t,toABGR:t=>t});function T(...t){return t[0]instanceof V?t[0].as(t[1]):new V(...t)}const Y=T;class V{constructor(t,e,n,i){this.width=t,this.height=e,this.format=n.__packed?n:j(n),this.pixels=i||s.typedArray(n.type,t*e)}static fromImage(t,e,s,n=s){return V.fromCanvas(f(t,s,n).canvas,e)}static fromCanvas(t,e=M){const n=c(t),i=t.width,r=t.height;let a;if(e===M)a=n.pixels;else{a=s.typedArray(e.type,i*r);const t=n.pixels,h=e.fromABGR;for(let e=a.length;--e>=0;)a[e]=h(t[e])}return new V(i,r,e,a)}get stride(){return 1}as(t){return this.getRegion(0,0,this.width,this.height,t)}copy(){const t=this.empty();return t.pixels.set(this.pixels),t}empty(){return new V(this.width,this.height,this.format)}getAt(t,e){return t>=0&&t<this.width&&e>=0&&e<this.height?this.pixels[(0|t)+(0|e)*this.width]:0}setAt(t,e,s){return t>=0&&t<this.width&&e>=0&&e<this.height&&(this.pixels[(0|t)+(0|e)*this.width]=s),this}getChannelAt(t,e,s,n=!1){const i=u(this.format,s),r=this.getAt(t,e);return n?i.float(r):i.int(r)}setChannelAt(t,e,s,n,i=!1){const r=u(this.format,s),a=this.getAt(t,e);return i?r.setFloat(a,n):r.setInt(a,n),this}blend(t,e,s){let n=this.width,i=e.width;const{sx:r,sy:a,dx:h,dy:l,rw:o,rh:c}=A(this,e,s);if(o<1||c<1)return e;const f=this.pixels,p=e.pixels,u=this.format.toABGR,d=e.format.toABGR,m=e.format.fromABGR;for(let e=(0|r)+(0|a)*n,s=(0|h)+(0|l)*i,A=0;A<c;A++,e+=n,s+=i)for(let n=0;n<o;n++)p[s+n]=m(t(u(f[e+n]),d(p[s+n])));return e}blit(t,e){let s=this.width,n=t.width;const{sx:i,sy:r,dx:a,dy:h,rw:l,rh:o}=A(this,t,e);if(l<1||o<1)return t;const c=this.pixels,f=t.pixels,p=this.format.toABGR,u=t.format.fromABGR,d=this.format!==t.format?(t,e)=>{for(let s=0;s<l;s++)f[e+s]=u(p(c[t+s]))}:(t,e)=>f.set(c.subarray(t,t+l),e);for(let t=(0|i)+(0|r)*s,e=(0|a)+(0|h)*n,l=0;l<o;l++,t+=s,e+=n)d(t,e);return t}blitCanvas(t,e=0,s=0){const n=t.getContext("2d"),i=new ImageData(this.width,this.height),r=new Uint32Array(i.data.buffer),a=this.pixels,h=this.format.toABGR;for(let t=r.length;--t>=0;)r[t]=h(a[t]);return n.putImageData(i,e,s),t}getRegion(t,e,s,n,i){const[r,a,h,l]=m(t,e,s,n,this.width,this.height);return this.blit(new V(h,l,i||this.format),{sx:r,sy:a,w:h,h:l})}getChannel(e){const n=u(this.format,e),i=new V(this.width,this.height,{type:s.uintTypeForBits(n.size),size:n.size,channels:[{size:n.size,lane:t.Lane.RED}],fromABGR:b(n.size),toABGR:$(n.size)}),r=this.pixels,a=i.pixels,h=n.int;for(let t=r.length;--t>=0;)a[t]=h(r[t]);return i}setChannel(t,s){const n=u(this.format,t),i=this.pixels,r=n.setInt;if(e.isNumber(s))g(i,s,r);else{const t=s.pixels,e=s.format.channels[0];p(t,this.width,this.height),n.size===e.size?R(i,t,e.int,r):w(i,t,this.format.fromABGR,s.format.toABGR,n.maskA)}return this}invert(){const{format:t,pixels:e}=this,s=Math.pow(2,t.size-t.alpha)-1;for(let t=e.length;--t>=0;)e[t]^=s;return this}premultiply(){return L(this.pixels,this.format,i.premultiplyInt),this}postmultiply(){return L(this.pixels,this.format,i.postmultiplyInt),this}isPremultiplied(){const t=this.pixels,e=this.format.toABGR;for(let s=t.length;--s>=0;)if(!i.isPremultipliedInt(e(t[s])))return!1;return!0}forEach(t){const e=this.pixels;for(let s=e.length;--s>=0;)e[s]=t(e[s]);return this}dither(t,s){const{pixels:n,format:i,width:r}=this,a=e.isNumber(s)?new Array(i.channels.length).fill(s):s,h=e.isNumber(t)?I(t):t;for(let t=0,e=n.length,s=i.channels.length,l=0,o=0;t<e;t++){let e=n[t];for(let t=0;t<s;t++){const s=i.channels[t],n=a[t];n>0&&(e=s.setInt(e,s.dither(h,n,l,o,s.int(e))))}n[t]=e,++l===r&&(l=0,o++)}return this}flipY(){const{pixels:t,width:e}=this,n=s.typedArray(this.format.type,e);for(let s=0,i=t.length-e;s<i;s+=e,i-=e)n.set(t.subarray(s,s+e)),t.copyWithin(s,i,i+e),t.set(n,i);return this}downsample(t){t|=0;const{width:e,height:s,pixels:n}=this,i=new V(e/t|0,s/t|0,this.format),{width:r,height:a,pixels:h}=i;for(let s=0,i=0;s<a;s++)for(let a=0,l=s*t*e;a<r;a++,i++,l+=t)h[i]=n[l];return i}}class X{constructor(t,e,s,n){this.width=t,this.height=e,this.format=s.__float?s:N(s),this.stride=s.channels.length,this.rowStride=t*this.stride,this.pixels=n||new Float32Array(t*e*this.stride),this.__empty=Object.freeze(new Array(this.stride).fill(0))}static fromPacked(t,e){const s=new X(t.width,t.height,e),{pixels:n,format:i,stride:r}=s,{pixels:a,format:h}=t;for(let t=a.length;--t>=0;)n.set(i.fromABGR(h.toABGR(a[t])),t*r);return s}as(t){const{width:e,height:s,stride:n,pixels:i,format:r}=this,a=new V(e,s,t),h=a.pixels;for(let e=0,s=0,a=i.length;e<a;e+=n,s++)h[s]=t.fromABGR(r.toABGR(i.subarray(e,e+n)));return a}copy(){const t=this.empty();return t.pixels.set(this.pixels),t}empty(){return new X(this.width,this.height,this.format)}getAt(t,e){const{width:s,stride:n}=this;if(t>=0&&t<s&&e>=0&&e<this.height){const s=(0|t)*n+(0|e)*this.rowStride;return this.pixels.subarray(s,s+n)}return this.__empty}setAt(t,e,s){return t>=0&&t<this.width&&e>=0&&e<this.height&&this.pixels.set(s,(0|t)*this.stride+(0|e)*this.rowStride),this}getChannelAt(t,e,s){u(this.format,s);const{width:n,stride:i}=this;if(t>=0&&t<n&&e>=0&&e<this.height)return this.pixels[(0|t)*i+(0|e)*this.rowStride+s]}setChannelAt(t,e,s,n){u(this.format,s);const{width:i,stride:r}=this;return t>=0&&t<i&&e>=0&&e<this.height&&(this.pixels[(0|t)*r+(0|e)*this.rowStride+s]=n),this}getChannel(t){u(this.format,t);const{pixels:e,stride:s}=this,i=new Float32Array(this.width*this.height);for(let r=t,a=0,h=e.length;r<h;r+=s,a++)i[a]=n.clamp01(e[r]);return new X(this.width,this.height,C,i)}setChannel(t,s){u(this.format,t);const{pixels:n,stride:i}=this;if(e.isNumber(s))for(let e=t,r=n.length;e<r;e+=i)n[e]=s;else{const{pixels:e,stride:r}=s;p(e,this.width,this.height,r);for(let s=t,a=0,h=n.length;s<h;s+=i,a+=r)n[s]=e[a]}return this}blend(t,e,s){this.ensureFormat(e);const{sx:n,sy:i,dx:r,dy:a,rw:h,rh:l}=A(this,e,s);if(h<1||l<1)return e;const o=this.pixels,c=e.pixels,f=this.rowStride,p=e.rowStride,u=this.stride;for(let e=(0|n)*u+(0|i)*f,s=(0|r)*u+(0|a)*p,d=0;d<l;d++,e+=f,s+=p)for(let n=h,i=e,r=s;--n>=0;i+=u,r+=u){const e=c.subarray(r,r+u);t(e,o.subarray(i,i+u),e)}return e}blit(t,e){this.ensureFormat(t);const{sx:s,sy:n,dx:i,dy:r,rw:a,rh:h}=A(this,t,e);if(a<1||h<1)return t;const l=this.pixels,o=t.pixels,c=this.rowStride,f=t.rowStride,p=a*this.stride;for(let t=(0|s)*this.stride+(0|n)*c,e=(0|i)*this.stride+(0|r)*f,a=0;a<h;a++,t+=c,e+=f)o.set(l.subarray(t,t+p),e);return t}blitCanvas(t,e=0,s=0){const n=t.getContext("2d"),i=new ImageData(this.width,this.height),r=new Uint32Array(i.data.buffer),{stride:a,pixels:h,format:l}=this;for(let t=0,e=0,s=h.length;t<s;t+=a,e++)r[e]=l.toABGR(h.subarray(t,t+a));return n.putImageData(i,e,s),t}getRegion(t,e,s,n){const[i,r,a,h]=m(t,e,s,n,this.width,this.height);return this.blit(new X(a,h,this.format),{sx:i,sy:r,w:a,h})}forEach(t){const{pixels:e,stride:s}=this;for(let n=0,i=e.length;n<i;n+s)t(e.subarray(n,n+s));return this}clamp(){const t=this.pixels;for(let e=t.length;--e>=0;)t[e]=n.clamp01(t[e]);return this}clampChannel(t){u(this.format,t);const{pixels:e,stride:s}=this;for(let i=t,r=e.length;i<r;i+=s)e[i]=n.clamp01(e[i])}flipY(){const{pixels:t,rowStride:e}=this,s=new Float32Array(e);for(let n=0,i=t.length-e;n<i;n+=e,i-=e)s.set(t.subarray(n,n+e)),t.copyWithin(n,i,i+e),t.set(s,i);return this}downsample(t){t|=0;const{width:e,height:s,stride:n,pixels:i}=this,r=new X(e/t|0,s/t|0,this.format),{width:a,height:h,pixels:l}=r;t*=n;for(let s=0,r=0;s<h;s++)for(let h=0,o=s*t*e;h<a;h++,r+=n,o+=t)l.set(i.subarray(o,o+n),r);return r}ensureFormat(t){s.assert(t.format===this.format,"dest buffer format must be same as src")}}const q=(t,e)=>W(J(t,e)),W=({channel:t,dest:e,dwidth:s,kernel:n,kh2:i,kw2:r,pad:a,rowStride:h,scale:l,src:o,srcStride:c,strideX:f,strideY:p})=>{u(o.format,t);const d=o.height-i,m=o.width-r,A=a&&1===f,g=a&&1===p,R=e.pixels;for(let e=i,a=g?i:0;e<d;e+=p,a++)for(let i=r,o=e*h+i*c+t,p=a*s+(A?i:0);i<m;i+=f,o+=f*c,p++)R[p]=n(o)*l;return e},K=(t,s,n,i)=>(e.isFunction(s.fn)?s.fn:Z(s.spec||s.pool,n,i))(t),J=(t,e)=>{const{kernel:n,channel:i,stride:r,pad:a,scale:h}=Object.assign({channel:0,pad:!0,scale:1,stride:1},e),l=n.size,[o,c]=B(l),[f,p]=B(r);s.assert(f>=1&&p>=1,`illegal stride: ${r}`);const{width:u,height:d,stride:m,rowStride:A}=t;s.assert(o>=0&&o<=u&&c>=0&&c<=d,`invalid kernel size: ${l}`);const g=Q(u,f,o,a),R=Q(d,p,c,a);return{channel:i,dest:new X(g,R,C),dheight:R,dwidth:g,kernel:K(t,n,o,c),kh2:c>>1,kw2:o>>1,pad:a,rowStride:A,scale:h,src:t,srcStride:m,strideX:f,strideY:p}},Q=(t,e,s,n)=>n?Math.floor(t/e):Math.ceil((t-s+1)/e),Z=(t,s,n)=>{if(s*n>1024&&!e.isFunction(t))return tt(t,s,n);const i=e.isFunction(t),r=[],a=[],h=[],l=[],o=n>>1,c=s>>1;for(let e=0,f=0;e<n;e++){const n=e-o,p=[];for(let a=0;a<s;a++,f++){const s=`k${e}_${a}`;l.push(s);const h=a-c,o=(0!==n?`i${e}`:"i")+(0!==h?`+x${a}`:"");i?p.push(`pix[${o}]`):0!==t[f]&&p.push(`${s}*pix[${o}]`),0===e&&0!==h&&r.push(`const x${a} = ${h} * stride;`)}p.length&&h.push(...p),0!==n&&(r.push(`const y${e} = ${n} * rowStride;`),a.push(`const i${e} = i + y${e};`))}const f=i?"":`const [${l.join(", ")}] = [${t.join(", ")}];`,p=i?t(h,s,n):h.join(" + "),u=[f,"const { pixels: pix, stride, rowStride } = src;",...r,"return (i) => {",...a,`return ${p};`,"}"].join("\n");return new Function("src",u)},tt=(t,e,s)=>{const n=s>>1,i=e>>1;return e=>{const{pixels:s,rowStride:r,stride:a}=e;return e=>{let h=0;for(let l=-n,o=0;l<=n;l++)for(let n=-i,c=e+l*r;n<=i;n++,c+=a,o++)h+=t[o]*s[c];return h}}},et=(t,e,s)=>t[(s>>1)*e+(e>>1)],st=(t,e,s)=>`(${t.join("+")})*${1/(e*s)}`,nt={pool:st,size:3},it={pool:st,size:5},rt=t=>t/127.5-1,at=(t,e)=>n.clamp(127.5*t+128,0,255)<<e,ht={__float:!0,alpha:!1,gray:!1,channels:[t.Lane.RED,t.Lane.GREEN,t.Lane.BLUE],shift:{3:0,2:8,1:16},size:3,fromABGR:t=>[rt(255&t),rt(t>>8&255),rt(t>>16&255)],toABGR:t=>at(t[0],0)|at(t[1],8)|at(t[2],16)|4278190080},lt=j({type:"u8",size:8,alpha:8,channels:[{size:8,lane:0}]}),ot=j({type:"u16",size:16,alpha:1,channels:[{size:1,lane:t.Lane.ALPHA},{size:5,lane:t.Lane.RED},{size:5,lane:t.Lane.GREEN},{size:5,lane:t.Lane.BLUE}]}),ct=j({type:"u16",size:16,alpha:4,channels:[{size:4,lane:t.Lane.ALPHA},{size:4,lane:t.Lane.RED},{size:4,lane:t.Lane.GREEN},{size:4,lane:t.Lane.BLUE}]}),ft=j({type:"u32",size:32,alpha:8,channels:[{size:8,lane:t.Lane.ALPHA},{size:8,lane:t.Lane.RED},{size:8,lane:t.Lane.GREEN},{size:8,lane:t.Lane.BLUE}],fromABGR:r.swapLane13,toABGR:r.swapLane13}),pt=j({type:"u32",size:24,channels:[{size:8,lane:t.Lane.BLUE},{size:8,lane:t.Lane.GREEN},{size:8,lane:t.Lane.RED}],fromABGR:t=>16777215&t,toABGR:t=>4278190080|t}),ut=j({type:"u16",size:16,channels:[{size:16,lane:t.Lane.RED}],fromABGR:t=>257*(d(t)+.5|0),toABGR:t=>(4278190080|65793*(t>>>8))>>>0}),dt=j({type:"u32",size:32,channels:[{size:8,lane:t.Lane.ALPHA},{size:16,lane:t.Lane.RED}],fromABGR:t=>257*(d(t)+.5|0)|257*(t>>>8&16711680),toABGR:t=>(4278190080&t|65793*(t>>>8&255))>>>0}),mt=j({type:"u8",size:8,channels:[{size:8,lane:t.Lane.RED}],fromABGR:t=>d(t),toABGR:t=>(4278190080|65793*(255&t))>>>0}),At=j({type:"u16",size:16,alpha:8,channels:[{size:8,lane:t.Lane.ALPHA},{size:8,lane:t.Lane.RED}],fromABGR:t=>d(t)|t>>>16&65280,toABGR:t=>((65280&t)<<16|65793*(255&t))>>>0}),gt=j({type:"u16",size:16,channels:[{size:5,lane:t.Lane.RED},{size:6,lane:t.Lane.GREEN},{size:5,lane:t.Lane.BLUE}]}),Rt=j({type:"u32",size:24,channels:[{size:8,lane:t.Lane.RED},{size:8,lane:t.Lane.GREEN},{size:8,lane:t.Lane.BLUE}]}),wt=N({gray:!0,alpha:!0,channels:[t.Lane.RED,t.Lane.ALPHA]}),Lt=Math.abs,xt=Math.min,Bt=N({alpha:!0,channels:[t.Lane.RED,t.Lane.GREEN,t.Lane.BLUE,t.Lane.ALPHA],convert:{fromABGR:(t,e=[])=>{const s=(t>>>24&255)/255,i=(t>>>16&255)/255,r=(t>>>8&255)/255,a=(255&t)/255;let h,l,o,c,f,p,u,d;r<i?(h=i,l=r,o=-1,c=2/3):(h=r,l=i,o=0,c=-1/3),a<h?(f=h,p=l,u=c,d=a):(f=a,p=l,u=o,d=h);const m=f-xt(p,d);return f=n.clamp01(f),e[0]=n.clamp01(Lt((d-p)/(6*m+n.EPS)+u)),e[1]=n.clamp01(m/(f+n.EPS)),e[2]=f,e[3]=s,e},toABGR:t=>{const e=6*t[0],s=t[1],i=255*t[2],r=255*t[3],a=((n.clamp01(Lt(e-3)-1)-1)*s+1)*i,h=((n.clamp01(2-Lt(e-2))-1)*s+1)*i;return(r<<24|((n.clamp01(2-Lt(e-4))-1)*s+1)*i<<16|h<<8|a)>>>0}}}),yt=N({channels:[t.Lane.RED,t.Lane.GREEN,t.Lane.BLUE]}),Gt=N({alpha:!0,channels:[t.Lane.RED,t.Lane.GREEN,t.Lane.BLUE,t.Lane.ALPHA]});t.ABGR8888=M,t.ALPHA8=lt,t.ARGB1555=ot,t.ARGB4444=ct,t.ARGB8888=ft,t.BGR888=pt,t.BOX_BLUR3=nt,t.BOX_BLUR5=it,t.FLOAT_GRAY=C,t.FLOAT_GRAY_ALPHA=wt,t.FLOAT_HSVA=Bt,t.FLOAT_NORMAL=ht,t.FLOAT_RGB=yt,t.FLOAT_RGBA=Gt,t.FloatBuffer=X,t.GAUSSIAN=t=>{t|=0,s.assert(t>0,`invalid kernel radius: ${t}`);const e=-1/(2*(Math.hypot(t,t)/3)**2),n=[];let i=0;for(let s=-t;s<=t;s++)for(let r=-t;r<=t;r++){const t=Math.exp((r*r+s*s)*e);n.push(t),i+=t}return{spec:n.map((t=>t/i)),size:2*t+1}},t.GAUSSIAN_BLUR3={spec:[1/16,1/8,1/16,1/8,1/4,1/8,1/16,1/8,1/16],size:3},t.GAUSSIAN_BLUR5={spec:[1/256,1/64,3/128,1/64,1/256,1/64,1/16,3/32,1/16,1/64,3/128,3/32,9/64,3/32,3/128,1/64,1/16,3/32,1/16,1/64,1/256,1/64,3/128,1/64,1/256],size:5},t.GRAY16=ut,t.GRAY8=mt,t.GRAY_ALPHA16=dt,t.GRAY_ALPHA8=At,t.HIGHPASS3={spec:[-1,-1,-1,-1,9,-1,-1,-1,-1],size:3},t.POOL_MAX=t=>`Math.max(${t.join(",")})`,t.POOL_MEAN=st,t.POOL_MIN=t=>`Math.min(${t.join(",")})`,t.POOL_NEAREST=et,t.POOL_THRESHOLD=(t=0)=>(e,s,n)=>`(${et(e,s,n)} - ${`(${e.join("+")})/${s*n}`} + ${t}) < 0 ? 0 : 1`,t.PackedBuffer=V,t.RGB565=gt,t.RGB888=Rt,t.SHARPEN3={spec:[0,-1,0,-1,5,-1,0,-1,0],size:3},t.SOBEL_X={spec:[-1,-2,-1,0,0,0,1,2,1],size:3},t.SOBEL_Y={spec:[-1,0,1,-2,0,2,-1,0,1],size:3},t.UNSHARP_MASK5={spec:[-1/256,-1/64,-3/128,-1/64,-1/256,-1/64,-1/16,-3/32,-1/16,-1/64,-3/128,-3/32,119/64,-3/32,-3/128,-1/64,-1/16,-3/32,-1/16,-1/64,-1/256,-1/64,-3/128,-1/64,-1/256],size:5},t.asIntVec=B,t.asVec=x,t.buffer=Y,t.canvas2d=o,t.canvasPixels=c,t.clampRegion=m,t.compileFromABGR=S,t.compileGrayFromABGR=b,t.compileGrayToABGR=$,t.compileToABGR=_,t.convolveChannel=q,t.convolveImage=(t,e)=>{const s=J(t,e),n=new X(s.dwidth,s.dheight,t.format);for(let i of e.channels||y(t.format.channels.length))n.setChannel(i,W(Object.assign(Object.assign({},s),{channel:i})));return n},t.defBayer=I,t.defFloatFormat=N,t.defKernel=Z,t.defLargeKernel=tt,t.defPackedFormat=j,t.ditherPixels=(t,e,s,n,i,r,a,h)=>{!t&&(t=e.slice()),a--;for(let l=0,o=0;l<n;l++)for(let n=0;n<s;n++)t[o]=H(i,r,a,h,n,l,e[o]),o++;return t},t.ensureChannel=u,t.ensureSize=p,t.floatBuffer=function(...t){return t[0]instanceof V?X.fromPacked(...t):new X(...t)},t.imageCanvas=f,t.imagePromise=async t=>{const e=new Image;return e.src=t,await e.decode(),e},t.luminanceABGR=d,t.normalMap=(t,e)=>{const{channel:s,step:n,scale:i,z:r}=Object.assign({channel:0,step:0,scale:1,z:1},e);u(t.format,s);const a=[-1,...new Array(n).fill(0),1],[h,l]=x(i),o=new X(t.width,t.height,ht);return o.setChannel(0,q(t,{kernel:{spec:a,size:[n+2,1]},scale:h,channel:s})),o.setChannel(1,q(t,{kernel:{spec:a,size:[1,n+2]},scale:l,channel:s})),o.setChannel(2,r),o},t.orderedDither=H,t.packedBuffer=T,t.prepRegions=A,t.range=y,t.setChannelConvert=w,t.setChannelSame=R,t.setChannelUni=g,t.transformABGR=L,Object.defineProperty(t,"__esModule",{value:!0})}));
{
"name": "@thi.ng/pixel",
"version": "0.6.1",
"description": "Typed array backed, integer and floating point pixel buffers w/ customizable formats, blitting, dithering, conversions",
"version": "0.7.1",
"description": "Typedarray integer & float pixel buffers w/ customizable formats, blitting, dithering, convolution",
"module": "./index.js",

@@ -42,3 +42,3 @@ "main": "./lib/index.js",

"@istanbuljs/nyc-config-typescript": "^1.0.1",
"@microsoft/api-extractor": "^7.12.1",
"@microsoft/api-extractor": "^7.13.1",
"@types/mocha": "^8.2.0",

@@ -49,11 +49,11 @@ "@types/node": "^14.14.14",

"ts-node": "^9.1.1",
"typedoc": "^0.20.26",
"typescript": "^4.1.5"
"typedoc": "^0.20.28",
"typescript": "^4.2.2"
},
"dependencies": {
"@thi.ng/api": "^7.0.0",
"@thi.ng/binary": "^2.1.0",
"@thi.ng/checks": "^2.9.0",
"@thi.ng/math": "^3.2.0",
"@thi.ng/porter-duff": "^0.1.38"
"@thi.ng/api": "^7.1.1",
"@thi.ng/binary": "^2.2.1",
"@thi.ng/checks": "^2.9.2",
"@thi.ng/math": "^3.2.2",
"@thi.ng/porter-duff": "^0.1.40"
},

@@ -77,2 +77,3 @@ "files": [

"blit",
"blur",
"canvas",

@@ -82,2 +83,3 @@ "channel",

"conversion",
"convolution",
"datastructure",

@@ -87,2 +89,3 @@ "dither",

"format",
"gaussian",
"grayscale",

@@ -92,5 +95,7 @@ "image",

"multiformat",
"normal",
"pixel",
"resize",
"rgb565",
"sharpen",
"typedarray",

@@ -109,3 +114,3 @@ "typescript"

},
"gitHead": "2cb9a54255d6a5d7f21c9875002b86c42dd038de"
"gitHead": "a59cd470a1728cb5f40e96e010fb98ead30e3142"
}

@@ -1,2 +0,2 @@

import { Fn, UIntArray } from "@thi.ng/api";
import { Fn, ICopy, IEmpty, UIntArray } from "@thi.ng/api";
import { BayerMatrix, BayerSize, BlendFnInt, BlitOpts, IPixelBuffer, PackedFormat, PackedFormatSpec } from "./api";

@@ -11,10 +11,20 @@ /**

*/
export declare const packedBuffer: (w: number, h: number, fmt: PackedFormat | PackedFormatSpec, pixels?: Uint8Array | Uint8ClampedArray | Uint16Array | Uint32Array | undefined) => PackedBuffer;
export declare function packedBuffer(w: number, h: number, fmt: PackedFormat | PackedFormatSpec, pixels?: UIntArray): PackedBuffer;
export declare function packedBuffer(src: PackedBuffer, fmt: PackedFormat | PackedFormatSpec): PackedBuffer;
/**
* @deprecated use {@link packedBuffer} instead.
*/
export declare const buffer: (w: number, h: number, fmt: PackedFormat | PackedFormatSpec, pixels?: Uint8Array | Uint8ClampedArray | Uint16Array | Uint32Array | undefined) => PackedBuffer;
export declare class PackedBuffer implements IPixelBuffer<UIntArray, number> {
static fromImage(img: HTMLImageElement, fmt: PackedFormat, width?: number, height?: number | undefined): PackedBuffer;
static fromCanvas(canvas: HTMLCanvasElement): PackedBuffer;
export declare const buffer: typeof packedBuffer;
export declare class PackedBuffer implements IPixelBuffer<UIntArray, number>, ICopy<PackedBuffer>, IEmpty<PackedBuffer> {
/**
* Creates a new pixel buffer from given HTML image element with optional
* support for format conversion (default: {@link ABGR8888} & resizing.
*
* @param img
* @param fmt
* @param width
* @param height
*/
static fromImage(img: HTMLImageElement, fmt?: PackedFormat, width?: number, height?: number | undefined): PackedBuffer;
static fromCanvas(canvas: HTMLCanvasElement, fmt?: PackedFormat): PackedBuffer;
readonly width: number;

@@ -28,2 +38,3 @@ readonly height: number;

copy(): PackedBuffer;
empty(): PackedBuffer;
getAt(x: number, y: number): number;

@@ -30,0 +41,0 @@ setAt(x: number, y: number, col: number): this;

@@ -1,2 +0,2 @@

import { typedArray, uintTypeForBits } from "@thi.ng/api";
import { typedArray, uintTypeForBits, } from "@thi.ng/api";
import { isNumber } from "@thi.ng/checks";

@@ -11,12 +11,9 @@ import { isPremultipliedInt, postmultiplyInt, premultiplyInt, } from "@thi.ng/porter-duff";

import { clampRegion, ensureChannel, ensureSize, prepRegions, setChannelConvert, setChannelSame, setChannelUni, transformABGR, } from "./utils";
export function packedBuffer(...args) {
return args[0] instanceof PackedBuffer
? args[0].as(args[1])
: // @ts-ignore
new PackedBuffer(...args);
}
/**
* Syntax sugar for {@link PackedBuffer} ctor.
*
* @param w -
* @param h -
* @param fmt -
* @param pixels -
*/
export const packedBuffer = (w, h, fmt, pixels) => new PackedBuffer(w, h, fmt, pixels);
/**
* @deprecated use {@link packedBuffer} instead.

@@ -34,17 +31,32 @@ */

}
/**
* Creates a new pixel buffer from given HTML image element with optional
* support for format conversion (default: {@link ABGR8888} & resizing.
*
* @param img
* @param fmt
* @param width
* @param height
*/
static fromImage(img, fmt, width, height = width) {
const ctx = imageCanvas(img, width, height);
const w = ctx.canvas.width;
const h = ctx.canvas.height;
const src = new Uint32Array(ctx.ctx.getImageData(0, 0, w, h).data.buffer);
const dest = typedArray(fmt.type, w * h);
const from = fmt.fromABGR;
for (let i = dest.length; --i >= 0;) {
dest[i] = from(src[i]);
return PackedBuffer.fromCanvas(imageCanvas(img, width, height).canvas, fmt);
}
static fromCanvas(canvas, fmt = ABGR8888) {
const ctx = canvasPixels(canvas);
const w = canvas.width;
const h = canvas.height;
let dest;
if (fmt === ABGR8888) {
dest = ctx.pixels;
}
else {
dest = typedArray(fmt.type, w * h);
const src = ctx.pixels;
const from = fmt.fromABGR;
for (let i = dest.length; --i >= 0;) {
dest[i] = from(src[i]);
}
}
return new PackedBuffer(w, h, fmt, dest);
}
static fromCanvas(canvas) {
return new PackedBuffer(canvas.width, canvas.height, ABGR8888, canvasPixels(canvas).pixels);
}
get stride() {

@@ -57,6 +69,9 @@ return 1;

copy() {
const dest = new PackedBuffer(this.width, this.height, this.format);
const dest = this.empty();
dest.pixels.set(this.pixels);
return dest;
}
empty() {
return new PackedBuffer(this.width, this.height, this.format);
}
getAt(x, y) {

@@ -63,0 +78,0 @@ return x >= 0 && x < this.width && y >= 0 && y < this.height

@@ -16,2 +16,4 @@ <!-- This file is generated - DO NOT EDIT! -->

- [Floating point pixel formats](#floating-point-pixel-formats)
- [Strided convolution & pooling](#strided-convolution--pooling)
- [Normal map generation](#normal-map-generation)
- [Status](#status)

@@ -29,3 +31,3 @@ - [Support packages](#support-packages)

Typed array backed, integer and floating point pixel buffers w/ customizable formats, blitting, dithering, conversions.
Typedarray integer & float pixel buffers w/ customizable formats, blitting, dithering, convolution.

@@ -43,6 +45,9 @@ ![screenshot](https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/pixel/pixel-basics.png)

- Single-channel manipulation / extraction / replacement / conversion
- Convolution w/ arbitrary shaped/sized kernels, pooling, striding (resizing)
- Convolution kernel & pooling kernels presets
- Customizable normal map generation (i.e. X/Y gradients plus static Z component)
- Inversion
- XY pixel accessors
- 10 packed integer and 4 floating point preset formats (see table
below)
- Image downsampling (nearest neighbor, mean/min/max pooling)
- XY full pixel & channel-only accessors
- 12 packed integer and 6 floating point preset formats (see table below)
- Ordered dithering w/ customizable Bayer matrix size and target color

@@ -108,10 +113,83 @@ steps (int formats only)

| `FLOAT_GRAY_ALPHA` | 2 | Grayscale and alpha channel |
| `FLOAT_NORMAL` | 3 | Normal map (signed values) |
| `FLOAT_RGB` | 3 | Red, Green, Blue |
| `FLOAT_RGBA` | 4 | Red, Green, Blue, Alpha |
- All color channels are unclamped (but can be clamped via
`buf.clamp()`). For conversion to packed int formats assumed to
contain normalized data (i.e. [0..1] interval)
- All color channels are unclamped (but can be clamped via `buf.clamp()`). For
conversion to packed int formats assumed to contain normalized data (i.e.
[0..1] interval, with exception of `FLOAT_NORMAL` which uses [-1..1] range)
- Conversion between float formats is currently unsupported
### Strided convolution & pooling
Floating point buffers can be processed using arbitrary convolution kernels. The
following convolution kernel presets are provided for convenience:
| Kernel | Size |
|------------------|-------------|
| `BOX_BLUR3` | 3x3 |
| `BOX_BLUR5` | 5x5 |
| `GAUSSIAN_BLUR3` | 3x3 |
| `GAUSSIAN_BLUR5` | 5x5 |
| `GAUSSIAN(n)` | 2n+1 x 2n+1 |
| `HIGHPASS3` | 3x3 |
| `SHARPEN3` | 3x3 |
| `SOBEL_X` | 3x3 |
| `SOBEL_Y` | 3x3 |
| `UNSHARP_MASK5` | 5x5 |
Furthermore, convolution supports striding (i.e. only processing every nth pixel
column/row) and pixel pooling (e.g. for ML applications). Available pooling
kernel presets (kernel sizes are configured independently):
| Kernel | Description |
|------------------------|--------------------|
| `POOL_MEAN` | Moving average |
| `POOL_MAX` | Local maximum |
| `POOL_MIN` | Local minimum |
| `POOL_NEAREST` | Nearest neighbor |
| `POOL_THRESHOLD(bias)` | Adaptive threshold |
Convolution can be applied to single, multiple or all channels of a
`FloatBuffer`. See
[`convolveChannel()`](https://docs.thi.ng/umbrella/pixel/modules.html#convolvechannel)
and
[`convolveImage()`](https://docs.thi.ng/umbrella/pixel/modules.html#convolveimage)
TODO add image & code example
### Normal map generation
Normal maps can be created via `normalMap()`. This function uses an adjustable
convolution kernel size to control gradient smoothness & details. Result X/Y
gradients can also be scaled (uniform or anisotropic) and the Z component can be
customized to (default: 1.0). The resulting image is in `FLOAT_NORMAL` format,
using signed channel values. This channel format is auto-translating these into
unsigned values when the image is converted into an integer format.
| Step | Scale = 1 | Scale = 2 | Scale = 4 | Scale = 8 |
|------|------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------|
| 0 | ![](https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/pixel/nmap-0-1.jpg) | ![](https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/pixel/nmap-0-2.jpg) | ![](https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/pixel/nmap-0-4.jpg) | ![](https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/pixel/nmap-0-8.jpg) |
| 1 | ![](https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/pixel/nmap-1-1.jpg) | ![](https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/pixel/nmap-1-2.jpg) | ![](https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/pixel/nmap-1-4.jpg) | ![](https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/pixel/nmap-1-8.jpg) |
| 2 | ![](https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/pixel/nmap-2-1.jpg) | ![](https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/pixel/nmap-2-2.jpg) | ![](https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/pixel/nmap-2-4.jpg) | ![](https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/pixel/nmap-2-8.jpg) |
| 3 | ![](https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/pixel/nmap-3-1.jpg) | ![](https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/pixel/nmap-3-2.jpg) | ![](https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/pixel/nmap-3-4.jpg) | ![](https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/pixel/nmap-3-8.jpg) |
```ts
import { floatBuffer, normalMap, FLOAT_GRAY, RGB888 } from "@thi.ng/pixel";
import { asPPM, read } from "@thi.ng/pixel-io-netpbm";
// read source image into a single channel floating point buffer
const src = floatBuffer(read(readFileSync("noise.pgm")), FLOAT_GRAY);
// create normal map (w/ default options)
const nmap = normalMap(src, { step: 0, scale: 1 });
// pixel lookup (vectors are stored _un_normalized)
nmap.getAt(10, 10);
// Float32Array(3) [ -0.019607841968536377, -0.04313725233078003, 1 ]
// save as 24bit PBM, conversion to RGB int format first
writeFileSync("noise-normal.ppm", asPPM(nmap.as(RGB888)));
```
### Status

@@ -145,3 +223,3 @@

Package sizes (gzipped, pre-treeshake): ESM: 5.35 KB / CJS: 5.57 KB / UMD: 5.52 KB
Package sizes (gzipped, pre-treeshake): ESM: 7.18 KB / CJS: 7.45 KB / UMD: 7.29 KB

@@ -148,0 +226,0 @@ ## Dependencies

@@ -32,2 +32,8 @@ import { Fn, Fn2, FnN, TypedArray, UIntArray } from "@thi.ng/api";

export declare const transformABGR: (pix: UIntArray, format: PackedFormat, fn: Fn<number, number>) => void;
/** @internal */
export declare const asVec: (x: number | [number, number]) => number[];
/** @internal */
export declare const asIntVec: (x: number | [number, number]) => number[];
/** @internal */
export declare const range: (n: number) => Uint8Array;
//# sourceMappingURL=utils.d.ts.map
import { assert } from "@thi.ng/api";
import { isNumber } from "@thi.ng/checks";
import { clamp } from "@thi.ng/math";

@@ -60,1 +61,10 @@ /** @internal */

};
/** @internal */
export const asVec = (x) => isNumber(x) ? [x, x] : x;
/** @internal */
export const asIntVec = (x) => {
const v = asVec(x);
return [v[0] | 0, v[1] | 0];
};
/** @internal */
export const range = (n) => new Uint8Array(n).map((_, i) => i);

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc