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,177 @@
1
+ import Foundation
2
+
3
+ /// Swift mirror of the canonical pipeline reference (docs/PIPELINE_SPEC.md; src/pipeline/*.ts).
4
+ /// Pure math, no UIKit/Metal imports — the SPM fixture suite (swift-tests/) compiles this same
5
+ /// file against test/pipeline-fixtures.json, exactly like the Kotlin PipelineMath JVM tests.
6
+ /// When the spec changes, change TS + Kotlin + this file in the same commit.
7
+ public enum PipelineMath {
8
+
9
+ // MARK: - Constants (PIPELINE_SPEC §2, §3, §4, §7 — single home on the iOS side)
10
+
11
+ /// Gaussian support multiplier for capture expansion (spec §3.1).
12
+ public static let captureSupportK = 3.0
13
+ /// Auto-downsample: keep sigmaSnapshot >= this many snapshot px (spec §4).
14
+ public static let minSigmaSnapshot = 1.0
15
+ /// Auto-downsample: captures below this area (device px^2) never exceed 2x (spec §4).
16
+ public static let smallCaptureAreaPx = 256.0 * 256.0
17
+ /// Rec. 709 luma coefficients (spec §7).
18
+ public static let lumaR = 0.2126
19
+ public static let lumaG = 0.7152
20
+ public static let lumaB = 0.0722
21
+
22
+ public struct Rect: Equatable {
23
+ public var x: Double
24
+ public var y: Double
25
+ public var width: Double
26
+ public var height: Double
27
+ public init(x: Double, y: Double, width: Double, height: Double) {
28
+ self.x = x; self.y = y; self.width = width; self.height = height
29
+ }
30
+ }
31
+
32
+ public struct RGBA: Equatable {
33
+ public var r: Double
34
+ public var g: Double
35
+ public var b: Double
36
+ public var a: Double
37
+ public init(r: Double, g: Double, b: Double, a: Double) {
38
+ self.r = r; self.g = g; self.b = b; self.a = a
39
+ }
40
+ }
41
+
42
+ // MARK: - Units (spec §1)
43
+
44
+ public static func sigmaPxFromDp(_ blurRadiusDp: Double, displayScale: Double) -> Double {
45
+ guard blurRadiusDp.isFinite, blurRadiusDp > 0 else { return 0 }
46
+ guard displayScale.isFinite, displayScale > 0 else { return 0 }
47
+ return blurRadiusDp * displayScale
48
+ }
49
+
50
+ public static func sigmaSnapshotFromPx(_ sigmaPx: Double, downsample: Int) -> Double {
51
+ guard sigmaPx.isFinite, sigmaPx > 0 else { return 0 }
52
+ return sigmaPx / Double(downsample)
53
+ }
54
+
55
+ /// iOS blur parameter (spec §2): MPSImageGaussianBlur takes the snapshot sigma directly.
56
+ public static func iosSigmaForSigma(_ sigmaSnapshot: Double) -> Double {
57
+ guard sigmaSnapshot.isFinite, sigmaSnapshot > 0 else { return 0 }
58
+ return sigmaSnapshot
59
+ }
60
+
61
+ // MARK: - Downsample selection (spec §4)
62
+
63
+ public static func autoDownsample(sigmaPx: Double, captureAreaPx: Double, quality: String) -> Int {
64
+ guard sigmaPx.isFinite, sigmaPx > 0 else { return 1 }
65
+ let maxByQuality: Int
66
+ switch quality {
67
+ case "high": maxByQuality = 2
68
+ case "performance": maxByQuality = 8
69
+ default: maxByQuality = 4 // balanced
70
+ }
71
+ let maxBySigma = Int((sigmaPx / minSigmaSnapshot).rounded(.down))
72
+ let area = captureAreaPx.isFinite ? captureAreaPx : 0
73
+ let maxByArea = area < smallCaptureAreaPx ? 2 : 8
74
+ let cap = min(maxByQuality, min(maxBySigma, maxByArea))
75
+ for factor in [8, 4, 2, 1] where factor <= cap {
76
+ return factor
77
+ }
78
+ return 1
79
+ }
80
+
81
+ public static func resolveDownsample(
82
+ prop: Int, sigmaPx: Double, captureAreaPx: Double, quality: String
83
+ ) -> Int {
84
+ // 0 = the 'auto' sentinel (see ParityBlurViewNativeComponent.ts).
85
+ if prop == 1 || prop == 2 || prop == 4 || prop == 8 { return prop }
86
+ return autoDownsample(sigmaPx: sigmaPx, captureAreaPx: captureAreaPx, quality: quality)
87
+ }
88
+
89
+ // MARK: - Capture-rect math (spec §3); all rects in target-local DEVICE PX
90
+
91
+ public static func supportMarginPx(_ sigmaPx: Double, k: Double = captureSupportK) -> Double {
92
+ guard sigmaPx.isFinite, sigmaPx > 0 else { return 0 }
93
+ return (k * sigmaPx).rounded(.up)
94
+ }
95
+
96
+ public static func intersect(_ a: Rect, _ b: Rect) -> Rect {
97
+ let x0 = max(a.x, b.x)
98
+ let y0 = max(a.y, b.y)
99
+ let x1 = min(a.x + a.width, b.x + b.width)
100
+ let y1 = min(a.y + a.height, b.y + b.height)
101
+ let w = max(0, x1 - x0)
102
+ let h = max(0, y1 - y0)
103
+ if w == 0 || h == 0 { return Rect(x: x0, y: y0, width: 0, height: 0) }
104
+ return Rect(x: x0, y: y0, width: w, height: h)
105
+ }
106
+
107
+ public static func expandCaptureRect(
108
+ visible: Rect, targetBounds: Rect, sigmaPx: Double, k: Double = captureSupportK
109
+ ) -> Rect {
110
+ let m = supportMarginPx(sigmaPx, k: k)
111
+ let expanded = Rect(
112
+ x: visible.x - m, y: visible.y - m,
113
+ width: visible.width + 2 * m, height: visible.height + 2 * m
114
+ )
115
+ return intersect(expanded, targetBounds)
116
+ }
117
+
118
+ public static func rectArea(_ r: Rect) -> Double { r.width * r.height }
119
+
120
+ /// Snapshot rect in INTEGER snapshot px: origin FLOORS, far edge CEILS (locked rule, spec §3.3).
121
+ public static func snapshotRectFor(_ captureRectPx: Rect, downsample: Int) -> Rect {
122
+ let d = Double(downsample)
123
+ let x = (captureRectPx.x / d).rounded(.down)
124
+ let y = (captureRectPx.y / d).rounded(.down)
125
+ let farX = ((captureRectPx.x + captureRectPx.width) / d).rounded(.up)
126
+ let farY = ((captureRectPx.y + captureRectPx.height) / d).rounded(.up)
127
+ return Rect(x: x, y: y, width: max(0, farX - x), height: max(0, farY - y))
128
+ }
129
+
130
+ /// Fractional crop rect in SNAPSHOT PX selecting the visible region (spec §3.4).
131
+ public static func cropRectFor(visible: Rect, snapshotRect: Rect, downsample: Int) -> Rect {
132
+ let d = Double(downsample)
133
+ return Rect(
134
+ x: visible.x / d - snapshotRect.x,
135
+ y: visible.y / d - snapshotRect.y,
136
+ width: visible.width / d,
137
+ height: visible.height / d
138
+ )
139
+ }
140
+
141
+ // MARK: - Saturation matrix (spec §7): row-major 4x5, Android ColorMatrix layout
142
+
143
+ public static func saturationMatrix(
144
+ _ s: Double, lr: Double = lumaR, lg: Double = lumaG, lb: Double = lumaB
145
+ ) -> [Double] {
146
+ let t = 1 - s
147
+ return [
148
+ t * lr + s, t * lg, t * lb, 0, 0,
149
+ t * lr, t * lg + s, t * lb, 0, 0,
150
+ t * lr, t * lg, t * lb + s, 0, 0,
151
+ 0, 0, 0, 1, 0,
152
+ ]
153
+ }
154
+
155
+ public static func applySaturation(
156
+ matrix m: [Double], r: Double, g: Double, b: Double, a: Double
157
+ ) -> RGBA {
158
+ func c01(_ v: Double) -> Double { v < 0 ? 0 : (v > 1 ? 1 : v) }
159
+ return RGBA(
160
+ r: c01(m[0] * r + m[1] * g + m[2] * b + m[3] * a + m[4]),
161
+ g: c01(m[5] * r + m[6] * g + m[7] * b + m[8] * a + m[9]),
162
+ b: c01(m[10] * r + m[11] * g + m[12] * b + m[13] * a + m[14]),
163
+ a: m[15] * r + m[16] * g + m[17] * b + m[18] * a + m[19]
164
+ )
165
+ }
166
+
167
+ // MARK: - Overlay source-over (spec §8), straight alpha
168
+
169
+ public static func sourceOver(src: RGBA, dst: RGBA) -> RGBA {
170
+ let outA = src.a + dst.a * (1 - src.a)
171
+ guard outA > 0 else { return RGBA(r: 0, g: 0, b: 0, a: 0) }
172
+ func blend(_ sc: Double, _ dc: Double) -> Double {
173
+ (sc * src.a + dc * dst.a * (1 - src.a)) / outA
174
+ }
175
+ return RGBA(r: blend(src.r, dst.r), g: blend(src.g, dst.g), b: blend(src.b, dst.b), a: outA)
176
+ }
177
+ }
@@ -0,0 +1,128 @@
1
+ import Foundation
2
+ import UIKit
3
+
4
+ /// Exclusion registry (plan §16): every ParityBlur presentation surface registers here and is
5
+ /// hidden at the MODEL-layer level for the duration of a capture pass, restored before the
6
+ /// runloop commits — the render server never sees the hidden state (M0-verified flicker-free;
7
+ /// presentation-layer probe measured 0 leaks).
8
+ final class BlurSurfaceRegistry {
9
+ static let shared = BlurSurfaceRegistry()
10
+ private let table = NSHashTable<UIView>.weakObjects()
11
+ func register(_ v: UIView) { table.add(v) }
12
+ func unregister(_ v: UIView) { table.remove(v) }
13
+ var all: [UIView] { table.allObjects }
14
+ }
15
+
16
+ /// Reusable CPU snapshot buffer: BGRA8, premultiplied, sRGB-ENCODED bytes. The bytes are
17
+ /// uploaded to a non-sRGB (.bgra8Unorm) texture so the GPU convolves the encoded values —
18
+ /// gamma-space blur, matching Android/Skia (spec §6; M0 color-domain proof).
19
+ final class SnapshotBuffer {
20
+ private(set) var width = 0
21
+ private(set) var height = 0
22
+ private(set) var bytesPerRow = 0
23
+ private(set) var data: UnsafeMutableRawPointer?
24
+ private(set) var context: CGContext?
25
+
26
+ func ensure(width w: Int, height h: Int) -> Bool {
27
+ guard w > 0, h > 0 else { return false }
28
+ if w == width && h == height, context != nil { return true }
29
+ release()
30
+ bytesPerRow = ((w * 4 + 63) / 64) * 64
31
+ guard let mem = malloc(bytesPerRow * h) else { return false }
32
+ data = mem
33
+ let space = CGColorSpace(name: CGColorSpace.sRGB)!
34
+ let info = CGImageAlphaInfo.premultipliedFirst.rawValue | CGBitmapInfo.byteOrder32Little.rawValue
35
+ guard let ctx = CGContext(
36
+ data: mem, width: w, height: h, bitsPerComponent: 8,
37
+ bytesPerRow: bytesPerRow, space: space, bitmapInfo: info
38
+ ) else {
39
+ release()
40
+ return false
41
+ }
42
+ context = ctx
43
+ width = w
44
+ height = h
45
+ return true
46
+ }
47
+
48
+ func release() {
49
+ free(data)
50
+ data = nil
51
+ context = nil
52
+ width = 0
53
+ height = 0
54
+ bytesPerRow = 0
55
+ }
56
+
57
+ deinit { free(data) }
58
+ }
59
+
60
+ /// Capture request: everything is expressed in the SNAPSHOT-PIXEL grid derived by PipelineMath
61
+ /// (spec §3): the buffer's pixel (0,0) is snapshotRect's origin, honoring the locked
62
+ /// floor-origin/ceil-far-edge rounding.
63
+ struct SnapshotRequest {
64
+ let target: UIView
65
+ /// Integer snapshot-pixel rect (PipelineMath.snapshotRectFor output).
66
+ let snapshotRect: PipelineMath.Rect
67
+ /// Downsample factor D.
68
+ let downsample: Int
69
+ /// Device pixels per point (UIScreen scale).
70
+ let displayScale: CGFloat
71
+ }
72
+
73
+ /// Snapshot provider abstraction (plan §15.3). v1 ships the model-state provider as default;
74
+ /// a committed-state drawHierarchy provider can slot in later for structurally-separate targets
75
+ /// (M0: drawHierarchy renders the last committed frame and cannot honor model-layer exclusion).
76
+ protocol SnapshotProvider: AnyObject {
77
+ var buffer: SnapshotBuffer { get }
78
+ @discardableResult
79
+ func capture(_ req: SnapshotRequest, excluding: [UIView]) -> Bool
80
+ }
81
+
82
+ /// Default provider (M0 finding 1): CALayer.render(in:) renders MODEL state, so registry
83
+ /// exclusion works in-tree, and the mandatory clip keeps cost proportional to the capture
84
+ /// region (M0: 4.8x cheaper with the clip).
85
+ final class LayerRenderSnapshotProvider: SnapshotProvider {
86
+ let buffer = SnapshotBuffer()
87
+
88
+ @discardableResult
89
+ func capture(_ req: SnapshotRequest, excluding: [UIView]) -> Bool {
90
+ let w = Int(req.snapshotRect.width)
91
+ let h = Int(req.snapshotRect.height)
92
+ guard buffer.ensure(width: w, height: h), let ctx = buffer.context else { return false }
93
+
94
+ var saved: [(CALayer, Bool)] = []
95
+ for v in excluding {
96
+ saved.append((v.layer, v.layer.isHidden))
97
+ v.layer.isHidden = true
98
+ }
99
+ defer {
100
+ // Restore within the same runloop turn — never committed (plan §16.5/§16.6).
101
+ for (layer, wasHidden) in saved { layer.isHidden = wasHidden }
102
+ }
103
+
104
+ // Points-per-snapshot-pixel and the buffer origin in target-local points.
105
+ let d = Double(req.downsample)
106
+ let scale = Double(req.displayScale)
107
+ let pxPerPoint = scale / d // snapshot px per point
108
+ let originPtX = req.snapshotRect.x * d / scale
109
+ let originPtY = req.snapshotRect.y * d / scale
110
+ let capturePtRect = CGRect(
111
+ x: originPtX, y: originPtY,
112
+ width: Double(w) / pxPerPoint, height: Double(h) / pxPerPoint
113
+ )
114
+
115
+ ctx.saveGState()
116
+ ctx.clear(CGRect(x: 0, y: 0, width: CGFloat(w), height: CGFloat(h)))
117
+ // Flip to UIKit coordinates, then map the capture rect onto the buffer.
118
+ ctx.translateBy(x: 0, y: CGFloat(h))
119
+ ctx.scaleBy(x: 1, y: -1)
120
+ ctx.scaleBy(x: CGFloat(pxPerPoint), y: CGFloat(pxPerPoint))
121
+ ctx.translateBy(x: CGFloat(-originPtX), y: CGFloat(-originPtY))
122
+ // Mandatory clip (M0 finding 2): lets CoreGraphics cull content outside the region.
123
+ ctx.clip(to: capturePtRect)
124
+ req.target.layer.render(in: ctx)
125
+ ctx.restoreGState()
126
+ return true
127
+ }
128
+ }
@@ -0,0 +1,111 @@
1
+ import Foundation
2
+ import UIKit
3
+
4
+ /// Per-window active-view registry + capture scheduling (plan §15.2, §20, §21, §39).
5
+ ///
6
+ /// STATIC mode: capture requests coalesce to ONE capture per view on the next main-runloop
7
+ /// turn (plan §29, §45.8).
8
+ ///
9
+ /// LIVE mode (Milestone 6): ONE shared CADisplayLink per window — never one per view (plan
10
+ /// §45.6) — created when the first live view registers and torn down when the last leaves
11
+ /// (plan §24/§27: zero frame work while no live blur is visible). Each tick filters by the
12
+ /// visibility heuristic and each view's own maxFps throttle + in-flight backpressure
13
+ /// (plan §21/§22: at most 1 in-flight GPU pass per view, latest-wins, stale frames dropped).
14
+ /// The link pauses while the app is inactive (plan §21 app/scene lifecycle pause).
15
+ final class WindowBlurContext {
16
+
17
+ /// Attached ParityBlur core views in this window (weak bookkeeping).
18
+ private let activeViews = NSHashTable<ParityBlurCoreView>.weakObjects()
19
+
20
+ private var pendingCaptures = NSHashTable<ParityBlurCoreView>.weakObjects()
21
+ private var flushScheduled = false
22
+
23
+ // ------------------------------------------------------------------- live (Milestone 6)
24
+
25
+ private let liveViews = NSHashTable<ParityBlurCoreView>.weakObjects()
26
+ private var displayLink: CADisplayLink?
27
+ private var lifecycleObservers: [NSObjectProtocol] = []
28
+
29
+ /// CADisplayLink retains its target; this proxy breaks the cycle so the context (and its
30
+ /// window) can deallocate naturally.
31
+ private final class LinkProxy {
32
+ weak var context: WindowBlurContext?
33
+ init(_ c: WindowBlurContext) { context = c }
34
+ @objc func tick(_ link: CADisplayLink) { context?.liveTick(link) }
35
+ }
36
+
37
+ deinit {
38
+ displayLink?.invalidate()
39
+ for o in lifecycleObservers { NotificationCenter.default.removeObserver(o) }
40
+ }
41
+
42
+ func register(_ view: ParityBlurCoreView) {
43
+ activeViews.add(view)
44
+ }
45
+
46
+ func unregister(_ view: ParityBlurCoreView) {
47
+ activeViews.remove(view)
48
+ pendingCaptures.remove(view)
49
+ unregisterLive(view)
50
+ }
51
+
52
+ func registerLive(_ view: ParityBlurCoreView) {
53
+ liveViews.add(view)
54
+ ensureDisplayLink()
55
+ }
56
+
57
+ func unregisterLive(_ view: ParityBlurCoreView) {
58
+ liveViews.remove(view)
59
+ if liveViews.count == 0 {
60
+ displayLink?.invalidate()
61
+ displayLink = nil
62
+ ParityBlurDebug.log("scheduler-uninstall window=\(ObjectIdentifier(self))")
63
+ }
64
+ }
65
+
66
+ private func ensureDisplayLink() {
67
+ guard displayLink == nil else { return }
68
+ let link = CADisplayLink(target: LinkProxy(self), selector: #selector(LinkProxy.tick(_:)))
69
+ // Cap the link at the highest maxFps among live views; per-view throttles refine below it.
70
+ let cap = Float(liveViews.allObjects.map(\.maxFps).max() ?? 30)
71
+ link.preferredFrameRateRange = CAFrameRateRange(minimum: 10, maximum: cap, preferred: cap)
72
+ link.add(to: .main, forMode: .common)
73
+ displayLink = link
74
+ ParityBlurDebug.log("scheduler-install window=\(ObjectIdentifier(self))")
75
+
76
+ if lifecycleObservers.isEmpty {
77
+ let nc = NotificationCenter.default
78
+ lifecycleObservers.append(nc.addObserver(
79
+ forName: UIApplication.willResignActiveNotification, object: nil, queue: .main
80
+ ) { [weak self] _ in self?.displayLink?.isPaused = true })
81
+ lifecycleObservers.append(nc.addObserver(
82
+ forName: UIApplication.didBecomeActiveNotification, object: nil, queue: .main
83
+ ) { [weak self] _ in self?.displayLink?.isPaused = false })
84
+ }
85
+ }
86
+
87
+ private func liveTick(_ link: CADisplayLink) {
88
+ let now = link.timestamp
89
+ for view in liveViews.allObjects where view.isLiveEligible(now: now) {
90
+ view.performLiveTick(now: now)
91
+ }
92
+ }
93
+
94
+ /// Coalesce a static capture request to the next main-runloop turn (plan §20).
95
+ func scheduleCapture(_ view: ParityBlurCoreView) {
96
+ pendingCaptures.add(view)
97
+ guard !flushScheduled else { return }
98
+ flushScheduled = true
99
+ DispatchQueue.main.async { [weak self] in
100
+ guard let self else { return }
101
+ self.flushScheduled = false
102
+ let batch = self.pendingCaptures.allObjects
103
+ self.pendingCaptures.removeAllObjects()
104
+ for v in batch where v.window != nil {
105
+ v.performScheduledCapture()
106
+ }
107
+ }
108
+ }
109
+
110
+ var isEmpty: Bool { activeViews.count == 0 }
111
+ }
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+
3
+ import * as React from 'react';
4
+ import NativeParityBlurView, { Commands as NativeCommands } from './ParityBlurViewNativeComponent';
5
+ import { DEFAULT_BLUR_RADIUS, DEFAULT_DOWNSAMPLE, DEFAULT_MAX_FPS, DEFAULT_MODE, DEFAULT_OVERLAY_COLOR, DEFAULT_QUALITY, DEFAULT_SATURATION, MAX_MAX_FPS, MIN_MAX_FPS } from "./defaults.js";
6
+ import { BlurPresets } from "./presets.js";
7
+ import { jsx as _jsx } from "react/jsx-runtime";
8
+ const LOG_TAG = '[react-native-parity-blur]';
9
+ function clampBlurRadius(value) {
10
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
11
+ return DEFAULT_BLUR_RADIUS;
12
+ }
13
+ return value < 0 ? 0 : value;
14
+ }
15
+ function clampSaturation(value) {
16
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
17
+ return DEFAULT_SATURATION;
18
+ }
19
+ return value < 0 ? 0 : value;
20
+ }
21
+ function clampMaxFps(value) {
22
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
23
+ return DEFAULT_MAX_FPS;
24
+ }
25
+ return Math.min(MAX_MAX_FPS, Math.max(MIN_MAX_FPS, value));
26
+ }
27
+
28
+ /**
29
+ * Normalizes the public `downsample` prop (`'auto' | 1 | 2 | 4 | 8`) into the
30
+ * Int32 the native Fabric prop accepts, where `0` is a sentinel for `'auto'`.
31
+ * See the deviation note in ParityBlurViewNativeComponent.ts for why this
32
+ * isn't a string/numeric enum.
33
+ */
34
+ function normalizeDownsample(value) {
35
+ if (value === undefined || value === 'auto') {
36
+ return 0;
37
+ }
38
+ if (value === 1 || value === 2 || value === 4 || value === 8) {
39
+ return value;
40
+ }
41
+ if (__DEV__) {
42
+ console.warn(`${LOG_TAG} Invalid "downsample" value: ${JSON.stringify(value)}. Expected 'auto' | 1 | 2 | 4 | 8. Falling back to '${DEFAULT_DOWNSAMPLE}'.`);
43
+ }
44
+ return 0;
45
+ }
46
+ /**
47
+ * Public backdrop blur component (plan §28/§29/§30).
48
+ *
49
+ * Renders the native ParityBlurView host view backed by a real blur on both
50
+ * platforms: Android API 31+ real-time `RenderEffect` Gaussian blur (older
51
+ * Android renders `fallbackColor` instead — real CPU blur is intentionally
52
+ * out of scope, plan §44), iOS `MPSImageGaussianBlur`. `refresh()` is fully
53
+ * wired to a native coalesced recapture (plan §29). See
54
+ * docs/PIPELINE_SPEC.md for the canonical pipeline both backends implement
55
+ * and docs/CALIBRATION_REPORT.md for measured cross-platform parity.
56
+ */
57
+ export const BlurView = /*#__PURE__*/React.forwardRef(function BlurView(props, ref) {
58
+ const {
59
+ preset,
60
+ blurRadius,
61
+ mode = DEFAULT_MODE,
62
+ overlayColor,
63
+ saturation,
64
+ quality = DEFAULT_QUALITY,
65
+ downsample,
66
+ maxFps,
67
+ fallbackColor,
68
+ // Accepted for public-API shape stability (plan §18); not yet wired
69
+ // to native capture logic. Deferred to v1.1 — see docs/LIMITATIONS.md.
70
+ blurTarget: _blurTarget,
71
+ children,
72
+ style
73
+ } = props;
74
+
75
+ // A preset (plan §11) is pure JS sugar for a base
76
+ // { blurRadius, saturation, overlayColor } bundle — resolved here,
77
+ // client-side, with no native awareness of presets at all. Any of
78
+ // those three props passed explicitly above still wins per-prop.
79
+ const presetProps = preset ? BlurPresets[preset] : undefined;
80
+ const resolvedBlurRadius = blurRadius ?? presetProps?.blurRadius;
81
+ const resolvedSaturation = saturation ?? presetProps?.saturation;
82
+ const resolvedOverlayColor = overlayColor ?? presetProps?.overlayColor ?? DEFAULT_OVERLAY_COLOR;
83
+ const nativeRef = React.useRef(null);
84
+ React.useImperativeHandle(ref, () => ({
85
+ refresh() {
86
+ if (nativeRef.current) {
87
+ NativeCommands.refresh(nativeRef.current);
88
+ }
89
+ }
90
+ }), []);
91
+ if (__DEV__) {
92
+ if (preset !== undefined && !(preset in BlurPresets)) {
93
+ console.warn(`${LOG_TAG} Unknown "preset" value: ${JSON.stringify(preset)}. Ignoring.`);
94
+ }
95
+ if (blurRadius !== undefined && (typeof blurRadius !== 'number' || !Number.isFinite(blurRadius) || blurRadius < 0)) {
96
+ console.warn(`${LOG_TAG} "blurRadius" must be a finite number >= 0. Received: ${blurRadius}. Clamping to ${clampBlurRadius(blurRadius)}.`);
97
+ }
98
+ if (saturation !== undefined && (typeof saturation !== 'number' || !Number.isFinite(saturation) || saturation < 0)) {
99
+ console.warn(`${LOG_TAG} "saturation" must be a finite number >= 0. Received: ${saturation}. Clamping to ${clampSaturation(saturation)}.`);
100
+ }
101
+ if (maxFps !== undefined && (typeof maxFps !== 'number' || !Number.isFinite(maxFps) || maxFps < MIN_MAX_FPS || maxFps > MAX_MAX_FPS)) {
102
+ console.warn(`${LOG_TAG} "maxFps" must be a finite number between ${MIN_MAX_FPS} and ${MAX_MAX_FPS}. Received: ${maxFps}. Clamping to ${clampMaxFps(maxFps)}.`);
103
+ }
104
+ }
105
+ const safeBlurRadius = clampBlurRadius(resolvedBlurRadius);
106
+ const safeSaturation = clampSaturation(resolvedSaturation);
107
+ const safeMaxFps = clampMaxFps(maxFps);
108
+ const safeDownsample = normalizeDownsample(downsample);
109
+ return /*#__PURE__*/_jsx(NativeParityBlurView, {
110
+ ref: nativeRef,
111
+ style: style,
112
+ blurRadius: safeBlurRadius,
113
+ mode: mode,
114
+ overlayColor: resolvedOverlayColor,
115
+ saturation: safeSaturation,
116
+ quality: quality,
117
+ downsample: safeDownsample,
118
+ maxFps: safeMaxFps,
119
+ fallbackColor: fallbackColor,
120
+ children: children
121
+ });
122
+ });
123
+ export default BlurView;
124
+ //# sourceMappingURL=BlurView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["React","NativeParityBlurView","Commands","NativeCommands","DEFAULT_BLUR_RADIUS","DEFAULT_DOWNSAMPLE","DEFAULT_MAX_FPS","DEFAULT_MODE","DEFAULT_OVERLAY_COLOR","DEFAULT_QUALITY","DEFAULT_SATURATION","MAX_MAX_FPS","MIN_MAX_FPS","BlurPresets","jsx","_jsx","LOG_TAG","clampBlurRadius","value","Number","isFinite","clampSaturation","clampMaxFps","Math","min","max","normalizeDownsample","undefined","__DEV__","console","warn","JSON","stringify","BlurView","forwardRef","props","ref","preset","blurRadius","mode","overlayColor","saturation","quality","downsample","maxFps","fallbackColor","blurTarget","_blurTarget","children","style","presetProps","resolvedBlurRadius","resolvedSaturation","resolvedOverlayColor","nativeRef","useRef","useImperativeHandle","refresh","current","safeBlurRadius","safeSaturation","safeMaxFps","safeDownsample"],"sourceRoot":"../../src","sources":["BlurView.tsx"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Fabric codegen spec for the ParityBlurView native host view.
3
+ *
4
+ * Every public prop from plan §28 is plumbed through to native, and the
5
+ * `refresh()` Fabric command is wired end-to-end to a real coalesced
6
+ * recapture. Both native backends (Android Kotlin, iOS Swift) implement the
7
+ * real capture/blur/saturate/overlay pipeline defined in
8
+ * docs/PIPELINE_SPEC.md — this file only declares the codegen shape, not
9
+ * the behavior.
10
+ *
11
+ * Deviation from plan §28 (documented in the Milestone 1 report): the public
12
+ * `downsample` prop is `'auto' | 1 | 2 | 4 | 8` (see src/types.ts). Fabric
13
+ * codegen's TypeScript parser rejects a union that mixes string and numeric
14
+ * literals for one prop ("Mixed types are not supported"), and a plain
15
+ * string enum with numeric-looking values ('1', '2', ...) is also unsafe
16
+ * because the iOS/C++ generator turns each enum option into a bare
17
+ * identifier (`ParityBlurViewDownsample::1` is not valid C++). Instead this
18
+ * native prop is a plain Int32 where `0` is a sentinel for `'auto'` and
19
+ * `1 | 2 | 4 | 8` pass through unchanged. `src/BlurView.tsx` performs the
20
+ * 'auto' <-> 0 translation; native code treats 0 as "auto".
21
+ *
22
+ * Note: this file deliberately imports everything from the 'react-native'
23
+ * package root rather than deep 'react-native/Libraries/...' paths. This
24
+ * project's tsconfig opts into the `react-native-strict-api` custom
25
+ * condition, and react-native's package.json `exports` map explicitly
26
+ * blocks deep `Libraries/*` subpath imports under that condition -- so
27
+ * `codegenNativeCommands`, `codegenNativeComponent`, and the CodegenTypes
28
+ * (Double/Int32/WithDefault) must come from the root barrel, which
29
+ * re-exports all three for exactly this reason.
30
+ *
31
+ * Also note: do NOT re-export `CodegenTypes.WithDefault`/`Double`/`Int32` as
32
+ * local generic type aliases (an earlier draft of this file did, to shorten
33
+ * the qualified names). @react-native/codegen's alias resolution does plain
34
+ * textual substitution with no generic instantiation, so a chain like
35
+ * `NativeBlurMode -> WithDefault<'static'|'live','static'> -> (local
36
+ * `WithDefault<Type,Value>` alias) -> CodegenTypes.WithDefault<Type,Value>`
37
+ * loses the real type arguments and fails with "The default value in
38
+ * WithDefault must be string, number, boolean or null." Referencing
39
+ * `CodegenTypes.WithDefault<...>` directly (qualified) avoids the extra
40
+ * alias hop and keeps the real arguments intact.
41
+ */
42
+ import {
43
+ codegenNativeCommands,
44
+ codegenNativeComponent,
45
+ type CodegenTypes,
46
+ type ColorValue,
47
+ type HostComponent,
48
+ type ViewProps,
49
+ } from 'react-native';
50
+ import type * as React from 'react';
51
+
52
+ type NativeBlurMode = CodegenTypes.WithDefault<'static' | 'live', 'static'>;
53
+ type NativeBlurQuality = CodegenTypes.WithDefault<
54
+ 'high' | 'balanced' | 'performance',
55
+ 'balanced'
56
+ >;
57
+
58
+ export interface NativeProps extends ViewProps {
59
+ blurRadius?: CodegenTypes.WithDefault<CodegenTypes.Double, 0>;
60
+ mode?: NativeBlurMode;
61
+ overlayColor?: ColorValue;
62
+ saturation?: CodegenTypes.WithDefault<CodegenTypes.Double, 1>;
63
+ quality?: NativeBlurQuality;
64
+ // 0 = 'auto' sentinel; see file-level deviation note above.
65
+ downsample?: CodegenTypes.WithDefault<CodegenTypes.Int32, 0>;
66
+ maxFps?: CodegenTypes.WithDefault<CodegenTypes.Int32, 30>;
67
+ fallbackColor?: ColorValue;
68
+ }
69
+
70
+ type ComponentType = HostComponent<NativeProps>;
71
+
72
+ interface NativeCommands {
73
+ refresh: (viewRef: React.ComponentRef<ComponentType>) => void;
74
+ }
75
+
76
+ export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
77
+ supportedCommands: ['refresh'],
78
+ });
79
+
80
+ export default codegenNativeComponent<NativeProps>(
81
+ 'ParityBlurView'
82
+ ) as ComponentType;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+
3
+ export const DEFAULT_BLUR_RADIUS = 0;
4
+ export const DEFAULT_MODE = 'static';
5
+ export const DEFAULT_OVERLAY_COLOR = 'transparent';
6
+ export const DEFAULT_SATURATION = 1;
7
+ export const DEFAULT_QUALITY = 'balanced';
8
+ export const DEFAULT_DOWNSAMPLE = 'auto';
9
+ export const DEFAULT_MAX_FPS = 30;
10
+ export const MIN_MAX_FPS = 1;
11
+ export const MAX_MAX_FPS = 120;
12
+ export const ALLOWED_DOWNSAMPLE_VALUES = ['auto', 1, 2, 4, 8];
13
+ //# sourceMappingURL=defaults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["DEFAULT_BLUR_RADIUS","DEFAULT_MODE","DEFAULT_OVERLAY_COLOR","DEFAULT_SATURATION","DEFAULT_QUALITY","DEFAULT_DOWNSAMPLE","DEFAULT_MAX_FPS","MIN_MAX_FPS","MAX_MAX_FPS","ALLOWED_DOWNSAMPLE_VALUES"],"sourceRoot":"../../src","sources":["defaults.ts"],"mappings":";;AAEA,OAAO,MAAMA,mBAAmB,GAAG,CAAC;AACpC,OAAO,MAAMC,YAAsB,GAAG,QAAQ;AAC9C,OAAO,MAAMC,qBAAqB,GAAG,aAAa;AAClD,OAAO,MAAMC,kBAAkB,GAAG,CAAC;AACnC,OAAO,MAAMC,eAA4B,GAAG,UAAU;AACtD,OAAO,MAAMC,kBAAkC,GAAG,MAAM;AACxD,OAAO,MAAMC,eAAe,GAAG,EAAE;AAEjC,OAAO,MAAMC,WAAW,GAAG,CAAC;AAC5B,OAAO,MAAMC,WAAW,GAAG,GAAG;AAE9B,OAAO,MAAMC,yBAAwD,GAAG,CACtE,MAAM,EACN,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,CACF","ignoreList":[]}
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+
3
+ export { BlurView, default } from "./BlurView.js";
4
+ export { BlurPresets } from "./presets.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["BlurView","default","BlurPresets"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;AAAA,SAASA,QAAQ,EAAEC,OAAO,QAAQ,eAAY;AAQ9C,SAASC,WAAW,QAAQ,cAAW","ignoreList":[]}
@@ -0,0 +1 @@
1
+ {"type":"module"}