Skip to content

Commit d0249df

Browse files
authored
Update ImageOrientation (#717)
1 parent 7e32912 commit d0249df

File tree

11 files changed

+1026
-247
lines changed

11 files changed

+1026
-247
lines changed

Sources/OpenSwiftUICore/View/Image/Image.swift

Lines changed: 179 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -2,100 +2,196 @@
22
// Image.swift
33
// OpenSwiftUICore
44
//
5-
// Audited for 6.0.87
6-
// Status: WIP
5+
// Audited for 6.5.4
6+
// Status: Blocked by ImageResolutionContext and View
7+
// ID: BE2D783904D422377BBEBAC3C942583C (SwiftUICore)
78

8-
#if canImport(CoreGraphics)
9-
public import CoreGraphics
10-
#endif
9+
package import OpenAttributeGraphShims
1110

11+
// MARK: - Image
12+
13+
/// A view that displays an image.
14+
///
15+
/// Use an `Image` instance when you want to add images to your OpenSwiftUI app.
16+
/// You can create images from many sources:
17+
///
18+
/// * Image files in your app's asset library or bundle. Supported types include
19+
/// PNG, JPEG, HEIC, and more.
20+
/// * Instances of platform-specific image types, like
21+
/// [UIImage](https://developer.apple.com/documentation/uikit/uiimage) and
22+
/// [NSImage](https://developer.apple.com/documentation/appkit/nsimage).
23+
/// * A bitmap stored in a Core Graphics
24+
/// [CGImage](https://developer.apple.com/documentation/coregraphics/cgimage)
25+
/// instance.
26+
/// * System graphics from the SF Symbols set.
27+
///
28+
/// The following example shows how to load an image from the app's asset
29+
/// library or bundle and scale it to fit within its container:
30+
///
31+
/// Image("Landscape_4")
32+
/// .resizable()
33+
/// .aspectRatio(contentMode: .fit)
34+
/// Text("Water wheel")
35+
///
36+
/// ![An image of a water wheel and its adjoining building, resized to fit the
37+
/// width of an iPhone display. The words Water wheel appear under this
38+
/// image.](Image-1.png)
39+
///
40+
/// You can use methods on the `Image` type as well as
41+
/// standard view modifiers to adjust the size of the image to fit your app's
42+
/// interface. Here, the `Image` type's
43+
/// ``Image/resizable(capInsets:resizingMode:)`` method scales the image to fit
44+
/// the current view. Then, the
45+
/// ``View/aspectRatio(_:contentMode:)`` view modifier adjusts
46+
/// this resizing behavior to maintain the image's original aspect ratio, rather
47+
/// than scaling the x- and y-axes independently to fill all four sides of the
48+
/// view. The article
49+
/// <doc:Fitting-Images-into-Available-Space> shows how to apply scaling,
50+
/// clipping, and tiling to `Image` instances of different sizes.
51+
///
52+
/// An `Image` is a late-binding token; the system resolves its actual value
53+
/// only when it's about to use the image in an environment.
54+
///
55+
/// ### Making images accessible
56+
///
57+
/// To use an image as a control, use one of the initializers that takes a
58+
/// `label` parameter. This allows the system's accessibility frameworks to use
59+
/// the label as the name of the control for users who use features like
60+
/// VoiceOver. For images that are only present for aesthetic reasons, use an
61+
/// initializer with the `decorative` parameter; the accessibility systems
62+
/// ignore these images.
63+
@available(OpenSwiftUI_v1_0, *)
1264
@frozen
1365
public struct Image: Equatable, Sendable {
14-
#if canImport(CoreGraphics)
15-
// FIXME
16-
public init(decorative: CGImage, scale: CGFloat, orientation: Image.Orientation) {
66+
package var provider: AnyImageProviderBox
67+
68+
package init<P>(_ provider: P) where P: ImageProvider {
69+
self.provider = ImageProviderBox(base: provider)
70+
}
71+
72+
package func resolve(in context: ImageResolutionContext) -> Image.Resolved {
73+
provider.resolve(in: context)
74+
}
75+
76+
package func resolveNamedImage(in context: ImageResolutionContext) -> Image.NamedResolved? {
77+
provider.resolve(in: context)
78+
}
79+
80+
public static func == (lhs: Image, rhs: Image) -> Bool {
81+
lhs.provider.isEqual(to: rhs.provider)
82+
}
83+
}
84+
85+
// MARK: - ImageResolutionContext [WIP]
86+
87+
package struct ImageResolutionContext {
88+
package struct Options: OptionSet {
89+
package let rawValue: UInt8
90+
91+
package init(rawValue: UInt8) {
92+
self.rawValue = rawValue
93+
}
94+
95+
package static let inferSymbolRenderingMode: ImageResolutionContext.Options = .init(rawValue: 1 << 0)
96+
97+
package static let isArchived: ImageResolutionContext.Options = .init(rawValue: 1 << 1)
98+
99+
package static let useCatalogReferences: ImageResolutionContext.Options = .init(rawValue: 1 << 2)
100+
101+
package static let animationsDisabled: ImageResolutionContext.Options = .init(rawValue: 1 << 3)
102+
103+
package static let preservesVectors: ImageResolutionContext.Options = .init(rawValue: 1 << 4)
104+
}
105+
106+
package var environment: EnvironmentValues
107+
108+
// package var symbolAnimator: RBSymbolAnimator?
109+
110+
package var textStyle: Text.Style?
111+
112+
package var transaction: OptionalAttribute<Transaction>
113+
114+
// package var symbolRenderingMode: SymbolRenderingMode?
115+
116+
package var allowedDynamicRange: Image.DynamicRange?
117+
118+
package var options: ImageResolutionContext.Options
119+
120+
package init(
121+
environment: EnvironmentValues,
122+
textStyle: Text.Style? = nil,
123+
transaction: OptionalAttribute<Transaction> = .init()
124+
) {
17125
_openSwiftUIUnimplementedFailure()
18126
}
19-
#endif
20127

21-
// FIXME
22-
package enum Resolved {}
128+
// package var effectiveAllowedDynamicRange: Image.DynamicRange? {
129+
// _openSwiftUIUnimplementedFailure()
130+
// }
131+
}
132+
133+
// MARK: - ImageProvider
134+
135+
package protocol ImageProvider: Equatable {
136+
func resolve(in context: ImageResolutionContext) -> Image.Resolved
137+
138+
func resolveNamedImage(in context: ImageResolutionContext) -> Image.NamedResolved?
139+
}
140+
141+
// MARK: - Image + View [WIP]
23142

24-
package enum NamedResolved {}
143+
package protocol ImageStyleProtocol {
144+
static func _makeImageView(view: _GraphValue<Image>, inputs: _ViewInputs) -> _ViewOutputs
25145
}
26146

147+
@available(OpenSwiftUI_v1_0, *)
148+
extension Image: View, UnaryView, PrimitiveView {
149+
package struct Style: ViewInput {
150+
package static let defaultValue: Stack<any ImageStyleProtocol.Type> = .empty
151+
}
152+
153+
nonisolated public static func _makeView(view: _GraphValue<Image>, inputs: _ViewInputs) -> _ViewOutputs {
154+
_openSwiftUIUnimplementedFailure()
155+
}
156+
}
157+
158+
// MARK: - ImageProviderBox
159+
160+
@available(OpenSwiftUI_v1_0, *)
161+
@usableFromInline
162+
package class AnyImageProviderBox: @unchecked Sendable {
163+
func resolve(in context: ImageResolutionContext) -> Image.Resolved {
164+
_openSwiftUIBaseClassAbstractMethod()
165+
}
166+
167+
func resolveNamedImage(in context: ImageResolutionContext) -> Image.NamedResolved? {
168+
_openSwiftUIBaseClassAbstractMethod()
169+
}
170+
171+
func isEqual(to other: AnyImageProviderBox) -> Bool {
172+
_openSwiftUIBaseClassAbstractMethod()
173+
}
174+
}
175+
176+
final package class ImageProviderBox<Base>: AnyImageProviderBox, @unchecked Sendable where Base: ImageProvider {
177+
package let base: Base
178+
179+
init(base: Base) {
180+
self.base = base
181+
}
182+
183+
override func resolve(in context: ImageResolutionContext) -> Image.Resolved {
184+
base.resolve(in: context)
185+
}
186+
187+
override func resolveNamedImage(in context: ImageResolutionContext) -> Image.NamedResolved? {
188+
base.resolveNamedImage(in: context)
189+
}
27190

28-
extension Image: PrimitiveView {}
29-
30-
extension Image {
31-
/// The orientation of an image.
32-
///
33-
/// Many image formats such as JPEG include orientation metadata in the
34-
/// image data. In other cases, you can specify image orientation
35-
/// in code. Properly specifying orientation is often important both for
36-
/// displaying the image and for certain kinds of image processing.
37-
///
38-
/// In OpenSwiftUI, you provide an orientation value when initializing an
39-
/// ``Image`` from an existing
40-
/// [CGImage](https://developer.apple.com/documentation/coregraphics/cgimage).
41-
@frozen
42-
public enum Orientation: UInt8, CaseIterable, Hashable {
43-
/// A value that indicates the original pixel data matches the image's
44-
/// intended display orientation.
45-
case up = 0
46-
47-
/// A value that indicates a horizontal flip of the image from the
48-
/// orientation of its original pixel data.
49-
case upMirrored = 2
50-
51-
/// A value that indicates a 180° rotation of the image from the
52-
/// orientation of its original pixel data.
53-
case down = 6
54-
55-
/// A value that indicates a vertical flip of the image from the
56-
/// orientation of its original pixel data.
57-
case downMirrored = 4
58-
59-
/// A value that indicates a 90° counterclockwise rotation from the
60-
/// orientation of its original pixel data.
61-
case left = 1
62-
63-
/// A value that indicates a 90° clockwise rotation and horizontal
64-
/// flip of the image from the orientation of its original pixel
65-
/// data.
66-
case leftMirrored = 3
67-
68-
/// A value that indicates a 90° clockwise rotation of the image from
69-
/// the orientation of its original pixel data.
70-
case right = 7
71-
72-
/// A value that indicates a 90° counterclockwise rotation and
73-
/// horizontal flip from the orientation of its original pixel data.
74-
case rightMirrored = 5
75-
76-
/// Creates an image orientation from an EXIF orientation value.
77-
///
78-
/// This initializer converts the standard EXIF orientation values (1-8)
79-
/// to the corresponding `Image.Orientation` cases.
80-
///
81-
/// - Parameter exifValue: An integer representing the EXIF orientation.
82-
/// Valid values are 1 through 8, corresponding to the standard EXIF
83-
/// orientation values.
84-
/// - Returns: The corresponding orientation, or `nil` if the provided
85-
/// value is not a valid EXIF orientation value.
86-
@_spi(Private)
87-
public init?(exifValue: Int) {
88-
switch exifValue {
89-
case 1: self = .up
90-
case 2: self = .upMirrored
91-
case 3: self = .down
92-
case 4: self = .downMirrored
93-
case 5: self = .leftMirrored
94-
case 6: self = .right
95-
case 7: self = .rightMirrored
96-
case 8: self = .left
97-
default: return nil
98-
}
191+
override func isEqual(to other: AnyImageProviderBox) -> Bool {
192+
guard let other = (other as? ImageProviderBox<Base>) else {
193+
return false
99194
}
195+
return base == other.base
100196
}
101197
}

0 commit comments

Comments
 (0)