Repository: WebKit/WebKit
Pull request #43423: Make web process send ShareableBitmap instead of SharedBuffer for icon data
Author: login: szewai
Labels: name: New Bugs
description: Unclassified bugs are placed in this component until the correct component can be determined.
Created: 2025-04-01T23:25:31Z | Merged: 2025-04-08T20:16:41Z

#### db75e4455949f92e7e15b3ca1514466d66811788
<pre>
Make web process send ShareableBitmap instead of SharedBuffer for icon data
<a href="https://bugs.webkit.org/show_bug.cgi?id=290873">https://bugs.webkit.org/show_bug.cgi?id=290873</a>
<a href="https://rdar.apple.com/143579665">rdar://143579665</a>

Reviewed by Said Abou-Hallawa and Anne van Kesteren.

In existing implementation of icon data generation, UI process sends image data to web process, and web process decodes
image from data, generates images with different sizes, combines them into one ico image and sends back image data to UI
process. To make the process more safe, this patch makes web process send images (bitmaps) with different sizes to UI
process, and UI process is responsible for combining them into an ico image.

* Source/WebCore/platform/graphics/ImageAdapter.h:
(WebCore::ImageAdapter::image const):
* Source/WebCore/platform/graphics/ImageUtilities.h:
* Source/WebCore/platform/graphics/ShareableBitmap.cpp:
(WebCore::ShareableBitmap::createFromImageDraw):
* Source/WebCore/platform/graphics/ShareableBitmap.h:
* Source/WebCore/platform/graphics/cg/ImageUtilitiesCG.cpp:
(WebCore::createBitmapsFromNativeImage):
(WebCore::createNativeImageFromSVGImage):
(WebCore::createBitmapsFromSVGImage):
(WebCore::createBitmapsFromImageData):
(WebCore::createIconDataFromBitmaps):
(WebCore::expandNativeImageToData): Deleted.
(WebCore::expandSVGImageToData): Deleted.
(WebCore::createIconDataFromImageData): Deleted.
* Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm:
(WebKit::WebPageProxy::createIconDataFromImageData):
* Source/WebKit/WebProcess/WebPage/Cocoa/WebPageCocoa.mm:
(WebKit::WebPage::createBitmapsFromImageData):
(WebKit::WebPage::createIconDataFromImageData): Deleted.
* Source/WebKit/WebProcess/WebPage/WebPage.h:
* Source/WebKit/WebProcess/WebPage/WebPage.messages.in:
* Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* Tools/TestWebKitAPI/Tests/WebKit/icon-svg-16.tiff: Added.
* Tools/TestWebKitAPI/Tests/WebKit/icon-svg-256.tiff: Added.
* Tools/TestWebKitAPI/Tests/WebKitCocoa/LoadAndDecodeImage.mm:
(TestWebKitAPI::tiffRepresentation):
(TestWebKitAPI::TEST(WebKit, CreateIconDataFromImageDataSVG)):

Canonical link: <a href="https://commits.webkit.org/293430@main">https://commits.webkit.org/293430@main</a>
</pre>
<!--EWS-Status-Bubble-Start-->
https://github.com/WebKit/WebKit/commit/b248879bd902c642dd58ac2f964a96bfe63a4968

| Misc | iOS, visionOS, tvOS & watchOS  | macOS  | Linux |  Windows |
| ----- | ---------------------- | ------- |  ----- |  --------- |
| [✅ 🧪 style](https://ews-build.webkit.org/#/builders/38/builds/98889 "Passed style check") | [✅ 🛠 ios](https://ews-build.webkit.org/#/builders/131/builds/18526 "Built successfully") | [✅ 🛠 mac](https://ews-build.webkit.org/#/builders/138/builds/8768 "Built successfully") | [✅ 🛠 wpe](https://ews-build.webkit.org/#/builders/5/builds/104015 "Built successfully") | [✅ 🛠 win](https://ews-build.webkit.org/#/builders/59/builds/49478 "Built successfully") 
| [✅ 🧪 bindings](https://ews-build.webkit.org/#/builders/9/builds/100934 "Passed tests") | [✅ 🛠 ios-sim](https://ews-build.webkit.org/#/builders/130/builds/18821 "Built successfully") | [✅ 🛠 mac-AS-debug](https://ews-build.webkit.org/#/builders/123/builds/26976 "Built successfully") | [✅ 🧪 wpe-wk2](https://ews-build.webkit.org/#/builders/34/builds/75282 "Passed tests") | [✅ 🧪 win-tests](https://ews-build.webkit.org/#/builders/60/builds/32413 "Passed tests") 
| [✅ 🧪 webkitperl](https://ews-build.webkit.org/#/builders/11/builds/101893 "Passed tests") | [✅ 🧪 ios-wk2](https://ews-build.webkit.org/#/builders/132/builds/14299 "Passed tests") | [✅ 🧪 api-mac](https://ews-build.webkit.org/#/builders/18/builds/89306 "Passed tests") | [✅ 🧪 api-wpe](https://ews-build.webkit.org/#/builders/41/builds/55643 "Passed tests") | 
| | [  ~~🧪 ios-wk2-wpt~~](https://ews-build.webkit.org/#/builders/133/builds/14090 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | [✅ 🧪 mac-wk1](https://ews-build.webkit.org/#/builders/135/builds/7278 "Passed tests") | [✅ 🛠 wpe-cairo](https://ews-build.webkit.org/#/builders/65/builds/48858 "Built successfully") | 
| | [  ~~🧪 api-ios~~](https://ews-build.webkit.org/#/builders/13/builds/84027 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | [✅ 🧪 mac-wk2](https://ews-build.webkit.org/#/builders/136/builds/7351 "Passed tests") | [✅ 🛠 gtk](https://ews-build.webkit.org/#/builders/2/builds/106384 "Built successfully") | 
| | [✅ 🛠 vision](https://ews-build.webkit.org/#/builders/128/builds/25986 "Built successfully") | [✅ 🧪 mac-AS-debug-wk2](https://ews-build.webkit.org/#/builders/122/builds/18942 "Passed tests") | [✅ 🧪 gtk-wk2](https://ews-build.webkit.org/#/builders/1/builds/84246 "Passed tests") | 
| | [✅ 🛠 vision-sim](https://ews-build.webkit.org/#/builders/121/builds/26361 "Built successfully") | [✅ 🧪 mac-wk2-stress](https://ews-build.webkit.org/#/builders/8/builds/85504 "Passed tests") | [✅ 🧪 api-gtk](https://ews-build.webkit.org/#/builders/21/builds/83744 "Passed tests") | 
| [✅ 🛠 🧪 merge](https://ews-build.webkit.org/#/builders/19/builds/21226 "Built successfully and passed tests") | [✅ 🧪 vision-wk2](https://ews-build.webkit.org/#/builders/126/builds/28399 "Passed tests") | [✅ 🧪 mac-intel-wk2](https://ews-build.webkit.org/#/builders/137/builds/6071 "Passed tests") | [✅ 🛠 playstation](https://ews-build.webkit.org/#/builders/134/builds/19708 "Built successfully") | 
| | [✅ 🛠 tv](https://ews-build.webkit.org/#/builders/127/builds/25939 "Built successfully") | [✅ 🛠 mac-safer-cpp](https://ews-build.webkit.org/#/builders/120/builds/31125 "Built successfully") | | 
| | [✅ 🛠 tv-sim](https://ews-build.webkit.org/#/builders/125/builds/25759 "Built successfully") | | | 
| | [✅ 🛠 watch](https://ews-build.webkit.org/#/builders/129/builds/29079 "Built successfully") | | | 
| | [✅ 🛠 watch-sim](https://ews-build.webkit.org/#/builders/124/builds/27333 "Built successfully") | | | 
<!--EWS-Status-Bubble-End-->


Diff:
diff --git a/Source/WebCore/platform/graphics/ImageAdapter.h b/Source/WebCore/platform/graphics/ImageAdapter.h
index b091bf744b33..0610d25e3d8a 100644
--- a/Source/WebCore/platform/graphics/ImageAdapter.h
+++ b/Source/WebCore/platform/graphics/ImageAdapter.h
@@ -106,13 +106,13 @@ class ImageAdapter {
 #endif
     void invalidate();
 
-private:
-    Image& image() const { return m_image.get(); }
-
 #if PLATFORM(COCOA)
-    static RetainPtr<CFDataRef> tiffRepresentation(const Vector<Ref<NativeImage>>&);
+    WEBCORE_EXPORT static RetainPtr<CFDataRef> tiffRepresentation(const Vector<Ref<NativeImage>>&);
 #endif
 
+private:
+    Image& image() const { return m_image.get(); }
+
     RefPtr<NativeImage> nativeImageOfSize(const IntSize&);
     Vector<Ref<NativeImage>> allNativeImages();
 
diff --git a/Source/WebCore/platform/graphics/ImageUtilities.h b/Source/WebCore/platform/graphics/ImageUtilities.h
index 3b97113a8621..32799f29dc92 100644
--- a/Source/WebCore/platform/graphics/ImageUtilities.h
+++ b/Source/WebCore/platform/graphics/ImageUtilities.h
@@ -56,7 +56,8 @@ enum class ImageDecodingError : uint8_t {
 };
 WEBCORE_EXPORT String descriptionString(ImageDecodingError);
 WEBCORE_EXPORT Expected<std::pair<String, Vector<IntSize>>, ImageDecodingError> utiAndAvailableSizesFromImageData(std::span<const uint8_t>);
-WEBCORE_EXPORT void createIconDataFromImageData(std::span<const uint8_t> data, std::span<const unsigned> lengths, CompletionHandler<void(RefPtr<SharedBuffer>&&)>&&);
+WEBCORE_EXPORT void createBitmapsFromImageData(std::span<const uint8_t> data, std::span<const unsigned> lengths, CompletionHandler<void(Vector<Ref<ShareableBitmap>>&&)>&&);
+WEBCORE_EXPORT RefPtr<SharedBuffer> createIconDataFromBitmaps(Vector<Ref<ShareableBitmap>>&&);
 WEBCORE_EXPORT void decodeImageWithSize(std::span<const uint8_t> data, std::optional<FloatSize>, CompletionHandler<void(RefPtr<ShareableBitmap>&&)>&&);
 
 } // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/ShareableBitmap.cpp b/Source/WebCore/platform/graphics/ShareableBitmap.cpp
index 481100187fa9..a7006d0f1252 100644
--- a/Source/WebCore/platform/graphics/ShareableBitmap.cpp
+++ b/Source/WebCore/platform/graphics/ShareableBitmap.cpp
@@ -109,9 +109,14 @@ RefPtr<ShareableBitmap> ShareableBitmap::createFromImageDraw(NativeImage& image,
     return createFromImageDraw(image, colorSpace, image.size());
 }
 
-RefPtr<ShareableBitmap> ShareableBitmap::createFromImageDraw(NativeImage& image, const DestinationColorSpace& colorSpace, const IntSize& imageSize)
+RefPtr<ShareableBitmap> ShareableBitmap::createFromImageDraw(NativeImage& image, const DestinationColorSpace& colorSpace, const IntSize& destinationSize)
 {
-    auto bitmap = ShareableBitmap::create({ imageSize, colorSpace });
+    return createFromImageDraw(image, colorSpace, destinationSize, destinationSize);
+}
+
+RefPtr<ShareableBitmap> ShareableBitmap::createFromImageDraw(NativeImage& image, const DestinationColorSpace& colorSpace, const IntSize& destinationSize, const IntSize& sourceSize)
+{
+    auto bitmap = ShareableBitmap::create({ destinationSize, colorSpace });
     if (!bitmap)
         return nullptr;
 
@@ -119,7 +124,7 @@ RefPtr<ShareableBitmap> ShareableBitmap::createFromImageDraw(NativeImage& image,
     if (!context)
         return nullptr;
 
-    context->drawNativeImage(image, FloatRect({ }, imageSize), FloatRect({ }, imageSize), { CompositeOperator::Copy });
+    context->drawNativeImage(image, FloatRect({ }, destinationSize), FloatRect({ }, sourceSize), { CompositeOperator::Copy });
     return bitmap;
 }
 
diff --git a/Source/WebCore/platform/graphics/ShareableBitmap.h b/Source/WebCore/platform/graphics/ShareableBitmap.h
index ee6ed484d45b..d99e598719e8 100644
--- a/Source/WebCore/platform/graphics/ShareableBitmap.h
+++ b/Source/WebCore/platform/graphics/ShareableBitmap.h
@@ -141,6 +141,7 @@ class ShareableBitmap : public ThreadSafeRefCounted<ShareableBitmap> {
 #endif
     WEBCORE_EXPORT static RefPtr<ShareableBitmap> createFromImageDraw(NativeImage&, const DestinationColorSpace&);
     WEBCORE_EXPORT static RefPtr<ShareableBitmap> createFromImageDraw(NativeImage&, const DestinationColorSpace&, const IntSize&);
+    WEBCORE_EXPORT static RefPtr<ShareableBitmap> createFromImageDraw(NativeImage&, const DestinationColorSpace&, const IntSize& destinationSize, const IntSize& sourceSize);
 
     // Create a shareable bitmap from a handle.
     WEBCORE_EXPORT static RefPtr<ShareableBitmap> create(Handle&&, SharedMemory::Protection = SharedMemory::Protection::ReadWrite);
diff --git a/Source/WebCore/platform/graphics/cg/ImageUtilitiesCG.cpp b/Source/WebCore/platform/graphics/cg/ImageUtilitiesCG.cpp
index 59cd0497a9e5..8a80d5a88c01 100644
--- a/Source/WebCore/platform/graphics/cg/ImageUtilitiesCG.cpp
+++ b/Source/WebCore/platform/graphics/cg/ImageUtilitiesCG.cpp
@@ -206,82 +206,97 @@ static void tryCreateNativeImageFromData(std::span<const uint8_t> data, std::opt
     });
 }
 
-static RefPtr<SharedBuffer> expandNativeImageToData(NativeImage& image, ASCIILiteral uti, std::span<const unsigned> lengths)
+static Vector<Ref<ShareableBitmap>> createBitmapsFromNativeImage(NativeImage& image, std::span<const unsigned> lengths)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
+    Vector<Ref<ShareableBitmap>> bitmaps;
+    auto sourceColorSpace = image.colorSpace();
+    // The conversion could lead to loss of HDR contents.
+    auto destinationColorSpace = sourceColorSpace.supportsOutput() ? sourceColorSpace : DestinationColorSpace::SRGB();
     for (auto length : lengths) {
-        RetainPtr context = adoptCF(CGBitmapContextCreate(nullptr, length, length, 8, length * 4, colorSpace.get(), kCGImageAlphaPremultipliedLast));
-        if (!context)
-            return nullptr;
+        RefPtr bitmap = ShareableBitmap::createFromImageDraw(image, destinationColorSpace, { (int)length, (int)length }, image.size());
+        if (!bitmap)
+            return { };
 
-        // Quality is more important than rendering speed for the current use case.
-        CGContextSetInterpolationQuality(context.get(), kCGInterpolationHigh);
-        CGContextDrawImage(context.get(), CGRectMake(0, 0, length, length), image.platformImage().get());
-        RetainPtr newImage = adoptCF(CGBitmapContextCreateImage(context.get()));
-        if (!newImage)
-            return nullptr;
-
-        CGImageDestinationAddImage(destination.get(), newImage.get(), nullptr);
+        bitmaps.append(bitmap.releaseNonNull());
     }
 
-    if (!CGImageDestinationFinalize(destination.get()))
+    return bitmaps;
+}
+
+static RefPtr<NativeImage> createNativeImageFromSVGImage(SVGImage& image, const IntSize& size)
+{
+    RefPtr buffer = ImageBuffer::create(size, RenderingMode::Unaccelerated, RenderingPurpose::Unspecified, 1, DestinationColorSpace::SRGB(), ImageBufferPixelFormat::BGRA8);
+    if (!buffer)
         return nullptr;
 
-    return SharedBuffer::create(destinationData.get());
+    Ref svgImageContainer = SVGImageForContainer::create(&image, size, 1, { });
+    buffer->context().drawImage(svgImageContainer.get(), FloatPoint::zero());
+
+    return ImageBuffer::sinkIntoNativeImage(WTFMove(buffer));
 }
 
-static RefPtr<SharedBuffer> expandSVGImageToData(SVGImage& svgImage, ASCIILiteral uti, std::span<const unsigned> lengths)
+static Vector<Ref<ShareableBitmap>> createBitmapsFromSVGImage(SVGImage& image, std::span<const unsigned> lengths)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
+    Vector<Ref<ShareableBitmap>> bitmaps;
     for (auto length : lengths) {
-        auto size = FloatSize { float(length), float(length) };
-        RefPtr buffer = ImageBuffer::create(size, RenderingMode::Unaccelerated, RenderingPurpose::Unspecified, 1, DestinationColorSpace::SRGB(), ImageBufferPixelFormat::BGRA8);
-        if (!buffer)
-            return nullptr;
+        IntSize size { (int)length, (int)length };
+        RefPtr nativeImage = createNativeImageFromSVGImage(image, size);
+        if (!nativeImage)
+            return { };
 
-        Ref svgImageContainer = SVGImageForContainer::create(&svgImage, size, 1, { });
-        buffer->context().drawImage(svgImageContainer.get(), FloatPoint::zero());
-
-        RefPtr newImage = ImageBuffer::sinkIntoNativeImage(WTFMove(buffer));
-        if (!newImage)
-            return nullptr;
+        RefPtr bitmap = ShareableBitmap::createFromImageDraw(*nativeImage, DestinationColorSpace::SRGB());
+        if (!bitmap)
+            return { };
 
-        CGImageDestinationAddImage(destination.get(), newImage->platformImage().get(), nullptr);
+        bitmaps.append(bitmap.releaseNonNull());
     }
 
-    if (!CGImageDestinationFinalize(destination.get()))
-        return nullptr;
-
-    return SharedBuffer::create(destinationData.get());
+    return bitmaps;
 }
 
-void createIconDataFromImageData(std::span<const uint8_t> data, std::span<const unsigned> lengths, CompletionHandler<void(RefPtr<SharedBuffer>&&)>&& completionHandler)
+void createBitmapsFromImageData(std::span<const uint8_t> data, std::span<const unsigned> lengths, CompletionHandler<void(Vector<Ref<ShareableBitmap>>&&)>&& completionHandler)
 {
-    // Supported ICO image sizes by ImageIO.
-    constexpr std::array<unsigned, 5> availableLengths { { 16, 32, 48, 128, 256 } };
-    constexpr auto icoUTI = "com.microsoft.ico"_s;
-    auto targetLengths = lengths.empty() ? std::span { availableLengths } : lengths;
-
     if (RefPtr nativeImage = tryCreateNativeImageFromBitmapImageData(data, std::nullopt)) {
-        completionHandler(expandNativeImageToData(*nativeImage, icoUTI, targetLengths));
+        completionHandler(createBitmapsFromNativeImage(*nativeImage, lengths));
         return;
     }
 
-    SVGImage::tryCreateFromData(data, [icoUTI, targetLengthsVector = Vector<unsigned> { targetLengths }, completionHandler = WTFMove(completionHandler)](auto svgImage) mutable {
+    SVGImage::tryCreateFromData(data, [lengthsVector = Vector<unsigned> { lengths }, completionHandler = WTFMove(completionHandler)](auto svgImage) mutable {
         if (!svgImage) {
-            completionHandler(nullptr);
+            completionHandler({ });
             return;
         }
-        completionHandler(expandSVGImageToData(*svgImage, icoUTI, targetLengthsVector.span()));
+        completionHandler(createBitmapsFromSVGImage(*svgImage, lengthsVector.span()));
     });
 }
 
+RefPtr<SharedBuffer> createIconDataFromBitmaps(Vector<Ref<ShareableBitmap>>&& bitmaps)
+{
+    if (bitmaps.isEmpty())
+        return nullptr;
+
+    constexpr auto icoUTI = "com.microsoft.ico"_s;
+    RetainPtr cfUTI = icoUTI.createCFString();
+    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
+    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
+    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), bitmaps.size(), nullptr));
+
+    for (Ref bitmap : bitmaps) {
+        RetainPtr cgImage = bitmap->makeCGImageCopy();
+        if (!cgImage) {
+            RELEASE_LOG_ERROR(Images, "createIconDataFromBitmaps: Fails to create CGImage with size { %d , %d }", bitmap->size().width(), bitmap->size().height());
+            return nullptr;
+        }
+
+        CGImageDestinationAddImage(destination.get(), cgImage.get(), nullptr);
+    }
+
+    if (!CGImageDestinationFinalize(destination.get()))
+        return nullptr;
+
+    return SharedBuffer::create(destinationData.get());
+}
+
 // FIXME: This does not implement preferredSize for SVG at the moment as there are no callers that pass preferredSize.
 void decodeImageWithSize(std::span<const uint8_t> data, std::optional<FloatSize> preferredSize, CompletionHandler<void(RefPtr<ShareableBitmap>&&)>&& completionHandler)
 {
diff --git a/Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm b/Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm
index c3b7504b7283..2f0eea6cb3e2 100644
--- a/Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm
+++ b/Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm
@@ -1511,8 +1511,15 @@ static bool exceedsRenderTreeSizeSizeThreshold(uint64_t thresholdSize, uint64_t
 
 void WebPageProxy::createIconDataFromImageData(Ref<WebCore::SharedBuffer>&& buffer, const Vector<unsigned>& lengths, CompletionHandler<void(RefPtr<WebCore::SharedBuffer>&&)>&& completionHandler)
 {
-    ensureProtectedRunningProcess()->sendWithAsyncReply(Messages::WebPage::CreateIconDataFromImageData(WTFMove(buffer), lengths), [preventProcessShutdownScope = protectedLegacyMainFrameProcess()->shutdownPreventingScope(), completionHandler = WTFMove(completionHandler)] (auto result) mutable {
-        completionHandler(WTFMove(result));
+    // Supported ICO image sizes by ImageIO.
+    constexpr std::array<unsigned, 5> availableLengths { { 16, 32, 48, 128, 256 } };
+    auto targetLengths = lengths.isEmpty() ? std::span { availableLengths } : lengths;
+
+    ensureProtectedRunningProcess()->sendWithAsyncReply(Messages::WebPage::CreateBitmapsFromImageData(WTFMove(buffer), targetLengths), [preventProcessShutdownScope = protectedLegacyMainFrameProcess()->shutdownPreventingScope(), completionHandler = WTFMove(completionHandler)] (auto bitmaps) mutable {
+        if (bitmaps.isEmpty())
+            return completionHandler(nullptr);
+
+        completionHandler(createIconDataFromBitmaps(WTFMove(bitmaps)));
     }, webPageIDInMainFrameProcess());
 }
 
diff --git a/Source/WebKit/WebProcess/WebPage/Cocoa/WebPageCocoa.mm b/Source/WebKit/WebProcess/WebPage/Cocoa/WebPageCocoa.mm
index bad7609883da..8f32dd40ab66 100644
--- a/Source/WebKit/WebProcess/WebPage/Cocoa/WebPageCocoa.mm
+++ b/Source/WebKit/WebProcess/WebPage/Cocoa/WebPageCocoa.mm
@@ -1197,9 +1197,9 @@ static bool selectionIsTransparentOrFullyClipped(const VisibleSelection& selecti
     completionHandler(textIndicator->data());
 }
 
-void WebPage::createIconDataFromImageData(Ref<WebCore::SharedBuffer>&& buffer, const Vector<unsigned>& lengths, CompletionHandler<void(RefPtr<WebCore::SharedBuffer>&&)>&& completionHandler)
+void WebPage::createBitmapsFromImageData(Ref<WebCore::SharedBuffer>&& buffer, const Vector<unsigned>& lengths, CompletionHandler<void(Vector<Ref<WebCore::ShareableBitmap>>&&)>&& completionHandler)
 {
-    WebCore::createIconDataFromImageData(buffer->span(), lengths.span(), WTFMove(completionHandler));
+    WebCore::createBitmapsFromImageData(buffer->span(), lengths.span(), WTFMove(completionHandler));
 }
 
 void WebPage::decodeImageData(Ref<WebCore::SharedBuffer>&& buffer, std::optional<WebCore::FloatSize> preferredSize, CompletionHandler<void(RefPtr<WebCore::ShareableBitmap>&&)>&& completionHandler)
diff --git a/Source/WebKit/WebProcess/WebPage/WebPage.h b/Source/WebKit/WebProcess/WebPage/WebPage.h
index c57088d98e0f..31f784d93177 100644
--- a/Source/WebKit/WebProcess/WebPage/WebPage.h
+++ b/Source/WebKit/WebProcess/WebPage/WebPage.h
@@ -1592,7 +1592,7 @@ class WebPage final : public API::ObjectImpl<API::Object::Type::BundlePage>, pub
     void loadAndDecodeImage(WebCore::ResourceRequest&&, std::optional<WebCore::FloatSize> sizeConstraint, size_t, CompletionHandler<void(Expected<Ref<WebCore::ShareableBitmap>, WebCore::ResourceError>&&)>&&);
 #if PLATFORM(COCOA)
     void getInformationFromImageData(const Vector<uint8_t>&, CompletionHandler<void(Expected<std::pair<String, Vector<WebCore::IntSize>>, WebCore::ImageDecodingError>&&)>&&);
-    void createIconDataFromImageData(Ref<WebCore::SharedBuffer>&&, const Vector<unsigned>&, CompletionHandler<void(RefPtr<WebCore::SharedBuffer>&&)>&&);
+    void createBitmapsFromImageData(Ref<WebCore::SharedBuffer>&&, const Vector<unsigned>&, CompletionHandler<void(Vector<Ref<WebCore::ShareableBitmap>>&&)>&&);
     void decodeImageData(Ref<WebCore::SharedBuffer>&&, std::optional<WebCore::FloatSize>, CompletionHandler<void(RefPtr<WebCore::ShareableBitmap>&&)>&&);
 #endif
 
diff --git a/Source/WebKit/WebProcess/WebPage/WebPage.messages.in b/Source/WebKit/WebProcess/WebPage/WebPage.messages.in
index 98af0cbe1050..4e060a2a3f02 100644
--- a/Source/WebKit/WebProcess/WebPage/WebPage.messages.in
+++ b/Source/WebKit/WebProcess/WebPage/WebPage.messages.in
@@ -855,7 +855,7 @@ messages -> WebPage WantsAsyncDispatchMessage {
     LoadAndDecodeImage(WebCore::ResourceRequest request, std::optional<WebCore::FloatSize> sizeConstraint, size_t maximumBytesFromNetwork) -> (Expected<Ref<WebCore::ShareableBitmap>, WebCore::ResourceError> result)
 #if PLATFORM(COCOA)
     GetInformationFromImageData(Vector<uint8_t> imageData) -> (Expected<std::pair<String, Vector<WebCore::IntSize>>, WebCore::ImageDecodingError> result)
-    CreateIconDataFromImageData(Ref<WebCore::SharedBuffer> imageData, Vector<unsigned> lengths) -> (RefPtr<WebCore::SharedBuffer> result)
+    CreateBitmapsFromImageData(Ref<WebCore::SharedBuffer> imageData, Vector<unsigned> lengths) -> ( Vector<Ref<WebCore::ShareableBitmap>> result);
     DecodeImageData(Ref<WebCore::SharedBuffer> imageData, std::optional<WebCore::FloatSize> preferredSize) -> (RefPtr<WebCore::ShareableBitmap> result)
 #endif
 
diff --git a/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj b/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
index 4f7014ef82f8..cd75b8524a3e 100644
--- a/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
+++ b/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
@@ -167,6 +167,8 @@
 		3A9C438828E3E057002F7294 /* ASTStringDumperTests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3A9C438028E3E057002F7294 /* ASTStringDumperTests.cpp */; };
 		3A9C438B28E3E240002F7294 /* TestWGSLAPI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3A9C438928E3E240002F7294 /* TestWGSLAPI.cpp */; };
 		3ADD8988299F192F005DCE4E /* MetalGenerationTests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3ADD897E299F1400005DCE4E /* MetalGenerationTests.cpp */; };
+		3AF4A0952D9DEE0B00B3FF5E /* icon-svg-16.tiff in Copy Resources */ = {isa = PBXBuildFile; fileRef = 3AF4A0932D9DEE0B00B3FF5E /* icon-svg-16.tiff */; };
+		3AF4A0962D9DEE0B00B3FF5E /* icon-svg-256.tiff in Copy Resources */ = {isa = PBXBuildFile; fileRef = 3AF4A0942D9DEE0B00B3FF5E /* icon-svg-256.tiff */; };
 		4102EE1727845ED500D6BE74 /* ServiceWorkerRoutines.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4102EE1627845ED500D6BE74 /* ServiceWorkerRoutines.cpp */; };
 		411204CE2D3AAD8E009A8554 /* SharedVideoFrame.mm in Sources */ = {isa = PBXBuildFile; fileRef = 411204CD2D3AAD8E009A8554 /* SharedVideoFrame.mm */; };
 		411223C726035FBF00B0A0B6 /* WebRTC.mm in Sources */ = {isa = PBXBuildFile; fileRef = 411223C626035FBE00B0A0B6 /* WebRTC.mm */; };
@@ -1831,6 +1833,8 @@
 				A17C47792C98E5C20023F3C7 /* Helvetica_light.svg in Copy Resources */,
 				A17C46772C98E4D20023F3C7 /* HTMLCollectionNamedItem.html in Copy Resources */,
 				A17C46782C98E4D20023F3C7 /* HTMLFormCollectionNamedItem.html in Copy Resources */,
+				3AF4A0952D9DEE0B00B3FF5E /* icon-svg-16.tiff in Copy Resources */,
+				3AF4A0962D9DEE0B00B3FF5E /* icon-svg-256.tiff in Copy Resources */,
 				D2D9A6BE2D96B539004A2C92 /* icon-with-subresource.svg in Copy Resources */,
 				A17C46C12C98E54B0023F3C7 /* icon.png in Copy Resources */,
 				A17C46C12C98E54B0023F3C8 /* icon.svg in Copy Resources */,
@@ -2592,6 +2596,8 @@
 		3AA7856029B6C8AA00C4374A /* TestWebKitAPIPrefix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestWebKitAPIPrefix.h; sourceTree = "<group>"; };
 		3ADD897E299F1400005DCE4E /* MetalGenerationTests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MetalGenerationTests.cpp; sourceTree = "<group>"; };
 		3AEA55B22CB0897200A9ECAA /* WKPreferences.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = WKPreferences.mm; sourceTree = "<group>"; };
+		3AF4A0932D9DEE0B00B3FF5E /* icon-svg-16.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "icon-svg-16.tiff"; sourceTree = "<group>"; };
+		3AF4A0942D9DEE0B00B3FF5E /* icon-svg-256.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "icon-svg-256.tiff"; sourceTree = "<group>"; };
 		3F1B52681D3D7129008D60C4 /* FullscreenLayoutConstraints.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FullscreenLayoutConstraints.mm; sourceTree = "<group>"; };
 		3FBD1B491D39D1DB00E6D6FA /* FullscreenLayoutConstraints.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = FullscreenLayoutConstraints.html; sourceTree = "<group>"; };
 		3FCC4FE41EC4E8520076E37C /* PictureInPictureDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PictureInPictureDelegate.mm; sourceTree = "<group>"; };
@@ -5982,6 +5988,8 @@
 				41BAF4E225AC9DB800D82F32 /* getUserMedia2.html */,
 				4A410F4D19AF7BEF002EBAC5 /* getUserMediaAudioVideoCapture.html */,
 				4198524F27AD7B70005477B7 /* getUserMediaPermission.html */,
+				3AF4A0932D9DEE0B00B3FF5E /* icon-svg-16.tiff */,
+				3AF4A0942D9DEE0B00B3FF5E /* icon-svg-256.tiff */,
 				D2D9A6BD2D96B539004A2C92 /* icon-with-subresource.svg */,
 				BCBD372E125ABBE600D2C29F /* icon.png */,
 				BCBD372E125ABBE600D2C2AF /* icon.svg */,
diff --git a/Tools/TestWebKitAPI/Tests/WebKit/icon-svg-16.tiff b/Tools/TestWebKitAPI/Tests/WebKit/icon-svg-16.tiff
new file mode 100644
index 000000000000..82ce7c03b7d8
Binary files /dev/null and b/Tools/TestWebKitAPI/Tests/WebKit/icon-svg-16.tiff differ
diff --git a/Tools/TestWebKitAPI/Tests/WebKit/icon-svg-256.tiff b/Tools/TestWebKitAPI/Tests/WebKit/icon-svg-256.tiff
new file mode 100644
index 000000000000..dfac92b51212
Binary files /dev/null and b/Tools/TestWebKitAPI/Tests/WebKit/icon-svg-256.tiff differ
diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/LoadAndDecodeImage.mm b/Tools/TestWebKitAPI/Tests/WebKitCocoa/LoadAndDecodeImage.mm
index 0ab7d2fb2162..b9cc24f57760 100644
--- a/Tools/TestWebKitAPI/Tests/WebKitCocoa/LoadAndDecodeImage.mm
+++ b/Tools/TestWebKitAPI/Tests/WebKitCocoa/LoadAndDecodeImage.mm
@@ -32,9 +32,12 @@
 #import "TestNavigationDelegate.h"
 #import "TestWKWebView.h"
 #import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
+#import <WebCore/Image.h>
+#import <WebCore/NativeImage.h>
 #import <WebKit/WKWebViewPrivate.h>
 #import <wtf/Expected.h>
 #import <wtf/RetainPtr.h>
+#import <wtf/cocoa/TypeCastsCocoa.h>
 #import <wtf/cocoa/VectorCocoa.h>
 #import <wtf/text/Base64.h>
 
@@ -266,10 +269,30 @@
     Util::run(&done);
 }
 
+RetainPtr<NSData> tiffRepresentation(CocoaImage *image)
+{
+#if USE(APPKIT)
+    return [image TIFFRepresentation];
+#else
+    RetainPtr cgImage = [image CGImage];
+    if (!cgImage)
+        return nullptr;
+
+    RefPtr nativeImage = WebCore::NativeImage::create(WTFMove(cgImage));
+    if (!nativeImage)
+        return nullptr;
+
+    Ref nativeImageRef { nativeImage.releaseNonNull() };
+    return bridge_cast(WebCore::ImageAdapter::tiffRepresentation({ nativeImageRef }));
+#endif
+}
+
 TEST(WebKit, CreateIconDataFromImageDataSVG)
 {
     RetainPtr webView = adoptNS([WKWebView new]);
     RetainPtr imageData = [NSData dataWithContentsOfFile:[NSBundle.test_resourcesBundle pathForResource:@"icon" ofType:@"svg"]];
+    RetainPtr expectedIconData16 = [NSData dataWithContentsOfFile:[NSBundle.test_resourcesBundle pathForResource:@"icon-svg-16" ofType:@"tiff"]];
+    RetainPtr expectedIconData256 = [NSData dataWithContentsOfFile:[NSBundle.test_resourcesBundle pathForResource:@"icon-svg-256" ofType:@"tiff"]];
     RetainPtr length1 = [NSNumber numberWithUnsignedInt:16];
     RetainPtr length2 = [NSNumber numberWithUnsignedInt:256];
     NSArray *lengths = @[length1.get(), length2.get()];
@@ -289,6 +312,7 @@
         EXPECT_NOT_NULL(result);
         EXPECT_EQ(result.size.width, 256);
         EXPECT_EQ(result.size.height, 256);
+        EXPECT_TRUE([tiffRepresentation(result) isEqualToData:expectedIconData256.get()]);
         done = true;
     }];
     Util::run(&done);
@@ -299,6 +323,7 @@
         EXPECT_NOT_NULL(result);
         EXPECT_EQ(result.size.width, 16);
         EXPECT_EQ(result.size.height, 16);
+        EXPECT_TRUE([tiffRepresentation(result) isEqualToData:expectedIconData16.get()]);
         done = true;
     }];
     Util::run(&done);
@@ -309,6 +334,7 @@
         EXPECT_NOT_NULL(result);
         EXPECT_EQ(result.size.width, 256);
         EXPECT_EQ(result.size.height, 256);
+        EXPECT_TRUE([tiffRepresentation(result) isEqualToData:expectedIconData256.get()]);
         done = true;
     }];
     Util::run(&done);


Reviews:
user: login: annevk
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T16:48:31Z
user: login: szewai
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T22:57:59Z
user: login: annevk
body: This looks good to me. Ideally @shallawa takes a look as well though.

Unfortunately I'm not really sure how to make the new tests cross-platform, but I suppose worst case we could only run those asserts on macOS.
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-03T07:06:57Z
user: login: sheeparegreat
body: Looks good to me. Return flow type is `Vector<RefPtr<ShareableBitmap>>`
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-03T18:43:15Z
user: login: shallawa
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T17:39:47Z
user: login: szewai
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-05T17:01:51Z
user: login: szewai
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-05T17:13:29Z
user: login: szewai
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-05T17:15:25Z
user: login: szewai
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-05T17:18:38Z
user: login: shallawa
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T17:06:07Z
user: login: szewai
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T19:09:01Z
user: login: shallawa
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T21:02:15Z

Review comments:
diff_hunk: @@ -206,82 +206,87 @@ static void tryCreateNativeImageFromData(std::span<const uint8_t> data, std::opt
     });
 }
 
-static RefPtr<SharedBuffer> expandNativeImageToData(NativeImage& image, ASCIILiteral uti, std::span<const unsigned> lengths)
+static Vector<RefPtr<ShareableBitmap>> getBitmapsFromNativeImage(NativeImage& image, std::span<const unsigned> lengths)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
+    Vector<RefPtr<ShareableBitmap>> bitmaps;
+    auto sourceColorSpace = image.colorSpace();
+    auto destinationColorSpace = sourceColorSpace.supportsOutput() ? sourceColorSpace : DestinationColorSpace::SRGB();
     for (auto length : lengths) {
-        RetainPtr context = adoptCF(CGBitmapContextCreate(nullptr, length, length, 8, length * 4, colorSpace.get(), kCGImageAlphaPremultipliedLast));
-        if (!context)
-            return nullptr;
-
-        // Quality is more important than rendering speed for the current use case.
-        CGContextSetInterpolationQuality(context.get(), kCGInterpolationHigh);
-        CGContextDrawImage(context.get(), CGRectMake(0, 0, length, length), image.platformImage().get());
-        RetainPtr newImage = adoptCF(CGBitmapContextCreateImage(context.get()));
-        if (!newImage)
-            return nullptr;
-
-        CGImageDestinationAddImage(destination.get(), newImage.get(), nullptr);
+        if (RefPtr bitmap = ShareableBitmap::createFromImageDraw(image, destinationColorSpace, { (int)length, (int)length }, ShareableBitmap::ShouldShrinkToFit::Yes)) {
+            bitmaps.append(WTFMove(bitmap));
+            continue;
+        }
+        return { };
     }
 
-    if (!CGImageDestinationFinalize(destination.get()))
-        return nullptr;
-
-    return SharedBuffer::create(destinationData.get());
+    return bitmaps;
 }
 
-static RefPtr<SharedBuffer> expandSVGImageToData(SVGImage& svgImage, ASCIILiteral uti, std::span<const unsigned> lengths)
+static Vector<RefPtr<ShareableBitmap>> getBitmapsFromSVGImage(SVGImage& image, std::span<const unsigned> lengths)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
+    Vector<RefPtr<ShareableBitmap>> bitmaps;
     for (auto length : lengths) {
-        auto size = FloatSize { float(length), float(length) };
-        RefPtr buffer = ImageBuffer::create(size, RenderingMode::Unaccelerated, RenderingPurpose::Unspecified, 1, DestinationColorSpace::SRGB(), ImageBufferPixelFormat::BGRA8);
-        if (!buffer)
-            return nullptr;
-
-        Ref svgImageContainer = SVGImageForContainer::create(&svgImage, size, 1, { });
-        buffer->context().drawImage(svgImageContainer.get(), FloatPoint::zero());
-
-        RefPtr newImage = ImageBuffer::sinkIntoNativeImage(WTFMove(buffer));
-        if (!newImage)
-            return nullptr;
-
-        CGImageDestinationAddImage(destination.get(), newImage->platformImage().get(), nullptr);
+        IntSize size { (int)length, (int)length };
+        if (RefPtr nativeImage = image.nativeImage(image.size())) {
+            if (RefPtr bitmap = ShareableBitmap::createFromImageDraw(*nativeImage, DestinationColorSpace::SRGB(), size, ShareableBitmap::ShouldShrinkToFit::Yes)) {
path: Source/WebCore/platform/graphics/cg/ImageUtilitiesCG.cpp
user: login: annevk
body: Unless I'm missing something this refactor moves from vector scaling to bitmap scaling. We should probably continue to use `SVGImageForContainer`. Anyone ideas for how to add test coverage for that?
created_at: 2025-04-02T16:48:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -56,7 +56,8 @@ enum class ImageDecodingError : uint8_t {
 };
 WEBCORE_EXPORT String descriptionString(ImageDecodingError);
 WEBCORE_EXPORT Expected<std::pair<String, Vector<IntSize>>, ImageDecodingError> utiAndAvailableSizesFromImageData(std::span<const uint8_t>);
-WEBCORE_EXPORT void createIconDataFromImageData(std::span<const uint8_t> data, std::span<const unsigned> lengths, CompletionHandler<void(RefPtr<SharedBuffer>&&)>&&);
+WEBCORE_EXPORT void getBitmapsForIconFromImageData(std::span<const uint8_t> data, std::span<const unsigned> lengths, CompletionHandler<void(Vector<RefPtr<ShareableBitmap>>&&)>&&);
path: Source/WebCore/platform/graphics/ImageUtilities.h
user: login: shallawa
body: How about `createBitmapsFromImageData()`?

1. We do not like to use the verb `get` in the function names. Besides `create` means you are returning something that does not exist.
2. The function is creating different `ShareableBitmaps` form different lengths. It should not be specific for`Icon`.
created_at: 2025-04-02T22:18:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -109,7 +109,7 @@ RefPtr<ShareableBitmap> ShareableBitmap::createFromImageDraw(NativeImage& image,
     return createFromImageDraw(image, colorSpace, image.size());
 }
 
-RefPtr<ShareableBitmap> ShareableBitmap::createFromImageDraw(NativeImage& image, const DestinationColorSpace& colorSpace, const IntSize& imageSize)
+RefPtr<ShareableBitmap> ShareableBitmap::createFromImageDraw(NativeImage& image, const DestinationColorSpace& colorSpace, const IntSize& imageSize, ShouldShrinkToFit shouldShrinkToFit)
path: Source/WebCore/platform/graphics/ShareableBitmap.cpp
user: login: shallawa
body: I do not think this `shouldShrinkToFit ` is needed. The caller can just call the above function:

```
RefPtr<ShareableBitmap> ShareableBitmap::createFromImageDraw(NativeImage& image, const DestinationColorSpace& colorSpace)
{
    return createFromImageDraw(image, colorSpace, image.size());
}
```
It will do the same thing.
created_at: 2025-04-02T22:29:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -206,82 +206,87 @@ static void tryCreateNativeImageFromData(std::span<const uint8_t> data, std::opt
     });
 }
 
-static RefPtr<SharedBuffer> expandNativeImageToData(NativeImage& image, ASCIILiteral uti, std::span<const unsigned> lengths)
+static Vector<RefPtr<ShareableBitmap>> getBitmapsFromNativeImage(NativeImage& image, std::span<const unsigned> lengths)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
+    Vector<RefPtr<ShareableBitmap>> bitmaps;
+    auto sourceColorSpace = image.colorSpace();
+    auto destinationColorSpace = sourceColorSpace.supportsOutput() ? sourceColorSpace : DestinationColorSpace::SRGB();
     for (auto length : lengths) {
-        RetainPtr context = adoptCF(CGBitmapContextCreate(nullptr, length, length, 8, length * 4, colorSpace.get(), kCGImageAlphaPremultipliedLast));
-        if (!context)
-            return nullptr;
-
-        // Quality is more important than rendering speed for the current use case.
-        CGContextSetInterpolationQuality(context.get(), kCGInterpolationHigh);
-        CGContextDrawImage(context.get(), CGRectMake(0, 0, length, length), image.platformImage().get());
-        RetainPtr newImage = adoptCF(CGBitmapContextCreateImage(context.get()));
-        if (!newImage)
-            return nullptr;
-
-        CGImageDestinationAddImage(destination.get(), newImage.get(), nullptr);
+        if (RefPtr bitmap = ShareableBitmap::createFromImageDraw(image, destinationColorSpace, { (int)length, (int)length }, ShareableBitmap::ShouldShrinkToFit::Yes)) {
+            bitmaps.append(WTFMove(bitmap));
+            continue;
+        }
+        return { };
     }
 
-    if (!CGImageDestinationFinalize(destination.get()))
-        return nullptr;
-
-    return SharedBuffer::create(destinationData.get());
+    return bitmaps;
 }
 
-static RefPtr<SharedBuffer> expandSVGImageToData(SVGImage& svgImage, ASCIILiteral uti, std::span<const unsigned> lengths)
+static Vector<RefPtr<ShareableBitmap>> getBitmapsFromSVGImage(SVGImage& image, std::span<const unsigned> lengths)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
+    Vector<RefPtr<ShareableBitmap>> bitmaps;
     for (auto length : lengths) {
-        auto size = FloatSize { float(length), float(length) };
-        RefPtr buffer = ImageBuffer::create(size, RenderingMode::Unaccelerated, RenderingPurpose::Unspecified, 1, DestinationColorSpace::SRGB(), ImageBufferPixelFormat::BGRA8);
-        if (!buffer)
-            return nullptr;
-
-        Ref svgImageContainer = SVGImageForContainer::create(&svgImage, size, 1, { });
-        buffer->context().drawImage(svgImageContainer.get(), FloatPoint::zero());
-
-        RefPtr newImage = ImageBuffer::sinkIntoNativeImage(WTFMove(buffer));
-        if (!newImage)
-            return nullptr;
-
-        CGImageDestinationAddImage(destination.get(), newImage->platformImage().get(), nullptr);
+        IntSize size { (int)length, (int)length };
+        if (RefPtr nativeImage = image.nativeImage(image.size())) {
+            if (RefPtr bitmap = ShareableBitmap::createFromImageDraw(*nativeImage, DestinationColorSpace::SRGB(), size, ShareableBitmap::ShouldShrinkToFit::Yes)) {
path: Source/WebCore/platform/graphics/cg/ImageUtilitiesCG.cpp
user: login: szewai
body: I updated the patch to use `SVGImageForContainer` (in new function `nativeImageFromSVGImage`).
Also updated the test to ensure image data is expected.
created_at: 2025-04-02T22:57:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -206,82 +206,99 @@ static void tryCreateNativeImageFromData(std::span<const uint8_t> data, std::opt
     });
 }
 
-static RefPtr<SharedBuffer> expandNativeImageToData(NativeImage& image, ASCIILiteral uti, std::span<const unsigned> lengths)
+static Vector<RefPtr<ShareableBitmap>> bitmapsFromNativeImage(NativeImage& image, std::span<const unsigned> lengths)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
+    Vector<RefPtr<ShareableBitmap>> bitmaps;
+    auto sourceColorSpace = image.colorSpace();
+    auto destinationColorSpace = sourceColorSpace.supportsOutput() ? sourceColorSpace : DestinationColorSpace::SRGB();
     for (auto length : lengths) {
-        RetainPtr context = adoptCF(CGBitmapContextCreate(nullptr, length, length, 8, length * 4, colorSpace.get(), kCGImageAlphaPremultipliedLast));
-        if (!context)
-            return nullptr;
-
-        // Quality is more important than rendering speed for the current use case.
-        CGContextSetInterpolationQuality(context.get(), kCGInterpolationHigh);
-        CGContextDrawImage(context.get(), CGRectMake(0, 0, length, length), image.platformImage().get());
-        RetainPtr newImage = adoptCF(CGBitmapContextCreateImage(context.get()));
-        if (!newImage)
-            return nullptr;
-
-        CGImageDestinationAddImage(destination.get(), newImage.get(), nullptr);
+        if (RefPtr bitmap = ShareableBitmap::createFromImageDraw(image, destinationColorSpace, { (int)length, (int)length }, ShareableBitmap::ShouldShrinkToFit::Yes)) {
+            bitmaps.append(WTFMove(bitmap));
+            continue;
+        }
+        return { };
     }
 
-    if (!CGImageDestinationFinalize(destination.get()))
-        return nullptr;
-
-    return SharedBuffer::create(destinationData.get());
+    return bitmaps;
 }
 
-static RefPtr<SharedBuffer> expandSVGImageToData(SVGImage& svgImage, ASCIILiteral uti, std::span<const unsigned> lengths)
+static RefPtr<NativeImage> nativeImageFromSVGImage(SVGImage& image, unsigned length)
path: Source/WebCore/platform/graphics/cg/ImageUtilitiesCG.cpp
user: login: annevk
body: I wonder if we want to be clearer here that this always returns a square/icon. But since it's a static and pretty obvious it doesn't matter too much I think.
created_at: 2025-04-03T07:06:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -206,82 +206,99 @@ static void tryCreateNativeImageFromData(std::span<const uint8_t> data, std::opt
     });
 }
 
-static RefPtr<SharedBuffer> expandNativeImageToData(NativeImage& image, ASCIILiteral uti, std::span<const unsigned> lengths)
+static Vector<RefPtr<ShareableBitmap>> bitmapsFromNativeImage(NativeImage& image, std::span<const unsigned> lengths)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
+    Vector<RefPtr<ShareableBitmap>> bitmaps;
+    auto sourceColorSpace = image.colorSpace();
+    auto destinationColorSpace = sourceColorSpace.supportsOutput() ? sourceColorSpace : DestinationColorSpace::SRGB();
path: Source/WebCore/platform/graphics/cg/ImageUtilitiesCG.cpp
user: login: shallawa
body: If the NativeImage is HDR and its colorspace does not support output, the HDR contents will be lost by choosing SRGB.
created_at: 2025-04-04T16:06:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -206,82 +206,99 @@ static void tryCreateNativeImageFromData(std::span<const uint8_t> data, std::opt
     });
 }
 
-static RefPtr<SharedBuffer> expandNativeImageToData(NativeImage& image, ASCIILiteral uti, std::span<const unsigned> lengths)
+static Vector<RefPtr<ShareableBitmap>> bitmapsFromNativeImage(NativeImage& image, std::span<const unsigned> lengths)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
+    Vector<RefPtr<ShareableBitmap>> bitmaps;
+    auto sourceColorSpace = image.colorSpace();
+    auto destinationColorSpace = sourceColorSpace.supportsOutput() ? sourceColorSpace : DestinationColorSpace::SRGB();
     for (auto length : lengths) {
-        RetainPtr context = adoptCF(CGBitmapContextCreate(nullptr, length, length, 8, length * 4, colorSpace.get(), kCGImageAlphaPremultipliedLast));
-        if (!context)
-            return nullptr;
-
-        // Quality is more important than rendering speed for the current use case.
-        CGContextSetInterpolationQuality(context.get(), kCGInterpolationHigh);
-        CGContextDrawImage(context.get(), CGRectMake(0, 0, length, length), image.platformImage().get());
-        RetainPtr newImage = adoptCF(CGBitmapContextCreateImage(context.get()));
-        if (!newImage)
-            return nullptr;
-
-        CGImageDestinationAddImage(destination.get(), newImage.get(), nullptr);
+        if (RefPtr bitmap = ShareableBitmap::createFromImageDraw(image, destinationColorSpace, { (int)length, (int)length }, ShareableBitmap::ShouldShrinkToFit::Yes)) {
path: Source/WebCore/platform/graphics/cg/ImageUtilitiesCG.cpp
user: login: shallawa
body: Again this interface is a little bit weird. You are passing the target size of the new image but you are telling `ShareableBitmap::createFromImageDraw()` also to use this size not the image size.
created_at: 2025-04-04T16:09:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -206,82 +206,99 @@ static void tryCreateNativeImageFromData(std::span<const uint8_t> data, std::opt
     });
 }
 
-static RefPtr<SharedBuffer> expandNativeImageToData(NativeImage& image, ASCIILiteral uti, std::span<const unsigned> lengths)
+static Vector<RefPtr<ShareableBitmap>> bitmapsFromNativeImage(NativeImage& image, std::span<const unsigned> lengths)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
+    Vector<RefPtr<ShareableBitmap>> bitmaps;
+    auto sourceColorSpace = image.colorSpace();
+    auto destinationColorSpace = sourceColorSpace.supportsOutput() ? sourceColorSpace : DestinationColorSpace::SRGB();
     for (auto length : lengths) {
-        RetainPtr context = adoptCF(CGBitmapContextCreate(nullptr, length, length, 8, length * 4, colorSpace.get(), kCGImageAlphaPremultipliedLast));
-        if (!context)
-            return nullptr;
-
-        // Quality is more important than rendering speed for the current use case.
-        CGContextSetInterpolationQuality(context.get(), kCGInterpolationHigh);
-        CGContextDrawImage(context.get(), CGRectMake(0, 0, length, length), image.platformImage().get());
-        RetainPtr newImage = adoptCF(CGBitmapContextCreateImage(context.get()));
-        if (!newImage)
-            return nullptr;
-
-        CGImageDestinationAddImage(destination.get(), newImage.get(), nullptr);
+        if (RefPtr bitmap = ShareableBitmap::createFromImageDraw(image, destinationColorSpace, { (int)length, (int)length }, ShareableBitmap::ShouldShrinkToFit::Yes)) {
+            bitmaps.append(WTFMove(bitmap));
+            continue;
+        }
+        return { };
     }
 
-    if (!CGImageDestinationFinalize(destination.get()))
-        return nullptr;
-
-    return SharedBuffer::create(destinationData.get());
+    return bitmaps;
 }
 
-static RefPtr<SharedBuffer> expandSVGImageToData(SVGImage& svgImage, ASCIILiteral uti, std::span<const unsigned> lengths)
+static RefPtr<NativeImage> nativeImageFromSVGImage(SVGImage& image, unsigned length)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
-    for (auto length : lengths) {
-        auto size = FloatSize { float(length), float(length) };
-        RefPtr buffer = ImageBuffer::create(size, RenderingMode::Unaccelerated, RenderingPurpose::Unspecified, 1, DestinationColorSpace::SRGB(), ImageBufferPixelFormat::BGRA8);
-        if (!buffer)
-            return nullptr;
+    FloatSize size { float(length), float(length) };
path: Source/WebCore/platform/graphics/cg/ImageUtilitiesCG.cpp
user: login: shallawa
body: I think there are several ways to avoid this casting:
```
FloatSize size(length, length);
IntSize size(length, length); //  ImageBuffer::create() will do the casting from IntSize to FloatSize
FloatSize size { IntSize { length, length } };
```
created_at: 2025-04-04T16:32:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -206,82 +206,99 @@ static void tryCreateNativeImageFromData(std::span<const uint8_t> data, std::opt
     });
 }
 
-static RefPtr<SharedBuffer> expandNativeImageToData(NativeImage& image, ASCIILiteral uti, std::span<const unsigned> lengths)
+static Vector<RefPtr<ShareableBitmap>> bitmapsFromNativeImage(NativeImage& image, std::span<const unsigned> lengths)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
+    Vector<RefPtr<ShareableBitmap>> bitmaps;
+    auto sourceColorSpace = image.colorSpace();
+    auto destinationColorSpace = sourceColorSpace.supportsOutput() ? sourceColorSpace : DestinationColorSpace::SRGB();
     for (auto length : lengths) {
-        RetainPtr context = adoptCF(CGBitmapContextCreate(nullptr, length, length, 8, length * 4, colorSpace.get(), kCGImageAlphaPremultipliedLast));
-        if (!context)
-            return nullptr;
-
-        // Quality is more important than rendering speed for the current use case.
-        CGContextSetInterpolationQuality(context.get(), kCGInterpolationHigh);
-        CGContextDrawImage(context.get(), CGRectMake(0, 0, length, length), image.platformImage().get());
-        RetainPtr newImage = adoptCF(CGBitmapContextCreateImage(context.get()));
-        if (!newImage)
-            return nullptr;
-
-        CGImageDestinationAddImage(destination.get(), newImage.get(), nullptr);
+        if (RefPtr bitmap = ShareableBitmap::createFromImageDraw(image, destinationColorSpace, { (int)length, (int)length }, ShareableBitmap::ShouldShrinkToFit::Yes)) {
+            bitmaps.append(WTFMove(bitmap));
+            continue;
+        }
+        return { };
     }
 
-    if (!CGImageDestinationFinalize(destination.get()))
-        return nullptr;
-
-    return SharedBuffer::create(destinationData.get());
+    return bitmaps;
 }
 
-static RefPtr<SharedBuffer> expandSVGImageToData(SVGImage& svgImage, ASCIILiteral uti, std::span<const unsigned> lengths)
+static RefPtr<NativeImage> nativeImageFromSVGImage(SVGImage& image, unsigned length)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
-    for (auto length : lengths) {
-        auto size = FloatSize { float(length), float(length) };
-        RefPtr buffer = ImageBuffer::create(size, RenderingMode::Unaccelerated, RenderingPurpose::Unspecified, 1, DestinationColorSpace::SRGB(), ImageBufferPixelFormat::BGRA8);
-        if (!buffer)
-            return nullptr;
+    FloatSize size { float(length), float(length) };
+    RefPtr buffer = ImageBuffer::create(size, RenderingMode::Unaccelerated, RenderingPurpose::Unspecified, 1, DestinationColorSpace::SRGB(), ImageBufferPixelFormat::BGRA8);
path: Source/WebCore/platform/graphics/cg/ImageUtilitiesCG.cpp
user: login: shallawa
body: Using SRGB will remove all HDR contents if they exist. This is not specific to this patch. SRGB is used everywhere and we should revisit of all of them. So you not have to fix it in this PR.
created_at: 2025-04-04T16:36:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -206,82 +206,99 @@ static void tryCreateNativeImageFromData(std::span<const uint8_t> data, std::opt
     });
 }
 
-static RefPtr<SharedBuffer> expandNativeImageToData(NativeImage& image, ASCIILiteral uti, std::span<const unsigned> lengths)
+static Vector<RefPtr<ShareableBitmap>> bitmapsFromNativeImage(NativeImage& image, std::span<const unsigned> lengths)
path: Source/WebCore/platform/graphics/cg/ImageUtilitiesCG.cpp
user: login: shallawa
body: `bitmapsFromNativeImage` -> `createBitmapsFromNativeImage`?
created_at: 2025-04-04T16:37:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -206,82 +206,99 @@ static void tryCreateNativeImageFromData(std::span<const uint8_t> data, std::opt
     });
 }
 
-static RefPtr<SharedBuffer> expandNativeImageToData(NativeImage& image, ASCIILiteral uti, std::span<const unsigned> lengths)
+static Vector<RefPtr<ShareableBitmap>> bitmapsFromNativeImage(NativeImage& image, std::span<const unsigned> lengths)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
+    Vector<RefPtr<ShareableBitmap>> bitmaps;
+    auto sourceColorSpace = image.colorSpace();
+    auto destinationColorSpace = sourceColorSpace.supportsOutput() ? sourceColorSpace : DestinationColorSpace::SRGB();
     for (auto length : lengths) {
-        RetainPtr context = adoptCF(CGBitmapContextCreate(nullptr, length, length, 8, length * 4, colorSpace.get(), kCGImageAlphaPremultipliedLast));
-        if (!context)
-            return nullptr;
-
-        // Quality is more important than rendering speed for the current use case.
-        CGContextSetInterpolationQuality(context.get(), kCGInterpolationHigh);
-        CGContextDrawImage(context.get(), CGRectMake(0, 0, length, length), image.platformImage().get());
-        RetainPtr newImage = adoptCF(CGBitmapContextCreateImage(context.get()));
-        if (!newImage)
-            return nullptr;
-
-        CGImageDestinationAddImage(destination.get(), newImage.get(), nullptr);
+        if (RefPtr bitmap = ShareableBitmap::createFromImageDraw(image, destinationColorSpace, { (int)length, (int)length }, ShareableBitmap::ShouldShrinkToFit::Yes)) {
+            bitmaps.append(WTFMove(bitmap));
+            continue;
+        }
+        return { };
     }
 
-    if (!CGImageDestinationFinalize(destination.get()))
-        return nullptr;
-
-    return SharedBuffer::create(destinationData.get());
+    return bitmaps;
 }
 
-static RefPtr<SharedBuffer> expandSVGImageToData(SVGImage& svgImage, ASCIILiteral uti, std::span<const unsigned> lengths)
+static RefPtr<NativeImage> nativeImageFromSVGImage(SVGImage& image, unsigned length)
path: Source/WebCore/platform/graphics/cg/ImageUtilitiesCG.cpp
user: login: shallawa
body: `nativeImageFromSVGImage ` -> `createNativeImageFromSVGImage `?

Also can the caller pass (..., const FloatSize& size) instead of passing `(...,unsigned length)`?
created_at: 2025-04-04T16:40:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -206,82 +206,99 @@ static void tryCreateNativeImageFromData(std::span<const uint8_t> data, std::opt
     });
 }
 
-static RefPtr<SharedBuffer> expandNativeImageToData(NativeImage& image, ASCIILiteral uti, std::span<const unsigned> lengths)
+static Vector<RefPtr<ShareableBitmap>> bitmapsFromNativeImage(NativeImage& image, std::span<const unsigned> lengths)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
+    Vector<RefPtr<ShareableBitmap>> bitmaps;
+    auto sourceColorSpace = image.colorSpace();
+    auto destinationColorSpace = sourceColorSpace.supportsOutput() ? sourceColorSpace : DestinationColorSpace::SRGB();
     for (auto length : lengths) {
-        RetainPtr context = adoptCF(CGBitmapContextCreate(nullptr, length, length, 8, length * 4, colorSpace.get(), kCGImageAlphaPremultipliedLast));
-        if (!context)
-            return nullptr;
-
-        // Quality is more important than rendering speed for the current use case.
-        CGContextSetInterpolationQuality(context.get(), kCGInterpolationHigh);
-        CGContextDrawImage(context.get(), CGRectMake(0, 0, length, length), image.platformImage().get());
-        RetainPtr newImage = adoptCF(CGBitmapContextCreateImage(context.get()));
-        if (!newImage)
-            return nullptr;
-
-        CGImageDestinationAddImage(destination.get(), newImage.get(), nullptr);
+        if (RefPtr bitmap = ShareableBitmap::createFromImageDraw(image, destinationColorSpace, { (int)length, (int)length }, ShareableBitmap::ShouldShrinkToFit::Yes)) {
+            bitmaps.append(WTFMove(bitmap));
+            continue;
+        }
+        return { };
     }
 
-    if (!CGImageDestinationFinalize(destination.get()))
-        return nullptr;
-
-    return SharedBuffer::create(destinationData.get());
+    return bitmaps;
 }
 
-static RefPtr<SharedBuffer> expandSVGImageToData(SVGImage& svgImage, ASCIILiteral uti, std::span<const unsigned> lengths)
+static RefPtr<NativeImage> nativeImageFromSVGImage(SVGImage& image, unsigned length)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
-    for (auto length : lengths) {
-        auto size = FloatSize { float(length), float(length) };
-        RefPtr buffer = ImageBuffer::create(size, RenderingMode::Unaccelerated, RenderingPurpose::Unspecified, 1, DestinationColorSpace::SRGB(), ImageBufferPixelFormat::BGRA8);
-        if (!buffer)
-            return nullptr;
+    FloatSize size { float(length), float(length) };
+    RefPtr buffer = ImageBuffer::create(size, RenderingMode::Unaccelerated, RenderingPurpose::Unspecified, 1, DestinationColorSpace::SRGB(), ImageBufferPixelFormat::BGRA8);
+    if (!buffer)
+        return nullptr;
 
-        Ref svgImageContainer = SVGImageForContainer::create(&svgImage, size, 1, { });
-        buffer->context().drawImage(svgImageContainer.get(), FloatPoint::zero());
+    Ref svgImageContainer = SVGImageForContainer::create(&image, size, 1, { });
+    buffer->context().drawImage(svgImageContainer.get(), FloatPoint::zero());
 
-        RefPtr newImage = ImageBuffer::sinkIntoNativeImage(WTFMove(buffer));
-        if (!newImage)
-            return nullptr;
+    return ImageBuffer::sinkIntoNativeImage(WTFMove(buffer));
+}
 
-        CGImageDestinationAddImage(destination.get(), newImage->platformImage().get(), nullptr);
+static Vector<RefPtr<ShareableBitmap>> bitmapsFromSVGImage(SVGImage& image, std::span<const unsigned> lengths)
path: Source/WebCore/platform/graphics/cg/ImageUtilitiesCG.cpp
user: login: shallawa
body: `bitmapsFromSVGImage` -> `createBitmapsFromSVGImage`?
created_at: 2025-04-04T16:41:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -206,82 +206,99 @@ static void tryCreateNativeImageFromData(std::span<const uint8_t> data, std::opt
     });
 }
 
-static RefPtr<SharedBuffer> expandNativeImageToData(NativeImage& image, ASCIILiteral uti, std::span<const unsigned> lengths)
+static Vector<RefPtr<ShareableBitmap>> bitmapsFromNativeImage(NativeImage& image, std::span<const unsigned> lengths)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
+    Vector<RefPtr<ShareableBitmap>> bitmaps;
+    auto sourceColorSpace = image.colorSpace();
+    auto destinationColorSpace = sourceColorSpace.supportsOutput() ? sourceColorSpace : DestinationColorSpace::SRGB();
     for (auto length : lengths) {
-        RetainPtr context = adoptCF(CGBitmapContextCreate(nullptr, length, length, 8, length * 4, colorSpace.get(), kCGImageAlphaPremultipliedLast));
-        if (!context)
-            return nullptr;
-
-        // Quality is more important than rendering speed for the current use case.
-        CGContextSetInterpolationQuality(context.get(), kCGInterpolationHigh);
-        CGContextDrawImage(context.get(), CGRectMake(0, 0, length, length), image.platformImage().get());
-        RetainPtr newImage = adoptCF(CGBitmapContextCreateImage(context.get()));
-        if (!newImage)
-            return nullptr;
-
-        CGImageDestinationAddImage(destination.get(), newImage.get(), nullptr);
+        if (RefPtr bitmap = ShareableBitmap::createFromImageDraw(image, destinationColorSpace, { (int)length, (int)length }, ShareableBitmap::ShouldShrinkToFit::Yes)) {
+            bitmaps.append(WTFMove(bitmap));
+            continue;
+        }
+        return { };
     }
 
-    if (!CGImageDestinationFinalize(destination.get()))
-        return nullptr;
-
-    return SharedBuffer::create(destinationData.get());
+    return bitmaps;
 }
 
-static RefPtr<SharedBuffer> expandSVGImageToData(SVGImage& svgImage, ASCIILiteral uti, std::span<const unsigned> lengths)
+static RefPtr<NativeImage> nativeImageFromSVGImage(SVGImage& image, unsigned length)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
-    for (auto length : lengths) {
-        auto size = FloatSize { float(length), float(length) };
-        RefPtr buffer = ImageBuffer::create(size, RenderingMode::Unaccelerated, RenderingPurpose::Unspecified, 1, DestinationColorSpace::SRGB(), ImageBufferPixelFormat::BGRA8);
-        if (!buffer)
-            return nullptr;
+    FloatSize size { float(length), float(length) };
+    RefPtr buffer = ImageBuffer::create(size, RenderingMode::Unaccelerated, RenderingPurpose::Unspecified, 1, DestinationColorSpace::SRGB(), ImageBufferPixelFormat::BGRA8);
+    if (!buffer)
+        return nullptr;
 
-        Ref svgImageContainer = SVGImageForContainer::create(&svgImage, size, 1, { });
-        buffer->context().drawImage(svgImageContainer.get(), FloatPoint::zero());
+    Ref svgImageContainer = SVGImageForContainer::create(&image, size, 1, { });
+    buffer->context().drawImage(svgImageContainer.get(), FloatPoint::zero());
 
-        RefPtr newImage = ImageBuffer::sinkIntoNativeImage(WTFMove(buffer));
-        if (!newImage)
-            return nullptr;
+    return ImageBuffer::sinkIntoNativeImage(WTFMove(buffer));
+}
 
-        CGImageDestinationAddImage(destination.get(), newImage->platformImage().get(), nullptr);
+static Vector<RefPtr<ShareableBitmap>> bitmapsFromSVGImage(SVGImage& image, std::span<const unsigned> lengths)
+{
+    Vector<RefPtr<ShareableBitmap>> bitmaps;
+    for (auto length : lengths) {
+        if (RefPtr nativeImage = nativeImageFromSVGImage(image, length)) {
+            if (RefPtr bitmap = ShareableBitmap::createFromImageDraw(*nativeImage, DestinationColorSpace::SRGB(), { (int)length, (int)length }, ShareableBitmap::ShouldShrinkToFit::Yes)) {
+                bitmaps.append(WTFMove(bitmap));
+                continue;
+            }
+        }
+        return { };
     }
path: Source/WebCore/platform/graphics/cg/ImageUtilitiesCG.cpp
user: login: shallawa
body: Early returns will make this code more readable. It took sometimes to realize the `continue; ` is there just to avoid going through `return { };`
created_at: 2025-04-04T16:45:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -206,82 +206,99 @@ static void tryCreateNativeImageFromData(std::span<const uint8_t> data, std::opt
     });
 }
 
-static RefPtr<SharedBuffer> expandNativeImageToData(NativeImage& image, ASCIILiteral uti, std::span<const unsigned> lengths)
+static Vector<RefPtr<ShareableBitmap>> bitmapsFromNativeImage(NativeImage& image, std::span<const unsigned> lengths)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
+    Vector<RefPtr<ShareableBitmap>> bitmaps;
+    auto sourceColorSpace = image.colorSpace();
+    auto destinationColorSpace = sourceColorSpace.supportsOutput() ? sourceColorSpace : DestinationColorSpace::SRGB();
     for (auto length : lengths) {
-        RetainPtr context = adoptCF(CGBitmapContextCreate(nullptr, length, length, 8, length * 4, colorSpace.get(), kCGImageAlphaPremultipliedLast));
-        if (!context)
-            return nullptr;
-
-        // Quality is more important than rendering speed for the current use case.
-        CGContextSetInterpolationQuality(context.get(), kCGInterpolationHigh);
-        CGContextDrawImage(context.get(), CGRectMake(0, 0, length, length), image.platformImage().get());
-        RetainPtr newImage = adoptCF(CGBitmapContextCreateImage(context.get()));
-        if (!newImage)
-            return nullptr;
-
-        CGImageDestinationAddImage(destination.get(), newImage.get(), nullptr);
+        if (RefPtr bitmap = ShareableBitmap::createFromImageDraw(image, destinationColorSpace, { (int)length, (int)length }, ShareableBitmap::ShouldShrinkToFit::Yes)) {
+            bitmaps.append(WTFMove(bitmap));
+            continue;
+        }
+        return { };
     }
 
-    if (!CGImageDestinationFinalize(destination.get()))
-        return nullptr;
-
-    return SharedBuffer::create(destinationData.get());
+    return bitmaps;
 }
 
-static RefPtr<SharedBuffer> expandSVGImageToData(SVGImage& svgImage, ASCIILiteral uti, std::span<const unsigned> lengths)
+static RefPtr<NativeImage> nativeImageFromSVGImage(SVGImage& image, unsigned length)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
-    for (auto length : lengths) {
-        auto size = FloatSize { float(length), float(length) };
-        RefPtr buffer = ImageBuffer::create(size, RenderingMode::Unaccelerated, RenderingPurpose::Unspecified, 1, DestinationColorSpace::SRGB(), ImageBufferPixelFormat::BGRA8);
-        if (!buffer)
-            return nullptr;
+    FloatSize size { float(length), float(length) };
+    RefPtr buffer = ImageBuffer::create(size, RenderingMode::Unaccelerated, RenderingPurpose::Unspecified, 1, DestinationColorSpace::SRGB(), ImageBufferPixelFormat::BGRA8);
+    if (!buffer)
+        return nullptr;
 
-        Ref svgImageContainer = SVGImageForContainer::create(&svgImage, size, 1, { });
-        buffer->context().drawImage(svgImageContainer.get(), FloatPoint::zero());
+    Ref svgImageContainer = SVGImageForContainer::create(&image, size, 1, { });
+    buffer->context().drawImage(svgImageContainer.get(), FloatPoint::zero());
 
-        RefPtr newImage = ImageBuffer::sinkIntoNativeImage(WTFMove(buffer));
-        if (!newImage)
-            return nullptr;
+    return ImageBuffer::sinkIntoNativeImage(WTFMove(buffer));
+}
 
-        CGImageDestinationAddImage(destination.get(), newImage->platformImage().get(), nullptr);
+static Vector<RefPtr<ShareableBitmap>> bitmapsFromSVGImage(SVGImage& image, std::span<const unsigned> lengths)
+{
+    Vector<RefPtr<ShareableBitmap>> bitmaps;
+    for (auto length : lengths) {
+        if (RefPtr nativeImage = nativeImageFromSVGImage(image, length)) {
+            if (RefPtr bitmap = ShareableBitmap::createFromImageDraw(*nativeImage, DestinationColorSpace::SRGB(), { (int)length, (int)length }, ShareableBitmap::ShouldShrinkToFit::Yes)) {
+                bitmaps.append(WTFMove(bitmap));
+                continue;
+            }
+        }
+        return { };
     }
 
-    if (!CGImageDestinationFinalize(destination.get()))
-        return nullptr;
-
-    return SharedBuffer::create(destinationData.get());
+    return bitmaps;
 }
 
-void createIconDataFromImageData(std::span<const uint8_t> data, std::span<const unsigned> lengths, CompletionHandler<void(RefPtr<SharedBuffer>&&)>&& completionHandler)
+void getBitmapsForIconFromImageData(std::span<const uint8_t> data, std::span<const unsigned> lengths, CompletionHandler<void(Vector<RefPtr<ShareableBitmap>>&&)>&& completionHandler)
path: Source/WebCore/platform/graphics/cg/ImageUtilitiesCG.cpp
user: login: shallawa
body: `getBitmapsForIconFromImageData` -> `createBitmapsFromImageData`?

With this PR, nothing in this function is related to `Icon`.
created_at: 2025-04-04T16:50:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1541,8 +1541,11 @@ static bool exceedsRenderTreeSizeSizeThreshold(uint64_t thresholdSize, uint64_t
 
 void WebPageProxy::createIconDataFromImageData(Ref<WebCore::SharedBuffer>&& buffer, const Vector<unsigned>& lengths, CompletionHandler<void(RefPtr<WebCore::SharedBuffer>&&)>&& completionHandler)
 {
-    ensureProtectedRunningProcess()->sendWithAsyncReply(Messages::WebPage::CreateIconDataFromImageData(WTFMove(buffer), lengths), [preventProcessShutdownScope = protectedLegacyMainFrameProcess()->shutdownPreventingScope(), completionHandler = WTFMove(completionHandler)] (auto result) mutable {
-        completionHandler(WTFMove(result));
+    ensureProtectedRunningProcess()->sendWithAsyncReply(Messages::WebPage::GetBitmapsForIconFromImageData(WTFMove(buffer), lengths), [preventProcessShutdownScope = protectedLegacyMainFrameProcess()->shutdownPreventingScope(), completionHandler = WTFMove(completionHandler)] (auto bitmaps) mutable {
+        if (bitmaps.isEmpty())
+            return completionHandler(nullptr);
+
+        completionHandler(createIconDataFromBitmaps(WTFMove(bitmaps)));
path: Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm
user: login: shallawa
body: Let's stick with one verb `create` or `get` so the calling sequence is easy to follow:
```
createIconDataFromImageData()
    createBitmapsFromImageData()
        createBitmapsFromSVGImage()
            createNativeImageFromSVGImage()
    createIconDataFromBitmaps()
```
created_at: 2025-04-04T16:57:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -206,82 +206,99 @@ static void tryCreateNativeImageFromData(std::span<const uint8_t> data, std::opt
     });
 }
 
-static RefPtr<SharedBuffer> expandNativeImageToData(NativeImage& image, ASCIILiteral uti, std::span<const unsigned> lengths)
+static Vector<RefPtr<ShareableBitmap>> bitmapsFromNativeImage(NativeImage& image, std::span<const unsigned> lengths)
path: Source/WebCore/platform/graphics/cg/ImageUtilitiesCG.cpp
user: login: shallawa
body: Also I think this function should return `Vector<Ref<ShareableBitmap>>` since we will never add a nullptr in the returned `Vector`.
created_at: 2025-04-04T16:59:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -206,82 +206,99 @@ static void tryCreateNativeImageFromData(std::span<const uint8_t> data, std::opt
     });
 }
 
-static RefPtr<SharedBuffer> expandNativeImageToData(NativeImage& image, ASCIILiteral uti, std::span<const unsigned> lengths)
+static Vector<RefPtr<ShareableBitmap>> bitmapsFromNativeImage(NativeImage& image, std::span<const unsigned> lengths)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
+    Vector<RefPtr<ShareableBitmap>> bitmaps;
+    auto sourceColorSpace = image.colorSpace();
+    auto destinationColorSpace = sourceColorSpace.supportsOutput() ? sourceColorSpace : DestinationColorSpace::SRGB();
     for (auto length : lengths) {
-        RetainPtr context = adoptCF(CGBitmapContextCreate(nullptr, length, length, 8, length * 4, colorSpace.get(), kCGImageAlphaPremultipliedLast));
-        if (!context)
-            return nullptr;
-
-        // Quality is more important than rendering speed for the current use case.
-        CGContextSetInterpolationQuality(context.get(), kCGInterpolationHigh);
-        CGContextDrawImage(context.get(), CGRectMake(0, 0, length, length), image.platformImage().get());
-        RetainPtr newImage = adoptCF(CGBitmapContextCreateImage(context.get()));
-        if (!newImage)
-            return nullptr;
-
-        CGImageDestinationAddImage(destination.get(), newImage.get(), nullptr);
+        if (RefPtr bitmap = ShareableBitmap::createFromImageDraw(image, destinationColorSpace, { (int)length, (int)length }, ShareableBitmap::ShouldShrinkToFit::Yes)) {
+            bitmaps.append(WTFMove(bitmap));
+            continue;
+        }
+        return { };
     }
 
-    if (!CGImageDestinationFinalize(destination.get()))
-        return nullptr;
-
-    return SharedBuffer::create(destinationData.get());
+    return bitmaps;
 }
 
-static RefPtr<SharedBuffer> expandSVGImageToData(SVGImage& svgImage, ASCIILiteral uti, std::span<const unsigned> lengths)
+static RefPtr<NativeImage> nativeImageFromSVGImage(SVGImage& image, unsigned length)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
-    for (auto length : lengths) {
-        auto size = FloatSize { float(length), float(length) };
-        RefPtr buffer = ImageBuffer::create(size, RenderingMode::Unaccelerated, RenderingPurpose::Unspecified, 1, DestinationColorSpace::SRGB(), ImageBufferPixelFormat::BGRA8);
-        if (!buffer)
-            return nullptr;
+    FloatSize size { float(length), float(length) };
+    RefPtr buffer = ImageBuffer::create(size, RenderingMode::Unaccelerated, RenderingPurpose::Unspecified, 1, DestinationColorSpace::SRGB(), ImageBufferPixelFormat::BGRA8);
+    if (!buffer)
+        return nullptr;
 
-        Ref svgImageContainer = SVGImageForContainer::create(&svgImage, size, 1, { });
-        buffer->context().drawImage(svgImageContainer.get(), FloatPoint::zero());
+    Ref svgImageContainer = SVGImageForContainer::create(&image, size, 1, { });
+    buffer->context().drawImage(svgImageContainer.get(), FloatPoint::zero());
 
-        RefPtr newImage = ImageBuffer::sinkIntoNativeImage(WTFMove(buffer));
-        if (!newImage)
-            return nullptr;
+    return ImageBuffer::sinkIntoNativeImage(WTFMove(buffer));
+}
 
-        CGImageDestinationAddImage(destination.get(), newImage->platformImage().get(), nullptr);
+static Vector<RefPtr<ShareableBitmap>> bitmapsFromSVGImage(SVGImage& image, std::span<const unsigned> lengths)
+{
+    Vector<RefPtr<ShareableBitmap>> bitmaps;
+    for (auto length : lengths) {
+        if (RefPtr nativeImage = nativeImageFromSVGImage(image, length)) {
+            if (RefPtr bitmap = ShareableBitmap::createFromImageDraw(*nativeImage, DestinationColorSpace::SRGB(), { (int)length, (int)length }, ShareableBitmap::ShouldShrinkToFit::Yes)) {
+                bitmaps.append(WTFMove(bitmap));
+                continue;
+            }
+        }
+        return { };
     }
 
-    if (!CGImageDestinationFinalize(destination.get()))
-        return nullptr;
-
-    return SharedBuffer::create(destinationData.get());
+    return bitmaps;
 }
 
-void createIconDataFromImageData(std::span<const uint8_t> data, std::span<const unsigned> lengths, CompletionHandler<void(RefPtr<SharedBuffer>&&)>&& completionHandler)
+void getBitmapsForIconFromImageData(std::span<const uint8_t> data, std::span<const unsigned> lengths, CompletionHandler<void(Vector<RefPtr<ShareableBitmap>>&&)>&& completionHandler)
 {
     // Supported ICO image sizes by ImageIO.
     constexpr std::array<unsigned, 5> availableLengths { { 16, 32, 48, 128, 256 } };
-    constexpr auto icoUTI = "com.microsoft.ico"_s;
     auto targetLengths = lengths.empty() ? std::span { availableLengths } : lengths;
 
     if (RefPtr nativeImage = tryCreateNativeImageFromBitmapImageData(data, std::nullopt)) {
-        completionHandler(expandNativeImageToData(*nativeImage, icoUTI, targetLengths));
+        completionHandler(bitmapsFromNativeImage(*nativeImage, targetLengths));
         return;
     }
 
-    SVGImage::tryCreateFromData(data, [icoUTI, targetLengthsVector = Vector<unsigned> { targetLengths }, completionHandler = WTFMove(completionHandler)](auto svgImage) mutable {
+    SVGImage::tryCreateFromData(data, [targetLengthsVector = Vector<unsigned> { targetLengths }, completionHandler = WTFMove(completionHandler)](auto svgImage) mutable {
         if (!svgImage) {
-            completionHandler(nullptr);
+            completionHandler({ });
             return;
         }
-        completionHandler(expandSVGImageToData(*svgImage, icoUTI, targetLengthsVector.span()));
+        completionHandler(bitmapsFromSVGImage(*svgImage, targetLengthsVector.span()));
     });
 }
 
+RefPtr<SharedBuffer> createIconDataFromBitmaps(Vector<RefPtr<ShareableBitmap>>&& bitmaps)
+{
+    if (bitmaps.isEmpty())
+        return nullptr;
+
+    constexpr auto icoUTI = "com.microsoft.ico"_s;
+    RetainPtr cfUTI = icoUTI.createCFString();
+    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
+    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
+    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), bitmaps.size(), nullptr));
+
+    for (auto bitmap : bitmaps) {
+        if (!bitmap)
+            return nullptr;
path: Source/WebCore/platform/graphics/cg/ImageUtilitiesCG.cpp
user: login: shallawa
body: This will not be needed if this functions takes `Vector<Ref<ShareableBitmap>>`.
created_at: 2025-04-04T17:00:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -206,82 +206,99 @@ static void tryCreateNativeImageFromData(std::span<const uint8_t> data, std::opt
     });
 }
 
-static RefPtr<SharedBuffer> expandNativeImageToData(NativeImage& image, ASCIILiteral uti, std::span<const unsigned> lengths)
+static Vector<RefPtr<ShareableBitmap>> bitmapsFromNativeImage(NativeImage& image, std::span<const unsigned> lengths)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
+    Vector<RefPtr<ShareableBitmap>> bitmaps;
+    auto sourceColorSpace = image.colorSpace();
+    auto destinationColorSpace = sourceColorSpace.supportsOutput() ? sourceColorSpace : DestinationColorSpace::SRGB();
     for (auto length : lengths) {
-        RetainPtr context = adoptCF(CGBitmapContextCreate(nullptr, length, length, 8, length * 4, colorSpace.get(), kCGImageAlphaPremultipliedLast));
-        if (!context)
-            return nullptr;
-
-        // Quality is more important than rendering speed for the current use case.
-        CGContextSetInterpolationQuality(context.get(), kCGInterpolationHigh);
-        CGContextDrawImage(context.get(), CGRectMake(0, 0, length, length), image.platformImage().get());
-        RetainPtr newImage = adoptCF(CGBitmapContextCreateImage(context.get()));
-        if (!newImage)
-            return nullptr;
-
-        CGImageDestinationAddImage(destination.get(), newImage.get(), nullptr);
+        if (RefPtr bitmap = ShareableBitmap::createFromImageDraw(image, destinationColorSpace, { (int)length, (int)length }, ShareableBitmap::ShouldShrinkToFit::Yes)) {
+            bitmaps.append(WTFMove(bitmap));
+            continue;
+        }
+        return { };
     }
 
-    if (!CGImageDestinationFinalize(destination.get()))
-        return nullptr;
-
-    return SharedBuffer::create(destinationData.get());
+    return bitmaps;
 }
 
-static RefPtr<SharedBuffer> expandSVGImageToData(SVGImage& svgImage, ASCIILiteral uti, std::span<const unsigned> lengths)
+static RefPtr<NativeImage> nativeImageFromSVGImage(SVGImage& image, unsigned length)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
-    for (auto length : lengths) {
-        auto size = FloatSize { float(length), float(length) };
-        RefPtr buffer = ImageBuffer::create(size, RenderingMode::Unaccelerated, RenderingPurpose::Unspecified, 1, DestinationColorSpace::SRGB(), ImageBufferPixelFormat::BGRA8);
-        if (!buffer)
-            return nullptr;
+    FloatSize size { float(length), float(length) };
+    RefPtr buffer = ImageBuffer::create(size, RenderingMode::Unaccelerated, RenderingPurpose::Unspecified, 1, DestinationColorSpace::SRGB(), ImageBufferPixelFormat::BGRA8);
+    if (!buffer)
+        return nullptr;
 
-        Ref svgImageContainer = SVGImageForContainer::create(&svgImage, size, 1, { });
-        buffer->context().drawImage(svgImageContainer.get(), FloatPoint::zero());
+    Ref svgImageContainer = SVGImageForContainer::create(&image, size, 1, { });
+    buffer->context().drawImage(svgImageContainer.get(), FloatPoint::zero());
 
-        RefPtr newImage = ImageBuffer::sinkIntoNativeImage(WTFMove(buffer));
-        if (!newImage)
-            return nullptr;
+    return ImageBuffer::sinkIntoNativeImage(WTFMove(buffer));
+}
 
-        CGImageDestinationAddImage(destination.get(), newImage->platformImage().get(), nullptr);
+static Vector<RefPtr<ShareableBitmap>> bitmapsFromSVGImage(SVGImage& image, std::span<const unsigned> lengths)
+{
+    Vector<RefPtr<ShareableBitmap>> bitmaps;
+    for (auto length : lengths) {
+        if (RefPtr nativeImage = nativeImageFromSVGImage(image, length)) {
+            if (RefPtr bitmap = ShareableBitmap::createFromImageDraw(*nativeImage, DestinationColorSpace::SRGB(), { (int)length, (int)length }, ShareableBitmap::ShouldShrinkToFit::Yes)) {
+                bitmaps.append(WTFMove(bitmap));
+                continue;
+            }
+        }
+        return { };
     }
 
-    if (!CGImageDestinationFinalize(destination.get()))
-        return nullptr;
-
-    return SharedBuffer::create(destinationData.get());
+    return bitmaps;
 }
 
-void createIconDataFromImageData(std::span<const uint8_t> data, std::span<const unsigned> lengths, CompletionHandler<void(RefPtr<SharedBuffer>&&)>&& completionHandler)
+void getBitmapsForIconFromImageData(std::span<const uint8_t> data, std::span<const unsigned> lengths, CompletionHandler<void(Vector<RefPtr<ShareableBitmap>>&&)>&& completionHandler)
 {
     // Supported ICO image sizes by ImageIO.
     constexpr std::array<unsigned, 5> availableLengths { { 16, 32, 48, 128, 256 } };
-    constexpr auto icoUTI = "com.microsoft.ico"_s;
     auto targetLengths = lengths.empty() ? std::span { availableLengths } : lengths;
 
     if (RefPtr nativeImage = tryCreateNativeImageFromBitmapImageData(data, std::nullopt)) {
-        completionHandler(expandNativeImageToData(*nativeImage, icoUTI, targetLengths));
+        completionHandler(bitmapsFromNativeImage(*nativeImage, targetLengths));
         return;
     }
 
-    SVGImage::tryCreateFromData(data, [icoUTI, targetLengthsVector = Vector<unsigned> { targetLengths }, completionHandler = WTFMove(completionHandler)](auto svgImage) mutable {
+    SVGImage::tryCreateFromData(data, [targetLengthsVector = Vector<unsigned> { targetLengths }, completionHandler = WTFMove(completionHandler)](auto svgImage) mutable {
         if (!svgImage) {
-            completionHandler(nullptr);
+            completionHandler({ });
             return;
         }
-        completionHandler(expandSVGImageToData(*svgImage, icoUTI, targetLengthsVector.span()));
+        completionHandler(bitmapsFromSVGImage(*svgImage, targetLengthsVector.span()));
     });
 }
 
+RefPtr<SharedBuffer> createIconDataFromBitmaps(Vector<RefPtr<ShareableBitmap>>&& bitmaps)
+{
+    if (bitmaps.isEmpty())
+        return nullptr;
+
+    constexpr auto icoUTI = "com.microsoft.ico"_s;
+    RetainPtr cfUTI = icoUTI.createCFString();
+    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
+    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
+    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), bitmaps.size(), nullptr));
+
+    for (auto bitmap : bitmaps) {
path: Source/WebCore/platform/graphics/cg/ImageUtilitiesCG.cpp
user: login: shallawa
body: Should it `RefPtr` instead of `auto`?
created_at: 2025-04-04T17:02:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -206,82 +206,99 @@ static void tryCreateNativeImageFromData(std::span<const uint8_t> data, std::opt
     });
 }
 
-static RefPtr<SharedBuffer> expandNativeImageToData(NativeImage& image, ASCIILiteral uti, std::span<const unsigned> lengths)
+static Vector<RefPtr<ShareableBitmap>> bitmapsFromNativeImage(NativeImage& image, std::span<const unsigned> lengths)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
+    Vector<RefPtr<ShareableBitmap>> bitmaps;
+    auto sourceColorSpace = image.colorSpace();
+    auto destinationColorSpace = sourceColorSpace.supportsOutput() ? sourceColorSpace : DestinationColorSpace::SRGB();
     for (auto length : lengths) {
-        RetainPtr context = adoptCF(CGBitmapContextCreate(nullptr, length, length, 8, length * 4, colorSpace.get(), kCGImageAlphaPremultipliedLast));
-        if (!context)
-            return nullptr;
-
-        // Quality is more important than rendering speed for the current use case.
-        CGContextSetInterpolationQuality(context.get(), kCGInterpolationHigh);
-        CGContextDrawImage(context.get(), CGRectMake(0, 0, length, length), image.platformImage().get());
-        RetainPtr newImage = adoptCF(CGBitmapContextCreateImage(context.get()));
-        if (!newImage)
-            return nullptr;
-
-        CGImageDestinationAddImage(destination.get(), newImage.get(), nullptr);
+        if (RefPtr bitmap = ShareableBitmap::createFromImageDraw(image, destinationColorSpace, { (int)length, (int)length }, ShareableBitmap::ShouldShrinkToFit::Yes)) {
+            bitmaps.append(WTFMove(bitmap));
+            continue;
+        }
+        return { };
     }
 
-    if (!CGImageDestinationFinalize(destination.get()))
-        return nullptr;
-
-    return SharedBuffer::create(destinationData.get());
+    return bitmaps;
 }
 
-static RefPtr<SharedBuffer> expandSVGImageToData(SVGImage& svgImage, ASCIILiteral uti, std::span<const unsigned> lengths)
+static RefPtr<NativeImage> nativeImageFromSVGImage(SVGImage& image, unsigned length)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
-    for (auto length : lengths) {
-        auto size = FloatSize { float(length), float(length) };
-        RefPtr buffer = ImageBuffer::create(size, RenderingMode::Unaccelerated, RenderingPurpose::Unspecified, 1, DestinationColorSpace::SRGB(), ImageBufferPixelFormat::BGRA8);
-        if (!buffer)
-            return nullptr;
+    FloatSize size { float(length), float(length) };
+    RefPtr buffer = ImageBuffer::create(size, RenderingMode::Unaccelerated, RenderingPurpose::Unspecified, 1, DestinationColorSpace::SRGB(), ImageBufferPixelFormat::BGRA8);
+    if (!buffer)
+        return nullptr;
 
-        Ref svgImageContainer = SVGImageForContainer::create(&svgImage, size, 1, { });
-        buffer->context().drawImage(svgImageContainer.get(), FloatPoint::zero());
+    Ref svgImageContainer = SVGImageForContainer::create(&image, size, 1, { });
+    buffer->context().drawImage(svgImageContainer.get(), FloatPoint::zero());
 
-        RefPtr newImage = ImageBuffer::sinkIntoNativeImage(WTFMove(buffer));
-        if (!newImage)
-            return nullptr;
+    return ImageBuffer::sinkIntoNativeImage(WTFMove(buffer));
+}
 
-        CGImageDestinationAddImage(destination.get(), newImage->platformImage().get(), nullptr);
+static Vector<RefPtr<ShareableBitmap>> bitmapsFromSVGImage(SVGImage& image, std::span<const unsigned> lengths)
+{
+    Vector<RefPtr<ShareableBitmap>> bitmaps;
+    for (auto length : lengths) {
+        if (RefPtr nativeImage = nativeImageFromSVGImage(image, length)) {
+            if (RefPtr bitmap = ShareableBitmap::createFromImageDraw(*nativeImage, DestinationColorSpace::SRGB(), { (int)length, (int)length }, ShareableBitmap::ShouldShrinkToFit::Yes)) {
+                bitmaps.append(WTFMove(bitmap));
+                continue;
+            }
+        }
+        return { };
     }
 
-    if (!CGImageDestinationFinalize(destination.get()))
-        return nullptr;
-
-    return SharedBuffer::create(destinationData.get());
+    return bitmaps;
 }
 
-void createIconDataFromImageData(std::span<const uint8_t> data, std::span<const unsigned> lengths, CompletionHandler<void(RefPtr<SharedBuffer>&&)>&& completionHandler)
+void getBitmapsForIconFromImageData(std::span<const uint8_t> data, std::span<const unsigned> lengths, CompletionHandler<void(Vector<RefPtr<ShareableBitmap>>&&)>&& completionHandler)
 {
     // Supported ICO image sizes by ImageIO.
     constexpr std::array<unsigned, 5> availableLengths { { 16, 32, 48, 128, 256 } };
-    constexpr auto icoUTI = "com.microsoft.ico"_s;
     auto targetLengths = lengths.empty() ? std::span { availableLengths } : lengths;
 
     if (RefPtr nativeImage = tryCreateNativeImageFromBitmapImageData(data, std::nullopt)) {
-        completionHandler(expandNativeImageToData(*nativeImage, icoUTI, targetLengths));
+        completionHandler(bitmapsFromNativeImage(*nativeImage, targetLengths));
         return;
     }
 
-    SVGImage::tryCreateFromData(data, [icoUTI, targetLengthsVector = Vector<unsigned> { targetLengths }, completionHandler = WTFMove(completionHandler)](auto svgImage) mutable {
+    SVGImage::tryCreateFromData(data, [targetLengthsVector = Vector<unsigned> { targetLengths }, completionHandler = WTFMove(completionHandler)](auto svgImage) mutable {
         if (!svgImage) {
-            completionHandler(nullptr);
+            completionHandler({ });
             return;
         }
-        completionHandler(expandSVGImageToData(*svgImage, icoUTI, targetLengthsVector.span()));
+        completionHandler(bitmapsFromSVGImage(*svgImage, targetLengthsVector.span()));
     });
 }
 
+RefPtr<SharedBuffer> createIconDataFromBitmaps(Vector<RefPtr<ShareableBitmap>>&& bitmaps)
+{
+    if (bitmaps.isEmpty())
+        return nullptr;
+
+    constexpr auto icoUTI = "com.microsoft.ico"_s;
+    RetainPtr cfUTI = icoUTI.createCFString();
+    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
+    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
+    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), bitmaps.size(), nullptr));
+
+    for (auto bitmap : bitmaps) {
+        if (!bitmap)
+            return nullptr;
+
+        RetainPtr cgImage = bitmap->makeCGImageCopy();
+        if (!cgImage)
+            return nullptr;
path: Source/WebCore/platform/graphics/cg/ImageUtilitiesCG.cpp
user: login: shallawa
body: Because makeCGImageCopy() can return nullptr, should we log some information about this image? At least we can log the size of the `ShareableBitmap`:
```
        LOG_WITH_STREAM(Images, stream
            << "createIconDataFromBitmaps() failed ShareableBitmap::makeCGImageCopy()"
            << " bitmap size: " << bitmap->size());
```
created_at: 2025-04-04T17:08:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -266,10 +268,30 @@
     Util::run(&done);
 }
 
+RetainPtr<NSData> tiffRepresentation(CocoaImage *image)
+{
+#if USE(APPKIT)
+    return [image TIFFRepresentation];
+#else
+    RetainPtr cgImage = [image CGImage];
+    if (!cgImage)
+        return nullptr;
+
+    RefPtr bitmapImage = WebCore::BitmapImage::create(WTFMove(cgImage));
+    if (!bitmapImage)
+        return nullptr;
+
+    WebCore::ImageAdapter adapter { *bitmapImage };
+    return bridge_cast(adapter.tiffRepresentation());
path: Tools/TestWebKitAPI/Tests/WebKitCocoa/LoadAndDecodeImage.mm
user: login: shallawa
body: I don't think you need to create a `BitmapImage` or `ImageAdapter` here. You can use the static method `ImageAdapter::tiffRepresentation()`
```
WebCore::ImageAdapter::tiffRepresentation({ WebCore::NativeImage::create(WTFMove(cgImage)) });
```

created_at: 2025-04-04T17:39:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -56,7 +56,8 @@ enum class ImageDecodingError : uint8_t {
 };
 WEBCORE_EXPORT String descriptionString(ImageDecodingError);
 WEBCORE_EXPORT Expected<std::pair<String, Vector<IntSize>>, ImageDecodingError> utiAndAvailableSizesFromImageData(std::span<const uint8_t>);
-WEBCORE_EXPORT void createIconDataFromImageData(std::span<const uint8_t> data, std::span<const unsigned> lengths, CompletionHandler<void(RefPtr<SharedBuffer>&&)>&&);
+WEBCORE_EXPORT void getBitmapsForIconFromImageData(std::span<const uint8_t> data, std::span<const unsigned> lengths, CompletionHandler<void(Vector<RefPtr<ShareableBitmap>>&&)>&&);
path: Source/WebCore/platform/graphics/ImageUtilities.h
user: login: szewai
body: > The function is creating different ShareableBitmaps form different lengths. It should not be specific forIcon.

This function sets `lengths` to supported ico image size if `lengths` is empty; I will move the setting to `WebPageProxy::createIconDataFromImageData` and make suggested changes for this function.
created_at: 2025-04-05T17:01:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -109,7 +109,7 @@ RefPtr<ShareableBitmap> ShareableBitmap::createFromImageDraw(NativeImage& image,
     return createFromImageDraw(image, colorSpace, image.size());
 }
 
-RefPtr<ShareableBitmap> ShareableBitmap::createFromImageDraw(NativeImage& image, const DestinationColorSpace& colorSpace, const IntSize& imageSize)
+RefPtr<ShareableBitmap> ShareableBitmap::createFromImageDraw(NativeImage& image, const DestinationColorSpace& colorSpace, const IntSize& imageSize, ShouldShrinkToFit shouldShrinkToFit)
path: Source/WebCore/platform/graphics/ShareableBitmap.cpp
user: login: szewai
body: hmm I cannot use that because I need to scale an image to a target size and draw.
To do that we need to specify the source size (the original image size) and target size to `drawNativeImage` (e.g. an image has size { 10, 10 } and target size is { 20, 20 }, then I need to pass both sizes to `drawNativeImage `).
The existing implementation of `createFromImageDraw` only passes one size to `drawNativeImage`.
Maybe I misunderstood your proposal.
created_at: 2025-04-05T17:13:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -206,82 +206,99 @@ static void tryCreateNativeImageFromData(std::span<const uint8_t> data, std::opt
     });
 }
 
-static RefPtr<SharedBuffer> expandNativeImageToData(NativeImage& image, ASCIILiteral uti, std::span<const unsigned> lengths)
+static Vector<RefPtr<ShareableBitmap>> bitmapsFromNativeImage(NativeImage& image, std::span<const unsigned> lengths)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
+    Vector<RefPtr<ShareableBitmap>> bitmaps;
+    auto sourceColorSpace = image.colorSpace();
+    auto destinationColorSpace = sourceColorSpace.supportsOutput() ? sourceColorSpace : DestinationColorSpace::SRGB();
path: Source/WebCore/platform/graphics/cg/ImageUtilitiesCG.cpp
user: login: szewai
body: I guess this is probably not so important for the specific case (icon); I will add a comment here.
created_at: 2025-04-05T17:15:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -206,82 +206,99 @@ static void tryCreateNativeImageFromData(std::span<const uint8_t> data, std::opt
     });
 }
 
-static RefPtr<SharedBuffer> expandNativeImageToData(NativeImage& image, ASCIILiteral uti, std::span<const unsigned> lengths)
+static Vector<RefPtr<ShareableBitmap>> bitmapsFromNativeImage(NativeImage& image, std::span<const unsigned> lengths)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
+    Vector<RefPtr<ShareableBitmap>> bitmaps;
+    auto sourceColorSpace = image.colorSpace();
+    auto destinationColorSpace = sourceColorSpace.supportsOutput() ? sourceColorSpace : DestinationColorSpace::SRGB();
     for (auto length : lengths) {
-        RetainPtr context = adoptCF(CGBitmapContextCreate(nullptr, length, length, 8, length * 4, colorSpace.get(), kCGImageAlphaPremultipliedLast));
-        if (!context)
-            return nullptr;
-
-        // Quality is more important than rendering speed for the current use case.
-        CGContextSetInterpolationQuality(context.get(), kCGInterpolationHigh);
-        CGContextDrawImage(context.get(), CGRectMake(0, 0, length, length), image.platformImage().get());
-        RetainPtr newImage = adoptCF(CGBitmapContextCreateImage(context.get()));
-        if (!newImage)
-            return nullptr;
-
-        CGImageDestinationAddImage(destination.get(), newImage.get(), nullptr);
+        if (RefPtr bitmap = ShareableBitmap::createFromImageDraw(image, destinationColorSpace, { (int)length, (int)length }, ShareableBitmap::ShouldShrinkToFit::Yes)) {
path: Source/WebCore/platform/graphics/cg/ImageUtilitiesCG.cpp
user: login: szewai
body: This is because we need scaling at the draw so we need both sizes (image size and target size); I tried passing one size and it didn't work (with the current implementation of `createFromImageDraw` that only passes one size to `drawNativeImage`).
created_at: 2025-04-05T17:18:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -109,7 +109,7 @@ RefPtr<ShareableBitmap> ShareableBitmap::createFromImageDraw(NativeImage& image,
     return createFromImageDraw(image, colorSpace, image.size());
 }
 
-RefPtr<ShareableBitmap> ShareableBitmap::createFromImageDraw(NativeImage& image, const DestinationColorSpace& colorSpace, const IntSize& imageSize)
+RefPtr<ShareableBitmap> ShareableBitmap::createFromImageDraw(NativeImage& image, const DestinationColorSpace& colorSpace, const IntSize& imageSize, ShouldShrinkToFit shouldShrinkToFit)
path: Source/WebCore/platform/graphics/ShareableBitmap.cpp
user: login: shallawa
body: Oh I see. In this case please use the same pattern which is used in `GraphicsContext::drawImage()`
```
RefPtr<ShareableBitmap> ShareableBitmap::createFromImageDraw(NativeImage& image, const DestinationColorSpace& colorSpace)
{
    return createFromImageDraw(image, colorspace, image.size());
}

RefPtr<ShareableBitmap> ShareableBitmap::createFromImageDraw(NativeImage& image, const DestinationColorSpace& colorSpace, const IntSize& destination)
{
    return createFromImageDraw(image, colorSpace, destination, destination);
}

RefPtr<ShareableBitmap> ShareableBitmap::createFromImageDraw(NativeImage& image, const DestinationColorSpace& colorSpace, const IntSize& destination,  const IntSize& source)
{
    auto bitmap = ShareableBitmap::create({ destination, colorSpace });
    ...
    context->drawNativeImage(image, FloatRect({ }, destination), FloatRect({ }, source), { CompositeOperator::Copy });
    return bitmap;
}
```

created_at: 2025-04-07T17:02:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -206,82 +206,97 @@ static void tryCreateNativeImageFromData(std::span<const uint8_t> data, std::opt
     });
 }
 
-static RefPtr<SharedBuffer> expandNativeImageToData(NativeImage& image, ASCIILiteral uti, std::span<const unsigned> lengths)
+static Vector<Ref<ShareableBitmap>> createBitmapsFromNativeImage(NativeImage& image, std::span<const unsigned> lengths)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
+    Vector<Ref<ShareableBitmap>> bitmaps;
+    auto sourceColorSpace = image.colorSpace();
+    // The conversion could lead to loss of HDR contents.
+    auto destinationColorSpace = sourceColorSpace.supportsOutput() ? sourceColorSpace : DestinationColorSpace::SRGB();
     for (auto length : lengths) {
-        RetainPtr context = adoptCF(CGBitmapContextCreate(nullptr, length, length, 8, length * 4, colorSpace.get(), kCGImageAlphaPremultipliedLast));
-        if (!context)
-            return nullptr;
+        RefPtr bitmap = ShareableBitmap::createFromImageDraw(image, destinationColorSpace, { (int)length, (int)length }, ShareableBitmap::ShouldShrinkToFit::Yes);
path: Source/WebCore/platform/graphics/cg/ImageUtilitiesCG.cpp
user: login: shallawa
body: This can be written like this (See above):
``` 
RefPtr bitmap = ShareableBitmap::createFromImageDraw(image, destinationColorSpace, { (int)length, (int)length }, image.size());
```
created_at: 2025-04-07T17:04:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -206,82 +206,97 @@ static void tryCreateNativeImageFromData(std::span<const uint8_t> data, std::opt
     });
 }
 
-static RefPtr<SharedBuffer> expandNativeImageToData(NativeImage& image, ASCIILiteral uti, std::span<const unsigned> lengths)
+static Vector<Ref<ShareableBitmap>> createBitmapsFromNativeImage(NativeImage& image, std::span<const unsigned> lengths)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
+    Vector<Ref<ShareableBitmap>> bitmaps;
+    auto sourceColorSpace = image.colorSpace();
+    // The conversion could lead to loss of HDR contents.
+    auto destinationColorSpace = sourceColorSpace.supportsOutput() ? sourceColorSpace : DestinationColorSpace::SRGB();
     for (auto length : lengths) {
-        RetainPtr context = adoptCF(CGBitmapContextCreate(nullptr, length, length, 8, length * 4, colorSpace.get(), kCGImageAlphaPremultipliedLast));
-        if (!context)
-            return nullptr;
+        RefPtr bitmap = ShareableBitmap::createFromImageDraw(image, destinationColorSpace, { (int)length, (int)length }, ShareableBitmap::ShouldShrinkToFit::Yes);
+        if (!bitmap)
+            return { };
 
-        // Quality is more important than rendering speed for the current use case.
-        CGContextSetInterpolationQuality(context.get(), kCGInterpolationHigh);
-        CGContextDrawImage(context.get(), CGRectMake(0, 0, length, length), image.platformImage().get());
-        RetainPtr newImage = adoptCF(CGBitmapContextCreateImage(context.get()));
-        if (!newImage)
-            return nullptr;
-
-        CGImageDestinationAddImage(destination.get(), newImage.get(), nullptr);
+        bitmaps.append(bitmap.releaseNonNull());
     }
 
-    if (!CGImageDestinationFinalize(destination.get()))
+    return bitmaps;
+}
+
+static RefPtr<NativeImage> createNativeImageFromSVGImage(SVGImage& image, const IntSize& size)
+{
+    RefPtr buffer = ImageBuffer::create(size, RenderingMode::Unaccelerated, RenderingPurpose::Unspecified, 1, DestinationColorSpace::SRGB(), ImageBufferPixelFormat::BGRA8);
+    if (!buffer)
         return nullptr;
 
-    return SharedBuffer::create(destinationData.get());
+    Ref svgImageContainer = SVGImageForContainer::create(&image, size, 1, { });
+    buffer->context().drawImage(svgImageContainer.get(), FloatPoint::zero());
+
+    return ImageBuffer::sinkIntoNativeImage(WTFMove(buffer));
 }
 
-static RefPtr<SharedBuffer> expandSVGImageToData(SVGImage& svgImage, ASCIILiteral uti, std::span<const unsigned> lengths)
+static Vector<Ref<ShareableBitmap>> createBitmapsFromSVGImage(SVGImage& image, std::span<const unsigned> lengths)
 {
-    RetainPtr colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
-    RetainPtr destinationData = adoptCF(CFDataCreateMutable(0, 0));
-    RetainPtr cfUTI = uti.createCFString();
-    RetainPtr destination = adoptCF(CGImageDestinationCreateWithData(destinationData.get(), cfUTI.get(), lengths.size(), nullptr));
+    Vector<Ref<ShareableBitmap>> bitmaps;
     for (auto length : lengths) {
-        auto size = FloatSize { float(length), float(length) };
-        RefPtr buffer = ImageBuffer::create(size, RenderingMode::Unaccelerated, RenderingPurpose::Unspecified, 1, DestinationColorSpace::SRGB(), ImageBufferPixelFormat::BGRA8);
-        if (!buffer)
-            return nullptr;
+        IntSize size { (int)length, (int)length };
+        RefPtr nativeImage = createNativeImageFromSVGImage(image, size);
+        if (!nativeImage)
+            return { };
 
-        Ref svgImageContainer = SVGImageForContainer::create(&svgImage, size, 1, { });
-        buffer->context().drawImage(svgImageContainer.get(), FloatPoint::zero());
-
-        RefPtr newImage = ImageBuffer::sinkIntoNativeImage(WTFMove(buffer));
-        if (!newImage)
-            return nullptr;
+        RefPtr bitmap = ShareableBitmap::createFromImageDraw(*nativeImage, DestinationColorSpace::SRGB(), size, ShareableBitmap::ShouldShrinkToFit::Yes);
path: Source/WebCore/platform/graphics/cg/ImageUtilitiesCG.cpp
user: login: shallawa
body: This can be written like this (See above):
``` 
RefPtr bitmap = ShareableBitmap::createFromImageDraw(*nativeImage, DestinationColorSpace::SRGB(), size, nativeImage->size());
```
created_at: 2025-04-07T17:05:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -109,7 +109,7 @@ RefPtr<ShareableBitmap> ShareableBitmap::createFromImageDraw(NativeImage& image,
     return createFromImageDraw(image, colorSpace, image.size());
 }
 
-RefPtr<ShareableBitmap> ShareableBitmap::createFromImageDraw(NativeImage& image, const DestinationColorSpace& colorSpace, const IntSize& imageSize)
+RefPtr<ShareableBitmap> ShareableBitmap::createFromImageDraw(NativeImage& image, const DestinationColorSpace& colorSpace, const IntSize& imageSize, ShouldShrinkToFit shouldShrinkToFit)
path: Source/WebCore/platform/graphics/ShareableBitmap.cpp
user: login: szewai
body: Sure, updated; thanks for review!
created_at: 2025-04-07T19:09:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -32,9 +32,11 @@
 #import "TestNavigationDelegate.h"
 #import "TestWKWebView.h"
 #import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
+#import <WebCore/BitmapImage.h>
path: Tools/TestWebKitAPI/Tests/WebKitCocoa/LoadAndDecodeImage.mm
user: login: shallawa
body: I think this header file is not needed.
created_at: 2025-04-07T20:58:14Z
author_association: CONTRIBUTOR

PR comments:
user: login: webkit-early-warning-system
created_at: 2025-04-01T23:25:37Z
body: EWS run on previous version of this PR (hash https://github.com/WebKit/WebKit/commit/bbb2fd92445e90922c82ec2871117483a7bfebe7)<details>

| Misc | iOS, visionOS, tvOS & watchOS  | macOS  | Linux |  Windows |
| ----- | ---------------------- | ------- |  ----- |  --------- |
| [✅ 🧪 style](https://ews-build.webkit.org/#/builders/38/builds/97826 "Passed style check") | [✅ 🛠 ios](https://ews-build.webkit.org/#/builders/131/builds/17451 "Built successfully") | [✅ 🛠 mac](https://ews-build.webkit.org/#/builders/138/builds/7670 "Built successfully") | [✅ 🛠 wpe](https://ews-build.webkit.org/#/builders/5/builds/102938 "Built successfully") | [✅ 🛠 win](https://ews-build.webkit.org/#/builders/59/builds/48355 "Built successfully") 
| [✅ 🧪 bindings](https://ews-build.webkit.org/#/builders/9/builds/99871 "Passed tests") | [✅ 🛠 ios-sim](https://ews-build.webkit.org/#/builders/130/builds/17745 "Built successfully") | [✅ 🛠 mac-AS-debug](https://ews-build.webkit.org/#/builders/123/builds/25904 "Built successfully") | [✅ 🧪 wpe-wk2](https://ews-build.webkit.org/#/builders/34/builds/74496 "Passed tests") | [✅ 🧪 win-tests](https://ews-build.webkit.org/#/builders/60/builds/31671 "Passed tests") 
| [✅ 🧪 webkitperl](https://ews-build.webkit.org/#/builders/11/builds/100829 "Passed tests") | [✅ 🧪 ios-wk2](https://ews-build.webkit.org/#/builders/132/builds/13440 "Passed tests") | [✅ 🧪 api-mac](https://ews-build.webkit.org/#/builders/18/builds/88413 "Passed tests") | [✅ 🧪 api-wpe](https://ews-build.webkit.org/#/builders/41/builds/54853 "Passed tests") | 
| | [✅ 🧪 ios-wk2-wpt](https://ews-build.webkit.org/#/builders/133/builds/13217 "Passed tests") | [✅ 🧪 mac-wk1](https://ews-build.webkit.org/#/builders/135/builds/6338 "Passed tests") | [✅ 🛠 wpe-cairo](https://ews-build.webkit.org/#/builders/65/builds/47798 "Built successfully") | 
| | [✅ 🧪 api-ios](https://ews-build.webkit.org/#/builders/13/builds/83207 "Passed tests") | [✅ 🧪 mac-wk2](https://ews-build.webkit.org/#/builders/136/builds/6411 "Passed tests") | [✅ 🛠 gtk](https://ews-build.webkit.org/#/builders/2/builds/105318 "Built successfully") | 
| | [✅ 🛠 vision](https://ews-build.webkit.org/#/builders/128/builds/24906 "Built successfully") | [❌ 🧪 mac-AS-debug-wk2](https://ews-build.webkit.org/#/builders/122/builds/18143 "Found 1 new test failure: ipc/send-gpu-GetShareableBitmap-RemoteRenderingBackend.html (failure)") | [✅ 🧪 gtk-wk2](https://ews-build.webkit.org/#/builders/1/builds/83516 "Passed tests") | 
| | [✅ 🛠 vision-sim](https://ews-build.webkit.org/#/builders/121/builds/25278 "Built successfully") | [✅ 🧪 mac-wk2-stress](https://ews-build.webkit.org/#/builders/8/builds/84585 "Passed tests") | [✅ 🧪 api-gtk](https://ews-build.webkit.org/#/builders/21/builds/82952 "Passed tests") | 
| | [✅ 🧪 vision-wk2](https://ews-build.webkit.org/#/builders/126/builds/27563 "Passed tests") | [✅ 🧪 mac-intel-wk2](https://ews-build.webkit.org/#/builders/137/builds/5239 "Passed tests") | [✅ 🛠 playstation](https://ews-build.webkit.org/#/builders/134/builds/18513 "Built successfully") | 
| | [✅ 🛠 tv](https://ews-build.webkit.org/#/builders/127/builds/24867 "Built successfully") | [✅ 🛠 mac-safer-cpp](https://ews-build.webkit.org/#/builders/120/builds/30036 "Built successfully") | | 
| | [✅ 🛠 tv-sim](https://ews-build.webkit.org/#/builders/125/builds/24689 "Built successfully") | | | 
| | [✅ 🛠 watch](https://ews-build.webkit.org/#/builders/129/builds/28003 "Built successfully") | | | 
| | [✅ 🛠 watch-sim](https://ews-build.webkit.org/#/builders/124/builds/26263 "Built successfully") | | | </details>
author_association: COLLABORATOR
user: login: webkit-early-warning-system
created_at: 2025-04-02T22:55:57Z
body: EWS run on previous version of this PR (hash https://github.com/WebKit/WebKit/commit/fcc0c8ef38336ad4293e185df47dfbdc11de4e5f)<details>

| Misc | iOS, visionOS, tvOS & watchOS  | macOS  | Linux |  Windows |
| ----- | ---------------------- | ------- |  ----- |  --------- |
| [✅ 🧪 style](https://ews-build.webkit.org/#/builders/38/builds/98007 "Passed style check") | [❌ 🛠 ios](https://ews-build.webkit.org/#/builders/131/builds/17638 "Hash fcc0c8ef for PR 43423 does not build (failure)") | [✅ 🛠 mac](https://ews-build.webkit.org/#/builders/138/builds/7865 "Built successfully") | [✅ 🛠 wpe](https://ews-build.webkit.org/#/builders/5/builds/103122 "Built successfully") | [✅ 🛠 win](https://ews-build.webkit.org/#/builders/59/builds/48536 "Built successfully") 
| [✅ 🧪 bindings](https://ews-build.webkit.org/#/builders/9/builds/100052 "Passed tests") | [❌ 🛠 ios-sim](https://ews-build.webkit.org/#/builders/130/builds/17930 "Hash fcc0c8ef for PR 43423 does not build (failure)") | [✅ 🛠 mac-AS-debug](https://ews-build.webkit.org/#/builders/123/builds/26089 "Built successfully") | [✅ 🧪 wpe-wk2](https://ews-build.webkit.org/#/builders/34/builds/74620 "Passed tests") | [✅ 🧪 win-tests](https://ews-build.webkit.org/#/builders/60/builds/31803 "Passed tests") 
| [✅ 🧪 webkitperl](https://ews-build.webkit.org/#/builders/11/builds/101011 "Passed tests") | [❌ 🧪 ios-wk2](https://ews-build.webkit.org/#/builders/130/builds/17930 "Hash fcc0c8ef for PR 43423 does not build (failure)") | [✅ 🧪 api-mac](https://ews-build.webkit.org/#/builders/18/builds/88560 "Passed tests") | [✅ 🧪 api-wpe](https://ews-build.webkit.org/#/builders/41/builds/54980 "Passed tests") | 
| | [❌ 🧪 ios-wk2-wpt](https://ews-build.webkit.org/#/builders/130/builds/17930 "Hash fcc0c8ef for PR 43423 does not build (failure)") | [✅ 🧪 mac-wk1](https://ews-build.webkit.org/#/builders/135/builds/6490 "Passed tests") | [✅ 🛠 wpe-cairo](https://ews-build.webkit.org/#/builders/65/builds/47978 "Built successfully") | 
| | [❌ 🧪 api-ios](https://ews-build.webkit.org/#/builders/130/builds/17930 "Hash fcc0c8ef for PR 43423 does not build (failure)") | [✅ 🧪 mac-wk2](https://ews-build.webkit.org/#/builders/136/builds/6573 "Passed tests") | [✅ 🛠 gtk](https://ews-build.webkit.org/#/builders/2/builds/105500 "Built successfully") | 
| | [❌ 🛠 vision](https://ews-build.webkit.org/#/builders/128/builds/25093 "Hash fcc0c8ef for PR 43423 does not build (failure)") | [✅ 🧪 mac-AS-debug-wk2](https://ews-build.webkit.org/#/builders/122/builds/18274 "Passed tests") | [✅ 🧪 gtk-wk2](https://ews-build.webkit.org/#/builders/1/builds/83606 "Passed tests") | 
| | [❌ 🛠 vision-sim](https://ews-build.webkit.org/#/builders/121/builds/25466 "Hash fcc0c8ef for PR 43423 does not build (failure)") | [✅ 🧪 mac-wk2-stress](https://ews-build.webkit.org/#/builders/8/builds/84738 "Passed tests") | [✅ 🧪 api-gtk](https://ews-build.webkit.org/#/builders/21/builds/83060 "Passed tests") | 
| | [⏳ 🧪 vision-wk2 ](https://ews-build.webkit.org/#/builders/visionOS-2-Simulator-WK2-Tests-EWS "Waiting in queue, processing has not started yet") | [✅ 🧪 mac-intel-wk2](https://ews-build.webkit.org/#/builders/137/builds/5373 "Passed tests") | [✅ 🛠 playstation](https://ews-build.webkit.org/#/builders/134/builds/18716 "Built successfully") | 
| | [❌ 🛠 tv](https://ews-build.webkit.org/#/builders/127/builds/25052 "Hash fcc0c8ef for PR 43423 does not build (failure)") | [✅ 🛠 mac-safer-cpp](https://ews-build.webkit.org/#/builders/120/builds/30226 "Built successfully") | | 
| | [❌ 🛠 tv-sim](https://ews-build.webkit.org/#/builders/125/builds/24872 "Hash fcc0c8ef for PR 43423 does not build (failure)") | | | 
| | [❌ 🛠 watch](https://ews-build.webkit.org/#/builders/129/builds/28188 "Hash fcc0c8ef for PR 43423 does not build (failure)") | | | 
| | [❌ 🛠 watch-sim](https://ews-build.webkit.org/#/builders/124/builds/26447 "Hash fcc0c8ef for PR 43423 does not build (failure)") | | | </details>
author_association: COLLABORATOR
user: login: webkit-early-warning-system
created_at: 2025-04-03T18:32:31Z
body: EWS run on previous version of this PR (hash https://github.com/WebKit/WebKit/commit/88bcaa294697edead546d3056e47bf6fa1e47738)<details>

| Misc | iOS, visionOS, tvOS & watchOS  | macOS  | Linux |  Windows |
| ----- | ---------------------- | ------- |  ----- |  --------- |
| [✅ 🧪 style](https://ews-build.webkit.org/#/builders/38/builds/98146 "Passed style check") | [✅ 🛠 ios](https://ews-build.webkit.org/#/builders/131/builds/17777 "Built successfully") | [✅ 🛠 mac](https://ews-build.webkit.org/#/builders/138/builds/8004 "Built successfully") | [✅ 🛠 wpe](https://ews-build.webkit.org/#/builders/5/builds/103262 "Built successfully") | [✅ 🛠 win](https://ews-build.webkit.org/#/builders/59/builds/48675 "Built successfully") 
| [✅ 🧪 bindings](https://ews-build.webkit.org/#/builders/9/builds/100191 "Passed tests") | [✅ 🛠 ios-sim](https://ews-build.webkit.org/#/builders/130/builds/18069 "Built successfully") | [✅ 🛠 mac-AS-debug](https://ews-build.webkit.org/#/builders/123/builds/26228 "Built successfully") | [✅ 🧪 wpe-wk2](https://ews-build.webkit.org/#/builders/34/builds/74726 "Passed tests") | [✅ 🧪 win-tests](https://ews-build.webkit.org/#/builders/60/builds/31906 "Passed tests") 
| [✅ 🧪 webkitperl](https://ews-build.webkit.org/#/builders/11/builds/101150 "Passed tests") | [✅ 🧪 ios-wk2](https://ews-build.webkit.org/#/builders/132/builds/13697 "Passed tests") | [✅ 🧪 api-mac](https://ews-build.webkit.org/#/builders/18/builds/88683 "Passed tests") | [✅ 🧪 api-wpe](https://ews-build.webkit.org/#/builders/41/builds/55086 "Passed tests") | 
| | [✅ 🧪 ios-wk2-wpt](https://ews-build.webkit.org/#/builders/133/builds/13481 "Passed tests") | [✅ 🧪 mac-wk1](https://ews-build.webkit.org/#/builders/135/builds/6618 "Passed tests") | [✅ 🛠 wpe-cairo](https://ews-build.webkit.org/#/builders/65/builds/48117 "Built successfully") | 
| | [✅ 🧪 api-ios](https://ews-build.webkit.org/#/builders/13/builds/83456 "Passed tests") | [✅ 🧪 mac-wk2](https://ews-build.webkit.org/#/builders/136/builds/6697 "Passed tests") | [✅ 🛠 gtk](https://ews-build.webkit.org/#/builders/2/builds/105639 "Built successfully") | 
| | [✅ 🛠 vision](https://ews-build.webkit.org/#/builders/128/builds/25232 "Built successfully") | [✅ 🧪 mac-AS-debug-wk2](https://ews-build.webkit.org/#/builders/122/builds/18382 "Passed tests") | [✅ 🧪 gtk-wk2](https://ews-build.webkit.org/#/builders/1/builds/83717 "Passed tests") | 
| | [✅ 🛠 vision-sim](https://ews-build.webkit.org/#/builders/121/builds/25605 "Built successfully") | [✅ 🧪 mac-wk2-stress](https://ews-build.webkit.org/#/builders/8/builds/84864 "Passed tests") | [✅ 🧪 api-gtk](https://ews-build.webkit.org/#/builders/21/builds/83171 "Passed tests") | 
| | [✅ 🧪 vision-wk2](https://ews-build.webkit.org/#/builders/126/builds/27811 "Passed tests") | [✅ 🧪 mac-intel-wk2](https://ews-build.webkit.org/#/builders/137/builds/5492 "Passed tests") | [✅ 🛠 playstation](https://ews-build.webkit.org/#/builders/134/builds/18872 "Built successfully") | 
| | [✅ 🛠 tv](https://ews-build.webkit.org/#/builders/127/builds/25191 "Built successfully") | [✅ 🛠 mac-safer-cpp](https://ews-build.webkit.org/#/builders/120/builds/30365 "Built successfully") | | 
| | [✅ 🛠 tv-sim](https://ews-build.webkit.org/#/builders/125/builds/25011 "Built successfully") | | | 
| | [✅ 🛠 watch](https://ews-build.webkit.org/#/builders/129/builds/28327 "Built successfully") | | | 
| | [✅ 🛠 watch-sim](https://ews-build.webkit.org/#/builders/124/builds/26586 "Built successfully") | | | </details>
author_association: COLLABORATOR
user: login: webkit-early-warning-system
created_at: 2025-04-05T17:35:48Z
body: EWS run on previous version of this PR (hash https://github.com/WebKit/WebKit/commit/e67c89ebe1f655f726546887fbb556edfec17b74)<details>

| Misc | iOS, visionOS, tvOS & watchOS  | macOS  | Linux |  Windows |
| ----- | ---------------------- | ------- |  ----- |  --------- |
| [✅ 🧪 style](https://ews-build.webkit.org/#/builders/38/builds/98506 "Passed style check") | [❌ 🛠 ios](https://ews-build.webkit.org/#/builders/131/builds/18139 "Hash e67c89eb for PR 43423 does not build (failure)") | [✅ 🛠 mac](https://ews-build.webkit.org/#/builders/138/builds/8371 "Built successfully") | [✅ 🛠 wpe](https://ews-build.webkit.org/#/builders/5/builds/103627 "Built successfully") | [✅ 🛠 win](https://ews-build.webkit.org/#/builders/59/builds/49039 "Built successfully") 
| [✅ 🧪 bindings](https://ews-build.webkit.org/#/builders/9/builds/100550 "Passed tests") | [❌ 🛠 ios-sim](https://ews-build.webkit.org/#/builders/130/builds/18432 "Hash e67c89eb for PR 43423 does not build (failure)") | [✅ 🛠 mac-AS-debug](https://ews-build.webkit.org/#/builders/123/builds/26590 "Built successfully") | [✅ 🧪 wpe-wk2](https://ews-build.webkit.org/#/builders/34/builds/74986 "Passed tests") | [✅ 🧪 win-tests](https://ews-build.webkit.org/#/builders/60/builds/32144 "Passed tests") 
| [✅ 🧪 webkitperl](https://ews-build.webkit.org/#/builders/11/builds/101510 "Passed tests") | [❌ 🧪 ios-wk2](https://ews-build.webkit.org/#/builders/130/builds/18432 "Hash e67c89eb for PR 43423 does not build (failure)") | [❌ 🧪 api-mac](https://ews-build.webkit.org/#/builders/18/builds/88969 "Found 1 new API test failure: TestWebKitAPI.WebKit.CreateIconDataFromImageDataSVGWithSubresource (failure)") | [✅ 🧪 api-wpe](https://ews-build.webkit.org/#/builders/41/builds/55345 "Passed tests") | 
| | [❌ 🧪 ios-wk2-wpt](https://ews-build.webkit.org/#/builders/130/builds/18432 "Hash e67c89eb for PR 43423 does not build (failure)") | [✅ 🧪 mac-wk1](https://ews-build.webkit.org/#/builders/135/builds/6933 "Passed tests") | [✅ 🛠 wpe-cairo](https://ews-build.webkit.org/#/builders/65/builds/48476 "Built successfully") | 
| | [❌ 🧪 api-ios](https://ews-build.webkit.org/#/builders/130/builds/18432 "Hash e67c89eb for PR 43423 does not build (failure)") | [✅ 🧪 mac-wk2](https://ews-build.webkit.org/#/builders/136/builds/7011 "Passed tests") | [✅ 🛠 gtk](https://ews-build.webkit.org/#/builders/2/builds/106000 "Built successfully") | 
| | [❌ 🛠 vision](https://ews-build.webkit.org/#/builders/128/builds/25596 "Hash e67c89eb for PR 43423 does not build (failure)") | [✅ 🧪 mac-AS-debug-wk2](https://ews-build.webkit.org/#/builders/122/builds/18639 "Passed tests") | [✅ 🧪 gtk-wk2](https://ews-build.webkit.org/#/builders/1/builds/83955 "Passed tests") | 
| | [❌ 🛠 vision-sim](https://ews-build.webkit.org/#/builders/121/builds/25972 "Hash e67c89eb for PR 43423 does not build (failure)") | [✅ 🧪 mac-wk2-stress](https://ews-build.webkit.org/#/builders/8/builds/85169 "Passed tests") | [✅ 🧪 api-gtk](https://ews-build.webkit.org/#/builders/21/builds/83440 "Passed tests") | 
| | [⏳ 🧪 vision-wk2 ](https://ews-build.webkit.org/#/builders/visionOS-2-Simulator-WK2-Tests-EWS "Waiting in queue, processing has not started yet") | [✅ 🧪 mac-intel-wk2](https://ews-build.webkit.org/#/builders/137/builds/5761 "Passed tests") | [✅ 🛠 playstation](https://ews-build.webkit.org/#/builders/134/builds/19264 "Built successfully") | 
| | [❌ 🛠 tv](https://ews-build.webkit.org/#/builders/127/builds/25554 "Hash e67c89eb for PR 43423 does not build (failure)") | [✅ 🛠 mac-safer-cpp](https://ews-build.webkit.org/#/builders/120/builds/30735 "Built successfully") | | 
| | [❌ 🛠 tv-sim](https://ews-build.webkit.org/#/builders/125/builds/25372 "Hash e67c89eb for PR 43423 does not build (failure)") | | | 
| | [❌ 🛠 watch](https://ews-build.webkit.org/#/builders/129/builds/28692 "Hash e67c89eb for PR 43423 does not build (failure)") | | | 
| | [❌ 🛠 watch-sim](https://ews-build.webkit.org/#/builders/124/builds/26947 "Hash e67c89eb for PR 43423 does not build (failure)") | | | </details>
author_association: COLLABORATOR
user: login: webkit-early-warning-system
created_at: 2025-04-07T16:58:48Z
body: EWS run on previous version of this PR (hash https://github.com/WebKit/WebKit/commit/2d83954e7fe6f6fe6ab4a64510d3eb0517c6903d)<details>

| Misc | iOS, visionOS, tvOS & watchOS  | macOS  | Linux |  Windows |
| ----- | ---------------------- | ------- |  ----- |  --------- |
| [✅ 🧪 style](https://ews-build.webkit.org/#/builders/38/builds/98712 "Passed style check") | [❌ 🛠 ios](https://ews-build.webkit.org/#/builders/131/builds/18345 "Hash 2d83954e for PR 43423 does not build (failure)") | [✅ 🛠 mac](https://ews-build.webkit.org/#/builders/138/builds/8582 "Built successfully") | [✅ 🛠 wpe](https://ews-build.webkit.org/#/builders/5/builds/103837 "Built successfully") | [✅ 🛠 win](https://ews-build.webkit.org/#/builders/59/builds/49300 "Built successfully") 
| [✅ 🧪 bindings](https://ews-build.webkit.org/#/builders/9/builds/100757 "Passed tests") | [❌ 🛠 ios-sim](https://ews-build.webkit.org/#/builders/130/builds/18638 "Hash 2d83954e for PR 43423 does not build (failure)") | [✅ 🛠 mac-AS-debug](https://ews-build.webkit.org/#/builders/123/builds/26796 "Built successfully") | [✅ 🧪 wpe-wk2](https://ews-build.webkit.org/#/builders/34/builds/75153 "Passed tests") | [✅ 🧪 win-tests](https://ews-build.webkit.org/#/builders/60/builds/32299 "Passed tests") 
| [✅ 🧪 webkitperl](https://ews-build.webkit.org/#/builders/11/builds/101716 "Passed tests") | [❌ 🧪 ios-wk2](https://ews-build.webkit.org/#/builders/130/builds/18638 "Hash 2d83954e for PR 43423 does not build (failure)") | [  ~~🧪 api-mac~~](https://ews-build.webkit.org/#/builders/18/builds/89148 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | [✅ 🧪 api-wpe](https://ews-build.webkit.org/#/builders/41/builds/55510 "Passed tests") | 
| | [❌ 🧪 ios-wk2-wpt](https://ews-build.webkit.org/#/builders/130/builds/18638 "Hash 2d83954e for PR 43423 does not build (failure)") | [✅ 🧪 mac-wk1](https://ews-build.webkit.org/#/builders/135/builds/7113 "Passed tests") | [✅ 🛠 wpe-cairo](https://ews-build.webkit.org/#/builders/65/builds/48682 "Built successfully") | 
| | [❌ 🧪 api-ios](https://ews-build.webkit.org/#/builders/130/builds/18638 "Hash 2d83954e for PR 43423 does not build (failure)") | [✅ 🧪 mac-wk2](https://ews-build.webkit.org/#/builders/136/builds/7191 "Passed tests") | [✅ 🛠 gtk](https://ews-build.webkit.org/#/builders/2/builds/106208 "Built successfully") | 
| | [❌ 🛠 vision](https://ews-build.webkit.org/#/builders/128/builds/25802 "Hash 2d83954e for PR 43423 does not build (failure)") | [  ~~🧪 mac-AS-debug-wk2~~](https://ews-build.webkit.org/#/builders/122/builds/18816 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | [✅ 🧪 gtk-wk2](https://ews-build.webkit.org/#/builders/1/builds/84124 "Passed tests") | 
| | [❌ 🛠 vision-sim](https://ews-build.webkit.org/#/builders/121/builds/26179 "Hash 2d83954e for PR 43423 does not build (failure)") | [![loading](https://user-images.githubusercontent.com/3098702/171232313-daa606f1-8fd6-4b0f-a20b-2cb93c43d19b.png) 🧪 mac-wk2-stress](https://ews-build.webkit.org/#/builders/8/builds/85349 "Build is in progress. Recent messages:OS: Sonoma (14.7.3), Xcode: 15.4; Running apply-patch; Checked out pull request; Skipped layout-tests") | [✅ 🧪 api-gtk](https://ews-build.webkit.org/#/builders/21/builds/83611 "Passed tests") | 
| | [⏳ 🧪 vision-wk2 ](https://ews-build.webkit.org/#/builders/visionOS-2-Simulator-WK2-Tests-EWS "Waiting in queue, processing has not started yet") | [✅ 🧪 mac-intel-wk2](https://ews-build.webkit.org/#/builders/137/builds/5937 "Passed tests") | [✅ 🛠 playstation](https://ews-build.webkit.org/#/builders/134/builds/19514 "Built successfully") | 
| | [❌ 🛠 tv](https://ews-build.webkit.org/#/builders/127/builds/25760 "Hash 2d83954e for PR 43423 does not build (failure)") | [✅ 🛠 mac-safer-cpp](https://ews-build.webkit.org/#/builders/120/builds/30942 "Built successfully") | | 
| | [❌ 🛠 tv-sim](https://ews-build.webkit.org/#/builders/125/builds/25578 "Hash 2d83954e for PR 43423 does not build (failure)") | | | 
| | [❌ 🛠 watch](https://ews-build.webkit.org/#/builders/129/builds/28898 "Hash 2d83954e for PR 43423 does not build (failure)") | | | 
| | [❌ 🛠 watch-sim](https://ews-build.webkit.org/#/builders/124/builds/27153 "Hash 2d83954e for PR 43423 does not build (failure)") | | | </details>
author_association: COLLABORATOR
user: login: webkit-early-warning-system
created_at: 2025-04-07T19:08:04Z
body: EWS run on previous version of this PR (hash https://github.com/WebKit/WebKit/commit/b1917e1acf8786e973b0b1496992e23e58de195a)<details>

| Misc | iOS, visionOS, tvOS & watchOS  | macOS  | Linux |  Windows |
| ----- | ---------------------- | ------- |  ----- |  --------- |
| [✅ 🧪 style](https://ews-build.webkit.org/#/builders/38/builds/98735 "Passed style check") | [✅ 🛠 ios](https://ews-build.webkit.org/#/builders/131/builds/18368 "Built successfully") | [✅ 🛠 mac](https://ews-build.webkit.org/#/builders/138/builds/8605 "Built successfully") | [✅ 🛠 wpe](https://ews-build.webkit.org/#/builders/5/builds/103860 "Built successfully") | [✅ 🛠 win](https://ews-build.webkit.org/#/builders/59/builds/49325 "Built successfully") 
| [✅ 🧪 bindings](https://ews-build.webkit.org/#/builders/9/builds/100780 "Passed tests") | [✅ 🛠 ios-sim](https://ews-build.webkit.org/#/builders/130/builds/18661 "Built successfully") | [✅ 🛠 mac-AS-debug](https://ews-build.webkit.org/#/builders/123/builds/26819 "Built successfully") | [✅ 🧪 wpe-wk2](https://ews-build.webkit.org/#/builders/34/builds/75169 "Passed tests") | [✅ 🧪 win-tests](https://ews-build.webkit.org/#/builders/60/builds/32313 "Passed tests") 
| [✅ 🧪 webkitperl](https://ews-build.webkit.org/#/builders/11/builds/101739 "Passed tests") | [✅ 🧪 ios-wk2](https://ews-build.webkit.org/#/builders/132/builds/14176 "Passed tests") | [❌ 🧪 api-mac](https://ews-build.webkit.org/#/builders/18/builds/89168 "Found 1 new API test failure: TestWebKitAPI.WebKit.CreateIconDataFromImageDataSVGWithSubresource (failure)") | [✅ 🧪 api-wpe](https://ews-build.webkit.org/#/builders/41/builds/55526 "Passed tests") | 
| | [✅ 🧪 ios-wk2-wpt](https://ews-build.webkit.org/#/builders/133/builds/13962 "Passed tests") | [✅ 🧪 mac-wk1](https://ews-build.webkit.org/#/builders/135/builds/7133 "Passed tests") | [✅ 🛠 wpe-cairo](https://ews-build.webkit.org/#/builders/65/builds/48705 "Built successfully") | 
| | [❌ 🧪 api-ios](https://ews-build.webkit.org/#/builders/13/builds/83935 "Found 1 new API test failure: TestWebKitAPI.WebKit.CreateIconDataFromImageDataSVGWithSubresource (failure)") | [✅ 🧪 mac-wk2](https://ews-build.webkit.org/#/builders/136/builds/7211 "Passed tests") | [✅ 🛠 gtk](https://ews-build.webkit.org/#/builders/2/builds/106231 "Built successfully") | 
| | [✅ 🛠 vision](https://ews-build.webkit.org/#/builders/128/builds/25825 "Built successfully") | [✅ 🧪 mac-AS-debug-wk2](https://ews-build.webkit.org/#/builders/122/builds/18829 "Passed tests") | [✅ 🧪 gtk-wk2](https://ews-build.webkit.org/#/builders/1/builds/84149 "Passed tests") | 
| | [✅ 🛠 vision-sim](https://ews-build.webkit.org/#/builders/121/builds/26202 "Built successfully") | [✅ 🧪 mac-wk2-stress](https://ews-build.webkit.org/#/builders/8/builds/85369 "Passed tests") | [✅ 🧪 api-gtk](https://ews-build.webkit.org/#/builders/21/builds/83639 "Passed tests") | 
| | [✅ 🧪 vision-wk2](https://ews-build.webkit.org/#/builders/126/builds/28276 "Passed tests") | [✅ 🧪 mac-intel-wk2](https://ews-build.webkit.org/#/builders/137/builds/5953 "Passed tests") | [✅ 🛠 playstation](https://ews-build.webkit.org/#/builders/134/builds/19544 "Built successfully") | 
| | [✅ 🛠 tv](https://ews-build.webkit.org/#/builders/127/builds/25783 "Built successfully") | [✅ 🛠 mac-safer-cpp](https://ews-build.webkit.org/#/builders/120/builds/30965 "Built successfully") | | 
| | [✅ 🛠 tv-sim](https://ews-build.webkit.org/#/builders/125/builds/25601 "Built successfully") | | | 
| | [✅ 🛠 watch](https://ews-build.webkit.org/#/builders/129/builds/28921 "Built successfully") | | | 
| | [✅ 🛠 watch-sim](https://ews-build.webkit.org/#/builders/124/builds/27176 "Built successfully") | | | </details>
author_association: COLLABORATOR
user: login: webkit-early-warning-system
created_at: 2025-04-08T16:30:22Z
body: EWS run on previous version of this PR (hash https://github.com/WebKit/WebKit/commit/dcf4e2756305e006aef71de8818fa0eae4ff90f1)<details>

| Misc | iOS, visionOS, tvOS & watchOS  | macOS  | Linux |  Windows |
| ----- | ---------------------- | ------- |  ----- |  --------- |
| [✅ 🧪 style](https://ews-build.webkit.org/#/builders/38/builds/98869 "Passed style check") | [❌ 🛠 ios](https://ews-build.webkit.org/#/builders/131/builds/18506 "Hash dcf4e275 for PR 43423 does not build (failure)") | [✅ 🛠 mac](https://ews-build.webkit.org/#/builders/138/builds/8748 "Built successfully") | [✅ 🛠 wpe](https://ews-build.webkit.org/#/builders/5/builds/103995 "Built successfully") | [✅ 🛠 win](https://ews-build.webkit.org/#/builders/59/builds/49458 "Built successfully") 
| [✅ 🧪 bindings](https://ews-build.webkit.org/#/builders/9/builds/100914 "Passed tests") | [❌ 🛠 ios-sim](https://ews-build.webkit.org/#/builders/130/builds/18801 "Hash dcf4e275 for PR 43423 does not build (failure)") | [✅ 🛠 mac-AS-debug](https://ews-build.webkit.org/#/builders/123/builds/26956 "Built successfully") | [  ~~🧪 wpe-wk2~~](https://ews-build.webkit.org/#/builders/34/builds/75271 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | [✅ 🧪 win-tests](https://ews-build.webkit.org/#/builders/60/builds/32404 "Passed tests") 
| [✅ 🧪 webkitperl](https://ews-build.webkit.org/#/builders/11/builds/101873 "Passed tests") | [❌ 🧪 ios-wk2](https://ews-build.webkit.org/#/builders/130/builds/18801 "Hash dcf4e275 for PR 43423 does not build (failure)") | [  ~~🧪 api-mac~~](https://ews-build.webkit.org/#/builders/18/builds/89292 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | [✅ 🧪 api-wpe](https://ews-build.webkit.org/#/builders/41/builds/55632 "Passed tests") | 
| | [❌ 🧪 ios-wk2-wpt](https://ews-build.webkit.org/#/builders/130/builds/18801 "Hash dcf4e275 for PR 43423 does not build (failure)") | [✅ 🧪 mac-wk1](https://ews-build.webkit.org/#/builders/135/builds/7264 "Passed tests") | [✅ 🛠 wpe-cairo](https://ews-build.webkit.org/#/builders/65/builds/48838 "Built successfully") | 
| | [❌ 🧪 api-ios](https://ews-build.webkit.org/#/builders/130/builds/18801 "Hash dcf4e275 for PR 43423 does not build (failure)") | [✅ 🧪 mac-wk2](https://ews-build.webkit.org/#/builders/136/builds/7337 "Passed tests") | [✅ 🛠 gtk](https://ews-build.webkit.org/#/builders/2/builds/106364 "Built successfully") | 
| | [❌ 🛠 vision](https://ews-build.webkit.org/#/builders/128/builds/25966 "Hash dcf4e275 for PR 43423 does not build (failure)") | [✅ 🧪 mac-AS-debug-wk2](https://ews-build.webkit.org/#/builders/122/builds/18932 "Passed tests") | [  ~~🧪 gtk-wk2~~](https://ews-build.webkit.org/#/builders/1/builds/84242 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | 
| | [❌ 🛠 vision-sim](https://ews-build.webkit.org/#/builders/121/builds/26341 "Hash dcf4e275 for PR 43423 does not build (failure)") | [✅ 🧪 mac-wk2-stress](https://ews-build.webkit.org/#/builders/8/builds/85490 "Passed tests") | [✅ 🧪 api-gtk](https://ews-build.webkit.org/#/builders/21/builds/83740 "Passed tests") | 
| | [⏳ 🧪 vision-wk2 ](https://ews-build.webkit.org/#/builders/visionOS-2-Simulator-WK2-Tests-EWS "Waiting in queue, processing has not started yet") | [✅ 🧪 mac-intel-wk2](https://ews-build.webkit.org/#/builders/137/builds/6057 "Passed tests") | [✅ 🛠 playstation](https://ews-build.webkit.org/#/builders/134/builds/19688 "Built successfully") | 
| | [❌ 🛠 tv](https://ews-build.webkit.org/#/builders/127/builds/25919 "Hash dcf4e275 for PR 43423 does not build (failure)") | [✅ 🛠 mac-safer-cpp](https://ews-build.webkit.org/#/builders/120/builds/31105 "Built successfully") | | 
| | [❌ 🛠 tv-sim](https://ews-build.webkit.org/#/builders/125/builds/25739 "Hash dcf4e275 for PR 43423 does not build (failure)") | | | 
| | [  ~~🛠 watch~~](https://ews-build.webkit.org/#/builders/129/builds/29059 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | | | 
| | [❌ 🛠 watch-sim](https://ews-build.webkit.org/#/builders/124/builds/27313 "Hash dcf4e275 for PR 43423 does not build (failure)") | | | </details>
author_association: COLLABORATOR
user: login: webkit-early-warning-system
created_at: 2025-04-08T17:37:57Z
body: EWS run on current version of this PR (hash https://github.com/WebKit/WebKit/commit/b248879bd902c642dd58ac2f964a96bfe63a4968)<details>

| Misc | iOS, visionOS, tvOS & watchOS  | macOS  | Linux |  Windows |
| ----- | ---------------------- | ------- |  ----- |  --------- |
| [✅ 🧪 style](https://ews-build.webkit.org/#/builders/38/builds/98889 "Passed style check") | [✅ 🛠 ios](https://ews-build.webkit.org/#/builders/131/builds/18526 "Built successfully") | [✅ 🛠 mac](https://ews-build.webkit.org/#/builders/138/builds/8768 "Built successfully") | [✅ 🛠 wpe](https://ews-build.webkit.org/#/builders/5/builds/104015 "Built successfully") | [✅ 🛠 win](https://ews-build.webkit.org/#/builders/59/builds/49478 "Built successfully") 
| [✅ 🧪 bindings](https://ews-build.webkit.org/#/builders/9/builds/100934 "Passed tests") | [✅ 🛠 ios-sim](https://ews-build.webkit.org/#/builders/130/builds/18821 "Built successfully") | [✅ 🛠 mac-AS-debug](https://ews-build.webkit.org/#/builders/123/builds/26976 "Built successfully") | [✅ 🧪 wpe-wk2](https://ews-build.webkit.org/#/builders/34/builds/75282 "Passed tests") | [✅ 🧪 win-tests](https://ews-build.webkit.org/#/builders/60/builds/32413 "Passed tests") 
| [✅ 🧪 webkitperl](https://ews-build.webkit.org/#/builders/11/builds/101893 "Passed tests") | [✅ 🧪 ios-wk2](https://ews-build.webkit.org/#/builders/132/builds/14299 "Passed tests") | [✅ 🧪 api-mac](https://ews-build.webkit.org/#/builders/18/builds/89306 "Passed tests") | [✅ 🧪 api-wpe](https://ews-build.webkit.org/#/builders/41/builds/55643 "Passed tests") | 
| | [  ~~🧪 ios-wk2-wpt~~](https://ews-build.webkit.org/#/builders/133/builds/14090 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | [✅ 🧪 mac-wk1](https://ews-build.webkit.org/#/builders/135/builds/7278 "Passed tests") | [✅ 🛠 wpe-cairo](https://ews-build.webkit.org/#/builders/65/builds/48858 "Built successfully") | 
| | [  ~~🧪 api-ios~~](https://ews-build.webkit.org/#/builders/13/builds/84027 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | [✅ 🧪 mac-wk2](https://ews-build.webkit.org/#/builders/136/builds/7351 "Passed tests") | [✅ 🛠 gtk](https://ews-build.webkit.org/#/builders/2/builds/106384 "Built successfully") | 
| | [✅ 🛠 vision](https://ews-build.webkit.org/#/builders/128/builds/25986 "Built successfully") | [✅ 🧪 mac-AS-debug-wk2](https://ews-build.webkit.org/#/builders/122/builds/18942 "Passed tests") | [✅ 🧪 gtk-wk2](https://ews-build.webkit.org/#/builders/1/builds/84246 "Passed tests") | 
| | [✅ 🛠 vision-sim](https://ews-build.webkit.org/#/builders/121/builds/26361 "Built successfully") | [✅ 🧪 mac-wk2-stress](https://ews-build.webkit.org/#/builders/8/builds/85504 "Passed tests") | [✅ 🧪 api-gtk](https://ews-build.webkit.org/#/builders/21/builds/83744 "Passed tests") | 
| [✅ 🛠 🧪 merge](https://ews-build.webkit.org/#/builders/19/builds/21226 "Built successfully and passed tests") | [✅ 🧪 vision-wk2](https://ews-build.webkit.org/#/builders/126/builds/28399 "Passed tests") | [✅ 🧪 mac-intel-wk2](https://ews-build.webkit.org/#/builders/137/builds/6071 "Passed tests") | [✅ 🛠 playstation](https://ews-build.webkit.org/#/builders/134/builds/19708 "Built successfully") | 
| | [✅ 🛠 tv](https://ews-build.webkit.org/#/builders/127/builds/25939 "Built successfully") | [✅ 🛠 mac-safer-cpp](https://ews-build.webkit.org/#/builders/120/builds/31125 "Built successfully") | | 
| | [✅ 🛠 tv-sim](https://ews-build.webkit.org/#/builders/125/builds/25759 "Built successfully") | | | 
| | [✅ 🛠 watch](https://ews-build.webkit.org/#/builders/129/builds/29079 "Built successfully") | | | 
| | [✅ 🛠 watch-sim](https://ews-build.webkit.org/#/builders/124/builds/27333 "Built successfully") | | | </details>
author_association: COLLABORATOR
user: login: webkit-commit-queue
created_at: 2025-04-08T20:16:41Z
body: Committed 293430@main (db75e4455949): <https://commits.webkit.org/293430@main>

Reviewed commits have been landed. Closing PR #43423 and removing active labels.
author_association: COLLABORATOR