From 0e3cf3157066efad7aba4898388571560e5d2bc8 Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 12 Jan 2026 01:14:51 +0800 Subject: [PATCH 1/6] Update CGImageProvider --- .../View/Image/CGImageProvider.swift | 90 +++++++++++++++++++ .../View/Image/NamedImage.swift | 18 ++-- 2 files changed, 101 insertions(+), 7 deletions(-) create mode 100644 Sources/OpenSwiftUICore/View/Image/CGImageProvider.swift diff --git a/Sources/OpenSwiftUICore/View/Image/CGImageProvider.swift b/Sources/OpenSwiftUICore/View/Image/CGImageProvider.swift new file mode 100644 index 000000000..073498bb6 --- /dev/null +++ b/Sources/OpenSwiftUICore/View/Image/CGImageProvider.swift @@ -0,0 +1,90 @@ +// +// CGImageProvider.swift +// OpenSwiftUICore +// +// Audited for 6.5.4 +// Status: Blocked by Image.Resolved +// ID: BB7900A03A030BC988C08113497314C3 (SwiftUICore?) + +public import OpenCoreGraphicsShims + +@available(OpenSwiftUI_v1_0, *) +extension Image { + + /// Creates a labeled image based on a Core Graphics image instance, usable + /// as content for controls. + /// + /// - Parameters: + /// - cgImage: The base graphical image. + /// - scale: The scale factor for the image, + /// with a value like `1.0`, `2.0`, or `3.0`. + /// - orientation: The orientation of the image. The default is + /// ``Image/Orientation/up``. + /// - label: The label associated with the image. OpenSwiftUI uses the label + /// for accessibility. + public init( + _ cgImage: CGImage, + scale: CGFloat, + orientation: Image.Orientation = .up, + label: Text + ) { + self.init( + CGImageProvider( + image: cgImage, + scale: scale, + orientation: orientation, + label: label, + decorative: false + ) + ) + } + + /// Creates an unlabeled, decorative image based on a Core Graphics image + /// instance. + /// + /// OpenSwiftUI ignores this image for accessibility purposes. + /// + /// - Parameters: + /// - cgImage: The base graphical image. + /// - scale: The scale factor for the image, + /// with a value like `1.0`, `2.0`, or `3.0`. + /// - orientation: The orientation of the image. The default is + /// ``Image/Orientation/up``. + public init( + decorative cgImage: CGImage, + scale: CGFloat, + orientation: Image.Orientation = .up + ) { + self.init( + CGImageProvider( + image: cgImage, + scale: scale, + orientation: orientation, + label: nil, + decorative: true + ) + ) + } +} + +private struct CGImageProvider: ImageProvider { + var image: CGImage + var scale: CGFloat + var orientation: Image.Orientation + var label: Text? + var decorative: Bool + + func resolve(in context: ImageResolutionContext) -> Image.Resolved { + _openSwiftUIUnimplementedFailure() + } + + func resolveNamedImage(in context: ImageResolutionContext) -> Image.NamedResolved? { + nil + } +} + +extension CGImage { + package var size: CGSize { + CGSize(width: CGFloat(width), height: CGFloat(height)) + } +} diff --git a/Sources/OpenSwiftUICore/View/Image/NamedImage.swift b/Sources/OpenSwiftUICore/View/Image/NamedImage.swift index 65eda8c9a..80352ede2 100644 --- a/Sources/OpenSwiftUICore/View/Image/NamedImage.swift +++ b/Sources/OpenSwiftUICore/View/Image/NamedImage.swift @@ -9,15 +9,19 @@ public import OpenCoreGraphicsShims extension Image { - #if canImport(CoreGraphics) // FIXME - public init(decorative: CGImage, scale: CGFloat, orientation: Image.Orientation) { - _openSwiftUIUnimplementedFailure() - } - #endif + package struct Resolved { + package init( + image: GraphicsImage, + decorative: Bool, + label: AccessibilityImageLabel? = nil, + basePlatformItemImage: AnyObject? = nil, + // backgroundShape: SymbolVariants.Shape? = nil, + backgroundCornerRadius: CGFloat? = nil + ) { - // FIXME - package enum Resolved {} + } + } package enum NamedResolved {} } From a58e4577f7d028c353eeb36cc77c058eeac6165b Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 12 Jan 2026 01:45:18 +0800 Subject: [PATCH 2/6] Add ImageResizing --- .../View/Image/ImageResizing.swift | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 Sources/OpenSwiftUICore/View/Image/ImageResizing.swift diff --git a/Sources/OpenSwiftUICore/View/Image/ImageResizing.swift b/Sources/OpenSwiftUICore/View/Image/ImageResizing.swift new file mode 100644 index 000000000..7cd67ed3e --- /dev/null +++ b/Sources/OpenSwiftUICore/View/Image/ImageResizing.swift @@ -0,0 +1,114 @@ +// +// ImageResizing.swift +// OpenSwiftUICore +// +// Audited for 6.5.4 +// Status: Blocked by Image.Resolved + +@available(OpenSwiftUI_v1_0, *) +extension Image { + + /// The modes that OpenSwiftUI uses to resize an image to fit within + /// its containing view. + public enum ResizingMode : Sendable { + + /// A mode to repeat the image at its original size, as many + /// times as necessary to fill the available space. + case tile + + /// A mode to enlarge or reduce the size of an image so that it + /// fills the available space. + case stretch + } + + /// Sets the mode by which OpenSwiftUI resizes an image to fit its space. + /// - Parameters: + /// - capInsets: Inset values that indicate a portion of the image that + /// OpenSwiftUI doesn't resize. + /// - resizingMode: The mode by which OpenSwiftUI resizes the image. + /// - Returns: An image, with the new resizing behavior set. + public func resizable(capInsets: EdgeInsets = EdgeInsets(), resizingMode: Image.ResizingMode = .stretch) -> Image { + Image( + ResizableProvider( + base: self, + capInsets: capInsets, + resizingMode: resizingMode + ) + ) + } + + package struct ResizingInfo: Equatable { + package var capInsets: EdgeInsets + + package var mode: Image.ResizingMode + + package static let resizable: Image.ResizingInfo = .init(capInsets: .zero, mode: .stretch) + + package init(capInsets: EdgeInsets, mode: Image.ResizingMode) { + self.capInsets = capInsets + self.mode = mode + } + } + + package struct ResizableProvider: ImageProvider { + package var base: Image + + package var capInsets: EdgeInsets + + package var resizingMode: Image.ResizingMode + + package func resolve(in context: ImageResolutionContext) -> Image.Resolved { + var resolved = base.resolve(in: context) + // TODO: Image.Resolved + _openSwiftUIUnimplementedFailure() + return resolved + } + + package func resolveNamedImage(in context: ImageResolutionContext) -> Image.NamedResolved? { + base.resolveNamedImage(in: context) + } + } +} + +// MARK: - Image.ResizingInfo + ProtobufMessage + +extension Image.ResizingInfo: ProtobufMessage { + package func encode(to encoder: inout ProtobufEncoder) throws { + encoder.enumField(1, mode, defaultValue: .stretch) + try encoder.messageField(2, capInsets, defaultValue: .zero) + } + + package init(from decoder: inout ProtobufDecoder) throws { + var mode: Image.ResizingMode? + var capInsets: EdgeInsets = .zero + while let field = try decoder.nextField() { + let tag = field.tag + switch tag { + case 1: mode = try decoder.enumField(field) + case 2: capInsets = try decoder.messageField(field) + default: + try decoder.skipField(field) + } + } + self.init(capInsets: capInsets, mode: mode ?? .stretch) + } +} + +// MARK: - Image.ResizingMode + ProtobufEnum + +extension Image.ResizingMode: ProtobufEnum { + package var protobufValue: UInt { + switch self { + case .tile: 1 + case .stretch: 0 + } + } + + package init?(protobufValue value: UInt) { + switch value { + case 0: self = .stretch + case 1: self = .tile + default: return nil + } + } +} From 5bb368a54aba7f6ab87fa2a850371ffbb4f9865f Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 12 Jan 2026 01:59:41 +0800 Subject: [PATCH 3/6] Add ImageInterpolation --- .../View/Image/ImageInterpolation.swift | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 Sources/OpenSwiftUICore/View/Image/ImageInterpolation.swift diff --git a/Sources/OpenSwiftUICore/View/Image/ImageInterpolation.swift b/Sources/OpenSwiftUICore/View/Image/ImageInterpolation.swift new file mode 100644 index 000000000..7fe2cd3f1 --- /dev/null +++ b/Sources/OpenSwiftUICore/View/Image/ImageInterpolation.swift @@ -0,0 +1,131 @@ +// +// ImageInterpolation.swift +// OpenSwiftUICore +// +// Audited for 6.5.4 +// Status: Blocked by Image.Resolved +// ID: B65D626E77C8D6CB107EB45FECFC60F0 (SwiftUICore?) + +// MARK: - Image.Interpolation + +@available(OpenSwiftUI_v1_0, *) +extension Image { + + /// The level of quality for rendering an image that requires interpolation, + /// such as a scaled image. + /// + /// The ``Image/interpolation(_:)`` modifier specifies the interpolation + /// behavior when using the ``Image/resizable(capInsets:resizingMode:)`` + /// modifier on an ``Image``. Use this behavior to prioritize rendering + /// performance or image quality. + public enum Interpolation: Sendable { + + /// A value that indicates OpenSwiftUI doesn't interpolate image data. + case none + + /// A value that indicates a low level of interpolation quality, which may + /// speed up image rendering. + case low + + /// A value that indicates a medium level of interpolation quality, + /// between the low- and high-quality values. + case medium + + /// A value that indicates a high level of interpolation quality, which + /// may slow down image rendering. + case high + } +} + +// MARK: - InterpolatedProvider & AntialiasedProvider [WIP] + +private struct InterpolatedProvider: ImageProvider { + var base: Image + + var interpolation: Image.Interpolation + + func resolve(in context: ImageResolutionContext) -> Image.Resolved { + var resolved = base.resolve(in: context) + // TODO + return resolved + } + + func resolveNamedImage(in context: ImageResolutionContext) -> Image.NamedResolved? { + base.resolveNamedImage(in: context) + } +} + +private struct AntialiasedProvider: ImageProvider { + var base: Image + + var isAntialiased: Bool + + func resolve(in context: ImageResolutionContext) -> Image.Resolved { + var resolved = base.resolve(in: context) + // TODO + return resolved + } + + func resolveNamedImage(in context: ImageResolutionContext) -> Image.NamedResolved? { + base.resolveNamedImage(in: context) + } +} + +@available(OpenSwiftUI_v1_0, *) +extension Image { + + /// Specifies the current level of quality for rendering an + /// image that requires interpolation. + /// + /// See the article for examples + /// of using `interpolation(_:)` when scaling an ``Image``. + /// - Parameter interpolation: The quality level, expressed as a value of + /// the `Interpolation` type, that OpenSwiftUI applies when interpolating + /// an image. + /// - Returns: An image with the given interpolation value set. + public func interpolation(_ interpolation: Image.Interpolation) -> Image { + Image( + InterpolatedProvider( + base: self, + interpolation: interpolation + ) + ) + } + + /// Specifies whether OpenSwiftUI applies antialiasing when rendering + /// the image. + /// - Parameter isAntialiased: A Boolean value that specifies whether to + /// allow antialiasing. Pass `true` to allow antialising, `false` otherwise. + /// - Returns: An image with the antialiasing behavior set. + public func antialiased(_ isAntialiased: Bool) -> Image { + Image( + AntialiasedProvider( + base: self, + isAntialiased: isAntialiased + ) + ) + } +} + +// MARK: - Image.Interpolation + ProtobufEnum + +extension Image.Interpolation: ProtobufEnum { + package var protobufValue: UInt { + switch self { + case .none: 0 + case .low: 1 + case .medium: 2 + case .high: 3 + } + } + + package init?(protobufValue value: UInt) { + switch value { + case 0: self = .none + case 1: self = .low + case 2: self = .medium + case 3: self = .high + default: return nil + } + } +} From c4a62d6234ffa2d25b061483b31393207d98aa03 Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 12 Jan 2026 02:16:58 +0800 Subject: [PATCH 4/6] Add Image+PlatformRepresentation --- .../Image/Image+PlatformRepresentation.swift | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 Sources/OpenSwiftUICore/View/Image/Image+PlatformRepresentation.swift diff --git a/Sources/OpenSwiftUICore/View/Image/Image+PlatformRepresentation.swift b/Sources/OpenSwiftUICore/View/Image/Image+PlatformRepresentation.swift new file mode 100644 index 000000000..1d7dafbd6 --- /dev/null +++ b/Sources/OpenSwiftUICore/View/Image/Image+PlatformRepresentation.swift @@ -0,0 +1,79 @@ +// +// Image+PlatformRepresentation.swift +// OpenSwiftUICore +// +// Audited for 6.5.4 +// Status: Complete +// ID: 9FE4F19E3F2D6B2A0FD05C040386BBC3 (SwiftUICore) + +package import OpenAttributeGraphShims + +// MARK: - PlatformImageRepresentable + +package protocol PlatformImageRepresentable { + static func shouldMakeRepresentation(inputs: _ViewInputs) -> Bool + + static func makeRepresentation(inputs: _ViewInputs, context: Attribute, outputs: inout _ViewOutputs) + + typealias Context = PlatformImageRepresentableContext +} + +package struct PlatformImageRepresentableContext { + package var image: Image.Resolved + + package var tintColor: Color? + + package var foregroundStyle: AnyShapeStyle? +} + +extension _ViewInputs { + package var requestedImageRepresentation: (any PlatformImageRepresentable.Type)? { + get { base.requestedImageRepresentation } + set { base.requestedImageRepresentation = newValue } + } +} + +extension _GraphInputs { + private struct ImageRepresentationKey: GraphInput { + static var defaultValue: (any PlatformImageRepresentable.Type)? { nil } + } + + package var requestedImageRepresentation: (any PlatformImageRepresentable.Type)? { + get { self[ImageRepresentationKey.self] } + set { self[ImageRepresentationKey.self] = newValue } + } +} + +// MARK: - PlatformNamedImageRepresentable + +package protocol PlatformNamedImageRepresentable { + static func shouldMakeRepresentation(inputs: _ViewInputs) -> Bool + + static func makeRepresentation(inputs: _ViewInputs, context: Attribute, outputs: inout _ViewOutputs) + + typealias Context = PlatformNamedImageRepresentableContext +} + +package struct PlatformNamedImageRepresentableContext { + package var image: Image + + package var environment: EnvironmentValues +} + +extension _ViewInputs { + package var requestedNamedImageRepresentation: (any PlatformNamedImageRepresentable.Type)? { + get { base.requestedNamedImageRepresentation } + set { base.requestedNamedImageRepresentation = newValue } + } +} + +extension _GraphInputs { + private struct NamedImageRepresentationKey: GraphInput { + static var defaultValue: (any PlatformNamedImageRepresentable.Type)? { nil } + } + + package var requestedNamedImageRepresentation: (any PlatformNamedImageRepresentable.Type)? { + get { self[NamedImageRepresentationKey.self] } + set { self[NamedImageRepresentationKey.self] = newValue } + } +} From e536bf44ac1e0a0a4659ae734d2abdb8712541e6 Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 12 Jan 2026 03:30:26 +0800 Subject: [PATCH 5/6] Fix resolveNamedImage --- Sources/OpenSwiftUICore/View/Image/Image.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/OpenSwiftUICore/View/Image/Image.swift b/Sources/OpenSwiftUICore/View/Image/Image.swift index e3ecb2e80..67da9cb08 100644 --- a/Sources/OpenSwiftUICore/View/Image/Image.swift +++ b/Sources/OpenSwiftUICore/View/Image/Image.swift @@ -74,7 +74,7 @@ public struct Image: Equatable, Sendable { } package func resolveNamedImage(in context: ImageResolutionContext) -> Image.NamedResolved? { - provider.resolve(in: context) + provider.resolveNamedImage(in: context) } public static func == (lhs: Image, rhs: Image) -> Bool { From 0a35069046f65e60435179f3e853748054d4bb57 Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 12 Jan 2026 04:36:04 +0800 Subject: [PATCH 6/6] Fix CGImage missing issue --- Package.resolved | 2 +- Sources/OpenSwiftUICore/View/Image/CGImageProvider.swift | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Package.resolved b/Package.resolved index d0eec9faf..dd05a7f06 100644 --- a/Package.resolved +++ b/Package.resolved @@ -25,7 +25,7 @@ "location" : "https://github.com/OpenSwiftUIProject/OpenCoreGraphics", "state" : { "branch" : "main", - "revision" : "735ce20fa83d9e7f73dff980b3812a62a488d8d7" + "revision" : "02ae0d558852d18219aab3d364fba7ba030f9e91" } }, { diff --git a/Sources/OpenSwiftUICore/View/Image/CGImageProvider.swift b/Sources/OpenSwiftUICore/View/Image/CGImageProvider.swift index 073498bb6..bf3df3cb5 100644 --- a/Sources/OpenSwiftUICore/View/Image/CGImageProvider.swift +++ b/Sources/OpenSwiftUICore/View/Image/CGImageProvider.swift @@ -85,6 +85,10 @@ private struct CGImageProvider: ImageProvider { extension CGImage { package var size: CGSize { + #if canImport(CoreGraphics) CGSize(width: CGFloat(width), height: CGFloat(height)) + #else + _openSwiftUIPlatformUnimplementedFailure() + #endif } }