Title: [277751] trunk/Source
Revision
277751
Author
wei...@apple.com
Date
2021-05-19 14:18:55 -0700 (Wed, 19 May 2021)

Log Message

HTMLCanvasElement toDataURL and toBlob do unnecessary data copies through a CFDataRef
https://bugs.webkit.org/show_bug.cgi?id=225853

Reviewed by Darin Adler.

When trying to encode either a PixelBuffer or a ImageBuffer to an image for
blob or dataURL creation, we were using a method that required always first
creating a CFDataRef and then copying that CFDataRef's data into what we needed.

Instead, we can use a callback based CGDataConsumer and some passed around
functors to get the data directly as it is being encoded and process it as
needed. ScopedLambda is used to avoid unnecessary inlining or allocation that
a template based functor or WTF::Function would require respectively.

* platform/graphics/cg/ImageBufferCGBackend.h:
* platform/graphics/cg/ImageBufferCGBackend.cpp:
(WebCore::ImageBufferCGBackend::toCFData const): Deleted.
(WebCore::ImageBufferCGBackend::copyPlatformImageForEncoding const):
Factor out PlatformImagePtr creation into its own function. This replaces the
toCFData virtual function, and is used in conjunction with the data/dataURL
functions in ImageBufferCGBackend to produce the encoded data.

(WebCore::ImageBufferCGBackend::toData const):
(WebCore::ImageBufferCGBackend::toDataURL const):
Rework to use copyPlatformImageForEncoding and data/dataURL in ImageBufferCGBackend
to produce the encoded data.

* platform/graphics/cg/ImageBufferIOSurfaceBackend.h:
* platform/graphics/cg/ImageBufferIOSurfaceBackend.cpp:
(WebCore::ImageBufferIOSurfaceBackend::copyPlatformImageForEncoding const):
(WebCore::ImageBufferIOSurfaceBackend::toCFData const): Deleted.
Like toCFData before it, copyPlatformImageForEncoding is virtual allowing for this
override in ImageBufferIOSurfaceBackend.

* platform/graphics/cg/ImageBufferUtilitiesCG.h:
* platform/graphics/cg/ImageBufferUtilitiesCG.cpp:
(WebCore::encode):
(WebCore::encodeToVector):
(WebCore::encodeToDataURL):
(WebCore::data):
(WebCore::dataURL):
(WebCore::encodeImage): Deleted.
(WebCore::cfData): Deleted.
(WebCore::dataVector): Deleted.
Replace encodeImage(), which filled in a CFMutableDataRef, with overloads of data() and
dataURL() that now use a callback based CGDataConsumer and some callback functors to
allow the encoded data to be consumed as it is being created. Also makes use of new
base64Encoded() adapter to base64 encode directly in makeString().

Modified Paths

Diff

Modified: trunk/Source/WTF/wtf/Forward.h (277750 => 277751)


--- trunk/Source/WTF/wtf/Forward.h	2021-05-19 21:00:39 UTC (rev 277750)
+++ trunk/Source/WTF/wtf/Forward.h	2021-05-19 21:18:55 UTC (rev 277751)
@@ -74,6 +74,7 @@
 template<typename T, typename = RawPtrTraits<T>> class Ref;
 template<typename T, typename = RawPtrTraits<T>, typename = DefaultRefDerefTraits<T>> class RefPtr;
 template<typename> class RetainPtr;
+template<typename> class ScopedLambda;
 template<typename> class StringBuffer;
 template<typename> class StringParsingBuffer;
 template<typename, typename = void> class StringTypeAdapter;
@@ -139,6 +140,7 @@
 using WTF::RefPtr;
 using WTF::RetainPtr;
 using WTF::SHA1;
+using WTF::ScopedLambda;
 using WTF::String;
 using WTF::StringBuffer;
 using WTF::StringBuilder;

Modified: trunk/Source/WebCore/ChangeLog (277750 => 277751)


--- trunk/Source/WebCore/ChangeLog	2021-05-19 21:00:39 UTC (rev 277750)
+++ trunk/Source/WebCore/ChangeLog	2021-05-19 21:18:55 UTC (rev 277751)
@@ -1,3 +1,54 @@
+2021-05-19  Sam Weinig  <wei...@apple.com>
+
+        HTMLCanvasElement toDataURL and toBlob do unnecessary data copies through a CFDataRef
+        https://bugs.webkit.org/show_bug.cgi?id=225853
+
+        Reviewed by Darin Adler.
+
+        When trying to encode either a PixelBuffer or a ImageBuffer to an image for
+        blob or dataURL creation, we were using a method that required always first
+        creating a CFDataRef and then copying that CFDataRef's data into what we needed.
+
+        Instead, we can use a callback based CGDataConsumer and some passed around
+        functors to get the data directly as it is being encoded and process it as
+        needed. ScopedLambda is used to avoid unnecessary inlining or allocation that
+        a template based functor or WTF::Function would require respectively.
+
+        * platform/graphics/cg/ImageBufferCGBackend.h:
+        * platform/graphics/cg/ImageBufferCGBackend.cpp:
+        (WebCore::ImageBufferCGBackend::toCFData const): Deleted.
+        (WebCore::ImageBufferCGBackend::copyPlatformImageForEncoding const):
+        Factor out PlatformImagePtr creation into its own function. This replaces the
+        toCFData virtual function, and is used in conjunction with the data/dataURL
+        functions in ImageBufferCGBackend to produce the encoded data.
+
+        (WebCore::ImageBufferCGBackend::toData const):
+        (WebCore::ImageBufferCGBackend::toDataURL const):
+        Rework to use copyPlatformImageForEncoding and data/dataURL in ImageBufferCGBackend
+        to produce the encoded data.
+
+        * platform/graphics/cg/ImageBufferIOSurfaceBackend.h:
+        * platform/graphics/cg/ImageBufferIOSurfaceBackend.cpp:
+        (WebCore::ImageBufferIOSurfaceBackend::copyPlatformImageForEncoding const):
+        (WebCore::ImageBufferIOSurfaceBackend::toCFData const): Deleted.
+        Like toCFData before it, copyPlatformImageForEncoding is virtual allowing for this
+        override in ImageBufferIOSurfaceBackend.
+
+        * platform/graphics/cg/ImageBufferUtilitiesCG.h:
+        * platform/graphics/cg/ImageBufferUtilitiesCG.cpp:
+        (WebCore::encode):
+        (WebCore::encodeToVector):
+        (WebCore::encodeToDataURL):
+        (WebCore::data):
+        (WebCore::dataURL):
+        (WebCore::encodeImage): Deleted.
+        (WebCore::cfData): Deleted.
+        (WebCore::dataVector): Deleted.
+        Replace encodeImage(), which filled in a CFMutableDataRef, with overloads of data() and
+        dataURL() that now use a callback based CGDataConsumer and some callback functors to 
+        allow the encoded data to be consumed as it is being created. Also makes use of new 
+        base64Encoded() adapter to base64 encode directly in makeString().
+
 2021-05-19  Chris Dumez  <cdu...@apple.com>
 
         Drop "get" prefix from SQLiteStatement member functions as well as out-parameters

Modified: trunk/Source/WebCore/platform/graphics/cg/ImageBufferCGBackend.cpp (277750 => 277751)


--- trunk/Source/WebCore/platform/graphics/cg/ImageBufferCGBackend.cpp	2021-05-19 21:00:39 UTC (rev 277750)
+++ trunk/Source/WebCore/platform/graphics/cg/ImageBufferCGBackend.cpp	2021-05-19 21:18:55 UTC (rev 277751)
@@ -168,20 +168,11 @@
     CGContextTranslateCTM(cgContext, -destRect.x(), -destRect.maxY());
 }
 
-RetainPtr<CFDataRef> ImageBufferCGBackend::toCFData(const String& mimeType, Optional<double> quality, PreserveResolution preserveResolution) const
+RetainPtr<CGImageRef> ImageBufferCGBackend::copyCGImageForEncoding(CFStringRef destinationUTI, PreserveResolution preserveResolution) const
 {
-#if ENABLE(GPU_PROCESS)
-    ASSERT_IMPLIES(!isInGPUProcess(), MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
-#else
-    ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
-#endif
+    if (CFEqual(destinationUTI, jpegUTI())) {
+        // FIXME: Should this be using the same logic as ImageBufferUtilitiesCG?
 
-    auto uti = utiFromImageBufferMIMEType(mimeType);
-    ASSERT(uti);
-
-    PlatformImagePtr image;
-
-    if (CFEqual(uti.get(), jpegUTI())) {
         // JPEGs don't have an alpha channel, so we have to manually composite on top of black.
         PixelBufferFormat format { AlphaPremultiplication::Premultiplied, PixelFormat::RGBA8, DestinationColorSpace::SRGB };
         auto pixelBuffer = getPixelBuffer(format, logicalRect());
@@ -197,49 +188,57 @@
         auto dataProvider = adoptCF(CGDataProviderCreateWithData(&pixelArray.leakRef(), data, dataSize, [] (void* context, const void*, size_t) {
             static_cast<JSC::Uint8ClampedArray*>(context)->deref();
         }));
-        
         if (!dataProvider)
             return nullptr;
 
         auto imageSize = pixelBuffer->size();
-        image = adoptCF(CGImageCreate(imageSize.width(), imageSize.height(), 8, 32, 4 * imageSize.width(), cachedCGColorSpace(pixelBuffer->format().colorSpace), kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipLast, dataProvider.get(), 0, false, kCGRenderingIntentDefault));
-    } else if (resolutionScale() == 1 || preserveResolution == PreserveResolution::Yes) {
+        return adoptCF(CGImageCreate(imageSize.width(), imageSize.height(), 8, 32, 4 * imageSize.width(), cachedCGColorSpace(pixelBuffer->format().colorSpace), kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipLast, dataProvider.get(), 0, false, kCGRenderingIntentDefault));
+    }
+
+    if (resolutionScale() == 1 || preserveResolution == PreserveResolution::Yes) {
         auto nativeImage = copyNativeImage(CopyBackingStore);
         if (!nativeImage)
             return nullptr;
-        image = nativeImage->platformImage();
-        image = createCroppedImageIfNecessary(image.get(), backendSize());
-    } else {
-        auto nativeImage = copyNativeImage(DontCopyBackingStore);
-        if (!nativeImage)
-            return nullptr;
-        image = nativeImage->platformImage();
-        auto context = adoptCF(CGBitmapContextCreate(0, backendSize().width(), backendSize().height(), 8, 4 * backendSize().width(), sRGBColorSpaceRef(), kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host));
-        CGContextSetBlendMode(context.get(), kCGBlendModeCopy);
-        CGContextClipToRect(context.get(), CGRectMake(0, 0, backendSize().width(), backendSize().height()));
-        CGContextDrawImage(context.get(), CGRectMake(0, 0, backendSize().width(), backendSize().height()), image.get());
-        image = adoptCF(CGBitmapContextCreateImage(context.get()));
+        return createCroppedImageIfNecessary(nativeImage->platformImage().get(), backendSize());
     }
-
-    auto cfData = adoptCF(CFDataCreateMutable(kCFAllocatorDefault, 0));
-    if (!encodeImage(image.get(), uti.get(), quality, cfData.get()))
+    
+    auto nativeImage = copyNativeImage(DontCopyBackingStore);
+    if (!nativeImage)
         return nullptr;
-
-    return WTFMove(cfData);
+    auto image = nativeImage->platformImage();
+    auto context = adoptCF(CGBitmapContextCreate(0, backendSize().width(), backendSize().height(), 8, 4 * backendSize().width(), sRGBColorSpaceRef(), kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host));
+    CGContextSetBlendMode(context.get(), kCGBlendModeCopy);
+    CGContextClipToRect(context.get(), CGRectMake(0, 0, backendSize().width(), backendSize().height()));
+    CGContextDrawImage(context.get(), CGRectMake(0, 0, backendSize().width(), backendSize().height()), image.get());
+    return adoptCF(CGBitmapContextCreateImage(context.get()));
 }
 
 Vector<uint8_t> ImageBufferCGBackend::toData(const String& mimeType, Optional<double> quality) const
 {
-    if (auto data = "" quality, PreserveResolution::No))
-        return dataVector(data.get());
-    return { };
+#if ENABLE(GPU_PROCESS)
+    ASSERT_IMPLIES(!isInGPUProcess(), MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
+#else
+    ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
+#endif
+
+    auto destinationUTI = utiFromImageBufferMIMEType(mimeType);
+    auto image = copyCGImageForEncoding(destinationUTI.get(), PreserveResolution::No);
+
+    return WebCore::data(image.get(), destinationUTI.get(), quality);
 }
 
 String ImageBufferCGBackend::toDataURL(const String& mimeType, Optional<double> quality, PreserveResolution preserveResolution) const
 {
-    if (auto data = "" quality, preserveResolution))
-        return dataURL(data.get(), mimeType);
-    return "data:,"_s;
+#if ENABLE(GPU_PROCESS)
+    ASSERT_IMPLIES(!isInGPUProcess(), MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
+#else
+    ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
+#endif
+
+    auto destinationUTI = utiFromImageBufferMIMEType(mimeType);
+    auto image = copyCGImageForEncoding(destinationUTI.get(), preserveResolution);
+
+    return WebCore::dataURL(image.get(), destinationUTI.get(), mimeType, quality);
 }
 
 std::unique_ptr<ThreadSafeImageBufferFlusher> ImageBufferCGBackend::createFlusher()

Modified: trunk/Source/WebCore/platform/graphics/cg/ImageBufferCGBackend.h (277750 => 277751)


--- trunk/Source/WebCore/platform/graphics/cg/ImageBufferCGBackend.h	2021-05-19 21:00:39 UTC (rev 277750)
+++ trunk/Source/WebCore/platform/graphics/cg/ImageBufferCGBackend.h	2021-05-19 21:18:55 UTC (rev 277751)
@@ -28,7 +28,10 @@
 #if USE(CG)
 
 #include "ImageBufferBackend.h"
+#include <wtf/Forward.h>
 
+typedef struct CGImage* CGImageRef;
+
 namespace WebCore {
 
 class WEBCORE_EXPORT ImageBufferCGBackend : public ImageBufferBackend {
@@ -53,7 +56,8 @@
 
     static RetainPtr<CGColorSpaceRef> contextColorSpace(const GraphicsContext&);
     void setupContext() const;
-    virtual RetainPtr<CFDataRef> toCFData(const String& mimeType, Optional<double> quality, PreserveResolution) const;
+
+    virtual RetainPtr<CGImageRef> copyCGImageForEncoding(CFStringRef destinationUTI, PreserveResolution) const;
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/platform/graphics/cg/ImageBufferIOSurfaceBackend.cpp (277750 => 277751)


--- trunk/Source/WebCore/platform/graphics/cg/ImageBufferIOSurfaceBackend.cpp	2021-05-19 21:00:39 UTC (rev 277750)
+++ trunk/Source/WebCore/platform/graphics/cg/ImageBufferIOSurfaceBackend.cpp	2021-05-19 21:18:55 UTC (rev 277751)
@@ -166,7 +166,7 @@
         destContext.drawNativeImage(*image, backendSize, destRect, adjustedSrcRect, options);
 }
 
-RetainPtr<CFDataRef> ImageBufferIOSurfaceBackend::toCFData(const String& mimeType, Optional<double> quality, PreserveResolution preserveResolution) const
+RetainPtr<CGImageRef> ImageBufferIOSurfaceBackend::copyCGImageForEncoding(CFStringRef destinationUTI, PreserveResolution preserveResolution) const
 {
     if (m_requiresDrawAfterPutPixelBuffer) {
         // Force recreating the IOSurface cached image.
@@ -174,7 +174,7 @@
         context().fillRect(FloatRect(1, 1, 0, 0));
         m_requiresDrawAfterPutPixelBuffer = false;
     }
-    return ImageBufferCGBackend::toCFData(mimeType, quality, preserveResolution);
+    return ImageBufferCGBackend::copyCGImageForEncoding(destinationUTI, preserveResolution);
 }
 
 Vector<uint8_t> ImageBufferIOSurfaceBackend::toBGRAData() const

Modified: trunk/Source/WebCore/platform/graphics/cg/ImageBufferIOSurfaceBackend.h (277750 => 277751)


--- trunk/Source/WebCore/platform/graphics/cg/ImageBufferIOSurfaceBackend.h	2021-05-19 21:00:39 UTC (rev 277750)
+++ trunk/Source/WebCore/platform/graphics/cg/ImageBufferIOSurfaceBackend.h	2021-05-19 21:18:55 UTC (rev 277751)
@@ -60,7 +60,6 @@
 
     void drawConsuming(GraphicsContext&, const FloatRect& destRect, const FloatRect& srcRect, const ImagePaintingOptions&) override;
 
-    RetainPtr<CFDataRef> toCFData(const String& mimeType, Optional<double> quality, PreserveResolution) const override;
     Vector<uint8_t> toBGRAData() const override;
 
     Optional<PixelBuffer> getPixelBuffer(const PixelBufferFormat& outputFormat, const IntRect&) const override;
@@ -78,6 +77,9 @@
     static RetainPtr<CGColorSpaceRef> contextColorSpace(const GraphicsContext&);
     unsigned bytesPerRow() const override;
 
+    // ImageBufferCGBackend overrides.
+    RetainPtr<CGImageRef> copyCGImageForEncoding(CFStringRef destinationUTI, PreserveResolution) const final;
+
     std::unique_ptr<IOSurface> m_surface;
     mutable bool m_requiresDrawAfterPutPixelBuffer { false };
 

Modified: trunk/Source/WebCore/platform/graphics/cg/ImageBufferUtilitiesCG.cpp (277750 => 277751)


--- trunk/Source/WebCore/platform/graphics/cg/ImageBufferUtilitiesCG.cpp	2021-05-19 21:00:39 UTC (rev 277750)
+++ trunk/Source/WebCore/platform/graphics/cg/ImageBufferUtilitiesCG.cpp	2021-05-19 21:18:55 UTC (rev 277751)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018-2020 Apple Inc. All rights reserved.
+ * Copyright (C) 2018-2021 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -33,6 +33,7 @@
 #include "PixelBuffer.h"
 #include <ImageIO/ImageIO.h>
 #include <wtf/CheckedArithmetic.h>
+#include <wtf/ScopedLambda.h>
 #include <wtf/text/Base64.h>
 
 #if PLATFORM(COCOA)
@@ -41,6 +42,8 @@
 
 namespace WebCore {
 
+using PutBytesCallback = size_t(const void*, size_t);
+
 uint8_t verifyImageBufferIsBigEnough(const void* buffer, size_t bufferSize)
 {
     RELEASE_ASSERT(bufferSize);
@@ -93,37 +96,46 @@
 ALLOW_DEPRECATED_DECLARATIONS_END
 }
 
-bool encodeImage(CGImageRef image, CFStringRef uti, Optional<double> quality, CFMutableDataRef data)
+static bool encode(CGImageRef image, CFStringRef destinationUTI, Optional<double> quality, const ScopedLambda<PutBytesCallback>& function)
 {
-    if (!image || !uti || !data)
+    if (!image || !destinationUTI)
         return false;
 
-    auto destination = adoptCF(CGImageDestinationCreateWithData(data, uti, 1, 0));
-    if (!destination)
-        return false;
+    CGDataConsumerCallbacks callbacks {
+        [](void* context, const void* buffer, size_t count) -> size_t {
+            auto functor = *static_cast<const ScopedLambda<PutBytesCallback>*>(context);
+            return functor(buffer, count);
+        },
+        nullptr
+    };
 
-    RetainPtr<CFDictionaryRef> imageProperties;
-    if (CFEqual(uti, jpegUTI()) && quality && *quality >= 0.0 && *quality <= 1.0) {
-        // Apply the compression quality to the JPEG image destination.
-        auto compressionQuality = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &*quality));
-        const void* key = kCGImageDestinationLossyCompressionQuality;
-        const void* value = compressionQuality.get();
-        imageProperties = adoptCF(CFDictionaryCreate(0, &key, &value, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
-    }
+    auto consumer = adoptCF(CGDataConsumerCreate(const_cast<ScopedLambda<PutBytesCallback>*>(&function), &callbacks));
+    auto destination = adoptCF(CGImageDestinationCreateWithDataConsumer(consumer.get(), destinationUTI, 1, nullptr));
+    
+    auto imageProperties = [&] () -> RetainPtr<CFDictionaryRef> {
+        if (CFEqual(destinationUTI, jpegUTI()) && quality && *quality >= 0.0 && *quality <= 1.0) {
+            // Apply the compression quality to the JPEG image destination.
+            auto compressionQuality = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &*quality));
+            const void* key = kCGImageDestinationLossyCompressionQuality;
+            const void* value = compressionQuality.get();
+            return adoptCF(CFDictionaryCreate(0, &key, &value, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
+        }
+        return nullptr;
+    }();
 
-    // Setting kCGImageDestinationBackgroundColor to black for JPEG images in imageProperties would save some math
+    // FIXME: Setting kCGImageDestinationBackgroundColor to black for JPEG images in imageProperties would save some math
     // in the calling functions, but it doesn't seem to work.
 
     CGImageDestinationAddImage(destination.get(), image, imageProperties.get());
+
     return CGImageDestinationFinalize(destination.get());
 }
 
-static RetainPtr<CFDataRef> cfData(const PixelBuffer& source, const String& mimeType, Optional<double> quality)
+static bool encode(const PixelBuffer& source, const String& mimeType, Optional<double> quality, const ScopedLambda<PutBytesCallback>& function)
 {
     ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
 
-    auto uti = utiFromImageBufferMIMEType(mimeType);
-    ASSERT(uti);
+    auto destinationUTI = utiFromImageBufferMIMEType(mimeType);
 
     CGImageAlphaInfo dataAlphaInfo = kCGImageAlphaLast;
     
@@ -132,10 +144,12 @@
 
     Vector<uint8_t> premultipliedData;
 
-    if (CFEqual(uti.get(), jpegUTI())) {
+    if (CFEqual(destinationUTI.get(), jpegUTI())) {
+        // FIXME: Use PixelBufferConversion for this once it supports RGBX.
+
         // JPEGs don't have an alpha channel, so we have to manually composite on top of black.
         if (!premultipliedData.tryReserveCapacity(dataSize))
-            return nullptr;
+            return false;
 
         premultipliedData.grow(dataSize);
         unsigned char* buffer = premultipliedData.data();
@@ -165,39 +179,54 @@
     auto imageSize = source.size();
     auto image = adoptCF(CGImageCreate(imageSize.width(), imageSize.height(), 8, 32, 4 * imageSize.width(), cachedCGColorSpace(source.format().colorSpace), kCGBitmapByteOrderDefault | dataAlphaInfo, dataProvider.get(), 0, false, kCGRenderingIntentDefault));
 
-    auto cfData = adoptCF(CFDataCreateMutable(kCFAllocatorDefault, 0));
-    if (!encodeImage(image.get(), uti.get(), quality, cfData.get()))
-        return nullptr;
+    return encode(image.get(), destinationUTI.get(), quality, function);
+}
 
-    return WTFMove(cfData);
+template<typename Source, typename SourceDescription> static Vector<uint8_t> encodeToVector(Source&& source, SourceDescription&& sourceDescription, Optional<double> quality)
+{
+    Vector<uint8_t> result;
+
+    bool success = encode(std::forward<Source>(source), std::forward<SourceDescription>(sourceDescription), quality, scopedLambdaRef<PutBytesCallback>([&] (const void* data, size_t length) {
+        result.append(static_cast<const uint8_t*>(data), length);
+        return length;
+    }));
+    if (!success)
+        return { };
+
+    return result;
 }
 
-String dataURL(CFDataRef data, const String& mimeType)
+template<typename Source, typename SourceDescription> static String encodeToDataURL(Source&& source, SourceDescription&& sourceDescription, const String& mimeType, Optional<double> quality)
 {
-    return makeString("data:", mimeType, ";base64,", base64Encoded(CFDataGetBytePtr(data), CFDataGetLength(data)));
+    // FIXME: This could be done more efficiently with a streaming base64 encoder.
+
+    auto encodedData = encodeToVector(std::forward<Source>(source), std::forward<SourceDescription>(sourceDescription), quality);
+    if (encodedData.isEmpty())
+        return "data:,"_s;
+
+    return makeString("data:", mimeType, ";base64,", base64Encoded(encodedData));
 }
 
-String dataURL(const PixelBuffer& source, const String& mimeType, Optional<double> quality)
+Vector<uint8_t> data(CGImageRef image, CFStringRef destinationUTI, Optional<double> quality)
 {
-    if (auto data = "" mimeType, quality))
-        return dataURL(data.get(), mimeType);
-    return "data:,"_s;
+    return encodeToVector(image, destinationUTI, quality);
 }
 
-Vector<uint8_t> dataVector(CFDataRef cfData)
+Vector<uint8_t> data(const PixelBuffer& pixelBuffer, const String& mimeType, Optional<double> quality)
 {
-    Vector<uint8_t> data;
-    data.append(CFDataGetBytePtr(cfData), CFDataGetLength(cfData));
-    return data;
+    return encodeToVector(pixelBuffer, mimeType, quality);
 }
 
-Vector<uint8_t> data(const PixelBuffer& source, const String& mimeType, Optional<double> quality)
+String dataURL(CGImageRef image, CFStringRef destinationUTI, const String& mimeType, Optional<double> quality)
 {
-    if (auto data = "" mimeType, quality))
-        return dataVector(data.get());
-    return { };
+    return encodeToDataURL(image, destinationUTI, mimeType, quality);
 }
 
+String dataURL(const PixelBuffer& pixelBuffer, const String& mimeType, Optional<double> quality)
+{
+    return encodeToDataURL(pixelBuffer, mimeType, mimeType, quality);
+}
+
 } // namespace WebCore
 
 #endif // USE(CG)

Modified: trunk/Source/WebCore/platform/graphics/cg/ImageBufferUtilitiesCG.h (277750 => 277751)


--- trunk/Source/WebCore/platform/graphics/cg/ImageBufferUtilitiesCG.h	2021-05-19 21:00:39 UTC (rev 277750)
+++ trunk/Source/WebCore/platform/graphics/cg/ImageBufferUtilitiesCG.h	2021-05-19 21:18:55 UTC (rev 277751)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018-2020 Apple Inc. All rights reserved.
+ * Copyright (C) 2018-2021 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -28,10 +28,6 @@
 #if USE(CG)
 
 #include <wtf/Forward.h>
-#include <wtf/Optional.h>
-#include <wtf/RetainPtr.h>
-#include <wtf/Vector.h>
-#include <wtf/text/WTFString.h>
 
 namespace WebCore {
 
@@ -42,14 +38,12 @@
 CFStringRef jpegUTI();
 RetainPtr<CFStringRef> utiFromImageBufferMIMEType(const String&);
 
-bool encodeImage(CGImageRef, CFStringRef uti, Optional<double> quality, CFMutableDataRef);
+Vector<uint8_t> data(CGImageRef, CFStringRef destinationUTI, Optional<double> quality);
+Vector<uint8_t> data(const PixelBuffer&, const String& mimeType, Optional<double> quality);
 
-String dataURL(CFDataRef, const String& mimeType);
+String dataURL(CGImageRef, CFStringRef destinationUTI, const String& mimeType, Optional<double> quality);
 String dataURL(const PixelBuffer&, const String& mimeType, Optional<double> quality);
 
-Vector<uint8_t> dataVector(CFDataRef);
-Vector<uint8_t> data(const PixelBuffer&, const String& mimeType, Optional<double> quality);
-
 } // namespace WebCore
 
 #endif // USE(CG)
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to