react-native-parity-blur 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.
Files changed (109) hide show
  1. package/LICENSE +20 -0
  2. package/ParityBlur.podspec +20 -0
  3. package/README.md +198 -0
  4. package/android/build.gradle +57 -0
  5. package/android/src/main/AndroidManifest.xml +2 -0
  6. package/android/src/main/java/com/parityblur/AndroidBlurCalibration.kt +82 -0
  7. package/android/src/main/java/com/parityblur/BlurEngine.kt +132 -0
  8. package/android/src/main/java/com/parityblur/ParityBlurDebug.kt +24 -0
  9. package/android/src/main/java/com/parityblur/ParityBlurPackage.kt +17 -0
  10. package/android/src/main/java/com/parityblur/ParityBlurView.kt +542 -0
  11. package/android/src/main/java/com/parityblur/ParityBlurViewManager.kt +161 -0
  12. package/android/src/main/java/com/parityblur/PipelineMath.kt +202 -0
  13. package/android/src/main/java/com/parityblur/PixelCopySnapshotProvider.kt +108 -0
  14. package/android/src/main/java/com/parityblur/SoftwareSnapshotProvider.kt +173 -0
  15. package/android/src/main/java/com/parityblur/WindowBlurContext.kt +116 -0
  16. package/android/src/test/java/com/parityblur/MiniJson.kt +179 -0
  17. package/android/src/test/java/com/parityblur/PipelineFixturesTest.kt +222 -0
  18. package/ios/BlurEngine.swift +106 -0
  19. package/ios/ColorPipeline.swift +72 -0
  20. package/ios/MetalPresentationSurface.swift +51 -0
  21. package/ios/ParityBlurCoreView.swift +382 -0
  22. package/ios/ParityBlurDebug.swift +18 -0
  23. package/ios/ParityBlurView.h +14 -0
  24. package/ios/ParityBlurView.mm +135 -0
  25. package/ios/PipelineMath.swift +177 -0
  26. package/ios/SnapshotProvider.swift +128 -0
  27. package/ios/WindowBlurContext.swift +111 -0
  28. package/lib/module/BlurView.js +124 -0
  29. package/lib/module/BlurView.js.map +1 -0
  30. package/lib/module/ParityBlurViewNativeComponent.ts +82 -0
  31. package/lib/module/defaults.js +13 -0
  32. package/lib/module/defaults.js.map +1 -0
  33. package/lib/module/index.js +5 -0
  34. package/lib/module/index.js.map +1 -0
  35. package/lib/module/package.json +1 -0
  36. package/lib/module/pipeline/androidCalibration.js +55 -0
  37. package/lib/module/pipeline/androidCalibration.js.map +1 -0
  38. package/lib/module/pipeline/captureRect.js +151 -0
  39. package/lib/module/pipeline/captureRect.js.map +1 -0
  40. package/lib/module/pipeline/color.js +82 -0
  41. package/lib/module/pipeline/color.js.map +1 -0
  42. package/lib/module/pipeline/constants.js +65 -0
  43. package/lib/module/pipeline/constants.js.map +1 -0
  44. package/lib/module/pipeline/downsample.js +61 -0
  45. package/lib/module/pipeline/downsample.js.map +1 -0
  46. package/lib/module/pipeline/index.js +23 -0
  47. package/lib/module/pipeline/index.js.map +1 -0
  48. package/lib/module/pipeline/overlay.js +56 -0
  49. package/lib/module/pipeline/overlay.js.map +1 -0
  50. package/lib/module/pipeline/saturation.js +52 -0
  51. package/lib/module/pipeline/saturation.js.map +1 -0
  52. package/lib/module/pipeline/types.js +23 -0
  53. package/lib/module/pipeline/types.js.map +1 -0
  54. package/lib/module/pipeline/units.js +33 -0
  55. package/lib/module/pipeline/units.js.map +1 -0
  56. package/lib/module/presets.js +96 -0
  57. package/lib/module/presets.js.map +1 -0
  58. package/lib/module/types.js +4 -0
  59. package/lib/module/types.js.map +1 -0
  60. package/lib/typescript/package.json +1 -0
  61. package/lib/typescript/src/BlurView.d.ts +16 -0
  62. package/lib/typescript/src/BlurView.d.ts.map +1 -0
  63. package/lib/typescript/src/ParityBlurViewNativeComponent.d.ts +63 -0
  64. package/lib/typescript/src/ParityBlurViewNativeComponent.d.ts.map +1 -0
  65. package/lib/typescript/src/defaults.d.ts +12 -0
  66. package/lib/typescript/src/defaults.d.ts.map +1 -0
  67. package/lib/typescript/src/index.d.ts +5 -0
  68. package/lib/typescript/src/index.d.ts.map +1 -0
  69. package/lib/typescript/src/pipeline/androidCalibration.d.ts +42 -0
  70. package/lib/typescript/src/pipeline/androidCalibration.d.ts.map +1 -0
  71. package/lib/typescript/src/pipeline/captureRect.d.ts +84 -0
  72. package/lib/typescript/src/pipeline/captureRect.d.ts.map +1 -0
  73. package/lib/typescript/src/pipeline/color.d.ts +19 -0
  74. package/lib/typescript/src/pipeline/color.d.ts.map +1 -0
  75. package/lib/typescript/src/pipeline/constants.d.ts +52 -0
  76. package/lib/typescript/src/pipeline/constants.d.ts.map +1 -0
  77. package/lib/typescript/src/pipeline/downsample.d.ts +31 -0
  78. package/lib/typescript/src/pipeline/downsample.d.ts.map +1 -0
  79. package/lib/typescript/src/pipeline/index.d.ts +20 -0
  80. package/lib/typescript/src/pipeline/index.d.ts.map +1 -0
  81. package/lib/typescript/src/pipeline/overlay.d.ts +31 -0
  82. package/lib/typescript/src/pipeline/overlay.d.ts.map +1 -0
  83. package/lib/typescript/src/pipeline/saturation.d.ts +38 -0
  84. package/lib/typescript/src/pipeline/saturation.d.ts.map +1 -0
  85. package/lib/typescript/src/pipeline/types.d.ts +32 -0
  86. package/lib/typescript/src/pipeline/types.d.ts.map +1 -0
  87. package/lib/typescript/src/pipeline/units.d.ts +23 -0
  88. package/lib/typescript/src/pipeline/units.d.ts.map +1 -0
  89. package/lib/typescript/src/presets.d.ts +57 -0
  90. package/lib/typescript/src/presets.d.ts.map +1 -0
  91. package/lib/typescript/src/types.d.ts +116 -0
  92. package/lib/typescript/src/types.d.ts.map +1 -0
  93. package/package.json +167 -0
  94. package/src/BlurView.tsx +195 -0
  95. package/src/ParityBlurViewNativeComponent.ts +82 -0
  96. package/src/defaults.ts +20 -0
  97. package/src/index.ts +10 -0
  98. package/src/pipeline/androidCalibration.ts +64 -0
  99. package/src/pipeline/captureRect.ts +172 -0
  100. package/src/pipeline/color.ts +66 -0
  101. package/src/pipeline/constants.ts +65 -0
  102. package/src/pipeline/downsample.ts +74 -0
  103. package/src/pipeline/index.ts +20 -0
  104. package/src/pipeline/overlay.ts +46 -0
  105. package/src/pipeline/saturation.ts +65 -0
  106. package/src/pipeline/types.ts +37 -0
  107. package/src/pipeline/units.ts +32 -0
  108. package/src/presets.ts +103 -0
  109. package/src/types.ts +132 -0
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Android RenderEffect calibration (plan §5.2).
5
+ *
6
+ * INTERNAL MODULE. The single place the HWUI sigma<->radius relation lives on
7
+ * the TypeScript side; the Kotlin `AndroidBlurCalibration` must mirror it
8
+ * exactly. Verified on a Pixel 6a (API 36) in M0: radiusForSigma(10) exactly
9
+ * equals 16.454486... (matching the on-device measurement 16.45449).
10
+ *
11
+ * Relation (HWUI): sigma = SLOPE * radiusPlatform + INTERCEPT
12
+ * Inverse: radiusPlatform = (sigma - INTERCEPT) / SLOPE
13
+ *
14
+ * When sigmaSnapshot <= INTERCEPT (0.5) the inverse is non-positive, so we
15
+ * clamp to a no-blur passthrough rather than feeding a bogus radius to
16
+ * RenderEffect.createBlurEffect (plan §5.2 "no-op blur" for very small sigma).
17
+ */
18
+
19
+ import { ANDROID_MIN_BLUR_SIGMA, HWUI_SIGMA_INTERCEPT, HWUI_SIGMA_SLOPE } from "./constants.js";
20
+ /**
21
+ * Forward relation, exposed for tests/verification: the sigma that HWUI
22
+ * produces for a given platform radius.
23
+ */
24
+ export function sigmaForRadius(radiusPlatform) {
25
+ return HWUI_SIGMA_SLOPE * radiusPlatform + HWUI_SIGMA_INTERCEPT;
26
+ }
27
+
28
+ /**
29
+ * Inverse relation: the platform radius required to achieve a target
30
+ * snapshot-domain sigma. Below the intercept this returns a no-blur result.
31
+ * Monotonically non-decreasing in sigmaSnapshot.
32
+ */
33
+ export function radiusForSigma(sigmaSnapshot) {
34
+ if (!Number.isFinite(sigmaSnapshot) || sigmaSnapshot <= ANDROID_MIN_BLUR_SIGMA) {
35
+ return {
36
+ noBlur: true,
37
+ radiusPlatform: 0
38
+ };
39
+ }
40
+ return {
41
+ noBlur: false,
42
+ radiusPlatform: (sigmaSnapshot - HWUI_SIGMA_INTERCEPT) / HWUI_SIGMA_SLOPE
43
+ };
44
+ }
45
+
46
+ /**
47
+ * iOS MPSImageGaussianBlur takes the snapshot-domain sigma directly (plan §5.3).
48
+ * Provided here for symmetry so both platform mappings live in the reference.
49
+ * Sigma at or below 0 means no blur.
50
+ */
51
+ export function iosSigmaForSigma(sigmaSnapshot) {
52
+ if (!Number.isFinite(sigmaSnapshot) || sigmaSnapshot <= 0) return 0;
53
+ return sigmaSnapshot;
54
+ }
55
+ //# sourceMappingURL=androidCalibration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["ANDROID_MIN_BLUR_SIGMA","HWUI_SIGMA_INTERCEPT","HWUI_SIGMA_SLOPE","sigmaForRadius","radiusPlatform","radiusForSigma","sigmaSnapshot","Number","isFinite","noBlur","iosSigmaForSigma"],"sourceRoot":"../../../src","sources":["pipeline/androidCalibration.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SACEA,sBAAsB,EACtBC,oBAAoB,EACpBC,gBAAgB,QACX,gBAAa;AAYpB;AACA;AACA;AACA;AACA,OAAO,SAASC,cAAcA,CAACC,cAAsB,EAAU;EAC7D,OAAOF,gBAAgB,GAAGE,cAAc,GAAGH,oBAAoB;AACjE;;AAEA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASI,cAAcA,CAACC,aAAqB,EAAoB;EACtE,IAAI,CAACC,MAAM,CAACC,QAAQ,CAACF,aAAa,CAAC,IAAIA,aAAa,IAAIN,sBAAsB,EAAE;IAC9E,OAAO;MAAES,MAAM,EAAE,IAAI;MAAEL,cAAc,EAAE;IAAE,CAAC;EAC5C;EACA,OAAO;IACLK,MAAM,EAAE,KAAK;IACbL,cAAc,EAAE,CAACE,aAAa,GAAGL,oBAAoB,IAAIC;EAC3D,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASQ,gBAAgBA,CAACJ,aAAqB,EAAU;EAC9D,IAAI,CAACC,MAAM,CAACC,QAAQ,CAACF,aAAa,CAAC,IAAIA,aAAa,IAAI,CAAC,EAAE,OAAO,CAAC;EACnE,OAAOA,aAAa;AACtB","ignoreList":[]}
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Capture-rect math (plan §7, §12).
5
+ *
6
+ * INTERNAL MODULE. All rectangles here are in TARGET-LOCAL DEVICE PIXELS unless
7
+ * a field/return is explicitly documented as snapshot pixels.
8
+ *
9
+ * Pipeline of this module:
10
+ * 1. supportMarginPx — Gaussian support margin = ceil(K * sigmaPx)
11
+ * 2. expandCaptureRect — expand visible rect by margin, clamp to target bounds
12
+ * 3. snapshotRectFor — map the clamped device-px rect into integer snapshot
13
+ * pixels (floor origin, ceil far edge = conservative
14
+ * cover)
15
+ * 4. cropRectFor — the sub-region of the snapshot that maps back to the
16
+ * visible rect (fractional, snapshot px)
17
+ * 5. cropRectToViewPx — inverse mapping used to prove round-tripping
18
+ *
19
+ * Rounding rule (LOCKED): in the snapshot domain the origin FLOORS and the far
20
+ * edge CEILS, so `size = ceil(farEdge/D) - floor(origin/D)`. This is the
21
+ * conservative reading of "floor for origin, ceil for size": it guarantees
22
+ * every device pixel of the clamped capture rect is covered by the snapshot
23
+ * rect, at the cost of at most one extra snapshot pixel per side. A naive
24
+ * `floor(origin), ceil(width)` would fail to cover the far edge and is NOT used.
25
+ */
26
+
27
+ import { CAPTURE_SUPPORT_K } from "./constants.js";
28
+ /** Gaussian support margin in device px (plan §7): ceil(K * sigmaPx). */
29
+ export function supportMarginPx(sigmaPx, k = CAPTURE_SUPPORT_K) {
30
+ if (!Number.isFinite(sigmaPx) || sigmaPx <= 0) return 0;
31
+ return Math.ceil(k * sigmaPx);
32
+ }
33
+
34
+ /** Intersection of two rects; empty (zero-size) rect if they do not overlap. */
35
+ export function intersectRect(a, b) {
36
+ const x0 = Math.max(a.x, b.x);
37
+ const y0 = Math.max(a.y, b.y);
38
+ const x1 = Math.min(a.x + a.width, b.x + b.width);
39
+ const y1 = Math.min(a.y + a.height, b.y + b.height);
40
+ const width = Math.max(0, x1 - x0);
41
+ const height = Math.max(0, y1 - y0);
42
+ if (width === 0 || height === 0) {
43
+ return {
44
+ x: x0,
45
+ y: y0,
46
+ width: 0,
47
+ height: 0
48
+ };
49
+ }
50
+ return {
51
+ x: x0,
52
+ y: y0,
53
+ width,
54
+ height
55
+ };
56
+ }
57
+
58
+ /**
59
+ * Expand the visible blur rect by the Gaussian support margin and clamp to the
60
+ * target bounds. Returns a float device-px rect (plan §7). The margin only
61
+ * covers content that exists inside the target; CLAMP handles the outer edge
62
+ * where the margin was cut off (plan §8).
63
+ */
64
+ export function expandCaptureRect(visibleRect, targetBounds, sigmaPx, k = CAPTURE_SUPPORT_K) {
65
+ const margin = supportMarginPx(sigmaPx, k);
66
+ const expanded = {
67
+ x: visibleRect.x - margin,
68
+ y: visibleRect.y - margin,
69
+ width: visibleRect.width + 2 * margin,
70
+ height: visibleRect.height + 2 * margin
71
+ };
72
+ return intersectRect(expanded, targetBounds);
73
+ }
74
+
75
+ /** Area of a rect in device px^2 (helper for downsample selection). */
76
+ export function rectArea(r) {
77
+ return r.width * r.height;
78
+ }
79
+
80
+ /**
81
+ * Map a clamped device-px capture rect into integer snapshot pixels for
82
+ * downsample factor D. Floor the origin, ceil the far edge (conservative
83
+ * cover). The returned rect is in SNAPSHOT PIXELS (indices), so its device-px
84
+ * footprint is `snapshotRect * D`.
85
+ */
86
+ export function snapshotRectFor(captureRectPx, downsample) {
87
+ const x = Math.floor(captureRectPx.x / downsample);
88
+ const y = Math.floor(captureRectPx.y / downsample);
89
+ const farX = Math.ceil((captureRectPx.x + captureRectPx.width) / downsample);
90
+ const farY = Math.ceil((captureRectPx.y + captureRectPx.height) / downsample);
91
+ return {
92
+ x,
93
+ y,
94
+ width: Math.max(0, farX - x),
95
+ height: Math.max(0, farY - y)
96
+ };
97
+ }
98
+
99
+ /**
100
+ * The fractional crop rect, in SNAPSHOT PIXELS, that selects the visible region
101
+ * out of the (larger) snapshot rect after blur. Because the snapshot origin was
102
+ * floored, the crop origin carries a fractional remainder in [0, 1) snapshot px
103
+ * which the upsample step resolves with bilinear sampling (plan §12).
104
+ *
105
+ * cropX = visibleRect.x / D - snapshotRect.x
106
+ * cropW = visibleRect.width / D
107
+ */
108
+ export function cropRectFor(visibleRect, snapshotRect, downsample) {
109
+ return {
110
+ x: visibleRect.x / downsample - snapshotRect.x,
111
+ y: visibleRect.y / downsample - snapshotRect.y,
112
+ width: visibleRect.width / downsample,
113
+ height: visibleRect.height / downsample
114
+ };
115
+ }
116
+
117
+ /**
118
+ * Inverse of {@link cropRectFor}: map a snapshot-px crop rect back to
119
+ * target-local device px. Used to verify round-tripping (should recover the
120
+ * original visible rect within half a snapshot pixel = D/2 device px).
121
+ */
122
+ export function cropRectToViewPx(cropRect, snapshotRect, downsample) {
123
+ return {
124
+ x: (snapshotRect.x + cropRect.x) * downsample,
125
+ y: (snapshotRect.y + cropRect.y) * downsample,
126
+ width: cropRect.width * downsample,
127
+ height: cropRect.height * downsample
128
+ };
129
+ }
130
+ /**
131
+ * Assemble the full capture plan for a given visible rect, target bounds,
132
+ * sigmaPx and pre-resolved downsample factor. Downsample resolution lives in
133
+ * downsample.ts and takes `captureAreaPx` from this same computation, so the
134
+ * caller sequences: expand -> area -> resolveDownsample -> buildCapturePlan.
135
+ */
136
+ export function buildCapturePlan(visibleRect, targetBounds, sigmaPx, downsample, k = CAPTURE_SUPPORT_K) {
137
+ const marginPx = supportMarginPx(sigmaPx, k);
138
+ const captureRectPx = expandCaptureRect(visibleRect, targetBounds, sigmaPx, k);
139
+ const snapshotRect = snapshotRectFor(captureRectPx, downsample);
140
+ const cropRect = cropRectFor(visibleRect, snapshotRect, downsample);
141
+ return {
142
+ visibleRect,
143
+ marginPx,
144
+ captureRectPx,
145
+ captureAreaPx: rectArea(captureRectPx),
146
+ downsample,
147
+ snapshotRect,
148
+ cropRect
149
+ };
150
+ }
151
+ //# sourceMappingURL=captureRect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["CAPTURE_SUPPORT_K","supportMarginPx","sigmaPx","k","Number","isFinite","Math","ceil","intersectRect","a","b","x0","max","x","y0","y","x1","min","width","y1","height","expandCaptureRect","visibleRect","targetBounds","margin","expanded","rectArea","r","snapshotRectFor","captureRectPx","downsample","floor","farX","farY","cropRectFor","snapshotRect","cropRectToViewPx","cropRect","buildCapturePlan","marginPx","captureAreaPx"],"sourceRoot":"../../../src","sources":["pipeline/captureRect.ts"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Color parsing for the overlay prop.
5
+ *
6
+ * INTERNAL MODULE. Parses the subset of CSS color syntaxes React Native
7
+ * accepts for a string color prop into straight-alpha RGBA in [0,1]:
8
+ * - 'transparent'
9
+ * - #rgb / #rgba / #rrggbb / #rrggbbaa
10
+ * - rgb(r,g,b) / rgba(r,g,b,a) (r,g,b integer 0-255, a float 0-1)
11
+ *
12
+ * Any unrecognised value returns a fully-transparent color (no overlay), which
13
+ * is the documented default (plan §11). The native backends parse the same
14
+ * prop with their platform color parsers; this reference fixes the expected
15
+ * numeric result for parity fixtures.
16
+ */
17
+
18
+ export const TRANSPARENT = {
19
+ r: 0,
20
+ g: 0,
21
+ b: 0,
22
+ a: 0
23
+ };
24
+ function clamp01(v) {
25
+ return v < 0 ? 0 : v > 1 ? 1 : v;
26
+ }
27
+ function hexPair(s) {
28
+ return parseInt(s, 16) / 255;
29
+ }
30
+
31
+ /** Parse a color string into straight-alpha RGBA. Unrecognised -> transparent. */
32
+ export function parseColor(input) {
33
+ if (input == null) return TRANSPARENT;
34
+ const s = input.trim().toLowerCase();
35
+ if (s === 'transparent') return TRANSPARENT;
36
+ if (s.startsWith('#')) {
37
+ const hex = s.slice(1);
38
+ if (hex.length === 3 || hex.length === 4) {
39
+ const r = hexPair(hex[0] + hex[0]);
40
+ const g = hexPair(hex[1] + hex[1]);
41
+ const b = hexPair(hex[2] + hex[2]);
42
+ const a = hex.length === 4 ? hexPair(hex[3] + hex[3]) : 1;
43
+ return {
44
+ r,
45
+ g,
46
+ b,
47
+ a
48
+ };
49
+ }
50
+ if (hex.length === 6 || hex.length === 8) {
51
+ const r = hexPair(hex.slice(0, 2));
52
+ const g = hexPair(hex.slice(2, 4));
53
+ const b = hexPair(hex.slice(4, 6));
54
+ const a = hex.length === 8 ? hexPair(hex.slice(6, 8)) : 1;
55
+ return {
56
+ r,
57
+ g,
58
+ b,
59
+ a
60
+ };
61
+ }
62
+ return TRANSPARENT;
63
+ }
64
+ const m = s.match(/^rgba?\(([^)]+)\)$/);
65
+ if (m) {
66
+ const parts = m[1].split(',').map(p => p.trim());
67
+ if (parts.length === 3 || parts.length === 4) {
68
+ const r = clamp01(parseFloat(parts[0]) / 255);
69
+ const g = clamp01(parseFloat(parts[1]) / 255);
70
+ const b = clamp01(parseFloat(parts[2]) / 255);
71
+ const a = parts.length === 4 ? clamp01(parseFloat(parts[3])) : 1;
72
+ if ([r, g, b, a].every(v => Number.isFinite(v))) return {
73
+ r,
74
+ g,
75
+ b,
76
+ a
77
+ };
78
+ }
79
+ }
80
+ return TRANSPARENT;
81
+ }
82
+ //# sourceMappingURL=color.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["TRANSPARENT","r","g","b","a","clamp01","v","hexPair","s","parseInt","parseColor","input","trim","toLowerCase","startsWith","hex","slice","length","m","match","parts","split","map","p","parseFloat","every","Number","isFinite"],"sourceRoot":"../../../src","sources":["pipeline/color.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAIA,OAAO,MAAMA,WAAiB,GAAG;EAAEC,CAAC,EAAE,CAAC;EAAEC,CAAC,EAAE,CAAC;EAAEC,CAAC,EAAE,CAAC;EAAEC,CAAC,EAAE;AAAE,CAAC;AAE3D,SAASC,OAAOA,CAACC,CAAS,EAAU;EAClC,OAAOA,CAAC,GAAG,CAAC,GAAG,CAAC,GAAGA,CAAC,GAAG,CAAC,GAAG,CAAC,GAAGA,CAAC;AAClC;AAEA,SAASC,OAAOA,CAACC,CAAS,EAAU;EAClC,OAAOC,QAAQ,CAACD,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG;AAC9B;;AAEA;AACA,OAAO,SAASE,UAAUA,CAACC,KAAgC,EAAQ;EACjE,IAAIA,KAAK,IAAI,IAAI,EAAE,OAAOX,WAAW;EACrC,MAAMQ,CAAC,GAAGG,KAAK,CAACC,IAAI,CAAC,CAAC,CAACC,WAAW,CAAC,CAAC;EACpC,IAAIL,CAAC,KAAK,aAAa,EAAE,OAAOR,WAAW;EAE3C,IAAIQ,CAAC,CAACM,UAAU,CAAC,GAAG,CAAC,EAAE;IACrB,MAAMC,GAAG,GAAGP,CAAC,CAACQ,KAAK,CAAC,CAAC,CAAC;IACtB,IAAID,GAAG,CAACE,MAAM,KAAK,CAAC,IAAIF,GAAG,CAACE,MAAM,KAAK,CAAC,EAAE;MACxC,MAAMhB,CAAC,GAAGM,OAAO,CAACQ,GAAG,CAAC,CAAC,CAAC,GAAIA,GAAG,CAAC,CAAC,CAAE,CAAC;MACpC,MAAMb,CAAC,GAAGK,OAAO,CAACQ,GAAG,CAAC,CAAC,CAAC,GAAIA,GAAG,CAAC,CAAC,CAAE,CAAC;MACpC,MAAMZ,CAAC,GAAGI,OAAO,CAACQ,GAAG,CAAC,CAAC,CAAC,GAAIA,GAAG,CAAC,CAAC,CAAE,CAAC;MACpC,MAAMX,CAAC,GAAGW,GAAG,CAACE,MAAM,KAAK,CAAC,GAAGV,OAAO,CAACQ,GAAG,CAAC,CAAC,CAAC,GAAIA,GAAG,CAAC,CAAC,CAAE,CAAC,GAAG,CAAC;MAC3D,OAAO;QAAEd,CAAC;QAAEC,CAAC;QAAEC,CAAC;QAAEC;MAAE,CAAC;IACvB;IACA,IAAIW,GAAG,CAACE,MAAM,KAAK,CAAC,IAAIF,GAAG,CAACE,MAAM,KAAK,CAAC,EAAE;MACxC,MAAMhB,CAAC,GAAGM,OAAO,CAACQ,GAAG,CAACC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;MAClC,MAAMd,CAAC,GAAGK,OAAO,CAACQ,GAAG,CAACC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;MAClC,MAAMb,CAAC,GAAGI,OAAO,CAACQ,GAAG,CAACC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;MAClC,MAAMZ,CAAC,GAAGW,GAAG,CAACE,MAAM,KAAK,CAAC,GAAGV,OAAO,CAACQ,GAAG,CAACC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;MACzD,OAAO;QAAEf,CAAC;QAAEC,CAAC;QAAEC,CAAC;QAAEC;MAAE,CAAC;IACvB;IACA,OAAOJ,WAAW;EACpB;EAEA,MAAMkB,CAAC,GAAGV,CAAC,CAACW,KAAK,CAAC,oBAAoB,CAAC;EACvC,IAAID,CAAC,EAAE;IACL,MAAME,KAAK,GAAGF,CAAC,CAAC,CAAC,CAAC,CAAEG,KAAK,CAAC,GAAG,CAAC,CAACC,GAAG,CAAEC,CAAC,IAAKA,CAAC,CAACX,IAAI,CAAC,CAAC,CAAC;IACnD,IAAIQ,KAAK,CAACH,MAAM,KAAK,CAAC,IAAIG,KAAK,CAACH,MAAM,KAAK,CAAC,EAAE;MAC5C,MAAMhB,CAAC,GAAGI,OAAO,CAACmB,UAAU,CAACJ,KAAK,CAAC,CAAC,CAAE,CAAC,GAAG,GAAG,CAAC;MAC9C,MAAMlB,CAAC,GAAGG,OAAO,CAACmB,UAAU,CAACJ,KAAK,CAAC,CAAC,CAAE,CAAC,GAAG,GAAG,CAAC;MAC9C,MAAMjB,CAAC,GAAGE,OAAO,CAACmB,UAAU,CAACJ,KAAK,CAAC,CAAC,CAAE,CAAC,GAAG,GAAG,CAAC;MAC9C,MAAMhB,CAAC,GAAGgB,KAAK,CAACH,MAAM,KAAK,CAAC,GAAGZ,OAAO,CAACmB,UAAU,CAACJ,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC,GAAG,CAAC;MACjE,IAAI,CAACnB,CAAC,EAAEC,CAAC,EAAEC,CAAC,EAAEC,CAAC,CAAC,CAACqB,KAAK,CAAEnB,CAAC,IAAKoB,MAAM,CAACC,QAAQ,CAACrB,CAAC,CAAC,CAAC,EAAE,OAAO;QAAEL,CAAC;QAAEC,CAAC;QAAEC,CAAC;QAAEC;MAAE,CAAC;IAC1E;EACF;EAEA,OAAOJ,WAAW;AACpB","ignoreList":[]}
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Locked calibration constants for the canonical pipeline.
5
+ *
6
+ * INTERNAL MODULE. These are the single source of truth for the reference
7
+ * implementation. Native backends must mirror these exact values. Per the
8
+ * plan (§5.2, §38) constants remain provisional until the parity milestone,
9
+ * but M0 device verification already confirmed the HWUI relation on API 36.
10
+ */
11
+
12
+ /**
13
+ * HWUI RenderEffect Gaussian relation, verified exact on a Pixel 6a (API 36)
14
+ * in M0: `sigma = HWUI_SIGMA_SLOPE * radiusPlatform + HWUI_SIGMA_INTERCEPT`.
15
+ * Isolated here so a future version-aware calibration can override it in one
16
+ * place (plan §5.2). Do NOT inline these anywhere else.
17
+ */
18
+ export const HWUI_SIGMA_SLOPE = 0.57735;
19
+ export const HWUI_SIGMA_INTERCEPT = 0.5;
20
+
21
+ /**
22
+ * Below this snapshot-domain sigma the Android RenderEffect relation inverts
23
+ * to a non-positive radius, so we emit a no-blur passthrough instead. Equal to
24
+ * HWUI_SIGMA_INTERCEPT by construction (radius 0 <=> sigma 0.5).
25
+ */
26
+ export const ANDROID_MIN_BLUR_SIGMA = HWUI_SIGMA_INTERCEPT;
27
+
28
+ /** Gaussian support multiplier for capture expansion (plan §7). */
29
+ export const CAPTURE_SUPPORT_K = 3;
30
+
31
+ /**
32
+ * Auto-downsample: target lower bound on snapshot-domain sigma. Keeping
33
+ * sigmaSnapshot at or above this prevents over-downsampling that would make
34
+ * the kernel degenerate (plan §12). 1 snapshot px.
35
+ */
36
+ export const MIN_SIGMA_SNAPSHOT = 1.0;
37
+
38
+ /**
39
+ * Auto-downsample: captures with area (device px^2) below this are considered
40
+ * "small" and are never downsampled beyond 2x — aggressive reduction of a tiny
41
+ * region buys no meaningful performance while degrading quality.
42
+ * 256 * 256 device px.
43
+ */
44
+ export const SMALL_CAPTURE_AREA_PX = 256 * 256;
45
+
46
+ /**
47
+ * Auto-downsample: per-quality ceiling on the downsample factor (plan §13).
48
+ * high -> prefer 1x or 2x
49
+ * balanced -> prefer 2x or 4x
50
+ * performance -> prefer 4x or 8x
51
+ */
52
+ export const QUALITY_MAX_DOWNSAMPLE = {
53
+ high: 2,
54
+ balanced: 4,
55
+ performance: 8
56
+ };
57
+
58
+ /**
59
+ * Rec. 709 luminance coefficients (plan §10). Sum to 1.0. Used for the
60
+ * saturation matrix on both platforms.
61
+ */
62
+ export const LUMA_R = 0.2126;
63
+ export const LUMA_G = 0.7152;
64
+ export const LUMA_B = 0.0722;
65
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["HWUI_SIGMA_SLOPE","HWUI_SIGMA_INTERCEPT","ANDROID_MIN_BLUR_SIGMA","CAPTURE_SUPPORT_K","MIN_SIGMA_SNAPSHOT","SMALL_CAPTURE_AREA_PX","QUALITY_MAX_DOWNSAMPLE","high","balanced","performance","LUMA_R","LUMA_G","LUMA_B"],"sourceRoot":"../../../src","sources":["pipeline/constants.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMA,gBAAgB,GAAG,OAAO;AACvC,OAAO,MAAMC,oBAAoB,GAAG,GAAG;;AAEvC;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,sBAAsB,GAAGD,oBAAoB;;AAE1D;AACA,OAAO,MAAME,iBAAiB,GAAG,CAAC;;AAElC;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,kBAAkB,GAAG,GAAG;;AAErC;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,qBAAqB,GAAG,GAAG,GAAG,GAAG;;AAE9C;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,sBAGZ,GAAG;EACFC,IAAI,EAAE,CAAC;EACPC,QAAQ,EAAE,CAAC;EACXC,WAAW,EAAE;AACf,CAAC;;AAED;AACA;AACA;AACA;AACA,OAAO,MAAMC,MAAM,GAAG,MAAM;AAC5B,OAAO,MAAMC,MAAM,GAAG,MAAM;AAC5B,OAAO,MAAMC,MAAM,GAAG,MAAM","ignoreList":[]}
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Auto downsample selection (plan §12, §13).
5
+ *
6
+ * INTERNAL MODULE. Deterministic function of (sigmaPx, captureAreaPx, quality)
7
+ * -> D in {1,2,4,8}. Both backends must select identically for identical input.
8
+ *
9
+ * The chosen factor is the largest allowed value that satisfies ALL of three
10
+ * independent ceilings, then snapped down to a real factor in {1,2,4,8}:
11
+ *
12
+ * 1. quality ceiling — QUALITY_MAX_DOWNSAMPLE[quality]
13
+ * 2. sigma floor — keep sigmaSnapshot = sigmaPx / D >= MIN_SIGMA_SNAPSHOT,
14
+ * i.e. D <= floor(sigmaPx / MIN_SIGMA_SNAPSHOT)
15
+ * 3. area ceiling — tiny captures (< SMALL_CAPTURE_AREA_PX) never exceed 2x
16
+ *
17
+ * See docs/PIPELINE_SPEC.md for the locked decision table.
18
+ */
19
+
20
+ import { MIN_SIGMA_SNAPSHOT, QUALITY_MAX_DOWNSAMPLE, SMALL_CAPTURE_AREA_PX } from "./constants.js";
21
+ import { DOWNSAMPLE_FACTORS } from "./types.js";
22
+
23
+ /** Largest allowed factor in {8,4,2,1} that is <= cap (never below 1). */
24
+ function largestFactorAtMost(cap) {
25
+ for (const factor of DOWNSAMPLE_FACTORS) {
26
+ if (factor <= cap) return factor;
27
+ }
28
+ return 1;
29
+ }
30
+
31
+ /**
32
+ * Resolve the auto-selected downsample factor.
33
+ *
34
+ * @param sigmaPx device-pixel sigma (blurRadius * displayScale)
35
+ * @param captureAreaPx expanded capture area in device px^2
36
+ * @param quality quality tier
37
+ */
38
+ export function autoDownsample(sigmaPx, captureAreaPx, quality) {
39
+ // No blur requested -> never downsample.
40
+ if (!Number.isFinite(sigmaPx) || sigmaPx <= 0) return 1;
41
+ const maxByQuality = QUALITY_MAX_DOWNSAMPLE[quality];
42
+
43
+ // Sigma floor: keep snapshot sigma at/above the minimum useful value.
44
+ const maxBySigma = Math.floor(sigmaPx / MIN_SIGMA_SNAPSHOT);
45
+
46
+ // Area ceiling: small captures are capped at 2x.
47
+ const area = Number.isFinite(captureAreaPx) ? captureAreaPx : 0;
48
+ const maxByArea = area < SMALL_CAPTURE_AREA_PX ? 2 : 8;
49
+ const cap = Math.min(maxByQuality, maxBySigma, maxByArea);
50
+ return largestFactorAtMost(cap);
51
+ }
52
+
53
+ /**
54
+ * Resolve a public `downsample` prop to a concrete factor. An explicit numeric
55
+ * value is honoured verbatim; 'auto' delegates to {@link autoDownsample}.
56
+ */
57
+ export function resolveDownsample(prop, sigmaPx, captureAreaPx, quality) {
58
+ if (prop === 'auto') return autoDownsample(sigmaPx, captureAreaPx, quality);
59
+ return prop;
60
+ }
61
+ //# sourceMappingURL=downsample.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["MIN_SIGMA_SNAPSHOT","QUALITY_MAX_DOWNSAMPLE","SMALL_CAPTURE_AREA_PX","DOWNSAMPLE_FACTORS","largestFactorAtMost","cap","factor","autoDownsample","sigmaPx","captureAreaPx","quality","Number","isFinite","maxByQuality","maxBySigma","Math","floor","area","maxByArea","min","resolveDownsample","prop"],"sourceRoot":"../../../src","sources":["pipeline/downsample.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SACEA,kBAAkB,EAClBC,sBAAsB,EACtBC,qBAAqB,QAChB,gBAAa;AAEpB,SAASC,kBAAkB,QAAQ,YAAS;;AAE5C;AACA,SAASC,mBAAmBA,CAACC,GAAW,EAAc;EACpD,KAAK,MAAMC,MAAM,IAAIH,kBAAkB,EAAE;IACvC,IAAIG,MAAM,IAAID,GAAG,EAAE,OAAOC,MAAM;EAClC;EACA,OAAO,CAAC;AACV;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,cAAcA,CAC5BC,OAAe,EACfC,aAAqB,EACrBC,OAAoB,EACR;EACZ;EACA,IAAI,CAACC,MAAM,CAACC,QAAQ,CAACJ,OAAO,CAAC,IAAIA,OAAO,IAAI,CAAC,EAAE,OAAO,CAAC;EAEvD,MAAMK,YAAY,GAAGZ,sBAAsB,CAACS,OAAO,CAAC;;EAEpD;EACA,MAAMI,UAAU,GAAGC,IAAI,CAACC,KAAK,CAACR,OAAO,GAAGR,kBAAkB,CAAC;;EAE3D;EACA,MAAMiB,IAAI,GAAGN,MAAM,CAACC,QAAQ,CAACH,aAAa,CAAC,GAAGA,aAAa,GAAG,CAAC;EAC/D,MAAMS,SAAS,GAAGD,IAAI,GAAGf,qBAAqB,GAAG,CAAC,GAAG,CAAC;EAEtD,MAAMG,GAAG,GAAGU,IAAI,CAACI,GAAG,CAACN,YAAY,EAAEC,UAAU,EAAEI,SAAS,CAAC;EACzD,OAAOd,mBAAmB,CAACC,GAAG,CAAC;AACjC;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASe,iBAAiBA,CAC/BC,IAAoB,EACpBb,OAAe,EACfC,aAAqB,EACrBC,OAAoB,EACR;EACZ,IAAIW,IAAI,KAAK,MAAM,EAAE,OAAOd,cAAc,CAACC,OAAO,EAAEC,aAAa,EAAEC,OAAO,CAAC;EAC3E,OAAOW,IAAI;AACb","ignoreList":[]}
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Canonical pipeline reference implementation — INTERNAL barrel.
5
+ *
6
+ * This module is the executable form of docs/PIPELINE_SPEC.md: pure TypeScript,
7
+ * no react-native imports, no side effects. Native backends (Kotlin / Swift)
8
+ * must reproduce these exact rules.
9
+ *
10
+ * IMPORTANT: this barrel is intentionally NOT re-exported from src/index.ts.
11
+ * It is a spec/testing artifact, not part of the package's public API.
12
+ */
13
+
14
+ export * from "./types.js";
15
+ export * from "./constants.js";
16
+ export * from "./units.js";
17
+ export * from "./androidCalibration.js";
18
+ export * from "./downsample.js";
19
+ export * from "./captureRect.js";
20
+ export * from "./saturation.js";
21
+ export * from "./color.js";
22
+ export * from "./overlay.js";
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sourceRoot":"../../../src","sources":["pipeline/index.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,cAAc,YAAS;AACvB,cAAc,gBAAa;AAC3B,cAAc,YAAS;AACvB,cAAc,yBAAsB;AACpC,cAAc,iBAAc;AAC5B,cAAc,kBAAe;AAC7B,cAAc,iBAAc;AAC5B,cAAc,YAAS;AACvB,cAAc,cAAW","ignoreList":[]}
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Overlay source-over compositing (plan §11).
5
+ *
6
+ * INTERNAL MODULE. The overlay color (straight alpha, parsed from the prop) is
7
+ * the SOURCE; the saturated blur result is the DESTINATION. Applied AFTER
8
+ * saturation, BEFORE final clipping (plan §6 step 10). This is the only tint in
9
+ * the pipeline — no hidden platform tint is permitted.
10
+ *
11
+ * Straight-alpha (non-premultiplied) source-over, per channel:
12
+ *
13
+ * outA = srcA + dstA * (1 - srcA)
14
+ * outC = (srcC*srcA + dstC*dstA*(1 - srcA)) / outA (outA > 0)
15
+ * 0 (outA = 0)
16
+ *
17
+ * In v1 the capture surface is an opaque backdrop, so dstA = 1 and this reduces
18
+ * to the familiar linear blend outC = srcC*srcA + dstC*(1 - srcA), outA = 1.
19
+ * The general form is implemented so the reference stays correct if a future
20
+ * version captures translucent backdrops.
21
+ */
22
+
23
+ /**
24
+ * Source-over composite of a straight-alpha source over a straight-alpha
25
+ * destination. Returns straight-alpha RGBA.
26
+ */
27
+ export function sourceOver(src, dst) {
28
+ const outA = src.a + dst.a * (1 - src.a);
29
+ if (outA <= 0) return {
30
+ r: 0,
31
+ g: 0,
32
+ b: 0,
33
+ a: 0
34
+ };
35
+ const blend = (sc, dc) => (sc * src.a + dc * dst.a * (1 - src.a)) / outA;
36
+ return {
37
+ r: blend(src.r, dst.r),
38
+ g: blend(src.g, dst.g),
39
+ b: blend(src.b, dst.b),
40
+ a: outA
41
+ };
42
+ }
43
+
44
+ /**
45
+ * Convenience: composite an overlay color over an opaque (alpha=1) blurred
46
+ * pixel — the v1 case. Equivalent to sourceOver(overlay, {..dst, a:1}).
47
+ */
48
+ export function overlayOnOpaque(overlay, dst) {
49
+ return sourceOver(overlay, {
50
+ r: dst.r,
51
+ g: dst.g,
52
+ b: dst.b,
53
+ a: 1
54
+ });
55
+ }
56
+ //# sourceMappingURL=overlay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["sourceOver","src","dst","outA","a","r","g","b","blend","sc","dc","overlayOnOpaque","overlay"],"sourceRoot":"../../../src","sources":["pipeline/overlay.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAIA;AACA;AACA;AACA;AACA,OAAO,SAASA,UAAUA,CAACC,GAAS,EAAEC,GAAS,EAAQ;EACrD,MAAMC,IAAI,GAAGF,GAAG,CAACG,CAAC,GAAGF,GAAG,CAACE,CAAC,IAAI,CAAC,GAAGH,GAAG,CAACG,CAAC,CAAC;EACxC,IAAID,IAAI,IAAI,CAAC,EAAE,OAAO;IAAEE,CAAC,EAAE,CAAC;IAAEC,CAAC,EAAE,CAAC;IAAEC,CAAC,EAAE,CAAC;IAAEH,CAAC,EAAE;EAAE,CAAC;EAChD,MAAMI,KAAK,GAAGA,CAACC,EAAU,EAAEC,EAAU,KACnC,CAACD,EAAE,GAAGR,GAAG,CAACG,CAAC,GAAGM,EAAE,GAAGR,GAAG,CAACE,CAAC,IAAI,CAAC,GAAGH,GAAG,CAACG,CAAC,CAAC,IAAID,IAAI;EAChD,OAAO;IACLE,CAAC,EAAEG,KAAK,CAACP,GAAG,CAACI,CAAC,EAAEH,GAAG,CAACG,CAAC,CAAC;IACtBC,CAAC,EAAEE,KAAK,CAACP,GAAG,CAACK,CAAC,EAAEJ,GAAG,CAACI,CAAC,CAAC;IACtBC,CAAC,EAAEC,KAAK,CAACP,GAAG,CAACM,CAAC,EAAEL,GAAG,CAACK,CAAC,CAAC;IACtBH,CAAC,EAAED;EACL,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASQ,eAAeA,CAACC,OAAa,EAAEV,GAAS,EAAQ;EAC9D,OAAOF,UAAU,CAACY,OAAO,EAAE;IAAEP,CAAC,EAAEH,GAAG,CAACG,CAAC;IAAEC,CAAC,EAAEJ,GAAG,CAACI,CAAC;IAAEC,CAAC,EAAEL,GAAG,CAACK,CAAC;IAAEH,CAAC,EAAE;EAAE,CAAC,CAAC;AACpE","ignoreList":[]}
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Saturation color matrix (plan §10).
5
+ *
6
+ * INTERNAL MODULE. One canonical 4x5 color matrix (Android ColorMatrix layout:
7
+ * 4 output rows R,G,B,A; 5 columns R,G,B,A,offset) using Rec.709 luminance
8
+ * coefficients. Applied AFTER blur, BEFORE overlay (plan §6 step 9).
9
+ *
10
+ * For saturation s and luma coefficients (lr,lg,lb) with t = 1 - s:
11
+ *
12
+ * R' = (t*lr + s)*R + t*lg*G + t*lb*B
13
+ * G' = t*lr*R + (t*lg + s)*G + t*lb*B
14
+ * B' = t*lr*R + t*lg*G + (t*lb + s)*B
15
+ * A' = A
16
+ *
17
+ * s = 1 -> identity; s = 0 -> luminance grayscale; s > 1 -> more saturated.
18
+ *
19
+ * The alpha row is untouched and there is no offset column (all offsets 0).
20
+ * The matrix operates on the same channel representation the backend blurs in
21
+ * (gamma-space, straight color for the opaque v1 backdrop). It does NOT itself
22
+ * premultiply — see docs/PIPELINE_SPEC.md §Color.
23
+ */
24
+
25
+ import { LUMA_B, LUMA_G, LUMA_R } from "./constants.js";
26
+
27
+ /**
28
+ * Row-major 4x5 saturation matrix (20 numbers), matching Android's
29
+ * ColorMatrix and directly transcribable to a Metal 4x4 + bias.
30
+ */
31
+ export function saturationMatrix(s, lr = LUMA_R, lg = LUMA_G, lb = LUMA_B) {
32
+ const t = 1 - s;
33
+ return [t * lr + s, t * lg, t * lb, 0, 0, t * lr, t * lg + s, t * lb, 0, 0, t * lr, t * lg, t * lb + s, 0, 0, 0, 0, 0, 1, 0];
34
+ }
35
+
36
+ /**
37
+ * Apply a 4x5 saturation matrix to a straight-alpha RGBA tuple (channels in
38
+ * [0,1]). Returned RGB is clamped to [0,1]; alpha is passed through untouched.
39
+ * Reference for backend parity tests.
40
+ */
41
+ export function applySaturation(matrix, r, g, b, a) {
42
+ const clamp01 = v => v < 0 ? 0 : v > 1 ? 1 : v;
43
+ const m = matrix;
44
+ return {
45
+ // Indices per row-major 4x5 layout.
46
+ r: clamp01(m[0] * r + m[1] * g + m[2] * b + m[3] * a + m[4]),
47
+ g: clamp01(m[5] * r + m[6] * g + m[7] * b + m[8] * a + m[9]),
48
+ b: clamp01(m[10] * r + m[11] * g + m[12] * b + m[13] * a + m[14]),
49
+ a: m[15] * r + m[16] * g + m[17] * b + m[18] * a + m[19]
50
+ };
51
+ }
52
+ //# sourceMappingURL=saturation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["LUMA_B","LUMA_G","LUMA_R","saturationMatrix","s","lr","lg","lb","t","applySaturation","matrix","r","g","b","a","clamp01","v","m"],"sourceRoot":"../../../src","sources":["pipeline/saturation.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SAASA,MAAM,EAAEC,MAAM,EAAEC,MAAM,QAAQ,gBAAa;;AAEpD;AACA;AACA;AACA;AACA,OAAO,SAASC,gBAAgBA,CAC9BC,CAAS,EACTC,EAAU,GAAGH,MAAM,EACnBI,EAAU,GAAGL,MAAM,EACnBM,EAAU,GAAGP,MAAM,EACT;EACV,MAAMQ,CAAC,GAAG,CAAC,GAAGJ,CAAC;EACf,OAAO,CACLI,CAAC,GAAGH,EAAE,GAAGD,CAAC,EAAEI,CAAC,GAAGF,EAAE,EAAEE,CAAC,GAAGD,EAAE,EAAE,CAAC,EAAE,CAAC,EAChCC,CAAC,GAAGH,EAAE,EAAEG,CAAC,GAAGF,EAAE,GAAGF,CAAC,EAAEI,CAAC,GAAGD,EAAE,EAAE,CAAC,EAAE,CAAC,EAChCC,CAAC,GAAGH,EAAE,EAAEG,CAAC,GAAGF,EAAE,EAAEE,CAAC,GAAGD,EAAE,GAAGH,CAAC,EAAE,CAAC,EAAE,CAAC,EAChC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CACd;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASK,eAAeA,CAC7BC,MAAgB,EAChBC,CAAS,EACTC,CAAS,EACTC,CAAS,EACTC,CAAS,EACuC;EAChD,MAAMC,OAAO,GAAIC,CAAS,IAAMA,CAAC,GAAG,CAAC,GAAG,CAAC,GAAGA,CAAC,GAAG,CAAC,GAAG,CAAC,GAAGA,CAAE;EAC1D,MAAMC,CAAC,GAAGP,MAAM;EAChB,OAAO;IACL;IACAC,CAAC,EAAEI,OAAO,CAACE,CAAC,CAAC,CAAC,CAAC,GAAIN,CAAC,GAAGM,CAAC,CAAC,CAAC,CAAC,GAAIL,CAAC,GAAGK,CAAC,CAAC,CAAC,CAAC,GAAIJ,CAAC,GAAGI,CAAC,CAAC,CAAC,CAAC,GAAIH,CAAC,GAAGG,CAAC,CAAC,CAAC,CAAE,CAAC;IACjEL,CAAC,EAAEG,OAAO,CAACE,CAAC,CAAC,CAAC,CAAC,GAAIN,CAAC,GAAGM,CAAC,CAAC,CAAC,CAAC,GAAIL,CAAC,GAAGK,CAAC,CAAC,CAAC,CAAC,GAAIJ,CAAC,GAAGI,CAAC,CAAC,CAAC,CAAC,GAAIH,CAAC,GAAGG,CAAC,CAAC,CAAC,CAAE,CAAC;IACjEJ,CAAC,EAAEE,OAAO,CAACE,CAAC,CAAC,EAAE,CAAC,GAAIN,CAAC,GAAGM,CAAC,CAAC,EAAE,CAAC,GAAIL,CAAC,GAAGK,CAAC,CAAC,EAAE,CAAC,GAAIJ,CAAC,GAAGI,CAAC,CAAC,EAAE,CAAC,GAAIH,CAAC,GAAGG,CAAC,CAAC,EAAE,CAAE,CAAC;IACtEH,CAAC,EAAEG,CAAC,CAAC,EAAE,CAAC,GAAIN,CAAC,GAAGM,CAAC,CAAC,EAAE,CAAC,GAAIL,CAAC,GAAGK,CAAC,CAAC,EAAE,CAAC,GAAIJ,CAAC,GAAGI,CAAC,CAAC,EAAE,CAAC,GAAIH,CAAC,GAAGG,CAAC,CAAC,EAAE;EAC7D,CAAC;AACH","ignoreList":[]}
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Shared value types for the canonical pipeline reference implementation.
5
+ *
6
+ * INTERNAL MODULE. Nothing here is re-exported from src/index.ts. These types
7
+ * describe the language-neutral contract that both native backends implement;
8
+ * they are the executable form of docs/PIPELINE_SPEC.md.
9
+ */
10
+
11
+ /** Integer downsample factor actually used by the pipeline (never 'auto'). */
12
+
13
+ /** The four concrete downsample factors, largest first (selection order). */
14
+ export const DOWNSAMPLE_FACTORS = [8, 4, 2, 1];
15
+
16
+ /**
17
+ * Axis-aligned rectangle. Units are documented per-field by the producing
18
+ * function (device px in target-local space, or snapshot px). Origin is the
19
+ * top-left corner; width/height grow right/down.
20
+ */
21
+
22
+ /** Straight-alpha RGBA colour, all channels normalised to [0, 1]. */
23
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["DOWNSAMPLE_FACTORS"],"sourceRoot":"../../../src","sources":["pipeline/types.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;;AAIA;;AAGA;AACA,OAAO,MAAMA,kBAAyC,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;;AAErE;AACA;AACA;AACA;AACA;;AAQA","ignoreList":[]}
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Canonical unit conversions (plan §5.1).
5
+ *
6
+ * INTERNAL MODULE. Pure functions, no side effects, no react-native imports.
7
+ *
8
+ * blurRadius = Gaussian sigma in dp (public unit)
9
+ * sigmaPx = blurRadius * displayScale (device pixels)
10
+ * sigmaSnapshot= sigmaPx / D (downsampled snapshot px)
11
+ *
12
+ * The blur backend always receives a parameter derived from sigmaSnapshot.
13
+ */
14
+
15
+ /**
16
+ * Convert the public blurRadius (sigma in dp) to sigma in device pixels.
17
+ * Non-finite or negative inputs clamp to 0 (plan §28 safe clamping).
18
+ */
19
+ export function sigmaPxFromDp(blurRadiusDp, displayScale) {
20
+ if (!Number.isFinite(blurRadiusDp) || blurRadiusDp <= 0) return 0;
21
+ if (!Number.isFinite(displayScale) || displayScale <= 0) return 0;
22
+ return blurRadiusDp * displayScale;
23
+ }
24
+
25
+ /**
26
+ * Convert device-pixel sigma to snapshot-domain sigma given a downsample
27
+ * factor D. D is a positive integer in {1,2,4,8}.
28
+ */
29
+ export function sigmaSnapshotFromPx(sigmaPx, downsample) {
30
+ if (!Number.isFinite(sigmaPx) || sigmaPx <= 0) return 0;
31
+ return sigmaPx / downsample;
32
+ }
33
+ //# sourceMappingURL=units.js.map