pixel-data-js 0.27.0 → 0.28.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.
Files changed (139) hide show
  1. package/README.md +12 -2
  2. package/dist/index.prod.cjs +2222 -1045
  3. package/dist/index.prod.cjs.map +1 -1
  4. package/dist/index.prod.d.ts +542 -417
  5. package/dist/index.prod.js +2167 -1024
  6. package/dist/index.prod.js.map +1 -1
  7. package/package.json +11 -11
  8. package/src/Algorithm/floodFillSelection.ts +8 -6
  9. package/src/Algorithm/forEachLinePoint.ts +6 -6
  10. package/src/{Internal/resample32.ts → Algorithm/resampleUint32Array.ts} +11 -21
  11. package/src/BlendModes/blend-modes-fast.ts +169 -0
  12. package/src/BlendModes/blend-modes-perfect.ts +207 -0
  13. package/src/BlendModes/blend-modes.ts +9 -0
  14. package/src/Canvas/CanvasFrameRenderer.ts +20 -28
  15. package/src/Canvas/CanvasPixelDataRenderer.ts +23 -0
  16. package/src/Canvas/PixelCanvas.ts +2 -7
  17. package/src/Canvas/ReusableCanvas.ts +4 -12
  18. package/src/Canvas/_canvas-types.ts +26 -0
  19. package/src/History/PixelAccumulator.ts +17 -17
  20. package/src/History/PixelEngineConfig.ts +3 -3
  21. package/src/History/PixelMutator/mutatorApplyAlphaMask.ts +4 -3
  22. package/src/History/PixelMutator/mutatorApplyBinaryMask.ts +4 -3
  23. package/src/History/PixelMutator/mutatorApplyMask.ts +4 -3
  24. package/src/History/PixelMutator/mutatorBlendAlphaMask.ts +6 -4
  25. package/src/History/PixelMutator/mutatorBlendBinaryMask.ts +6 -4
  26. package/src/History/PixelMutator/mutatorBlendColor.ts +2 -2
  27. package/src/History/PixelMutator/mutatorBlendColorPaintAlphaMask.ts +2 -1
  28. package/src/History/PixelMutator/mutatorBlendColorPaintBinaryMask.ts +2 -1
  29. package/src/History/PixelMutator/mutatorBlendColorPaintMask.ts +3 -1
  30. package/src/History/PixelMutator/mutatorBlendColorPaintRect.ts +3 -3
  31. package/src/History/PixelMutator/mutatorBlendMask.ts +6 -4
  32. package/src/History/PixelMutator/mutatorBlendPixelData.ts +5 -4
  33. package/src/History/PixelMutator/mutatorClear.ts +4 -3
  34. package/src/History/PixelMutator/mutatorFill.ts +5 -4
  35. package/src/History/PixelMutator/mutatorFillBinaryMask.ts +2 -1
  36. package/src/History/PixelMutator/mutatorInvert.ts +2 -2
  37. package/src/History/PixelMutator.ts +1 -1
  38. package/src/History/PixelPatchTiles.ts +7 -7
  39. package/src/History/PixelWriter.ts +12 -63
  40. package/src/ImageData/ImageDataLike.ts +1 -1
  41. package/src/ImageData/_ImageData-types.ts +13 -0
  42. package/src/ImageData/copyImageData.ts +1 -1
  43. package/src/ImageData/extractImageDataBuffer.ts +3 -2
  44. package/src/ImageData/imageDataToUint32Array.ts +18 -0
  45. package/src/ImageData/resampleImageData.ts +3 -3
  46. package/src/ImageData/resizeImageData.ts +1 -1
  47. package/src/ImageData/serialization.ts +1 -1
  48. package/src/ImageData/uInt32ArrayToImageData.ts +1 -1
  49. package/src/ImageData/writeImageData.ts +2 -2
  50. package/src/ImageData/writeImageDataBuffer.ts +2 -2
  51. package/src/IndexedImage/IndexedImage.ts +56 -98
  52. package/src/IndexedImage/_indexedImage-types.ts +18 -0
  53. package/src/IndexedImage/getIndexedImageColorCounts.ts +3 -3
  54. package/src/IndexedImage/indexedImageToAverageColor.ts +1 -1
  55. package/src/IndexedImage/indexedImageToImageData.ts +4 -6
  56. package/src/IndexedImage/resampleIndexedImage.ts +7 -15
  57. package/src/Input/fileToImageData.ts +1 -1
  58. package/src/Internal/_errors.ts +2 -0
  59. package/src/Internal/macros.ts +14 -0
  60. package/src/Mask/AlphaMask.ts +1 -1
  61. package/src/Mask/BinaryMask/makeBinaryMaskFromAlphaMask.ts +23 -0
  62. package/src/Mask/BinaryMask/makeBinaryMaskOutline.ts +88 -0
  63. package/src/Mask/BinaryMask/makeCircleBinaryMaskOutline.ts +104 -0
  64. package/src/Mask/BinaryMask/makeRectBinaryMaskOutline.ts +34 -0
  65. package/src/Mask/BinaryMask.ts +1 -1
  66. package/src/Mask/_mask-types.ts +73 -0
  67. package/src/Mask/applyBinaryMaskToAlphaMask.ts +2 -1
  68. package/src/Mask/copyMask.ts +1 -1
  69. package/src/Mask/extractMask.ts +2 -1
  70. package/src/Mask/extractMaskBuffer.ts +1 -1
  71. package/src/Mask/mergeAlphaMasks.ts +6 -3
  72. package/src/Mask/mergeBinaryMasks.ts +2 -1
  73. package/src/Mask/setMaskData.ts +1 -1
  74. package/src/MaskRect/merge2BinaryMaskRects.ts +2 -2
  75. package/src/MaskRect/mergeBinaryMaskRects.ts +1 -1
  76. package/src/MaskRect/subtractBinaryMaskRects.ts +1 -1
  77. package/src/Paint/AlphaMaskPaintBuffer.ts +339 -0
  78. package/src/Paint/AlphaMaskPaintBufferCanvasRenderer.ts +78 -0
  79. package/src/Paint/BinaryMaskPaintBuffer.ts +254 -0
  80. package/src/Paint/BinaryMaskPaintBufferCanvasRenderer.ts +67 -0
  81. package/src/Paint/{PaintBuffer.ts → ColorPaintBuffer.ts} +148 -77
  82. package/src/Paint/{PaintBufferCanvasRenderer.ts → ColorPaintBufferCanvasRenderer.ts} +6 -5
  83. package/src/Paint/PaintCursorRenderer.ts +117 -0
  84. package/src/Paint/_paint-types.ts +22 -0
  85. package/src/Paint/eachTileInBounds.ts +45 -0
  86. package/src/Paint/makeCirclePaintMask.ts +74 -0
  87. package/src/Paint/makePaintMask.ts +5 -2
  88. package/src/Paint/makeRectFalloffPaintAlphaMask.ts +4 -2
  89. package/src/PixelData/PixelData.ts +15 -19
  90. package/src/PixelData/ReusablePixelData.ts +36 -0
  91. package/src/PixelData/_pixelData-types.ts +17 -0
  92. package/src/PixelData/applyAlphaMaskToPixelData.ts +80 -43
  93. package/src/PixelData/applyBinaryMaskToPixelData.ts +10 -8
  94. package/src/PixelData/applyMaskToPixelData.ts +4 -9
  95. package/src/PixelData/blendColorPixelData.ts +9 -8
  96. package/src/PixelData/blendColorPixelDataAlphaMask.ts +9 -7
  97. package/src/PixelData/blendColorPixelDataBinaryMask.ts +9 -7
  98. package/src/PixelData/blendColorPixelDataMask.ts +4 -2
  99. package/src/PixelData/blendColorPixelDataPaintAlphaMask.ts +4 -2
  100. package/src/PixelData/blendColorPixelDataPaintBinaryMask.ts +4 -2
  101. package/src/PixelData/blendColorPixelDataPaintMask.ts +5 -2
  102. package/src/PixelData/blendPixel.ts +6 -5
  103. package/src/PixelData/blendPixelData.ts +14 -13
  104. package/src/PixelData/blendPixelDataAlphaMask.ts +15 -13
  105. package/src/PixelData/blendPixelDataBinaryMask.ts +15 -13
  106. package/src/PixelData/blendPixelDataMask.ts +5 -3
  107. package/src/PixelData/blendPixelDataPaintBuffer.ts +5 -4
  108. package/src/PixelData/clearPixelDataFast.ts +4 -2
  109. package/src/PixelData/copyPixelData.ts +14 -0
  110. package/src/PixelData/extractPixelData.ts +8 -7
  111. package/src/PixelData/extractPixelDataBuffer.ts +9 -8
  112. package/src/PixelData/fillPixelData.ts +16 -14
  113. package/src/PixelData/fillPixelDataBinaryMask.ts +10 -8
  114. package/src/PixelData/fillPixelDataFast.ts +16 -14
  115. package/src/PixelData/invertPixelData.ts +9 -8
  116. package/src/PixelData/pixelDataToAlphaMask.ts +9 -8
  117. package/src/PixelData/reflectPixelData.ts +9 -9
  118. package/src/PixelData/resamplePixelData.ts +20 -9
  119. package/src/PixelData/rotatePixelData.ts +8 -7
  120. package/src/PixelData/uInt32ArrayToPixelData.ts +15 -0
  121. package/src/PixelData/writePaintBufferToPixelData.ts +5 -5
  122. package/src/PixelData/writePixelDataBuffer.ts +10 -9
  123. package/src/Rect/_rect-types.ts +7 -0
  124. package/src/Rect/getRectsBounds.ts +1 -1
  125. package/src/Rect/trimMaskRectBounds.ts +2 -1
  126. package/src/Rect/trimRectBounds.ts +1 -1
  127. package/src/Tile/MaskTile.ts +40 -0
  128. package/src/Tile/PixelTile.ts +23 -0
  129. package/src/{PixelTile/PixelTilePool.ts → Tile/TilePool.ts} +9 -9
  130. package/src/Tile/_tile-types.ts +33 -0
  131. package/src/_errors.ts +1 -0
  132. package/src/_types.ts +2 -118
  133. package/src/index.ts +46 -21
  134. package/src/ImageData/imageDataToUInt32Array.ts +0 -13
  135. package/src/Internal/helpers.ts +0 -5
  136. package/src/Paint/makeCirclePaintAlphaMask.ts +0 -41
  137. package/src/Paint/makeCirclePaintBinaryMask.ts +0 -29
  138. package/src/PixelTile/PixelTile.ts +0 -21
  139. /package/src/{Internal → Rect}/resolveClipping.ts +0 -0
@@ -1,4 +1,4 @@
1
- import type { ImageDataLike } from '../_types'
1
+ import type { ImageDataLike } from './_ImageData-types'
2
2
 
3
3
  export function makeImageDataLike(width: number, height: number, data?: Buffer): ImageDataLike {
4
4
  const size = width * height * 4
@@ -0,0 +1,13 @@
1
+ export interface ImageDataLike {
2
+ width: number
3
+ height: number
4
+ data: Uint8ClampedArray<ArrayBufferLike>
5
+ }
6
+
7
+ export type SerializedImageData = {
8
+ width: number
9
+ height: number
10
+ data: string
11
+ }
12
+
13
+ export type Base64EncodedUInt8Array = string & { readonly __brandBase64UInt8Array: unique symbol }
@@ -1,4 +1,4 @@
1
- import type { ImageDataLike } from '../_types'
1
+ import type { ImageDataLike } from './_ImageData-types'
2
2
 
3
3
  export function copyImageData({ data, width, height }: ImageDataLike): ImageData {
4
4
  return new ImageData(data.slice(), width, height)
@@ -1,5 +1,6 @@
1
- import type { ImageDataLike, Rect } from '../_types'
2
- import { makeClippedBlit, resolveBlitClipping } from '../Internal/resolveClipping'
1
+ import type { Rect } from '../Rect/_rect-types'
2
+ import { makeClippedBlit, resolveBlitClipping } from '../Rect/resolveClipping'
3
+ import type { ImageDataLike } from './_ImageData-types'
3
4
 
4
5
  const SCRATCH_BLIT = makeClippedBlit()
5
6
 
@@ -0,0 +1,18 @@
1
+ import type { ImageDataLike } from './_ImageData-types'
2
+
3
+ /**
4
+ * converts {@link ImageData} to a faster Uint32Array
5
+ */
6
+ export function imageDataToUint32Array(imageData: ImageDataLike): Uint32Array {
7
+ return _macro_imageDataToUint32Array(imageData)
8
+ }
9
+
10
+ // @__INLINE_MACRO__
11
+ export function _macro_imageDataToUint32Array(imageData: ImageDataLike): Uint32Array {
12
+ return new Uint32Array(
13
+ imageData.data.buffer,
14
+ imageData.data.byteOffset,
15
+ // Shift right by 2 is a fast bitwise division by 4.
16
+ imageData.data.byteLength >> 2,
17
+ )
18
+ }
@@ -2,7 +2,7 @@
2
2
  * Resamples ImageData by a specific factor.
3
3
  * Factor > 1 upscales, Factor < 1 downscales.
4
4
  */
5
- import { resample32 } from '../Internal/resample32'
5
+ import { resampleUint32Array } from '../Algorithm/resampleUint32Array'
6
6
 
7
7
  /**
8
8
  * Resamples ImageData by a specific factor.
@@ -10,8 +10,8 @@ import { resample32 } from '../Internal/resample32'
10
10
  */
11
11
  export function resampleImageData(source: ImageData, factor: number): ImageData {
12
12
  const src32 = new Uint32Array(source.data.buffer)
13
- const { data, width, height } = resample32(src32, source.width, source.height, factor)
13
+ const { data, w, h } = resampleUint32Array(src32, source.width, source.height, factor)
14
14
 
15
15
  const uint8ClampedArray = new Uint8ClampedArray(data.buffer) as Uint8ClampedArray<ArrayBuffer>
16
- return new ImageData(uint8ClampedArray, width, height)
16
+ return new ImageData(uint8ClampedArray, w, h)
17
17
  }
@@ -1,4 +1,4 @@
1
- import type { ImageDataLike } from '../_types'
1
+ import type { ImageDataLike } from './_ImageData-types'
2
2
 
3
3
  /**
4
4
  * Non destructively resizes the {@link ImageData} buffer to new dimensions, optionally
@@ -1,4 +1,4 @@
1
- import type { Base64EncodedUInt8Array, ImageDataLike, SerializedImageData } from '../_types'
1
+ import type { Base64EncodedUInt8Array, ImageDataLike, SerializedImageData } from './_ImageData-types'
2
2
 
3
3
  export function base64EncodeArrayBuffer(buffer: ArrayBufferLike): Base64EncodedUInt8Array {
4
4
  const uint8 = new Uint8Array(buffer)
@@ -1,4 +1,4 @@
1
- import type { ImageDataLike } from '../_types'
1
+ import type { ImageDataLike } from './_ImageData-types'
2
2
 
3
3
  export function uInt32ArrayToImageData(
4
4
  data: Uint32Array,
@@ -1,5 +1,5 @@
1
- import { MaskType } from '../_types'
2
- import { makeClippedBlit, resolveBlitClipping } from '../Internal/resolveClipping'
1
+ import { MaskType } from '../Mask/_mask-types'
2
+ import { makeClippedBlit, resolveBlitClipping } from '../Rect/resolveClipping'
3
3
 
4
4
  const SCRATCH_BLIT = makeClippedBlit()
5
5
 
@@ -1,5 +1,5 @@
1
- import type { Rect } from '../_types'
2
- import { makeClippedBlit, resolveBlitClipping } from '../Internal/resolveClipping'
1
+ import type { Rect } from '../Rect/_rect-types'
2
+ import { makeClippedBlit, resolveBlitClipping } from '../Rect/resolveClipping'
3
3
 
4
4
  const SCRATCH_BLIT = makeClippedBlit()
5
5
 
@@ -1,113 +1,71 @@
1
1
  import type { Color32 } from '../_types'
2
+ import type { IndexedImage } from './_indexedImage-types'
2
3
 
3
- /**
4
- * Represents an image using a palette-based indexing system.
5
- * Instead of storing 4 bytes (RGBA) per pixel, this class stores a single index
6
- * into a color palette. This format is optimized for memory efficiency and
7
- * high-speed pattern matching or recoloring operations.
8
- */
9
- export class IndexedImage {
10
- /** The width of the image in pixels. */
11
- public readonly width: number
12
- /** The height of the image in pixels. */
13
- public readonly height: number
14
- /** Flat array of palette indices. Index = x + (y * width). */
15
- public readonly data: Int32Array
16
- /** The palette of unique 32-bit colors (ABGR/RGBA packed) found in the image. */
17
- public readonly palette: Uint32Array
18
- /** The specific index in the palette reserved for fully transparent pixels. */
19
- public readonly transparentPalletIndex: number
20
-
21
- /**
22
- * @param width - Image width.
23
- * @param height - Image height.
24
- * @param data - The indexed pixel data.
25
- * @param palette - The array of packed colors.
26
- * @param transparentPalletIndex - The index representing alpha 0.
27
- */
28
- constructor(
29
- width: number,
30
- height: number,
31
- data: Int32Array,
32
- palette: Uint32Array,
33
- transparentPalletIndex: number,
34
- ) {
35
- this.width = width
36
- this.height = height
37
- this.data = data
38
- this.palette = palette
39
- this.transparentPalletIndex = transparentPalletIndex
4
+ export function makeIndexedImage(
5
+ width: number,
6
+ height: number,
7
+ data: Uint32Array,
8
+ palette: Uint32Array,
9
+ transparentPalletIndex: number,
10
+ ): IndexedImage {
11
+ return {
12
+ w: width,
13
+ h: height,
14
+ data,
15
+ palette,
16
+ transparentPalletIndex,
40
17
  }
18
+ }
41
19
 
42
- /**
43
- * Creates an IndexedImage from standard browser ImageData.
44
- * @param imageData - The source ImageData to convert.
45
- * @returns A new IndexedImage instance.
46
- */
47
- static fromImageData(imageData: ImageData): IndexedImage {
48
- return IndexedImage.fromRaw(imageData.data, imageData.width, imageData.height)
49
- }
20
+ export function makeIndexedImageFromImageDataRaw(
21
+ data: Uint8ClampedArray,
22
+ width: number,
23
+ height: number,
24
+ ): IndexedImage {
25
+ const buffer = data.buffer
26
+ const rawData = new Uint32Array(buffer)
27
+ const indexedData = new Uint32Array(rawData.length)
28
+ const colorMap = new Map<number, number>()
29
+ const transparentColor = 0
30
+ const transparentPalletIndex = 0
50
31
 
51
- /**
52
- * Creates an IndexedImage from a raw byte buffer and dimensions.
53
- * Any pixel with an alpha channel of 0 is normalized to the transparent palette index.
54
- * @param data - Raw RGBA byte data.
55
- * @param width - Image width.
56
- * @param height - Image height.
57
- * @returns A new IndexedImage instance.
58
- */
59
- static fromRaw(
60
- data: Uint8ClampedArray,
61
- width: number,
62
- height: number,
63
- ): IndexedImage {
64
- const buffer = data.buffer
65
- const rawData = new Uint32Array(buffer)
66
- const indexedData = new Int32Array(rawData.length)
67
- const colorMap = new Map<number, number>()
68
- const transparentColor = 0
69
- const transparentPalletIndex = 0
32
+ // Initialize palette with normalized transparent color
33
+ colorMap.set(transparentColor, transparentPalletIndex)
70
34
 
71
- // Initialize palette with normalized transparent color
72
- colorMap.set(transparentColor, transparentPalletIndex)
35
+ for (let i = 0; i < rawData.length; i++) {
36
+ const pixel = rawData[i] as number
37
+ const alpha = (pixel >>> 24) & 0xFF
38
+ const isTransparent = alpha === 0
39
+ const colorKey = isTransparent ? transparentColor : (pixel >>> 0)
73
40
 
74
- for (let i = 0; i < rawData.length; i++) {
75
- const pixel = rawData[i] as number
76
- const alpha = (pixel >>> 24) & 0xFF
77
- const isTransparent = alpha === 0
78
- const colorKey = isTransparent ? transparentColor : (pixel >>> 0)
41
+ let id = colorMap.get(colorKey)
79
42
 
80
- let id = colorMap.get(colorKey)
43
+ if (id === undefined) {
44
+ id = colorMap.size
45
+ colorMap.set(colorKey, id)
46
+ }
81
47
 
82
- if (id === undefined) {
83
- id = colorMap.size
84
- colorMap.set(colorKey, id)
85
- }
48
+ indexedData[i] = id
49
+ }
86
50
 
87
- indexedData[i] = id
88
- }
51
+ const palette = Uint32Array.from(colorMap.keys())
89
52
 
90
- const palette = Uint32Array.from(colorMap.keys())
53
+ return makeIndexedImage(
54
+ width,
55
+ height,
56
+ indexedData,
57
+ palette,
58
+ transparentPalletIndex,
59
+ )
60
+ }
91
61
 
92
- return new IndexedImage(
93
- width,
94
- height,
95
- indexedData,
96
- palette,
97
- transparentPalletIndex,
98
- )
99
- }
62
+ export function makeIndexedImageFromImageData(imageData: ImageData): IndexedImage {
63
+ return makeIndexedImageFromImageDataRaw(imageData.data, imageData.width, imageData.height)
64
+ }
100
65
 
101
- /**
102
- * Retrieves the 32-bit packed color value at the given coordinates.
103
- * @param x - X coordinate.
104
- * @param y - Y coordinate.
105
- * @returns The packed color from the palette.
106
- */
107
- public getColorAt(x: number, y: number): Color32 {
108
- const index = x + y * this.width
109
- const paletteIndex = this.data[index]
66
+ export function getIndexedImageColor(target: IndexedImage, x: number, y: number): Color32 {
67
+ const index = x + y * target.w
68
+ const paletteIndex = target.data[index]
110
69
 
111
- return this.palette[paletteIndex] as Color32
112
- }
70
+ return target.palette[paletteIndex] as Color32
113
71
  }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Represents an image using a palette-based indexing system.
3
+ * Instead of storing 4 bytes (RGBA) per pixel, this class stores a single index
4
+ * into a color palette. This format is optimized for memory efficiency and
5
+ * high-speed pattern matching or recoloring operations.
6
+ */
7
+ export interface IndexedImage {
8
+ /** The width of the image in pixels. */
9
+ readonly w: number
10
+ /** The height of the image in pixels. */
11
+ readonly h: number
12
+ /** Flat array of palette indices. Index = x + (y * width). */
13
+ readonly data: Uint32Array
14
+ /** The palette of unique 32-bit colors (ABGR/RGBA packed) found in the image. */
15
+ readonly palette: Uint32Array
16
+ /** The specific index in the palette reserved for fully transparent pixels. */
17
+ readonly transparentPalletIndex: number
18
+ }
@@ -1,4 +1,4 @@
1
- import type { IndexedImage } from './IndexedImage.ts'
1
+ import type { IndexedImage } from './_indexedImage-types'
2
2
 
3
3
  /**
4
4
  * Calculates the frequency of each palette index based on the image data.
@@ -6,10 +6,10 @@ import type { IndexedImage } from './IndexedImage.ts'
6
6
  * @param indexedImage - The source image containing data and palette definitions.
7
7
  * @returns A typed array where each entry represents the total count of that palette index.
8
8
  */
9
- export function getIndexedImageColorCounts(indexedImage: IndexedImage): Int32Array {
9
+ export function getIndexedImageColorCounts(indexedImage: IndexedImage): Uint32Array {
10
10
  const data = indexedImage.data
11
11
  const palette = indexedImage.palette
12
- const frequencies = new Int32Array(palette.length)
12
+ const frequencies = new Uint32Array(palette.length)
13
13
 
14
14
  for (let i = 0; i < data.length; i++) {
15
15
  const colorIndex = data[i]!
@@ -1,6 +1,6 @@
1
1
  import type { Color32 } from '../_types'
2
2
  import { packColor } from '../color'
3
- import type { IndexedImage } from './IndexedImage'
3
+ import type { IndexedImage } from './_indexedImage-types'
4
4
 
5
5
  /**
6
6
  * Calculates the area-weighted average color of an IndexedImage.
@@ -1,18 +1,16 @@
1
- import type { IndexedImage } from './IndexedImage'
1
+ import type { IndexedImage } from './_indexedImage-types'
2
2
 
3
3
  /**
4
4
  * Converts an IndexedImage back into standard ImageData.
5
5
  */
6
6
  export function indexedImageToImageData(indexedImage: IndexedImage): ImageData {
7
- const { width, height, data, palette } = indexedImage
8
- const result = new ImageData(width, height)
7
+ const { w, h, data, palette } = indexedImage
8
+ const result = new ImageData(w, h)
9
9
  const data32 = new Uint32Array(result.data.buffer)
10
10
 
11
11
  for (let i = 0; i < data.length; i++) {
12
12
  const paletteIndex = data[i]
13
- const color = palette[paletteIndex]
14
-
15
- data32[i] = color
13
+ data32[i] = palette[paletteIndex]
16
14
  }
17
15
 
18
16
  return result
@@ -2,8 +2,8 @@
2
2
  * Resamples an IndexedImage by a specific factor using nearest neighbor
3
3
  * Factor > 1 upscales, Factor < 1 downscales.
4
4
  */
5
- import { IndexedImage } from '../index'
6
- import { resample32 } from '../Internal/resample32'
5
+ import { resampleUint32Array } from '../Algorithm/resampleUint32Array'
6
+ import { type IndexedImage } from '../index'
7
7
 
8
8
  /**
9
9
  * Resamples an IndexedImage by a specific factor using nearest neighbor
@@ -14,18 +14,10 @@ export function resampleIndexedImage(
14
14
  factor: number,
15
15
  ): IndexedImage {
16
16
 
17
- const { data, width, height } = resample32(
18
- source.data,
19
- source.width,
20
- source.height,
21
- factor,
22
- )
17
+ const output = {
18
+ palette: source.palette,
19
+ transparentPalletIndex: source.transparentPalletIndex,
20
+ } as IndexedImage
23
21
 
24
- return new IndexedImage(
25
- width,
26
- height,
27
- data,
28
- source.palette,
29
- source.transparentPalletIndex,
30
- )
22
+ return resampleUint32Array(source.data, source.w, source.h, factor, output) as IndexedImage
31
23
  }
@@ -1,4 +1,4 @@
1
- import { OFFSCREEN_CANVAS_CTX_FAILED } from '../../support/error-strings'
1
+ import { OFFSCREEN_CANVAS_CTX_FAILED } from '../Internal/_errors'
2
2
 
3
3
  /**
4
4
  * Thrown when the user provides a file that isn't an image.
@@ -0,0 +1,2 @@
1
+ export const OFFSCREEN_CANVAS_CTX_FAILED = 'Failed to create OffscreenCanvas context'
2
+ export const CANVAS_CTX_FAILED = 'Failed to create Canvas context'
@@ -0,0 +1,14 @@
1
+ // @__INLINE_MACRO__
2
+ export const _macro_halfAndFloor = (value: number) => value >> 1
3
+
4
+ // @__INLINE_MACRO__
5
+ export const _macro_paintRectCenterOffset = (size: number) => -((size - 1) >> 1)
6
+
7
+ // @__INLINE_MACRO__
8
+ export const _macro_paintCircleCenterOffset = (radius: number) => -Math.ceil(radius - 0.5)
9
+
10
+ // for reference only
11
+ // const _inline_multiplyAlpha = (a: number, b: number) => {
12
+ // const t = a * b + 128
13
+ // return (t + (t >> 8)) >> 8
14
+ // }
@@ -1,4 +1,4 @@
1
- import { type AlphaMask, MaskType } from '../_types'
1
+ import { type AlphaMask, MaskType } from './_mask-types'
2
2
 
3
3
  /**
4
4
  * Creates an Alpha Mask
@@ -0,0 +1,23 @@
1
+ import { type AlphaMask, type BinaryMask, MaskType, type MutableBinaryMask } from '../_mask-types'
2
+
3
+ export function makeBinaryMaskFromAlphaMask(mask: AlphaMask, threshold: number, out?: MutableBinaryMask): BinaryMask {
4
+ const w = mask.w
5
+ const h = mask.h
6
+ const alphaData = mask.data
7
+ const area = w * h
8
+
9
+ const binaryData = new Uint8Array(area)
10
+
11
+ for (let i = 0; i < area; i++) {
12
+ if (alphaData[i] >= threshold) {
13
+ binaryData[i] = 1
14
+ }
15
+ }
16
+
17
+ out = out ?? { type: MaskType.BINARY } as MutableBinaryMask
18
+ out.data = binaryData
19
+ out.w = w
20
+ out.h = h
21
+
22
+ return out
23
+ }
@@ -0,0 +1,88 @@
1
+ import { type BinaryMask, MaskType } from '../_mask-types'
2
+
3
+ export function makeBinaryMaskOutline(
4
+ mask: BinaryMask,
5
+ scale = 1,
6
+ ): BinaryMask {
7
+ const w = mask.w
8
+ const h = mask.h
9
+ const maskData = mask.data
10
+
11
+ const size = w * scale + 2
12
+ const outData = new Uint8Array(size * size)
13
+
14
+ for (let iy = 0; iy < h; iy++) {
15
+ for (let ix = 0; ix < w; ix++) {
16
+ const i = iy * w + ix
17
+ if (maskData[i] === 0) continue
18
+
19
+ const lx = ix * scale + 1
20
+ const ly = iy * scale + 1
21
+
22
+ const top = iy === 0 || maskData[i - w] === 0
23
+ const bottom = iy === h - 1 || maskData[i + w] === 0
24
+ const left = ix === 0 || maskData[i - 1] === 0
25
+ const right = ix === w - 1 || maskData[i + 1] === 0
26
+
27
+ const topLeft = iy === 0 || ix === 0 || maskData[i - w - 1] === 0
28
+ const topRight = iy === 0 || ix === w - 1 || maskData[i - w + 1] === 0
29
+ const bottomLeft = iy === h - 1 || ix === 0 || maskData[i + w - 1] === 0
30
+ const bottomRight = iy === h - 1 || ix === w - 1 || maskData[i + w + 1] === 0
31
+
32
+ if (top) {
33
+ for (let sx = 0; sx < scale; sx++) {
34
+ const outIdx = (ly - 1) * size + (lx + sx)
35
+ outData[outIdx] = 1
36
+ }
37
+ }
38
+
39
+ if (bottom) {
40
+ for (let sx = 0; sx < scale; sx++) {
41
+ const outIdx = (ly + scale) * size + (lx + sx)
42
+ outData[outIdx] = 1
43
+ }
44
+ }
45
+
46
+ if (left) {
47
+ for (let sy = 0; sy < scale; sy++) {
48
+ const outIdx = (ly + sy) * size + (lx - 1)
49
+ outData[outIdx] = 1
50
+ }
51
+ }
52
+
53
+ if (right) {
54
+ for (let sy = 0; sy < scale; sy++) {
55
+ const outIdx = (ly + sy) * size + (lx + scale)
56
+ outData[outIdx] = 1
57
+ }
58
+ }
59
+
60
+ if (topLeft) {
61
+ const outIdx = (ly - 1) * size + (lx - 1)
62
+ outData[outIdx] = 1
63
+ }
64
+
65
+ if (topRight) {
66
+ const outIdx = (ly - 1) * size + (lx + scale)
67
+ outData[outIdx] = 1
68
+ }
69
+
70
+ if (bottomLeft) {
71
+ const outIdx = (ly + scale) * size + (lx - 1)
72
+ outData[outIdx] = 1
73
+ }
74
+
75
+ if (bottomRight) {
76
+ const outIdx = (ly + scale) * size + (lx + scale)
77
+ outData[outIdx] = 1
78
+ }
79
+ }
80
+ }
81
+
82
+ return {
83
+ type: MaskType.BINARY,
84
+ w: size,
85
+ h: size,
86
+ data: outData,
87
+ }
88
+ }
@@ -0,0 +1,104 @@
1
+ import { type BinaryMask, MaskType } from '../_mask-types'
2
+
3
+ export function makeCircleBinaryMaskOutline(size: number, scale: number): BinaryMask {
4
+ const outSize = size * scale + 2
5
+ const outArea = outSize * outSize
6
+ const data = new Uint8Array(outArea)
7
+
8
+ const radius = size / 2
9
+ const r2 = radius * radius
10
+
11
+ let prevMinX = -1
12
+ let prevMaxX = -1
13
+
14
+ let currMinX = -1
15
+ let currMaxX = -1
16
+
17
+ const initialDy = 0 - radius + 0.5
18
+ const initialDy2 = initialDy * initialDy
19
+
20
+ if (initialDy2 <= r2) {
21
+ const dx = Math.sqrt(r2 - initialDy2)
22
+ currMinX = Math.ceil(radius - 0.5 - dx)
23
+ currMaxX = Math.floor(radius - 0.5 + dx)
24
+ }
25
+
26
+ for (let iy = 0; iy < size; iy++) {
27
+ let nextMinX = -1
28
+ let nextMaxX = -1
29
+
30
+ if (iy + 1 < size) {
31
+ const ny = (iy + 1) - radius + 0.5
32
+ const ny2 = ny * ny
33
+
34
+ if (ny2 <= r2) {
35
+ const dx = Math.sqrt(r2 - ny2)
36
+ nextMinX = Math.ceil(radius - 0.5 - dx)
37
+ nextMaxX = Math.floor(radius - 0.5 + dx)
38
+ }
39
+ }
40
+
41
+ if (currMinX !== -1) {
42
+ for (let ix = currMinX; ix <= currMaxX; ix++) {
43
+ // Offset by 1 to leave room for the top/left outline edges
44
+ const sx = ix * scale + 1
45
+ const sy = iy * scale + 1
46
+
47
+ const isTop = prevMinX === -1 || ix < prevMinX || ix > prevMaxX
48
+ const isBottom = nextMinX === -1 || ix < nextMinX || ix > nextMaxX
49
+ const isLeft = ix === currMinX
50
+ const isRight = ix === currMaxX
51
+
52
+ if (isTop) {
53
+ const leftOut = prevMinX === -1 || (ix - 1) < prevMinX || (ix - 1) > prevMaxX
54
+ const rightOut = prevMinX === -1 || (ix + 1) < prevMinX || (ix + 1) > prevMaxX
55
+ const startX = leftOut ? sx - 1 : sx
56
+ const endX = rightOut ? sx + scale : sx + scale - 1
57
+
58
+ for (let x = startX; x <= endX; x++) {
59
+ const index = (sy - 1) * outSize + x
60
+ data[index] = 1
61
+ }
62
+ }
63
+
64
+ if (isBottom) {
65
+ const leftOut = nextMinX === -1 || (ix - 1) < nextMinX || (ix - 1) > nextMaxX
66
+ const rightOut = nextMinX === -1 || (ix + 1) < nextMinX || (ix + 1) > nextMaxX
67
+ const startX = leftOut ? sx - 1 : sx
68
+ const endX = rightOut ? sx + scale : sx + scale - 1
69
+
70
+ for (let x = startX; x <= endX; x++) {
71
+ const index = (sy + scale) * outSize + x
72
+ data[index] = 1
73
+ }
74
+ }
75
+
76
+ if (isLeft) {
77
+ for (let y = sy; y < sy + scale; y++) {
78
+ const index = y * outSize + (sx - 1)
79
+ data[index] = 1
80
+ }
81
+ }
82
+
83
+ if (isRight) {
84
+ for (let y = sy; y < sy + scale; y++) {
85
+ const index = y * outSize + (sx + scale)
86
+ data[index] = 1
87
+ }
88
+ }
89
+ }
90
+ }
91
+
92
+ prevMinX = currMinX
93
+ prevMaxX = currMaxX
94
+ currMinX = nextMinX
95
+ currMaxX = nextMaxX
96
+ }
97
+
98
+ return {
99
+ type: MaskType.BINARY,
100
+ w: outSize,
101
+ h: outSize,
102
+ data,
103
+ }
104
+ }