Skip to content

Allow attaching an IWICBitmapSource instance directly. #1266

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 8 commits into from
Aug 14, 2025
Merged
Show file tree
Hide file tree
Changes from 4 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 @@ -26,32 +26,33 @@ public import WinSDK
///
/// - [`HBITMAP`](https://learn.microsoft.com/en-us/windows/win32/gdi/bitmaps)
/// - [`HICON`](https://learn.microsoft.com/en-us/windows/win32/menurc/icons)
/// - [`IWICBitmap`](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmap)
/// - [`IWICBitmapSource`](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmapsource)
/// (including its subclasses declared by Windows Imaging Component)
///
/// You do not generally need to add your own conformances to this protocol. If
/// you have an image in another format that needs to be attached to a test,
/// first convert it to an instance of one of the types above.
@_spi(Experimental)
public protocol _AttachableByAddressAsIWICBitmap {
/// Create a WIC bitmap representing an instance of this type at the given
/// address.
public protocol _AttachableByAddressAsIWICBitmapSource {
/// Create a WIC bitmap source representing an instance of this type at the
/// given address.
///
/// - Parameters:
/// - imageAddress: The address of the instance of this type.
/// - factory: A WIC imaging factory that can be used to create additional
/// WIC objects.
///
/// - Returns: A pointer to a new WIC bitmap representing this image. The
/// caller is responsible for releasing this image when done with it.
/// - 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.
///
/// This function is not part of the public interface of the testing library.
/// It may be removed in a future update.
static func _copyAttachableIWICBitmap(
static func _copyAttachableIWICBitmapSource(
from imageAddress: UnsafeMutablePointer<Self>,
using factory: UnsafeMutablePointer<IWICImagingFactory>
) throws -> UnsafeMutablePointer<IWICBitmap>
) throws -> UnsafeMutablePointer<IWICBitmapSource>

/// Make a copy of the instance of this type at the given address.
///
Expand Down Expand Up @@ -84,7 +85,7 @@ public protocol _AttachableByAddressAsIWICBitmap {
/// does not call this function.
///
/// This function is not responsible for releasing the image returned from
/// `_copyAttachableIWICBitmap(from:using:)`.
/// `_copyAttachableIWICBitmapSource(from:using:)`.
///
/// This function is not part of the public interface of the testing library.
/// It may be removed in a future update.
Expand All @@ -104,13 +105,14 @@ public protocol _AttachableByAddressAsIWICBitmap {
///
/// - [`HBITMAP`](https://learn.microsoft.com/en-us/windows/win32/gdi/bitmaps)
/// - [`HICON`](https://learn.microsoft.com/en-us/windows/win32/menurc/icons)
/// - [`IWICBitmap`](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmap)
/// - [`IWICBitmapSource`](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmapsource)
/// (including its subclasses declared by Windows Imaging Component)
///
/// You do not generally need to add your own conformances to this protocol. If
/// you have an image in another format that needs to be attached to a test,
/// first convert it to an instance of one of the types above.
@_spi(Experimental)
public protocol AttachableAsIWICBitmap {
public protocol AttachableAsIWICBitmapSource {
/// Create a WIC bitmap representing an instance of this type.
///
/// - Parameters:
Expand All @@ -124,9 +126,9 @@ public protocol AttachableAsIWICBitmap {
///
/// This function is not part of the public interface of the testing library.
/// It may be removed in a future update.
borrowing func _copyAttachableIWICBitmap(
borrowing func _copyAttachableIWICBitmapSource(
using factory: UnsafeMutablePointer<IWICImagingFactory>
) throws -> UnsafeMutablePointer<IWICBitmap>
) throws -> UnsafeMutablePointer<IWICBitmapSource>

/// Make a copy of this instance.
///
Expand All @@ -152,7 +154,7 @@ public protocol AttachableAsIWICBitmap {
/// automatically invokes this function as needed.
///
/// This function is not responsible for releasing the image returned from
/// `_copyAttachableIWICBitmap(using:)`.
/// `_copyAttachableIWICBitmapSource(using:)`.
///
/// The default implementation of this function when `Self` conforms to
/// `Sendable` does nothing.
Expand All @@ -162,42 +164,7 @@ public protocol AttachableAsIWICBitmap {
func _deinitializeAttachableValue()
}

extension AttachableAsIWICBitmap {
/// Create a WIC bitmap representing an instance of this type and return it as
/// an instance of `IWICBitmapSource`.
///
/// - Parameters:
/// - factory: A WIC imaging factory that can be used to create additional
/// WIC objects.
///
/// - Returns: A pointer to a new WIC bitmap 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.
///
/// This function is a convenience over `_copyAttachableIWICBitmap(using:)`
/// that casts the result of that function to `IWICBitmapSource` (as needed
/// by WIC when it encodes the image.)
borrowing func copyAttachableIWICBitmapSource(
using factory: UnsafeMutablePointer<IWICImagingFactory>
) throws -> UnsafeMutablePointer<IWICBitmapSource> {
let bitmap = try _copyAttachableIWICBitmap(using: factory)
defer {
_ = bitmap.pointee.lpVtbl.pointee.Release(bitmap)
}

return try withUnsafePointer(to: IID_IWICBitmapSource) { IID_IWICBitmapSource in
var bitmapSource: UnsafeMutableRawPointer?
let rQuery = bitmap.pointee.lpVtbl.pointee.QueryInterface(bitmap, IID_IWICBitmapSource, &bitmapSource)
guard rQuery == S_OK, let bitmapSource else {
throw ImageAttachmentError.queryInterfaceFailed(IWICBitmapSource.self, rQuery)
}
return bitmapSource.assumingMemoryBound(to: IWICBitmapSource.self)
}
}
}

extension AttachableAsIWICBitmap where Self: Sendable {
extension AttachableAsIWICBitmapSource where Self: Sendable {
public func _copyAttachableValue() -> Self {
self
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@ extension Attachment where AttachableValue: ~Copyable {
/// attachment.
///
/// The following system-provided image types conform to the
/// ``AttachableAsIWICBitmap`` protocol and can be attached to a test:
/// ``AttachableAsIWICBitmapSource`` protocol and can be attached to a test:
///
/// - [`HBITMAP`](https://learn.microsoft.com/en-us/windows/win32/gdi/bitmaps)
/// - [`HICON`](https://learn.microsoft.com/en-us/windows/win32/menurc/icons)
/// - [`IWICBitmap`](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmap)
/// - [`IWICBitmapSource`](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmapsource)
/// (including its subclasses declared by Windows Imaging Component)
///
/// The testing library uses the image format specified by `imageFormat`. Pass
/// `nil` to let the testing library decide which image format to use. If you
Expand Down Expand Up @@ -66,11 +67,12 @@ extension Attachment where AttachableValue: ~Copyable {
/// and immediately attaches it to the current test.
///
/// The following system-provided image types conform to the
/// ``AttachableAsIWICBitmap`` protocol and can be attached to a test:
/// ``AttachableAsIWICBitmapSource`` protocol and can be attached to a test:
///
/// - [`HBITMAP`](https://learn.microsoft.com/en-us/windows/win32/gdi/bitmaps)
/// - [`HICON`](https://learn.microsoft.com/en-us/windows/win32/menurc/icons)
/// - [`IWICBitmap`](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmap)
/// - [`IWICBitmapSource`](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmapsource)
/// (including its subclasses declared by Windows Imaging Component)
///
/// The testing library uses the image format specified by `imageFormat`. Pass
/// `nil` to let the testing library decide which image format to use. If you
Expand All @@ -92,7 +94,7 @@ extension Attachment where AttachableValue: ~Copyable {
}

@_spi(Experimental)
extension Attachment where AttachableValue: AttachableWrapper, AttachableValue.Wrapped: AttachableAsIWICBitmap {
extension Attachment where AttachableValue: AttachableWrapper, AttachableValue.Wrapped: AttachableAsIWICBitmapSource {
/// The image format to use when encoding the represented image.
@_disfavoredOverload
public var imageFormat: AttachableImageFormat? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@ import Testing
public import WinSDK

@_spi(Experimental)
extension HBITMAP__: _AttachableByAddressAsIWICBitmap {
public static func _copyAttachableIWICBitmap(
extension HBITMAP__: _AttachableByAddressAsIWICBitmapSource {
public static func _copyAttachableIWICBitmapSource(
from imageAddress: UnsafeMutablePointer<Self>,
using factory: UnsafeMutablePointer<IWICImagingFactory>
) throws -> UnsafeMutablePointer<IWICBitmap> {
var bitmap: UnsafeMutablePointer<IWICBitmap>!
) throws -> UnsafeMutablePointer<IWICBitmapSource> {
var bitmap: UnsafeMutablePointer<IWICBitmap>?
let rCreate = factory.pointee.lpVtbl.pointee.CreateBitmapFromHBITMAP(factory, imageAddress, nil, WICBitmapUsePremultipliedAlpha, &bitmap)
guard rCreate == S_OK, let bitmap else {
throw ImageAttachmentError.comObjectCreationFailed(IWICBitmap.self, rCreate)
}
return bitmap
return try bitmap.cast(to: IWICBitmapSource.self)
}

public static func _copyAttachableValue(at imageAddress: UnsafeMutablePointer<Self>) -> UnsafeMutablePointer<Self> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@ import Testing
public import WinSDK

@_spi(Experimental)
extension HICON__: _AttachableByAddressAsIWICBitmap {
public static func _copyAttachableIWICBitmap(
extension HICON__: _AttachableByAddressAsIWICBitmapSource {
public static func _copyAttachableIWICBitmapSource(
from imageAddress: UnsafeMutablePointer<Self>,
using factory: UnsafeMutablePointer<IWICImagingFactory>
) throws -> UnsafeMutablePointer<IWICBitmap> {
var bitmap: UnsafeMutablePointer<IWICBitmap>!
) throws -> UnsafeMutablePointer<IWICBitmapSource> {
var bitmap: UnsafeMutablePointer<IWICBitmap>?
let rCreate = factory.pointee.lpVtbl.pointee.CreateBitmapFromHICON(factory, imageAddress, &bitmap)
guard rCreate == S_OK, let bitmap else {
throw ImageAttachmentError.comObjectCreationFailed(IWICBitmap.self, rCreate)
}
return bitmap
return try bitmap.cast(to: IWICBitmapSource.self)
}

public static func _copyAttachableValue(at imageAddress: UnsafeMutablePointer<Self>) -> UnsafeMutablePointer<Self> {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
//

#if os(Windows)
import Testing

public import WinSDK

/// A protocol that identifies a type as a COM subclass of `IWICBitmapSource`.
///
/// Because COM class inheritance is not visible in Swift, we must manually
/// apply conformances to this protocol to each COM type that inherits from
/// `IWICBitmapSource`.
///
/// Because this protocol is not `public`, we must also explicitly restate
/// conformance to the public protocol `_AttachableByAddressAsIWICBitmapSource`
/// even though this protocol refines that one. This protocol refines
/// `_AttachableByAddressAsIWICBitmapSource` because otherwise the compiler will
/// not allow us to declare `public` members in its extension that provides the
/// implementation of `_AttachableByAddressAsIWICBitmapSource` below.
///
/// This protocol is not part of the public interface of the testing library. It
/// allows us to reuse code across all subclasses of `IWICBitmapSource`.
protocol IWICBitmapSourceProtocol: _AttachableByAddressAsIWICBitmapSource {}

@_spi(Experimental)
extension IWICBitmapSource: _AttachableByAddressAsIWICBitmapSource, IWICBitmapSourceProtocol {}

@_spi(Experimental)
extension IWICBitmap: _AttachableByAddressAsIWICBitmapSource, IWICBitmapSourceProtocol {}

@_spi(Experimental)
extension IWICBitmapClipper: _AttachableByAddressAsIWICBitmapSource, IWICBitmapSourceProtocol {}

@_spi(Experimental)
extension IWICBitmapFlipRotator: _AttachableByAddressAsIWICBitmapSource, IWICBitmapSourceProtocol {}

@_spi(Experimental)
extension IWICBitmapFrameDecode: _AttachableByAddressAsIWICBitmapSource, IWICBitmapSourceProtocol {}

@_spi(Experimental)
extension IWICBitmapScaler: _AttachableByAddressAsIWICBitmapSource, IWICBitmapSourceProtocol {}

@_spi(Experimental)
extension IWICBitmapSourceTransform2: _AttachableByAddressAsIWICBitmapSource, IWICBitmapSourceProtocol {}

@_spi(Experimental)
extension IWICColorTransform: _AttachableByAddressAsIWICBitmapSource, IWICBitmapSourceProtocol {}

@_spi(Experimental)
extension IWICFormatConverter: _AttachableByAddressAsIWICBitmapSource, IWICBitmapSourceProtocol {}

@_spi(Experimental)
extension IWICPlanarFormatConverter: _AttachableByAddressAsIWICBitmapSource, IWICBitmapSourceProtocol {}

// MARK: - Upcasting conveniences

extension UnsafeMutablePointer where Pointee: IWICBitmapSourceProtocol {
/// Upcast this WIC bitmap to a WIC bitmap source (its parent type).
///
/// - Returns: `self`, cast to the parent type via `QueryInterface()`. The
/// caller is responsible for releasing the resulting object.
///
/// - Throws: Any error that occurs while calling `QueryInterface()`. In
/// practice, this function is not expected to throw an error as it should
/// always be possible to cast a valid instance of `IWICBitmap` to
/// `IWICBitmapSource`.
///
/// - Important: This function consumes a reference to `self` even if the cast
/// fails.
consuming func cast(to _: IWICBitmapSource.Type) throws -> UnsafeMutablePointer<IWICBitmapSource> {
try self.withMemoryRebound(to: IUnknown.self, capacity: 1) { `self` in
defer {
_ = self.pointee.lpVtbl.pointee.Release(self)
}

return try withUnsafePointer(to: IID_IWICBitmapSource) { IID_IWICBitmapSource in
var bitmapSource: UnsafeMutableRawPointer?
let rQuery = self.pointee.lpVtbl.pointee.QueryInterface(self, IID_IWICBitmapSource, &bitmapSource)
guard rQuery == S_OK, let bitmapSource else {
throw ImageAttachmentError.queryInterfaceFailed(IWICBitmapSource.self, rQuery)
}
return bitmapSource.assumingMemoryBound(to: IWICBitmapSource.self)
}
}
}
}

// MARK: - _AttachableByAddressAsIWICBitmapSource implementation

extension IWICBitmapSourceProtocol {
public static func _copyAttachableIWICBitmapSource(
from imageAddress: UnsafeMutablePointer<Self>,
using factory: UnsafeMutablePointer<IWICImagingFactory>
) throws -> UnsafeMutablePointer<IWICBitmapSource> {
try _copyAttachableValue(at: imageAddress).cast(to: IWICBitmapSource.self)
}

public static func _copyAttachableValue(at imageAddress: UnsafeMutablePointer<Self>) -> UnsafeMutablePointer<Self> {
imageAddress.withMemoryRebound(to: IUnknown.self, capacity: 1) { imageAddress in
_ = imageAddress.pointee.lpVtbl.pointee.AddRef(imageAddress)
}
return imageAddress
}

public static func _deinitializeAttachableValue(at imageAddress: UnsafeMutablePointer<Self>) {
imageAddress.withMemoryRebound(to: IUnknown.self, capacity: 1) { imageAddress in
_ = imageAddress.pointee.lpVtbl.pointee.Release(imageAddress)
}
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ import Testing
public import WinSDK

@_spi(Experimental)
extension UnsafeMutablePointer: AttachableAsIWICBitmap where Pointee: _AttachableByAddressAsIWICBitmap {
public func _copyAttachableIWICBitmap(using factory: UnsafeMutablePointer<IWICImagingFactory>) throws -> UnsafeMutablePointer<IWICBitmap> {
try Pointee._copyAttachableIWICBitmap(from: self, using: factory)
extension UnsafeMutablePointer: AttachableAsIWICBitmapSource where Pointee: _AttachableByAddressAsIWICBitmapSource {
public func _copyAttachableIWICBitmapSource(using factory: UnsafeMutablePointer<IWICImagingFactory>) throws -> UnsafeMutablePointer<IWICBitmapSource> {
try Pointee._copyAttachableIWICBitmapSource(from: self, using: factory)
}

public func _copyAttachableValue() -> Self {
Expand Down
Loading