Use "= default" to denote default constructor or destructor
[WebKit-https.git] / Source / WebCore / platform / graphics / cg / ImageBufferCG.cpp
index 7bd7f52..2f749a8 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
- * Copyright (C) 2008, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2008-2017 Apple Inc. All rights reserved.
  * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -28,6 +28,8 @@
 #include "config.h"
 #include "ImageBuffer.h"
 
+#if USE(CG)
+
 #include "BitmapImage.h"
 #include "GraphicsContext.h"
 #include "GraphicsContextCG.h"
 #include <wtf/RetainPtr.h>
 #include <wtf/text/Base64.h>
 #include <wtf/text/WTFString.h>
+
 #if PLATFORM(COCOA)
-#include "WebCoreSystemInterface.h"
+#include "UTIUtilities.h"
 #endif
 
 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
 #include "IOSurface.h"
-#include "IOSurfaceSPI.h"
+#include <pal/spi/cocoa/IOSurfaceSPI.h>
 #endif
 
 // CA uses ARGB32 for textures and ARGB32 -> ARGB32 resampling is optimized.
 
 namespace WebCore {
 
-static void releaseImageData(void*, const void* data, size_t)
-{
-    fastFree(const_cast<void*>(data));
-}
-
 static FloatSize scaleSizeToUserSpace(const FloatSize& logicalSize, const IntSize& backingStoreSize, const IntSize& internalSize)
 {
     float xMagnification = static_cast<float>(backingStoreSize.width()) / internalSize.width();
@@ -69,10 +67,50 @@ static FloatSize scaleSizeToUserSpace(const FloatSize& logicalSize, const IntSiz
     return FloatSize(logicalSize.width() * xMagnification, logicalSize.height() * yMagnification);
 }
 
-ImageBuffer::ImageBuffer(const FloatSize& size, float resolutionScale, ColorSpace imageColorSpace, RenderingMode renderingMode, bool& success)
+std::unique_ptr<ImageBuffer> ImageBuffer::createCompatibleBuffer(const FloatSize& size, const GraphicsContext& context)
+{
+    if (size.isEmpty())
+        return nullptr;
+
+    RetainPtr<CGColorSpaceRef> colorSpace;
+#if PLATFORM(COCOA)
+    CGContextRef cgContext = context.platformContext();
+    switch (CGContextGetType(cgContext)) {
+    case kCGContextTypeBitmap:
+        colorSpace = CGBitmapContextGetColorSpace(cgContext);
+        break;
+#if USE(IOSURFACE)
+    case kCGContextTypeIOSurface:
+        colorSpace = CGIOSurfaceContextGetColorSpace(cgContext);
+        break;
+#endif
+    default:
+        colorSpace = adoptCF(CGContextCopyDeviceColorSpace(cgContext));
+    }
+
+    if (!colorSpace)
+        colorSpace = sRGBColorSpaceRef();
+#else
+    colorSpace = sRGBColorSpaceRef();
+#endif
+    RenderingMode renderingMode = context.renderingMode();
+    IntSize scaledSize = ImageBuffer::compatibleBufferSize(size, context);
+    bool success = false;
+    std::unique_ptr<ImageBuffer> buffer(new ImageBuffer(scaledSize, 1, colorSpace.get(), renderingMode, success));
+
+    if (!success)
+        return nullptr;
+
+    // Set up a corresponding scale factor on the graphics context.
+    buffer->context().scale(scaledSize / size);
+    return buffer;
+}
+
+ImageBuffer::ImageBuffer(const FloatSize& size, float resolutionScale, CGColorSpaceRef colorSpace, RenderingMode renderingMode, bool& success)
     : m_logicalSize(size)
     , m_resolutionScale(resolutionScale)
 {
+    success = false; // Make early return mean failure.
     float scaledWidth = ceilf(resolutionScale * size.width());
     float scaledHeight = ceilf(resolutionScale * size.height());
 
@@ -83,7 +121,6 @@ ImageBuffer::ImageBuffer(const FloatSize& size, float resolutionScale, ColorSpac
     m_size = IntSize(scaledWidth, scaledHeight);
     m_data.backingStoreSize = m_size;
 
-    success = false;  // Make early return mean failure.
     bool accelerateRendering = renderingMode == Accelerated;
     if (m_size.width() <= 0 || m_size.height() <= 0)
         return;
@@ -107,18 +144,21 @@ ImageBuffer::ImageBuffer(const FloatSize& size, float resolutionScale, ColorSpac
     ASSERT(renderingMode == Unaccelerated);
 #endif
 
-    m_data.colorSpace = cachedCGColorSpace(imageColorSpace);
+    m_data.colorSpace = colorSpace;
 
     RetainPtr<CGContextRef> cgContext;
     if (accelerateRendering) {
 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
-        FloatSize userBounds = scaleSizeToUserSpace(FloatSize(width.unsafeGet(), height.unsafeGet()), m_data.backingStoreSize, m_size);
-        m_data.surface = IOSurface::create(m_data.backingStoreSize, IntSize(userBounds), imageColorSpace);
-        cgContext = m_data.surface->ensurePlatformContext();
-        if (cgContext)
-            CGContextClearRect(cgContext.get(), FloatRect(FloatPoint(), userBounds));
+        FloatSize userBounds = sizeForDestinationSize(FloatSize(width.unsafeGet(), height.unsafeGet()));
+        m_data.surface = IOSurface::create(m_data.backingStoreSize, IntSize(userBounds), colorSpace);
+        if (m_data.surface) {
+            cgContext = m_data.surface->ensurePlatformContext();
+            if (cgContext)
+                CGContextClearRect(cgContext.get(), FloatRect(FloatPoint(), userBounds));
+            else
+                m_data.surface = nullptr;
+        }
 #endif
-
         if (!cgContext)
             accelerateRendering = false; // If allocation fails, fall back to non-accelerated path.
     }
@@ -134,6 +174,9 @@ ImageBuffer::ImageBuffer(const FloatSize& size, float resolutionScale, ColorSpac
         m_data.bitmapInfo = kCGImageAlphaPremultipliedLast;
 #endif
         cgContext = adoptCF(CGBitmapContextCreate(m_data.data, m_data.backingStoreSize.width(), m_data.backingStoreSize.height(), 8, m_data.bytesPerRow.unsafeGet(), m_data.colorSpace, m_data.bitmapInfo));
+        const auto releaseImageData = [] (void*, const void* data, size_t) {
+            fastFree(const_cast<void*>(data));
+        };
         // Create a live image that wraps the data.
         m_data.dataProvider = adoptCF(CGDataProviderCreateWithData(0, m_data.data, numBytes.unsafeGet(), releaseImageData));
 
@@ -150,9 +193,40 @@ ImageBuffer::ImageBuffer(const FloatSize& size, float resolutionScale, ColorSpac
     success = true;
 }
 
-ImageBuffer::~ImageBuffer()
+ImageBuffer::ImageBuffer(const FloatSize& size, float resolutionScale, ColorSpace imageColorSpace, RenderingMode renderingMode, bool& success)
+    : ImageBuffer(size, resolutionScale, cachedCGColorSpace(imageColorSpace), renderingMode, success)
+{
+}
+
+ImageBuffer::~ImageBuffer() = default;
+
+FloatSize ImageBuffer::sizeForDestinationSize(FloatSize destinationSize) const
+{
+    return scaleSizeToUserSpace(destinationSize, m_data.backingStoreSize, internalSize());
+}
+
+#if USE(IOSURFACE_CANVAS_BACKING_STORE)
+size_t ImageBuffer::memoryCost() const
+{
+    // memoryCost() may be invoked concurrently from a GC thread, and we need to be careful about what data we access here and how.
+    // It's safe to access internalSize() because it doesn't do any pointer chasing.
+    // It's safe to access m_data.surface because the surface can only be assigned during construction of this ImageBuffer.
+    // It's safe to access m_data.surface->totalBytes() because totalBytes() doesn't chase pointers.
+    if (m_data.surface)
+        return m_data.surface->totalBytes();
+    return 4 * internalSize().width() * internalSize().height();
+}
+
+size_t ImageBuffer::externalMemoryCost() const
 {
+    // externalMemoryCost() may be invoked concurrently from a GC thread, and we need to be careful about what data we access here and how.
+    // It's safe to access m_data.surface because the surface can only be assigned during construction of this ImageBuffer.
+    // It's safe to access m_data.surface->totalBytes() because totalBytes() doesn't chase pointers.
+    if (m_data.surface)
+        return m_data.surface->totalBytes();
+    return 0;
 }
+#endif
 
 GraphicsContext& ImageBuffer::context() const
 {
@@ -170,25 +244,20 @@ void ImageBuffer::flushContext() const
 
 static RetainPtr<CGImageRef> createCroppedImageIfNecessary(CGImageRef image, const IntSize& bounds)
 {
-    if (image && (CGImageGetWidth(image) != static_cast<size_t>(bounds.width())
-        || CGImageGetHeight(image) != static_cast<size_t>(bounds.height()))) {
+    if (image && (CGImageGetWidth(image) != static_cast<size_t>(bounds.width()) || CGImageGetHeight(image) != static_cast<size_t>(bounds.height())))
         return adoptCF(CGImageCreateWithImageInRect(image, CGRectMake(0, 0, bounds.width(), bounds.height())));
-    }
     return image;
 }
 
-RefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, ScaleBehavior scaleBehavior) const
+static RefPtr<Image> createBitmapImageAfterScalingIfNeeded(RetainPtr<CGImageRef>&& image, IntSize internalSize, IntSize logicalSize, IntSize backingStoreSize, float resolutionScale, ScaleBehavior scaleBehavior)
 {
-    RetainPtr<CGImageRef> image;
-    if (m_resolutionScale == 1 || scaleBehavior == Unscaled) {
-        image = copyNativeImage(copyBehavior);
-        image = createCroppedImageIfNecessary(image.get(), internalSize());
-    } else {
-        image = copyNativeImage(DontCopyBackingStore);
-        RetainPtr<CGContextRef> context = adoptCF(CGBitmapContextCreate(0, logicalSize().width(), logicalSize().height(), 8, 4 * logicalSize().width(), deviceRGBColorSpaceRef(), kCGImageAlphaPremultipliedLast));
+    if (resolutionScale == 1 || scaleBehavior == Unscaled)
+        image = createCroppedImageIfNecessary(image.get(), internalSize);
+    else {
+        auto context = adoptCF(CGBitmapContextCreate(0, logicalSize.width(), logicalSize.height(), 8, 4 * logicalSize.width(), sRGBColorSpaceRef(), kCGImageAlphaPremultipliedLast));
         CGContextSetBlendMode(context.get(), kCGBlendModeCopy);
-        CGContextClipToRect(context.get(), FloatRect(FloatPoint::zero(), logicalSize()));
-        FloatSize imageSizeInUserSpace = scaleSizeToUserSpace(logicalSize(), m_data.backingStoreSize, internalSize());
+        CGContextClipToRect(context.get(), FloatRect(FloatPoint::zero(), logicalSize));
+        FloatSize imageSizeInUserSpace = scaleSizeToUserSpace(logicalSize, backingStoreSize, internalSize);
         CGContextDrawImage(context.get(), FloatRect(FloatPoint::zero(), imageSizeInUserSpace), image.get());
         image = adoptCF(CGBitmapContextCreateImage(context.get()));
     }
@@ -196,7 +265,28 @@ RefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, ScaleBehavio
     if (!image)
         return nullptr;
 
-    return BitmapImage::create(image.get());
+    return BitmapImage::create(WTFMove(image));
+}
+
+RefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, ScaleBehavior scaleBehavior) const
+{
+    RetainPtr<CGImageRef> image;
+    if (m_resolutionScale == 1 || scaleBehavior == Unscaled)
+        image = copyNativeImage(copyBehavior);
+    else
+        image = copyNativeImage(DontCopyBackingStore);
+
+    return createBitmapImageAfterScalingIfNeeded(WTFMove(image), internalSize(), logicalSize(), m_data.backingStoreSize, m_resolutionScale, scaleBehavior);
+}
+
+RefPtr<Image> ImageBuffer::sinkIntoImage(std::unique_ptr<ImageBuffer> imageBuffer, ScaleBehavior scaleBehavior)
+{
+    IntSize internalSize = imageBuffer->internalSize();
+    IntSize logicalSize = imageBuffer->logicalSize();
+    IntSize backingStoreSize = imageBuffer->m_data.backingStoreSize;
+    float resolutionScale = imageBuffer->m_resolutionScale;
+
+    return createBitmapImageAfterScalingIfNeeded(sinkIntoNativeImage(WTFMove(imageBuffer)), internalSize, logicalSize, backingStoreSize, resolutionScale, scaleBehavior);
 }
 
 BackingStoreCopy ImageBuffer::fastCopyImageMode()
@@ -204,6 +294,18 @@ BackingStoreCopy ImageBuffer::fastCopyImageMode()
     return DontCopyBackingStore;
 }
 
+RetainPtr<CGImageRef> ImageBuffer::sinkIntoNativeImage(std::unique_ptr<ImageBuffer> imageBuffer)
+{
+#if USE(IOSURFACE_CANVAS_BACKING_STORE)
+    if (!imageBuffer->m_data.surface)
+        return imageBuffer->copyNativeImage(DontCopyBackingStore);
+
+    return IOSurface::sinkIntoImage(IOSurface::createFromImageBuffer(WTFMove(imageBuffer)));
+#else
+    return imageBuffer->copyNativeImage(DontCopyBackingStore);
+#endif
+}
+
 RetainPtr<CGImageRef> ImageBuffer::copyNativeImage(BackingStoreCopy copyBehavior) const
 {
     RetainPtr<CGImageRef> image;
@@ -228,11 +330,31 @@ RetainPtr<CGImageRef> ImageBuffer::copyNativeImage(BackingStoreCopy copyBehavior
     return image;
 }
 
-void ImageBuffer::draw(GraphicsContext& destContext, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode, bool useLowQualityScale)
+void ImageBuffer::drawConsuming(std::unique_ptr<ImageBuffer> imageBuffer, GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode)
 {
-    UNUSED_PARAM(useLowQualityScale);
-    ColorSpace colorSpace = (&destContext == &context()) ? ColorSpaceDeviceRGB : styleColorSpace;
+#if USE(IOSURFACE_CANVAS_BACKING_STORE)
+    if (!imageBuffer->m_data.surface) {
+        imageBuffer->draw(destContext, destRect, srcRect, op, blendMode);
+        return;
+    }
+    
+    ASSERT(destContext.isAcceleratedContext());
+    
+    float resolutionScale = imageBuffer->m_resolutionScale;
+    IntSize backingStoreSize = imageBuffer->m_data.backingStoreSize;
+
+    RetainPtr<CGImageRef> image = IOSurface::sinkIntoImage(IOSurface::createFromImageBuffer(WTFMove(imageBuffer)));
+    
+    FloatRect adjustedSrcRect = srcRect;
+    adjustedSrcRect.scale(resolutionScale);
+    destContext.drawNativeImage(image.get(), backingStoreSize, destRect, adjustedSrcRect, op, blendMode);
+#else
+    imageBuffer->draw(destContext, destRect, srcRect, op, blendMode);
+#endif
+}
 
+void ImageBuffer::draw(GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode)
+{
     RetainPtr<CGImageRef> image;
     if (&destContext == &context() || destContext.isAcceleratedContext())
         image = copyNativeImage(CopyBackingStore); // Drawing into our own buffer, need to deep copy.
@@ -240,37 +362,30 @@ void ImageBuffer::draw(GraphicsContext& destContext, ColorSpace styleColorSpace,
         image = copyNativeImage(DontCopyBackingStore);
 
     FloatRect adjustedSrcRect = srcRect;
-    adjustedSrcRect.scale(m_resolutionScale, m_resolutionScale);
-    destContext.drawNativeImage(image.get(), m_data.backingStoreSize, colorSpace, destRect, adjustedSrcRect, op, blendMode);
+    adjustedSrcRect.scale(m_resolutionScale);
+    destContext.drawNativeImage(image.get(), m_data.backingStoreSize, destRect, adjustedSrcRect, op, blendMode);
 }
 
-void ImageBuffer::drawPattern(GraphicsContext& destContext, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect, BlendMode blendMode)
+void ImageBuffer::drawPattern(GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, BlendMode blendMode)
 {
     FloatRect adjustedSrcRect = srcRect;
-    adjustedSrcRect.scale(m_resolutionScale, m_resolutionScale);
+    adjustedSrcRect.scale(m_resolutionScale);
 
     if (!context().isAcceleratedContext()) {
         if (&destContext == &context() || destContext.isAcceleratedContext()) {
             if (RefPtr<Image> copy = copyImage(CopyBackingStore)) // Drawing into our own buffer, need to deep copy.
-                copy->drawPattern(destContext, adjustedSrcRect, patternTransform, phase, spacing, styleColorSpace, op, destRect, blendMode);
+                copy->drawPattern(destContext, destRect, adjustedSrcRect, patternTransform, phase, spacing, op, blendMode);
         } else {
             if (RefPtr<Image> imageForRendering = copyImage(DontCopyBackingStore))
-                imageForRendering->drawPattern(destContext, adjustedSrcRect, patternTransform, phase, spacing, styleColorSpace, op, destRect, blendMode);
+                imageForRendering->drawPattern(destContext, destRect, adjustedSrcRect, patternTransform, phase, spacing, op, blendMode);
         }
     } else {
         if (RefPtr<Image> copy = copyImage(CopyBackingStore))
-            copy->drawPattern(destContext, adjustedSrcRect, patternTransform, phase, spacing, styleColorSpace, op, destRect, blendMode);
+            copy->drawPattern(destContext, destRect, adjustedSrcRect, patternTransform, phase, spacing, op, blendMode);
     }
 }
 
-void ImageBuffer::clip(GraphicsContext& contextToClip, const FloatRect& rect) const
-{
-    FloatSize backingStoreSizeInUserSpace = scaleSizeToUserSpace(rect.size(), m_data.backingStoreSize, internalSize());
-    RetainPtr<CGImageRef> image = copyNativeImage(DontCopyBackingStore);
-    contextToClip.clipToNativeImage(image.get(), rect, backingStoreSizeInUserSpace);
-}
-
-PassRefPtr<Uint8ClampedArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect, CoordinateSystem coordinateSystem) const
+RefPtr<Uint8ClampedArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect, IntSize* pixelArrayDimensions, CoordinateSystem coordinateSystem) const
 {
     if (context().isAcceleratedContext())
         flushContext();
@@ -279,10 +394,13 @@ PassRefPtr<Uint8ClampedArray> ImageBuffer::getUnmultipliedImageData(const IntRec
     if (coordinateSystem == LogicalCoordinateSystem)
         srcRect.scale(m_resolutionScale);
 
+    if (pixelArrayDimensions)
+        *pixelArrayDimensions = srcRect.size();
+
     return m_data.getData(srcRect, internalSize(), context().isAcceleratedContext(), true, 1);
 }
 
-PassRefPtr<Uint8ClampedArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect, CoordinateSystem coordinateSystem) const
+RefPtr<Uint8ClampedArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect, IntSize* pixelArrayDimensions, CoordinateSystem coordinateSystem) const
 {
     if (context().isAcceleratedContext())
         flushContext();
@@ -291,57 +409,30 @@ PassRefPtr<Uint8ClampedArray> ImageBuffer::getPremultipliedImageData(const IntRe
     if (coordinateSystem == LogicalCoordinateSystem)
         srcRect.scale(m_resolutionScale);
 
+    if (pixelArrayDimensions)
+        *pixelArrayDimensions = srcRect.size();
+
     return m_data.getData(srcRect, internalSize(), context().isAcceleratedContext(), false, 1);
 }
 
 void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, CoordinateSystem coordinateSystem)
 {
-    if (!context().isAcceleratedContext()) {
-        IntRect scaledSourceRect = sourceRect;
-        IntSize scaledSourceSize = sourceSize;
-        if (coordinateSystem == LogicalCoordinateSystem) {
-            scaledSourceRect.scale(m_resolutionScale);
-            scaledSourceSize.scale(m_resolutionScale);
-        }
+    if (context().isAcceleratedContext())
+        flushContext();
 
-        m_data.putData(source, scaledSourceSize, scaledSourceRect, destPoint, internalSize(), false, multiplied == Unmultiplied, 1);
-        return;
+    IntRect scaledSourceRect = sourceRect;
+    IntSize scaledSourceSize = sourceSize;
+    if (coordinateSystem == LogicalCoordinateSystem) {
+        scaledSourceRect.scale(m_resolutionScale);
+        scaledSourceSize.scale(m_resolutionScale);
     }
 
-#if USE(IOSURFACE_CANVAS_BACKING_STORE)
-    // Make a copy of the source to ensure the bits don't change before being drawn
-    IntSize sourceCopySize(sourceRect.width(), sourceRect.height());
-    // FIXME (149431): Should this ImageBuffer be unconditionally unaccelerated? Making it match the context seems to break putData().
-    std::unique_ptr<ImageBuffer> sourceCopy = ImageBuffer::create(sourceCopySize, Unaccelerated, 1, ColorSpaceDeviceRGB);
-    if (!sourceCopy)
-        return;
-
-    sourceCopy->m_data.putData(source, sourceSize, sourceRect, IntPoint(-sourceRect.x(), -sourceRect.y()), sourceCopy->internalSize(), sourceCopy->context().isAcceleratedContext(), multiplied == Unmultiplied, 1);
-
-    // Set up context for using drawImage as a direct bit copy
-    CGContextRef destContext = context().platformContext();
-    CGContextSaveGState(destContext);
-    if (coordinateSystem == LogicalCoordinateSystem)
-        CGContextConcatCTM(destContext, AffineTransform(wkGetUserToBaseCTM(destContext)).inverse());
-    else
-        CGContextConcatCTM(destContext, AffineTransform(CGContextGetCTM(destContext)).inverse());
-    wkCGContextResetClip(destContext);
-    CGContextSetInterpolationQuality(destContext, kCGInterpolationNone);
-    CGContextSetAlpha(destContext, 1.0);
-    CGContextSetBlendMode(destContext, kCGBlendModeCopy);
-    CGContextSetShadowWithColor(destContext, CGSizeZero, 0, 0);
-
-    // Draw the image in CG coordinate space
-    FloatSize scaledDestSize = scaleSizeToUserSpace(coordinateSystem == LogicalCoordinateSystem ? logicalSize() : internalSize(), m_data.backingStoreSize, internalSize());
-    IntPoint destPointInCGCoords(destPoint.x() + sourceRect.x(), scaledDestSize.height() - (destPoint.y() + sourceRect.y()) - sourceRect.height());
-    IntRect destRectInCGCoords(destPointInCGCoords, sourceCopySize);
-    CGContextClipToRect(destContext, destRectInCGCoords);
-
-    RetainPtr<CGImageRef> sourceCopyImage = sourceCopy->copyNativeImage();
-    FloatRect backingStoreInDestRect = FloatRect(FloatPoint(destPointInCGCoords.x(), destPointInCGCoords.y() + sourceCopySize.height() - (int)CGImageGetHeight(sourceCopyImage.get())), FloatSize(CGImageGetWidth(sourceCopyImage.get()), CGImageGetHeight(sourceCopyImage.get())));
-    CGContextDrawImage(destContext, backingStoreInDestRect, sourceCopyImage.get());
-    CGContextRestoreGState(destContext);
-#endif
+    m_data.putData(source, scaledSourceSize, scaledSourceRect, destPoint, internalSize(), context().isAcceleratedContext(), multiplied == Unmultiplied, 1);
+    
+    // Force recreating the IOSurface cached image if it is requested through CGIOSurfaceContextCreateImage().
+    // See https://bugs.webkit.org/show_bug.cgi?id=157966 for explaining why this is necessary.
+    if (context().isAcceleratedContext())
+        context().fillRect(FloatRect(1, 1, 0, 0));
 }
 
 static inline CFStringRef jpegUTI()
@@ -352,10 +443,11 @@ static inline CFStringRef jpegUTI()
     return kUTTypeJPEG;
 }
     
-static RetainPtr<CFStringRef> utiFromMIMEType(const String& mimeType)
+static RetainPtr<CFStringRef> utiFromImageBufferMIMEType(const String& mimeType)
 {
+    // FIXME: Why doesn't iOS use the CoreServices version?
 #if PLATFORM(MAC)
-    return adoptCF(UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType.createCFString().get(), 0));
+    return UTIFromMIMEType(mimeType).createCFString();
 #else
     ASSERT(isMainThread()); // It is unclear if CFSTR is threadsafe.
 
@@ -364,11 +456,11 @@ static RetainPtr<CFStringRef> utiFromMIMEType(const String& mimeType)
     static const CFStringRef kUTTypePNG = CFSTR("public.png");
     static const CFStringRef kUTTypeGIF = CFSTR("com.compuserve.gif");
 
-    if (equalIgnoringCase(mimeType, "image/png"))
+    if (equalLettersIgnoringASCIICase(mimeType, "image/png"))
         return kUTTypePNG;
-    if (equalIgnoringCase(mimeType, "image/jpeg"))
+    if (equalLettersIgnoringASCIICase(mimeType, "image/jpeg"))
         return jpegUTI();
-    if (equalIgnoringCase(mimeType, "image/gif"))
+    if (equalLettersIgnoringASCIICase(mimeType, "image/gif"))
         return kUTTypeGIF;
 
     ASSERT_NOT_REACHED();
@@ -376,19 +468,19 @@ static RetainPtr<CFStringRef> utiFromMIMEType(const String& mimeType)
 #endif
 }
 
-static bool CGImageEncodeToData(CGImageRef image, CFStringRef uti, const double* quality, CFMutableDataRef data)
+static bool encodeImage(CGImageRef image, CFStringRef uti, std::optional<double> quality, CFMutableDataRef data)
 {
     if (!image || !uti || !data)
         return false;
 
-    RetainPtr<CGImageDestinationRef> destination = adoptCF(CGImageDestinationCreateWithData(data, uti, 1, 0));
+    auto destination = adoptCF(CGImageDestinationCreateWithData(data, uti, 1, 0));
     if (!destination)
         return false;
 
-    RetainPtr<CFDictionaryRef> imageProperties = 0;
+    RetainPtr<CFDictionaryRef> imageProperties;
     if (CFEqual(uti, jpegUTI()) && quality && *quality >= 0.0 && *quality <= 1.0) {
         // Apply the compression quality to the JPEG image destination.
-        RetainPtr<CFNumberRef> compressionQuality = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, quality));
+        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));
@@ -401,69 +493,85 @@ static bool CGImageEncodeToData(CGImageRef image, CFStringRef uti, const double*
     return CGImageDestinationFinalize(destination.get());
 }
 
-static String CGImageToDataURL(CGImageRef image, const String& mimeType, const double* quality)
+static String dataURL(CFDataRef data, const String& mimeType)
 {
-    RetainPtr<CFStringRef> uti = utiFromMIMEType(mimeType);
-    ASSERT(uti);
-
-    RetainPtr<CFMutableDataRef> data = adoptCF(CFDataCreateMutable(kCFAllocatorDefault, 0));
-    if (!CGImageEncodeToData(image, uti.get(), quality, data.get()))
-        return "data:,";
-
     Vector<char> base64Data;
-    base64Encode(CFDataGetBytePtr(data.get()), CFDataGetLength(data.get()), base64Data);
+    base64Encode(CFDataGetBytePtr(data), CFDataGetLength(data), base64Data);
 
     return "data:" + mimeType + ";base64," + base64Data;
 }
 
-String ImageBuffer::toDataURL(const String& mimeType, const double* quality, CoordinateSystem) const
+static Vector<uint8_t> dataVector(CFDataRef cfData)
+{
+    Vector<uint8_t> data;
+    data.append(CFDataGetBytePtr(cfData), CFDataGetLength(cfData));
+    return data;
+}
+
+String ImageBuffer::toDataURL(const String& mimeType, std::optional<double> quality, CoordinateSystem) const
+{
+    if (auto data = toCFData(mimeType, quality))
+        return dataURL(data.get(), mimeType);
+    return ASCIILiteral("data:,");
+}
+
+Vector<uint8_t> ImageBuffer::toData(const String& mimeType, std::optional<double> quality) const
+{
+    if (auto data = toCFData(mimeType, quality))
+        return dataVector(data.get());
+    return { };
+}
+
+RetainPtr<CFDataRef> ImageBuffer::toCFData(const String& mimeType, std::optional<double> quality) const
 {
     ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
 
     if (context().isAcceleratedContext())
         flushContext();
 
-    RetainPtr<CFStringRef> uti = utiFromMIMEType(mimeType);
+    auto uti = utiFromImageBufferMIMEType(mimeType);
     ASSERT(uti);
 
-    RefPtr<Uint8ClampedArray> premultipliedData;
     RetainPtr<CGImageRef> image;
+    RefPtr<Uint8ClampedArray> premultipliedData;
 
     if (CFEqual(uti.get(), jpegUTI())) {
         // JPEGs don't have an alpha channel, so we have to manually composite on top of black.
-        premultipliedData = getPremultipliedImageData(IntRect(IntPoint(0, 0), logicalSize()));
+        premultipliedData = getPremultipliedImageData(IntRect(IntPoint(), logicalSize()));
         if (!premultipliedData)
-            return "data:,";
+            return nullptr;
 
-        RetainPtr<CGDataProviderRef> dataProvider;
-        dataProvider = adoptCF(CGDataProviderCreateWithData(0, premultipliedData->data(), 4 * logicalSize().width() * logicalSize().height(), 0));
+        size_t dataSize = 4 * logicalSize().width() * logicalSize().height();
+        auto dataProvider = adoptCF(CGDataProviderCreateWithData(nullptr, premultipliedData->data(), dataSize, nullptr));
         if (!dataProvider)
-            return "data:,";
+            return nullptr;
 
-        image = adoptCF(CGImageCreate(logicalSize().width(), logicalSize().height(), 8, 32, 4 * logicalSize().width(),
-                                    deviceRGBColorSpaceRef(), kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipLast,
-                                    dataProvider.get(), 0, false, kCGRenderingIntentDefault));
+        image = adoptCF(CGImageCreate(logicalSize().width(), logicalSize().height(), 8, 32, 4 * logicalSize().width(), sRGBColorSpaceRef(), kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipLast, dataProvider.get(), 0, false, kCGRenderingIntentDefault));
     } else if (m_resolutionScale == 1) {
         image = copyNativeImage(CopyBackingStore);
         image = createCroppedImageIfNecessary(image.get(), internalSize());
     } else {
         image = copyNativeImage(DontCopyBackingStore);
-        RetainPtr<CGContextRef> context = adoptCF(CGBitmapContextCreate(0, logicalSize().width(), logicalSize().height(), 8, 4 * logicalSize().width(), deviceRGBColorSpaceRef(), kCGImageAlphaPremultipliedLast));
+        auto context = adoptCF(CGBitmapContextCreate(0, logicalSize().width(), logicalSize().height(), 8, 4 * logicalSize().width(), sRGBColorSpaceRef(), kCGImageAlphaPremultipliedLast));
         CGContextSetBlendMode(context.get(), kCGBlendModeCopy);
         CGContextClipToRect(context.get(), CGRectMake(0, 0, logicalSize().width(), logicalSize().height()));
-        FloatSize imageSizeInUserSpace = scaleSizeToUserSpace(logicalSize(), m_data.backingStoreSize, internalSize());
+        FloatSize imageSizeInUserSpace = sizeForDestinationSize(logicalSize());
         CGContextDrawImage(context.get(), CGRectMake(0, 0, imageSizeInUserSpace.width(), imageSizeInUserSpace.height()), image.get());
         image = adoptCF(CGBitmapContextCreateImage(context.get()));
     }
 
-    return CGImageToDataURL(image.get(), mimeType, quality);
+    auto cfData = adoptCF(CFDataCreateMutable(kCFAllocatorDefault, 0));
+    if (!encodeImage(image.get(), uti.get(), quality, cfData.get()))
+        return nullptr;
+
+    return WTFMove(cfData);
 }
 
-String ImageDataToDataURL(const ImageData& source, const String& mimeType, const double* quality)
+static RetainPtr<CFDataRef> cfData(const ImageData& source, const String& mimeType, std::optional<double> quality)
 {
     ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
 
-    RetainPtr<CFStringRef> uti = utiFromMIMEType(mimeType);
+    auto uti = utiFromImageBufferMIMEType(mimeType);
     ASSERT(uti);
 
     CGImageAlphaInfo dataAlphaInfo = kCGImageAlphaLast;
@@ -474,9 +582,9 @@ String ImageDataToDataURL(const ImageData& source, const String& mimeType, const
         // JPEGs don't have an alpha channel, so we have to manually composite on top of black.
         size_t size = 4 * source.width() * source.height();
         if (!premultipliedData.tryReserveCapacity(size))
-            return "data:,";
+            return nullptr;
 
-        premultipliedData.resize(size);
+        premultipliedData.grow(size);
         unsigned char *buffer = premultipliedData.data();
         for (size_t i = 0; i < size; i += 4) {
             unsigned alpha = data[i + 3];
@@ -495,17 +603,33 @@ String ImageDataToDataURL(const ImageData& source, const String& mimeType, const
         data = premultipliedData.data();
     }
 
-    RetainPtr<CGDataProviderRef> dataProvider;
-    dataProvider = adoptCF(CGDataProviderCreateWithData(0, data, 4 * source.width() * source.height(), 0));
+    auto dataProvider = adoptCF(CGDataProviderCreateWithData(0, data, 4 * source.width() * source.height(), 0));
     if (!dataProvider)
-        return "data:,";
+        return nullptr;
 
-    RetainPtr<CGImageRef> image;
-    image = adoptCF(CGImageCreate(source.width(), source.height(), 8, 32, 4 * source.width(),
-                                deviceRGBColorSpaceRef(), kCGBitmapByteOrderDefault | dataAlphaInfo,
-                                dataProvider.get(), 0, false, kCGRenderingIntentDefault));
+    auto image = adoptCF(CGImageCreate(source.width(), source.height(), 8, 32, 4 * source.width(), sRGBColorSpaceRef(), 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 CGImageToDataURL(image.get(), mimeType, quality);
+    return WTFMove(cfData);
+}
+
+String dataURL(const ImageData& source, const String& mimeType, std::optional<double> quality)
+{
+    if (auto data = cfData(source, mimeType, quality))
+        return dataURL(data.get(), mimeType);
+    return ASCIILiteral("data:,");
+}
+
+Vector<uint8_t> data(const ImageData& source, const String& mimeType, std::optional<double> quality)
+{
+    if (auto data = cfData(source, mimeType, quality))
+        return dataVector(data.get());
+    return { };
 }
 
 } // namespace WebCore
+
+#endif