-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add ResolvedPaint implementation * Add PaintTests
- Loading branch information
Showing
11 changed files
with
297 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
// | ||
// Paint.swift | ||
// OpenSwiftUICore | ||
// | ||
// Audited for iOS 18.0 | ||
// Status: Blocked by Gradient, Image and Shader | ||
|
||
package import Foundation | ||
|
||
// MARK: - ResolvedPaint | ||
|
||
package protocol ResolvedPaint: Equatable, Animatable, ProtobufEncodableMessage { | ||
func draw(path: Path, style: PathDrawingStyle, in context: GraphicsContext, bounds: CGRect?) | ||
var isClear: Bool { get } | ||
var isOpaque: Bool { get } | ||
var resolvedGradient: ResolvedGradient? { get } | ||
var isCALayerCompatible: Bool { get } | ||
static var leafProtobufTag: CodableResolvedPaint.Tag? { get } | ||
func encodePaint(to encoder: inout ProtobufEncoder) throws | ||
} | ||
|
||
// MARK: - ResolvedPaint + Default Implementations | ||
|
||
extension ResolvedPaint { | ||
package var isClear: Bool { false } | ||
package var isOpaque: Bool { false } | ||
package var resolvedGradient: ResolvedGradient? { nil } | ||
package var isCALayerCompatible: Bool { true } | ||
package func encodePaint(to encoder: inout ProtobufEncoder) throws { | ||
if let tag = Self.leafProtobufTag { | ||
try encoder.messageField(tag.rawValue, self) | ||
} else { | ||
try encode(to: &encoder) | ||
} | ||
} | ||
} | ||
|
||
// MARK: - AnyResolvedPaint | ||
|
||
package class AnyResolvedPaint: Equatable { | ||
package func draw(path: Path, style: PathDrawingStyle, in ctx: GraphicsContext, bounds: CGRect?) {} | ||
package var protobufPaint: Any? { nil } | ||
package var isClear: Bool { false } | ||
package var isOpaque: Bool { false } | ||
package var resolvedGradient: ResolvedGradient? { nil } | ||
package var isCALayerCompatible: Bool { false } | ||
package func isEqual(to other: AnyResolvedPaint) -> Bool { false } | ||
package func visit<V>(_ visitor: inout V) where V : ResolvedPaintVisitor {} | ||
package func encode(to encoder: any Encoder) throws { preconditionFailure("") } | ||
package func encode(to encoder: inout ProtobufEncoder) throws { preconditionFailure("") } | ||
package static func == (lhs: AnyResolvedPaint, rhs: AnyResolvedPaint) -> Bool { lhs.isEqual(to: rhs) } | ||
} | ||
|
||
// MARK: - _AnyResolvedPaint | ||
|
||
final package class _AnyResolvedPaint<P>: AnyResolvedPaint where P: ResolvedPaint { | ||
package let paint: P | ||
package init(_ paint: P) { | ||
self.paint = paint | ||
} | ||
|
||
override package func draw(path: Path, style: PathDrawingStyle, in ctx: GraphicsContext, bounds: CGRect?) { | ||
paint.draw(path: path, style: style, in: ctx, bounds: bounds) | ||
} | ||
|
||
override package var protobufPaint: Any? { | ||
paint | ||
} | ||
|
||
override package var isClear: Bool { | ||
paint.isClear | ||
} | ||
|
||
override package var isOpaque: Bool { | ||
paint.isOpaque | ||
} | ||
|
||
override package var resolvedGradient: ResolvedGradient? { | ||
paint.resolvedGradient | ||
} | ||
|
||
override package var isCALayerCompatible: Bool { | ||
paint.isCALayerCompatible | ||
} | ||
|
||
override package func isEqual(to other: AnyResolvedPaint) -> Bool { | ||
guard let other = other as? _AnyResolvedPaint<P> else { | ||
return false | ||
} | ||
return paint == other.paint | ||
} | ||
|
||
override package func visit<V>(_ visitor: inout V) where V : ResolvedPaintVisitor { | ||
visitor.visitPaint(paint) | ||
} | ||
|
||
override package func encode(to encoder: inout ProtobufEncoder) throws { | ||
try paint.encodePaint(to: &encoder) | ||
} | ||
} | ||
|
||
// FIXME | ||
extension AnyResolvedPaint: @unchecked Sendable {} | ||
extension _AnyResolvedPaint: @unchecked Sendable {} | ||
|
||
// MARK: - ResolvedPaintVisitor | ||
|
||
package protocol ResolvedPaintVisitor { | ||
mutating func visitPaint<P>(_ paint: P) where P: ResolvedPaint | ||
} | ||
|
||
// MARK: - CodableResolvedPaint [TODO] | ||
|
||
package struct CodableResolvedPaint: ProtobufMessage { | ||
package struct Tag: Equatable, ProtobufTag { | ||
package let rawValue: UInt | ||
|
||
package init(rawValue: UInt) { | ||
self.rawValue = rawValue | ||
} | ||
|
||
package static let color: CodableResolvedPaint.Tag = .init(rawValue: 1) | ||
package static let linearGradient: CodableResolvedPaint.Tag = .init(rawValue: 2) | ||
package static let radialGradient: CodableResolvedPaint.Tag = .init(rawValue: 3) | ||
package static let angularGradient: CodableResolvedPaint.Tag = .init(rawValue: 4) | ||
package static let ellipticalGradient: CodableResolvedPaint.Tag = .init(rawValue: 5) | ||
package static let image: CodableResolvedPaint.Tag = .init(rawValue: 6) | ||
package static let anchorRect: CodableResolvedPaint.Tag = .init(rawValue: 7) | ||
package static let shader: CodableResolvedPaint.Tag = .init(rawValue: 8) | ||
package static let meshGradient: CodableResolvedPaint.Tag = .init(rawValue: 9) | ||
} | ||
|
||
package var base: AnyResolvedPaint | ||
|
||
package init(_ paint: AnyResolvedPaint) { | ||
base = paint | ||
} | ||
|
||
package func encode(to encoder: inout ProtobufEncoder) throws { | ||
try base.encode(to: &encoder) | ||
} | ||
|
||
package init(from decoder: inout ProtobufDecoder) throws { | ||
var base: AnyResolvedPaint? | ||
while let field = try decoder.nextField() { | ||
switch field.tag { | ||
case Tag.color.rawValue: | ||
let color: Color.Resolved = try decoder.messageField(field) | ||
base = _AnyResolvedPaint(color) | ||
case Tag.linearGradient.rawValue: | ||
break // TODO | ||
case Tag.radialGradient.rawValue: | ||
break // TODO | ||
case Tag.angularGradient.rawValue: | ||
break // TODO | ||
case Tag.ellipticalGradient.rawValue: | ||
break // TODO | ||
case Tag.image.rawValue: | ||
break // TODO | ||
case Tag.anchorRect.rawValue: | ||
break // TODO | ||
case Tag.shader.rawValue: | ||
break // TODO | ||
case Tag.meshGradient.rawValue: | ||
break // TODO | ||
default: | ||
try decoder.skipField(field) | ||
} | ||
} | ||
if let base { | ||
self.init(base) | ||
} else { | ||
throw ProtobufDecoder.DecodingError.failed | ||
} | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
Sources/OpenSwiftUICore/Graphic/Gradient/ResolvedGradient.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
// | ||
// ResolvedGradient.swift | ||
// OpenSwiftUICore | ||
// | ||
// Audited for iOS 18.0 | ||
// Status: Empty | ||
|
||
package struct ResolvedGradient: Equatable { | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,5 +34,3 @@ public struct FillStyle: Equatable { | |
self.isAntialiased = antialiased | ||
} | ||
} | ||
|
||
extension FillStyle: Sendable {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -79,5 +79,3 @@ extension StrokeStyle: Animatable { | |
} | ||
} | ||
} | ||
|
||
extension StrokeStyle: Sendable {} |
2 changes: 1 addition & 1 deletion
2
Tests/OpenSwiftUICoreTests/Graphics/Color/ColorMatrixTests.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
Tests/OpenSwiftUICoreTests/Graphics/Color/ColorResolvedTests.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
// | ||
// ColorResolvedTests.swift | ||
// OpenSwiftUITests | ||
// OpenSwiftUICoreTests | ||
|
||
#if canImport(Darwin) | ||
|
||
|
94 changes: 94 additions & 0 deletions
94
Tests/OpenSwiftUICoreTests/Graphics/Color/PaintTests.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
// | ||
// PaintTests.swift | ||
// OpenSwiftUICoreTests | ||
|
||
@testable import OpenSwiftUICore | ||
import Testing | ||
import Foundation | ||
|
||
struct PaintTests { | ||
@Test | ||
func anyResolvedPaintEquality() { | ||
let color1 = Color.Resolved(red: 1, green: 0, blue: 0, opacity: 1) | ||
let color2 = Color.Resolved(red: 1, green: 0, blue: 0, opacity: 1) | ||
let color3 = Color.Resolved(red: 0, green: 1, blue: 0, opacity: 1) | ||
|
||
let paint1 = _AnyResolvedPaint(color1) | ||
let paint2 = _AnyResolvedPaint(color2) | ||
let paint3 = _AnyResolvedPaint(color3) | ||
|
||
#expect(paint1 == paint2) | ||
#expect(paint1 != paint3) | ||
} | ||
|
||
@Test | ||
func resolvedPaintProperties() { | ||
// Test with a clear color | ||
let clearColor = Color.Resolved(red: 0, green: 0, blue: 0, opacity: 0) | ||
let clearPaint = _AnyResolvedPaint(clearColor) | ||
|
||
#expect(clearPaint.isClear == true) | ||
#expect(clearPaint.isOpaque == false) | ||
#expect(clearPaint.resolvedGradient == nil) | ||
#expect(clearPaint.isCALayerCompatible == true) | ||
|
||
// Test with an opaque color | ||
let opaqueColor = Color.Resolved(red: 1, green: 1, blue: 1, opacity: 1) | ||
let opaquePaint = _AnyResolvedPaint(opaqueColor) | ||
|
||
#expect(opaquePaint.isClear == false) | ||
#expect(opaquePaint.isOpaque == true) | ||
#expect(opaquePaint.resolvedGradient == nil) | ||
#expect(opaquePaint.isCALayerCompatible == true) | ||
} | ||
|
||
@Test | ||
func codableResolvedPaintEncoding() throws { | ||
let color = Color.Resolved(red: 1, green: 0, blue: 0, opacity: 1) | ||
let paint = _AnyResolvedPaint(color) | ||
let codablePaint = CodableResolvedPaint(paint) | ||
|
||
var encoder = ProtobufEncoder() | ||
try codablePaint.encode(to: &encoder) | ||
|
||
let data = try ProtobufEncoder.encoding { encoder in | ||
try codablePaint.encode(to: &encoder) | ||
} | ||
#expect(data.hexString == "0a0a0d0000803f250000803f") | ||
} | ||
|
||
@Test | ||
func codableResolvedPaintDecoding() throws { | ||
// Create encoded data for a red color | ||
let color = Color.Resolved(red: 1, green: 0, blue: 0, opacity: 1) | ||
let paint = _AnyResolvedPaint(color) | ||
let originalCodablePaint = CodableResolvedPaint(paint) | ||
|
||
let data = try #require(Data(hexString: "0a0a0d0000803f250000803f")) | ||
var decoder = ProtobufDecoder(data) | ||
let decodedPaint = try CodableResolvedPaint(from: &decoder) | ||
|
||
#expect(originalCodablePaint.base == decodedPaint.base) | ||
} | ||
|
||
@Test | ||
func resolvedPaintVisitor() { | ||
struct TestVisitor: ResolvedPaintVisitor { | ||
var visitedColor: Color.Resolved? | ||
|
||
mutating func visitPaint<P>(_ paint: P) where P: ResolvedPaint { | ||
if let colorPaint = paint as? Color.Resolved { | ||
visitedColor = colorPaint | ||
} | ||
} | ||
} | ||
|
||
let color = Color.Resolved(red: 1, green: 0, blue: 0, opacity: 1) | ||
let paint = _AnyResolvedPaint(color) | ||
|
||
var visitor = TestVisitor() | ||
paint.visit(&visitor) | ||
|
||
#expect(visitor.visitedColor == color) | ||
} | ||
} |