pixel-data-js 0.27.0 → 0.29.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.
- package/README.md +12 -2
- package/dist/index.prod.cjs +2355 -1124
- package/dist/index.prod.cjs.map +1 -1
- package/dist/index.prod.d.ts +558 -424
- package/dist/index.prod.js +2304 -1115
- package/dist/index.prod.js.map +1 -1
- package/package.json +11 -11
- package/src/Algorithm/floodFillSelection.ts +8 -6
- package/src/Algorithm/forEachLinePoint.ts +6 -6
- package/src/{Internal/resample32.ts → Algorithm/resampleUint32Array.ts} +11 -21
- package/src/BlendModes/blend-modes-fast.ts +169 -0
- package/src/BlendModes/blend-modes-perfect.ts +207 -0
- package/src/BlendModes/blend-modes.ts +9 -0
- package/src/Canvas/CanvasFrameRenderer.ts +20 -28
- package/src/Canvas/CanvasPixelDataRenderer.ts +23 -0
- package/src/Canvas/PixelCanvas.ts +2 -7
- package/src/Canvas/ReusableCanvas.ts +4 -12
- package/src/Canvas/_canvas-types.ts +26 -0
- package/src/History/PixelAccumulator.ts +17 -17
- package/src/History/PixelEngineConfig.ts +3 -3
- package/src/History/PixelMutator/mutatorApplyAlphaMask.ts +4 -3
- package/src/History/PixelMutator/mutatorApplyBinaryMask.ts +4 -3
- package/src/History/PixelMutator/mutatorApplyMask.ts +4 -3
- package/src/History/PixelMutator/mutatorBlendAlphaMask.ts +6 -4
- package/src/History/PixelMutator/mutatorBlendBinaryMask.ts +6 -4
- package/src/History/PixelMutator/mutatorBlendColor.ts +2 -2
- package/src/History/PixelMutator/mutatorBlendColorPaintAlphaMask.ts +2 -1
- package/src/History/PixelMutator/mutatorBlendColorPaintBinaryMask.ts +2 -1
- package/src/History/PixelMutator/mutatorBlendColorPaintMask.ts +3 -1
- package/src/History/PixelMutator/mutatorBlendColorPaintRect.ts +3 -3
- package/src/History/PixelMutator/mutatorBlendMask.ts +6 -4
- package/src/History/PixelMutator/mutatorBlendPixelData.ts +5 -4
- package/src/History/PixelMutator/mutatorClear.ts +4 -3
- package/src/History/PixelMutator/mutatorFill.ts +5 -4
- package/src/History/PixelMutator/mutatorFillBinaryMask.ts +2 -1
- package/src/History/PixelMutator/mutatorInvert.ts +2 -2
- package/src/History/PixelMutator.ts +1 -1
- package/src/History/PixelPatchTiles.ts +7 -7
- package/src/History/PixelWriter.ts +12 -63
- package/src/ImageData/ImageDataLike.ts +1 -1
- package/src/ImageData/_ImageData-types.ts +13 -0
- package/src/ImageData/copyImageData.ts +1 -1
- package/src/ImageData/extractImageDataBuffer.ts +3 -2
- package/src/ImageData/imageDataToUint32Array.ts +18 -0
- package/src/ImageData/resampleImageData.ts +3 -3
- package/src/ImageData/resizeImageData.ts +1 -1
- package/src/ImageData/serialization.ts +1 -1
- package/src/ImageData/uInt32ArrayToImageData.ts +1 -1
- package/src/ImageData/writeImageData.ts +2 -2
- package/src/ImageData/writeImageDataBuffer.ts +2 -2
- package/src/IndexedImage/IndexedImage.ts +56 -98
- package/src/IndexedImage/_indexedImage-types.ts +18 -0
- package/src/IndexedImage/getIndexedImageColorCounts.ts +3 -3
- package/src/IndexedImage/indexedImageToAverageColor.ts +1 -1
- package/src/IndexedImage/indexedImageToImageData.ts +4 -6
- package/src/IndexedImage/resampleIndexedImage.ts +7 -15
- package/src/Input/fileToImageData.ts +1 -1
- package/src/Internal/_constants.ts +3 -0
- package/src/Internal/_errors.ts +2 -0
- package/src/Internal/macros.ts +14 -0
- package/src/Mask/AlphaMask.ts +1 -1
- package/src/Mask/BinaryMask/makeBinaryMaskFromAlphaMask.ts +23 -0
- package/src/Mask/BinaryMask/makeBinaryMaskOutline.ts +88 -0
- package/src/Mask/BinaryMask/makeCircleBinaryMaskOutline.ts +104 -0
- package/src/Mask/BinaryMask/makeRectBinaryMaskOutline.ts +34 -0
- package/src/Mask/BinaryMask.ts +1 -1
- package/src/Mask/_mask-types.ts +73 -0
- package/src/Mask/applyBinaryMaskToAlphaMask.ts +2 -1
- package/src/Mask/copyMask.ts +1 -1
- package/src/Mask/extractMask.ts +2 -1
- package/src/Mask/extractMaskBuffer.ts +1 -1
- package/src/Mask/mergeAlphaMasks.ts +6 -3
- package/src/Mask/mergeBinaryMasks.ts +2 -1
- package/src/Mask/setMaskData.ts +1 -1
- package/src/MaskRect/merge2BinaryMaskRects.ts +2 -2
- package/src/MaskRect/mergeBinaryMaskRects.ts +1 -1
- package/src/MaskRect/subtractBinaryMaskRects.ts +1 -1
- package/src/Paint/AlphaMaskPaintBuffer.ts +283 -0
- package/src/Paint/BinaryMaskPaintBuffer.ts +198 -0
- package/src/Paint/{PaintBuffer.ts → ColorPaintBuffer.ts} +95 -77
- package/src/Paint/Commit/AlphaMaskPaintBufferCommitter.ts +26 -0
- package/src/Paint/Commit/AlphaMaskPaintBufferManager.ts +34 -0
- package/src/Paint/Commit/BinaryMaskPaintBufferCommitter.ts +26 -0
- package/src/Paint/Commit/BinaryMaskPaintBufferManager.ts +31 -0
- package/src/Paint/Commit/ColorPaintBufferCommitter.ts +23 -0
- package/src/Paint/Commit/ColorPaintBufferManager.ts +34 -0
- package/src/Paint/Commit/commitColorPaintBuffer.ts +55 -0
- package/src/Paint/Commit/commitMaskPaintBuffer.ts +78 -0
- package/src/Paint/Render/AlphaMaskPaintBufferCanvasRenderer.ts +78 -0
- package/src/Paint/Render/BinaryMaskPaintBufferCanvasRenderer.ts +67 -0
- package/src/Paint/{PaintBufferCanvasRenderer.ts → Render/ColorPaintBufferCanvasRenderer.ts} +13 -14
- package/src/Paint/Render/PaintCursorRenderer.ts +118 -0
- package/src/Paint/_paint-types.ts +22 -0
- package/src/Paint/eachTileInBounds.ts +45 -0
- package/src/Paint/makeCirclePaintMask.ts +74 -0
- package/src/Paint/makePaintMask.ts +5 -2
- package/src/Paint/makeRectFalloffPaintAlphaMask.ts +4 -2
- package/src/PixelData/PixelData.ts +15 -19
- package/src/PixelData/ReusablePixelData.ts +36 -0
- package/src/PixelData/_pixelData-types.ts +17 -0
- package/src/PixelData/applyAlphaMaskToPixelData.ts +80 -43
- package/src/PixelData/applyBinaryMaskToPixelData.ts +10 -8
- package/src/PixelData/applyMaskToPixelData.ts +4 -9
- package/src/PixelData/blendColorPixelData.ts +9 -8
- package/src/PixelData/blendColorPixelDataAlphaMask.ts +9 -7
- package/src/PixelData/blendColorPixelDataBinaryMask.ts +9 -7
- package/src/PixelData/blendColorPixelDataMask.ts +4 -2
- package/src/PixelData/blendColorPixelDataPaintAlphaMask.ts +4 -2
- package/src/PixelData/blendColorPixelDataPaintBinaryMask.ts +4 -2
- package/src/PixelData/blendColorPixelDataPaintMask.ts +5 -2
- package/src/PixelData/blendPixel.ts +6 -5
- package/src/PixelData/blendPixelData.ts +14 -13
- package/src/PixelData/blendPixelDataAlphaMask.ts +15 -13
- package/src/PixelData/blendPixelDataBinaryMask.ts +15 -13
- package/src/PixelData/blendPixelDataMask.ts +5 -3
- package/src/PixelData/blendPixelDataPaintBuffer.ts +5 -4
- package/src/PixelData/clearPixelDataFast.ts +4 -2
- package/src/PixelData/copyPixelData.ts +14 -0
- package/src/PixelData/extractPixelData.ts +8 -7
- package/src/PixelData/extractPixelDataBuffer.ts +9 -8
- package/src/PixelData/fillPixelData.ts +16 -14
- package/src/PixelData/fillPixelDataBinaryMask.ts +10 -8
- package/src/PixelData/fillPixelDataFast.ts +16 -14
- package/src/PixelData/invertPixelData.ts +9 -8
- package/src/PixelData/pixelDataToAlphaMask.ts +9 -8
- package/src/PixelData/reflectPixelData.ts +9 -9
- package/src/PixelData/resamplePixelData.ts +20 -9
- package/src/PixelData/rotatePixelData.ts +8 -7
- package/src/PixelData/uInt32ArrayToPixelData.ts +15 -0
- package/src/PixelData/writePaintBufferToPixelData.ts +5 -5
- package/src/PixelData/writePixelDataBuffer.ts +10 -9
- package/src/Rect/_rect-types.ts +7 -0
- package/src/Rect/getRectsBounds.ts +1 -1
- package/src/Rect/trimMaskRectBounds.ts +2 -1
- package/src/Rect/trimRectBounds.ts +1 -1
- package/src/Tile/MaskTile.ts +40 -0
- package/src/Tile/PixelTile.ts +23 -0
- package/src/{PixelTile/PixelTilePool.ts → Tile/TilePool.ts} +9 -9
- package/src/Tile/_tile-types.ts +33 -0
- package/src/_errors.ts +1 -0
- package/src/_types.ts +2 -118
- package/src/index.ts +57 -21
- package/src/ImageData/imageDataToUInt32Array.ts +0 -13
- package/src/Internal/helpers.ts +0 -5
- package/src/Paint/makeCirclePaintAlphaMask.ts +0 -41
- package/src/Paint/makeCirclePaintBinaryMask.ts +0 -29
- package/src/PixelTile/PixelTile.ts +0 -21
- /package/src/{Internal → Rect}/resolveClipping.ts +0 -0
|
@@ -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,5 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import { makeClippedBlit, resolveBlitClipping } from '../
|
|
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 {
|
|
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,
|
|
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,
|
|
16
|
+
return new ImageData(uint8ClampedArray, w, h)
|
|
17
17
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Base64EncodedUInt8Array, ImageDataLike, SerializedImageData } from '
|
|
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,5 +1,5 @@
|
|
|
1
|
-
import { MaskType } from '../
|
|
2
|
-
import { makeClippedBlit, resolveBlitClipping } from '../
|
|
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 '../
|
|
2
|
-
import { makeClippedBlit, resolveBlitClipping } from '../
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
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
|
-
|
|
72
|
-
|
|
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
|
-
|
|
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
|
-
|
|
43
|
+
if (id === undefined) {
|
|
44
|
+
id = colorMap.size
|
|
45
|
+
colorMap.set(colorKey, id)
|
|
46
|
+
}
|
|
81
47
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
colorMap.set(colorKey, id)
|
|
85
|
-
}
|
|
48
|
+
indexedData[i] = id
|
|
49
|
+
}
|
|
86
50
|
|
|
87
|
-
|
|
88
|
-
}
|
|
51
|
+
const palette = Uint32Array.from(colorMap.keys())
|
|
89
52
|
|
|
90
|
-
|
|
53
|
+
return makeIndexedImage(
|
|
54
|
+
width,
|
|
55
|
+
height,
|
|
56
|
+
indexedData,
|
|
57
|
+
palette,
|
|
58
|
+
transparentPalletIndex,
|
|
59
|
+
)
|
|
60
|
+
}
|
|
91
61
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
103
|
-
|
|
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
|
-
|
|
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 './
|
|
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):
|
|
9
|
+
export function getIndexedImageColorCounts(indexedImage: IndexedImage): Uint32Array {
|
|
10
10
|
const data = indexedImage.data
|
|
11
11
|
const palette = indexedImage.palette
|
|
12
|
-
const frequencies = new
|
|
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,18 +1,16 @@
|
|
|
1
|
-
import type { IndexedImage } from './
|
|
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 {
|
|
8
|
-
const result = new ImageData(
|
|
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
|
-
|
|
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 {
|
|
6
|
-
import {
|
|
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
|
|
18
|
-
source.
|
|
19
|
-
source.
|
|
20
|
-
|
|
21
|
-
factor,
|
|
22
|
-
)
|
|
17
|
+
const output = {
|
|
18
|
+
palette: source.palette,
|
|
19
|
+
transparentPalletIndex: source.transparentPalletIndex,
|
|
20
|
+
} as IndexedImage
|
|
23
21
|
|
|
24
|
-
return
|
|
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
|
}
|
|
@@ -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
|
+
// }
|
package/src/Mask/AlphaMask.ts
CHANGED
|
@@ -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
|
+
}
|