@swf2es/player 0.1.0 → 0.1.1
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/package.json +7 -7
- package/dist/audio.d.ts +0 -22
- package/dist/audio.d.ts.map +0 -1
- package/dist/audio.js +0 -106
- package/dist/audio.js.map +0 -1
- package/dist/bitmap-filters.d.ts +0 -29
- package/dist/bitmap-filters.d.ts.map +0 -1
- package/dist/bitmap-filters.js +0 -906
- package/dist/bitmap-filters.js.map +0 -1
- package/dist/bitmap-ops.d.ts +0 -113
- package/dist/bitmap-ops.d.ts.map +0 -1
- package/dist/bitmap-ops.js +0 -725
- package/dist/bitmap-ops.js.map +0 -1
- package/dist/bitmap.d.ts +0 -98
- package/dist/bitmap.d.ts.map +0 -1
- package/dist/bitmap.js +0 -314
- package/dist/bitmap.js.map +0 -1
- package/dist/bounds.d.ts +0 -26
- package/dist/bounds.d.ts.map +0 -1
- package/dist/bounds.js +0 -300
- package/dist/bounds.js.map +0 -1
- package/dist/color.d.ts +0 -7
- package/dist/color.d.ts.map +0 -1
- package/dist/color.js +0 -42
- package/dist/color.js.map +0 -1
- package/dist/display.d.ts +0 -425
- package/dist/display.d.ts.map +0 -1
- package/dist/display.js +0 -1274
- package/dist/display.js.map +0 -1
- package/dist/drawing.d.ts +0 -52
- package/dist/drawing.d.ts.map +0 -1
- package/dist/drawing.js +0 -204
- package/dist/drawing.js.map +0 -1
- package/dist/filters.d.ts +0 -54
- package/dist/filters.d.ts.map +0 -1
- package/dist/filters.js +0 -155
- package/dist/filters.js.map +0 -1
- package/dist/fonts.d.ts +0 -23
- package/dist/fonts.d.ts.map +0 -1
- package/dist/fonts.js +0 -91
- package/dist/fonts.js.map +0 -1
- package/dist/geometry.d.ts +0 -21
- package/dist/geometry.d.ts.map +0 -1
- package/dist/geometry.js +0 -76
- package/dist/geometry.js.map +0 -1
- package/dist/gradients.d.ts +0 -29
- package/dist/gradients.d.ts.map +0 -1
- package/dist/gradients.js +0 -116
- package/dist/gradients.js.map +0 -1
- package/dist/images.d.ts +0 -22
- package/dist/images.d.ts.map +0 -1
- package/dist/images.js +0 -147
- package/dist/images.js.map +0 -1
- package/dist/input.d.ts +0 -79
- package/dist/input.d.ts.map +0 -1
- package/dist/input.js +0 -366
- package/dist/input.js.map +0 -1
- package/dist/keyboard.d.ts +0 -97
- package/dist/keyboard.d.ts.map +0 -1
- package/dist/keyboard.js +0 -583
- package/dist/keyboard.js.map +0 -1
- package/dist/morph.d.ts +0 -17
- package/dist/morph.d.ts.map +0 -1
- package/dist/morph.js +0 -218
- package/dist/morph.js.map +0 -1
- package/dist/pixi-blend.d.ts +0 -7
- package/dist/pixi-blend.d.ts.map +0 -1
- package/dist/pixi-blend.js +0 -253
- package/dist/pixi-blend.js.map +0 -1
- package/dist/pixi-color.d.ts +0 -54
- package/dist/pixi-color.d.ts.map +0 -1
- package/dist/pixi-color.js +0 -336
- package/dist/pixi-color.js.map +0 -1
- package/dist/pixi-filters.d.ts +0 -40
- package/dist/pixi-filters.d.ts.map +0 -1
- package/dist/pixi-filters.js +0 -953
- package/dist/pixi-filters.js.map +0 -1
- package/dist/pixi-resolve.d.ts +0 -4
- package/dist/pixi-resolve.d.ts.map +0 -1
- package/dist/pixi-resolve.js +0 -76
- package/dist/pixi-resolve.js.map +0 -1
- package/dist/pixi.d.ts +0 -219
- package/dist/pixi.d.ts.map +0 -1
- package/dist/pixi.js +0 -2169
- package/dist/pixi.js.map +0 -1
- package/dist/playerglobal/flash/filters/filters.d.ts +0 -26
- package/dist/playerglobal/flash/filters/filters.d.ts.map +0 -1
- package/dist/playerglobal/flash/filters/filters.js +0 -287
- package/dist/playerglobal/flash/filters/filters.js.map +0 -1
- package/dist/playerglobal/flash/system/FSCommand.d.ts +0 -4
- package/dist/playerglobal/flash/system/FSCommand.d.ts.map +0 -1
- package/dist/playerglobal/flash/system/FSCommand.js +0 -18
- package/dist/playerglobal/flash/system/FSCommand.js.map +0 -1
- package/dist/playerglobal/natives.d.ts +0 -10
- package/dist/playerglobal/natives.d.ts.map +0 -1
- package/dist/playerglobal/natives.js +0 -53
- package/dist/playerglobal/natives.js.map +0 -1
- package/dist/png.d.ts +0 -4
- package/dist/png.d.ts.map +0 -1
- package/dist/png.js +0 -140
- package/dist/png.js.map +0 -1
- package/dist/render/art.d.ts +0 -148
- package/dist/render/art.d.ts.map +0 -1
- package/dist/render/art.js +0 -575
- package/dist/render/art.js.map +0 -1
- package/dist/render/batchers.d.ts +0 -23
- package/dist/render/batchers.d.ts.map +0 -1
- package/dist/render/batchers.js +0 -52
- package/dist/render/batchers.js.map +0 -1
- package/dist/render/bitmaps.d.ts +0 -117
- package/dist/render/bitmaps.d.ts.map +0 -1
- package/dist/render/bitmaps.js +0 -422
- package/dist/render/bitmaps.js.map +0 -1
- package/dist/render/blend.d.ts +0 -37
- package/dist/render/blend.d.ts.map +0 -1
- package/dist/render/blend.js +0 -195
- package/dist/render/blend.js.map +0 -1
- package/dist/render/color.d.ts +0 -111
- package/dist/render/color.d.ts.map +0 -1
- package/dist/render/color.js +0 -533
- package/dist/render/color.js.map +0 -1
- package/dist/render/compositing.d.ts +0 -77
- package/dist/render/compositing.d.ts.map +0 -1
- package/dist/render/compositing.js +0 -188
- package/dist/render/compositing.js.map +0 -1
- package/dist/render/filters.d.ts +0 -98
- package/dist/render/filters.d.ts.map +0 -1
- package/dist/render/filters.js +0 -1326
- package/dist/render/filters.js.map +0 -1
- package/dist/render/hooks.d.ts +0 -4
- package/dist/render/hooks.d.ts.map +0 -1
- package/dist/render/hooks.js +0 -48
- package/dist/render/hooks.js.map +0 -1
- package/dist/render/node.d.ts +0 -139
- package/dist/render/node.d.ts.map +0 -1
- package/dist/render/node.js +0 -59
- package/dist/render/node.js.map +0 -1
- package/dist/render/patches.d.ts +0 -54
- package/dist/render/patches.d.ts.map +0 -1
- package/dist/render/patches.js +0 -154
- package/dist/render/patches.js.map +0 -1
- package/dist/render/planes.d.ts +0 -74
- package/dist/render/planes.d.ts.map +0 -1
- package/dist/render/planes.js +0 -267
- package/dist/render/planes.js.map +0 -1
- package/dist/render/pointer.d.ts +0 -5
- package/dist/render/pointer.d.ts.map +0 -1
- package/dist/render/pointer.js +0 -279
- package/dist/render/pointer.js.map +0 -1
- package/dist/render/pools.d.ts +0 -122
- package/dist/render/pools.d.ts.map +0 -1
- package/dist/render/pools.js +0 -238
- package/dist/render/pools.js.map +0 -1
- package/dist/render/resolve.d.ts +0 -4
- package/dist/render/resolve.d.ts.map +0 -1
- package/dist/render/resolve.js +0 -76
- package/dist/render/resolve.js.map +0 -1
- package/dist/render/saffron.d.ts +0 -135
- package/dist/render/saffron.d.ts.map +0 -1
- package/dist/render/saffron.js +0 -725
- package/dist/render/saffron.js.map +0 -1
- package/dist/render/settle.d.ts +0 -60
- package/dist/render/settle.d.ts.map +0 -1
- package/dist/render/settle.js +0 -128
- package/dist/render/settle.js.map +0 -1
- package/dist/render/shared.d.ts +0 -25
- package/dist/render/shared.d.ts.map +0 -1
- package/dist/render/shared.js +0 -33
- package/dist/render/shared.js.map +0 -1
- package/dist/render/snapshot.d.ts +0 -21
- package/dist/render/snapshot.d.ts.map +0 -1
- package/dist/render/snapshot.js +0 -91
- package/dist/render/snapshot.js.map +0 -1
- package/dist/render/spares.d.ts +0 -56
- package/dist/render/spares.d.ts.map +0 -1
- package/dist/render/spares.js +0 -160
- package/dist/render/spares.js.map +0 -1
- package/dist/render/strokes.d.ts +0 -107
- package/dist/render/strokes.d.ts.map +0 -1
- package/dist/render/strokes.js +0 -448
- package/dist/render/strokes.js.map +0 -1
- package/dist/render/table.d.ts +0 -213
- package/dist/render/table.d.ts.map +0 -1
- package/dist/render/table.js +0 -795
- package/dist/render/table.js.map +0 -1
- package/dist/render/tessellate.d.ts +0 -89
- package/dist/render/tessellate.d.ts.map +0 -1
- package/dist/render/tessellate.js +0 -386
- package/dist/render/tessellate.js.map +0 -1
- package/dist/render/text.d.ts +0 -40
- package/dist/render/text.d.ts.map +0 -1
- package/dist/render/text.js +0 -309
- package/dist/render/text.js.map +0 -1
- package/dist/render/three.d.ts +0 -83
- package/dist/render/three.d.ts.map +0 -1
- package/dist/render/three.js +0 -511
- package/dist/render/three.js.map +0 -1
- package/dist/render/triangles.d.ts +0 -28
- package/dist/render/triangles.d.ts.map +0 -1
- package/dist/render/triangles.js +0 -189
- package/dist/render/triangles.js.map +0 -1
- package/dist/render/view.d.ts +0 -238
- package/dist/render/view.d.ts.map +0 -1
- package/dist/render/view.js +0 -1173
- package/dist/render/view.js.map +0 -1
- package/dist/render/worker/bitmaps.d.ts +0 -43
- package/dist/render/worker/bitmaps.d.ts.map +0 -1
- package/dist/render/worker/bitmaps.js +0 -151
- package/dist/render/worker/bitmaps.js.map +0 -1
- package/dist/render/worker/drawing.d.ts +0 -21
- package/dist/render/worker/drawing.d.ts.map +0 -1
- package/dist/render/worker/drawing.js +0 -356
- package/dist/render/worker/drawing.js.map +0 -1
- package/dist/render/worker/filters.d.ts +0 -7
- package/dist/render/worker/filters.d.ts.map +0 -1
- package/dist/render/worker/filters.js +0 -125
- package/dist/render/worker/filters.js.map +0 -1
- package/dist/render/worker/frames.d.ts +0 -32
- package/dist/render/worker/frames.d.ts.map +0 -1
- package/dist/render/worker/frames.js +0 -124
- package/dist/render/worker/frames.js.map +0 -1
- package/dist/render/worker/host.d.ts +0 -60
- package/dist/render/worker/host.d.ts.map +0 -1
- package/dist/render/worker/host.js +0 -297
- package/dist/render/worker/host.js.map +0 -1
- package/dist/render/worker/libraries.d.ts +0 -36
- package/dist/render/worker/libraries.d.ts.map +0 -1
- package/dist/render/worker/libraries.js +0 -153
- package/dist/render/worker/libraries.js.map +0 -1
- package/dist/render/worker/presentation.d.ts +0 -18
- package/dist/render/worker/presentation.d.ts.map +0 -1
- package/dist/render/worker/presentation.js +0 -47
- package/dist/render/worker/presentation.js.map +0 -1
- package/dist/render/worker/renderer.d.ts +0 -4
- package/dist/render/worker/renderer.d.ts.map +0 -1
- package/dist/render/worker/renderer.js +0 -21
- package/dist/render/worker/renderer.js.map +0 -1
- package/dist/render/worker/scene.d.ts +0 -32
- package/dist/render/worker/scene.d.ts.map +0 -1
- package/dist/render/worker/scene.js +0 -611
- package/dist/render/worker/scene.js.map +0 -1
- package/dist/render/worker/text.d.ts +0 -47
- package/dist/render/worker/text.d.ts.map +0 -1
- package/dist/render/worker/text.js +0 -261
- package/dist/render/worker/text.js.map +0 -1
- package/dist/render/worker/thread.d.ts +0 -23
- package/dist/render/worker/thread.d.ts.map +0 -1
- package/dist/render/worker/thread.js +0 -107
- package/dist/render/worker/thread.js.map +0 -1
- package/dist/render/worker/values.d.ts +0 -18
- package/dist/render/worker/values.d.ts.map +0 -1
- package/dist/render/worker/values.js +0 -64
- package/dist/render/worker/values.js.map +0 -1
- package/dist/sha256.d.ts +0 -5
- package/dist/sha256.d.ts.map +0 -1
- package/dist/sha256.js +0 -96
- package/dist/sha256.js.map +0 -1
- package/dist/shapes.d.ts +0 -71
- package/dist/shapes.d.ts.map +0 -1
- package/dist/shapes.js +0 -340
- package/dist/shapes.js.map +0 -1
- package/dist/static-text.d.ts +0 -33
- package/dist/static-text.d.ts.map +0 -1
- package/dist/static-text.js +0 -105
- package/dist/static-text.js.map +0 -1
- package/dist/text-layout.d.ts +0 -66
- package/dist/text-layout.d.ts.map +0 -1
- package/dist/text-layout.js +0 -315
- package/dist/text-layout.js.map +0 -1
- package/dist/text.d.ts +0 -67
- package/dist/text.d.ts.map +0 -1
- package/dist/text.js +0 -383
- package/dist/text.js.map +0 -1
- package/dist/timeline.d.ts +0 -150
- package/dist/timeline.d.ts.map +0 -1
- package/dist/timeline.js +0 -266
- package/dist/timeline.js.map +0 -1
package/dist/bitmap-ops.js
DELETED
|
@@ -1,725 +0,0 @@
|
|
|
1
|
-
// The pixel operations of BitmapData that read and write its store alone
|
|
2
|
-
// (bitmap.ts): each on unmultiplied ARGB as Flash's results show it works,
|
|
3
|
-
// the store premultiplying again as it writes. The rules are the ones
|
|
4
|
-
// Flash traces under adl and records in Ruffle's corpus.
|
|
5
|
-
import { unmultiply } from "./bitmap.js";
|
|
6
|
-
const M = 2147483647;
|
|
7
|
-
/**
|
|
8
|
-
* Flash's noise generator: Park-Miller's minimal standard, x * 16807 mod
|
|
9
|
-
* 2^31 - 1, a seed of 0 taken as 1 and one under 0 as -seed + 1 (all 25
|
|
10
|
-
* pixels Ruffle's bitmapdata_getvector records of noise(0) match).
|
|
11
|
-
*/
|
|
12
|
-
export class ParkMiller {
|
|
13
|
-
x;
|
|
14
|
-
constructor(seed) {
|
|
15
|
-
let s = seed | 0;
|
|
16
|
-
if (s <= 0) {
|
|
17
|
-
s = -s + 1;
|
|
18
|
-
}
|
|
19
|
-
this.x = s % M || 1;
|
|
20
|
-
}
|
|
21
|
-
next() {
|
|
22
|
-
// 16807 * x stays below 2^46: exact in a double.
|
|
23
|
-
this.x = (this.x * 16807) % M;
|
|
24
|
-
return this.x;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* noise: each pixel's channels drawn in R, G, B order, then alpha, which
|
|
29
|
-
* only a transparent bitmap draws; a grayscale draw is one for all three
|
|
30
|
-
* colours, then alpha. A value is low + r % (high - low + 1), high capped
|
|
31
|
-
* at 255 and a range upside down giving low. A colour left out is 0, an
|
|
32
|
-
* alpha left out 255.
|
|
33
|
-
*/
|
|
34
|
-
export function noise(store, seed, low, high, channels, gray) {
|
|
35
|
-
const rng = new ParkMiller(seed);
|
|
36
|
-
const lo = Math.max(0, Math.min(255, low));
|
|
37
|
-
const hi = Math.max(0, Math.min(255, high));
|
|
38
|
-
const span = hi - lo + 1;
|
|
39
|
-
const draw = () => (span <= 0 ? lo : lo + (rng.next() % span));
|
|
40
|
-
const alpha = (channels & 8) !== 0 && store.transparent;
|
|
41
|
-
for (let i = 0; i < store.pixels.length; i++) {
|
|
42
|
-
let r = 0;
|
|
43
|
-
let g = 0;
|
|
44
|
-
let b = 0;
|
|
45
|
-
if (gray) {
|
|
46
|
-
r = g = b = draw();
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
if (channels & 1) {
|
|
50
|
-
r = draw();
|
|
51
|
-
}
|
|
52
|
-
if (channels & 2) {
|
|
53
|
-
g = draw();
|
|
54
|
-
}
|
|
55
|
-
if (channels & 4) {
|
|
56
|
-
b = draw();
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
const a = alpha ? draw() : 255;
|
|
60
|
-
store.pixels[i] = store.premultiplied(((a << 24) | (r << 16) | (g << 8) | b) >>> 0);
|
|
61
|
-
}
|
|
62
|
-
store.changed();
|
|
63
|
-
}
|
|
64
|
-
/** The channel's shift in ARGB: 1 red, 2 green, 4 blue, 8 alpha; -1 for another value. */
|
|
65
|
-
function shiftOf(channel) {
|
|
66
|
-
switch (channel) {
|
|
67
|
-
case 1:
|
|
68
|
-
return 16;
|
|
69
|
-
case 2:
|
|
70
|
-
return 8;
|
|
71
|
-
case 4:
|
|
72
|
-
return 0;
|
|
73
|
-
case 8:
|
|
74
|
-
return 24;
|
|
75
|
-
default:
|
|
76
|
-
return -1;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
/** Each destination pixel's channel set from the source pixel's channel, both unmultiplied. */
|
|
80
|
-
export function copyChannel(store, source, rect, dx, dy, from, to) {
|
|
81
|
-
const fromShift = shiftOf(from);
|
|
82
|
-
const toShift = shiftOf(to);
|
|
83
|
-
if (toShift < 0 || (toShift === 24 && !store.transparent)) {
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
// A source of more than one channel, or none, copies 0, as Flash does.
|
|
87
|
-
forEachCopied(store, source, rect, dx, dy, (s, d) => {
|
|
88
|
-
const v = fromShift < 0 ? 0 : (s >>> fromShift) & 0xff;
|
|
89
|
-
return ((d & ~(0xff << toShift)) | (v << toShift)) >>> 0;
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* colorTransform as Flash computes it, fitted under adl: each channel read
|
|
94
|
-
* back as p * 255 / a, floored; then (c * floor(mul * 256) >> 8) +
|
|
95
|
-
* trunc(offset), clamped to 0..255, the alpha alike; and written
|
|
96
|
-
* premultiplied as c * (a + 1) >> 8, not as setPixel32 rounds. A pixel
|
|
97
|
-
* of alpha 0 is left as it is; an opaque bitmap's alpha is transformed
|
|
98
|
-
* like a transparent one's.
|
|
99
|
-
*/
|
|
100
|
-
export function colorTransform(store, rect, ct) {
|
|
101
|
-
const c = store.clip(rect);
|
|
102
|
-
if (!c) {
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
const fixed = (m) => Math.trunc(m * 256);
|
|
106
|
-
const [rm, gm, bm, am] = [ct.rMul, ct.gMul, ct.bMul, ct.aMul].map(fixed);
|
|
107
|
-
const [ro, go, bo, ao] = [ct.rAdd, ct.gAdd, ct.bAdd, ct.aAdd].map(Math.trunc);
|
|
108
|
-
const ch = (v, m, o) => Math.max(0, Math.min(255, Math.floor((v * m) / 256) + o));
|
|
109
|
-
for (let y = c.y; y < c.y + c.height; y++) {
|
|
110
|
-
for (let x = c.x; x < c.x + c.width; x++) {
|
|
111
|
-
const i = y * store.width + x;
|
|
112
|
-
const p = store.pixels[i];
|
|
113
|
-
const a0 = p >>> 24;
|
|
114
|
-
// A pixel of alpha 0 is left as it is, offsets and all.
|
|
115
|
-
if (a0 === 0) {
|
|
116
|
-
continue;
|
|
117
|
-
}
|
|
118
|
-
const straight = (shift) => a0 === 0 ? 0 : Math.min(255, Math.floor((((p >>> shift) & 0xff) * 255) / a0));
|
|
119
|
-
// An opaque bitmap's alpha is transformed too: down to 0, the pixel reads 0.
|
|
120
|
-
const a = ch(a0, am, ao);
|
|
121
|
-
const r = ch(straight(16), rm, ro);
|
|
122
|
-
const g = ch(straight(8), gm, go);
|
|
123
|
-
const b = ch(straight(0), bm, bo);
|
|
124
|
-
const out = (v) => (a === 255 ? v : (v * (a + 1)) >> 8);
|
|
125
|
-
store.pixels[i] = ((a << 24) | (out(r) << 16) | (out(g) << 8) | out(b)) >>> 0;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
store.changed();
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* merge: each channel ((s * m + d * (256 - m)) >> 8) & 0xFF, unmultiplied,
|
|
132
|
-
* the multiplier a uint and nothing clamped, as 18 multipliers from -257
|
|
133
|
-
* to 2^32 - 1 show Flash computing it under adl; a double holds the
|
|
134
|
-
* product, so the shift is a division.
|
|
135
|
-
*/
|
|
136
|
-
export function merge(store, source, rect, dx, dy, muls) {
|
|
137
|
-
const [rm, gm, bm, am] = muls;
|
|
138
|
-
const mix = (s, d, shift, m) => Math.floor((((s >>> shift) & 0xff) * m + ((d >>> shift) & 0xff) * (256 - m)) / 256) & 0xff;
|
|
139
|
-
forEachCopied(store, source, rect, dx, dy, (s, d) => ((mix(s, d, 24, am) << 24) |
|
|
140
|
-
(mix(s, d, 16, rm) << 16) |
|
|
141
|
-
(mix(s, d, 8, gm) << 8) |
|
|
142
|
-
mix(s, d, 0, bm)) >>>
|
|
143
|
-
0);
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* The source rect's pixels against the destination's at the point, both
|
|
147
|
-
* clipped and unmultiplied, each destination pixel set to what `f` makes
|
|
148
|
-
* of the pair; from a copy where the source is the destination.
|
|
149
|
-
*/
|
|
150
|
-
function forEachCopied(store, source, rect, dx, dy, f) {
|
|
151
|
-
const s = source.clip(rect);
|
|
152
|
-
if (!s) {
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
dx += s.x - rect.x;
|
|
156
|
-
dy += s.y - rect.y;
|
|
157
|
-
const d = store.clip({ x: dx, y: dy, width: s.width, height: s.height });
|
|
158
|
-
if (!d) {
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
const sx = s.x + (d.x - dx);
|
|
162
|
-
const sy = s.y + (d.y - dy);
|
|
163
|
-
const [pixels, pw, ox, oy] = readFrom(source, store, sx, sy, d.width, d.height);
|
|
164
|
-
for (let row = 0; row < d.height; row++) {
|
|
165
|
-
for (let i = 0; i < d.width; i++) {
|
|
166
|
-
const to = (d.y + row) * store.width + d.x + i;
|
|
167
|
-
const p = unmultiply(pixels[(sy - oy + row) * pw + sx - ox + i]);
|
|
168
|
-
store.pixels[to] = store.premultiplied(f(p, unmultiply(store.pixels[to])));
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
store.changed();
|
|
172
|
-
}
|
|
173
|
-
/** scroll: the pixels moved by (x, y); what nothing moved onto keeps its old pixels. */
|
|
174
|
-
export function scroll(store, x, y) {
|
|
175
|
-
const { width, height } = store;
|
|
176
|
-
const old = store.pixels.slice();
|
|
177
|
-
for (let row = 0; row < height; row++) {
|
|
178
|
-
const sy = row - y;
|
|
179
|
-
if (sy < 0 || sy >= height) {
|
|
180
|
-
continue;
|
|
181
|
-
}
|
|
182
|
-
for (let col = 0; col < width; col++) {
|
|
183
|
-
const sx = col - x;
|
|
184
|
-
if (sx >= 0 && sx < width) {
|
|
185
|
-
store.pixels[row * width + col] = old[sy * width + sx];
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
store.changed();
|
|
190
|
-
}
|
|
191
|
-
const OPERATIONS = {
|
|
192
|
-
"<": (a, b) => a < b,
|
|
193
|
-
"<=": (a, b) => a <= b,
|
|
194
|
-
">": (a, b) => a > b,
|
|
195
|
-
">=": (a, b) => a >= b,
|
|
196
|
-
"==": (a, b) => a === b,
|
|
197
|
-
"!=": (a, b) => a !== b,
|
|
198
|
-
};
|
|
199
|
-
/** Whether `op` is one threshold takes. */
|
|
200
|
-
export function isThresholdOperation(op) {
|
|
201
|
-
// Its own keys only: "constructor" and the rest of Object's are not operations.
|
|
202
|
-
return Object.hasOwn(OPERATIONS, op);
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* threshold: each source pixel, unmultiplied and masked, compared with the
|
|
206
|
-
* masked threshold as unsigned numbers; where the test holds the
|
|
207
|
-
* destination pixel becomes `color`, elsewhere the source pixel if
|
|
208
|
-
* `copySource`; how many held.
|
|
209
|
-
*/
|
|
210
|
-
export function threshold(store, source, rect, dx, dy, op, value, color, mask, copySource) {
|
|
211
|
-
const test = OPERATIONS[op];
|
|
212
|
-
const target = (value & mask) >>> 0;
|
|
213
|
-
let count = 0;
|
|
214
|
-
const s = source.clip(rect);
|
|
215
|
-
if (!s) {
|
|
216
|
-
return 0;
|
|
217
|
-
}
|
|
218
|
-
dx += s.x - rect.x;
|
|
219
|
-
dy += s.y - rect.y;
|
|
220
|
-
const d = store.clip({ x: dx, y: dy, width: s.width, height: s.height });
|
|
221
|
-
if (!d) {
|
|
222
|
-
return 0;
|
|
223
|
-
}
|
|
224
|
-
const sx = s.x + (d.x - dx);
|
|
225
|
-
const sy = s.y + (d.y - dy);
|
|
226
|
-
const [pixels, pw, ox, oy] = readFrom(source, store, sx, sy, d.width, d.height);
|
|
227
|
-
const fill = store.premultiplied(color >>> 0);
|
|
228
|
-
for (let row = 0; row < d.height; row++) {
|
|
229
|
-
for (let i = 0; i < d.width; i++) {
|
|
230
|
-
const to = (d.y + row) * store.width + d.x + i;
|
|
231
|
-
const raw = pixels[(sy - oy + row) * pw + sx - ox + i];
|
|
232
|
-
if (test((unmultiply(raw) & mask) >>> 0, target)) {
|
|
233
|
-
store.pixels[to] = fill;
|
|
234
|
-
count++;
|
|
235
|
-
}
|
|
236
|
-
else if (copySource) {
|
|
237
|
-
store.pixels[to] = store.transparent ? raw : (raw | 0xff000000) >>> 0;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
store.changed();
|
|
242
|
-
return count;
|
|
243
|
-
}
|
|
244
|
-
/**
|
|
245
|
-
* getColorBoundsRect: the smallest rect holding every pixel whose
|
|
246
|
-
* unmultiplied value, masked, is `color` (or is not, without findColor);
|
|
247
|
-
* empty where none is, and where the one found is the pixel at the
|
|
248
|
-
* origin alone, as Flash has it (Ruffle's bitmap_data).
|
|
249
|
-
*/
|
|
250
|
-
export function colorBounds(store, mask, color, find) {
|
|
251
|
-
let x0 = store.width;
|
|
252
|
-
let y0 = store.height;
|
|
253
|
-
let x1 = -1;
|
|
254
|
-
let y1 = -1;
|
|
255
|
-
const target = (color & mask) >>> 0;
|
|
256
|
-
for (let y = 0; y < store.height; y++) {
|
|
257
|
-
for (let x = 0; x < store.width; x++) {
|
|
258
|
-
const hit = (unmultiply(store.pixels[y * store.width + x]) & mask) >>> 0 === target;
|
|
259
|
-
if (hit === find) {
|
|
260
|
-
x0 = Math.min(x0, x);
|
|
261
|
-
y0 = Math.min(y0, y);
|
|
262
|
-
x1 = Math.max(x1, x);
|
|
263
|
-
y1 = Math.max(y1, y);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
// Flash takes extents that end at the origin for none found: a lone hit at (0, 0) gives an empty rect.
|
|
268
|
-
return x1 <= 0 && y1 <= 0
|
|
269
|
-
? { x: 0, y: 0, width: 0, height: 0 }
|
|
270
|
-
: { x: x0, y: y0, width: x1 - x0 + 1, height: y1 - y0 + 1 };
|
|
271
|
-
}
|
|
272
|
-
/** floodFill: the 4-connected region of the start pixel's exact value set to `color`. */
|
|
273
|
-
export function floodFill(store, x, y, color) {
|
|
274
|
-
const { width, height } = store;
|
|
275
|
-
if (x < 0 || y < 0 || x >= width || y >= height) {
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
const from = store.pixels[y * width + x];
|
|
279
|
-
const to = store.premultiplied(color >>> 0);
|
|
280
|
-
if (from === to) {
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
const stack = [y * width + x];
|
|
284
|
-
while (stack.length) {
|
|
285
|
-
const i = stack.pop();
|
|
286
|
-
if (store.pixels[i] !== from) {
|
|
287
|
-
continue;
|
|
288
|
-
}
|
|
289
|
-
store.pixels[i] = to;
|
|
290
|
-
const px = i % width;
|
|
291
|
-
if (px > 0) {
|
|
292
|
-
stack.push(i - 1);
|
|
293
|
-
}
|
|
294
|
-
if (px < width - 1) {
|
|
295
|
-
stack.push(i + 1);
|
|
296
|
-
}
|
|
297
|
-
if (i >= width) {
|
|
298
|
-
stack.push(i - width);
|
|
299
|
-
}
|
|
300
|
-
if (i + width < width * height) {
|
|
301
|
-
stack.push(i + width);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
store.changed();
|
|
305
|
-
}
|
|
306
|
-
/** histogram: four arrays of 256 counts, red, green, blue and alpha, of the rect's unmultiplied pixels. */
|
|
307
|
-
export function histogram(store, rect) {
|
|
308
|
-
const counts = [0, 1, 2, 3].map(() => new Array(256).fill(0));
|
|
309
|
-
const c = store.clip(rect);
|
|
310
|
-
if (!c) {
|
|
311
|
-
return counts;
|
|
312
|
-
}
|
|
313
|
-
for (let y = c.y; y < c.y + c.height; y++) {
|
|
314
|
-
for (let x = c.x; x < c.x + c.width; x++) {
|
|
315
|
-
const p = unmultiply(store.pixels[y * store.width + x]);
|
|
316
|
-
counts[0][(p >>> 16) & 0xff]++;
|
|
317
|
-
counts[1][(p >>> 8) & 0xff]++;
|
|
318
|
-
counts[2][p & 0xff]++;
|
|
319
|
-
counts[3][p >>> 24]++;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
return counts;
|
|
323
|
-
}
|
|
324
|
-
/** The unmultiplied alpha at (x, y), or -1 outside. */
|
|
325
|
-
export function alphaAt(store, x, y) {
|
|
326
|
-
if (x < 0 || y < 0 || x >= store.width || y >= store.height) {
|
|
327
|
-
return -1;
|
|
328
|
-
}
|
|
329
|
-
return store.pixels[y * store.width + x] >>> 24;
|
|
330
|
-
}
|
|
331
|
-
/**
|
|
332
|
-
* Flash's pixelDissolve taps, by the state's width in bits: a Galois LFSR,
|
|
333
|
-
* s >> 1, xor the tap where s was odd, read off adl for every width a
|
|
334
|
-
* BitmapData can have (2 to 26 bits).
|
|
335
|
-
*/
|
|
336
|
-
const DISSOLVE_TAPS = [
|
|
337
|
-
0, 0, 3, 6, 12, 20, 48, 96, 184, 272, 576, 1280, 3232, 6912, 13568, 24576, 46080, 73728, 132096,
|
|
338
|
-
466944, 589824, 1310720, 3145728, 4325376, 14155776, 18874368, 59244544,
|
|
339
|
-
];
|
|
340
|
-
/** Bits to number 0..n - 1: ceil(log2(n)). */
|
|
341
|
-
function bitsFor(n) {
|
|
342
|
-
return n <= 1 ? 0 : 32 - Math.clz32(n - 1);
|
|
343
|
-
}
|
|
344
|
-
/**
|
|
345
|
-
* pixelDissolve as Flash does it, fitted under adl: a state of
|
|
346
|
-
* ceil(log2 w) + ceil(log2 h) bits stands for the pixel (s & (2^bw - 1),
|
|
347
|
-
* s >> bw) of the rect, a state outside it skipped; each call writes
|
|
348
|
-
* numPixels pixels, from the seed on, and returns the state after the
|
|
349
|
-
* last, raw, so the next call goes on from there. Every call writes the
|
|
350
|
-
* pixel at the origin too, which no state stands for, and so n calls of
|
|
351
|
-
* 1 write n + 1 (Ruffle's bitmapdata_pixeldissolve); a seed past the
|
|
352
|
-
* generator's states is taken modulo 2^bits - 1, and 0 starts at the tap. A
|
|
353
|
-
* pixel takes the fill colour where the source is the destination, else
|
|
354
|
-
* the source's; a rect a pixel high or wide dissolves nothing.
|
|
355
|
-
*/
|
|
356
|
-
export function pixelDissolve(store, source, rect, dx, dy, seed, count, fill) {
|
|
357
|
-
// Over the rect clipped to the source and then to the destination, as the copies are.
|
|
358
|
-
const sc = source.clip(rect);
|
|
359
|
-
if (!sc) {
|
|
360
|
-
return 0;
|
|
361
|
-
}
|
|
362
|
-
const ox = dx + (sc.x - rect.x);
|
|
363
|
-
const oy = dy + (sc.y - rect.y);
|
|
364
|
-
const dc = store.clip({ x: ox, y: oy, width: sc.width, height: sc.height });
|
|
365
|
-
if (!dc) {
|
|
366
|
-
return 0;
|
|
367
|
-
}
|
|
368
|
-
const rx = sc.x + (dc.x - ox);
|
|
369
|
-
const ry = sc.y + (dc.y - oy);
|
|
370
|
-
dx = dc.x;
|
|
371
|
-
dy = dc.y;
|
|
372
|
-
const { width: w, height: h } = dc;
|
|
373
|
-
if (w <= 1 || h <= 1) {
|
|
374
|
-
return 0;
|
|
375
|
-
}
|
|
376
|
-
const bw = bitsFor(w);
|
|
377
|
-
const bits = bw + bitsFor(h);
|
|
378
|
-
const tap = DISSOLVE_TAPS[bits] ?? 0;
|
|
379
|
-
const mask = 2 ** bits - 1;
|
|
380
|
-
const xMask = (1 << bw) - 1;
|
|
381
|
-
const color = store.premultiplied(fill >>> 0);
|
|
382
|
-
const next = (s) => (s & 1 ? Math.floor(s / 2) ^ tap : Math.floor(s / 2));
|
|
383
|
-
// A position of the rect, written where it lies in both bitmaps.
|
|
384
|
-
const write = (state) => {
|
|
385
|
-
const px = state & xMask;
|
|
386
|
-
const py = Math.floor(state / 2 ** bw);
|
|
387
|
-
const sx = rx + px;
|
|
388
|
-
const sy = ry + py;
|
|
389
|
-
const tx = dx + px;
|
|
390
|
-
const ty = dy + py;
|
|
391
|
-
if (sx < 0 ||
|
|
392
|
-
sy < 0 ||
|
|
393
|
-
sx >= source.width ||
|
|
394
|
-
sy >= source.height ||
|
|
395
|
-
tx < 0 ||
|
|
396
|
-
ty < 0 ||
|
|
397
|
-
tx >= store.width ||
|
|
398
|
-
ty >= store.height) {
|
|
399
|
-
return;
|
|
400
|
-
}
|
|
401
|
-
store.pixels[ty * store.width + tx] =
|
|
402
|
-
source === store ? color : source.pixels[sy * source.width + sx];
|
|
403
|
-
};
|
|
404
|
-
const inside = (state) => (state & xMask) < w && Math.floor(state / 2 ** bw) < h;
|
|
405
|
-
// The origin is written on every call; the seed is reduced to the
|
|
406
|
-
// generator's period, and 0, which it never reaches, starts at the tap.
|
|
407
|
-
write(0);
|
|
408
|
-
const start = Math.abs(Math.trunc(seed));
|
|
409
|
-
let s = start > mask ? start % mask : start;
|
|
410
|
-
if (s === 0) {
|
|
411
|
-
s = tap;
|
|
412
|
-
}
|
|
413
|
-
// The states inside the rect come round every w * h - 1 writes (state 0
|
|
414
|
-
// is the origin's, reached from seed 0 alone), so a count past that is
|
|
415
|
-
// one full round, which writes every pixel, and the remainder: the same
|
|
416
|
-
// pixels and the seed Flash returns, without its seconds-long loop.
|
|
417
|
-
const round = w * h - 1;
|
|
418
|
-
const steps = count > round ? round + (count % round) : count;
|
|
419
|
-
for (let k = 0; k < steps; k++) {
|
|
420
|
-
// Every state but 0 comes round within 2^bits steps, so this ends.
|
|
421
|
-
while (!inside(s)) {
|
|
422
|
-
s = next(s);
|
|
423
|
-
}
|
|
424
|
-
write(s);
|
|
425
|
-
s = next(s);
|
|
426
|
-
}
|
|
427
|
-
store.changed();
|
|
428
|
-
return s;
|
|
429
|
-
}
|
|
430
|
-
/**
|
|
431
|
-
* The pixels a copy reads, with their row width and origin: the source's
|
|
432
|
-
* own, or where the source is the destination, a copy of the rect alone,
|
|
433
|
-
* so a one-pixel operation does not copy the whole store.
|
|
434
|
-
*/
|
|
435
|
-
function readFrom(source, store, x, y, w, h) {
|
|
436
|
-
if (source !== store) {
|
|
437
|
-
return [source.pixels, source.width, 0, 0];
|
|
438
|
-
}
|
|
439
|
-
const copy = new Uint32Array(w * h);
|
|
440
|
-
for (let row = 0; row < h; row++) {
|
|
441
|
-
const from = (y + row) * source.width + x;
|
|
442
|
-
copy.set(source.pixels.subarray(from, from + w), row * w);
|
|
443
|
-
}
|
|
444
|
-
return [copy, w, x, y];
|
|
445
|
-
}
|
|
446
|
-
/** The blend modes draw composites with; alpha and erase need a layer, which a bitmap drawn has not, and do nothing. */
|
|
447
|
-
const BLENDS = new Set([
|
|
448
|
-
"normal",
|
|
449
|
-
"layer",
|
|
450
|
-
"multiply",
|
|
451
|
-
"screen",
|
|
452
|
-
"lighten",
|
|
453
|
-
"darken",
|
|
454
|
-
"difference",
|
|
455
|
-
"add",
|
|
456
|
-
"subtract",
|
|
457
|
-
"invert",
|
|
458
|
-
"overlay",
|
|
459
|
-
"hardlight",
|
|
460
|
-
]);
|
|
461
|
-
/**
|
|
462
|
-
* draw of a bitmap source: each destination pixel in the clip takes the
|
|
463
|
-
* source pixel under its centre, through the matrix's inverse, clamped to
|
|
464
|
-
* the source's edges (bilinear when smoothing), scaled by how much of the
|
|
465
|
-
* pixel the transformed source covers (a 4 x 4 sample grid), passed
|
|
466
|
-
* through the colour transform as colorTransform does, and composited by
|
|
467
|
-
* the blend mode. Flash's own pixels, under adl: nearest at the centre,
|
|
468
|
-
* normal as the store's source-over, alpha and erase as nothing.
|
|
469
|
-
*/
|
|
470
|
-
export function drawBitmap(store, source, m, ct, mode, clip, smooth, rows = false) {
|
|
471
|
-
if (!BLENDS.has(mode)) {
|
|
472
|
-
return;
|
|
473
|
-
}
|
|
474
|
-
// Flash rasterises the translation snapped down to quarter pixels, which
|
|
475
|
-
// its 4 x 4 coverage then reads exactly (a move of 0.49 covers 3/4 and
|
|
476
|
-
// 1/4 of the edge pixels under adl).
|
|
477
|
-
m = { ...m, tx: Math.floor(m.tx * 4) / 4, ty: Math.floor(m.ty * 4) / 4 };
|
|
478
|
-
const det = m.a * m.d - m.b * m.c;
|
|
479
|
-
if (det === 0) {
|
|
480
|
-
return;
|
|
481
|
-
}
|
|
482
|
-
// The destination pixels the source can reach: its corners through the matrix.
|
|
483
|
-
const corners = [
|
|
484
|
-
[0, 0],
|
|
485
|
-
[source.width, 0],
|
|
486
|
-
[0, source.height],
|
|
487
|
-
[source.width, source.height],
|
|
488
|
-
].map(([x, y]) => [m.a * x + m.c * y + m.tx, m.b * x + m.d * y + m.ty]);
|
|
489
|
-
const xs = corners.map((p) => p[0]);
|
|
490
|
-
const ys = corners.map((p) => p[1]);
|
|
491
|
-
let area = store.clip({
|
|
492
|
-
x: Math.floor(Math.min(...xs)),
|
|
493
|
-
y: Math.floor(Math.min(...ys)),
|
|
494
|
-
width: Math.ceil(Math.max(...xs)) - Math.floor(Math.min(...xs)),
|
|
495
|
-
height: Math.ceil(Math.max(...ys)) - Math.floor(Math.min(...ys)),
|
|
496
|
-
});
|
|
497
|
-
if (area && clip) {
|
|
498
|
-
area = store.clip({
|
|
499
|
-
x: Math.max(area.x, clip.x),
|
|
500
|
-
y: Math.max(area.y, clip.y),
|
|
501
|
-
width: Math.min(area.x + area.width, clip.x + clip.width) - Math.max(area.x, clip.x),
|
|
502
|
-
height: Math.min(area.y + area.height, clip.y + clip.height) - Math.max(area.y, clip.y),
|
|
503
|
-
});
|
|
504
|
-
}
|
|
505
|
-
if (!area) {
|
|
506
|
-
return;
|
|
507
|
-
}
|
|
508
|
-
// Unscaled at a whole pixel, as every snapshot of a display object is
|
|
509
|
-
// composited: each covered pixel is covered whole and is the source's
|
|
510
|
-
// own, so it is copied straight, as the general path below would.
|
|
511
|
-
if (m.a === 1 &&
|
|
512
|
-
m.b === 0 &&
|
|
513
|
-
m.c === 0 &&
|
|
514
|
-
m.d === 1 &&
|
|
515
|
-
Number.isInteger(m.tx) &&
|
|
516
|
-
Number.isInteger(m.ty) &&
|
|
517
|
-
source !== store) {
|
|
518
|
-
composite(store, source, area, m.tx, m.ty, ct, mode);
|
|
519
|
-
store.changed();
|
|
520
|
-
return;
|
|
521
|
-
}
|
|
522
|
-
// The inverse: from a destination point to the source's.
|
|
523
|
-
const ia = m.d / det;
|
|
524
|
-
const ib = -m.b / det;
|
|
525
|
-
const ic = -m.c / det;
|
|
526
|
-
const id = m.a / det;
|
|
527
|
-
const itx = -(ia * m.tx + ic * m.ty);
|
|
528
|
-
const ity = -(ib * m.tx + id * m.ty);
|
|
529
|
-
const inside = (x, y) => {
|
|
530
|
-
const sx = ia * x + ic * y + itx;
|
|
531
|
-
const sy = ib * x + id * y + ity;
|
|
532
|
-
return sx >= 0 && sy >= 0 && sx < source.width && sy < source.height;
|
|
533
|
-
};
|
|
534
|
-
// Drawn into itself, a BitmapData goes a row at a time, top down, every
|
|
535
|
-
// pixel of the row read before one is written, as Flash does: a move
|
|
536
|
-
// right keeps the pixels, a move down smears rows. Through a Bitmap of
|
|
537
|
-
// itself it is plain scan order, which smears both ways (adl).
|
|
538
|
-
const row = rows && source === store ? new Uint32Array(area.width) : null;
|
|
539
|
-
const written = row ? new Uint8Array(area.width) : null;
|
|
540
|
-
for (let y = area.y; y < area.y + area.height; y++) {
|
|
541
|
-
if (row && written) {
|
|
542
|
-
written.fill(0);
|
|
543
|
-
}
|
|
544
|
-
for (let x = area.x; x < area.x + area.width; x++) {
|
|
545
|
-
let hits = 0;
|
|
546
|
-
for (let k = 0; k < 16; k++) {
|
|
547
|
-
if (inside(x + ((k & 3) + 0.5) / 4, y + ((k >> 2) + 0.5) / 4)) {
|
|
548
|
-
hits++;
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
if (hits === 0) {
|
|
552
|
-
continue;
|
|
553
|
-
}
|
|
554
|
-
const cx = x + 0.5;
|
|
555
|
-
const cy = y + 0.5;
|
|
556
|
-
const sx = ia * cx + ic * cy + itx;
|
|
557
|
-
const sy = ib * cx + id * cy + ity;
|
|
558
|
-
let p = smooth ? bilinear(source, sx - 0.5, sy - 0.5) : nearest(source, sx, sy);
|
|
559
|
-
if (ct) {
|
|
560
|
-
p = transformed(p, ct);
|
|
561
|
-
}
|
|
562
|
-
if (hits < 16) {
|
|
563
|
-
p = scaleBy(p, hits / 16);
|
|
564
|
-
}
|
|
565
|
-
if (row && written) {
|
|
566
|
-
row[x - area.x] = p;
|
|
567
|
-
written[x - area.x] = 1;
|
|
568
|
-
continue;
|
|
569
|
-
}
|
|
570
|
-
put(store, y * store.width + x, mode, p);
|
|
571
|
-
}
|
|
572
|
-
if (row && written) {
|
|
573
|
-
for (let k = 0; k < area.width; k++) {
|
|
574
|
-
if (written[k]) {
|
|
575
|
-
put(store, y * store.width + area.x + k, mode, row[k]);
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
store.changed();
|
|
581
|
-
}
|
|
582
|
-
/** `source` at (tx, ty) composited into `area` of the store, a pixel each. */
|
|
583
|
-
function composite(store, source, area, tx, ty, ct, mode) {
|
|
584
|
-
const into = store.pixels;
|
|
585
|
-
const from = source.pixels;
|
|
586
|
-
const opaque = store.transparent ? 0 : 0xff000000;
|
|
587
|
-
const plain = !ct && mode === "normal";
|
|
588
|
-
for (let y = area.y; y < area.y + area.height; y++) {
|
|
589
|
-
let i = y * store.width + area.x;
|
|
590
|
-
let j = (y - ty) * source.width + (area.x - tx);
|
|
591
|
-
for (let x = 0; x < area.width; x++, i++, j++) {
|
|
592
|
-
const p = from[j];
|
|
593
|
-
if (plain) {
|
|
594
|
-
// Source over, inline: what blend("normal") computes.
|
|
595
|
-
const sa = p >>> 24;
|
|
596
|
-
if (sa === 0) {
|
|
597
|
-
continue;
|
|
598
|
-
}
|
|
599
|
-
if (sa === 255) {
|
|
600
|
-
into[i] = p;
|
|
601
|
-
continue;
|
|
602
|
-
}
|
|
603
|
-
into[i] = (sourceOver(p, into[i]) | opaque) >>> 0;
|
|
604
|
-
continue;
|
|
605
|
-
}
|
|
606
|
-
put(store, i, mode, ct ? transformed(p, ct) : p);
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
/** A drawn pixel composited into the store by the blend mode, opaque where the store is. */
|
|
611
|
-
function put(store, i, mode, p) {
|
|
612
|
-
const out = blend(mode, p, store.pixels[i]);
|
|
613
|
-
store.pixels[i] = store.transparent ? out : (out | 0xff000000) >>> 0;
|
|
614
|
-
}
|
|
615
|
-
function nearest(source, x, y) {
|
|
616
|
-
const px = Math.min(source.width - 1, Math.max(0, Math.floor(x)));
|
|
617
|
-
const py = Math.min(source.height - 1, Math.max(0, Math.floor(y)));
|
|
618
|
-
return source.pixels[py * source.width + px];
|
|
619
|
-
}
|
|
620
|
-
/** Bilinear between the four premultiplied pixels round (x, y), clamped to the edges. */
|
|
621
|
-
function bilinear(source, x, y) {
|
|
622
|
-
const x0 = Math.floor(x);
|
|
623
|
-
const y0 = Math.floor(y);
|
|
624
|
-
const fx = x - x0;
|
|
625
|
-
const fy = y - y0;
|
|
626
|
-
const at = (px, py) => source.pixels[Math.min(source.height - 1, Math.max(0, py)) * source.width +
|
|
627
|
-
Math.min(source.width - 1, Math.max(0, px))];
|
|
628
|
-
const p00 = at(x0, y0);
|
|
629
|
-
const p10 = at(x0 + 1, y0);
|
|
630
|
-
const p01 = at(x0, y0 + 1);
|
|
631
|
-
const p11 = at(x0 + 1, y0 + 1);
|
|
632
|
-
let out = 0;
|
|
633
|
-
for (const shift of [24, 16, 8, 0]) {
|
|
634
|
-
const v = (p) => (p >>> shift) & 0xff;
|
|
635
|
-
const top = v(p00) * (1 - fx) + v(p10) * fx;
|
|
636
|
-
const bottom = v(p01) * (1 - fx) + v(p11) * fx;
|
|
637
|
-
out |= Math.round(top * (1 - fy) + bottom * fy) << shift;
|
|
638
|
-
}
|
|
639
|
-
return out >>> 0;
|
|
640
|
-
}
|
|
641
|
-
/** A premultiplied pixel through a colour transform, as colorTransform computes it. */
|
|
642
|
-
function transformed(p, ct) {
|
|
643
|
-
const a0 = p >>> 24;
|
|
644
|
-
if (a0 === 0 && ct.aAdd <= 0) {
|
|
645
|
-
return 0;
|
|
646
|
-
}
|
|
647
|
-
const fixed = (v) => Math.trunc(v * 256);
|
|
648
|
-
const ch = (v, mul, add) => Math.max(0, Math.min(255, Math.floor((v * fixed(mul)) / 256) + Math.trunc(add)));
|
|
649
|
-
const straight = (shift) => a0 === 0 ? 0 : Math.min(255, Math.floor((((p >>> shift) & 0xff) * 255) / a0));
|
|
650
|
-
const a = ch(a0, ct.aMul, ct.aAdd);
|
|
651
|
-
const out = (v) => (a === 255 ? v : (v * (a + 1)) >> 8);
|
|
652
|
-
return (((a << 24) |
|
|
653
|
-
(out(ch(straight(16), ct.rMul, ct.rAdd)) << 16) |
|
|
654
|
-
(out(ch(straight(8), ct.gMul, ct.gAdd)) << 8) |
|
|
655
|
-
out(ch(straight(0), ct.bMul, ct.bAdd))) >>>
|
|
656
|
-
0);
|
|
657
|
-
}
|
|
658
|
-
/** A premultiplied pixel with all four channels scaled by a coverage. */
|
|
659
|
-
function scaleBy(p, f) {
|
|
660
|
-
let out = 0;
|
|
661
|
-
for (const shift of [24, 16, 8, 0]) {
|
|
662
|
-
out |= Math.round(((p >>> shift) & 0xff) * f) << shift;
|
|
663
|
-
}
|
|
664
|
-
return out >>> 0;
|
|
665
|
-
}
|
|
666
|
-
/** Premultiplied s over d by a blend mode, the W3C's compositing with each mode's mix. */
|
|
667
|
-
function blend(mode, s, d) {
|
|
668
|
-
const sa = s >>> 24;
|
|
669
|
-
if (mode === "normal" || mode === "layer") {
|
|
670
|
-
return sourceOver(s, d);
|
|
671
|
-
}
|
|
672
|
-
const da = d >>> 24;
|
|
673
|
-
const mul = (x, y) => (x * y + 127) / 255;
|
|
674
|
-
const channel = (shift) => {
|
|
675
|
-
const cs = (s >>> shift) & 0xff;
|
|
676
|
-
const cd = (d >>> shift) & 0xff;
|
|
677
|
-
let b;
|
|
678
|
-
switch (mode) {
|
|
679
|
-
case "multiply":
|
|
680
|
-
b = mul(cs, cd);
|
|
681
|
-
break;
|
|
682
|
-
case "screen":
|
|
683
|
-
b = (cs * da) / 255 + (cd * sa) / 255 - mul(cs, cd);
|
|
684
|
-
break;
|
|
685
|
-
case "lighten":
|
|
686
|
-
b = Math.max(mul(cs, da), mul(cd, sa));
|
|
687
|
-
break;
|
|
688
|
-
case "darken":
|
|
689
|
-
b = Math.min(mul(cs, da), mul(cd, sa));
|
|
690
|
-
break;
|
|
691
|
-
case "difference":
|
|
692
|
-
b = Math.abs(mul(cs, da) - mul(cd, sa));
|
|
693
|
-
break;
|
|
694
|
-
case "add":
|
|
695
|
-
return Math.min(255, cs + cd);
|
|
696
|
-
case "subtract":
|
|
697
|
-
return Math.max(0, cd - cs);
|
|
698
|
-
case "invert":
|
|
699
|
-
// The destination inverted, the source's colour unused.
|
|
700
|
-
return Math.max(0, Math.min(255, Math.floor(mul(255 - cd, sa) + mul(cd, 255 - sa))));
|
|
701
|
-
case "overlay":
|
|
702
|
-
b = 2 * cd <= da ? 2 * mul(cs, cd) : mul(sa, da) - 2 * mul(da - cd, sa - cs);
|
|
703
|
-
break;
|
|
704
|
-
default:
|
|
705
|
-
b = 2 * cs <= sa ? 2 * mul(cs, cd) : mul(sa, da) - 2 * mul(da - cd, sa - cs);
|
|
706
|
-
break;
|
|
707
|
-
}
|
|
708
|
-
return Math.max(0, Math.min(255, Math.floor(b + mul(cs, 255 - da) + mul(cd, 255 - sa))));
|
|
709
|
-
};
|
|
710
|
-
const a = mode === "add" || mode === "subtract"
|
|
711
|
-
? Math.min(255, sa + da)
|
|
712
|
-
: Math.min(255, Math.round(sa + da - mul(sa, da)));
|
|
713
|
-
return ((a << 24) | (channel(16) << 16) | (channel(8) << 8) | channel(0)) >>> 0;
|
|
714
|
-
}
|
|
715
|
-
/** Source over destination, both premultiplied, as Flash composites: s + ((d * (256 - sa)) >> 8). */
|
|
716
|
-
function sourceOver(s, d) {
|
|
717
|
-
const sa = s >>> 24;
|
|
718
|
-
if (sa === 255) {
|
|
719
|
-
return s >>> 0;
|
|
720
|
-
}
|
|
721
|
-
const k = 256 - sa;
|
|
722
|
-
const ch = (shift) => Math.min(255, ((s >>> shift) & 0xff) + ((((d >>> shift) & 0xff) * k) >> 8));
|
|
723
|
-
return ((ch(24) << 24) | (ch(16) << 16) | (ch(8) << 8) | ch(0)) >>> 0;
|
|
724
|
-
}
|
|
725
|
-
//# sourceMappingURL=bitmap-ops.js.map
|