pixel-data-js 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,7 +11,7 @@ type BlendColor32 = (src: Color32, dst: Color32) => Color32;
11
11
  type ImageDataLike = {
12
12
  width: number;
13
13
  height: number;
14
- data: Uint8ClampedArray;
14
+ data: Uint8ClampedArray<ArrayBuffer>;
15
15
  };
16
16
  type SerializedImageData = {
17
17
  width: number;
@@ -21,103 +21,28 @@ type SerializedImageData = {
21
21
  type Base64EncodedUInt8Array = string & {
22
22
  readonly __brandBase64UInt8Array: unique symbol;
23
23
  };
24
-
25
- /**
26
- * Packs RGBA into a 32-bit integer compatible with
27
- * Little-Endian Uint32Array views on ImageData.
28
- */
29
- declare function packColor(r: number, g: number, b: number, a: number): Color32;
30
- declare function packRGBA({ r, g, b, a }: RGBA): Color32;
31
- declare const unpackRed: (packed: Color32) => number;
32
- declare const unpackGreen: (packed: Color32) => number;
33
- declare const unpackBlue: (packed: Color32) => number;
34
- declare const unpackAlpha: (packed: Color32) => number;
35
- declare function unpackColor(packed: Color32): RGBA;
36
- declare function unpackColorTo(packed: Color32, scratch?: RGBA): RGBA;
37
- declare function colorDistance(a: Color32, b: Color32): number;
38
- /**
39
- * Linearly interpolates between two 32-bit colors using a floating-point weight.
40
- * * This is the preferred method for UI animations or scenarios where high
41
- * precision is required. It uses the standard `a + t * (b - a)` formula
42
- * for each channel.
43
- * @param a - The starting color as a 32-bit integer (AABBGGRR).
44
- * @param b - The target color as a 32-bit integer (AABBGGRR).
45
- * @param t - The interpolation factor between 0.0 and 1.0.
46
- * @returns The interpolated 32-bit color.
47
- */
48
- declare function lerpColor32(a: Color32, b: Color32, t: number): Color32;
49
- /**
50
- * Linearly interpolates between two 32-bit colors using integer fixed-point math.
51
- * Highly optimized for image processing and real-time blitting. It processes
52
- * channels in parallel using bitmasks (RB and GA pairs).
53
- * @note Subject to a 1-bit drift (rounding down) due to fast bit-shift division.
54
- * @param src - The source (foreground) color as a 32-bit integer.
55
- * @param dst - The destination (background) color as a 32-bit integer.
56
- * @param w - The blend weight as a byte value from 0 to 255. Where 0 is 100% dst and 255 is 100% src
57
- * @returns The blended 32-bit color.
58
- */ declare function lerpColor32Fast(src: Color32, dst: Color32, w: number): Color32;
59
- declare function color32ToHex(color: Color32): string;
60
- /**
61
- * Converts a 32-bit integer (0xAABBGGRR) to a CSS rgba() string.
62
- * Example: 0xFF0000FF -> "rgba(255,0,0,1)"
63
- */
64
- declare function color32ToCssRGBA(color: Color32): string;
65
-
66
- declare function base64EncodeArrayBuffer(buffer: ArrayBufferLike): Base64EncodedUInt8Array;
67
- declare function base64DecodeArrayBuffer(encoded: Base64EncodedUInt8Array): Uint8ClampedArray;
68
- /**
69
- * Serialize for use in JSON. Pixel data is stored as base64 encoded string.
70
- */
71
- declare function serializeImageData<T extends ImageDataLike>(imageData: T): SerializedImageData;
72
- declare function serializeNullableImageData<T extends ImageDataLike | null>(imageData: T): T extends null ? null : SerializedImageData;
73
- declare function deserializeRawImageData<T extends SerializedImageData>(serialized: T): ImageDataLike;
74
- declare function deserializeImageData<T extends SerializedImageData>(serialized: T): ImageData;
75
- declare function deserializeNullableImageData<T extends SerializedImageData | null>(serialized: T): T extends null ? null : ImageData;
76
-
77
- type BlendImageDataOptions = {
78
- dx?: number;
79
- dy?: number;
80
- sx?: number;
81
- sy?: number;
82
- sw?: number;
83
- sh?: number;
84
- opacity?: number;
85
- alpha?: number;
86
- mask?: Uint8Array | null;
87
- maskMode?: 'binary' | 'alpha';
88
- blendFn?: BlendColor32;
24
+ type Rect = {
25
+ x: number;
26
+ y: number;
27
+ w: number;
28
+ h: number;
89
29
  };
90
- /**
91
- * Blits source ImageData into a destination ImageData using 32-bit integer bitwise blending.
92
- * * This function bypasses standard Canvas API limitations by operating directly on
93
- * Uint32Array views. It supports various blend modes, binary/alpha masking, and
94
- * automatic clipping of both source and destination bounds.
95
- * * @param dst - The destination ImageData to write into.
96
- * @param src - The source ImageData to read from.
97
- * @param dst - The destination ImageData to write to.
98
- * @param opts - Configuration for the blit operation.
99
- * @param opts.dx - Destination X offset. Defaults to 0.
100
- * @param opts.dy - Destination Y offset. Defaults to 0.
101
- * @param opts.sx - Source X offset. Defaults to 0.
102
- * @param opts.sy - Source Y offset. Defaults to 0.
103
- * @param opts.sw - Width of the source area to blit. Defaults to src.width.
104
- * @param opts.sh - Height of the source area to blit. Defaults to src.height.
105
- * @param opts.opacity - Global strength of the blit (0.0 to 1.0). Defaults to 1.0.
106
- * @param opts.alpha - Global strength of the blit (0 to 255). Overrides 'opacity' if provided.
107
- * @param opts.mask - An optional Uint8Array acting as a stencil or alpha mask.
108
- * Must match source dimensions.
109
- * @param opts.maskMode - 'binary' ignores pixels where mask is 0.
110
- * 'alpha' scales source alpha by mask value (0-255).
111
- * @param opts.blendFn - The math logic used to combine pixels.
112
- * Defaults to `sourceOverColor32`.
113
- * * @example
114
- * blendImageData32(ctx.getImageData(0,0,100,100), sprite, {
115
- * blendFn: COLOR_32_BLEND_MODES.multiply,
116
- * mask: brushMask,
117
- * maskMode: 'alpha'
118
- * });
119
- */
120
- declare function blendImageData(dst: ImageData, src: ImageData, opts: BlendImageDataOptions): void;
30
+ declare enum MaskType {
31
+ ALPHA = 0,
32
+ BINARY = 1
33
+ }
34
+ interface BaseMaskData {
35
+ readonly width: number;
36
+ readonly height: number;
37
+ readonly data: Uint8Array;
38
+ }
39
+ interface AlphaMask extends BaseMaskData {
40
+ readonly type: MaskType.ALPHA;
41
+ }
42
+ interface BinaryMask extends BaseMaskData {
43
+ readonly type: MaskType.BINARY;
44
+ }
45
+ type AnyMask = AlphaMask | BinaryMask;
121
46
 
122
47
  declare const sourceOverColor32: BlendColor32;
123
48
  /**
@@ -166,12 +91,182 @@ declare const COLOR_32_BLEND_MODES: {
166
91
  colorBurn: BlendColor32;
167
92
  };
168
93
 
94
+ type BlendImageDataOptions = {
95
+ /**
96
+ * The x-coordinate in the destination image where the blend begins.
97
+ * @default 0
98
+ */
99
+ dx?: number;
100
+ /**
101
+ * The y-coordinate in the destination image where the blend begins.
102
+ * @default 0
103
+ */
104
+ dy?: number;
105
+ /**
106
+ * The x-coordinate of the top-left corner of the sub-rectangle
107
+ * of the source image to extract.
108
+ * @default 0
109
+ */
110
+ sx?: number;
111
+ /**
112
+ * The y-coordinate of the top-left corner of the sub-rectangle
113
+ * of the source image to extract.
114
+ * @default 0
115
+ */
116
+ sy?: number;
117
+ /**
118
+ * The width of the sub-rectangle of the source image to extract.
119
+ * Defaults to the full remaining width of the source.
120
+ */
121
+ sw?: number;
122
+ /**
123
+ * The height of the sub-rectangle of the source image to extract.
124
+ * Defaults to the full remaining height of the source.
125
+ */
126
+ sh?: number;
127
+ /**
128
+ * Overall layer opacity, typically ranging from 0.0 (transparent) to 1.0 (opaque).
129
+ * @default 1.0
130
+ */
131
+ opacity?: number;
132
+ /**
133
+ * Same as opacity but is 0-255 and faster when processing. If Present opacity is ignored.
134
+ * @default undefined
135
+ */
136
+ alpha?: number;
137
+ /**
138
+ * An optional alpha mask buffer.
139
+ * The values in this array (0-255) determine the intensity of the blend
140
+ * at each corresponding pixel.
141
+ */
142
+ mask?: AnyMask | null;
143
+ /**
144
+ * The specific blending function/algorithm to use for pixel math
145
+ * (e.g., Multiply, Screen, Overlay).
146
+ */
147
+ blendFn?: BlendColor32;
148
+ };
149
+ /**
150
+ * Blits source ImageData into a destination ImageData using 32-bit integer bitwise blending.
151
+ * This function bypasses standard Canvas API limitations by operating directly on
152
+ * Uint32Array views. It supports various blend modes, binary/alpha masking, and
153
+ * automatic clipping of both source and destination bounds.
154
+ * @example
155
+ * blendImageData32(ctx.getImageData(0,0,100,100), sprite, {
156
+ * blendFn: COLOR_32_BLEND_MODES.multiply,
157
+ * mask: brushMask,
158
+ * maskMode: MaskMode.ALPHA
159
+ * });
160
+ */
161
+ declare function blendImageData(dst: ImageDataLike, src: ImageDataLike, opts: BlendImageDataOptions): void;
162
+
163
+ type ApplyMaskOptions = {
164
+ /**
165
+ * The x-coordinate in the destination image where the mask begins.
166
+ * @default 0
167
+ */
168
+ dx?: number;
169
+ /**
170
+ * The y-coordinate in the destination image where the mask begins.
171
+ * @default 0
172
+ */
173
+ dy?: number;
174
+ /**
175
+ * The x-coordinate of the top-left corner of the sub-rectangle
176
+ * of the source image to extract.
177
+ * @default 0
178
+ */
179
+ sx?: number;
180
+ /**
181
+ * The y-coordinate of the top-left corner of the sub-rectangle
182
+ * of the source image to extract.
183
+ * @default 0
184
+ */
185
+ sy?: number;
186
+ /**
187
+ * The width of the sub-rectangle of the source image to extract.
188
+ * Defaults to the full remaining width of the source.
189
+ */
190
+ sw?: number;
191
+ /**
192
+ * The height of the sub-rectangle of the source image to extract.
193
+ * Defaults to the full remaining height of the source.
194
+ */
195
+ sh?: number;
196
+ };
197
+ /**
198
+ * Applies a binary (on/off) mask to an RGBA buffer.
199
+ * If mask value is 0, pixel becomes transparent.
200
+ */
201
+ declare function applyBinaryMask(dst: ImageDataLike, mask: BinaryMask, opts?: ApplyMaskOptions): ImageDataLike | undefined;
202
+ /**
203
+ * Applies a smooth alpha mask to an RGBA buffer.
204
+ * Multiplies existing Alpha by (maskValue / 255).
205
+ */
206
+ declare function applyAlphaMask(dst: ImageData, mask: AlphaMask, opts?: ApplyMaskOptions): void;
207
+
169
208
  declare function makeImageDataColor32Adapter(imageData: ImageDataLike): {
170
209
  inBounds: (x: number, y: number) => boolean;
171
210
  imageData: ImageDataLike;
172
- data32: Uint32Array<ArrayBufferLike>;
211
+ data32: Uint32Array<ArrayBuffer>;
173
212
  setPixel: (x: number, y: number, color: Color32) => void;
174
213
  getPixel: (x: number, y: number) => Color32 | undefined;
175
214
  };
215
+ declare function extractPixelData(imageData: ImageDataLike, rect: Rect): Uint8ClampedArray;
216
+ declare function extractPixelData(imageData: ImageDataLike, x: number, y: number, w: number, h: number): Uint8ClampedArray;
217
+ declare function copyImageData({ data, width, height }: ImageDataLike): ImageData;
218
+ declare function copyImageDataLike({ data, width, height }: ImageDataLike): ImageDataLike;
219
+
220
+ declare function base64EncodeArrayBuffer(buffer: ArrayBufferLike): Base64EncodedUInt8Array;
221
+ declare function base64DecodeArrayBuffer(encoded: Base64EncodedUInt8Array): Uint8ClampedArray<ArrayBuffer>;
222
+ /**
223
+ * Serialize for use in JSON. Pixel data is stored as base64 encoded string.
224
+ */
225
+ declare function serializeImageData<T extends ImageDataLike>(imageData: T): SerializedImageData;
226
+ declare function serializeNullableImageData<T extends ImageDataLike | null>(imageData: T): T extends null ? null : SerializedImageData;
227
+ declare function deserializeRawImageData<T extends SerializedImageData>(serialized: T): ImageDataLike;
228
+ declare function deserializeImageData<T extends SerializedImageData>(serialized: T): ImageData;
229
+ declare function deserializeNullableImageData<T extends SerializedImageData | null>(serialized: T): T extends null ? null : ImageData;
230
+
231
+ /**
232
+ * Packs RGBA into a 32-bit integer compatible with
233
+ * Little-Endian Uint32Array views on ImageData.
234
+ */
235
+ declare function packColor(r: number, g: number, b: number, a: number): Color32;
236
+ declare function packRGBA({ r, g, b, a }: RGBA): Color32;
237
+ declare const unpackRed: (packed: Color32) => number;
238
+ declare const unpackGreen: (packed: Color32) => number;
239
+ declare const unpackBlue: (packed: Color32) => number;
240
+ declare const unpackAlpha: (packed: Color32) => number;
241
+ declare function unpackColor(packed: Color32): RGBA;
242
+ declare function unpackColorTo(packed: Color32, scratch?: RGBA): RGBA;
243
+ declare function colorDistance(a: Color32, b: Color32): number;
244
+ /**
245
+ * Linearly interpolates between two 32-bit colors using a floating-point weight.
246
+ * * This is the preferred method for UI animations or scenarios where high
247
+ * precision is required. It uses the standard `a + t * (b - a)` formula
248
+ * for each channel.
249
+ * @param a - The starting color as a 32-bit integer (AABBGGRR).
250
+ * @param b - The target color as a 32-bit integer (AABBGGRR).
251
+ * @param t - The interpolation factor between 0.0 and 1.0.
252
+ * @returns The interpolated 32-bit color.
253
+ */
254
+ declare function lerpColor32(a: Color32, b: Color32, t: number): Color32;
255
+ /**
256
+ * Linearly interpolates between two 32-bit colors using integer fixed-point math.
257
+ * Highly optimized for image processing and real-time blitting. It processes
258
+ * channels in parallel using bitmasks (RB and GA pairs).
259
+ * @note Subject to a 1-bit drift (rounding down) due to fast bit-shift division.
260
+ * @param src - The source (foreground) color as a 32-bit integer.
261
+ * @param dst - The destination (background) color as a 32-bit integer.
262
+ * @param w - The blend weight as a byte value from 0 to 255. Where 0 is 100% dst and 255 is 100% src
263
+ * @returns The blended 32-bit color.
264
+ */ declare function lerpColor32Fast(src: Color32, dst: Color32, w: number): Color32;
265
+ declare function color32ToHex(color: Color32): string;
266
+ /**
267
+ * Converts a 32-bit integer (0xAABBGGRR) to a CSS rgba() string.
268
+ * Example: 0xFF0000FF -> "rgba(255,0,0,1)"
269
+ */
270
+ declare function color32ToCssRGBA(color: Color32): string;
176
271
 
177
- export { type BlendImageDataOptions, COLOR_32_BLEND_MODES, base64DecodeArrayBuffer, base64EncodeArrayBuffer, blendImageData, color32ToCssRGBA, color32ToHex, colorBurnColor32, colorDistance, deserializeImageData, deserializeNullableImageData, deserializeRawImageData, differenceColor32, hardLightColor32, lerpColor32, lerpColor32Fast, linearDodgeColor32, makeImageDataColor32Adapter, multiplyColor32, overlayColor32, packColor, packRGBA, screenColor32, serializeImageData, serializeNullableImageData, sourceOverColor32, unpackAlpha, unpackBlue, unpackColor, unpackColorTo, unpackGreen, unpackRed };
272
+ export { type AlphaMask, type AnyMask, type ApplyMaskOptions, type Base64EncodedUInt8Array, type BinaryMask, type BlendColor32, type BlendImageDataOptions, COLOR_32_BLEND_MODES, type Color32, type ImageDataLike, MaskType, type RGBA, type Rect, type SerializedImageData, applyAlphaMask, applyBinaryMask, base64DecodeArrayBuffer, base64EncodeArrayBuffer, blendImageData, color32ToCssRGBA, color32ToHex, colorBurnColor32, colorDistance, copyImageData, copyImageDataLike, deserializeImageData, deserializeNullableImageData, deserializeRawImageData, differenceColor32, extractPixelData, hardLightColor32, lerpColor32, lerpColor32Fast, linearDodgeColor32, makeImageDataColor32Adapter, multiplyColor32, overlayColor32, packColor, packRGBA, screenColor32, serializeImageData, serializeNullableImageData, sourceOverColor32, unpackAlpha, unpackBlue, unpackColor, unpackColorTo, unpackGreen, unpackRed };
@@ -1,106 +1,3 @@
1
- // src/color.ts
2
- function packColor(r, g, b, a) {
3
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
4
- }
5
- function packRGBA({ r, g, b, a }) {
6
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
7
- }
8
- var unpackRed = (packed) => packed >>> 0 & 255;
9
- var unpackGreen = (packed) => packed >>> 8 & 255;
10
- var unpackBlue = (packed) => packed >>> 16 & 255;
11
- var unpackAlpha = (packed) => packed >>> 24 & 255;
12
- function unpackColor(packed) {
13
- return {
14
- r: packed >>> 0 & 255,
15
- g: packed >>> 8 & 255,
16
- b: packed >>> 16 & 255,
17
- a: packed >>> 24 & 255
18
- };
19
- }
20
- var SCRATCH_RGBA = { r: 0, g: 0, b: 0, a: 0 };
21
- function unpackColorTo(packed, scratch = SCRATCH_RGBA) {
22
- scratch.r = packed >>> 0 & 255;
23
- scratch.g = packed >>> 8 & 255;
24
- scratch.b = packed >>> 16 & 255;
25
- scratch.a = packed >>> 24 & 255;
26
- return scratch;
27
- }
28
- function colorDistance(a, b) {
29
- const dr = (a & 255) - (b & 255);
30
- const dg = (a >>> 8 & 255) - (b >>> 8 & 255);
31
- const db = (a >>> 16 & 255) - (b >>> 16 & 255);
32
- const da = (a >>> 24 & 255) - (b >>> 24 & 255);
33
- return dr * dr + dg * dg + db * db + da * da;
34
- }
35
- function lerpColor32(a, b, t) {
36
- const r = (a & 255) + t * ((b & 255) - (a & 255));
37
- const g = (a >>> 8 & 255) + t * ((b >>> 8 & 255) - (a >>> 8 & 255));
38
- const b_ = (a >>> 16 & 255) + t * ((b >>> 16 & 255) - (a >>> 16 & 255));
39
- const a_ = (a >>> 24 & 255) + t * ((b >>> 24 & 255) - (a >>> 24 & 255));
40
- return (a_ << 24 | b_ << 16 | g << 8 | r) >>> 0;
41
- }
42
- function lerpColor32Fast(src, dst, w) {
43
- const invA = 255 - w;
44
- const rb = (src & 16711935) * w + (dst & 16711935) * invA >>> 8 & 16711935;
45
- const ga = (src >>> 8 & 16711935) * w + (dst >>> 8 & 16711935) * invA >>> 8 & 16711935;
46
- return (rb | ga << 8) >>> 0;
47
- }
48
- function color32ToHex(color) {
49
- const r = (color & 255).toString(16).padStart(2, "0");
50
- const g = (color >>> 8 & 255).toString(16).padStart(2, "0");
51
- const b = (color >>> 16 & 255).toString(16).padStart(2, "0");
52
- const a = (color >>> 24 & 255).toString(16).padStart(2, "0");
53
- return `#${r}${g}${b}${a}`;
54
- }
55
- function color32ToCssRGBA(color) {
56
- const r = color & 255;
57
- const g = color >>> 8 & 255;
58
- const b = color >>> 16 & 255;
59
- const a = color >>> 24 & 255;
60
- const alpha = Number((a / 255).toFixed(3));
61
- return `rgba(${r},${g},${b},${alpha})`;
62
- }
63
-
64
- // src/ImageData/serialization.ts
65
- function base64EncodeArrayBuffer(buffer) {
66
- const binary = String.fromCharCode(...new Uint8Array(buffer));
67
- return btoa(binary);
68
- }
69
- function base64DecodeArrayBuffer(encoded) {
70
- const binary = atob(encoded);
71
- const bytes = new Uint8ClampedArray(binary.length);
72
- for (let i = 0; i < binary.length; i++) {
73
- bytes[i] = binary.charCodeAt(i);
74
- }
75
- return bytes;
76
- }
77
- function serializeImageData(imageData) {
78
- return {
79
- width: imageData.width,
80
- height: imageData.height,
81
- data: base64EncodeArrayBuffer(imageData.data.buffer)
82
- };
83
- }
84
- function serializeNullableImageData(imageData) {
85
- if (!imageData) return null;
86
- return serializeImageData(imageData);
87
- }
88
- function deserializeRawImageData(serialized) {
89
- return {
90
- width: serialized.width,
91
- height: serialized.height,
92
- data: base64DecodeArrayBuffer(serialized.data)
93
- };
94
- }
95
- function deserializeImageData(serialized) {
96
- const data = base64DecodeArrayBuffer(serialized.data);
97
- return new ImageData(data, serialized.width, serialized.height);
98
- }
99
- function deserializeNullableImageData(serialized) {
100
- if (!serialized) return null;
101
- return deserializeImageData(serialized);
102
- }
103
-
104
1
  // src/ImageData/blend-modes.ts
105
2
  var sourceOverColor32 = (src, dst) => {
106
3
  const a = src >>> 24 & 255;
@@ -237,6 +134,13 @@ var COLOR_32_BLEND_MODES = {
237
134
  colorBurn: colorBurnColor32
238
135
  };
239
136
 
137
+ // src/_types.ts
138
+ var MaskType = /* @__PURE__ */ ((MaskType2) => {
139
+ MaskType2[MaskType2["ALPHA"] = 0] = "ALPHA";
140
+ MaskType2[MaskType2["BINARY"] = 1] = "BINARY";
141
+ return MaskType2;
142
+ })(MaskType || {});
143
+
240
144
  // src/ImageData/blit.ts
241
145
  function blendImageData(dst, src, opts) {
242
146
  let {
@@ -246,7 +150,6 @@ function blendImageData(dst, src, opts) {
246
150
  sy = 0,
247
151
  sw = src.width,
248
152
  sh = src.height,
249
- maskMode = "alpha",
250
153
  opacity = 1,
251
154
  alpha,
252
155
  blendFn = sourceOverColor32,
@@ -282,7 +185,7 @@ function blendImageData(dst, src, opts) {
282
185
  const dw = dst.width;
283
186
  const sw_orig = src.width;
284
187
  const gAlpha = alpha !== void 0 ? alpha | 0 : Math.round(opacity * 255);
285
- const maskIsAlpha = maskMode === "alpha";
188
+ const maskIsAlpha = mask?.type === 0 /* ALPHA */;
286
189
  for (let iy = 0; iy < actualH; iy++) {
287
190
  const dRow = (iy + dy) * dw;
288
191
  const sRow = (iy + sy) * sw_orig;
@@ -294,7 +197,7 @@ function blendImageData(dst, src, opts) {
294
197
  if (sa === 0) continue;
295
198
  let activeWeight = gAlpha;
296
199
  if (mask) {
297
- const m = mask[si];
200
+ const m = mask.data[si];
298
201
  if (m === 0) continue;
299
202
  activeWeight = maskIsAlpha ? m * activeWeight + 128 >> 8 : activeWeight;
300
203
  }
@@ -308,6 +211,76 @@ function blendImageData(dst, src, opts) {
308
211
  }
309
212
  }
310
213
 
214
+ // src/ImageData/mask.ts
215
+ function applyBinaryMask(dst, mask, opts = {}) {
216
+ const { width: maskWidth, height: maskHeight } = mask;
217
+ const { dx = 0, dy = 0, sx = 0, sy = 0, sw = maskWidth, sh = maskHeight } = opts;
218
+ const x0 = Math.max(0, dx, dx + (0 - sx));
219
+ const y0 = Math.max(0, dy, dy + (0 - sy));
220
+ const x1 = Math.min(dst.width, dx + sw, dx + (maskWidth - sx));
221
+ const y1 = Math.min(dst.height, dy + sh, dy + (maskHeight - sy));
222
+ if (x1 <= x0 || y1 <= y0) return;
223
+ const { data: dstData, width: dstW } = dst;
224
+ for (let y = y0; y < y1; y++) {
225
+ const maskY = y - dy + sy;
226
+ const dstRowOffset = y * dstW * 4;
227
+ const maskRowOffset = maskY * maskWidth;
228
+ for (let x = x0; x < x1; x++) {
229
+ const maskX = x - dx + sx;
230
+ const mIdx = maskRowOffset + maskX;
231
+ if (mask.data[mIdx] === 0) {
232
+ const aIdx = dstRowOffset + x * 4 + 3;
233
+ dstData[aIdx] = 0;
234
+ }
235
+ }
236
+ }
237
+ return dst;
238
+ }
239
+ function applyAlphaMask(dst, mask, opts = {}) {
240
+ let { dx = 0, dy = 0, sx = 0, sy = 0, sw = mask.width, sh = mask.height } = opts;
241
+ if (dx < 0) {
242
+ sx -= dx;
243
+ sw += dx;
244
+ dx = 0;
245
+ }
246
+ if (dy < 0) {
247
+ sy -= dy;
248
+ sh += dy;
249
+ dy = 0;
250
+ }
251
+ if (sx < 0) {
252
+ dx -= sx;
253
+ sw += sx;
254
+ sx = 0;
255
+ }
256
+ if (sy < 0) {
257
+ dy -= sy;
258
+ sh += sy;
259
+ sy = 0;
260
+ }
261
+ const actualW = Math.min(sw, dst.width - dx, mask.width - sx);
262
+ const actualH = Math.min(sh, dst.height - dy, mask.height - sy);
263
+ if (actualW <= 0 || actualH <= 0) return;
264
+ const dData = dst.data;
265
+ const mData = mask.data;
266
+ const dW = dst.width;
267
+ const mW = mask.width;
268
+ for (let y = 0; y < actualH; y++) {
269
+ const dOffset = (dy + y) * dW + dx << 2;
270
+ const mOffset = (sy + y) * mW + sx;
271
+ for (let x = 0; x < actualW; x++) {
272
+ const mVal = mData[mOffset + x];
273
+ if (mVal === 255) continue;
274
+ const aIdx = dOffset + (x << 2) + 3;
275
+ if (mVal === 0) {
276
+ dData[aIdx] = 0;
277
+ continue;
278
+ }
279
+ dData[aIdx] = dData[aIdx] * mVal + 257 >> 8;
280
+ }
281
+ }
282
+ }
283
+
311
284
  // src/ImageData/read-write-pixels.ts
312
285
  function makeImageDataColor32Adapter(imageData) {
313
286
  const data32 = new Uint32Array(imageData.data.buffer);
@@ -330,8 +303,145 @@ function makeImageDataColor32Adapter(imageData) {
330
303
  getPixel
331
304
  };
332
305
  }
306
+ function extractPixelData(imageData, _x, _y, _w, _h) {
307
+ const { x, y, w, h } = typeof _x === "object" ? _x : { x: _x, y: _y, w: _w, h: _h };
308
+ const { width: srcW, height: srcH, data: src } = imageData;
309
+ if (w <= 0 || h <= 0) return new Uint8ClampedArray(0);
310
+ const out = new Uint8ClampedArray(w * h * 4);
311
+ const x0 = Math.max(0, x);
312
+ const y0 = Math.max(0, y);
313
+ const x1 = Math.min(srcW, x + w);
314
+ const y1 = Math.min(srcH, y + h);
315
+ if (x1 <= x0 || y1 <= y0) return out;
316
+ for (let row = 0; row < y1 - y0; row++) {
317
+ const srcRow = y0 + row;
318
+ const srcStart = (srcRow * srcW + x0) * 4;
319
+ const rowLen = (x1 - x0) * 4;
320
+ const dstRow = y0 - y + row;
321
+ const dstCol = x0 - x;
322
+ const dstStart = (dstRow * w + dstCol) * 4;
323
+ out.set(src.subarray(srcStart, srcStart + rowLen), dstStart);
324
+ }
325
+ return out;
326
+ }
327
+ function copyImageData({ data, width, height }) {
328
+ return new ImageData(data.slice(), width, height);
329
+ }
330
+ function copyImageDataLike({ data, width, height }) {
331
+ return {
332
+ data: data.slice(),
333
+ width,
334
+ height
335
+ };
336
+ }
337
+
338
+ // src/ImageData/serialization.ts
339
+ function base64EncodeArrayBuffer(buffer) {
340
+ const binary = String.fromCharCode(...new Uint8Array(buffer));
341
+ return btoa(binary);
342
+ }
343
+ function base64DecodeArrayBuffer(encoded) {
344
+ const binary = atob(encoded);
345
+ const bytes = new Uint8ClampedArray(binary.length);
346
+ for (let i = 0; i < binary.length; i++) {
347
+ bytes[i] = binary.charCodeAt(i);
348
+ }
349
+ return bytes;
350
+ }
351
+ function serializeImageData(imageData) {
352
+ return {
353
+ width: imageData.width,
354
+ height: imageData.height,
355
+ data: base64EncodeArrayBuffer(imageData.data.buffer)
356
+ };
357
+ }
358
+ function serializeNullableImageData(imageData) {
359
+ if (!imageData) return null;
360
+ return serializeImageData(imageData);
361
+ }
362
+ function deserializeRawImageData(serialized) {
363
+ return {
364
+ width: serialized.width,
365
+ height: serialized.height,
366
+ data: base64DecodeArrayBuffer(serialized.data)
367
+ };
368
+ }
369
+ function deserializeImageData(serialized) {
370
+ const data = base64DecodeArrayBuffer(serialized.data);
371
+ return new ImageData(data, serialized.width, serialized.height);
372
+ }
373
+ function deserializeNullableImageData(serialized) {
374
+ if (!serialized) return null;
375
+ return deserializeImageData(serialized);
376
+ }
377
+
378
+ // src/color.ts
379
+ function packColor(r, g, b, a) {
380
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
381
+ }
382
+ function packRGBA({ r, g, b, a }) {
383
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
384
+ }
385
+ var unpackRed = (packed) => packed >>> 0 & 255;
386
+ var unpackGreen = (packed) => packed >>> 8 & 255;
387
+ var unpackBlue = (packed) => packed >>> 16 & 255;
388
+ var unpackAlpha = (packed) => packed >>> 24 & 255;
389
+ function unpackColor(packed) {
390
+ return {
391
+ r: packed >>> 0 & 255,
392
+ g: packed >>> 8 & 255,
393
+ b: packed >>> 16 & 255,
394
+ a: packed >>> 24 & 255
395
+ };
396
+ }
397
+ var SCRATCH_RGBA = { r: 0, g: 0, b: 0, a: 0 };
398
+ function unpackColorTo(packed, scratch = SCRATCH_RGBA) {
399
+ scratch.r = packed >>> 0 & 255;
400
+ scratch.g = packed >>> 8 & 255;
401
+ scratch.b = packed >>> 16 & 255;
402
+ scratch.a = packed >>> 24 & 255;
403
+ return scratch;
404
+ }
405
+ function colorDistance(a, b) {
406
+ const dr = (a & 255) - (b & 255);
407
+ const dg = (a >>> 8 & 255) - (b >>> 8 & 255);
408
+ const db = (a >>> 16 & 255) - (b >>> 16 & 255);
409
+ const da = (a >>> 24 & 255) - (b >>> 24 & 255);
410
+ return dr * dr + dg * dg + db * db + da * da;
411
+ }
412
+ function lerpColor32(a, b, t) {
413
+ const r = (a & 255) + t * ((b & 255) - (a & 255));
414
+ const g = (a >>> 8 & 255) + t * ((b >>> 8 & 255) - (a >>> 8 & 255));
415
+ const b_ = (a >>> 16 & 255) + t * ((b >>> 16 & 255) - (a >>> 16 & 255));
416
+ const a_ = (a >>> 24 & 255) + t * ((b >>> 24 & 255) - (a >>> 24 & 255));
417
+ return (a_ << 24 | b_ << 16 | g << 8 | r) >>> 0;
418
+ }
419
+ function lerpColor32Fast(src, dst, w) {
420
+ const invA = 255 - w;
421
+ const rb = (src & 16711935) * w + (dst & 16711935) * invA >>> 8 & 16711935;
422
+ const ga = (src >>> 8 & 16711935) * w + (dst >>> 8 & 16711935) * invA >>> 8 & 16711935;
423
+ return (rb | ga << 8) >>> 0;
424
+ }
425
+ function color32ToHex(color) {
426
+ const r = (color & 255).toString(16).padStart(2, "0");
427
+ const g = (color >>> 8 & 255).toString(16).padStart(2, "0");
428
+ const b = (color >>> 16 & 255).toString(16).padStart(2, "0");
429
+ const a = (color >>> 24 & 255).toString(16).padStart(2, "0");
430
+ return `#${r}${g}${b}${a}`;
431
+ }
432
+ function color32ToCssRGBA(color) {
433
+ const r = color & 255;
434
+ const g = color >>> 8 & 255;
435
+ const b = color >>> 16 & 255;
436
+ const a = color >>> 24 & 255;
437
+ const alpha = Number((a / 255).toFixed(3));
438
+ return `rgba(${r},${g},${b},${alpha})`;
439
+ }
333
440
  export {
334
441
  COLOR_32_BLEND_MODES,
442
+ MaskType,
443
+ applyAlphaMask,
444
+ applyBinaryMask,
335
445
  base64DecodeArrayBuffer,
336
446
  base64EncodeArrayBuffer,
337
447
  blendImageData,
@@ -339,10 +449,13 @@ export {
339
449
  color32ToHex,
340
450
  colorBurnColor32,
341
451
  colorDistance,
452
+ copyImageData,
453
+ copyImageDataLike,
342
454
  deserializeImageData,
343
455
  deserializeNullableImageData,
344
456
  deserializeRawImageData,
345
457
  differenceColor32,
458
+ extractPixelData,
346
459
  hardLightColor32,
347
460
  lerpColor32,
348
461
  lerpColor32Fast,