This project is part of the @thi.ng/umbrella monorepo.
Typed array backed, packed integer and unpacked floating point pixel buffers w/ customizable formats, blitting, dithering, conversions.
- Buffer creation from HTML image elements w/ opt resize & format conversion (browser only)
- Buffer-to-buffer blitting w/ automatic format conversion
- Buffer-to-canvas blitting
- Buffer-to-buffer blending w/ Porter-Duff operators
- Pre/post-multiply alpha
- Region / sub-image extraction
- Single-channel manipulation / extraction / replacement / conversion
- Inversion
- XY pixel accessors
- 10 packed integer and 4 floating point preset formats (see table below)
- Ordered dithering w/ customizable Bayer matrix size and target color steps (int formats only)
- Declarative custom format & optimized code generation
- HTML canvas creation &
ImageData
utilities
- Accessors for normalized channel value
- Pre/Post-multipy (only if alpha is available)
- Re-add strided float buffers / formats
- Dithering
- Readonly texture sampling abstraction
- Wrap-around behaviors
- Filtered access (bilinear interpolation)
All packed integer formats use the canvas native ABGR 32bit format as common intermediate for conversions. During conversion to ABGR, channels with sizes smaller than 8 bits will be scaled appropriately to ensure an as full-range and as linear as possible mapping. E.g. a 4 bit channel will be scaled by 255 / 15 = 17.
Format specs can freely control channel layout within current limits:
- Channel sizes: 1 - 32 bits.
- Storage: 8, 16 or 32 bits per pixel
New formats can be defined via defPackedFormat()
.
Format ID | Bits per pixel | Description |
---|---|---|
ALPHA8 |
8 | 8 bit channel (alpha only) |
GRAY8 |
8 | 8 bit single channel (grayscale conv) |
GRAY_ALPHA8 |
16 | 8 bit single channel (grayscale conv), 8 bit alpha |
GRAY16 |
16 | 16 bit single channel (grayscale conv) |
GRAY_ALPHA16 |
32 | 16 bit single channel (grayscale conv), 16 bit alpha |
ARGB4444 |
16 | 4 channels @ 4 bits each |
ARGB1555 |
16 | 5 bits each for RGB, 1 bit alpha |
RGB565 |
16 | 5 bits red, 6 bits green, 5 bits blue |
RGB888 |
32 (24 effective) | 3 channels @ 8 bits each |
ARGB8888 |
32 | 4 channels @ 8 bits each |
BGR888 |
32 (24 effective) | 3 channels @ 8 bits each |
ABGR8888 |
32 | 4 channels @ 8 bits each |
ALPHA8
is mapped from/to ABGR alpha channelGRAY8/16
,GRAY_ALPHA8/16
compute grayscale/luminance when converting from ABGR and in return produce grayscale ABGR- In all built-in formats supporting it, the alpha channel always occupies the most-significant bits (up to format size)
Strided floating point format presets for use with floatBuffer()
. New
formats can be defined via defFloatFormat()
.
Format ID | Channel count | Description |
---|---|---|
FLOAT_GRAY |
1 | Single channel / grayscale |
FLOAT_GRAY_ALPHA |
2 | Grayscale and alpha channel |
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) - Conversion between float formats is currently unsupported
STABLE - used in production
- @thi.ng/porter-duff - Porter-Duff operators for packed ints & float-array alpha compositing
yarn add @thi.ng/pixel
// ES module
<script type="module" src="https://unpkg.com/@thi.ng/pixel?module" crossorigin></script>
// UMD
<script src="https://unpkg.com/@thi.ng/pixel/lib/index.umd.js" crossorigin></script>
Package sizes (gzipped, pre-treeshake): ESM: 4.78 KB / CJS: 4.96 KB / UMD: 4.88 KB
Several demos in this repo's /examples directory are using this package.
A selection:
Screenshot | Description | Live demo | Source |
---|---|---|---|
Interactive image processing (adaptive threshold) | Demo | Source | |
Pixel buffer manipulations | Demo | Source | |
Port-Duff image compositing / alpha blending | Demo | Source | |
Fork-join worker-based raymarch renderer | Demo | Source | |
Minimal multi-pass / GPGPU example | Demo | Source |
import * as pix from "@thi.ng/pixel";
import { SRC_OVER_I } from "@thi.ng/porter-duff";
import IMG from "../assets/haystack.jpg";
import LOGO from "../assets/logo-64.png";
Promise
.all([IMG, LOGO].map(pix.imagePromise))
.then(([img, logo]) => {
// init 16 bit packed RGB pixel buffer from image (resized to 256x256)
const buf = pix.PackedBuffer.fromImage(img, pix.RGB565, 256, 256);
// create grayscale buffer for logo and use Porter-Duff operator to
// composite with main image. Since the logo has transparency, we need
// to premultiply alpha first...
pix.PackedBuffer.fromImage(logo, pix.GRAY_ALPHA88)
.premultiply()
.blend(SRC_OVER_I, buf, {
dx: 10,
dy: 10
});
// extract sub-image
const region = buf.getRegion(32, 96, 128, 64);
// copy region back at new position
region.blit(buf, { dx: 96, dy: 32 });
// or alternatively blit buf into itself
// buf.blit(buf, { dx: 96, dy: 32, sx: 32, sy: 96, w: 128, h: 64 });
// create html canvas
// (returns obj of canvas & 2d context)
const ctx = pix.canvas2d(buf.width, buf.height * 3);
// write pixel buffer to canvas
buf.blitCanvas(ctx.canvas);
// manipulate single color channel (here red)
const id = 0;
// obtain channel & invert
const ch = buf.getChannel(id).invert();
// create dot pattern
for (let y = 0; y < ch.height; y += 2) {
for (let x = (y >> 1) & 1; x < ch.width; x += 2) {
ch.setAt(x, y, 0xff);
}
}
// replace original channel
buf.setChannel(id, ch);
// write pixel buffer to new position
buf.blitCanvas(ctx.canvas, 0, buf.height);
// create & write grayscale version
buf.as(GRAY8).blitCanvas(ctx.canvas, 0, buf.height * 2);
document.body.appendChild(ctx.canvas);
});
TODO see examples & source comments for now
Karsten Schmidt
© 2019 - 2020 Karsten Schmidt // Apache Software License 2.0