Replace WTF::move with WTFMove
[WebKit-https.git] / Source / WebCore / platform / graphics / cg / ImageBufferCG.cpp
index e573732..cd5aeb0 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
- * Copyright (C) 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2015 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
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 #include "GraphicsContext.h"
 #include "GraphicsContextCG.h"
 #include "ImageData.h"
+#include "IntRect.h"
 #include "MIMETypeRegistry.h"
-#include <ApplicationServices/ApplicationServices.h>
 #include <math.h>
+#include <CoreGraphics/CoreGraphics.h>
+#include <ImageIO/ImageIO.h>
 #include <wtf/Assertions.h>
 #include <wtf/CheckedArithmetic.h>
 #include <wtf/MainThread.h>
-#include <wtf/OwnArrayPtr.h>
 #include <wtf/RetainPtr.h>
-#include <wtf/UnusedParam.h>
 #include <wtf/text/Base64.h>
 #include <wtf/text/WTFString.h>
 
-#if PLATFORM(MAC) || PLATFORM(CHROMIUM)
+#if PLATFORM(COCOA)
 #include "WebCoreSystemInterface.h"
 #endif
 
 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
-#include <IOSurface/IOSurface.h>
+#include "IOSurface.h"
+#include "IOSurfaceSPI.h"
 #endif
 
-#if !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED == 1070
-#include <wtf/CurrentTime.h>
-#endif
-
-using namespace std;
+// CA uses ARGB32 for textures and ARGB32 -> ARGB32 resampling is optimized.
+#define USE_ARGB32 PLATFORM(IOS)
 
 namespace WebCore {
 
-#if USE(IOSURFACE_CANVAS_BACKING_STORE)
-static const int maxIOSurfaceDimension = 4096;
-
-static RetainPtr<IOSurfaceRef> createIOSurface(const IntSize& size)
+static void releaseImageData(void*, const void* data, size_t)
 {
-    unsigned pixelFormat = 'BGRA';
-    unsigned bytesPerElement = 4;
-    int width = size.width();
-    int height = size.height();
-
-    unsigned long bytesPerRow = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, size.width() * bytesPerElement);
-    if (!bytesPerRow)
-        return 0;
-
-    unsigned long allocSize = IOSurfaceAlignProperty(kIOSurfaceAllocSize, size.height() * bytesPerRow);
-    if (!allocSize)
-        return 0;
-
-    const void *keys[6];
-    const void *values[6];
-    keys[0] = kIOSurfaceWidth;
-    values[0] = CFNumberCreate(0, kCFNumberIntType, &width);
-    keys[1] = kIOSurfaceHeight;
-    values[1] = CFNumberCreate(0, kCFNumberIntType, &height);
-    keys[2] = kIOSurfacePixelFormat;
-    values[2] = CFNumberCreate(0, kCFNumberIntType, &pixelFormat);
-    keys[3] = kIOSurfaceBytesPerElement;
-    values[3] = CFNumberCreate(0, kCFNumberIntType, &bytesPerElement);
-    keys[4] = kIOSurfaceBytesPerRow;
-    values[4] = CFNumberCreate(0, kCFNumberLongType, &bytesPerRow);
-    keys[5] = kIOSurfaceAllocSize;
-    values[5] = CFNumberCreate(0, kCFNumberLongType, &allocSize);
-
-    RetainPtr<CFDictionaryRef> dict(AdoptCF, CFDictionaryCreate(0, keys, values, 6, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
-    for (unsigned i = 0; i < 6; i++)
-        CFRelease(values[i]);
-
-    return RetainPtr<IOSurfaceRef>(AdoptCF, IOSurfaceCreate(dict.get()));
+    fastFree(const_cast<void*>(data));
 }
-#endif
 
-static void releaseImageData(void*, const void* data, size_t)
+static FloatSize scaleSizeToUserSpace(const FloatSize& logicalSize, const IntSize& backingStoreSize, const IntSize& internalSize)
 {
-    fastFree(const_cast<void*>(data));
+    float xMagnification = static_cast<float>(backingStoreSize.width()) / internalSize.width();
+    float yMagnification = static_cast<float>(backingStoreSize.height()) / internalSize.height();
+    return FloatSize(logicalSize.width() * xMagnification, logicalSize.height() * yMagnification);
 }
 
-ImageBuffer::ImageBuffer(const IntSize& size, float resolutionScale, ColorSpace imageColorSpace, RenderingMode renderingMode, bool& success)
-    : m_data(size) // NOTE: The input here isn't important as ImageBufferDataCG's constructor just ignores it.
-    , m_logicalSize(size)
+ImageBuffer::ImageBuffer(const FloatSize& size, float resolutionScale, ColorSpace imageColorSpace, RenderingMode renderingMode, bool& success)
+    : m_logicalSize(size)
     , m_resolutionScale(resolutionScale)
 {
     float scaledWidth = ceilf(resolutionScale * size.width());
@@ -119,72 +82,72 @@ ImageBuffer::ImageBuffer(const IntSize& size, float resolutionScale, ColorSpace
         return;
 
     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;
 
+#if USE(IOSURFACE_CANVAS_BACKING_STORE)
     Checked<int, RecordOverflow> width = m_size.width();
     Checked<int, RecordOverflow> height = m_size.height();
+#endif
 
     // Prevent integer overflows
-    m_data.m_bytesPerRow = 4 * width;
-    Checked<size_t, RecordOverflow> numBytes = height * m_data.m_bytesPerRow;
+    m_data.bytesPerRow = 4 * Checked<unsigned, RecordOverflow>(m_data.backingStoreSize.width());
+    Checked<size_t, RecordOverflow> numBytes = Checked<unsigned, RecordOverflow>(m_data.backingStoreSize.height()) * m_data.bytesPerRow;
     if (numBytes.hasOverflowed())
         return;
 
 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
-    if (width.unsafeGet() >= maxIOSurfaceDimension || height.unsafeGet() >= maxIOSurfaceDimension)
+    IntSize maxSize = IOSurface::maximumSize();
+    if (width.unsafeGet() > maxSize.width() || height.unsafeGet() > maxSize.height())
         accelerateRendering = false;
 #else
     ASSERT(renderingMode == Unaccelerated);
 #endif
 
-    switch (imageColorSpace) {
-    case ColorSpaceDeviceRGB:
-        m_data.m_colorSpace = deviceRGBColorSpaceRef();
-        break;
-    case ColorSpaceSRGB:
-        m_data.m_colorSpace = sRGBColorSpaceRef();
-        break;
-    case ColorSpaceLinearRGB:
-        m_data.m_colorSpace = linearRGBColorSpaceRef();
-        break;
-    }
+    m_data.colorSpace = cachedCGColorSpace(imageColorSpace);
 
     RetainPtr<CGContextRef> cgContext;
     if (accelerateRendering) {
 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
-        m_data.m_surface = createIOSurface(m_size);
-        cgContext.adoptCF(wkIOSurfaceContextCreate(m_data.m_surface.get(), width.unsafeGet(), height.unsafeGet(), m_data.m_colorSpace));
+        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));
 #endif
+
         if (!cgContext)
             accelerateRendering = false; // If allocation fails, fall back to non-accelerated path.
     }
 
     if (!accelerateRendering) {
-        if (!tryFastCalloc(height.unsafeGet(), m_data.m_bytesPerRow.unsafeGet()).getValue(m_data.m_data))
+        if (!tryFastCalloc(m_data.backingStoreSize.height(), m_data.bytesPerRow.unsafeGet()).getValue(m_data.data))
             return;
-        ASSERT(!(reinterpret_cast<size_t>(m_data.m_data) & 2));
+        ASSERT(!(reinterpret_cast<intptr_t>(m_data.data) & 3));
 
-        m_data.m_bitmapInfo = kCGImageAlphaPremultipliedLast;
-        cgContext.adoptCF(CGBitmapContextCreate(m_data.m_data, width.unsafeGet(), height.unsafeGet(), 8, m_data.m_bytesPerRow.unsafeGet(), m_data.m_colorSpace, m_data.m_bitmapInfo));
+#if USE_ARGB32
+        m_data.bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
+#else
+        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));
         // Create a live image that wraps the data.
-        m_data.m_dataProvider.adoptCF(CGDataProviderCreateWithData(0, m_data.m_data, numBytes.unsafeGet(), releaseImageData));
+        m_data.dataProvider = adoptCF(CGDataProviderCreateWithData(0, m_data.data, numBytes.unsafeGet(), releaseImageData));
+
+        if (!cgContext)
+            return;
+
+        m_data.context = std::make_unique<GraphicsContext>(cgContext.get());
     }
 
-    if (!cgContext)
-        return;
+    context().scale(FloatSize(1, -1));
+    context().translate(0, -m_data.backingStoreSize.height());
+    context().applyDeviceScaleFactor(m_resolutionScale);
 
-    m_context = adoptPtr(new GraphicsContext(cgContext.get()));
-    m_context->applyDeviceScaleFactor(m_resolutionScale);
-    m_context->scale(FloatSize(1, -1));
-    m_context->translate(0, -size.height());
-    m_context->setIsAcceleratedContext(accelerateRendering);
-#if !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED == 1070
-    m_data.m_lastFlushTime = currentTimeMS();
-#endif
     success = true;
 }
 
@@ -192,59 +155,96 @@ ImageBuffer::~ImageBuffer()
 {
 }
 
-GraphicsContext* ImageBuffer::context() const
+GraphicsContext& ImageBuffer::context() const
 {
-#if !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED == 1070
-    // Force a flush if last flush was more than 20ms ago
-    if (m_context->isAcceleratedContext()) {
-        double elapsedTime = currentTimeMS() - m_data.m_lastFlushTime;
-        double maxFlushInterval = 20; // in ms
-
-        if (elapsedTime > maxFlushInterval) {
-            CGContextRef context = m_context->platformContext();
-            CGContextFlush(context);
-            m_data.m_lastFlushTime = currentTimeMS();
-        }
-    }
+#if USE(IOSURFACE_CANVAS_BACKING_STORE)
+    if (m_data.surface)
+        return m_data.surface->ensureGraphicsContext();
 #endif
+    return *m_data.context;
+}
 
-    return m_context.get();
+void ImageBuffer::flushContext() const
+{
+    CGContextFlush(context().platformContext());
 }
 
-PassRefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, ScaleBehavior scaleBehavior) const
+static RetainPtr<CGImageRef> createCroppedImageIfNecessary(CGImageRef image, const IntSize& bounds)
 {
-    RetainPtr<CGImageRef> image;
-    if (m_resolutionScale == 1 || scaleBehavior == Unscaled)
-        image = copyNativeImage(copyBehavior);
+    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;
+}
+
+static RefPtr<Image> createBitmapImageAfterScalingIfNeeded(RetainPtr<CGImageRef>&& image, IntSize internalSize, IntSize logicalSize, IntSize backingStoreSize, float resolutionScale, ScaleBehavior scaleBehavior)
+{
+    if (resolutionScale == 1 || scaleBehavior == Unscaled)
+        image = createCroppedImageIfNecessary(image.get(), internalSize);
     else {
-        image.adoptCF(copyNativeImage(DontCopyBackingStore));
-        RetainPtr<CGContextRef> context(AdoptCF, CGBitmapContextCreate(0, logicalSize().width(), logicalSize().height(), 8, 4 * logicalSize().width(), deviceRGBColorSpaceRef(), kCGImageAlphaPremultipliedLast));
+        RetainPtr<CGContextRef> context = adoptCF(CGBitmapContextCreate(0, logicalSize.width(), logicalSize.height(), 8, 4 * logicalSize.width(), sRGBColorSpaceRef(), kCGImageAlphaPremultipliedLast));
         CGContextSetBlendMode(context.get(), kCGBlendModeCopy);
-        CGContextDrawImage(context.get(), CGRectMake(0, 0, logicalSize().width(), logicalSize().height()), image.get());
-        image = CGBitmapContextCreateImage(context.get());
+        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()));
     }
 
     if (!image)
-        return 0;
+        return nullptr;
 
     return BitmapImage::create(image.get());
 }
 
+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()
 {
     return DontCopyBackingStore;
 }
 
-NativeImagePtr ImageBuffer::copyNativeImage(BackingStoreCopy copyBehavior) const
+RetainPtr<CGImageRef> ImageBuffer::sinkIntoNativeImage(std::unique_ptr<ImageBuffer> imageBuffer)
 {
-    CGImageRef image = 0;
-    if (!m_context->isAcceleratedContext()) {
+#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;
+    if (!context().isAcceleratedContext()) {
         switch (copyBehavior) {
         case DontCopyBackingStore:
-            image = CGImageCreate(internalSize().width(), internalSize().height(), 8, 32, m_data.m_bytesPerRow.unsafeGet(), m_data.m_colorSpace, m_data.m_bitmapInfo, m_data.m_dataProvider.get(), 0, true, kCGRenderingIntentDefault);
+            image = adoptCF(CGImageCreate(m_data.backingStoreSize.width(), m_data.backingStoreSize.height(), 8, 32, m_data.bytesPerRow.unsafeGet(), m_data.colorSpace, m_data.bitmapInfo, m_data.dataProvider.get(), 0, true, kCGRenderingIntentDefault));
             break;
         case CopyBackingStore:
-            image = CGBitmapContextCreateImage(context()->platformContext());
+            image = adoptCF(CGBitmapContextCreateImage(context().platformContext()));
             break;
         default:
             ASSERT_NOT_REACHED();
@@ -252,127 +252,156 @@ NativeImagePtr ImageBuffer::copyNativeImage(BackingStoreCopy copyBehavior) const
         }
     }
 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
-    else {
-        image = wkIOSurfaceContextCreateImage(context()->platformContext());
-#if !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED == 1070
-        m_data.m_lastFlushTime = currentTimeMS();
-#endif
-    }
+    else
+        image = m_data.surface->createImage();
 #endif
 
     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, bool useLowQualityScale)
 {
-    UNUSED_PARAM(useLowQualityScale);
-    ColorSpace colorSpace = (destContext == m_context) ? ColorSpaceDeviceRGB : styleColorSpace;
+#if USE(IOSURFACE_CANVAS_BACKING_STORE)
+    if (!imageBuffer->m_data.surface) {
+        imageBuffer->draw(destContext, destRect, srcRect, op, blendMode, useLowQualityScale);
+        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, resolutionScale);
+    destContext.drawNativeImage(image.get(), backingStoreSize, destRect, adjustedSrcRect, op, blendMode);
+#else
+    imageBuffer->draw(destContext, destRect, srcRect, op, blendMode, useLowQualityScale);
+#endif
+}
 
+void ImageBuffer::draw(GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode, bool)
+{
     RetainPtr<CGImageRef> image;
-    if (destContext == m_context || destContext->isAcceleratedContext())
-        image.adoptCF(copyNativeImage(CopyBackingStore)); // Drawing into our own buffer, need to deep copy.
+    if (&destContext == &context() || destContext.isAcceleratedContext())
+        image = copyNativeImage(CopyBackingStore); // Drawing into our own buffer, need to deep copy.
     else
-        image.adoptCF(copyNativeImage(DontCopyBackingStore));
+        image = copyNativeImage(DontCopyBackingStore);
 
     FloatRect adjustedSrcRect = srcRect;
     adjustedSrcRect.scale(m_resolutionScale, m_resolutionScale);
-    destContext->drawNativeImage(image.get(), internalSize(), colorSpace, destRect, adjustedSrcRect, op, blendMode);
+    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, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect)
+void ImageBuffer::drawPattern(GraphicsContext& destContext, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, const FloatRect& destRect, BlendMode blendMode)
 {
     FloatRect adjustedSrcRect = srcRect;
     adjustedSrcRect.scale(m_resolutionScale, m_resolutionScale);
 
-    if (!m_context->isAcceleratedContext()) {
-        if (destContext == m_context || destContext->isAcceleratedContext()) {
-            RefPtr<Image> copy = copyImage(CopyBackingStore); // Drawing into our own buffer, need to deep copy.
-            copy->drawPattern(destContext, adjustedSrcRect, patternTransform, phase, styleColorSpace, op, destRect);
+    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, op, destRect, blendMode);
         } else {
-            RefPtr<Image> imageForRendering = copyImage(DontCopyBackingStore);
-            imageForRendering->drawPattern(destContext, adjustedSrcRect, patternTransform, phase, styleColorSpace, op, destRect);
+            if (RefPtr<Image> imageForRendering = copyImage(DontCopyBackingStore))
+                imageForRendering->drawPattern(destContext, adjustedSrcRect, patternTransform, phase, spacing, op, destRect, blendMode);
         }
     } else {
-        RefPtr<Image> copy = copyImage(CopyBackingStore);
-        copy->drawPattern(destContext, adjustedSrcRect, patternTransform, phase, styleColorSpace, op, destRect);
+        if (RefPtr<Image> copy = copyImage(CopyBackingStore))
+            copy->drawPattern(destContext, adjustedSrcRect, patternTransform, phase, spacing, op, destRect, blendMode);
     }
 }
 
-void ImageBuffer::clip(GraphicsContext* contextToClip, const FloatRect& rect) const
+void ImageBuffer::clip(GraphicsContext& contextToClip, const FloatRect& rect) const
 {
-    CGContextRef platformContextToClip = contextToClip->platformContext();
-    // FIXME: This image needs to be grayscale to be used as an alpha mask here.
-    RetainPtr<CGImageRef> image(AdoptCF, copyNativeImage(DontCopyBackingStore));
-    CGContextTranslateCTM(platformContextToClip, rect.x(), rect.y() + rect.height());
-    CGContextScaleCTM(platformContextToClip, 1, -1);
-    CGContextClipToMask(platformContextToClip, FloatRect(FloatPoint(), rect.size()), image.get());
-    CGContextScaleCTM(platformContextToClip, 1, -1);
-    CGContextTranslateCTM(platformContextToClip, -rect.x(), -rect.y() - rect.height());
+    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
 {
-    if (m_context->isAcceleratedContext()) {
-        CGContextFlush(context()->platformContext());
-#if !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED == 1070
-        m_data.m_lastFlushTime = currentTimeMS();
-#endif
-    }
-    return m_data.getData(rect, internalSize(), m_context->isAcceleratedContext(), true, coordinateSystem == LogicalCoordinateSystem ? m_resolutionScale : 1);
+    if (context().isAcceleratedContext())
+        flushContext();
+
+    IntRect srcRect = rect;
+    if (coordinateSystem == LogicalCoordinateSystem)
+        srcRect.scale(m_resolutionScale);
+
+    return m_data.getData(srcRect, internalSize(), context().isAcceleratedContext(), true, 1);
 }
 
 PassRefPtr<Uint8ClampedArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect, CoordinateSystem coordinateSystem) const
 {
-    if (m_context->isAcceleratedContext()) {
-        CGContextFlush(context()->platformContext());
-#if !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED == 1070
-        m_data.m_lastFlushTime = currentTimeMS();
-#endif
-    }
-    return m_data.getData(rect, internalSize(), m_context->isAcceleratedContext(), false, coordinateSystem == LogicalCoordinateSystem ? m_resolutionScale : 1);
+    if (context().isAcceleratedContext())
+        flushContext();
+
+    IntRect srcRect = rect;
+    if (coordinateSystem == LogicalCoordinateSystem)
+        srcRect.scale(m_resolutionScale);
+
+    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 (!m_context->isAcceleratedContext()) {
-        m_data.putData(source, sourceSize, sourceRect, destPoint, internalSize(), m_context->isAcceleratedContext(), multiplied == Unmultiplied, coordinateSystem == LogicalCoordinateSystem ? m_resolutionScale : 1);
+    if (!context().isAcceleratedContext()) {
+        IntRect scaledSourceRect = sourceRect;
+        IntSize scaledSourceSize = sourceSize;
+        if (coordinateSystem == LogicalCoordinateSystem) {
+            scaledSourceRect.scale(m_resolutionScale);
+            scaledSourceSize.scale(m_resolutionScale);
+        }
+
+        m_data.putData(source, scaledSourceSize, scaledSourceRect, destPoint, internalSize(), false, multiplied == Unmultiplied, 1);
         return;
     }
 
 #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());
-    OwnPtr<ImageBuffer> sourceCopy = ImageBuffer::create(sourceCopySize, 1, ColorSpaceDeviceRGB, Unaccelerated);
+    // 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, ColorSpaceSRGB);
     if (!sourceCopy)
         return;
 
-    sourceCopy->m_data.putData(source, sourceSize, sourceRect, IntPoint(-sourceRect.x(), -sourceRect.y()), sourceCopy->internalSize(), sourceCopy->context()->isAcceleratedContext(), multiplied == Unmultiplied, 1);
+    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();
+    CGContextRef destContext = context().platformContext();
     CGContextSaveGState(destContext);
-    if (coordinateSystem == LogicalCoordinateSystem)
-        CGContextConcatCTM(destContext, AffineTransform(wkGetUserToBaseCTM(destContext)).inverse());
-    else
-        CGContextConcatCTM(destContext, AffineTransform(CGContextGetCTM(destContext)).inverse());
-    wkCGContextResetClip(destContext);
+    
+    if (coordinateSystem == LogicalCoordinateSystem) {
+        if (auto inverse = AffineTransform(getUserToBaseCTM(destContext)).inverse())
+            CGContextConcatCTM(destContext, inverse.value());
+    } else {
+        if (auto inverse = AffineTransform(CGContextGetCTM(destContext)).inverse())
+            CGContextConcatCTM(destContext, inverse.value());
+    }
+    CGContextResetClip(destContext);
     CGContextSetInterpolationQuality(destContext, kCGInterpolationNone);
     CGContextSetAlpha(destContext, 1.0);
     CGContextSetBlendMode(destContext, kCGBlendModeCopy);
     CGContextSetShadowWithColor(destContext, CGSizeZero, 0, 0);
 
     // Draw the image in CG coordinate space
-    IntPoint destPointInCGCoords(destPoint.x() + sourceRect.x(), (coordinateSystem == LogicalCoordinateSystem ? logicalSize() : internalSize()).height() - (destPoint.y() + sourceRect.y()) - sourceRect.height());
+    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);
-    RetainPtr<CGImageRef> sourceCopyImage(AdoptCF, sourceCopy->copyNativeImage());
-    CGContextDrawImage(destContext, destRectInCGCoords, sourceCopyImage.get());
+    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
 }
 
 static inline CFStringRef jpegUTI()
 {
-#if PLATFORM(WIN)
+#if PLATFORM(IOS) || PLATFORM(WIN)
     static const CFStringRef kUTTypeJPEG = CFSTR("public.jpeg");
 #endif
     return kUTTypeJPEG;
@@ -407,17 +436,17 @@ static bool CGImageEncodeToData(CGImageRef image, CFStringRef uti, const double*
     if (!image || !uti || !data)
         return false;
 
-    RetainPtr<CGImageDestinationRef> destination(AdoptCF, CGImageDestinationCreateWithData(data, uti, 1, 0));
+    RetainPtr<CGImageDestinationRef> destination = adoptCF(CGImageDestinationCreateWithData(data, uti, 1, 0));
     if (!destination)
         return false;
 
     RetainPtr<CFDictionaryRef> imageProperties = 0;
     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));
+        RetainPtr<CFNumberRef> compressionQuality = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, quality));
         const void* key = kCGImageDestinationLossyCompressionQuality;
         const void* value = compressionQuality.get();
-        imageProperties.adoptCF(CFDictionaryCreate(0, &key, &value, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
+        imageProperties = adoptCF(CFDictionaryCreate(0, &key, &value, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
     }
 
     // Setting kCGImageDestinationBackgroundColor to black for JPEG images in imageProperties would save some math
@@ -432,12 +461,12 @@ static String CGImageToDataURL(CGImageRef image, const String& mimeType, const d
     RetainPtr<CFStringRef> uti = utiFromMIMEType(mimeType);
     ASSERT(uti);
 
-    RetainPtr<CFMutableDataRef> data(AdoptCF, CFDataCreateMutable(kCFAllocatorDefault, 0));
+    RetainPtr<CFMutableDataRef> data = adoptCF(CFDataCreateMutable(kCFAllocatorDefault, 0));
     if (!CGImageEncodeToData(image, uti.get(), quality, data.get()))
         return "data:,";
 
     Vector<char> base64Data;
-    base64Encode(reinterpret_cast<const char*>(CFDataGetBytePtr(data.get())), CFDataGetLength(data.get()), base64Data);
+    base64Encode(CFDataGetBytePtr(data.get()), CFDataGetLength(data.get()), base64Data);
 
     return "data:" + mimeType + ";base64," + base64Data;
 }
@@ -446,6 +475,9 @@ String ImageBuffer::toDataURL(const String& mimeType, const double* quality, Coo
 {
     ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
 
+    if (context().isAcceleratedContext())
+        flushContext();
+
     RetainPtr<CFStringRef> uti = utiFromMIMEType(mimeType);
     ASSERT(uti);
 
@@ -459,21 +491,24 @@ String ImageBuffer::toDataURL(const String& mimeType, const double* quality, Coo
             return "data:,";
 
         RetainPtr<CGDataProviderRef> dataProvider;
-        dataProvider.adoptCF(CGDataProviderCreateWithData(0, premultipliedData->data(), 4 * logicalSize().width() * logicalSize().height(), 0));
+        dataProvider = adoptCF(CGDataProviderCreateWithData(0, premultipliedData->data(), 4 * logicalSize().width() * logicalSize().height(), 0));
         if (!dataProvider)
             return "data:,";
 
-        image.adoptCF(CGImageCreate(logicalSize().width(), logicalSize().height(), 8, 32, 4 * logicalSize().width(),
-                                    deviceRGBColorSpaceRef(), kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipLast,
+        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.adoptCF(copyNativeImage(CopyBackingStore));
-    else {
-        image.adoptCF(copyNativeImage(DontCopyBackingStore));
-        RetainPtr<CGContextRef> context(AdoptCF, CGBitmapContextCreate(0, logicalSize().width(), logicalSize().height(), 8, 4 * logicalSize().width(), deviceRGBColorSpaceRef(), kCGImageAlphaPremultipliedLast));
+    } 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(), sRGBColorSpaceRef(), kCGImageAlphaPremultipliedLast));
         CGContextSetBlendMode(context.get(), kCGBlendModeCopy);
-        CGContextDrawImage(context.get(), CGRectMake(0, 0, logicalSize().width(), logicalSize().height()), image.get());
-        image.adoptCF(CGBitmapContextCreateImage(context.get()));
+        CGContextClipToRect(context.get(), CGRectMake(0, 0, logicalSize().width(), logicalSize().height()));
+        FloatSize imageSizeInUserSpace = scaleSizeToUserSpace(logicalSize(), m_data.backingStoreSize, internalSize());
+        CGContextDrawImage(context.get(), CGRectMake(0, 0, imageSizeInUserSpace.width(), imageSizeInUserSpace.height()), image.get());
+        image = adoptCF(CGBitmapContextCreateImage(context.get()));
     }
 
     return CGImageToDataURL(image.get(), mimeType, quality);
@@ -496,6 +531,7 @@ String ImageDataToDataURL(const ImageData& source, const String& mimeType, const
         if (!premultipliedData.tryReserveCapacity(size))
             return "data:,";
 
+        premultipliedData.resize(size);
         unsigned char *buffer = premultipliedData.data();
         for (size_t i = 0; i < size; i += 4) {
             unsigned alpha = data[i + 3];
@@ -515,13 +551,13 @@ String ImageDataToDataURL(const ImageData& source, const String& mimeType, const
     }
 
     RetainPtr<CGDataProviderRef> dataProvider;
-    dataProvider.adoptCF(CGDataProviderCreateWithData(0, data, 4 * source.width() * source.height(), 0));
+    dataProvider = adoptCF(CGDataProviderCreateWithData(0, data, 4 * source.width() * source.height(), 0));
     if (!dataProvider)
         return "data:,";
 
     RetainPtr<CGImageRef> image;
-    image.adoptCF(CGImageCreate(source.width(), source.height(), 8, 32, 4 * source.width(),
-                                deviceRGBColorSpaceRef(), kCGBitmapByteOrderDefault | dataAlphaInfo,
+    image = adoptCF(CGImageCreate(source.width(), source.height(), 8, 32, 4 * source.width(),
+                                sRGBColorSpaceRef(), kCGBitmapByteOrderDefault | dataAlphaInfo,
                                 dataProvider.get(), 0, false, kCGRenderingIntentDefault));
 
     return CGImageToDataURL(image.get(), mimeType, quality);