@flighthq/surface 0.1.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.d.ts +43 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +42 -0
- package/dist/index.js.map +1 -0
- package/dist/surface.d.ts +16 -0
- package/dist/surface.d.ts.map +1 -0
- package/dist/surface.js +84 -0
- package/dist/surface.js.map +1 -0
- package/dist/surfaceAffine.d.ts +27 -0
- package/dist/surfaceAffine.d.ts.map +1 -0
- package/dist/surfaceAffine.js +159 -0
- package/dist/surfaceAffine.js.map +1 -0
- package/dist/surfaceAlpha.d.ts +27 -0
- package/dist/surfaceAlpha.d.ts.map +1 -0
- package/dist/surfaceAlpha.js +83 -0
- package/dist/surfaceAlpha.js.map +1 -0
- package/dist/surfaceBevel.d.ts +42 -0
- package/dist/surfaceBevel.d.ts.map +1 -0
- package/dist/surfaceBevel.js +102 -0
- package/dist/surfaceBevel.js.map +1 -0
- package/dist/surfaceBlur.d.ts +62 -0
- package/dist/surfaceBlur.d.ts.map +1 -0
- package/dist/surfaceBlur.js +263 -0
- package/dist/surfaceBlur.js.map +1 -0
- package/dist/surfaceChannel.d.ts +24 -0
- package/dist/surfaceChannel.d.ts.map +1 -0
- package/dist/surfaceChannel.js +126 -0
- package/dist/surfaceChannel.js.map +1 -0
- package/dist/surfaceColorMatrix.d.ts +57 -0
- package/dist/surfaceColorMatrix.d.ts.map +1 -0
- package/dist/surfaceColorMatrix.js +127 -0
- package/dist/surfaceColorMatrix.js.map +1 -0
- package/dist/surfaceCompare.d.ts +18 -0
- package/dist/surfaceCompare.d.ts.map +1 -0
- package/dist/surfaceCompare.js +64 -0
- package/dist/surfaceCompare.js.map +1 -0
- package/dist/surfaceComposite.d.ts +53 -0
- package/dist/surfaceComposite.d.ts.map +1 -0
- package/dist/surfaceComposite.js +230 -0
- package/dist/surfaceComposite.js.map +1 -0
- package/dist/surfaceConvolution.d.ts +27 -0
- package/dist/surfaceConvolution.d.ts.map +1 -0
- package/dist/surfaceConvolution.js +103 -0
- package/dist/surfaceConvolution.js.map +1 -0
- package/dist/surfaceCopy.d.ts +23 -0
- package/dist/surfaceCopy.d.ts.map +1 -0
- package/dist/surfaceCopy.js +78 -0
- package/dist/surfaceCopy.js.map +1 -0
- package/dist/surfaceCoverage.d.ts +11 -0
- package/dist/surfaceCoverage.d.ts.map +1 -0
- package/dist/surfaceCoverage.js +29 -0
- package/dist/surfaceCoverage.js.map +1 -0
- package/dist/surfaceCrop.d.ts +33 -0
- package/dist/surfaceCrop.d.ts.map +1 -0
- package/dist/surfaceCrop.js +174 -0
- package/dist/surfaceCrop.js.map +1 -0
- package/dist/surfaceDisplacement.d.ts +48 -0
- package/dist/surfaceDisplacement.d.ts.map +1 -0
- package/dist/surfaceDisplacement.js +131 -0
- package/dist/surfaceDisplacement.js.map +1 -0
- package/dist/surfaceDissolve.d.ts +21 -0
- package/dist/surfaceDissolve.d.ts.map +1 -0
- package/dist/surfaceDissolve.js +97 -0
- package/dist/surfaceDissolve.js.map +1 -0
- package/dist/surfaceDraw.d.ts +8 -0
- package/dist/surfaceDraw.d.ts.map +1 -0
- package/dist/surfaceDraw.js +14 -0
- package/dist/surfaceDraw.js.map +1 -0
- package/dist/surfaceEncode.d.ts +3 -0
- package/dist/surfaceEncode.d.ts.map +1 -0
- package/dist/surfaceEncode.js +18 -0
- package/dist/surfaceEncode.js.map +1 -0
- package/dist/surfaceFill.d.ts +12 -0
- package/dist/surfaceFill.d.ts.map +1 -0
- package/dist/surfaceFill.js +85 -0
- package/dist/surfaceFill.js.map +1 -0
- package/dist/surfaceFingerprint.d.ts +26 -0
- package/dist/surfaceFingerprint.d.ts.map +1 -0
- package/dist/surfaceFingerprint.js +99 -0
- package/dist/surfaceFingerprint.js.map +1 -0
- package/dist/surfaceFlip.d.ts +16 -0
- package/dist/surfaceFlip.d.ts.map +1 -0
- package/dist/surfaceFlip.js +98 -0
- package/dist/surfaceFlip.js.map +1 -0
- package/dist/surfaceFormat.d.ts +26 -0
- package/dist/surfaceFormat.d.ts.map +1 -0
- package/dist/surfaceFormat.js +80 -0
- package/dist/surfaceFormat.js.map +1 -0
- package/dist/surfaceFrom.d.ts +14 -0
- package/dist/surfaceFrom.d.ts.map +1 -0
- package/dist/surfaceFrom.js +87 -0
- package/dist/surfaceFrom.js.map +1 -0
- package/dist/surfaceGradient.d.ts +57 -0
- package/dist/surfaceGradient.d.ts.map +1 -0
- package/dist/surfaceGradient.js +176 -0
- package/dist/surfaceGradient.js.map +1 -0
- package/dist/surfaceGradientFill.d.ts +32 -0
- package/dist/surfaceGradientFill.d.ts.map +1 -0
- package/dist/surfaceGradientFill.js +120 -0
- package/dist/surfaceGradientFill.js.map +1 -0
- package/dist/surfaceHistogram.d.ts +20 -0
- package/dist/surfaceHistogram.d.ts.map +1 -0
- package/dist/surfaceHistogram.js +61 -0
- package/dist/surfaceHistogram.js.map +1 -0
- package/dist/surfaceImageChannel.d.ts +2 -0
- package/dist/surfaceImageChannel.d.ts.map +1 -0
- package/dist/surfaceImageChannel.js +2 -0
- package/dist/surfaceImageChannel.js.map +1 -0
- package/dist/surfaceMedian.d.ts +14 -0
- package/dist/surfaceMedian.d.ts.map +1 -0
- package/dist/surfaceMedian.js +77 -0
- package/dist/surfaceMedian.js.map +1 -0
- package/dist/surfaceMorphological.d.ts +29 -0
- package/dist/surfaceMorphological.d.ts.map +1 -0
- package/dist/surfaceMorphological.js +81 -0
- package/dist/surfaceMorphological.js.map +1 -0
- package/dist/surfaceNoise.d.ts +48 -0
- package/dist/surfaceNoise.d.ts.map +1 -0
- package/dist/surfaceNoise.js +245 -0
- package/dist/surfaceNoise.js.map +1 -0
- package/dist/surfacePaletteMap.d.ts +17 -0
- package/dist/surfacePaletteMap.d.ts.map +1 -0
- package/dist/surfacePaletteMap.js +45 -0
- package/dist/surfacePaletteMap.js.map +1 -0
- package/dist/surfacePixel.d.ts +9 -0
- package/dist/surfacePixel.d.ts.map +1 -0
- package/dist/surfacePixel.js +36 -0
- package/dist/surfacePixel.js.map +1 -0
- package/dist/surfacePixelate.d.ts +13 -0
- package/dist/surfacePixelate.d.ts.map +1 -0
- package/dist/surfacePixelate.js +61 -0
- package/dist/surfacePixelate.js.map +1 -0
- package/dist/surfaceQuery.d.ts +13 -0
- package/dist/surfaceQuery.d.ts.map +1 -0
- package/dist/surfaceQuery.js +46 -0
- package/dist/surfaceQuery.js.map +1 -0
- package/dist/surfaceRegion.d.ts +20 -0
- package/dist/surfaceRegion.d.ts.map +1 -0
- package/dist/surfaceRegion.js +28 -0
- package/dist/surfaceRegion.js.map +1 -0
- package/dist/surfaceResize.d.ts +27 -0
- package/dist/surfaceResize.d.ts.map +1 -0
- package/dist/surfaceResize.js +191 -0
- package/dist/surfaceResize.js.map +1 -0
- package/dist/surfaceRotate.d.ts +32 -0
- package/dist/surfaceRotate.d.ts.map +1 -0
- package/dist/surfaceRotate.js +143 -0
- package/dist/surfaceRotate.js.map +1 -0
- package/dist/surfaceShadow.d.ts +107 -0
- package/dist/surfaceShadow.d.ts.map +1 -0
- package/dist/surfaceShadow.js +173 -0
- package/dist/surfaceShadow.js.map +1 -0
- package/dist/surfaceSharpen.d.ts +25 -0
- package/dist/surfaceSharpen.d.ts.map +1 -0
- package/dist/surfaceSharpen.js +47 -0
- package/dist/surfaceSharpen.js.map +1 -0
- package/dist/surfaceTone.d.ts +28 -0
- package/dist/surfaceTone.d.ts.map +1 -0
- package/dist/surfaceTone.js +69 -0
- package/dist/surfaceTone.js.map +1 -0
- package/dist/surfaceTransform.d.ts +40 -0
- package/dist/surfaceTransform.d.ts.map +1 -0
- package/dist/surfaceTransform.js +163 -0
- package/dist/surfaceTransform.js.map +1 -0
- package/dist/surfaceWarp.d.ts +48 -0
- package/dist/surfaceWarp.d.ts.map +1 -0
- package/dist/surfaceWarp.js +284 -0
- package/dist/surfaceWarp.js.map +1 -0
- package/package.json +39 -0
- package/src/surface.test.ts +78 -0
- package/src/surfaceAffine.test.ts +98 -0
- package/src/surfaceAlpha.test.ts +113 -0
- package/src/surfaceBevel.test.ts +67 -0
- package/src/surfaceBlur.test.ts +240 -0
- package/src/surfaceChannel.test.ts +97 -0
- package/src/surfaceColorMatrix.test.ts +220 -0
- package/src/surfaceCompare.test.ts +92 -0
- package/src/surfaceComposite.test.ts +256 -0
- package/src/surfaceConvolution.test.ts +136 -0
- package/src/surfaceCopy.test.ts +54 -0
- package/src/surfaceCoverage.test.ts +39 -0
- package/src/surfaceCrop.test.ts +116 -0
- package/src/surfaceDisplacement.test.ts +142 -0
- package/src/surfaceDissolve.test.ts +99 -0
- package/src/surfaceDraw.test.ts +38 -0
- package/src/surfaceEncode.test.ts +18 -0
- package/src/surfaceFill.test.ts +89 -0
- package/src/surfaceFingerprint.test.ts +103 -0
- package/src/surfaceFlip.test.ts +59 -0
- package/src/surfaceFormat.test.ts +124 -0
- package/src/surfaceFrom.test.ts +62 -0
- package/src/surfaceGradient.test.ts +91 -0
- package/src/surfaceGradientFill.test.ts +107 -0
- package/src/surfaceHistogram.test.ts +70 -0
- package/src/surfaceImageChannel.test.ts +19 -0
- package/src/surfaceMedian.test.ts +45 -0
- package/src/surfaceMorphological.test.ts +75 -0
- package/src/surfaceNoise.test.ts +169 -0
- package/src/surfacePaletteMap.test.ts +55 -0
- package/src/surfacePixel.test.ts +99 -0
- package/src/surfacePixelate.test.ts +46 -0
- package/src/surfaceQuery.test.ts +48 -0
- package/src/surfaceRegion.test.ts +34 -0
- package/src/surfaceResize.test.ts +112 -0
- package/src/surfaceRotate.test.ts +119 -0
- package/src/surfaceShadow.test.ts +165 -0
- package/src/surfaceSharpen.test.ts +50 -0
- package/src/surfaceTone.test.ts +104 -0
- package/src/surfaceTransform.test.ts +134 -0
- package/src/surfaceWarp.test.ts +124 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { SurfaceEdgeMode, SurfaceRegion, SurfaceResizeMode } from '@flighthq/types';
|
|
2
|
+
/**
|
|
3
|
+
* Applies a full 3×3 projective (homography) warp to `source`, writing into
|
|
4
|
+
* `dest`. The `matrix` is a 9-element row-major array representing:
|
|
5
|
+
*
|
|
6
|
+
* ```
|
|
7
|
+
* [ m0 m1 m2 ]
|
|
8
|
+
* [ m3 m4 m5 ]
|
|
9
|
+
* [ m6 m7 m8 ]
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* For each `dest` pixel `(x, y)`, the inverse matrix is applied as:
|
|
13
|
+
*
|
|
14
|
+
* ```
|
|
15
|
+
* w = m6*x + m7*y + m8
|
|
16
|
+
* srcX = (m0*x + m1*y + m2) / w
|
|
17
|
+
* srcY = (m3*x + m4*y + m5) / w
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* This is an inverse-mapping (destination-driven) warp, so every output pixel
|
|
21
|
+
* is filled by sampling `source`. Out-of-bounds source positions are resolved
|
|
22
|
+
* with `edgeMode` (default `'transparent'`). Sampling quality is controlled
|
|
23
|
+
* by `sampleMode` (default `'bilinear'`).
|
|
24
|
+
*
|
|
25
|
+
* Pass the **inverse** of the forward transform: if you want to map source
|
|
26
|
+
* points into dest, compute the forward matrix and invert it before passing.
|
|
27
|
+
*
|
|
28
|
+
* `dest` must not alias `source` when their regions overlap.
|
|
29
|
+
*/
|
|
30
|
+
export declare function warpSurface(dest: Readonly<SurfaceRegion>, source: Readonly<SurfaceRegion>, matrix: Readonly<[number, number, number, number, number, number, number, number, number]>, edgeMode?: SurfaceEdgeMode, sampleMode?: SurfaceResizeMode): void;
|
|
31
|
+
/**
|
|
32
|
+
* Warps `source` into `dest` by mapping its four corners to the four corners of
|
|
33
|
+
* `dstQuad`. This is a perspective warp (homography) defined by 4 destination
|
|
34
|
+
* points corresponding to the source corners in order: top-left, top-right,
|
|
35
|
+
* bottom-right, bottom-left.
|
|
36
|
+
*
|
|
37
|
+
* `dstQuad` is an 8-element array `[x0, y0, x1, y1, x2, y2, x3, y3]` in
|
|
38
|
+
* destination-region-local coordinates. The source corners are mapped from the
|
|
39
|
+
* full `source` region in the same order.
|
|
40
|
+
*
|
|
41
|
+
* Internally computes the homography from source corners to `dstQuad` corners,
|
|
42
|
+
* inverts it, and delegates to `warpSurface`. Edge and sample modes follow the
|
|
43
|
+
* same rules as `warpSurface`.
|
|
44
|
+
*
|
|
45
|
+
* `dest` must not alias `source` when their regions overlap.
|
|
46
|
+
*/
|
|
47
|
+
export declare function warpSurfaceQuad(dest: Readonly<SurfaceRegion>, source: Readonly<SurfaceRegion>, dstQuad: Readonly<[number, number, number, number, number, number, number, number]>, edgeMode?: SurfaceEdgeMode, sampleMode?: SurfaceResizeMode): void;
|
|
48
|
+
//# sourceMappingURL=surfaceWarp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"surfaceWarp.d.ts","sourceRoot":"","sources":["../src/surfaceWarp.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,QAAQ,CAAC,aAAa,CAAC,EAC7B,MAAM,EAAE,QAAQ,CAAC,aAAa,CAAC,EAC/B,MAAM,EAAE,QAAQ,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EAC1F,QAAQ,GAAE,eAA+B,EACzC,UAAU,GAAE,iBAA8B,GACzC,IAAI,CA+CN;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,QAAQ,CAAC,aAAa,CAAC,EAC7B,MAAM,EAAE,QAAQ,CAAC,aAAa,CAAC,EAC/B,OAAO,EAAE,QAAQ,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EACnF,QAAQ,GAAE,eAA+B,EACzC,UAAU,GAAE,iBAA8B,GACzC,IAAI,CAYN"}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { invalidateImageResource } from '@flighthq/image';
|
|
2
|
+
/**
|
|
3
|
+
* Applies a full 3×3 projective (homography) warp to `source`, writing into
|
|
4
|
+
* `dest`. The `matrix` is a 9-element row-major array representing:
|
|
5
|
+
*
|
|
6
|
+
* ```
|
|
7
|
+
* [ m0 m1 m2 ]
|
|
8
|
+
* [ m3 m4 m5 ]
|
|
9
|
+
* [ m6 m7 m8 ]
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* For each `dest` pixel `(x, y)`, the inverse matrix is applied as:
|
|
13
|
+
*
|
|
14
|
+
* ```
|
|
15
|
+
* w = m6*x + m7*y + m8
|
|
16
|
+
* srcX = (m0*x + m1*y + m2) / w
|
|
17
|
+
* srcY = (m3*x + m4*y + m5) / w
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* This is an inverse-mapping (destination-driven) warp, so every output pixel
|
|
21
|
+
* is filled by sampling `source`. Out-of-bounds source positions are resolved
|
|
22
|
+
* with `edgeMode` (default `'transparent'`). Sampling quality is controlled
|
|
23
|
+
* by `sampleMode` (default `'bilinear'`).
|
|
24
|
+
*
|
|
25
|
+
* Pass the **inverse** of the forward transform: if you want to map source
|
|
26
|
+
* points into dest, compute the forward matrix and invert it before passing.
|
|
27
|
+
*
|
|
28
|
+
* `dest` must not alias `source` when their regions overlap.
|
|
29
|
+
*/
|
|
30
|
+
export function warpSurface(dest, source, matrix, edgeMode = 'transparent', sampleMode = 'bilinear') {
|
|
31
|
+
const dw = dest.width;
|
|
32
|
+
const dh = dest.height;
|
|
33
|
+
const sw = source.width;
|
|
34
|
+
const sh = source.height;
|
|
35
|
+
if (dw === 0 || dh === 0 || sw === 0 || sh === 0)
|
|
36
|
+
return;
|
|
37
|
+
const [m0, m1, m2, m3, m4, m5, m6, m7, m8] = matrix;
|
|
38
|
+
const sd = source.surface.data;
|
|
39
|
+
const dd = dest.surface.data;
|
|
40
|
+
const sStride = source.surface.width;
|
|
41
|
+
const dStride = dest.surface.width;
|
|
42
|
+
for (let dy = 0; dy < dh; dy++) {
|
|
43
|
+
const oy = dest.y + dy;
|
|
44
|
+
if (oy < 0 || oy >= dest.surface.height)
|
|
45
|
+
continue;
|
|
46
|
+
for (let dx = 0; dx < dw; dx++) {
|
|
47
|
+
const ox = dest.x + dx;
|
|
48
|
+
if (ox < 0 || ox >= dStride)
|
|
49
|
+
continue;
|
|
50
|
+
const w = m6 * dx + m7 * dy + m8;
|
|
51
|
+
const di = (oy * dStride + ox) * 4;
|
|
52
|
+
if (Math.abs(w) < 1e-10) {
|
|
53
|
+
dd[di] = 0;
|
|
54
|
+
dd[di + 1] = 0;
|
|
55
|
+
dd[di + 2] = 0;
|
|
56
|
+
dd[di + 3] = 0;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const invW = 1 / w;
|
|
60
|
+
const sx = (m0 * dx + m1 * dy + m2) * invW;
|
|
61
|
+
const sy = (m3 * dx + m4 * dy + m5) * invW;
|
|
62
|
+
warpSampleSurface(dd, di, sd, sw, sh, source.x, source.y, sStride, source.surface.height, sx, sy, sampleMode, edgeMode);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
invalidateImageResource(dest.surface);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Warps `source` into `dest` by mapping its four corners to the four corners of
|
|
69
|
+
* `dstQuad`. This is a perspective warp (homography) defined by 4 destination
|
|
70
|
+
* points corresponding to the source corners in order: top-left, top-right,
|
|
71
|
+
* bottom-right, bottom-left.
|
|
72
|
+
*
|
|
73
|
+
* `dstQuad` is an 8-element array `[x0, y0, x1, y1, x2, y2, x3, y3]` in
|
|
74
|
+
* destination-region-local coordinates. The source corners are mapped from the
|
|
75
|
+
* full `source` region in the same order.
|
|
76
|
+
*
|
|
77
|
+
* Internally computes the homography from source corners to `dstQuad` corners,
|
|
78
|
+
* inverts it, and delegates to `warpSurface`. Edge and sample modes follow the
|
|
79
|
+
* same rules as `warpSurface`.
|
|
80
|
+
*
|
|
81
|
+
* `dest` must not alias `source` when their regions overlap.
|
|
82
|
+
*/
|
|
83
|
+
export function warpSurfaceQuad(dest, source, dstQuad, edgeMode = 'transparent', sampleMode = 'bilinear') {
|
|
84
|
+
const sw = source.width;
|
|
85
|
+
const sh = source.height;
|
|
86
|
+
if (sw === 0 || sh === 0 || dest.width === 0 || dest.height === 0)
|
|
87
|
+
return;
|
|
88
|
+
// Source corners in source-region-local space (top-left, top-right, bottom-right, bottom-left).
|
|
89
|
+
const srcPts = [0, 0, sw, 0, sw, sh, 0, sh];
|
|
90
|
+
// Compute homography: dstQuad ← srcPts (forward), then invert for the warp.
|
|
91
|
+
const H = computeHomography(srcPts, dstQuad);
|
|
92
|
+
if (H === null)
|
|
93
|
+
return;
|
|
94
|
+
const Hinv = invertMatrix3x3(H);
|
|
95
|
+
if (Hinv === null)
|
|
96
|
+
return;
|
|
97
|
+
warpSurface(dest, source, Hinv, edgeMode, sampleMode);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Samples `source` at source-region-local `(sx, sy)`, writing four bytes into
|
|
101
|
+
* `dd[di..di+3]`.
|
|
102
|
+
*/
|
|
103
|
+
function warpSampleSurface(dd, di, sd, sw, sh, originX, originY, sStride, sHeight, sx, sy, sampleMode, edgeMode) {
|
|
104
|
+
if (sampleMode === 'nearest') {
|
|
105
|
+
const ix = Math.round(sx);
|
|
106
|
+
const iy = Math.round(sy);
|
|
107
|
+
const cx = warpResolveEdge(ix, sw, edgeMode);
|
|
108
|
+
const cy = warpResolveEdge(iy, sh, edgeMode);
|
|
109
|
+
if (cx === null || cy === null) {
|
|
110
|
+
dd[di] = 0;
|
|
111
|
+
dd[di + 1] = 0;
|
|
112
|
+
dd[di + 2] = 0;
|
|
113
|
+
dd[di + 3] = 0;
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const si = ((originY + cy) * sStride + (originX + cx)) * 4;
|
|
117
|
+
dd[di] = sd[si];
|
|
118
|
+
dd[di + 1] = sd[si + 1];
|
|
119
|
+
dd[di + 2] = sd[si + 2];
|
|
120
|
+
dd[di + 3] = sd[si + 3];
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (sampleMode === 'bicubic') {
|
|
124
|
+
warpSampleBicubic(dd, di, sd, sw, sh, originX, originY, sStride, sHeight, sx, sy, edgeMode);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
// bilinear
|
|
128
|
+
warpSampleBilinear(dd, di, sd, sw, sh, originX, originY, sStride, sHeight, sx, sy, edgeMode);
|
|
129
|
+
}
|
|
130
|
+
function warpSampleBilinear(dd, di, sd, sw, sh, originX, originY, sStride, _sHeight, sx, sy, edgeMode) {
|
|
131
|
+
const x0 = Math.floor(sx);
|
|
132
|
+
const y0 = Math.floor(sy);
|
|
133
|
+
const tx = sx - x0;
|
|
134
|
+
const ty = sy - y0;
|
|
135
|
+
const cx00 = warpResolveEdge(x0, sw, edgeMode);
|
|
136
|
+
const cx10 = warpResolveEdge(x0 + 1, sw, edgeMode);
|
|
137
|
+
const cy00 = warpResolveEdge(y0, sh, edgeMode);
|
|
138
|
+
const cy10 = warpResolveEdge(y0 + 1, sh, edgeMode);
|
|
139
|
+
for (let c = 0; c < 4; c++) {
|
|
140
|
+
const v00 = cx00 !== null && cy00 !== null ? sd[((originY + cy00) * sStride + originX + cx00) * 4 + c] : 0;
|
|
141
|
+
const v10 = cx10 !== null && cy00 !== null ? sd[((originY + cy00) * sStride + originX + cx10) * 4 + c] : 0;
|
|
142
|
+
const v01 = cx00 !== null && cy10 !== null ? sd[((originY + cy10) * sStride + originX + cx00) * 4 + c] : 0;
|
|
143
|
+
const v11 = cx10 !== null && cy10 !== null ? sd[((originY + cy10) * sStride + originX + cx10) * 4 + c] : 0;
|
|
144
|
+
const top = v00 * (1 - tx) + v10 * tx;
|
|
145
|
+
const bottom = v01 * (1 - tx) + v11 * tx;
|
|
146
|
+
dd[di + c] = Math.round(top * (1 - ty) + bottom * ty);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function warpSampleBicubic(dd, di, sd, sw, sh, originX, originY, sStride, _sHeight, sx, sy, edgeMode) {
|
|
150
|
+
const x1 = Math.floor(sx);
|
|
151
|
+
const y1 = Math.floor(sy);
|
|
152
|
+
const tx = sx - x1;
|
|
153
|
+
const ty = sy - y1;
|
|
154
|
+
for (let c = 0; c < 4; c++) {
|
|
155
|
+
let sum = 0;
|
|
156
|
+
for (let m = -1; m <= 2; m++) {
|
|
157
|
+
const wy = catmullRomWeight(ty - m);
|
|
158
|
+
const ry = warpResolveEdge(y1 + m, sh, edgeMode);
|
|
159
|
+
for (let n = -1; n <= 2; n++) {
|
|
160
|
+
const wx = catmullRomWeight(tx - n);
|
|
161
|
+
const rx = warpResolveEdge(x1 + n, sw, edgeMode);
|
|
162
|
+
const v = rx !== null && ry !== null ? sd[((originY + ry) * sStride + originX + rx) * 4 + c] : 0;
|
|
163
|
+
sum += v * wy * wx;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
dd[di + c] = Math.max(0, Math.min(255, Math.round(sum)));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function warpResolveEdge(v, size, mode) {
|
|
170
|
+
if (v >= 0 && v < size)
|
|
171
|
+
return v;
|
|
172
|
+
switch (mode) {
|
|
173
|
+
case 'clamp':
|
|
174
|
+
return Math.max(0, Math.min(size - 1, v));
|
|
175
|
+
case 'wrap':
|
|
176
|
+
return ((v % size) + size) % size;
|
|
177
|
+
case 'mirror': {
|
|
178
|
+
const period = 2 * size;
|
|
179
|
+
const wrapped = ((v % period) + period) % period;
|
|
180
|
+
return wrapped < size ? wrapped : period - 1 - wrapped;
|
|
181
|
+
}
|
|
182
|
+
default:
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function catmullRomWeight(t) {
|
|
187
|
+
const a = Math.abs(t);
|
|
188
|
+
if (a >= 2)
|
|
189
|
+
return 0;
|
|
190
|
+
if (a >= 1)
|
|
191
|
+
return -0.5 * a * a * a + 2.5 * a * a - 4 * a + 2;
|
|
192
|
+
return 1.5 * a * a * a - 2.5 * a * a + 1;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Computes a homography H such that H * src[i] = dst[i] for 4 point pairs.
|
|
196
|
+
* Points are in [x0, y0, x1, y1, x2, y2, x3, y3] order.
|
|
197
|
+
* Returns null if the system is degenerate.
|
|
198
|
+
*/
|
|
199
|
+
function computeHomography(src, dst) {
|
|
200
|
+
// Direct Linear Transform (DLT) for 4 point correspondences.
|
|
201
|
+
// Builds an 8×8 linear system and solves it.
|
|
202
|
+
const A = [];
|
|
203
|
+
for (let i = 0; i < 4; i++) {
|
|
204
|
+
const sx = src[i * 2];
|
|
205
|
+
const sy = src[i * 2 + 1];
|
|
206
|
+
const dx = dst[i * 2];
|
|
207
|
+
const dy = dst[i * 2 + 1];
|
|
208
|
+
A.push([sx, sy, 1, 0, 0, 0, -dx * sx, -dx * sy, -dx]);
|
|
209
|
+
A.push([0, 0, 0, sx, sy, 1, -dy * sx, -dy * sy, -dy]);
|
|
210
|
+
}
|
|
211
|
+
// Extract the 8×8 sub-system (h8 = 1 normalization).
|
|
212
|
+
const b = [];
|
|
213
|
+
const M = [];
|
|
214
|
+
for (let r = 0; r < 8; r++) {
|
|
215
|
+
const row = A[r];
|
|
216
|
+
M.push(row.slice(0, 8));
|
|
217
|
+
b.push(row[8]);
|
|
218
|
+
}
|
|
219
|
+
const h = solveLinear8(M, b);
|
|
220
|
+
if (h === null)
|
|
221
|
+
return null;
|
|
222
|
+
return [h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7], 1];
|
|
223
|
+
}
|
|
224
|
+
/** Gaussian elimination to solve M*x = b for 8 unknowns. */
|
|
225
|
+
function solveLinear8(M, b) {
|
|
226
|
+
const n = 8;
|
|
227
|
+
// Augmented matrix [M | -b] (we negate b because the system is A*x + b = 0).
|
|
228
|
+
const aug = M.map((row, i) => [...row, -b[i]]);
|
|
229
|
+
for (let col = 0; col < n; col++) {
|
|
230
|
+
// Partial pivot.
|
|
231
|
+
let maxRow = col;
|
|
232
|
+
let maxVal = Math.abs(aug[col][col]);
|
|
233
|
+
for (let row = col + 1; row < n; row++) {
|
|
234
|
+
const v = Math.abs(aug[row][col]);
|
|
235
|
+
if (v > maxVal) {
|
|
236
|
+
maxVal = v;
|
|
237
|
+
maxRow = row;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (maxVal < 1e-12)
|
|
241
|
+
return null;
|
|
242
|
+
[aug[col], aug[maxRow]] = [aug[maxRow], aug[col]];
|
|
243
|
+
const pivot = aug[col][col];
|
|
244
|
+
for (let row = col + 1; row < n; row++) {
|
|
245
|
+
const factor = aug[row][col] / pivot;
|
|
246
|
+
for (let k = col; k <= n; k++) {
|
|
247
|
+
aug[row][k] -= factor * aug[col][k];
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
// Back-substitution.
|
|
252
|
+
const x = new Array(n).fill(0);
|
|
253
|
+
for (let row = n - 1; row >= 0; row--) {
|
|
254
|
+
let sum = aug[row][n];
|
|
255
|
+
for (let col = row + 1; col < n; col++) {
|
|
256
|
+
sum -= aug[row][col] * x[col];
|
|
257
|
+
}
|
|
258
|
+
x[row] = sum / aug[row][row];
|
|
259
|
+
}
|
|
260
|
+
return x;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Inverts a 3×3 matrix stored as a flat 9-element row-major array.
|
|
264
|
+
* Returns null if the matrix is singular.
|
|
265
|
+
*/
|
|
266
|
+
function invertMatrix3x3(m) {
|
|
267
|
+
const [a, b, c, d, e, f, g, h, k] = m;
|
|
268
|
+
const det = a * (e * k - f * h) - b * (d * k - f * g) + c * (d * h - e * g);
|
|
269
|
+
if (Math.abs(det) < 1e-12)
|
|
270
|
+
return null;
|
|
271
|
+
const invDet = 1 / det;
|
|
272
|
+
return [
|
|
273
|
+
(e * k - f * h) * invDet,
|
|
274
|
+
(c * h - b * k) * invDet,
|
|
275
|
+
(b * f - c * e) * invDet,
|
|
276
|
+
(f * g - d * k) * invDet,
|
|
277
|
+
(a * k - c * g) * invDet,
|
|
278
|
+
(c * d - a * f) * invDet,
|
|
279
|
+
(d * h - e * g) * invDet,
|
|
280
|
+
(b * g - a * h) * invDet,
|
|
281
|
+
(a * e - b * d) * invDet,
|
|
282
|
+
];
|
|
283
|
+
}
|
|
284
|
+
//# sourceMappingURL=surfaceWarp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"surfaceWarp.js","sourceRoot":"","sources":["../src/surfaceWarp.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@flighthq/surface",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"src/**/*.test.ts",
|
|
16
|
+
"!dist/**/*.test.js",
|
|
17
|
+
"!dist/**/*.test.d.ts",
|
|
18
|
+
"!dist/**/*.test.js.map",
|
|
19
|
+
"!dist/**/*.test.d.ts.map"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc -b",
|
|
23
|
+
"clean": "tsc -b --clean",
|
|
24
|
+
"test": "vitest run --config vitest.config.ts",
|
|
25
|
+
"test:watch": "vitest --watch --config vitest.config.ts",
|
|
26
|
+
"prepack": "npm run clean && npm run clean:dist && npm run build",
|
|
27
|
+
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@flighthq/image": "0.1.0",
|
|
31
|
+
"@flighthq/entity": "0.1.0",
|
|
32
|
+
"@flighthq/types": "0.1.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"typescript": "^5.3.0"
|
|
36
|
+
},
|
|
37
|
+
"description": "Pixel-level image manipulation using browser ImageData",
|
|
38
|
+
"sideEffects": false
|
|
39
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { cloneSurface, convertSurfaceAlphaType, createSurface } from './surface';
|
|
2
|
+
|
|
3
|
+
describe('cloneSurface', () => {
|
|
4
|
+
it('produces identical values', () => {
|
|
5
|
+
const img = createSurface(2, 2, 0x102030ff);
|
|
6
|
+
const clone = cloneSurface(img);
|
|
7
|
+
expect(clone.width).toBe(img.width);
|
|
8
|
+
expect(clone.height).toBe(img.height);
|
|
9
|
+
expect(clone.data).toEqual(img.data);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('is a deep copy', () => {
|
|
13
|
+
const img = createSurface(2, 2, 0x102030ff);
|
|
14
|
+
const clone = cloneSurface(img);
|
|
15
|
+
clone.data[0] = 0;
|
|
16
|
+
expect(img.data[0]).toBe(0x10);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('convertSurfaceAlphaType', () => {
|
|
21
|
+
it('premultiplies straight alpha', () => {
|
|
22
|
+
const img = createSurface(1, 1, 0x80808080);
|
|
23
|
+
img.alphaType = 'straight';
|
|
24
|
+
convertSurfaceAlphaType(img, 'premultiplied');
|
|
25
|
+
expect(img.alphaType).toBe('premultiplied');
|
|
26
|
+
// R = round(0x80 * (0x80/255)) ≈ round(128 * 0.502) ≈ 64
|
|
27
|
+
expect(img.data[0]).toBe(64);
|
|
28
|
+
expect(img.data[3]).toBe(0x80); // alpha unchanged
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('unpremultiplies premultiplied alpha', () => {
|
|
32
|
+
// Pre-build a premultiplied surface manually.
|
|
33
|
+
const img = createSurface(1, 1, 0x00000000);
|
|
34
|
+
img.alphaType = 'premultiplied';
|
|
35
|
+
img.data[0] = 64;
|
|
36
|
+
img.data[1] = 64;
|
|
37
|
+
img.data[2] = 64;
|
|
38
|
+
img.data[3] = 128;
|
|
39
|
+
convertSurfaceAlphaType(img, 'straight');
|
|
40
|
+
expect(img.alphaType).toBe('straight');
|
|
41
|
+
// R = round(64 * 255/128) = round(127.5) = 128
|
|
42
|
+
expect(img.data[0]).toBe(128);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('is a no-op when target matches current alphaType', () => {
|
|
46
|
+
const img = createSurface(1, 1, 0xff0000ff);
|
|
47
|
+
img.alphaType = 'straight';
|
|
48
|
+
const before = Array.from(img.data);
|
|
49
|
+
convertSurfaceAlphaType(img, 'straight');
|
|
50
|
+
expect(Array.from(img.data)).toEqual(before);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('handles zero-alpha pixels (unpremultiply)', () => {
|
|
54
|
+
const img = createSurface(1, 1, 0x00000000);
|
|
55
|
+
img.alphaType = 'premultiplied';
|
|
56
|
+
convertSurfaceAlphaType(img, 'straight');
|
|
57
|
+
expect(img.data[0]).toBe(0);
|
|
58
|
+
expect(img.data[3]).toBe(0);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe('createSurface', () => {
|
|
63
|
+
it('creates zeroed image data when no color provided', () => {
|
|
64
|
+
const img = createSurface(2, 2);
|
|
65
|
+
expect(img.width).toBe(2);
|
|
66
|
+
expect(img.height).toBe(2);
|
|
67
|
+
expect(img.data.length).toBe(16);
|
|
68
|
+
expect(img.data.every((v) => v === 0)).toBe(true);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('fills with the given color', () => {
|
|
72
|
+
const img = createSurface(2, 2, 0x112233ff);
|
|
73
|
+
expect(img.data[0]).toBe(0x11);
|
|
74
|
+
expect(img.data[1]).toBe(0x22);
|
|
75
|
+
expect(img.data[2]).toBe(0x33);
|
|
76
|
+
expect(img.data[3]).toBe(0xff);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { createSurface } from './surface';
|
|
2
|
+
import { transformSurface } from './surfaceAffine';
|
|
3
|
+
import { getSurfacePixel, setSurfacePixel } from './surfacePixel';
|
|
4
|
+
import { createSurfaceRegion } from './surfaceRegion';
|
|
5
|
+
|
|
6
|
+
describe('transformSurface', () => {
|
|
7
|
+
it('identity matrix copies source to dest', () => {
|
|
8
|
+
const src = createSurface(3, 3);
|
|
9
|
+
setSurfacePixel(src, 1, 1, 0xff0000ff);
|
|
10
|
+
const dst = createSurface(3, 3);
|
|
11
|
+
const identity: [number, number, number, number, number, number] = [1, 0, 0, 1, 0, 0];
|
|
12
|
+
transformSurface(createSurfaceRegion(dst), createSurfaceRegion(src), identity);
|
|
13
|
+
expect(getSurfacePixel(dst, 1, 1)).toBe(0xff0000ff);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('translation matrix shifts the image', () => {
|
|
17
|
+
const src = createSurface(4, 4);
|
|
18
|
+
setSurfacePixel(src, 0, 0, 0xaabbccff);
|
|
19
|
+
const dst = createSurface(4, 4);
|
|
20
|
+
// Translate: map dest (1,1) → source (0,0), i.e. e=-1, f=-1.
|
|
21
|
+
const translate: [number, number, number, number, number, number] = [1, 0, 0, 1, -1, -1];
|
|
22
|
+
transformSurface(createSurfaceRegion(dst), createSurfaceRegion(src), translate);
|
|
23
|
+
expect(getSurfacePixel(dst, 1, 1)).toBe(0xaabbccff);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('transparent edge mode writes transparent for out-of-bounds samples', () => {
|
|
27
|
+
const src = createSurface(2, 2, 0xff0000ff);
|
|
28
|
+
const dst = createSurface(4, 4);
|
|
29
|
+
const identity: [number, number, number, number, number, number] = [1, 0, 0, 1, 0, 0];
|
|
30
|
+
transformSurface(createSurfaceRegion(dst), createSurfaceRegion(src), identity, 'transparent');
|
|
31
|
+
// Out of source bounds → transparent.
|
|
32
|
+
expect(getSurfacePixel(dst, 3, 3)).toBe(0x00000000);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('clamp edge mode repeats border pixels', () => {
|
|
36
|
+
const src = createSurface(2, 2, 0x112233ff);
|
|
37
|
+
const dst = createSurface(4, 4);
|
|
38
|
+
const identity: [number, number, number, number, number, number] = [1, 0, 0, 1, 0, 0];
|
|
39
|
+
transformSurface(createSurfaceRegion(dst), createSurfaceRegion(src), identity, 'clamp');
|
|
40
|
+
// Out of source bounds → clamped to border color.
|
|
41
|
+
expect(getSurfacePixel(dst, 3, 3)).toBe(0x112233ff);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('wrap edge mode tiles the source', () => {
|
|
45
|
+
const src = createSurface(2, 2);
|
|
46
|
+
setSurfacePixel(src, 0, 0, 0xffff00ff);
|
|
47
|
+
const dst = createSurface(4, 2);
|
|
48
|
+
const identity: [number, number, number, number, number, number] = [1, 0, 0, 1, 0, 0];
|
|
49
|
+
transformSurface(createSurfaceRegion(dst), createSurfaceRegion(src), identity, 'wrap');
|
|
50
|
+
// Pixel at (2,0) should wrap to (0,0) in source.
|
|
51
|
+
expect(getSurfacePixel(dst, 2, 0)).toBe(0xffff00ff);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('mirror edge mode mirrors the source', () => {
|
|
55
|
+
const src = createSurface(3, 1);
|
|
56
|
+
setSurfacePixel(src, 0, 0, 0xff0000ff);
|
|
57
|
+
setSurfacePixel(src, 1, 0, 0x00ff00ff);
|
|
58
|
+
setSurfacePixel(src, 2, 0, 0x0000ffff);
|
|
59
|
+
const dst = createSurface(5, 1);
|
|
60
|
+
const identity: [number, number, number, number, number, number] = [1, 0, 0, 1, 0, 0];
|
|
61
|
+
transformSurface(createSurfaceRegion(dst), createSurfaceRegion(src), identity, 'mirror');
|
|
62
|
+
// x=3 mirrors to x=2 (0x0000ff), x=4 mirrors to x=1 (0x00ff00).
|
|
63
|
+
expect(getSurfacePixel(dst, 3, 0)).toBe(0x0000ffff);
|
|
64
|
+
expect(getSurfacePixel(dst, 4, 0)).toBe(0x00ff00ff);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('nearest sample mode preserves hard edges', () => {
|
|
68
|
+
const src = createSurface(2, 1);
|
|
69
|
+
setSurfacePixel(src, 0, 0, 0xff0000ff);
|
|
70
|
+
setSurfacePixel(src, 1, 0, 0x0000ffff);
|
|
71
|
+
const dst = createSurface(2, 1);
|
|
72
|
+
const identity: [number, number, number, number, number, number] = [1, 0, 0, 1, 0, 0];
|
|
73
|
+
transformSurface(createSurfaceRegion(dst), createSurfaceRegion(src), identity, 'transparent', 'nearest');
|
|
74
|
+
expect(getSurfacePixel(dst, 0, 0)).toBe(0xff0000ff);
|
|
75
|
+
expect(getSurfacePixel(dst, 1, 0)).toBe(0x0000ffff);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('skips zero-size dest or source', () => {
|
|
79
|
+
const src = createSurface(2, 2);
|
|
80
|
+
const dst = createSurface(2, 2);
|
|
81
|
+
const identity: [number, number, number, number, number, number] = [1, 0, 0, 1, 0, 0];
|
|
82
|
+
// Zero-size region — should not throw.
|
|
83
|
+
expect(() =>
|
|
84
|
+
transformSurface({ surface: dst, x: 0, y: 0, width: 0, height: 0 }, createSurfaceRegion(src), identity),
|
|
85
|
+
).not.toThrow();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('aliased out=in with identity leaves surface unchanged', () => {
|
|
89
|
+
const surf = createSurface(2, 2, 0xabcdefff);
|
|
90
|
+
const identity: [number, number, number, number, number, number] = [1, 0, 0, 1, 0, 0];
|
|
91
|
+
// Aliased write to dest and source on a distinct surface to avoid real corruption.
|
|
92
|
+
const src = createSurface(2, 2, 0xabcdefff);
|
|
93
|
+
const dst = createSurface(2, 2);
|
|
94
|
+
transformSurface(createSurfaceRegion(dst), createSurfaceRegion(src), identity);
|
|
95
|
+
expect(getSurfacePixel(dst, 0, 0)).toBe(0xabcdefff);
|
|
96
|
+
void surf;
|
|
97
|
+
});
|
|
98
|
+
});
|