thi-ng/umbrella

View on GitHub
packages/pixel/src/internal/utils.ts

Summary

Maintainability
A
50 mins
Test Coverage
// thing:export
import type { Fn, Fn2, FnN, UIntArray } from "@thi.ng/api";
import { isNumber } from "@thi.ng/checks/is-number";
import { clamp } from "@thi.ng/math/interval";
import type {
    BlitCanvasOpts,
    BlitOpts,
    IntFormat,
    IPixelBuffer,
    IToImageData,
} from "../api.js";

export const __luminanceABGR: FnN = (c) =>
    (((c >>> 16) & 0xff) * 29 + ((c >>> 8) & 0xff) * 150 + (c & 0xff) * 76) /
    255;

/** @internal */
export const __clampRegion = (
    sx: number,
    sy: number,
    w: number,
    h: number,
    maxw: number,
    maxh: number,
    dx = 0,
    dy = 0
) => {
    sx |= 0;
    sy |= 0;
    w |= 0;
    h |= 0;
    sx < 0 && ((w += sx), (dx -= sx), (sx = 0));
    sy < 0 && ((h += sy), (dy -= sy), (sy = 0));
    return [sx, sy, clamp(w, 0, maxw - sx), clamp(h, 0, maxh - sy), dx, dy];
};

/** @internal */
export const __prepRegions = (
    src: { width: number; height: number },
    dest: { width: number; height: number },
    opts: Partial<BlitOpts> = {}
) => {
    const sw = src.width;
    const dw = dest.width;
    let sx: number, sy: number;
    let dx: number, dy: number;
    let rw: number, rh: number;
    [sx, sy, rw, rh] = __clampRegion(
        opts.sx || 0,
        opts.sy || 0,
        opts.w || sw,
        opts.h || src.height,
        sw,
        src.height
    );
    [dx, dy, rw, rh, sx, sy] = __clampRegion(
        opts.dx || 0,
        opts.dy || 0,
        rw,
        rh,
        dw,
        dest.height,
        sx,
        sy
    );
    return { sx, sy, dx, dy, rw, rh };
};

/** @internal */
export const __setChannelUni = (
    dbuf: UIntArray,
    src: number,
    set: Fn2<number, number, number>
) => {
    for (let i = dbuf.length; i-- > 0; ) {
        dbuf[i] = set(dbuf[i], src);
    }
};

/** @internal */
export const __setChannelSame = (
    dbuf: UIntArray,
    sbuf: UIntArray,
    get: Fn<number, number>,
    set: Fn2<number, number, number>
) => {
    for (let i = dbuf.length; i-- > 0; ) {
        dbuf[i] = set(dbuf[i], get(sbuf[i]));
    }
};

/** @internal */
export const __setChannelConvert = (
    dbuf: UIntArray,
    sbuf: UIntArray,
    from: Fn<number, number>,
    sto: Fn<number, number>,
    mask: number
) => {
    const invMask = ~mask;
    for (let i = dbuf.length; i-- > 0; ) {
        dbuf[i] = (dbuf[i] & invMask) | (from(sto(sbuf[i])) & mask);
    }
};

export const __transformABGR = (
    pix: UIntArray,
    format: IntFormat,
    fn: Fn<number, number>
) => {
    const from = format.fromABGR;
    const to = format.toABGR;
    for (let i = pix.length; i-- > 0; ) {
        pix[i] = from(fn(to(pix[i])));
    }
};

/** @internal */
export const __asVec = (x: number | [number, number]) =>
    isNumber(x) ? [x, x] : x;

/** @internal */
export const __asIntVec = (x: number | [number, number]) => {
    const v = __asVec(x);
    return [v[0] | 0, v[1] | 0];
};

/**
 * Swaps bytes lanes 1 & 3 (i.e. bits 16-23 with bits 0-7)
 *
 * @remarks
 * Extracted from thi.ng/binary to avoid dependency
 *
 * @param x -
 *
 * @internal
 */
export const __swapLane13: FnN = (x) =>
    ((x & 0xff) << 16) | ((x >> 16) & 0xff) | (x & 0xff00ff00);

/**
 * Shared implementation for {@link IBlit.blitCanvas}.
 *
 * @internal
 */
export const __blitCanvas = (
    buf: IPixelBuffer & IToImageData,
    canvas:
        | HTMLCanvasElement
        | CanvasRenderingContext2D
        | OffscreenCanvas
        | OffscreenCanvasRenderingContext2D,
    opts: Partial<BlitCanvasOpts> = {}
) =>
    (<CanvasImageData>(
        (canvas instanceof HTMLCanvasElement ||
        canvas instanceof OffscreenCanvas
            ? canvas.getContext("2d")!
            : canvas)
    )).putImageData(buf.toImageData(opts.data), opts.x || 0, opts.y || 0);