diff --git a/assets/pixel/resize-bicubic.jpg b/assets/pixel/resize-bicubic.jpg
index feef0fb00a..567c1c8842 100644
Binary files a/assets/pixel/resize-bicubic.jpg and b/assets/pixel/resize-bicubic.jpg differ
diff --git a/assets/pixel/resize-bilinear.jpg b/assets/pixel/resize-bilinear.jpg
index 229ff722dc..bf83cfbe93 100644
Binary files a/assets/pixel/resize-bilinear.jpg and b/assets/pixel/resize-bilinear.jpg differ
diff --git a/assets/pixel/resize-nearest.png b/assets/pixel/resize-nearest.png
index 6bd0ddc79f..173951531a 100644
Binary files a/assets/pixel/resize-nearest.png and b/assets/pixel/resize-nearest.png differ
diff --git a/packages/pixel/README.md b/packages/pixel/README.md
index 865991f80d..fcdb49abf7 100644
--- a/packages/pixel/README.md
+++ b/packages/pixel/README.md
@@ -150,7 +150,7 @@ sampler(-1.1, 0.5).toString(16)
// 'ff79643a'
// resize image to 1024x256 using bicubic sampling
-const img = resize(src, 1024, 256, "cubic");
+const img = src.resize(1024, 256, "cubic");
```
| Filter | |
@@ -259,7 +259,7 @@ yarn add @thi.ng/pixel
```
-Package sizes (gzipped, pre-treeshake): ESM: 8.17 KB / CJS: 8.45 KB / UMD: 8.20 KB
+Package sizes (gzipped, pre-treeshake): ESM: 8.19 KB / CJS: 8.47 KB / UMD: 8.22 KB
## Dependencies
diff --git a/packages/pixel/src/api.ts b/packages/pixel/src/api.ts
index ec45a3e086..65ed60b379 100644
--- a/packages/pixel/src/api.ts
+++ b/packages/pixel/src/api.ts
@@ -4,6 +4,7 @@ import type {
Fn2,
Fn3,
FnN,
+ FnU2,
IObjectOf,
NumericArray,
TypedArray,
@@ -439,3 +440,7 @@ export interface NormalMapOpts {
*/
z: number;
}
+
+export type IntSampler = FnU2;
+
+export type FloatSampler = FnU2;
diff --git a/packages/pixel/src/index.ts b/packages/pixel/src/index.ts
index 5c7acda926..7f23051821 100644
--- a/packages/pixel/src/index.ts
+++ b/packages/pixel/src/index.ts
@@ -6,7 +6,6 @@ export * from "./dither";
export * from "./float";
export * from "./normal-map";
export * from "./packed";
-export * from "./resize";
export * from "./sample";
export * from "./utils";
diff --git a/packages/pixel/src/packed.ts b/packages/pixel/src/packed.ts
index f5434adbed..5bf24c0dfb 100644
--- a/packages/pixel/src/packed.ts
+++ b/packages/pixel/src/packed.ts
@@ -1,4 +1,5 @@
import {
+ assert,
Fn,
ICopy,
IEmpty,
@@ -6,7 +7,7 @@ import {
UIntArray,
uintTypeForBits,
} from "@thi.ng/api";
-import { isNumber } from "@thi.ng/checks";
+import { isNumber, isString } from "@thi.ng/checks";
import {
isPremultipliedInt,
postmultiplyInt,
@@ -17,6 +18,8 @@ import {
BayerSize,
BlendFnInt,
BlitOpts,
+ Filter,
+ IntSampler,
IPixelBuffer,
Lane,
PackedChannel,
@@ -28,6 +31,7 @@ import { compileGrayFromABGR, compileGrayToABGR } from "./codegen";
import { defBayer } from "./dither";
import { ABGR8888 } from "./format/abgr8888";
import { defPackedFormat } from "./format/packed-format";
+import { defSampler } from "./sample";
import {
clampRegion,
ensureChannel,
@@ -425,27 +429,32 @@ export class PackedBuffer
}
/**
- * Returns new buffer of downscaled version (by given integer factor) using
- * simple nearest neighbor sampling.
+ * Returns scaled version of this buffer using given sampler or filter
+ * (default: `"linear"`) for interpolation. Syntax sugar for
+ * {@link PackedBuffer.resize}.
*
- * @param res
+ * @param scale
*/
- downsample(res: number) {
- res |= 0;
- const { width, height, pixels: sbuf } = this;
- const dest = new PackedBuffer(
- (width / res) | 0,
- (height / res) | 0,
- this.format
- );
- const { width: dwidth, height: dheight, pixels: dbuf } = dest;
- for (let y = 0, i = 0; y < dheight; y++) {
- for (
- let x = 0, j = y * res * width;
- x < dwidth;
- x++, i++, j += res
- ) {
- dbuf[i] = sbuf[j];
+ scale(scale: number, sampler: IntSampler | Filter = "linear") {
+ assert(scale > 0, `scale must be > 0`);
+ return this.resize(this.width * scale, this.height * scale, sampler);
+ }
+
+ resize(w: number, h: number, sampler: IntSampler | Filter = "linear") {
+ w |= 0;
+ h |= 0;
+ assert(w > 0 && h > 0, `target width & height must be > 0`);
+ const dest = packedBuffer(w, h, this.format);
+ const dpix = dest.pixels;
+ const scaleX = w > 0 ? this.width / w : 0;
+ const scaleY = h > 0 ? this.height / h : 0;
+ sampler = isString(sampler)
+ ? defSampler(this, sampler, "repeat")
+ : sampler;
+ for (let y = 0, i = 0; y < h; y++) {
+ const yy = y * scaleY;
+ for (let x = 0; x < w; x++, i++) {
+ dpix[i] = sampler(x * scaleX, yy);
}
}
return dest;
diff --git a/packages/pixel/src/resize.ts b/packages/pixel/src/resize.ts
deleted file mode 100644
index 64df311f74..0000000000
--- a/packages/pixel/src/resize.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import type { Filter } from "./api";
-import { packedBuffer, PackedBuffer } from "./packed";
-import { defSampler } from "./sample";
-
-export const resize = (
- src: PackedBuffer,
- w: number,
- h: number,
- filter: Filter = "linear"
-) => {
- const dest = packedBuffer(w, h, src.format);
- const sample = defSampler(src, filter, "repeat");
- const scaleX = w > 0 ? src.width / w : 0;
- const scaleY = h > 0 ? src.height / h : 0;
- for (let y = 0, i = 0; y < h; y++) {
- const yy = y * scaleY;
- for (let x = 0; x < w; x++, i++) {
- dest.pixels[i] = sample(x * scaleX, yy);
- }
- }
- return dest;
-};
diff --git a/packages/pixel/src/sample.ts b/packages/pixel/src/sample.ts
index 0eaeca9bd0..4a7e01f57a 100644
--- a/packages/pixel/src/sample.ts
+++ b/packages/pixel/src/sample.ts
@@ -1,19 +1,15 @@
-import {
- assert,
- FloatArray,
- Fn,
- FnU2,
- IObjectOf,
- NumericArray,
-} from "@thi.ng/api";
-import { clamp, fmod, fract, mixBilinear, mixBicubic } from "@thi.ng/math";
-import type { Filter, IPixelBuffer, Wrap } from "./api";
+import { assert, Fn, IObjectOf, NumericArray } from "@thi.ng/api";
+import { clamp, fmod, fract, mixBicubic, mixBilinear } from "@thi.ng/math";
+import type {
+ Filter,
+ FloatSampler,
+ IntSampler,
+ IPixelBuffer,
+ Wrap,
+} from "./api";
import type { FloatBuffer } from "./float";
import type { PackedBuffer } from "./packed";
-type IntSampler = FnU2;
-type FloatSampler = FnU2;
-
export function defSampler(
src: PackedBuffer,
filter?: Filter,
@@ -188,7 +184,9 @@ const mixBicubicChan = (
u: number,
v: number,
i: number,
- s = 4
+ s = 4,
+ min = 0,
+ max = 255
) =>
clamp(
mixBicubic(
@@ -211,14 +209,14 @@ const mixBicubicChan = (
u,
v
),
- 0,
- 255
+ min,
+ max
);
const bicubicABGR = (src: PackedBuffer, sample: IntSampler): IntSampler => {
const { fromABGR, toABGR } = src.format;
- const buf32 = new Uint32Array(16);
- const buf8 = new Uint8Array(buf32.buffer);
+ const u32 = new Uint32Array(16);
+ const u8 = new Uint8Array(u32.buffer);
return (x, y) => {
x -= 0.5;
y -= 0.5;
@@ -230,28 +228,28 @@ const bicubicABGR = (src: PackedBuffer, sample: IntSampler): IntSampler => {
const y3 = y + 2;
const u = fract(x);
const v = fract(y);
- buf32[0] = toABGR(sample(x1, y1));
- buf32[1] = toABGR(sample(x, y1));
- buf32[2] = toABGR(sample(x2, y1));
- buf32[3] = toABGR(sample(x3, y1));
- buf32[4] = toABGR(sample(x1, y));
- buf32[5] = toABGR(sample(x, y));
- buf32[6] = toABGR(sample(x2, y));
- buf32[7] = toABGR(sample(x3, y));
- buf32[8] = toABGR(sample(x1, y2));
- buf32[9] = toABGR(sample(x, y2));
- buf32[10] = toABGR(sample(x2, y2));
- buf32[11] = toABGR(sample(x3, y2));
- buf32[12] = toABGR(sample(x1, y3));
- buf32[13] = toABGR(sample(x, y3));
- buf32[14] = toABGR(sample(x2, y3));
- buf32[15] = toABGR(sample(x3, y3));
+ u32[0] = toABGR(sample(x1, y1));
+ u32[1] = toABGR(sample(x, y1));
+ u32[2] = toABGR(sample(x2, y1));
+ u32[3] = toABGR(sample(x3, y1));
+ u32[4] = toABGR(sample(x1, y));
+ u32[5] = toABGR(sample(x, y));
+ u32[6] = toABGR(sample(x2, y));
+ u32[7] = toABGR(sample(x3, y));
+ u32[8] = toABGR(sample(x1, y2));
+ u32[9] = toABGR(sample(x, y2));
+ u32[10] = toABGR(sample(x2, y2));
+ u32[11] = toABGR(sample(x3, y2));
+ u32[12] = toABGR(sample(x1, y3));
+ u32[13] = toABGR(sample(x, y3));
+ u32[14] = toABGR(sample(x2, y3));
+ u32[15] = toABGR(sample(x3, y3));
return (
fromABGR(
- (mixBicubicChan(buf8, u, v, 3) << 24) |
- (mixBicubicChan(buf8, u, v, 2) << 16) |
- (mixBicubicChan(buf8, u, v, 1) << 8) |
- mixBicubicChan(buf8, u, v, 0)
+ mixBicubicChan(u8, u, v, 0) |
+ (mixBicubicChan(u8, u, v, 1) << 8) |
+ (mixBicubicChan(u8, u, v, 2) << 16) |
+ (mixBicubicChan(u8, u, v, 3) << 24)
) >>> 0
);
};
diff --git a/packages/pixel/tpl.readme.md b/packages/pixel/tpl.readme.md
index 010b563013..5d2b655a51 100644
--- a/packages/pixel/tpl.readme.md
+++ b/packages/pixel/tpl.readme.md
@@ -131,7 +131,7 @@ sampler(-1.1, 0.5).toString(16)
// 'ff79643a'
// resize image to 1024x256 using bicubic sampling
-const img = resize(src, 1024, 256, "cubic");
+const img = src.resize(1024, 256, "cubic");
```
| Filter | |