9
9
//
10
10
11
11
#if SWT_TARGET_OS_APPLE && canImport(CoreGraphics)
12
- public import CoreGraphics
12
+ package import CoreGraphics
13
13
private import ImageIO
14
+ private import UniformTypeIdentifiers
14
15
15
16
/// A protocol describing images that can be converted to instances of
16
- /// [`Attachment`](https://developer.apple.com/documentation/testing/attachment).
17
+ /// [`Attachment`](https://developer.apple.com/documentation/testing/attachment)
18
+ /// and which can be represented as instances of [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage).
17
19
///
18
- /// Instances of types conforming to this protocol do not themselves conform to
19
- /// [`Attachable`](https://developer.apple.com/documentation/testing/attachable).
20
- /// Instead, the testing library provides additional initializers on [`Attachment`](https://developer.apple.com/documentation/testing/attachment)
21
- /// that take instances of such types and handle converting them to image data when needed.
22
- ///
23
- /// You can attach instances of the following system-provided image types to a
24
- /// test:
25
- ///
26
- /// | Platform | Supported Types |
27
- /// |-|-|
28
- /// | macOS | [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage), [`CIImage`](https://developer.apple.com/documentation/coreimage/ciimage), [`NSImage`](https://developer.apple.com/documentation/appkit/nsimage) |
29
- /// | iOS, watchOS, tvOS, and visionOS | [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage), [`CIImage`](https://developer.apple.com/documentation/coreimage/ciimage), [`UIImage`](https://developer.apple.com/documentation/uikit/uiimage) |
30
- /// | Windows | [`HBITMAP`](https://learn.microsoft.com/en-us/windows/win32/gdi/bitmaps), [`HICON`](https://learn.microsoft.com/en-us/windows/win32/menurc/icons), [`IWICBitmapSource`](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmapsource) (including its subclasses declared by Windows Imaging Component) |
31
- ///
32
- /// You do not generally need to add your own conformances to this protocol. If
33
- /// you have an image in another format that needs to be attached to a test,
34
- /// first convert it to an instance of one of the types above.
35
- ///
36
- /// @Metadata {
37
- /// @Available(Swift, introduced: 6.3)
38
- /// }
20
+ /// This protocol is not part of the public interface of the testing library. It
21
+ /// encapsulates Apple-specific logic for image attachments.
39
22
@available ( _uttypesAPI, * )
40
- public protocol AttachableAsCGImage : _AttachableAsImage , SendableMetatype {
23
+ package protocol AttachableAsCGImage : AttachableAsImage , SendableMetatype {
41
24
/// An instance of `CGImage` representing this image.
42
25
///
43
26
/// - Throws: Any error that prevents the creation of an image.
@@ -53,38 +36,64 @@ public protocol AttachableAsCGImage: _AttachableAsImage, SendableMetatype {
53
36
/// `CGImagePropertyOrientation`. The default value of this property is
54
37
/// `.up`.
55
38
///
56
- /// This property is not part of the public interface of the testing
57
- /// library. It may be removed in a future update.
58
- var _attachmentOrientation : UInt32 { get }
39
+ /// This property is not part of the public interface of the testing library.
40
+ /// It may be removed in a future update.
41
+ var attachmentOrientation : UInt32 { get }
59
42
60
43
/// The scale factor of the image.
61
44
///
62
45
/// The value of this property is typically greater than `1.0` when an image
63
46
/// originates from a Retina Display screenshot or similar. The default value
64
47
/// of this property is `1.0`.
65
48
///
66
- /// This property is not part of the public interface of the testing
67
- /// library. It may be removed in a future update.
68
- var _attachmentScaleFactor : CGFloat { get }
49
+ /// This property is not part of the public interface of the testing library.
50
+ /// It may be removed in a future update.
51
+ var attachmentScaleFactor : CGFloat { get }
69
52
}
70
53
71
54
@available ( _uttypesAPI, * )
72
55
extension AttachableAsCGImage {
73
- public var _attachmentOrientation : UInt32 {
56
+ package var attachmentOrientation : UInt32 {
74
57
CGImagePropertyOrientation . up. rawValue
75
58
}
76
59
77
- public var _attachmentScaleFactor : CGFloat {
60
+ package var attachmentScaleFactor : CGFloat {
78
61
1.0
79
62
}
80
63
81
- public func _deinitializeAttachableValue ( ) { }
82
- }
64
+ public func withUnsafeBytes < R > ( as imageFormat : AttachableImageFormat , _ body : ( UnsafeRawBufferPointer ) throws -> R ) throws -> R {
65
+ let data = NSMutableData ( )
83
66
84
- @available ( _uttypesAPI, * )
85
- extension AttachableAsCGImage where Self: Sendable {
86
- public func _copyAttachableValue( ) -> Self {
87
- self
67
+ // Convert the image to a CGImage.
68
+ let attachableCGImage = try attachableCGImage
69
+
70
+ // Create the image destination.
71
+ guard let dest = CGImageDestinationCreateWithData ( data as CFMutableData , imageFormat. contentType. identifier as CFString , 1 , nil ) else {
72
+ throw ImageAttachmentError . couldNotCreateImageDestination
73
+ }
74
+
75
+ // Configure the properties of the image conversion operation.
76
+ let orientation = attachmentOrientation
77
+ let scaleFactor = attachmentScaleFactor
78
+ let properties : [ CFString : Any ] = [
79
+ kCGImageDestinationLossyCompressionQuality: CGFloat ( imageFormat. encodingQuality) ,
80
+ kCGImagePropertyOrientation: orientation,
81
+ kCGImagePropertyDPIWidth: 72.0 * scaleFactor,
82
+ kCGImagePropertyDPIHeight: 72.0 * scaleFactor,
83
+ ]
84
+
85
+ // Perform the image conversion.
86
+ CGImageDestinationAddImage ( dest, attachableCGImage, properties as CFDictionary )
87
+ guard CGImageDestinationFinalize ( dest) else {
88
+ throw ImageAttachmentError . couldNotConvertImage
89
+ }
90
+
91
+ // Pass the bits of the image out to the body. Note that we have an
92
+ // NSMutableData here so we have to use slightly different API than we would
93
+ // with an instance of Data.
94
+ return try withExtendedLifetime ( data) {
95
+ try body ( UnsafeRawBufferPointer ( start: data. bytes, count: data. length) )
96
+ }
88
97
}
89
98
}
90
99
#endif
0 commit comments