pixel-data-js 0.3.0 → 0.4.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.
@@ -243,6 +243,29 @@ declare function deserializeRawImageData<T extends SerializedImageData>(serializ
243
243
  declare function deserializeImageData<T extends SerializedImageData>(serialized: T): ImageData;
244
244
  declare function deserializeNullableImageData<T extends SerializedImageData | null>(serialized: T): T extends null ? null : ImageData;
245
245
 
246
+ declare function writeImageData(imageData: ImageData, data: Uint8ClampedArray, rect: Rect): void;
247
+ declare function writeImageData(imageData: ImageData, data: Uint8ClampedArray, x: number, y: number, w: number, h: number): void;
248
+
249
+ /**
250
+ * Creates a new copy of a mask.
251
+ * Uses the underlying buffer's slice method for high-performance memory copying.
252
+ */
253
+ declare function copyMask<T extends AnyMask>(src: T): T;
254
+
255
+ /**
256
+ * Inverts a BinaryMask in-place.
257
+ */
258
+ declare function invertBinaryMask(dst: BinaryMask): void;
259
+ /**
260
+ * Inverts an AlphaMask in-place.
261
+ */
262
+ declare function invertAlphaMask(dst: AlphaMask): void;
263
+
264
+ /**
265
+ * Merges a source mask into a destination AlphaMask.
266
+ */
267
+ declare function mergeMasks(dst: AlphaMask, dstWidth: number, src: AnyMask, opts: ApplyMaskOptions): void;
268
+
246
269
  declare class PixelData {
247
270
  readonly imageData: ImageDataLike;
248
271
  readonly data32: Uint32Array;
@@ -251,6 +274,18 @@ declare class PixelData {
251
274
  constructor(imageData: ImageDataLike);
252
275
  }
253
276
 
277
+ /**
278
+ * Directly applies a mask to a region of PixelData,
279
+ * modifying the destination's alpha channel in-place.
280
+ */
281
+ declare function applyMaskToPixelData(dst: PixelData, mask: AnyMask, opts: ApplyMaskOptions): void;
282
+
283
+ /**
284
+ * Fills a rectangle in the destination PixelData with a single color,
285
+ * supporting blend modes, global alpha, and masking.
286
+ */
287
+ declare function blendColorPixelData(dst: PixelData, color: Color32, opts: ColorBlendOptions): void;
288
+
254
289
  /**
255
290
  * Blits source PixelData into a destination PixelData using 32-bit integer bitwise blending.
256
291
  * This function bypasses standard ImageData limitations by operating directly on
@@ -267,4 +302,15 @@ declare class PixelData {
267
302
  */
268
303
  declare function blendPixelData(dst: PixelData, src: PixelData, opts: PixelBlendOptions): void;
269
304
 
270
- export { type AlphaMask, type AnyMask, type ApplyMaskOptions, type Base64EncodedUInt8Array, type BinaryMask, type BlendColor32, COLOR_32_BLEND_MODES, type Color32, type ColorBlendOptions, type ImageDataLike, MaskType, type PixelBlendOptions, type PixelOptions, type RGBA, type Rect, type SerializedImageData, base64DecodeArrayBuffer, base64EncodeArrayBuffer, blendPixelData, color32ToCssRGBA, color32ToHex, colorBurnColor32, colorDistance, copyImageData, copyImageDataLike, deserializeImageData, deserializeNullableImageData, deserializeRawImageData, differenceColor32, extractImageData, hardLightColor32, lerpColor32, lerpColor32Fast, linearDodgeColor32, multiplyColor32, overlayColor32, packColor, packRGBA, screenColor32, serializeImageData, serializeNullableImageData, sourceOverColor32, unpackAlpha, unpackBlue, unpackColor, unpackColorTo, unpackGreen, unpackRed };
305
+ /**
306
+ * Clears a region of the PixelData to transparent (0x00000000).
307
+ * Internally uses the optimized fillPixelData.
308
+ */
309
+ declare function clearPixelData(dst: PixelData, rect?: Partial<Rect>): void;
310
+
311
+ /**
312
+ * A high-performance solid fill for PixelData.
313
+ */
314
+ declare function fillPixelData(dst: PixelData, color: Color32, rect?: Partial<Rect>): void;
315
+
316
+ export { type AlphaMask, type AnyMask, type ApplyMaskOptions, type Base64EncodedUInt8Array, type BinaryMask, type BlendColor32, COLOR_32_BLEND_MODES, type Color32, type ColorBlendOptions, type ImageDataLike, MaskType, type PixelBlendOptions, type PixelOptions, type RGBA, type Rect, type SerializedImageData, applyMaskToPixelData, base64DecodeArrayBuffer, base64EncodeArrayBuffer, blendColorPixelData, blendPixelData, clearPixelData, color32ToCssRGBA, color32ToHex, colorBurnColor32, colorDistance, copyImageData, copyImageDataLike, copyMask, deserializeImageData, deserializeNullableImageData, deserializeRawImageData, differenceColor32, extractImageData, fillPixelData, hardLightColor32, invertAlphaMask, invertBinaryMask, lerpColor32, lerpColor32Fast, linearDodgeColor32, mergeMasks, multiplyColor32, overlayColor32, packColor, packRGBA, screenColor32, serializeImageData, serializeNullableImageData, sourceOverColor32, unpackAlpha, unpackBlue, unpackColor, unpackColorTo, unpackGreen, unpackRed, writeImageData };
@@ -279,6 +279,306 @@ function deserializeNullableImageData(serialized) {
279
279
  return deserializeImageData(serialized);
280
280
  }
281
281
 
282
+ // src/ImageData/writeImageData.ts
283
+ function writeImageData(imageData, data, _x, _y, _w, _h) {
284
+ const { x, y, w, h } = typeof _x === "object" ? _x : { x: _x, y: _y, w: _w, h: _h };
285
+ const { width: dstW, height: dstH, data: dst } = imageData;
286
+ const x0 = Math.max(0, x);
287
+ const y0 = Math.max(0, y);
288
+ const x1 = Math.min(dstW, x + w);
289
+ const y1 = Math.min(dstH, y + h);
290
+ if (x1 <= x0 || y1 <= y0) return;
291
+ const rowLen = (x1 - x0) * 4;
292
+ const srcCol = x0 - x;
293
+ const srcYOffset = y0 - y;
294
+ const actualH = y1 - y0;
295
+ for (let row = 0; row < actualH; row++) {
296
+ const dstStart = ((y0 + row) * dstW + x0) * 4;
297
+ const srcRow = srcYOffset + row;
298
+ const o = (srcRow * w + srcCol) * 4;
299
+ dst.set(data.subarray(o, o + rowLen), dstStart);
300
+ }
301
+ }
302
+
303
+ // src/Mask/copyMask.ts
304
+ function copyMask(src) {
305
+ return src.slice();
306
+ }
307
+
308
+ // src/Mask/invertMask.ts
309
+ function invertBinaryMask(dst) {
310
+ const len = dst.length;
311
+ for (let i = 0; i < len; i++) {
312
+ dst[i] = dst[i] === 0 ? 1 : 0;
313
+ }
314
+ }
315
+ function invertAlphaMask(dst) {
316
+ const len = dst.length;
317
+ for (let i = 0; i < len; i++) {
318
+ dst[i] = 255 - dst[i];
319
+ }
320
+ }
321
+
322
+ // src/Mask/mergeMasks.ts
323
+ function mergeMasks(dst, dstWidth, src, opts) {
324
+ const {
325
+ x: targetX = 0,
326
+ y: targetY = 0,
327
+ w: width = 0,
328
+ h: height = 0,
329
+ alpha: globalAlpha = 255,
330
+ maskType = 0 /* ALPHA */,
331
+ mw,
332
+ mx = 0,
333
+ my = 0,
334
+ invertMask = false
335
+ } = opts;
336
+ if (width <= 0 || height <= 0 || globalAlpha === 0) {
337
+ return;
338
+ }
339
+ const sPitch = mw ?? width;
340
+ const isAlpha = maskType === 0 /* ALPHA */;
341
+ for (let iy = 0; iy < height; iy++) {
342
+ const dy = targetY + iy;
343
+ const sy = my + iy;
344
+ if (dy < 0 || sy < 0) {
345
+ continue;
346
+ }
347
+ for (let ix = 0; ix < width; ix++) {
348
+ const dx = targetX + ix;
349
+ const sx = mx + ix;
350
+ if (dx < 0 || dx >= dstWidth || sx < 0 || sx >= sPitch) {
351
+ continue;
352
+ }
353
+ const dIdx = dy * dstWidth + dx;
354
+ const sIdx = sy * sPitch + sx;
355
+ const mVal = src[sIdx];
356
+ let weight = globalAlpha;
357
+ if (isAlpha) {
358
+ const effectiveM = invertMask ? 255 - mVal : mVal;
359
+ if (effectiveM === 0) {
360
+ dst[dIdx] = 0;
361
+ continue;
362
+ }
363
+ weight = globalAlpha === 255 ? effectiveM : effectiveM * globalAlpha + 128 >> 8;
364
+ } else {
365
+ const isHit = invertMask ? mVal === 0 : mVal === 1;
366
+ if (!isHit) {
367
+ dst[dIdx] = 0;
368
+ continue;
369
+ }
370
+ weight = globalAlpha;
371
+ }
372
+ if (weight === 0) {
373
+ dst[dIdx] = 0;
374
+ continue;
375
+ }
376
+ const da = dst[dIdx];
377
+ if (da === 0) {
378
+ } else if (weight === 255) {
379
+ } else if (da === 255) {
380
+ dst[dIdx] = weight;
381
+ } else {
382
+ dst[dIdx] = da * weight + 128 >> 8;
383
+ }
384
+ }
385
+ }
386
+ }
387
+
388
+ // src/PixelData/applyMaskToPixelData.ts
389
+ function applyMaskToPixelData(dst, mask, opts) {
390
+ const {
391
+ x: targetX = 0,
392
+ y: targetY = 0,
393
+ w: width = dst.width,
394
+ h: height = dst.height,
395
+ alpha: globalAlpha = 255,
396
+ maskType = 0 /* ALPHA */,
397
+ mw,
398
+ mx = 0,
399
+ my = 0,
400
+ invertMask = false
401
+ } = opts;
402
+ let x = targetX;
403
+ let y = targetY;
404
+ let w = width;
405
+ let h = height;
406
+ if (x < 0) {
407
+ w += x;
408
+ x = 0;
409
+ }
410
+ if (y < 0) {
411
+ h += y;
412
+ y = 0;
413
+ }
414
+ const actualW = Math.min(w, dst.width - x);
415
+ const actualH = Math.min(h, dst.height - y);
416
+ if (actualW <= 0 || actualH <= 0 || globalAlpha === 0) {
417
+ return;
418
+ }
419
+ const dst32 = dst.data32;
420
+ const dw = dst.width;
421
+ const mPitch = mw ?? width;
422
+ const isAlpha = maskType === 0 /* ALPHA */;
423
+ const dx = x - targetX;
424
+ const dy = y - targetY;
425
+ let dIdx = y * dw + x;
426
+ let mIdx = (my + dy) * mPitch + (mx + dx);
427
+ const dStride = dw - actualW;
428
+ const mStride = mPitch - actualW;
429
+ for (let iy = 0; iy < actualH; iy++) {
430
+ for (let ix = 0; ix < actualW; ix++) {
431
+ const mVal = mask[mIdx];
432
+ let weight = globalAlpha;
433
+ if (isAlpha) {
434
+ const effectiveM = invertMask ? 255 - mVal : mVal;
435
+ if (effectiveM === 0) {
436
+ dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
437
+ dIdx++;
438
+ mIdx++;
439
+ continue;
440
+ }
441
+ weight = globalAlpha === 255 ? effectiveM : effectiveM * globalAlpha + 128 >> 8;
442
+ } else {
443
+ const isHit = invertMask ? mVal === 0 : mVal === 1;
444
+ if (!isHit) {
445
+ dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
446
+ dIdx++;
447
+ mIdx++;
448
+ continue;
449
+ }
450
+ weight = globalAlpha;
451
+ }
452
+ if (weight === 0) {
453
+ dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
454
+ } else {
455
+ const d = dst32[dIdx];
456
+ const da = d >>> 24;
457
+ let finalAlpha = da;
458
+ if (da === 0) {
459
+ } else if (weight === 255) {
460
+ } else if (da === 255) {
461
+ finalAlpha = weight;
462
+ } else {
463
+ finalAlpha = da * weight + 128 >> 8;
464
+ }
465
+ dst32[dIdx] = (d & 16777215 | finalAlpha << 24) >>> 0;
466
+ }
467
+ dIdx++;
468
+ mIdx++;
469
+ }
470
+ dIdx += dStride;
471
+ mIdx += mStride;
472
+ }
473
+ }
474
+
475
+ // src/PixelData/blendColorPixelData.ts
476
+ function blendColorPixelData(dst, color, opts) {
477
+ const {
478
+ x: targetX = 0,
479
+ y: targetY = 0,
480
+ w: width = dst.width,
481
+ h: height = dst.height,
482
+ alpha: globalAlpha = 255,
483
+ blendFn = sourceOverColor32,
484
+ mask,
485
+ maskType = 0 /* ALPHA */,
486
+ mw,
487
+ mx = 0,
488
+ my = 0,
489
+ invertMask = false
490
+ } = opts;
491
+ if (globalAlpha === 0) return;
492
+ let x = targetX;
493
+ let y = targetY;
494
+ let w = width;
495
+ let h = height;
496
+ if (x < 0) {
497
+ w += x;
498
+ x = 0;
499
+ }
500
+ if (y < 0) {
501
+ h += y;
502
+ y = 0;
503
+ }
504
+ const actualW = Math.min(w, dst.width - x);
505
+ const actualH = Math.min(h, dst.height - y);
506
+ if (actualW <= 0 || actualH <= 0) return;
507
+ const dst32 = dst.data32;
508
+ const dw = dst.width;
509
+ const mPitch = mw ?? width;
510
+ const isAlphaMask = maskType === 0 /* ALPHA */;
511
+ const dx = x - targetX;
512
+ const dy = y - targetY;
513
+ let dIdx = y * dw + x;
514
+ let mIdx = (my + dy) * mPitch + (mx + dx);
515
+ const dStride = dw - actualW;
516
+ const mStride = mPitch - actualW;
517
+ const baseSrcColor = color;
518
+ const baseSrcAlpha = baseSrcColor >>> 24;
519
+ for (let iy = 0; iy < actualH; iy++) {
520
+ for (let ix = 0; ix < actualW; ix++) {
521
+ if (baseSrcAlpha === 0) {
522
+ dIdx++;
523
+ mIdx++;
524
+ continue;
525
+ }
526
+ let weight = globalAlpha;
527
+ if (mask) {
528
+ const mVal = mask[mIdx];
529
+ if (isAlphaMask) {
530
+ const effectiveM = invertMask ? 255 - mVal : mVal;
531
+ if (effectiveM === 0) {
532
+ dIdx++;
533
+ mIdx++;
534
+ continue;
535
+ }
536
+ if (globalAlpha === 255) {
537
+ weight = effectiveM;
538
+ } else if (effectiveM === 255) {
539
+ weight = globalAlpha;
540
+ } else {
541
+ weight = effectiveM * globalAlpha + 128 >> 8;
542
+ }
543
+ } else {
544
+ const isHit = invertMask ? mVal === 0 : mVal === 1;
545
+ if (!isHit) {
546
+ dIdx++;
547
+ mIdx++;
548
+ continue;
549
+ }
550
+ weight = globalAlpha;
551
+ }
552
+ if (weight === 0) {
553
+ dIdx++;
554
+ mIdx++;
555
+ continue;
556
+ }
557
+ }
558
+ let currentSrcAlpha = baseSrcAlpha;
559
+ let currentSrcColor = baseSrcColor;
560
+ if (weight < 255) {
561
+ if (baseSrcAlpha === 255) {
562
+ currentSrcAlpha = weight;
563
+ } else {
564
+ currentSrcAlpha = baseSrcAlpha * weight + 128 >> 8;
565
+ }
566
+ if (currentSrcAlpha === 0) {
567
+ dIdx++;
568
+ mIdx++;
569
+ continue;
570
+ }
571
+ currentSrcColor = (baseSrcColor & 16777215 | currentSrcAlpha << 24) >>> 0;
572
+ }
573
+ dst32[dIdx] = blendFn(currentSrcColor, dst32[dIdx]);
574
+ dIdx++;
575
+ mIdx++;
576
+ }
577
+ dIdx += dStride;
578
+ mIdx += mStride;
579
+ }
580
+ }
581
+
282
582
  // src/PixelData/blendPixelData.ts
283
583
  function blendPixelData(dst, src, opts) {
284
584
  const {
@@ -414,27 +714,78 @@ function blendPixelData(dst, src, opts) {
414
714
  mIdx += mStride;
415
715
  }
416
716
  }
717
+
718
+ // src/PixelData/fillPixelData.ts
719
+ function fillPixelData(dst, color, rect) {
720
+ const {
721
+ x: targetX = 0,
722
+ y: targetY = 0,
723
+ w: width = dst.width,
724
+ h: height = dst.height
725
+ } = rect || {};
726
+ let x = targetX;
727
+ let y = targetY;
728
+ let w = width;
729
+ let h = height;
730
+ if (x < 0) {
731
+ w += x;
732
+ x = 0;
733
+ }
734
+ if (y < 0) {
735
+ h += y;
736
+ y = 0;
737
+ }
738
+ const actualW = Math.min(w, dst.width - x);
739
+ const actualH = Math.min(h, dst.height - y);
740
+ if (actualW <= 0 || actualH <= 0) {
741
+ return;
742
+ }
743
+ const dst32 = dst.data32;
744
+ const dw = dst.width;
745
+ if (actualW === dw && actualH === dst.height && x === 0 && y === 0) {
746
+ dst32.fill(color);
747
+ return;
748
+ }
749
+ for (let iy = 0; iy < actualH; iy++) {
750
+ const start = (y + iy) * dw + x;
751
+ const end = start + actualW;
752
+ dst32.fill(color, start, end);
753
+ }
754
+ }
755
+
756
+ // src/PixelData/clearPixelData.ts
757
+ function clearPixelData(dst, rect) {
758
+ fillPixelData(dst, 0, rect);
759
+ }
417
760
  export {
418
761
  COLOR_32_BLEND_MODES,
419
762
  MaskType,
763
+ applyMaskToPixelData,
420
764
  base64DecodeArrayBuffer,
421
765
  base64EncodeArrayBuffer,
766
+ blendColorPixelData,
422
767
  blendPixelData,
768
+ clearPixelData,
423
769
  color32ToCssRGBA,
424
770
  color32ToHex,
425
771
  colorBurnColor32,
426
772
  colorDistance,
427
773
  copyImageData,
428
774
  copyImageDataLike,
775
+ copyMask,
429
776
  deserializeImageData,
430
777
  deserializeNullableImageData,
431
778
  deserializeRawImageData,
432
779
  differenceColor32,
433
780
  extractImageData,
781
+ fillPixelData,
434
782
  hardLightColor32,
783
+ invertAlphaMask,
784
+ invertBinaryMask,
435
785
  lerpColor32,
436
786
  lerpColor32Fast,
437
787
  linearDodgeColor32,
788
+ mergeMasks,
438
789
  multiplyColor32,
439
790
  overlayColor32,
440
791
  packColor,
@@ -448,6 +799,7 @@ export {
448
799
  unpackColor,
449
800
  unpackColorTo,
450
801
  unpackGreen,
451
- unpackRed
802
+ unpackRed,
803
+ writeImageData
452
804
  };
453
805
  //# sourceMappingURL=index.prod.js.map