Skip to content

Revise Windows image attachments to match the draft API proposal. #1268

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,38 @@ extension AttachableImageFormat {
self.init(kind: .systemValue(contentType), encodingQuality: encodingQuality)
}
}

@available(_uttypesAPI, *)
@_spi(Experimental) // STOP: not part of ST-0014
extension AttachableImageFormat {
/// Construct an instance of this type with the given path extension and
/// encoding quality.
///
/// - Parameters:
/// - pathExtension: A path extension corresponding to the image format to
/// use when encoding images.
/// - encodingQuality: The encoding quality to use when encoding images. For
/// the lowest supported quality, pass `0.0`. For the highest supported
/// quality, pass `1.0`.
///
/// If the target image format does not support variable-quality encoding,
/// the value of the `encodingQuality` argument is ignored.
///
/// If `pathExtension` does not correspond to a recognized image format, this
/// initializer returns `nil`:
///
/// - On Apple platforms, the content type corresponding to `pathExtension`
/// must conform to [`UTType.image`](https://developer.apple.com/documentation/uniformtypeidentifiers/uttype-swift.struct/image).
/// - On Windows, there must be a corresponding subclass of [`IWICBitmapEncoder`](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmapencoder)
/// registered with Windows Imaging Component.
public init?(pathExtension: String, encodingQuality: Float = 1.0) {
let pathExtension = pathExtension.drop { $0 == "." }

guard let contentType = UTType(filenameExtension: String(pathExtension), conformingTo: .image) else {
return nil
}

self.init(contentType, encodingQuality: encodingQuality)
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,14 @@ public protocol _AttachableByAddressAsIWICBitmapSource {
/// first convert it to an instance of one of the types above.
@_spi(Experimental)
public protocol AttachableAsIWICBitmapSource {
/// Create a WIC bitmap source representing an instance of this type.
///
/// - Returns: A pointer to a new WIC bitmap source representing this image.
/// The caller is responsible for releasing this image when done with it.
///
/// - Throws: Any error that prevented the creation of the WIC bitmap source.
func copyAttachableIWICBitmapSource() throws -> UnsafeMutablePointer<IWICBitmapSource>

/// Create a WIC bitmap representing an instance of this type.
///
/// - Parameters:
Expand All @@ -124,9 +132,16 @@ public protocol AttachableAsIWICBitmapSource {
///
/// - Throws: Any error that prevented the creation of the WIC bitmap.
///
/// The default implementation of this function ignores `factory` and calls
/// ``copyAttachableIWICBitmapSource()``. If your implementation of
/// ``copyAttachableIWICBitmapSource()`` needs to create a WIC imaging factory
/// in order to return a result, it is more efficient to implement this
/// function too so that the testing library can pass the WIC imaging factory
/// it creates.
///
/// This function is not part of the public interface of the testing library.
/// It may be removed in a future update.
borrowing func _copyAttachableIWICBitmapSource(
func _copyAttachableIWICBitmapSource(
using factory: UnsafeMutablePointer<IWICImagingFactory>
) throws -> UnsafeMutablePointer<IWICBitmapSource>

Expand Down Expand Up @@ -164,6 +179,14 @@ public protocol AttachableAsIWICBitmapSource {
func _deinitializeAttachableValue()
}

extension AttachableAsIWICBitmapSource {
public func _copyAttachableIWICBitmapSource(
using factory: UnsafeMutablePointer<IWICImagingFactory>
) throws -> UnsafeMutablePointer<IWICBitmapSource> {
try copyAttachableIWICBitmapSource()
}
}

extension AttachableAsIWICBitmapSource where Self: Sendable {
public func _copyAttachableValue() -> Self {
self
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,19 +277,21 @@ extension AttachableImageFormat {
/// If the target image format does not support variable-quality encoding,
/// the value of the `encodingQuality` argument is ignored.
///
/// If `pathExtension` does not correspond to an image format that WIC can use
/// to encode images, this initializer returns `nil`. For a list of image
/// encoders supported by WIC, see the documentation for the [IWICBitmapEncoder](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmapencoder)
/// class.
/// If `pathExtension` does not correspond to a recognized image format, this
/// initializer returns `nil`:
///
/// - On Apple platforms, the content type corresponding to `pathExtension`
/// must conform to [`UTType.image`](https://developer.apple.com/documentation/uniformtypeidentifiers/uttype-swift.struct/image).
/// - On Windows, there must be a corresponding subclass of [`IWICBitmapEncoder`](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmapencoder)
/// registered with Windows Imaging Component.
public init?(pathExtension: String, encodingQuality: Float = 1.0) {
let pathExtension = pathExtension.drop { $0 == "." }

let clsid = Self._computeCLSID(forPathExtension: String(pathExtension))
if let clsid {
self.init(clsid, encodingQuality: encodingQuality)
} else {
guard let clsid = Self._computeCLSID(forPathExtension: String(pathExtension)) else {
return nil
}

self.init(clsid, encodingQuality: encodingQuality)
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
@_spi(Experimental) public import Testing

@_spi(Experimental)
extension Attachment where AttachableValue: ~Copyable {
extension Attachment {
/// Initialize an instance of this type that encloses the given image.
///
/// - Parameters:
Expand Down Expand Up @@ -42,7 +42,7 @@ extension Attachment where AttachableValue: ~Copyable {
/// correspond to an image format the operating system knows how to write, the
/// testing library selects an appropriate image format for you.
public init<T>(
_ image: borrowing T,
_ image: T,
named preferredName: String? = nil,
as imageFormat: AttachableImageFormat? = nil,
sourceLocation: SourceLocation = #_sourceLocation
Expand Down Expand Up @@ -82,7 +82,7 @@ extension Attachment where AttachableValue: ~Copyable {
/// correspond to an image format the operating system knows how to write, the
/// testing library selects an appropriate image format for you.
public static func record<T>(
_ image: borrowing T,
_ image: T,
named preferredName: String? = nil,
as imageFormat: AttachableImageFormat? = nil,
sourceLocation: SourceLocation = #_sourceLocation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ public import WinSDK

@_spi(Experimental)
extension UnsafeMutablePointer: AttachableAsIWICBitmapSource where Pointee: _AttachableByAddressAsIWICBitmapSource {
public func copyAttachableIWICBitmapSource() throws -> UnsafeMutablePointer<IWICBitmapSource> {
let factory = try IWICImagingFactory.create()
defer {
_ = factory.pointee.lpVtbl.pointee.Release(factory)
}
return try _copyAttachableIWICBitmapSource(using: factory)
}

public func _copyAttachableIWICBitmapSource(using factory: UnsafeMutablePointer<IWICImagingFactory>) throws -> UnsafeMutablePointer<IWICBitmapSource> {
try Pointee._copyAttachableIWICBitmapSource(from: self, using: factory)
}
Expand Down
2 changes: 2 additions & 0 deletions Sources/Testing/Attachments/AttachableImageFormat.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
/// - On Apple platforms, you can use [`CGImageDestinationCopyTypeIdentifiers()`](https://developer.apple.com/documentation/imageio/cgimagedestinationcopytypeidentifiers())
/// from the [Image I/O framework](https://developer.apple.com/documentation/imageio)
/// to determine which formats are supported.
/// - On Windows, you can use [`IWICImagingFactory.CreateComponentEnumerator()`](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nf-wincodec-iwicimagingfactory-createcomponentenumerator)
/// to enumerate the available image encoders.
@_spi(Experimental)
@available(_uttypesAPI, *)
public struct AttachableImageFormat: Sendable {
Expand Down
8 changes: 8 additions & 0 deletions Tests/TestingTests/AttachmentTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -836,6 +836,14 @@ extension AttachmentTests {
#expect(jpgjpegFilename == "example.jpg")
}
#endif

#if (canImport(CoreGraphics) && canImport(_Testing_CoreGraphics)) || (canImport(WinSDK) && canImport(_Testing_WinSDK))
@available(_uttypesAPI, *)
@Test func imageFormatFromPathExtension() {
let format = AttachableImageFormat(pathExtension: "png")
#expect(format != nil)
}
#endif
}
}

Expand Down