Skip to content

Commit 9fe0278

Browse files
authored
Merge pull request #16 from TimOliver/efficient-gradient
Increase the efficiency of the gradient renderer
2 parents 6e6c941 + 53c5881 commit 9fe0278

4 files changed

Lines changed: 34 additions & 32 deletions

File tree

BlurUIKit/Internal/GradientImageRenderer.swift

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -81,31 +81,33 @@ import UIKit
8181
// Clamp startLocation to valid range
8282
let clampedStartLocation = min(max(startLocation, 0.0), 1.0)
8383

84-
// Calculate gradient values for each pixel
85-
for i in 0..<length {
86-
// Determine normalized position along the gradient (0.0 to 1.0)
87-
let normalizedPosition = length > 1 ? CGFloat(i) / CGFloat(length - 1) : 0.0
88-
89-
// Calculate alpha with starting inset
90-
let alpha: CGFloat = {
91-
// Adjust for starting inset - pixels before startLocation are fully opaque/transparent
92-
let adjustedPosition: CGFloat
93-
if clampedStartLocation >= 1.0 {
94-
adjustedPosition = 0.0
95-
} else if normalizedPosition <= clampedStartLocation {
96-
adjustedPosition = 0.0
97-
} else {
98-
adjustedPosition = (normalizedPosition - clampedStartLocation) / (1.0 - clampedStartLocation)
99-
}
100-
101-
// Apply easing for smooth transition, or use linear interpolation
102-
let finalPosition = smooth ? easeInOutSine(adjustedPosition) : adjustedPosition
103-
104-
// Apply direction
105-
return reversed ? finalPosition : 1.0 - finalPosition
106-
}()
107-
108-
// Write gray (0) and alpha values
84+
// Precompute reciprocals used in the gradient calculation
85+
let lengthReciprocal: CGFloat = length > 1 ? 1.0 / CGFloat(length - 1) : 0.0
86+
let gradientRangeReciprocal: CGFloat = clampedStartLocation < 1.0 ? 1.0 / (1.0 - clampedStartLocation) : 0.0
87+
88+
// Calculate the pixel boundary where the gradient transition starts.
89+
// Pixels before this boundary all share the same constant alpha value.
90+
let gradientStartPixel: Int = {
91+
guard length > 1, clampedStartLocation > 0.0 else { return 0 }
92+
return min(Int(clampedStartLocation * CGFloat(length - 1)) + 1, length)
93+
}()
94+
95+
// Bulk-fill the constant region before the gradient (no per-pixel math needed)
96+
if gradientStartPixel > 0 {
97+
let constantAlpha: UInt8 = reversed ? 0 : 255
98+
for i in stride(from: 0, to: gradientStartPixel * bytesPerPixel, by: bytesPerPixel) {
99+
pixels[i] = 0
100+
pixels[i + 1] = constantAlpha
101+
}
102+
}
103+
104+
// Generate the gradient transition for the remaining pixels
105+
for i in gradientStartPixel..<length {
106+
let normalizedPosition = CGFloat(i) * lengthReciprocal
107+
let adjustedPosition = (normalizedPosition - clampedStartLocation) * gradientRangeReciprocal
108+
let eased = smooth ? easeInOutSine(adjustedPosition) : adjustedPosition
109+
let alpha = reversed ? eased : 1.0 - eased
110+
109111
let pixelIndex = i * bytesPerPixel
110112
pixels[pixelIndex] = 0
111113
pixels[pixelIndex + 1] = UInt8(min(max(alpha * 255.0, 0.0), 255.0))

BlurUIKit/SwiftUI/VariableBlur.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public struct VariableBlur: UIViewRepresentable {
5555
private var blurStartingInset: GradientSizing?
5656
private var dimmingTintColor: UIColor? = .systemBackground
5757
private var dimmingAlpha: DimmingAlpha? = .interfaceStyle(lightModeAlpha: 0.5, darkModeAlpha: 0.25)
58-
private var dimmingOvershoot: GradientSizing? = .relative(fraction: 0.25)
58+
private var dimmingOvershoot: GradientSizing? = .relative(fraction: 1.25)
5959
private var dimmingStartingInset: GradientSizing?
6060

6161
// MARK: - Initializer
@@ -138,7 +138,7 @@ public struct VariableBlur: UIViewRepresentable {
138138
///
139139
/// - Parameter overshoot: The overshoot distance, expressed as either an absolute point
140140
/// value or a fraction of the view's size. Pass `nil` to confine the dimming gradient
141-
/// to the view's bounds. Defaults to `.relative(fraction: 0.25)`.
141+
/// to the view's bounds. Defaults to `.relative(fraction: 1.25)`.
142142
/// - Returns: A modified `VariableBlur` with the updated dimming overshoot.
143143
public func dimmingOvershoot(_ overshoot: GradientSizing?) -> VariableBlur {
144144
var copy = self

BlurUIKit/UIKit/VariableBlurView.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public class VariableBlurView: UIView {
4141
public enum GradientSizing {
4242
// The amount of on-screen UI points starting from the origin, as a static value.
4343
case absolute(position: CGFloat)
44-
// A value between 0.0 and 1.0 relative to the size of the view. A value of 0.5 would be half the size of this view.
44+
// A relative fraction of this view's size along the gradient direction. (0.0 = 0%, 1.5 = 150%)
4545
case relative(fraction: CGFloat)
4646
}
4747

@@ -83,7 +83,7 @@ public class VariableBlurView: UIView {
8383
}
8484

8585
/// An optional overshoot value to allow the colored gradient to extend outside the blur view's bounds
86-
public var dimmingOvershoot: GradientSizing? = .relative(fraction: 0.25) {
86+
public var dimmingOvershoot: GradientSizing? = .relative(fraction: 1.25) {
8787
didSet { resetDimmingImage() }
8888
}
8989

@@ -372,7 +372,7 @@ extension VariableBlurView {
372372
case .absolute(position: let position):
373373
return (value + position).rounded(.up)
374374
case .relative(fraction: let fraction):
375-
return (value + (value * fraction)).rounded(.up)
375+
return (value * fraction).rounded(.up)
376376
}
377377
}
378378
}

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ let blurView = VariableBlurView()
3535
blurView.dimmingTintColor = .red
3636

3737
// The tint color can 'overshoot' the blur view to add more gradual transition
38-
blurView.dimmingOvershoot = .relative(fraction: 0.25)
38+
blurView.dimmingOvershoot = .relative(fraction: 1.25)
3939

4040
```
4141

@@ -46,7 +46,7 @@ blurView.dimmingOvershoot = .relative(fraction: 0.25)
4646
// Use the VariableBlur view with chainable modifiers
4747
VariableBlur(direction: .down)
4848
.dimmingTintColor(.red)
49-
.dimmingOvershoot(.relative(fraction: 0.25))
49+
.dimmingOvershoot(.relative(fraction: 1.25))
5050

5151
```
5252

0 commit comments

Comments
 (0)