pixel-data-js 0.36.0 → 0.37.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/dist/index.prod.cjs +372 -303
- package/dist/index.prod.cjs.map +1 -1
- package/dist/index.prod.d.ts +109 -67
- package/dist/index.prod.js +368 -302
- package/dist/index.prod.js.map +1 -1
- package/package.json +2 -2
- package/src/Algorithm/floodFillSelection.ts +3 -2
- package/src/BlendModes/blend-modes-fast.ts +2 -1
- package/src/BlendModes/blend-modes-perfect.ts +2 -1
- package/src/Canvas/ReusableCanvas.ts +0 -5
- package/src/Color/_color-types.ts +8 -0
- package/src/Color/colorDistance.ts +9 -0
- package/src/Color/convert-color.ts +43 -0
- package/src/Color/lerpColor32.ts +44 -0
- package/src/Color/pack-color.ts +38 -0
- package/src/History/HistoryAction.ts +2 -2
- package/src/History/PixelAccumulator.ts +13 -15
- package/src/History/PixelMutator/mutatorBlendColor.ts +2 -1
- package/src/History/PixelMutator/mutatorBlendColorPaintAlphaMask.ts +1 -1
- package/src/History/PixelMutator/mutatorBlendColorPaintBinaryMask.ts +1 -1
- package/src/History/PixelMutator/mutatorBlendColorPaintMask.ts +23 -8
- package/src/History/PixelMutator/mutatorBlendColorPaintRect.ts +2 -1
- package/src/History/PixelMutator/mutatorBlendPixel.ts +2 -1
- package/src/History/PixelMutator/mutatorClear.ts +1 -1
- package/src/History/PixelMutator/mutatorFill.ts +1 -1
- package/src/History/PixelMutator/mutatorFillBinaryMask.ts +1 -1
- package/src/History/PixelWriter.ts +5 -5
- package/src/IndexedImage/IndexedImage.ts +1 -1
- package/src/IndexedImage/indexedImageToAverageColor.ts +3 -2
- package/src/Mask/_mask-types.ts +9 -0
- package/src/Paint/AlphaMaskPaintBuffer.ts +26 -26
- package/src/Paint/BinaryMaskPaintBuffer.ts +19 -19
- package/src/Paint/ColorPaintBuffer.ts +40 -42
- package/src/Paint/Commit/AlphaMaskPaintBufferCommitter.ts +1 -1
- package/src/Paint/Commit/AlphaMaskPaintBufferManager.ts +6 -7
- package/src/Paint/Commit/BinaryMaskPaintBufferCommitter.ts +1 -1
- package/src/Paint/Commit/BinaryMaskPaintBufferManager.ts +6 -7
- package/src/Paint/Commit/ColorPaintBufferManager.ts +6 -7
- package/src/Paint/Commit/commitColorPaintBuffer.ts +2 -6
- package/src/Paint/Commit/commitMaskPaintBuffer.ts +3 -7
- package/src/Paint/Render/AlphaMaskPaintBufferCanvasRenderer.ts +42 -25
- package/src/Paint/Render/BinaryMaskPaintBufferCanvasRenderer.ts +40 -24
- package/src/Paint/Render/ColorPaintBufferCanvasRenderer.ts +21 -21
- package/src/Paint/Render/PaintCursorRenderer.ts +3 -2
- package/src/Paint/eachTileInBounds.ts +9 -10
- package/src/PixelData/blendColorPixelData.ts +2 -1
- package/src/PixelData/blendColorPixelDataAlphaMask.ts +2 -1
- package/src/PixelData/blendColorPixelDataBinaryMask.ts +2 -1
- package/src/PixelData/blendColorPixelDataMask.ts +2 -1
- package/src/PixelData/blendColorPixelDataPaintAlphaMask.ts +1 -1
- package/src/PixelData/blendColorPixelDataPaintBinaryMask.ts +1 -1
- package/src/PixelData/blendColorPixelDataPaintMask.ts +19 -8
- package/src/PixelData/blendPixel.ts +2 -1
- package/src/PixelData/blendPixelData.ts +2 -1
- package/src/PixelData/blendPixelDataAlphaMask.ts +2 -1
- package/src/PixelData/blendPixelDataBinaryMask.ts +2 -1
- package/src/PixelData/blendPixelDataPaintBuffer.ts +2 -3
- package/src/PixelData/clearPixelDataFast.ts +1 -1
- package/src/PixelData/fillPixelData.ts +1 -1
- package/src/PixelData/fillPixelDataBinaryMask.ts +1 -1
- package/src/PixelData/fillPixelDataFast.ts +1 -1
- package/src/PixelData/writePaintBufferToPixelData.ts +1 -5
- package/src/Tile/MaskTile.ts +4 -0
- package/src/Tile/PixelTile.ts +2 -0
- package/src/Tile/TilePool.ts +9 -8
- package/src/Tile/TileTargetConfig.ts +27 -0
- package/src/Tile/_tile-types.ts +16 -0
- package/src/_types.ts +1 -6
- package/src/index.ts +7 -3
- package/src/History/PixelEngineConfig.ts +0 -28
- package/src/Internal/_constants.ts +0 -3
- package/src/color.ts +0 -112
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
3
|
-
import { CANVAS_CTX_FAILED } from '../../Internal/_errors'
|
|
1
|
+
import type { ReusableCanvasFactory } from '../../Canvas/_canvas-types'
|
|
2
|
+
import { makeReusableOffscreenCanvas } from '../../Canvas/ReusableCanvas'
|
|
4
3
|
import type { ColorPaintBuffer } from '../ColorPaintBuffer'
|
|
5
4
|
|
|
6
5
|
export type ColorPaintBufferCanvasRenderer = ReturnType<typeof makeColorPaintBufferCanvasRenderer>
|
|
7
6
|
|
|
8
|
-
export function makeColorPaintBufferCanvasRenderer(
|
|
7
|
+
export function makeColorPaintBufferCanvasRenderer<T extends HTMLCanvasElement | OffscreenCanvas>(
|
|
9
8
|
paintBuffer: ColorPaintBuffer,
|
|
10
|
-
|
|
9
|
+
reusableCanvasFactory?: () => ReusableCanvasFactory<T>,
|
|
11
10
|
) {
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const tileShift = config.tileShift
|
|
15
|
-
const lookup = paintBuffer.lookup
|
|
11
|
+
const factory = (reusableCanvasFactory ?? makeReusableOffscreenCanvas) as unknown as () => ReusableCanvasFactory<T>
|
|
12
|
+
const getBuffer = factory()
|
|
16
13
|
|
|
17
|
-
|
|
18
|
-
const ctx = canvas.getContext('2d')
|
|
19
|
-
if (!ctx) throw new Error(CANVAS_CTX_FAILED)
|
|
20
|
-
ctx.imageSmoothingEnabled = false
|
|
21
|
-
|
|
22
|
-
return function drawPaintBuffer(
|
|
14
|
+
function draw(
|
|
23
15
|
targetCtx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
|
|
24
16
|
alpha = 255,
|
|
25
17
|
compOperation: GlobalCompositeOperation = 'source-over',
|
|
26
18
|
): void {
|
|
19
|
+
const buff = getBuffer(paintBuffer.config.tileSize, paintBuffer.config.tileSize)
|
|
20
|
+
const lookup = paintBuffer.lookup
|
|
21
|
+
const length = lookup.length
|
|
22
|
+
const ctx = buff.ctx
|
|
23
|
+
const canvas = buff.canvas
|
|
27
24
|
|
|
28
25
|
targetCtx.globalAlpha = alpha / 255
|
|
29
26
|
targetCtx.globalCompositeOperation = compOperation
|
|
30
27
|
|
|
31
|
-
for (let i = 0; i <
|
|
28
|
+
for (let i = 0; i < length; i++) {
|
|
32
29
|
const tile = lookup[i]
|
|
33
30
|
|
|
34
31
|
if (tile) {
|
|
35
|
-
const dx = tile.tx << tileShift
|
|
36
|
-
const dy = tile.ty << tileShift
|
|
37
|
-
|
|
38
32
|
ctx.putImageData(tile.imageData, 0, 0)
|
|
39
|
-
|
|
40
|
-
targetCtx.drawImage(canvas, dx, dy)
|
|
33
|
+
targetCtx.drawImage(canvas, tile.x, tile.y)
|
|
41
34
|
}
|
|
42
35
|
}
|
|
43
36
|
|
|
44
37
|
targetCtx.globalAlpha = 1
|
|
45
38
|
targetCtx.globalCompositeOperation = 'source-over'
|
|
46
39
|
}
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
draw,
|
|
43
|
+
setBuffer(value: ColorPaintBuffer) {
|
|
44
|
+
paintBuffer = value
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
47
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { type Color32 } from '../../_types'
|
|
2
1
|
import type { ReusableCanvasFactory } from '../../Canvas/_canvas-types'
|
|
3
2
|
import { makeReusableOffscreenCanvas } from '../../Canvas/ReusableCanvas'
|
|
4
|
-
import {
|
|
3
|
+
import type { Color32 } from '../../Color/_color-types'
|
|
4
|
+
|
|
5
|
+
import { packColor } from '../../Color/pack-color'
|
|
5
6
|
import { _macro_paintRectCenterOffset } from '../../Internal/macros'
|
|
6
7
|
import { type BinaryMask, MaskType } from '../../Mask/_mask-types'
|
|
7
8
|
import { makeBinaryMaskFromAlphaMask } from '../../Mask/BinaryMask/makeBinaryMaskFromAlphaMask'
|
|
@@ -1,32 +1,31 @@
|
|
|
1
|
-
import type { PixelEngineConfig } from '../History/PixelEngineConfig'
|
|
2
1
|
import type { Rect } from '../Rect/_rect-types'
|
|
3
|
-
import type { Tile } from '../Tile/_tile-types'
|
|
2
|
+
import type { Tile, TileTargetMeta } from '../Tile/_tile-types'
|
|
4
3
|
import type { TilePool } from '../Tile/TilePool'
|
|
5
4
|
|
|
6
5
|
export function eachTileInBounds<T extends Tile>(
|
|
7
|
-
config:
|
|
6
|
+
config: TileTargetMeta,
|
|
8
7
|
lookup: (T | undefined)[],
|
|
9
8
|
tilePool: TilePool<T>,
|
|
10
9
|
bounds: Rect,
|
|
11
10
|
callback: (tile: T, bX: number, bY: number, bW: number, bH: number) => void,
|
|
12
11
|
): void {
|
|
13
|
-
const {
|
|
12
|
+
const { targetRows, targetColumns, tileSize } = config
|
|
14
13
|
|
|
15
|
-
const x1 = Math.max(0, bounds.x
|
|
16
|
-
const y1 = Math.max(0, bounds.y
|
|
17
|
-
const x2 = Math.min(targetColumns - 1, (bounds.x + bounds.w - 1)
|
|
18
|
-
const y2 = Math.min(targetRows - 1, (bounds.y + bounds.h - 1)
|
|
14
|
+
const x1 = Math.max(0, Math.floor(bounds.x / tileSize))
|
|
15
|
+
const y1 = Math.max(0, Math.floor(bounds.y / tileSize))
|
|
16
|
+
const x2 = Math.min(targetColumns - 1, Math.floor((bounds.x + bounds.w - 1) / tileSize))
|
|
17
|
+
const y2 = Math.min(targetRows - 1, Math.floor((bounds.y + bounds.h - 1) / tileSize))
|
|
19
18
|
|
|
20
19
|
if (x1 > x2 || y1 > y2) return
|
|
21
20
|
|
|
22
21
|
for (let ty = y1; ty <= y2; ty++) {
|
|
23
22
|
const rowOffset = ty * targetColumns
|
|
24
|
-
const tileTop = ty
|
|
23
|
+
const tileTop = ty * tileSize
|
|
25
24
|
|
|
26
25
|
for (let tx = x1; tx <= x2; tx++) {
|
|
27
26
|
const id = rowOffset + tx
|
|
28
27
|
const tile = lookup[id] ?? (lookup[id] = tilePool.getTile(id, tx, ty))
|
|
29
|
-
const tileLeft = tx
|
|
28
|
+
const tileLeft = tx * tileSize
|
|
30
29
|
|
|
31
30
|
const startX = bounds.x > tileLeft ? bounds.x : tileLeft
|
|
32
31
|
const startY = bounds.y > tileTop ? bounds.y : tileTop
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type ColorBlendOptions } from '../_types'
|
|
2
2
|
import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
|
|
3
|
+
import type { Color32 } from '../Color/_color-types'
|
|
3
4
|
import type { PixelData32 } from './_pixelData-types'
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ColorBlendMaskOptions } from '../_types'
|
|
2
2
|
import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
|
|
3
|
+
import type { Color32 } from '../Color/_color-types'
|
|
3
4
|
import type { AlphaMask } from '../Mask/_mask-types'
|
|
4
5
|
import type { PixelData32 } from './_pixelData-types'
|
|
5
6
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ColorBlendMaskOptions } from '../_types'
|
|
2
2
|
import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
|
|
3
|
+
import type { Color32 } from '../Color/_color-types'
|
|
3
4
|
import type { BinaryMask } from '../Mask/_mask-types'
|
|
4
5
|
import type { PixelData32 } from './_pixelData-types'
|
|
5
6
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type ColorBlendMaskOptions } from '../_types'
|
|
2
|
+
import type { Color32 } from '../Color/_color-types'
|
|
2
3
|
import { type Mask, MaskType } from '../Mask/_mask-types'
|
|
3
4
|
import type { PixelData32 } from './_pixelData-types'
|
|
4
5
|
import { blendColorPixelDataAlphaMask } from './blendColorPixelDataAlphaMask'
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type Color32 } from '../_types'
|
|
2
1
|
import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
|
|
2
|
+
import type { Color32 } from '../Color/_color-types'
|
|
3
3
|
import type { PaintAlphaMask } from '../Paint/_paint-types'
|
|
4
4
|
import type { PixelData32 } from './_pixelData-types'
|
|
5
5
|
import { blendColorPixelDataAlphaMask } from './blendColorPixelDataAlphaMask'
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type Color32 } from '../_types'
|
|
2
1
|
import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
|
|
2
|
+
import type { Color32 } from '../Color/_color-types'
|
|
3
3
|
import type { PaintBinaryMask } from '../Paint/_paint-types'
|
|
4
4
|
import type { PixelData32 } from './_pixelData-types'
|
|
5
5
|
import { blendColorPixelDataBinaryMask } from './blendColorPixelDataBinaryMask'
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { type Color32 } from '../_types'
|
|
2
1
|
import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
|
|
2
|
+
import type { Color32 } from '../Color/_color-types'
|
|
3
3
|
import { MaskType } from '../Mask/_mask-types'
|
|
4
|
-
import type { PaintMask } from '../Paint/_paint-types'
|
|
4
|
+
import type { PaintMask, PaintRect } from '../Paint/_paint-types'
|
|
5
5
|
import type { PixelData32 } from './_pixelData-types'
|
|
6
|
+
import { blendColorPixelData } from './blendColorPixelData'
|
|
6
7
|
import { blendColorPixelDataAlphaMask } from './blendColorPixelDataAlphaMask'
|
|
7
8
|
import { blendColorPixelDataBinaryMask } from './blendColorPixelDataBinaryMask'
|
|
8
9
|
|
|
@@ -11,12 +12,14 @@ const SCRATCH_OPTS = {
|
|
|
11
12
|
y: 0,
|
|
12
13
|
alpha: 255,
|
|
13
14
|
blendFn: sourceOverPerfect,
|
|
15
|
+
w: undefined as number | undefined,
|
|
16
|
+
h: undefined as number | undefined,
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
export function blendColorPixelDataPaintMask(
|
|
17
|
-
|
|
20
|
+
target: PixelData32,
|
|
18
21
|
color: Color32,
|
|
19
|
-
mask: PaintMask,
|
|
22
|
+
mask: PaintMask | PaintRect,
|
|
20
23
|
x: number,
|
|
21
24
|
y: number,
|
|
22
25
|
alpha = 255,
|
|
@@ -29,10 +32,18 @@ export function blendColorPixelDataPaintMask(
|
|
|
29
32
|
SCRATCH_OPTS.y = ty
|
|
30
33
|
SCRATCH_OPTS.alpha = alpha
|
|
31
34
|
SCRATCH_OPTS.blendFn = blendFn
|
|
35
|
+
SCRATCH_OPTS.w = undefined
|
|
36
|
+
SCRATCH_OPTS.h = undefined
|
|
32
37
|
|
|
33
|
-
if (mask.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
if (mask.data) {
|
|
39
|
+
if (mask.type === MaskType.BINARY) {
|
|
40
|
+
return blendColorPixelDataBinaryMask(target, color, mask, SCRATCH_OPTS)
|
|
41
|
+
} else {
|
|
42
|
+
return blendColorPixelDataAlphaMask(target, color, mask, SCRATCH_OPTS)
|
|
43
|
+
}
|
|
37
44
|
}
|
|
45
|
+
SCRATCH_OPTS.w = mask.w
|
|
46
|
+
SCRATCH_OPTS.h = mask.h
|
|
47
|
+
|
|
48
|
+
return blendColorPixelData(target, color, SCRATCH_OPTS)
|
|
38
49
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { BlendColor32
|
|
1
|
+
import type { BlendColor32 } from '../_types'
|
|
2
2
|
import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
|
|
3
|
+
import type { Color32 } from '../Color/_color-types'
|
|
3
4
|
import type { PixelData32 } from './_pixelData-types'
|
|
4
5
|
|
|
5
6
|
export function blendPixel(
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type PixelBlendOptions } from '../_types'
|
|
2
2
|
import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
|
|
3
|
+
import type { Color32 } from '../Color/_color-types'
|
|
3
4
|
import type { PixelData32 } from './_pixelData-types'
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type PixelBlendMaskOptions } from '../_types'
|
|
2
2
|
import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
|
|
3
|
+
import type { Color32 } from '../Color/_color-types'
|
|
3
4
|
import type { AlphaMask } from '../Mask/_mask-types'
|
|
4
5
|
import type { PixelData32 } from './_pixelData-types'
|
|
5
6
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PixelBlendMaskOptions } from '../_types'
|
|
2
2
|
import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
|
|
3
|
+
import type { Color32 } from '../Color/_color-types'
|
|
3
4
|
import type { BinaryMask } from '../Mask/_mask-types'
|
|
4
5
|
import type { PixelData32 } from './_pixelData-types'
|
|
5
6
|
|
|
@@ -17,14 +17,13 @@ export function blendPixelDataPaintBuffer(
|
|
|
17
17
|
blendFn?: BlendColor32,
|
|
18
18
|
blendPixelDataFn = blendPixelData,
|
|
19
19
|
): void {
|
|
20
|
-
const tileShift = paintBuffer.config.tileShift
|
|
21
20
|
const lookup = paintBuffer.lookup
|
|
22
21
|
for (let i = 0; i < lookup.length; i++) {
|
|
23
22
|
const tile = lookup[i]
|
|
24
23
|
|
|
25
24
|
if (tile) {
|
|
26
|
-
const x = tile.
|
|
27
|
-
const y = tile.
|
|
25
|
+
const x = tile.x
|
|
26
|
+
const y = tile.y
|
|
28
27
|
|
|
29
28
|
SCRATCH_OPTS.x = x
|
|
30
29
|
SCRATCH_OPTS.y = y
|
|
@@ -10,17 +10,13 @@ export function writePaintBufferToPixelData(
|
|
|
10
10
|
paintBuffer: ColorPaintBuffer,
|
|
11
11
|
writePixelDataBufferFn = writePixelDataBuffer,
|
|
12
12
|
) {
|
|
13
|
-
const tileShift = paintBuffer.config.tileShift
|
|
14
13
|
const lookup = paintBuffer.lookup
|
|
15
14
|
|
|
16
15
|
for (let i = 0; i < lookup.length; i++) {
|
|
17
16
|
const tile = lookup[i]
|
|
18
17
|
|
|
19
18
|
if (tile) {
|
|
20
|
-
|
|
21
|
-
const dy = tile.ty << tileShift
|
|
22
|
-
|
|
23
|
-
writePixelDataBufferFn(target, tile.data, dx, dy, tile.w, tile.h)
|
|
19
|
+
writePixelDataBufferFn(target, tile.data, tile.x, tile.y, tile.w, tile.h)
|
|
24
20
|
}
|
|
25
21
|
}
|
|
26
22
|
}
|
package/src/Tile/MaskTile.ts
CHANGED
|
@@ -14,6 +14,8 @@ export const makeAlphaMaskTile: TileFactory<AlphaMaskTile> = (
|
|
|
14
14
|
data: new Uint8Array(tileArea),
|
|
15
15
|
w: tileSize,
|
|
16
16
|
h: tileSize,
|
|
17
|
+
x: tx * tileSize,
|
|
18
|
+
y: ty * tileSize,
|
|
17
19
|
id,
|
|
18
20
|
tx,
|
|
19
21
|
ty,
|
|
@@ -33,6 +35,8 @@ export const makeBinaryMaskTile: TileFactory<BinaryMaskTile> = (
|
|
|
33
35
|
data: new Uint8Array(tileArea),
|
|
34
36
|
w: tileSize,
|
|
35
37
|
h: tileSize,
|
|
38
|
+
x: tx * tileSize,
|
|
39
|
+
y: ty * tileSize,
|
|
36
40
|
id,
|
|
37
41
|
tx,
|
|
38
42
|
ty,
|
package/src/Tile/PixelTile.ts
CHANGED
package/src/Tile/TilePool.ts
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
import type { PixelEngineConfig } from '../History/PixelEngineConfig'
|
|
2
1
|
import type { Tile, TileFactory } from './_tile-types'
|
|
3
2
|
|
|
4
3
|
export class TilePool<T extends Tile> {
|
|
5
4
|
public pool: T[]
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
private tileArea: number
|
|
6
|
+
protected tileArea: number
|
|
9
7
|
|
|
10
8
|
constructor(
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
protected tileSize: number,
|
|
10
|
+
protected tileFactory: TileFactory<T>,
|
|
13
11
|
) {
|
|
14
12
|
this.pool = []
|
|
15
|
-
this.tileSize =
|
|
16
|
-
this.tileArea =
|
|
13
|
+
this.tileSize = tileSize
|
|
14
|
+
this.tileArea = tileSize * tileSize
|
|
17
15
|
}
|
|
18
16
|
|
|
19
17
|
getTile(
|
|
@@ -22,11 +20,14 @@ export class TilePool<T extends Tile> {
|
|
|
22
20
|
ty: number,
|
|
23
21
|
): T {
|
|
24
22
|
let tile = this.pool.pop()
|
|
23
|
+
const tileSize = this.tileSize
|
|
25
24
|
|
|
26
25
|
if (tile) {
|
|
27
26
|
tile.id = id
|
|
28
27
|
tile.tx = tx
|
|
29
28
|
tile.ty = ty
|
|
29
|
+
tile.x = tx * tileSize
|
|
30
|
+
tile.y = ty * tileSize
|
|
30
31
|
|
|
31
32
|
// Wipe dirty memory from previous uses before handing it out
|
|
32
33
|
tile.data.fill(0)
|
|
@@ -38,7 +39,7 @@ export class TilePool<T extends Tile> {
|
|
|
38
39
|
id,
|
|
39
40
|
tx,
|
|
40
41
|
ty,
|
|
41
|
-
|
|
42
|
+
tileSize,
|
|
42
43
|
this.tileArea,
|
|
43
44
|
)
|
|
44
45
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { PixelData } from '../PixelData/_pixelData-types'
|
|
2
|
+
import type { TileTargetConfig, TileTargetMeta } from './_tile-types'
|
|
3
|
+
|
|
4
|
+
export function makeTileTargetConfig(
|
|
5
|
+
tileSize: number,
|
|
6
|
+
target: PixelData,
|
|
7
|
+
): TileTargetConfig {
|
|
8
|
+
return {
|
|
9
|
+
target,
|
|
10
|
+
...makeTileTargetMeta(tileSize, target),
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function makeTileTargetMeta(
|
|
15
|
+
tileSize: number,
|
|
16
|
+
target: { w: number, h: number },
|
|
17
|
+
): TileTargetMeta {
|
|
18
|
+
return {
|
|
19
|
+
targetWidth: target.w,
|
|
20
|
+
targetHeight: target.h,
|
|
21
|
+
tileSize: tileSize,
|
|
22
|
+
invTileSize: 1 / tileSize,
|
|
23
|
+
tileArea: tileSize * tileSize,
|
|
24
|
+
targetColumns: Math.ceil(target.w / tileSize),
|
|
25
|
+
targetRows: Math.ceil(target.h / tileSize),
|
|
26
|
+
}
|
|
27
|
+
}
|
package/src/Tile/_tile-types.ts
CHANGED
|
@@ -11,6 +11,8 @@ interface BaseTile {
|
|
|
11
11
|
id: number
|
|
12
12
|
tx: number
|
|
13
13
|
ty: number
|
|
14
|
+
x: number
|
|
15
|
+
y: number
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
export interface PixelTile extends PixelData, BaseTile {
|
|
@@ -31,3 +33,17 @@ export type TileFactory<T extends Tile> = (
|
|
|
31
33
|
tileSize: number,
|
|
32
34
|
tileArea: number,
|
|
33
35
|
) => T
|
|
36
|
+
|
|
37
|
+
export interface TileTargetMeta {
|
|
38
|
+
readonly tileArea: number
|
|
39
|
+
readonly targetColumns: number
|
|
40
|
+
readonly targetRows: number
|
|
41
|
+
readonly targetWidth: number
|
|
42
|
+
readonly targetHeight: number
|
|
43
|
+
readonly tileSize: number
|
|
44
|
+
readonly invTileSize: number
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface TileTargetConfig extends TileTargetMeta {
|
|
48
|
+
readonly target: PixelData
|
|
49
|
+
}
|
package/src/_types.ts
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
|
+
import type { Color32 } from './Color/_color-types'
|
|
1
2
|
import type { BinaryMask } from './Mask/_mask-types'
|
|
2
3
|
|
|
3
|
-
/** ALL values are 0-255 (including alpha which in CSS is 0-1) */
|
|
4
|
-
export type RGBA = { r: number, g: number, b: number, a: number }
|
|
5
|
-
|
|
6
|
-
/** Represents a 32-bit color in 0xAABBGGRR (Little endian) */
|
|
7
|
-
export type Color32 = number & { readonly __brandColor32: unique symbol }
|
|
8
|
-
|
|
9
4
|
/**
|
|
10
5
|
* A function that defines how to combine a source color with a destination color.
|
|
11
6
|
* @param src - The incoming color (source).
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// generated by `npm run re-index`
|
|
1
|
+
// generated by `npm run re-index`
|
|
2
2
|
|
|
3
3
|
export * from './_errors'
|
|
4
4
|
export * from './_types'
|
|
@@ -24,7 +24,10 @@ export * from './Clipboard/getImageDataFromClipboard'
|
|
|
24
24
|
export * from './Clipboard/writeImageDataToClipboard'
|
|
25
25
|
export * from './Clipboard/writeImgBlobToClipboard'
|
|
26
26
|
|
|
27
|
-
export * from './
|
|
27
|
+
export * from './Color/_color-types'
|
|
28
|
+
export * from './Color/lerpColor32'
|
|
29
|
+
export * from './Color/convert-color'
|
|
30
|
+
export * from './Color/pack-color'
|
|
28
31
|
|
|
29
32
|
export * from './Control/BatchedQueue'
|
|
30
33
|
export * from './Control/RenderQueue'
|
|
@@ -32,7 +35,6 @@ export * from './Control/RenderQueue'
|
|
|
32
35
|
export * from './History/HistoryAction'
|
|
33
36
|
export * from './History/HistoryManager'
|
|
34
37
|
export * from './History/PixelAccumulator'
|
|
35
|
-
export * from './History/PixelEngineConfig'
|
|
36
38
|
export * from './History/PixelMutator'
|
|
37
39
|
|
|
38
40
|
export * from './History/PixelMutator/mutatorApplyAlphaMask'
|
|
@@ -181,3 +183,5 @@ export * from './Tile/_tile-types'
|
|
|
181
183
|
export * from './Tile/MaskTile'
|
|
182
184
|
export * from './Tile/PixelTile'
|
|
183
185
|
export * from './Tile/TilePool'
|
|
186
|
+
export * from './Tile/TileTargetConfig'
|
|
187
|
+
export { colorDistance } from './Color/colorDistance'
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import type { PixelData } from '../PixelData/_pixelData-types'
|
|
2
|
-
|
|
3
|
-
export class PixelEngineConfig {
|
|
4
|
-
readonly tileSize: number
|
|
5
|
-
// pixelX = tileX << tileShift
|
|
6
|
-
// pixelY = tileY << tileShift
|
|
7
|
-
readonly tileShift: number
|
|
8
|
-
readonly tileMask: number
|
|
9
|
-
readonly tileArea: number
|
|
10
|
-
readonly target!: PixelData
|
|
11
|
-
readonly targetColumns: number = 0
|
|
12
|
-
readonly targetRows: number = 0
|
|
13
|
-
|
|
14
|
-
constructor(tileSize: number, target: PixelData) {
|
|
15
|
-
// Ensure it's a power of 2 to guarantee bitwise math works
|
|
16
|
-
if ((tileSize & (tileSize - 1)) !== 0) {
|
|
17
|
-
throw new Error('tileSize must be a power of 2')
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
this.tileSize = tileSize
|
|
21
|
-
this.tileShift = 31 - Math.clz32(tileSize)
|
|
22
|
-
this.tileMask = tileSize - 1
|
|
23
|
-
this.tileArea = tileSize * tileSize
|
|
24
|
-
this.target = target
|
|
25
|
-
this.targetColumns = (target.w + this.tileMask) >> this.tileShift
|
|
26
|
-
this.targetRows = (target.h + this.tileMask) >> this.tileShift
|
|
27
|
-
}
|
|
28
|
-
}
|
package/src/color.ts
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import type { Color32, RGBA } from './_types'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Packs RGBA into a 32-bit integer compatible with
|
|
5
|
-
* Little-Endian Uint32Array views on ImageData.
|
|
6
|
-
*/
|
|
7
|
-
export function packColor(r: number, g: number, b: number, a: number): Color32 {
|
|
8
|
-
return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function packRGBA({ r, g, b, a }: RGBA): Color32 {
|
|
12
|
-
return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const unpackRed = (packed: Color32): number => (packed >>> 0) & 0xFF
|
|
16
|
-
export const unpackGreen = (packed: Color32): number => (packed >>> 8) & 0xFF
|
|
17
|
-
export const unpackBlue = (packed: Color32): number => (packed >>> 16) & 0xFF
|
|
18
|
-
export const unpackAlpha = (packed: Color32): number => (packed >>> 24) & 0xFF
|
|
19
|
-
|
|
20
|
-
export function unpackColor(packed: Color32): RGBA {
|
|
21
|
-
return {
|
|
22
|
-
r: (packed >>> 0) & 0xFF,
|
|
23
|
-
g: (packed >>> 8) & 0xFF,
|
|
24
|
-
b: (packed >>> 16) & 0xFF,
|
|
25
|
-
a: (packed >>> 24) & 0xFF,
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const SCRATCH_RGBA: RGBA = { r: 0, g: 0, b: 0, a: 0 }
|
|
30
|
-
|
|
31
|
-
// uses a scratch arg for memory perf. be careful about re-use.
|
|
32
|
-
export function unpackColorTo(packed: Color32, scratch = SCRATCH_RGBA): RGBA {
|
|
33
|
-
scratch.r = (packed >>> 0) & 0xFF
|
|
34
|
-
scratch.g = (packed >>> 8) & 0xFF
|
|
35
|
-
scratch.b = (packed >>> 16) & 0xFF
|
|
36
|
-
scratch.a = (packed >>> 24) & 0xFF
|
|
37
|
-
return scratch
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function colorDistance(a: Color32, b: Color32): number {
|
|
41
|
-
const dr = (a & 0xFF) - (b & 0xFF)
|
|
42
|
-
const dg = ((a >>> 8) & 0xFF) - ((b >>> 8) & 0xFF)
|
|
43
|
-
const db = ((a >>> 16) & 0xFF) - ((b >>> 16) & 0xFF)
|
|
44
|
-
const da = ((a >>> 24) & 0xFF) - ((b >>> 24) & 0xFF)
|
|
45
|
-
return dr * dr + dg * dg + db * db + da * da
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Linearly interpolates between two 32-bit colors using a floating-point weight.
|
|
50
|
-
* * This is the preferred method for UI animations or scenarios where high
|
|
51
|
-
* precision is required. It uses the standard `a + t * (b - a)` formula
|
|
52
|
-
* for each channel.
|
|
53
|
-
* @param a - The starting color as a 32-bit integer (AABBGGRR).
|
|
54
|
-
* @param b - The target color as a 32-bit integer (AABBGGRR).
|
|
55
|
-
* @param t - The interpolation factor between 0.0 and 1.0.
|
|
56
|
-
* @returns The interpolated 32-bit color.
|
|
57
|
-
*/
|
|
58
|
-
export function lerpColor32(a: Color32, b: Color32, t: number): Color32 {
|
|
59
|
-
const r = (a & 0xFF) + t * ((b & 0xFF) - (a & 0xFF))
|
|
60
|
-
const g = ((a >>> 8) & 0xFF) + t * (((b >>> 8) & 0xFF) - ((a >>> 8) & 0xFF))
|
|
61
|
-
const b_ = ((a >>> 16) & 0xFF) + t * (((b >>> 16) & 0xFF) - ((a >>> 16) & 0xFF))
|
|
62
|
-
const a_ = ((a >>> 24) & 0xFF) + t * (((b >>> 24) & 0xFF) - ((a >>> 24) & 0xFF))
|
|
63
|
-
|
|
64
|
-
return ((a_ << 24) | (b_ << 16) | (g << 8) | r) >>> 0 as Color32
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Linearly interpolates between two 32-bit colors using integer fixed-point math.
|
|
69
|
-
* Highly optimized for image processing and real-time blitting. It processes
|
|
70
|
-
* channels in parallel using bitmasks (RB and GA pairs).
|
|
71
|
-
* **Note:** Subject to a 1-bit drift (rounding down) due to fast bit-shift division.
|
|
72
|
-
* @param src - The source (foreground) color as a 32-bit integer.
|
|
73
|
-
* @param dst - The destination (background) color as a 32-bit integer.
|
|
74
|
-
* @param w - The blend weight as a byte value from 0 to 255. Where 0 is 100% dst and 255 is 100% src
|
|
75
|
-
* @returns The blended 32-bit color.
|
|
76
|
-
*/export function lerpColor32Fast(src: Color32, dst: Color32, w: number): Color32 {
|
|
77
|
-
const invA = 255 - w
|
|
78
|
-
|
|
79
|
-
// Masking Red and Blue: 0x00FF00FF
|
|
80
|
-
// We process R and B in one go, then shift back down
|
|
81
|
-
const rb = (((src & 0x00FF00FF) * w + (dst & 0x00FF00FF) * invA) >>> 8) & 0x00FF00FF
|
|
82
|
-
|
|
83
|
-
// Masking Green and Alpha: 0xFF00FF00
|
|
84
|
-
// We shift down first to avoid overflow, then shift back up
|
|
85
|
-
const ga = ((((src >>> 8) & 0x00FF00FF) * w + ((dst >>> 8) & 0x00FF00FF) * invA) >>> 8) & 0x00FF00FF
|
|
86
|
-
|
|
87
|
-
return (rb | (ga << 8)) >>> 0 as Color32
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Convert 0xAABBGGRR to #RRGGBBAA
|
|
91
|
-
export function color32ToHex(color: Color32): string {
|
|
92
|
-
const r = (color & 0xFF).toString(16).padStart(2, '0')
|
|
93
|
-
const g = ((color >>> 8) & 0xFF).toString(16).padStart(2, '0')
|
|
94
|
-
const b = ((color >>> 16) & 0xFF).toString(16).padStart(2, '0')
|
|
95
|
-
const a = ((color >>> 24) & 0xFF).toString(16).padStart(2, '0')
|
|
96
|
-
return `#${r}${g}${b}${a}`
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Converts a 32-bit integer (0xAABBGGRR) to a CSS rgba() string.
|
|
101
|
-
* Example: 0xFF0000FF -> "rgba(255,0,0,1)"
|
|
102
|
-
*/
|
|
103
|
-
export function color32ToCssRGBA(color: Color32): string {
|
|
104
|
-
const r = color & 0xFF
|
|
105
|
-
const g = (color >>> 8) & 0xFF
|
|
106
|
-
const b = (color >>> 16) & 0xFF
|
|
107
|
-
const a = (color >>> 24) & 0xFF
|
|
108
|
-
|
|
109
|
-
const alpha = Number((a / 255).toFixed(3))
|
|
110
|
-
|
|
111
|
-
return `rgba(${r},${g},${b},${alpha})`
|
|
112
|
-
}
|