Skip to content

Commit 0b48b60

Browse files
authored
Revise Windows image attachments to match the draft API proposal. (#1268)
This PR tweaks the Windows image attachments API to match what is proposed in swiftlang/swift-evolution#2940. ### Checklist: - [x] Code and documentation should follow the style of the [Style Guide](https://github.com/apple/swift-testing/blob/main/Documentation/StyleGuide.md). - [x] If public symbols are renamed or modified, DocC references should be updated.
1 parent 1603000 commit 0b48b60

File tree

7 files changed

+89
-12
lines changed

7 files changed

+89
-12
lines changed

Sources/Overlays/_Testing_CoreGraphics/Attachments/AttachableImageFormat+UTType.swift

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,38 @@ extension AttachableImageFormat {
9797
self.init(kind: .systemValue(contentType), encodingQuality: encodingQuality)
9898
}
9999
}
100+
101+
@available(_uttypesAPI, *)
102+
@_spi(Experimental) // STOP: not part of ST-0014
103+
extension AttachableImageFormat {
104+
/// Construct an instance of this type with the given path extension and
105+
/// encoding quality.
106+
///
107+
/// - Parameters:
108+
/// - pathExtension: A path extension corresponding to the image format to
109+
/// use when encoding images.
110+
/// - encodingQuality: The encoding quality to use when encoding images. For
111+
/// the lowest supported quality, pass `0.0`. For the highest supported
112+
/// quality, pass `1.0`.
113+
///
114+
/// If the target image format does not support variable-quality encoding,
115+
/// the value of the `encodingQuality` argument is ignored.
116+
///
117+
/// If `pathExtension` does not correspond to a recognized image format, this
118+
/// initializer returns `nil`:
119+
///
120+
/// - On Apple platforms, the content type corresponding to `pathExtension`
121+
/// must conform to [`UTType.image`](https://developer.apple.com/documentation/uniformtypeidentifiers/uttype-swift.struct/image).
122+
/// - On Windows, there must be a corresponding subclass of [`IWICBitmapEncoder`](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmapencoder)
123+
/// registered with Windows Imaging Component.
124+
public init?(pathExtension: String, encodingQuality: Float = 1.0) {
125+
let pathExtension = pathExtension.drop { $0 == "." }
126+
127+
guard let contentType = UTType(filenameExtension: String(pathExtension), conformingTo: .image) else {
128+
return nil
129+
}
130+
131+
self.init(contentType, encodingQuality: encodingQuality)
132+
}
133+
}
100134
#endif

Sources/Overlays/_Testing_WinSDK/Attachments/AttachableAsIWICBitmapSource.swift

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,14 @@ public protocol _AttachableByAddressAsIWICBitmapSource {
113113
/// first convert it to an instance of one of the types above.
114114
@_spi(Experimental)
115115
public protocol AttachableAsIWICBitmapSource {
116+
/// Create a WIC bitmap source representing an instance of this type.
117+
///
118+
/// - Returns: A pointer to a new WIC bitmap source representing this image.
119+
/// The caller is responsible for releasing this image when done with it.
120+
///
121+
/// - Throws: Any error that prevented the creation of the WIC bitmap source.
122+
func copyAttachableIWICBitmapSource() throws -> UnsafeMutablePointer<IWICBitmapSource>
123+
116124
/// Create a WIC bitmap representing an instance of this type.
117125
///
118126
/// - Parameters:
@@ -124,9 +132,16 @@ public protocol AttachableAsIWICBitmapSource {
124132
///
125133
/// - Throws: Any error that prevented the creation of the WIC bitmap.
126134
///
135+
/// The default implementation of this function ignores `factory` and calls
136+
/// ``copyAttachableIWICBitmapSource()``. If your implementation of
137+
/// ``copyAttachableIWICBitmapSource()`` needs to create a WIC imaging factory
138+
/// in order to return a result, it is more efficient to implement this
139+
/// function too so that the testing library can pass the WIC imaging factory
140+
/// it creates.
141+
///
127142
/// This function is not part of the public interface of the testing library.
128143
/// It may be removed in a future update.
129-
borrowing func _copyAttachableIWICBitmapSource(
144+
func _copyAttachableIWICBitmapSource(
130145
using factory: UnsafeMutablePointer<IWICImagingFactory>
131146
) throws -> UnsafeMutablePointer<IWICBitmapSource>
132147

@@ -164,6 +179,14 @@ public protocol AttachableAsIWICBitmapSource {
164179
func _deinitializeAttachableValue()
165180
}
166181

182+
extension AttachableAsIWICBitmapSource {
183+
public func _copyAttachableIWICBitmapSource(
184+
using factory: UnsafeMutablePointer<IWICImagingFactory>
185+
) throws -> UnsafeMutablePointer<IWICBitmapSource> {
186+
try copyAttachableIWICBitmapSource()
187+
}
188+
}
189+
167190
extension AttachableAsIWICBitmapSource where Self: Sendable {
168191
public func _copyAttachableValue() -> Self {
169192
self

Sources/Overlays/_Testing_WinSDK/Attachments/AttachableImageFormat+CLSID.swift

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -277,19 +277,21 @@ extension AttachableImageFormat {
277277
/// If the target image format does not support variable-quality encoding,
278278
/// the value of the `encodingQuality` argument is ignored.
279279
///
280-
/// If `pathExtension` does not correspond to an image format that WIC can use
281-
/// to encode images, this initializer returns `nil`. For a list of image
282-
/// encoders supported by WIC, see the documentation for the [IWICBitmapEncoder](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmapencoder)
283-
/// class.
280+
/// If `pathExtension` does not correspond to a recognized image format, this
281+
/// initializer returns `nil`:
282+
///
283+
/// - On Apple platforms, the content type corresponding to `pathExtension`
284+
/// must conform to [`UTType.image`](https://developer.apple.com/documentation/uniformtypeidentifiers/uttype-swift.struct/image).
285+
/// - On Windows, there must be a corresponding subclass of [`IWICBitmapEncoder`](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmapencoder)
286+
/// registered with Windows Imaging Component.
284287
public init?(pathExtension: String, encodingQuality: Float = 1.0) {
285288
let pathExtension = pathExtension.drop { $0 == "." }
286289

287-
let clsid = Self._computeCLSID(forPathExtension: String(pathExtension))
288-
if let clsid {
289-
self.init(clsid, encodingQuality: encodingQuality)
290-
} else {
290+
guard let clsid = Self._computeCLSID(forPathExtension: String(pathExtension)) else {
291291
return nil
292292
}
293+
294+
self.init(clsid, encodingQuality: encodingQuality)
293295
}
294296
}
295297
#endif

Sources/Overlays/_Testing_WinSDK/Attachments/Attachment+AttachableAsIWICBitmapSource.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
@_spi(Experimental) public import Testing
1313

1414
@_spi(Experimental)
15-
extension Attachment where AttachableValue: ~Copyable {
15+
extension Attachment {
1616
/// Initialize an instance of this type that encloses the given image.
1717
///
1818
/// - Parameters:
@@ -42,7 +42,7 @@ extension Attachment where AttachableValue: ~Copyable {
4242
/// correspond to an image format the operating system knows how to write, the
4343
/// testing library selects an appropriate image format for you.
4444
public init<T>(
45-
_ image: borrowing T,
45+
_ image: T,
4646
named preferredName: String? = nil,
4747
as imageFormat: AttachableImageFormat? = nil,
4848
sourceLocation: SourceLocation = #_sourceLocation
@@ -82,7 +82,7 @@ extension Attachment where AttachableValue: ~Copyable {
8282
/// correspond to an image format the operating system knows how to write, the
8383
/// testing library selects an appropriate image format for you.
8484
public static func record<T>(
85-
_ image: borrowing T,
85+
_ image: T,
8686
named preferredName: String? = nil,
8787
as imageFormat: AttachableImageFormat? = nil,
8888
sourceLocation: SourceLocation = #_sourceLocation

Sources/Overlays/_Testing_WinSDK/Attachments/UnsafeMutablePointer+AttachableAsIWICBitmapSource.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ public import WinSDK
1515

1616
@_spi(Experimental)
1717
extension UnsafeMutablePointer: AttachableAsIWICBitmapSource where Pointee: _AttachableByAddressAsIWICBitmapSource {
18+
public func copyAttachableIWICBitmapSource() throws -> UnsafeMutablePointer<IWICBitmapSource> {
19+
let factory = try IWICImagingFactory.create()
20+
defer {
21+
_ = factory.pointee.lpVtbl.pointee.Release(factory)
22+
}
23+
return try _copyAttachableIWICBitmapSource(using: factory)
24+
}
25+
1826
public func _copyAttachableIWICBitmapSource(using factory: UnsafeMutablePointer<IWICImagingFactory>) throws -> UnsafeMutablePointer<IWICBitmapSource> {
1927
try Pointee._copyAttachableIWICBitmapSource(from: self, using: factory)
2028
}

Sources/Testing/Attachments/AttachableImageFormat.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
/// - On Apple platforms, you can use [`CGImageDestinationCopyTypeIdentifiers()`](https://developer.apple.com/documentation/imageio/cgimagedestinationcopytypeidentifiers())
2424
/// from the [Image I/O framework](https://developer.apple.com/documentation/imageio)
2525
/// to determine which formats are supported.
26+
/// - On Windows, you can use [`IWICImagingFactory.CreateComponentEnumerator()`](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nf-wincodec-iwicimagingfactory-createcomponentenumerator)
27+
/// to enumerate the available image encoders.
2628
@_spi(Experimental)
2729
@available(_uttypesAPI, *)
2830
public struct AttachableImageFormat: Sendable {

Tests/TestingTests/AttachmentTests.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -836,6 +836,14 @@ extension AttachmentTests {
836836
#expect(jpgjpegFilename == "example.jpg")
837837
}
838838
#endif
839+
840+
#if (canImport(CoreGraphics) && canImport(_Testing_CoreGraphics)) || (canImport(WinSDK) && canImport(_Testing_WinSDK))
841+
@available(_uttypesAPI, *)
842+
@Test func imageFormatFromPathExtension() {
843+
let format = AttachableImageFormat(pathExtension: "png")
844+
#expect(format != nil)
845+
}
846+
#endif
839847
}
840848
}
841849

0 commit comments

Comments
 (0)