pixel-data-js 0.26.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.
- package/README.md +12 -2
- package/dist/index.prod.cjs +2227 -1050
- package/dist/index.prod.cjs.map +1 -1
- package/dist/index.prod.d.ts +549 -424
- package/dist/index.prod.js +2171 -1028
- 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/{mutatorBlendPaintRect.ts → mutatorBlendColorPaintRect.ts} +5 -5
- 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 +2 -2
- 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/_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 +339 -0
- package/src/Paint/AlphaMaskPaintBufferCanvasRenderer.ts +78 -0
- package/src/Paint/BinaryMaskPaintBuffer.ts +254 -0
- package/src/Paint/BinaryMaskPaintBufferCanvasRenderer.ts +67 -0
- package/src/Paint/{PaintBuffer.ts → ColorPaintBuffer.ts} +148 -77
- package/src/Paint/{PaintBufferCanvasRenderer.ts → ColorPaintBufferCanvasRenderer.ts} +6 -5
- package/src/Paint/PaintCursorRenderer.ts +117 -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 +47 -22
- 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
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pixel-data-js",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.28.0",
|
|
5
5
|
"packageManager": "pnpm@10.33.0",
|
|
6
6
|
"description": "JS Pixel and ImageData operations",
|
|
7
7
|
"author": {
|
|
@@ -17,7 +17,9 @@
|
|
|
17
17
|
"color",
|
|
18
18
|
"blend modes",
|
|
19
19
|
"color blend",
|
|
20
|
-
"canvas"
|
|
20
|
+
"canvas",
|
|
21
|
+
"draw",
|
|
22
|
+
"paint"
|
|
21
23
|
],
|
|
22
24
|
"main": "./dist/index.prod.cjs",
|
|
23
25
|
"module": "./dist/index.prod.js",
|
|
@@ -45,18 +47,17 @@
|
|
|
45
47
|
"scripts": {
|
|
46
48
|
"build": "tsup",
|
|
47
49
|
"test": "vitest --coverage --project unit",
|
|
48
|
-
"re-index": "tsx _scripts/re-index.ts",
|
|
49
|
-
"check": "npm run check-circ && npm run re-index && npm run sort && npm run typecheck",
|
|
50
50
|
"test:build": "pnpm build && vitest run --project dist",
|
|
51
|
-
"check-circ": "tsx _scripts/check-circular.ts",
|
|
52
51
|
"test:mutation": "stryker run",
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"check-exports": "tsx _scripts/check-exports.ts",
|
|
52
|
+
"re-index": "tsx _scripts/re-index.ts",
|
|
53
|
+
"check-circ": "tsx _scripts/check-circular.ts",
|
|
56
54
|
"sort": "tsx _scripts/apply-sorting.ts",
|
|
55
|
+
"check": "pnpm run check-circ && pnpm run re-index && pnpm run sort && pnpm run typecheck && pnpm typedoc",
|
|
56
|
+
"typecheck": "tsc --noEmit",
|
|
57
57
|
"bench": "tsx ./benchmark/run.ts",
|
|
58
58
|
"bench:all": "tsx ./benchmark/run-all.ts",
|
|
59
|
-
"bench:compare": "tsx ./benchmark/compare.ts"
|
|
59
|
+
"bench:compare": "tsx ./benchmark/compare.ts",
|
|
60
|
+
"docs": "pnpm typedoc"
|
|
60
61
|
},
|
|
61
62
|
"devDependencies": {
|
|
62
63
|
"@clack/prompts": "^1.1.0",
|
|
@@ -67,7 +68,6 @@
|
|
|
67
68
|
"@stryker-mutator/vitest-runner": "^9.5.1",
|
|
68
69
|
"@types/cli-progress": "^3.11.6",
|
|
69
70
|
"@types/node": "^25.2.3",
|
|
70
|
-
"@vitest/browser": "3.2.4",
|
|
71
71
|
"@vitest/coverage-v8": "3.2.4",
|
|
72
72
|
"cli-progress": "^3.12.0",
|
|
73
73
|
"cmd-ts": "^0.15.0",
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
"typedoc-plugin-mdn-links": "^5.1.1",
|
|
89
89
|
"typedoc-rhineai-theme": "^1.2.0",
|
|
90
90
|
"typescript": "^5.9.3",
|
|
91
|
-
"unplugin-inline": "^1.
|
|
91
|
+
"unplugin-inline": "^1.15.0",
|
|
92
92
|
"vite-tsconfig-paths": "^6.1.1",
|
|
93
93
|
"vitest": "3.2.4"
|
|
94
94
|
},
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type Color32 } from '../_types'
|
|
2
2
|
import { colorDistance } from '../color'
|
|
3
3
|
import { extractImageDataBuffer } from '../ImageData/extractImageDataBuffer'
|
|
4
|
-
import type
|
|
4
|
+
import { type BinaryMaskRect, MaskType } from '../Mask/_mask-types'
|
|
5
|
+
import type { PixelData } from '../PixelData/_pixelData-types'
|
|
6
|
+
import type { Rect } from '../Rect/_rect-types'
|
|
5
7
|
import { trimMaskRectBounds } from '../Rect/trimMaskRectBounds'
|
|
6
8
|
|
|
7
9
|
export type FloodFillResult = BinaryMaskRect & {
|
|
@@ -11,7 +13,7 @@ export type FloodFillResult = BinaryMaskRect & {
|
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
|
-
* Performs a color-based flood fill selection
|
|
16
|
+
* Performs a color-based flood fill selection {@link PixelData}.
|
|
15
17
|
* This utility identifies pixels starting from a specific coordinate that fall within a
|
|
16
18
|
* color tolerance. It can operate in "contiguous" mode (classic bucket fill) or
|
|
17
19
|
* "non-contiguous" mode (selects all matching pixels in the buffer).
|
|
@@ -51,9 +53,9 @@ export function floodFillSelection(
|
|
|
51
53
|
out?: FloodFillResult,
|
|
52
54
|
): FloodFillResult | null {
|
|
53
55
|
|
|
54
|
-
const data32 = target.
|
|
55
|
-
const width = target.
|
|
56
|
-
const height = target.
|
|
56
|
+
const data32 = target.data
|
|
57
|
+
const width = target.w
|
|
58
|
+
const height = target.h
|
|
57
59
|
|
|
58
60
|
const lx = bounds?.x ?? 0
|
|
59
61
|
const ly = bounds?.y ?? 0
|
|
@@ -9,18 +9,18 @@ export function forEachLinePoint(
|
|
|
9
9
|
y1: number,
|
|
10
10
|
callback: (x: number, y: number) => void,
|
|
11
11
|
): void {
|
|
12
|
+
|
|
13
|
+
if (x0 === x1 && y0 === y1) {
|
|
14
|
+
callback(x0, y0)
|
|
15
|
+
return
|
|
16
|
+
}
|
|
17
|
+
|
|
12
18
|
const dx = x1 - x0
|
|
13
19
|
const dy = y1 - y0
|
|
14
20
|
|
|
15
21
|
// Determine the number of steps based on the longest axis
|
|
16
22
|
const steps = Math.max(Math.abs(dx), Math.abs(dy))
|
|
17
23
|
|
|
18
|
-
// Handle the zero-length line (Single Stamp Case)
|
|
19
|
-
if (steps === 0) {
|
|
20
|
-
callback(x0, y0)
|
|
21
|
-
return
|
|
22
|
-
}
|
|
23
|
-
|
|
24
24
|
const xInc = dx / steps
|
|
25
25
|
const yInc = dy / steps
|
|
26
26
|
|
|
@@ -1,26 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
data: null as null | Int32Array,
|
|
3
|
-
width: 0,
|
|
4
|
-
height: 0,
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @internal
|
|
9
|
-
*/
|
|
10
|
-
type Resample32Result = { data: Int32Array; width: number; height: number }
|
|
1
|
+
import type { MutablePixelData32, PixelData32 } from '../PixelData/_pixelData-types'
|
|
11
2
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
*/
|
|
15
|
-
export function resample32(
|
|
16
|
-
srcData32: Uint32Array | Int32Array,
|
|
3
|
+
export function resampleUint32Array<T extends PixelData32, M extends MutablePixelData32>(
|
|
4
|
+
srcData32: Uint32Array,
|
|
17
5
|
srcW: number,
|
|
18
6
|
srcH: number,
|
|
19
7
|
factor: number,
|
|
20
|
-
|
|
8
|
+
out?: M,
|
|
9
|
+
): T {
|
|
21
10
|
const dstW = Math.max(1, (srcW * factor) | 0)
|
|
22
11
|
const dstH = Math.max(1, (srcH * factor) | 0)
|
|
23
|
-
const dstData = new
|
|
12
|
+
const dstData = new Uint32Array(dstW * dstH)
|
|
24
13
|
|
|
25
14
|
// Use the reciprocal to map back precisely
|
|
26
15
|
const scaleX = srcW / dstW
|
|
@@ -38,9 +27,10 @@ export function resample32(
|
|
|
38
27
|
}
|
|
39
28
|
}
|
|
40
29
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
30
|
+
out = out ?? {} as M
|
|
31
|
+
out.data = dstData
|
|
32
|
+
out.w = dstW
|
|
33
|
+
out.h = dstH
|
|
44
34
|
|
|
45
|
-
return
|
|
35
|
+
return out as unknown as T
|
|
46
36
|
}
|
|
@@ -4,6 +4,165 @@ import { makeBlendModeRegistry } from './BlendModeRegistry'
|
|
|
4
4
|
|
|
5
5
|
export const overwriteFast = overwriteBase
|
|
6
6
|
|
|
7
|
+
export const sourceInFast: BlendColor32 = (src, dst) => {
|
|
8
|
+
const da = (dst >>> 24) & 0xFF
|
|
9
|
+
if (da === 0) return 0 as Color32
|
|
10
|
+
if (da === 255) return src
|
|
11
|
+
|
|
12
|
+
const sa = (src >>> 24) & 0xFF
|
|
13
|
+
const sr = src & 0xFF
|
|
14
|
+
const sg = (src >>> 8) & 0xFF
|
|
15
|
+
const sb = (src >>> 16) & 0xFF
|
|
16
|
+
|
|
17
|
+
const r = (sr * da) >> 8
|
|
18
|
+
const g = (sg * da) >> 8
|
|
19
|
+
const b = (sb * da) >> 8
|
|
20
|
+
const a = (sa * da) >> 8
|
|
21
|
+
|
|
22
|
+
return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const sourceOutFast: BlendColor32 = (src, dst) => {
|
|
26
|
+
const da = (dst >>> 24) & 0xFF
|
|
27
|
+
if (da === 255) return 0 as Color32
|
|
28
|
+
if (da === 0) return src
|
|
29
|
+
|
|
30
|
+
const sa = (src >>> 24) & 0xFF
|
|
31
|
+
const sr = src & 0xFF
|
|
32
|
+
const sg = (src >>> 8) & 0xFF
|
|
33
|
+
const sb = (src >>> 16) & 0xFF
|
|
34
|
+
|
|
35
|
+
const invDa = 255 - da
|
|
36
|
+
const r = (sr * invDa) >> 8
|
|
37
|
+
const g = (sg * invDa) >> 8
|
|
38
|
+
const b = (sb * invDa) >> 8
|
|
39
|
+
const a = (sa * invDa) >> 8
|
|
40
|
+
|
|
41
|
+
return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const sourceAtopFast: BlendColor32 = (src, dst) => {
|
|
45
|
+
const sa = (src >>> 24) & 0xFF
|
|
46
|
+
const da = (dst >>> 24) & 0xFF
|
|
47
|
+
if (da === 0) return 0 as Color32
|
|
48
|
+
|
|
49
|
+
const sr = src & 0xFF
|
|
50
|
+
const sg = (src >>> 8) & 0xFF
|
|
51
|
+
const sb = (src >>> 16) & 0xFF
|
|
52
|
+
const dr = dst & 0xFF
|
|
53
|
+
const dg = (dst >>> 8) & 0xFF
|
|
54
|
+
const db = (dst >>> 16) & 0xFF
|
|
55
|
+
|
|
56
|
+
const invSa = 255 - sa
|
|
57
|
+
const r = (sr * da + dr * invSa) >> 8
|
|
58
|
+
const g = (sg * da + dg * invSa) >> 8
|
|
59
|
+
const b = (sb * da + db * invSa) >> 8
|
|
60
|
+
|
|
61
|
+
return ((da << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const destinationOverFast: BlendColor32 = (src, dst) => {
|
|
65
|
+
const da = (dst >>> 24) & 0xFF
|
|
66
|
+
if (da === 255) return dst
|
|
67
|
+
if (da === 0) return src
|
|
68
|
+
|
|
69
|
+
const sa = (src >>> 24) & 0xFF
|
|
70
|
+
const sr = src & 0xFF
|
|
71
|
+
const sg = (src >>> 8) & 0xFF
|
|
72
|
+
const sb = (src >>> 16) & 0xFF
|
|
73
|
+
const dr = dst & 0xFF
|
|
74
|
+
const dg = (dst >>> 8) & 0xFF
|
|
75
|
+
const db = (dst >>> 16) & 0xFF
|
|
76
|
+
|
|
77
|
+
const invDa = 255 - da
|
|
78
|
+
const r = (dr * 255 + sr * invDa) >> 8
|
|
79
|
+
const g = (dg * 255 + sg * invDa) >> 8
|
|
80
|
+
const b = (db * 255 + sb * invDa) >> 8
|
|
81
|
+
const a = (da * 255 + sa * invDa) >> 8
|
|
82
|
+
|
|
83
|
+
return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export const destinationInFast: BlendColor32 = (src, dst) => {
|
|
87
|
+
const sa = (src >>> 24) & 0xFF
|
|
88
|
+
if (sa === 0) return 0 as Color32
|
|
89
|
+
if (sa === 255) return dst
|
|
90
|
+
|
|
91
|
+
const da = (dst >>> 24) & 0xFF
|
|
92
|
+
const dr = dst & 0xFF
|
|
93
|
+
const dg = (dst >>> 8) & 0xFF
|
|
94
|
+
const db = (dst >>> 16) & 0xFF
|
|
95
|
+
|
|
96
|
+
const r = (dr * sa) >> 8
|
|
97
|
+
const g = (dg * sa) >> 8
|
|
98
|
+
const b = (db * sa) >> 8
|
|
99
|
+
const a = (da * sa) >> 8
|
|
100
|
+
|
|
101
|
+
return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export const destinationOutFast: BlendColor32 = (src, dst) => {
|
|
105
|
+
const sa = (src >>> 24) & 0xFF
|
|
106
|
+
if (sa === 255) return 0 as Color32
|
|
107
|
+
if (sa === 0) return dst
|
|
108
|
+
|
|
109
|
+
const da = (dst >>> 24) & 0xFF
|
|
110
|
+
const dr = dst & 0xFF
|
|
111
|
+
const dg = (dst >>> 8) & 0xFF
|
|
112
|
+
const db = (dst >>> 16) & 0xFF
|
|
113
|
+
|
|
114
|
+
const invSa = 255 - sa
|
|
115
|
+
const r = (dr * invSa) >> 8
|
|
116
|
+
const g = (dg * invSa) >> 8
|
|
117
|
+
const b = (db * invSa) >> 8
|
|
118
|
+
const a = (da * invSa) >> 8
|
|
119
|
+
|
|
120
|
+
return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export const destinationAtopFast: BlendColor32 = (src, dst) => {
|
|
124
|
+
const sa = (src >>> 24) & 0xFF
|
|
125
|
+
if (sa === 0) return 0 as Color32 // Rule: Final Alpha = Sa
|
|
126
|
+
const da = (dst >>> 24) & 0xFF
|
|
127
|
+
if (da === 0) return 0 as Color32
|
|
128
|
+
|
|
129
|
+
const sr = src & 0xFF
|
|
130
|
+
const sg = (src >>> 8) & 0xFF
|
|
131
|
+
const sb = (src >>> 16) & 0xFF
|
|
132
|
+
const dr = dst & 0xFF
|
|
133
|
+
const dg = (dst >>> 8) & 0xFF
|
|
134
|
+
const db = (dst >>> 16) & 0xFF
|
|
135
|
+
|
|
136
|
+
const invDa = 255 - da
|
|
137
|
+
const r = (dr * sa + sr * invDa) >> 8
|
|
138
|
+
const g = (dg * sa + sg * invDa) >> 8
|
|
139
|
+
const b = (db * sa + sb * invDa) >> 8
|
|
140
|
+
|
|
141
|
+
return ((sa << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export const xorFast: BlendColor32 = (src, dst) => {
|
|
145
|
+
const sa = (src >>> 24) & 0xFF
|
|
146
|
+
const da = (dst >>> 24) & 0xFF
|
|
147
|
+
|
|
148
|
+
const sr = src & 0xFF
|
|
149
|
+
const sg = (src >>> 8) & 0xFF
|
|
150
|
+
const sb = (src >>> 16) & 0xFF
|
|
151
|
+
const dr = dst & 0xFF
|
|
152
|
+
const dg = (dst >>> 8) & 0xFF
|
|
153
|
+
const db = (dst >>> 16) & 0xFF
|
|
154
|
+
|
|
155
|
+
const invDa = 255 - da
|
|
156
|
+
const invSa = 255 - sa
|
|
157
|
+
|
|
158
|
+
const r = (sr * invDa + dr * invSa) >> 8
|
|
159
|
+
const g = (sg * invDa + dg * invSa) >> 8
|
|
160
|
+
const b = (sb * invDa + db * invSa) >> 8
|
|
161
|
+
const a = (sa * invDa + da * invSa) >> 8
|
|
162
|
+
|
|
163
|
+
return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
|
|
164
|
+
}
|
|
165
|
+
|
|
7
166
|
export const sourceOverFast: BlendColor32 = (src, dst) => {
|
|
8
167
|
const sa = (src >>> 24) & 0xFF
|
|
9
168
|
if (sa === 255) return src
|
|
@@ -577,6 +736,16 @@ export const divideFast: BlendColor32 = (src, dst) => {
|
|
|
577
736
|
|
|
578
737
|
export const BASE_FAST_BLEND_MODE_FUNCTIONS: Record<number, BlendColor32> = {
|
|
579
738
|
[BaseBlendMode.overwrite]: overwriteFast,
|
|
739
|
+
|
|
740
|
+
[BaseBlendMode.sourceIn]: sourceInFast,
|
|
741
|
+
[BaseBlendMode.sourceOut]: sourceOutFast,
|
|
742
|
+
[BaseBlendMode.sourceAtop]: sourceAtopFast,
|
|
743
|
+
[BaseBlendMode.destinationOver]: destinationOverFast,
|
|
744
|
+
[BaseBlendMode.destinationIn]: destinationInFast,
|
|
745
|
+
[BaseBlendMode.destinationOut]: destinationOutFast,
|
|
746
|
+
[BaseBlendMode.destinationAtop]: destinationAtopFast,
|
|
747
|
+
[BaseBlendMode.xor]: xorFast,
|
|
748
|
+
|
|
580
749
|
[BaseBlendMode.sourceOver]: sourceOverFast,
|
|
581
750
|
[BaseBlendMode.darken]: darkenFast,
|
|
582
751
|
[BaseBlendMode.multiply]: multiplyFast,
|
|
@@ -4,6 +4,203 @@ import { makeBlendModeRegistry } from './BlendModeRegistry'
|
|
|
4
4
|
|
|
5
5
|
export const overwritePerfect = overwriteBase
|
|
6
6
|
|
|
7
|
+
export const sourceInPerfect: BlendColor32 = (src, dst) => {
|
|
8
|
+
const da = (dst >>> 24) & 0xFF
|
|
9
|
+
if (da === 0) return 0 as Color32
|
|
10
|
+
if (da === 255) return src
|
|
11
|
+
|
|
12
|
+
const sa = (src >>> 24) & 0xFF
|
|
13
|
+
const sr = src & 0xFF
|
|
14
|
+
const sg = (src >>> 8) & 0xFF
|
|
15
|
+
const sb = (src >>> 16) & 0xFF
|
|
16
|
+
|
|
17
|
+
// Result: [Sa * Da, Sc * Da]
|
|
18
|
+
const tR = sr * da
|
|
19
|
+
const r = (tR + 1 + (tR >> 8)) >> 8
|
|
20
|
+
const tG = sg * da
|
|
21
|
+
const g = (tG + 1 + (tG >> 8)) >> 8
|
|
22
|
+
const tB = sb * da
|
|
23
|
+
const b = (tB + 1 + (tB >> 8)) >> 8
|
|
24
|
+
const tA = sa * da
|
|
25
|
+
const a = (tA + 1 + (tA >> 8)) >> 8
|
|
26
|
+
|
|
27
|
+
return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const sourceOutPerfect: BlendColor32 = (src, dst) => {
|
|
31
|
+
const da = (dst >>> 24) & 0xFF
|
|
32
|
+
if (da === 255) return 0 as Color32
|
|
33
|
+
if (da === 0) return src
|
|
34
|
+
|
|
35
|
+
const sa = (src >>> 24) & 0xFF
|
|
36
|
+
const sr = src & 0xFF
|
|
37
|
+
const sg = (src >>> 8) & 0xFF
|
|
38
|
+
const sb = (src >>> 16) & 0xFF
|
|
39
|
+
|
|
40
|
+
const invDa = 255 - da
|
|
41
|
+
// Result: [Sa * (1 - Da), Sc * (1 - Da)]
|
|
42
|
+
const tR = sr * invDa
|
|
43
|
+
const r = (tR + 1 + (tR >> 8)) >> 8
|
|
44
|
+
const tG = sg * invDa
|
|
45
|
+
const g = (tG + 1 + (tG >> 8)) >> 8
|
|
46
|
+
const tB = sb * invDa
|
|
47
|
+
const b = (tB + 1 + (tB >> 8)) >> 8
|
|
48
|
+
const tA = sa * invDa
|
|
49
|
+
const a = (tA + 1 + (tA >> 8)) >> 8
|
|
50
|
+
|
|
51
|
+
return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const sourceAtopPerfect: BlendColor32 = (src, dst) => {
|
|
55
|
+
const sa = (src >>> 24) & 0xFF
|
|
56
|
+
const da = (dst >>> 24) & 0xFF
|
|
57
|
+
if (da === 0) return 0 as Color32
|
|
58
|
+
|
|
59
|
+
const sr = src & 0xFF
|
|
60
|
+
const sg = (src >>> 8) & 0xFF
|
|
61
|
+
const sb = (src >>> 16) & 0xFF
|
|
62
|
+
const dr = dst & 0xFF
|
|
63
|
+
const dg = (dst >>> 8) & 0xFF
|
|
64
|
+
const db = (dst >>> 16) & 0xFF
|
|
65
|
+
|
|
66
|
+
const invSa = 255 - sa
|
|
67
|
+
// Result: [Da, Sc * Da + Dc * (1 - Sa)]
|
|
68
|
+
const tR = sr * da + dr * invSa
|
|
69
|
+
const r = (tR + 1 + (tR >> 8)) >> 8
|
|
70
|
+
const tG = sg * da + dg * invSa
|
|
71
|
+
const g = (tG + 1 + (tG >> 8)) >> 8
|
|
72
|
+
const tB = sb * da + db * invSa
|
|
73
|
+
const b = (tB + 1 + (tB >> 8)) >> 8
|
|
74
|
+
|
|
75
|
+
return ((da << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const destinationOverPerfect: BlendColor32 = (src, dst) => {
|
|
79
|
+
const da = (dst >>> 24) & 0xFF
|
|
80
|
+
if (da === 255) return dst
|
|
81
|
+
if (da === 0) return src
|
|
82
|
+
|
|
83
|
+
const sa = (src >>> 24) & 0xFF
|
|
84
|
+
const sr = src & 0xFF
|
|
85
|
+
const sg = (src >>> 8) & 0xFF
|
|
86
|
+
const sb = (src >>> 16) & 0xFF
|
|
87
|
+
const dr = dst & 0xFF
|
|
88
|
+
const dg = (dst >>> 8) & 0xFF
|
|
89
|
+
const db = (dst >>> 16) & 0xFF
|
|
90
|
+
|
|
91
|
+
const invDa = 255 - da
|
|
92
|
+
// Result: [Da + Sa * (1 - Da), Dc + Sc * (1 - Da)]
|
|
93
|
+
const tR = dr * 255 + sr * invDa
|
|
94
|
+
const r = (tR + 1 + (tR >> 8)) >> 8
|
|
95
|
+
const tG = dg * 255 + sg * invDa
|
|
96
|
+
const g = (tG + 1 + (tG >> 8)) >> 8
|
|
97
|
+
const tB = db * 255 + sb * invDa
|
|
98
|
+
const b = (tB + 1 + (tB >> 8)) >> 8
|
|
99
|
+
const tA = da * 255 + sa * invDa
|
|
100
|
+
const a = (tA + 1 + (tA >> 8)) >> 8
|
|
101
|
+
|
|
102
|
+
return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export const destinationInPerfect: BlendColor32 = (src, dst) => {
|
|
106
|
+
const sa = (src >>> 24) & 0xFF
|
|
107
|
+
if (sa === 0) return 0 as Color32
|
|
108
|
+
if (sa === 255) return dst
|
|
109
|
+
|
|
110
|
+
const da = (dst >>> 24) & 0xFF
|
|
111
|
+
const dr = dst & 0xFF
|
|
112
|
+
const dg = (dst >>> 8) & 0xFF
|
|
113
|
+
const db = (dst >>> 16) & 0xFF
|
|
114
|
+
|
|
115
|
+
// Result: [Da * Sa, Dc * Sa]
|
|
116
|
+
const tR = dr * sa
|
|
117
|
+
const r = (tR + 1 + (tR >> 8)) >> 8
|
|
118
|
+
const tG = dg * sa
|
|
119
|
+
const g = (tG + 1 + (tG >> 8)) >> 8
|
|
120
|
+
const tB = db * sa
|
|
121
|
+
const b = (tB + 1 + (tB >> 8)) >> 8
|
|
122
|
+
const tA = da * sa
|
|
123
|
+
const a = (tA + 1 + (tA >> 8)) >> 8
|
|
124
|
+
|
|
125
|
+
return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export const destinationOutPerfect: BlendColor32 = (src, dst) => {
|
|
129
|
+
const sa = (src >>> 24) & 0xFF
|
|
130
|
+
if (sa === 255) return 0 as Color32
|
|
131
|
+
if (sa === 0) return dst
|
|
132
|
+
|
|
133
|
+
const da = (dst >>> 24) & 0xFF
|
|
134
|
+
const dr = dst & 0xFF
|
|
135
|
+
const dg = (dst >>> 8) & 0xFF
|
|
136
|
+
const db = (dst >>> 16) & 0xFF
|
|
137
|
+
|
|
138
|
+
const invSa = 255 - sa
|
|
139
|
+
// Result: [Da * (1 - Sa), Dc * (1 - Sa)]
|
|
140
|
+
const tR = dr * invSa
|
|
141
|
+
const r = (tR + 1 + (tR >> 8)) >> 8
|
|
142
|
+
const tG = dg * invSa
|
|
143
|
+
const g = (tG + 1 + (tG >> 8)) >> 8
|
|
144
|
+
const tB = db * invSa
|
|
145
|
+
const b = (tB + 1 + (tB >> 8)) >> 8
|
|
146
|
+
const tA = da * invSa
|
|
147
|
+
const a = (tA + 1 + (tA >> 8)) >> 8
|
|
148
|
+
|
|
149
|
+
return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export const destinationAtopPerfect: BlendColor32 = (src, dst) => {
|
|
153
|
+
const sa = (src >>> 24) & 0xFF
|
|
154
|
+
if (sa === 0) return 0 as Color32 // Rule: Final Alpha = Sa
|
|
155
|
+
const da = (dst >>> 24) & 0xFF
|
|
156
|
+
if (da === 0) return 0 as Color32
|
|
157
|
+
|
|
158
|
+
const sr = src & 0xFF
|
|
159
|
+
const sg = (src >>> 8) & 0xFF
|
|
160
|
+
const sb = (src >>> 16) & 0xFF
|
|
161
|
+
const dr = dst & 0xFF
|
|
162
|
+
const dg = (dst >>> 8) & 0xFF
|
|
163
|
+
const db = (dst >>> 16) & 0xFF
|
|
164
|
+
|
|
165
|
+
const invDa = 255 - da
|
|
166
|
+
// Result: [Sa, Dc * Sa + Sc * (1 - Da)]
|
|
167
|
+
const tR = dr * sa + sr * invDa
|
|
168
|
+
const r = (tR + 1 + (tR >> 8)) >> 8
|
|
169
|
+
const tG = dg * sa + sg * invDa
|
|
170
|
+
const g = (tG + 1 + (tG >> 8)) >> 8
|
|
171
|
+
const tB = db * sa + sb * invDa
|
|
172
|
+
const b = (tB + 1 + (tB >> 8)) >> 8
|
|
173
|
+
|
|
174
|
+
return ((sa << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export const xorPerfect: BlendColor32 = (src, dst) => {
|
|
178
|
+
const sa = (src >>> 24) & 0xFF
|
|
179
|
+
const da = (dst >>> 24) & 0xFF
|
|
180
|
+
|
|
181
|
+
const sr = src & 0xFF
|
|
182
|
+
const sg = (src >>> 8) & 0xFF
|
|
183
|
+
const sb = (src >>> 16) & 0xFF
|
|
184
|
+
const dr = dst & 0xFF
|
|
185
|
+
const dg = (dst >>> 8) & 0xFF
|
|
186
|
+
const db = (dst >>> 16) & 0xFF
|
|
187
|
+
|
|
188
|
+
const invDa = 255 - da
|
|
189
|
+
const invSa = 255 - sa
|
|
190
|
+
|
|
191
|
+
// Result: [Sa * (1 - Da) + Da * (1 - Sa), Sc * (1 - Da) + Dc * (1 - Sa)]
|
|
192
|
+
const tR = sr * invDa + dr * invSa
|
|
193
|
+
const r = (tR + 1 + (tR >> 8)) >> 8
|
|
194
|
+
const tG = sg * invDa + dg * invSa
|
|
195
|
+
const g = (tG + 1 + (tG >> 8)) >> 8
|
|
196
|
+
const tB = sb * invDa + db * invSa
|
|
197
|
+
const b = (tB + 1 + (tB >> 8)) >> 8
|
|
198
|
+
const tA = sa * invDa + da * invSa
|
|
199
|
+
const a = (tA + 1 + (tA >> 8)) >> 8
|
|
200
|
+
|
|
201
|
+
return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
|
|
202
|
+
}
|
|
203
|
+
|
|
7
204
|
export const sourceOverPerfect: BlendColor32 = (src, dst) => {
|
|
8
205
|
const sa = (src >>> 24) & 0xFF
|
|
9
206
|
if (sa === 255) return src
|
|
@@ -748,6 +945,16 @@ export const dividePerfect: BlendColor32 = (src, dst) => {
|
|
|
748
945
|
|
|
749
946
|
export const BASE_PERFECT_BLEND_MODE_FUNCTIONS: Record<number, BlendColor32> = {
|
|
750
947
|
[BaseBlendMode.overwrite]: overwritePerfect,
|
|
948
|
+
|
|
949
|
+
[BaseBlendMode.sourceIn]: sourceInPerfect,
|
|
950
|
+
[BaseBlendMode.sourceOut]: sourceOutPerfect,
|
|
951
|
+
[BaseBlendMode.sourceAtop]: sourceAtopPerfect,
|
|
952
|
+
[BaseBlendMode.destinationOver]: destinationOverPerfect,
|
|
953
|
+
[BaseBlendMode.destinationIn]: destinationInPerfect,
|
|
954
|
+
[BaseBlendMode.destinationOut]: destinationOutPerfect,
|
|
955
|
+
[BaseBlendMode.destinationAtop]: destinationAtopPerfect,
|
|
956
|
+
[BaseBlendMode.xor]: xorPerfect,
|
|
957
|
+
|
|
751
958
|
[BaseBlendMode.sourceOver]: sourceOverPerfect,
|
|
752
959
|
[BaseBlendMode.darken]: darkenPerfect,
|
|
753
960
|
[BaseBlendMode.multiply]: multiplyPerfect,
|
|
@@ -24,6 +24,15 @@ export const BaseBlendMode = {
|
|
|
24
24
|
exclusion: 20,
|
|
25
25
|
subtract: 21,
|
|
26
26
|
divide: 22,
|
|
27
|
+
|
|
28
|
+
sourceIn: 23,
|
|
29
|
+
sourceOut: 24,
|
|
30
|
+
sourceAtop: 25,
|
|
31
|
+
destinationOver: 26,
|
|
32
|
+
destinationIn: 27,
|
|
33
|
+
destinationOut: 28,
|
|
34
|
+
destinationAtop: 29,
|
|
35
|
+
xor: 30,
|
|
27
36
|
} as const
|
|
28
37
|
|
|
29
38
|
export interface RequiredBlendModes {
|
|
@@ -1,54 +1,46 @@
|
|
|
1
|
-
import type { PixelCanvas } from './
|
|
2
|
-
import {
|
|
1
|
+
import type { DrawPixelLayer, DrawScreenLayer, PixelCanvas, ReusableCanvasFactory } from './_canvas-types'
|
|
2
|
+
import { makeReusableOffscreenCanvas } from './ReusableCanvas'
|
|
3
3
|
|
|
4
|
-
export type
|
|
5
|
-
|
|
6
|
-
export type CanvasFrameRenderer = ReturnType<typeof makeCanvasFrameRenderer>
|
|
4
|
+
export type CanvasFrameRenderer<T extends HTMLCanvasElement | OffscreenCanvas = OffscreenCanvas> =
|
|
5
|
+
ReturnType<typeof makeCanvasFrameRenderer<T>>
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
type Deps = Partial<typeof defaults>
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* @param deps - @hidden
|
|
16
|
-
*/
|
|
17
|
-
export function makeCanvasFrameRenderer(deps: Deps = defaults) {
|
|
18
|
-
const {
|
|
19
|
-
makeReusableCanvas = defaults.makeReusableCanvas,
|
|
20
|
-
} = deps
|
|
21
|
-
|
|
22
|
-
const bufferCanvas = makeReusableCanvas()
|
|
7
|
+
export function makeCanvasFrameRenderer<T extends HTMLCanvasElement | OffscreenCanvas = OffscreenCanvas>(
|
|
8
|
+
reusableCanvasFactory: () => ReusableCanvasFactory<T> = makeReusableOffscreenCanvas as unknown as () => ReusableCanvasFactory<T>,
|
|
9
|
+
) {
|
|
10
|
+
const bufferCanvas = reusableCanvasFactory()
|
|
23
11
|
|
|
24
12
|
return function renderCanvasFrame(
|
|
25
13
|
pixelCanvas: PixelCanvas,
|
|
26
14
|
scale: number,
|
|
27
15
|
getImageData: () => ImageData | undefined | null,
|
|
28
|
-
drawPixelLayer?: DrawPixelLayer
|
|
16
|
+
drawPixelLayer?: DrawPixelLayer<T>,
|
|
29
17
|
drawScreenLayer?: DrawScreenLayer,
|
|
30
18
|
) {
|
|
31
|
-
const { canvas, ctx } = pixelCanvas
|
|
32
19
|
|
|
33
|
-
|
|
34
|
-
const
|
|
20
|
+
const canvas = pixelCanvas.canvas
|
|
21
|
+
const ctx = pixelCanvas.ctx
|
|
22
|
+
const w = canvas.width
|
|
23
|
+
const h = canvas.height
|
|
24
|
+
|
|
25
|
+
// 1. Clear pixel buffer
|
|
26
|
+
const buffer = bufferCanvas(w, h)
|
|
35
27
|
|
|
36
28
|
// 2. Draw pixel data into pixel buffer
|
|
37
29
|
const img = getImageData()
|
|
38
30
|
if (img) {
|
|
39
|
-
|
|
31
|
+
buffer.ctx.putImageData(img, 0, 0)
|
|
40
32
|
}
|
|
41
33
|
|
|
42
34
|
// draw transient pixel data
|
|
43
|
-
drawPixelLayer?.(
|
|
35
|
+
drawPixelLayer?.(buffer.ctx)
|
|
44
36
|
|
|
45
37
|
// clear target canvas
|
|
46
38
|
ctx.setTransform(1, 0, 0, 1, 0, 0)
|
|
47
|
-
ctx.clearRect(0, 0,
|
|
39
|
+
ctx.clearRect(0, 0, w, h)
|
|
48
40
|
|
|
49
41
|
// Draw pixel buffer scaled onto screen
|
|
50
42
|
ctx.setTransform(scale, 0, 0, scale, 0, 0)
|
|
51
|
-
ctx.drawImage(
|
|
43
|
+
ctx.drawImage(buffer.canvas, 0, 0)
|
|
52
44
|
|
|
53
45
|
// Draw overlays in screen space
|
|
54
46
|
ctx.setTransform(1, 0, 0, 1, 0, 0)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { PixelData } from '../PixelData/_pixelData-types'
|
|
2
|
+
import type { ReusableCanvasFactory } from './_canvas-types'
|
|
3
|
+
import { makeReusableOffscreenCanvas } from './ReusableCanvas'
|
|
4
|
+
|
|
5
|
+
export type CanvasPixelDataRenderer = ReturnType<typeof makeCanvasPixelDataRenderer>
|
|
6
|
+
|
|
7
|
+
export function makeCanvasPixelDataRenderer<T extends HTMLCanvasElement | OffscreenCanvas = OffscreenCanvas>(
|
|
8
|
+
reusableCanvasFactory: () => ReusableCanvasFactory<T> = makeReusableOffscreenCanvas as unknown as () => ReusableCanvasFactory<T>,
|
|
9
|
+
) {
|
|
10
|
+
const bufferCanvas = reusableCanvasFactory()
|
|
11
|
+
|
|
12
|
+
return function drawPixelData(
|
|
13
|
+
targetCtx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
|
|
14
|
+
pixelData: PixelData,
|
|
15
|
+
x = 0,
|
|
16
|
+
y = 0,
|
|
17
|
+
): void {
|
|
18
|
+
const buffer = bufferCanvas(pixelData.w, pixelData.h)
|
|
19
|
+
|
|
20
|
+
buffer.ctx.putImageData(pixelData.imageData, 0, 0)
|
|
21
|
+
targetCtx.drawImage(buffer.canvas, x, y)
|
|
22
|
+
}
|
|
23
|
+
}
|