pixel-data-js 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.dev.cjs +571 -239
- package/dist/index.dev.cjs.map +1 -1
- package/dist/index.dev.js +559 -233
- package/dist/index.dev.js.map +1 -1
- package/dist/index.prod.cjs +571 -239
- package/dist/index.prod.cjs.map +1 -1
- package/dist/index.prod.d.ts +191 -147
- package/dist/index.prod.js +559 -233
- package/dist/index.prod.js.map +1 -1
- package/package.json +3 -4
- package/src/ImageData/copyImageData.ts +13 -0
- package/src/ImageData/{read-write-pixels.ts → extractImageData.ts} +4 -51
- package/src/ImageData/writeImageData.ts +54 -0
- package/src/Mask/copyMask.ts +10 -0
- package/src/Mask/invertMask.ts +25 -0
- package/src/Mask/mergeMasks.ts +105 -0
- package/src/PixelData/applyMaskToPixelData.ts +129 -0
- package/src/PixelData/blendColorPixelData.ts +157 -0
- package/src/PixelData/blendPixelData.ts +196 -0
- package/src/PixelData/clearPixelData.ts +14 -0
- package/src/PixelData/fillPixelData.ts +56 -0
- package/src/PixelData.ts +20 -0
- package/src/_types.ts +120 -12
- package/src/{ImageData/blend-modes.ts → blend-modes.ts} +1 -1
- package/src/index.ts +16 -5
- package/src/ImageData/blit.ts +0 -177
- package/src/ImageData/mask.ts +0 -150
package/src/_types.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
/** ALL values are 0-255 (including alpha which in CSS is 0-1) */
|
|
2
2
|
export type RGBA = { r: number, g: number, b: number, a: number }
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
/** Represents a 32-bit color in 0xAABBGGRR (Little endian) */
|
|
5
5
|
export type Color32 = number & { readonly __brandColor32: unique symbol }
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* A function that defines how to combine a source color with a destination color.
|
|
9
|
+
* @param src - The incoming color (source).
|
|
10
|
+
* @param dst - The existing color in the buffer (destination).
|
|
11
|
+
* @returns The resulting 32-bit color to be written to the buffer.
|
|
12
|
+
*/
|
|
7
13
|
export type BlendColor32 = (src: Color32, dst: Color32) => Color32
|
|
8
14
|
|
|
9
15
|
export type ImageDataLike = {
|
|
@@ -20,25 +26,127 @@ export type SerializedImageData = {
|
|
|
20
26
|
|
|
21
27
|
export type Base64EncodedUInt8Array = string & { readonly __brandBase64UInt8Array: unique symbol }
|
|
22
28
|
|
|
23
|
-
|
|
29
|
+
/** Rectangle definition */
|
|
30
|
+
export type Rect = {
|
|
31
|
+
x: number
|
|
32
|
+
y: number
|
|
33
|
+
w: number
|
|
34
|
+
h: number
|
|
35
|
+
}
|
|
24
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Defines how mask values should be interpreted during a draw operation.
|
|
39
|
+
*/
|
|
25
40
|
export enum MaskType {
|
|
41
|
+
/**
|
|
42
|
+
* Values are treated as alpha weights.
|
|
43
|
+
* 0 is skipped, values > 0 are processed.
|
|
44
|
+
*/
|
|
26
45
|
ALPHA,
|
|
46
|
+
/**
|
|
47
|
+
* Values are treated as on/off.
|
|
48
|
+
* 0 is fully transparent (skipped), any other value is fully opaque.
|
|
49
|
+
*/
|
|
27
50
|
BINARY
|
|
28
51
|
}
|
|
29
52
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
53
|
+
/** Strictly 0 or 1 */
|
|
54
|
+
export type BinaryMask = Uint8Array & { readonly __brand: 'Binary' }
|
|
55
|
+
/** Strictly 0-255 */
|
|
56
|
+
export type AlphaMask = Uint8Array & { readonly __brand: 'Alpha' }
|
|
57
|
+
|
|
58
|
+
export type AnyMask = BinaryMask | AlphaMask
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Configuration for pixel manipulation operations.
|
|
62
|
+
* Designed to be used by spreading a Rect object ({x, y, w, h}) directly.
|
|
63
|
+
*/
|
|
64
|
+
export interface PixelOptions {
|
|
65
|
+
/**
|
|
66
|
+
* The starting X coordinate in the destination buffer.
|
|
67
|
+
* @Defaults 0.
|
|
68
|
+
* */
|
|
69
|
+
x?: number
|
|
70
|
+
/**
|
|
71
|
+
* The starting Y coordinate in the destination buffer.
|
|
72
|
+
* @Default 0.
|
|
73
|
+
* */
|
|
74
|
+
y?: number
|
|
75
|
+
/**
|
|
76
|
+
* The width of the region to process.
|
|
77
|
+
* @Default Source width.
|
|
78
|
+
* */
|
|
79
|
+
w?: number
|
|
80
|
+
/**
|
|
81
|
+
* The height of the region to process.
|
|
82
|
+
* @Default Source height.
|
|
83
|
+
* */
|
|
84
|
+
h?: number
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Overall layer opacity 0-255.
|
|
88
|
+
* @default 255
|
|
89
|
+
*/
|
|
90
|
+
alpha?: number
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Mask width.
|
|
94
|
+
* @default w
|
|
95
|
+
* */
|
|
96
|
+
mw?: number
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* X offset into the mask buffer.
|
|
100
|
+
* @default 0
|
|
101
|
+
* */
|
|
102
|
+
mx?: number
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Y offset into the mask buffer.
|
|
106
|
+
* @default 0
|
|
107
|
+
* */
|
|
108
|
+
my?: number
|
|
109
|
+
|
|
110
|
+
/** An optional mask to restrict where pixels are written. */
|
|
111
|
+
mask?: AnyMask | null
|
|
112
|
+
|
|
113
|
+
/** The interpretation logic for the provided mask. Defaults to MaskType.Binary. */
|
|
114
|
+
maskType?: MaskType
|
|
115
|
+
|
|
116
|
+
/** If true the inverse of the mask will be applied */
|
|
117
|
+
invertMask?: boolean
|
|
34
118
|
}
|
|
35
119
|
|
|
36
|
-
|
|
37
|
-
|
|
120
|
+
/**
|
|
121
|
+
* Configuration for blitting (copying/blending) one image into another.
|
|
122
|
+
*/
|
|
123
|
+
export interface PixelBlendOptions extends PixelOptions {
|
|
124
|
+
/**
|
|
125
|
+
* The source rectangle x-coordinate
|
|
126
|
+
* @default 0
|
|
127
|
+
*/
|
|
128
|
+
sx?: number
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* The source rectangle y-coordinate
|
|
132
|
+
* @default 0
|
|
133
|
+
*/
|
|
134
|
+
sy?: number
|
|
135
|
+
|
|
136
|
+
/** The specific blending function/algorithm to use for pixel math. */
|
|
137
|
+
blendFn?: BlendColor32
|
|
38
138
|
}
|
|
39
139
|
|
|
40
|
-
|
|
41
|
-
|
|
140
|
+
/**
|
|
141
|
+
* Configuration for operations that require color blending.
|
|
142
|
+
*/
|
|
143
|
+
export interface ColorBlendOptions extends PixelOptions {
|
|
144
|
+
/** The blending logic used to combine source and destination pixels. */
|
|
145
|
+
blendFn?: BlendColor32
|
|
42
146
|
}
|
|
43
147
|
|
|
44
|
-
export type
|
|
148
|
+
export type ApplyMaskOptions = Omit<PixelOptions, 'mask'>
|
|
149
|
+
|
|
150
|
+
// export function invertBinaryMask(dst: BinaryMask): void
|
|
151
|
+
// export function invertAlphaMask(dst: AlphaMask): void
|
|
152
|
+
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
|
-
export * from './ImageData/blend-modes'
|
|
2
|
-
export * from './ImageData/blit'
|
|
3
|
-
export * from './ImageData/mask'
|
|
4
|
-
export * from './ImageData/read-write-pixels'
|
|
5
|
-
export * from './ImageData/serialization'
|
|
6
1
|
export * from './_types'
|
|
2
|
+
export * from './blend-modes'
|
|
7
3
|
export * from './color'
|
|
4
|
+
|
|
5
|
+
export * from './ImageData/copyImageData'
|
|
6
|
+
export * from './ImageData/extractImageData'
|
|
7
|
+
export * from './ImageData/serialization'
|
|
8
|
+
export * from './ImageData/writeImageData'
|
|
9
|
+
|
|
10
|
+
export * from './Mask/copyMask'
|
|
11
|
+
export * from './Mask/invertMask'
|
|
12
|
+
export * from './Mask/mergeMasks'
|
|
13
|
+
|
|
14
|
+
export * from './PixelData/applyMaskToPixelData'
|
|
15
|
+
export * from './PixelData/blendColorPixelData'
|
|
16
|
+
export * from './PixelData/blendPixelData'
|
|
17
|
+
export * from './PixelData/clearPixelData'
|
|
18
|
+
export * from './PixelData/fillPixelData'
|
package/src/ImageData/blit.ts
DELETED
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
import { type AnyMask, type BlendColor32, type Color32, type ImageDataLike, MaskType } from '../_types'
|
|
2
|
-
import { sourceOverColor32 } from './blend-modes'
|
|
3
|
-
|
|
4
|
-
export type BlendImageDataOptions = {
|
|
5
|
-
/**
|
|
6
|
-
* The x-coordinate in the destination image where the blend begins.
|
|
7
|
-
* @default 0
|
|
8
|
-
*/
|
|
9
|
-
dx?: number
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* The y-coordinate in the destination image where the blend begins.
|
|
13
|
-
* @default 0
|
|
14
|
-
*/
|
|
15
|
-
dy?: number
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* The x-coordinate of the top-left corner of the sub-rectangle
|
|
19
|
-
* of the source image to extract.
|
|
20
|
-
* @default 0
|
|
21
|
-
*/
|
|
22
|
-
sx?: number
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* The y-coordinate of the top-left corner of the sub-rectangle
|
|
26
|
-
* of the source image to extract.
|
|
27
|
-
* @default 0
|
|
28
|
-
*/
|
|
29
|
-
sy?: number
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* The width of the sub-rectangle of the source image to extract.
|
|
33
|
-
* Defaults to the full remaining width of the source.
|
|
34
|
-
*/
|
|
35
|
-
sw?: number
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* The height of the sub-rectangle of the source image to extract.
|
|
39
|
-
* Defaults to the full remaining height of the source.
|
|
40
|
-
*/
|
|
41
|
-
sh?: number
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Overall layer opacity, typically ranging from 0.0 (transparent) to 1.0 (opaque).
|
|
45
|
-
* @default 1.0
|
|
46
|
-
*/
|
|
47
|
-
opacity?: number
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Same as opacity but is 0-255 and faster when processing. If Present opacity is ignored.
|
|
51
|
-
* @default undefined
|
|
52
|
-
*/
|
|
53
|
-
alpha?: number
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* An optional alpha mask buffer.
|
|
57
|
-
* The values in this array (0-255) determine the intensity of the blend
|
|
58
|
-
* at each corresponding pixel.
|
|
59
|
-
*/
|
|
60
|
-
mask?: AnyMask | null
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* The specific blending function/algorithm to use for pixel math
|
|
64
|
-
* (e.g., Multiply, Screen, Overlay).
|
|
65
|
-
*/
|
|
66
|
-
blendFn?: BlendColor32
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Blits source ImageData into a destination ImageData using 32-bit integer bitwise blending.
|
|
71
|
-
* This function bypasses standard Canvas API limitations by operating directly on
|
|
72
|
-
* Uint32Array views. It supports various blend modes, binary/alpha masking, and
|
|
73
|
-
* automatic clipping of both source and destination bounds.
|
|
74
|
-
* @example
|
|
75
|
-
* blendImageData32(ctx.getImageData(0,0,100,100), sprite, {
|
|
76
|
-
* blendFn: COLOR_32_BLEND_MODES.multiply,
|
|
77
|
-
* mask: brushMask,
|
|
78
|
-
* maskMode: MaskMode.ALPHA
|
|
79
|
-
* });
|
|
80
|
-
*/
|
|
81
|
-
export function blendImageData(
|
|
82
|
-
dst: ImageDataLike,
|
|
83
|
-
src: ImageDataLike,
|
|
84
|
-
opts: BlendImageDataOptions,
|
|
85
|
-
) {
|
|
86
|
-
let {
|
|
87
|
-
dx = 0,
|
|
88
|
-
dy = 0,
|
|
89
|
-
sx = 0,
|
|
90
|
-
sy = 0,
|
|
91
|
-
sw = src.width,
|
|
92
|
-
sh = src.height,
|
|
93
|
-
opacity = 1,
|
|
94
|
-
alpha,
|
|
95
|
-
blendFn = sourceOverColor32,
|
|
96
|
-
mask,
|
|
97
|
-
} = opts
|
|
98
|
-
|
|
99
|
-
// 1. Clip Source Area
|
|
100
|
-
if (sx < 0) {
|
|
101
|
-
dx -= sx
|
|
102
|
-
sw += sx
|
|
103
|
-
sx = 0
|
|
104
|
-
}
|
|
105
|
-
if (sy < 0) {
|
|
106
|
-
dy -= sy
|
|
107
|
-
sh += sy
|
|
108
|
-
sy = 0
|
|
109
|
-
}
|
|
110
|
-
sw = Math.min(sw, src.width - sx)
|
|
111
|
-
sh = Math.min(sh, src.height - sy)
|
|
112
|
-
|
|
113
|
-
// 2. Clip Destination Area
|
|
114
|
-
if (dx < 0) {
|
|
115
|
-
sx -= dx
|
|
116
|
-
sw += dx
|
|
117
|
-
dx = 0
|
|
118
|
-
}
|
|
119
|
-
if (dy < 0) {
|
|
120
|
-
sy -= dy
|
|
121
|
-
sh += dy
|
|
122
|
-
dy = 0
|
|
123
|
-
}
|
|
124
|
-
const actualW = Math.min(sw, dst.width - dx)
|
|
125
|
-
const actualH = Math.min(sh, dst.height - dy)
|
|
126
|
-
|
|
127
|
-
if (actualW <= 0 || actualH <= 0) return
|
|
128
|
-
|
|
129
|
-
// 32-bit views of the same memory
|
|
130
|
-
const dst32 = new Uint32Array(dst.data.buffer)
|
|
131
|
-
const src32 = new Uint32Array(src.data.buffer)
|
|
132
|
-
|
|
133
|
-
const dw = dst.width
|
|
134
|
-
const sw_orig = src.width
|
|
135
|
-
|
|
136
|
-
const gAlpha = alpha !== undefined
|
|
137
|
-
? (alpha | 0)
|
|
138
|
-
: Math.round(opacity * 255)
|
|
139
|
-
|
|
140
|
-
const maskIsAlpha = mask?.type === MaskType.ALPHA
|
|
141
|
-
|
|
142
|
-
for (let iy = 0; iy < actualH; iy++) {
|
|
143
|
-
const dRow = (iy + dy) * dw
|
|
144
|
-
const sRow = (iy + sy) * sw_orig
|
|
145
|
-
|
|
146
|
-
for (let ix = 0; ix < actualW; ix++) {
|
|
147
|
-
const di = dRow + (ix + dx)
|
|
148
|
-
const si = sRow + (ix + sx)
|
|
149
|
-
|
|
150
|
-
let s = src32[si] as Color32
|
|
151
|
-
let sa = (s >>> 24) & 0xFF
|
|
152
|
-
|
|
153
|
-
// skip fully transparent pixel
|
|
154
|
-
if (sa === 0) continue
|
|
155
|
-
|
|
156
|
-
let activeWeight = gAlpha
|
|
157
|
-
|
|
158
|
-
if (mask) {
|
|
159
|
-
const m = mask.data[si]
|
|
160
|
-
if (m === 0) continue
|
|
161
|
-
activeWeight = maskIsAlpha ? (m * activeWeight + 128) >> 8 : activeWeight
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
if (activeWeight < 255) {
|
|
165
|
-
sa = (sa * activeWeight + 128) >> 8
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// If combined alpha is 0 after masking/opacity, skip the blend math
|
|
169
|
-
if (sa === 0) continue
|
|
170
|
-
|
|
171
|
-
// Re-pack source with final calculated alpha
|
|
172
|
-
s = ((s & 0x00FFFFFF) | (sa << 24)) >>> 0 as Color32
|
|
173
|
-
|
|
174
|
-
dst32[di] = blendFn(s, dst32[di] as Color32)
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
package/src/ImageData/mask.ts
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import type { AlphaMask, BinaryMask, ImageDataLike } from '../_types'
|
|
2
|
-
|
|
3
|
-
export type ApplyMaskOptions = {
|
|
4
|
-
/**
|
|
5
|
-
* The x-coordinate in the destination image where the mask begins.
|
|
6
|
-
* @default 0
|
|
7
|
-
*/
|
|
8
|
-
dx?: number
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* The y-coordinate in the destination image where the mask begins.
|
|
12
|
-
* @default 0
|
|
13
|
-
*/
|
|
14
|
-
dy?: number
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* The x-coordinate of the top-left corner of the sub-rectangle
|
|
18
|
-
* of the source image to extract.
|
|
19
|
-
* @default 0
|
|
20
|
-
*/
|
|
21
|
-
sx?: number
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* The y-coordinate of the top-left corner of the sub-rectangle
|
|
25
|
-
* of the source image to extract.
|
|
26
|
-
* @default 0
|
|
27
|
-
*/
|
|
28
|
-
sy?: number
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* The width of the sub-rectangle of the source image to extract.
|
|
32
|
-
* Defaults to the full remaining width of the source.
|
|
33
|
-
*/
|
|
34
|
-
sw?: number
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* The height of the sub-rectangle of the source image to extract.
|
|
38
|
-
* Defaults to the full remaining height of the source.
|
|
39
|
-
*/
|
|
40
|
-
sh?: number;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Applies a binary (on/off) mask to an RGBA buffer.
|
|
45
|
-
* If mask value is 0, pixel becomes transparent.
|
|
46
|
-
*/
|
|
47
|
-
export function applyBinaryMask(
|
|
48
|
-
dst: ImageDataLike,
|
|
49
|
-
mask: BinaryMask,
|
|
50
|
-
opts: ApplyMaskOptions = {},
|
|
51
|
-
) {
|
|
52
|
-
const { width: maskWidth, height: maskHeight } = mask
|
|
53
|
-
|
|
54
|
-
const { dx = 0, dy = 0, sx = 0, sy = 0, sw = maskWidth, sh = maskHeight } = opts
|
|
55
|
-
|
|
56
|
-
// 1. Calculate intersection boundaries
|
|
57
|
-
const x0 = Math.max(0, dx, dx + (0 - sx))
|
|
58
|
-
const y0 = Math.max(0, dy, dy + (0 - sy))
|
|
59
|
-
const x1 = Math.min(dst.width, dx + sw, dx + (maskWidth - sx))
|
|
60
|
-
const y1 = Math.min(dst.height, dy + sh, dy + (maskHeight - sy))
|
|
61
|
-
|
|
62
|
-
if (x1 <= x0 || y1 <= y0) return
|
|
63
|
-
|
|
64
|
-
const { data: dstData, width: dstW } = dst
|
|
65
|
-
|
|
66
|
-
for (let y = y0; y < y1; y++) {
|
|
67
|
-
const maskY = y - dy + sy
|
|
68
|
-
const dstRowOffset = y * dstW * 4
|
|
69
|
-
const maskRowOffset = maskY * maskWidth
|
|
70
|
-
|
|
71
|
-
for (let x = x0; x < x1; x++) {
|
|
72
|
-
const maskX = x - dx + sx
|
|
73
|
-
const mIdx = maskRowOffset + maskX
|
|
74
|
-
|
|
75
|
-
// Binary check: If mask is 0, kill the alpha
|
|
76
|
-
if (mask.data[mIdx] === 0) {
|
|
77
|
-
const aIdx = dstRowOffset + (x * 4) + 3
|
|
78
|
-
dstData[aIdx] = 0
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return dst
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Applies a smooth alpha mask to an RGBA buffer.
|
|
88
|
-
* Multiplies existing Alpha by (maskValue / 255).
|
|
89
|
-
*/
|
|
90
|
-
export function applyAlphaMask(
|
|
91
|
-
dst: ImageData,
|
|
92
|
-
mask: AlphaMask,
|
|
93
|
-
opts: ApplyMaskOptions = {},
|
|
94
|
-
): void {
|
|
95
|
-
let { dx = 0, dy = 0, sx = 0, sy = 0, sw = mask.width, sh = mask.height } = opts
|
|
96
|
-
|
|
97
|
-
// 1. Clipping Logic
|
|
98
|
-
if (dx < 0) {
|
|
99
|
-
sx -= dx
|
|
100
|
-
sw += dx
|
|
101
|
-
dx = 0
|
|
102
|
-
}
|
|
103
|
-
if (dy < 0) {
|
|
104
|
-
sy -= dy
|
|
105
|
-
sh += dy
|
|
106
|
-
dy = 0
|
|
107
|
-
}
|
|
108
|
-
if (sx < 0) {
|
|
109
|
-
dx -= sx
|
|
110
|
-
sw += sx
|
|
111
|
-
sx = 0
|
|
112
|
-
}
|
|
113
|
-
if (sy < 0) {
|
|
114
|
-
dy -= sy
|
|
115
|
-
sh += sy
|
|
116
|
-
sy = 0
|
|
117
|
-
}
|
|
118
|
-
const actualW = Math.min(sw, dst.width - dx, mask.width - sx)
|
|
119
|
-
const actualH = Math.min(sh, dst.height - dy, mask.height - sy)
|
|
120
|
-
|
|
121
|
-
if (actualW <= 0 || actualH <= 0) return
|
|
122
|
-
|
|
123
|
-
const dData = dst.data
|
|
124
|
-
const mData = mask.data
|
|
125
|
-
const dW = dst.width
|
|
126
|
-
const mW = mask.width
|
|
127
|
-
|
|
128
|
-
for (let y = 0; y < actualH; y++) {
|
|
129
|
-
const dOffset = ((dy + y) * dW + dx) << 2
|
|
130
|
-
const mOffset = (sy + y) * mW + sx
|
|
131
|
-
|
|
132
|
-
for (let x = 0; x < actualW; x++) {
|
|
133
|
-
const mVal = mData[mOffset + x]
|
|
134
|
-
|
|
135
|
-
if (mVal === 255) continue
|
|
136
|
-
|
|
137
|
-
const aIdx = dOffset + (x << 2) + 3
|
|
138
|
-
|
|
139
|
-
// --- BRANCH: Zero Alpha ---
|
|
140
|
-
if (mVal === 0) {
|
|
141
|
-
dData[aIdx] = 0
|
|
142
|
-
continue
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// To get 101 from 200 * 128, we use the bias (a * m + 257) >> 8
|
|
146
|
-
// 25600 + 257 = 25857. 25857 >> 8 = 101.
|
|
147
|
-
dData[aIdx] = (dData[aIdx] * mVal + 257) >> 8
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|