Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,11 @@ import Testing
.snapshots(record: .never, diffTool: diffTool)
)
struct MatchedGeometryEffectUITests {
// FIXME: Check SharedFrame impl or DL
@Test(.disabled("MatchedGeometryEffect effect does not match the animation"))
@Test
func matchedGeometryEffect() {
struct ContentView: AnimationTestView {
nonisolated static var model: AnimationTestModel {
AnimationTestModel(duration: 3, count: 6)
AnimationTestModel(duration: 2, count: 4)
}

var body: some View {
Expand All @@ -26,25 +25,27 @@ struct MatchedGeometryEffectUITests {
}
openSwiftUIAssertAnimationSnapshot(
of: ContentView(),
precision: 0.8, // FIXME: general animation snapshot issue
size: CGSize(width: 300, height: 150)
)
}

// FIXME: Check SharedFrame impl or DL
@Test(.disabled("MatchedGeometryEffect effect does not match the animation"))
@Test
func matchedGeometryEffectWithClipShape() {
struct ContentView: AnimationTestView {
nonisolated static var model: AnimationTestModel {
AnimationTestModel(duration: 3, count: 6)
AnimationTestModel(duration: 2, count: 4)
}

var body: some View {
MatchedGeometryEffectClipShapeModifierExample()
}
}
openSwiftUIAssertAnimationSnapshot(
of: ContentView(),
size: CGSize(width: 300, height: 150)
)
withKnownIssue("clipShape rect bug") {
openSwiftUIAssertAnimationSnapshot(
of: ContentView(),
size: CGSize(width: 300, height: 150)
)
}
}
}
4 changes: 4 additions & 0 deletions Sources/OpenSwiftUICore/Graphic/Color/Paint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ package class AnyResolvedPaint: Equatable {
package func encode(to encoder: any Encoder) throws { _openSwiftUIBaseClassAbstractMethod() }
package func encode(to encoder: inout ProtobufEncoder) throws { _openSwiftUIBaseClassAbstractMethod() }
package static func == (lhs: AnyResolvedPaint, rhs: AnyResolvedPaint) -> Bool { lhs.isEqual(to: rhs) }

final func `as`<P>(type: P.Type) -> P? where P: ResolvedPaint {
(self as? _AnyResolvedPaint<P>).map { $0.paint }
}
}

// MARK: - _AnyResolvedPaint
Expand Down
32 changes: 31 additions & 1 deletion Sources/OpenSwiftUICore/Shape/Path.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,22 @@ public struct Path: Equatable, LosslessStringConvertible, @unchecked Sendable {
data = PathData(rbPath: path)
}

deinit {
switch kind {
#if canImport(CoreGraphics) || !OPENSWIFTUI_CF_CGTYPES
case .cgPath:
data.cgPath.release()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Deep Code Review Agent🐛

The .cgPath case now releases an unmanaged value that PathBox.init(_:) stores with passUnretained, so Path does not own the reference being released here. Any Path initialized from a caller-owned CGPath can over-release that object when the box is deallocated, which risks crashes or memory corruption.

Severity: high


🤖 Was this useful? React with 👍 or 👎

#endif
case .rbPath:
data.rbPath.release()
case .buffer:
withUnsafeMutablePointer(to: &data) { pointer in
let storage = unsafeBitCast(pointer, to: ORBPath.Storage.self)
storage.destroy()
}
}
}

private func prepareBuffer() {
let path: ORBPath
switch kind {
Expand Down Expand Up @@ -834,7 +850,21 @@ extension Path {
}

public func applying(_ transform: CGAffineTransform) -> Path {
_openSwiftUIUnimplementedFailure()
guard !transform.isIdentity else {
return self
}
switch storage {
case .empty:
return self
case let .rect(rect) where transform.isRectilinear:
return Path(rect.applying(transform))
case let .ellipse(rect) where transform.isRectilinear:
return Path(ellipseIn: rect.applying(transform))
case let .roundedRect(fixedRoundedRect) where transform.isRectilinear:
return Path(storage: .roundedRect(fixedRoundedRect.applying(transform)))
default:
_openSwiftUIUnimplementedFailure()

@augmentcode augmentcode Bot Aug 9, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Path.applying(_:) accepts arbitrary affine transforms, but every non-rectilinear transform—including a rotation or shear, and any buffer-backed path—reaches _openSwiftUIUnimplementedFailure(). This leaves legitimate rotated/sheared matched-geometry paths unable to render and terminates the process instead.

Severity: medium

Fix This in Augment

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

}
}

public func offsetBy(
Expand Down
63 changes: 61 additions & 2 deletions Sources/OpenSwiftUICore/Shape/ShapeLayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,48 @@ private struct ShapeLayerAsyncHelper: ResolvedPaintVisitor {
var result: Bool

mutating func visitPaint<P>(_ paint: P) where P: ResolvedPaint {
_openSwiftUIUnimplementedFailure()
guard let newPaint = new.pointee.paint.as(type: P.self) else {
return
}
// FIXME: PaintType
_openSwiftUIUnimplementedWarning()
let oldColor = (paint as? Color.Resolved)
?? (paint as? AnchoredResolvedPaint<Color.Resolved>)?.paint
let newColor = (newPaint as? Color.Resolved)
?? (newPaint as? AnchoredResolvedPaint<Color.Resolved>)?.paint
guard let oldColor, let newColor else {
return
}
switch (ShapeType(old.pointee.path), ShapeType(new.pointee.path)) {
case let (
.rect(_, oldRadius, oldCornerStyle),
.rect(_, newRadius, newCornerStyle)
):
switch (oldCornerStyle, newCornerStyle) {
case (.circular, .circular), (.continuous, .continuous):
break
default:
return
}
layer.pointee.update(
DisplayList.ViewUpdater.BackgroundColor.self,
from: oldColor,
to: newColor
)
layer.pointee.update(
DisplayList.ViewUpdater.CornerRadiusLayer.self,
from: oldRadius,
to: newRadius
)
layer.pointee.update(
DisplayList.ViewUpdater.ContentsScale.self,
from: old.pointee.contentsScale,
to: new.pointee.contentsScale
)
result = true
default:
return
}
}
}

Expand All @@ -257,7 +298,25 @@ private struct ShapeLayerAsyncShadowHelper: ResolvedPaintVisitor {
var result: Bool

mutating func visitPaint<P>(_ paint: P) where P: ResolvedPaint {
_openSwiftUIUnimplementedFailure()
guard let newPaint = newPaint.as(type: P.self) else {
return
}
// FIXME: PaintType
_openSwiftUIUnimplementedWarning()
let oldColor = (paint as? Color.Resolved)
?? (paint as? AnchoredResolvedPaint<Color.Resolved>)?.paint
let newColor = (newPaint as? Color.Resolved)
?? (newPaint as? AnchoredResolvedPaint<Color.Resolved>)?.paint
guard let oldColor, let newColor else {
return
}
result = _updateShadowAsync(
layer: &layer.pointee,
oldShadow: old.pointee.shadow,
newShadow: new.pointee.shadow,
oldPaintOpacity: oldColor.opacity,
newPaintOpacity: newColor.opacity
)
}
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/OpenSwiftUICore/View/Graph/ViewGraph.swift
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ package final class ViewGraph: GraphHost {
}
_ViewDebug.initialize(inputs: &inputs)
if inputs.needsGeometry {
// inputs.makeRootMatchedGeometryScope()
inputs.makeRootMatchedGeometryScope()
}
inputs.base.pushStableType(rootViewType)
$rootGeometry.mutateBody(
Expand Down
Loading