Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 26 additions & 17 deletions Sources/OpenSwiftUICore/Graphic/Color/ConstantColor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -162,34 +162,43 @@ extension Color.Resolved: CustomStringConvertible {
extension Color.Resolved : Animatable {
package static var legacyInterpolation: Bool = !isLinkedOnOrAfter(.v6)

public var animatableData: AnimatablePair<Float, AnimatablePair<Float, AnimatablePair<Float, Float>>> {
public var animatableData: AnimatablePair<Float, AnimatablePair<Float, AnimatablePair<Float, Float>>> {
get {
let values: (Float, Float, Float, Float)
if Self.legacyInterpolation {
// ResolvedGradient.Color.Space.convertIn(self)
_openSwiftUIUnimplementedFailure()
values = (linearRed, linearGreen, linearBlue, opacity)
} else {
return AnimatablePair(
linearRed.scaled(by: .unitScale),
let color = ResolvedGradient.ColorSpace.perceptual.convertIn(self)
values = (color.r, color.g, color.b, color.a)
}
return AnimatablePair(
values.0.scaled(by: .unitScale),
AnimatablePair(
values.1.scaled(by: .unitScale),
AnimatablePair(
linearGreen.scaled(by: .unitScale),
AnimatablePair(
linearBlue.scaled(by: .unitScale),
opacity.scaled(by: .unitScale)
)
values.2.scaled(by: .unitScale),
values.3.scaled(by: .unitScale)
)
)
}
)
}

set {
let values = newValue.scaled(by: .inverseUnitScale)
if Self.legacyInterpolation {
// ResolvedGradient.Color.Space.convertOut(self)
_openSwiftUIUnimplementedFailure()
linearRed = values.first
linearGreen = values.second.first
linearBlue = values.second.second.first
opacity = values.second.second.second
} else {
linearRed = newValue.first.scaled(by: .inverseUnitScale)
linearGreen = newValue.second.first.scaled(by: .inverseUnitScale)
linearBlue = newValue.second.second.first.scaled(by: .inverseUnitScale)
opacity = newValue.second.second.second.scaled(by: .inverseUnitScale)
self = ResolvedGradient.ColorSpace.perceptual.convertOut(
.init(
r: values.first,
g: values.second.first,
b: values.second.second.first,
a: values.second.second.second
)
)
}
}
}
Expand Down
91 changes: 91 additions & 0 deletions Sources/OpenSwiftUICore/Graphic/Color/Paint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,52 @@

package import Foundation

protocol Paint: ShapeStyle {
associatedtype ResolvedPaintType: ResolvedPaint

func resolvePaint(in env: EnvironmentValues) -> ResolvedPaintType
func fallbackColor(in env: EnvironmentValues) -> Color?
}

extension Paint {
public func _apply(to shape: inout _ShapeStyle_Shape) {
switch shape.operation {
case .prepareText:
shape.result = .none
case .resolveStyle(let name, let levels):
guard !levels.isEmpty else { return }
let resolvedPaint = resolvePaint(in: shape.environment)
let anyPaint: AnyResolvedPaint
if let bounds = shape.bounds {
anyPaint = _AnyResolvedPaint(
AnchoredResolvedPaint(resolvedPaint, bounds: bounds)
)
} else {
anyPaint = _AnyResolvedPaint(resolvedPaint)
}
var style = ShapeStyle.Pack.Style(.paint(anyPaint))
style.applyOpacity(shape.opacity(at: levels.lowerBound))
shape.stylePack[name, levels.lowerBound] = style
case .fallbackColor:
if let color = fallbackColor(in: shape.environment) {
shape.result = .color(color)
}
default:
break
}
}
}

extension Paint {
nonisolated public static func _makeView<S>(
view: _GraphValue<_ShapeView<S, Self>>,
inputs: _ViewInputs
) -> _ViewOutputs where S: Shape {
legacyMakeShapeView(view: view, inputs: inputs)
}
}


// MARK: - ResolvedPaint

package protocol ResolvedPaint: Equatable, Animatable, ProtobufEncodableMessage {
Expand Down Expand Up @@ -55,6 +101,7 @@ package class AnyResolvedPaint: Equatable {

final package class _AnyResolvedPaint<P>: AnyResolvedPaint where P: ResolvedPaint {
package let paint: P

package init(_ paint: P) {
self.paint = paint
}
Expand Down Expand Up @@ -103,6 +150,50 @@ final package class _AnyResolvedPaint<P>: AnyResolvedPaint where P: ResolvedPain
extension AnyResolvedPaint: @unchecked Sendable {}
extension _AnyResolvedPaint: @unchecked Sendable {}

// MARK: - AnchoredResolvedPaint

package struct AnchoredResolvedPaint<P>: ResolvedPaint where P: ResolvedPaint {
Copy link
Copy Markdown

@augmentcode augmentcode Bot Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sources/OpenSwiftUICore/Graphic/Color/Paint.swift:154: AnchoredResolvedPaint doesn’t override resolvedGradient, so wrapping a gradient paint with bounds makes AnyResolvedPaint.resolvedGradient become nil (and also prevents visitors that cast to LinearGradient._Paint from matching the underlying paint type).

Severity: high

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

var paint: P
var bounds: CGRect

package init(_ paint: P, bounds: CGRect) {
self.paint = paint
self.bounds = bounds
}

package func draw(path: Path, style: PathDrawingStyle, in context: GraphicsContext, bounds: CGRect?) {
let resolvedBounds: CGRect
if let bounds {
resolvedBounds = self.bounds.offsetBy(dx: bounds.origin.x, dy: bounds.origin.y)
} else {
resolvedBounds = self.bounds
}
paint.draw(path: path, style: style, in: context, bounds: resolvedBounds)
}

package func encode(to encoder: inout ProtobufEncoder) throws {
try paint.encodePaint(to: &encoder)
try encoder.messageField(CodableResolvedPaint.Tag.anchorRect, bounds)
}

package var animatableData: AnimatablePair<P.AnimatableData, AnimatablePair<AnimatablePair<CGFloat, CGFloat>, AnimatablePair<CGFloat, CGFloat>>> {
get { AnimatablePair(paint.animatableData, bounds.animatableData) }
set {
paint.animatableData = newValue.first
bounds.animatableData = newValue.second
}
}

package var isCALayerCompatible: Bool { paint.isCALayerCompatible }

package var isClear: Bool { paint.isClear }

package var isOpaque: Bool { paint.isOpaque }

package static var leafProtobufTag: CodableResolvedPaint.Tag? { nil }
}


// MARK: - ResolvedPaintVisitor

package protocol ResolvedPaintVisitor {
Expand Down
135 changes: 135 additions & 0 deletions Sources/OpenSwiftUICore/Graphic/Gradient/AnyGradient.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
//
// AnyGradient.swift
// OpenSwiftUICore
//
// Audited for 6.5.4
// ID: 50251084C79137239112ABA67F1FABAC
// Status: Compelete

// MARK: - AnyGradient

public struct AnyGradient: ShapeStyle, Hashable {
package var provider: AnyGradientBox

package init(box: AnyGradientBox) {
self.provider = box
}

package init<P>(provider: P) where P: GradientProvider {
self.provider = GradientBox(provider)
}

public init(_ gradient: Gradient) {
self.provider = GradientBox(gradient)
}

package func fallbackColor(in environment: EnvironmentValues) -> Color? {
provider.fallbackColor(in: environment)
}

public func hash(into hasher: inout Hasher) {
provider.hash(into: &hasher)
}

public func _apply(to shape: inout _ShapeStyle_Shape) {
provider.apply(to: &shape)
}

package func resolve(in environment: EnvironmentValues) -> ResolvedGradient {
provider.resolve(in: environment)
}

public static func == (lhs: AnyGradient, rhs: AnyGradient) -> Bool {
lhs.provider === rhs.provider ? true : lhs.provider.isEqual(to: rhs.provider)
}
}

// MARK: - AnyGradientBox

package class AnyGradientBox: AnyShapeStyleBox {
func resolve(in environment: EnvironmentValues) -> ResolvedGradient {
_openSwiftUIBaseClassAbstractMethod()
}

func fallbackColor(in environment: EnvironmentValues) -> Color? {
_openSwiftUIBaseClassAbstractMethod()
}

func hash(into: inout Hasher) {
_openSwiftUIBaseClassAbstractMethod()
}

override package func apply(to shape: inout _ShapeStyle_Shape) {
_AnyLinearGradient(
gradient: AnyGradient(box: self),
startPoint: .top,
endPoint: .bottom
)._apply(to: &shape)
}
}

// MARK: - GradientBox

private class GradientBox<P>: AnyGradientBox where P: GradientProvider {
let base: P

init(_ base: P) {
self.base = base
}

override func resolve(in environment: EnvironmentValues) -> ResolvedGradient {
base.resolve(in: environment)
}

override func fallbackColor(in environment: EnvironmentValues) -> Color? {
base.fallbackColor(in: environment)
}

override func isEqual(to other: AnyShapeStyleBox) -> Bool {
guard let other = other as? GradientBox<P> else { return false }
return base == other.base
}

override func hash(into hasher: inout Hasher) {
base.hash(into: &hasher)
}
}

// MARK: - EitherGradient

package enum EitherGradient: Hashable {
case gradient(Gradient)
case anyGradient(AnyGradient)

package func fallbackColor(in environment: EnvironmentValues) -> Color? {
switch self {
case .gradient: nil
case .anyGradient(let anyGradient):
anyGradient.fallbackColor(in: environment)
}
}

package func resolve(in environment: EnvironmentValues) -> ResolvedGradient {
switch self {
case .gradient(let gradient):
gradient.resolve(in: environment)
case .anyGradient(let anyGradient):
anyGradient.resolve(in: environment)
}
}

package var constantColor: Color? {
switch self {
case .gradient(let gradient):
if gradient.stops.count == 0 {
Color.clear
} else if gradient.stops.count == 1 {
gradient.stops[0].color
} else {
nil
}
case .anyGradient:
nil
}
}
}
Loading