Allow ImageBuffer to use an IOSurface that is larger than necessary
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 Dec 2013 21:35:38 +0000 (21:35 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 Dec 2013 21:35:38 +0000 (21:35 +0000)
https://bugs.webkit.org/show_bug.cgi?id=124626

Patch by Myles C. Maxfield <mmaxfield@apple.com> on 2013-12-04
Reviewed by Simon Fraser.

Source/WebCore:

Because creating ImageBuffer's backing store can be so expensive, it
would be beneficial to have a pool of pre-created backing stores
available. However, this means that ImageBuffer might have to use a
backing store that is larger than the exact dimensions that it needs.
This patch adds a field, m_backingStoreSize, to CG's ImageBufferData
class, and uses this new field when performing ImageBuffer operations
to allow for larger-than-necessary backing stores. Content is always
drawn in the top left corner of the backing store.

No new tests are necessary because there is no behavior change.

* platform/graphics/ImageBuffer.h:
(WebCore::ImageBuffer::baseTransform): The base transform has to put
content at the top left corner instead of bottom left
* platform/graphics/cg/ImageBufferCG.cpp:
(WebCore::createCroppedImageIfNecessary): Convenience function to figure out
the dimensions of the backing texture in user space
(WebCore::ImageBuffer::ImageBuffer): Set up new m_backingStoreSize member
(WebCore::maybeCropToBounds): Some ImageBuffer API functions require
outputting an image with logical size. This function performs the cropping
(WebCore::ImageBuffer::copyImage): Updated for larger-than-necessary
backing stores
(WebCore::ImageBuffer::copyNativeImage): Ditto
(WebCore::ImageBuffer::draw): Ditto
(WebCore::ImageBuffer::clip): Ditto
(WebCore::ImageBuffer::putByteArray): Ditto
(WebCore::ImageBuffer::toDataURL): Ditto
* platform/graphics/cg/ImageBufferDataCG.cpp:
(WebCore::ImageBufferData::getData): Ditto
(WebCore::ImageBufferData::putData): Ditto
* platform/graphics/cg/ImageBufferDataCG.h: New m_backingStoreSize field

LayoutTests:

Update tests to be more robust with respect to accelerated vs
non-accelerated ImageBuffers.

* fast/canvas/script-tests/canvas-fillPath-shadow.js: Don't sample a canvas at exactly
the corner of a drawn shape (because the corner might be antialiased). Instead, sample
a single pixel inside the shape
* fast/canvas/script-tests/canvas-scale-shadowBlur.js: Don't sample a canvas at exactly
the edge of the blur radius. Instead, sample a single pixel past the blur radius.
* fast/canvas/script-tests/canvas-scale-strokePath-shadow.js:
(shouldBeAround): Allow this test to be less strict when sampling inside a blurred region
* platform/mac/fast/canvas/canvas-scale-shadowBlur-expected.txt: Matching update w/r/t
canvas-scale-shadowBlur.js

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@160121 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/fast/canvas/script-tests/canvas-fillPath-shadow.js
LayoutTests/fast/canvas/script-tests/canvas-scale-shadowBlur.js
LayoutTests/fast/canvas/script-tests/canvas-scale-strokePath-shadow.js
LayoutTests/platform/mac/fast/canvas/canvas-scale-shadowBlur-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/ImageBuffer.h
Source/WebCore/platform/graphics/cg/ImageBufferCG.cpp
Source/WebCore/platform/graphics/cg/ImageBufferDataCG.cpp
Source/WebCore/platform/graphics/cg/ImageBufferDataCG.h

index a67c227..314cb37 100644 (file)
@@ -1,3 +1,23 @@
+2013-12-04  Myles C. Maxfield  <mmaxfield@apple.com>
+
+        Allow ImageBuffer to use an IOSurface that is larger than necessary
+        https://bugs.webkit.org/show_bug.cgi?id=124626
+
+        Reviewed by Simon Fraser.
+
+        Update tests to be more robust with respect to accelerated vs
+        non-accelerated ImageBuffers. 
+
+        * fast/canvas/script-tests/canvas-fillPath-shadow.js: Don't sample a canvas at exactly
+        the corner of a drawn shape (because the corner might be antialiased). Instead, sample
+        a single pixel inside the shape
+        * fast/canvas/script-tests/canvas-scale-shadowBlur.js: Don't sample a canvas at exactly
+        the edge of the blur radius. Instead, sample a single pixel past the blur radius.
+        * fast/canvas/script-tests/canvas-scale-strokePath-shadow.js:
+        (shouldBeAround): Allow this test to be less strict when sampling inside a blurred region
+        * platform/mac/fast/canvas/canvas-scale-shadowBlur-expected.txt: Matching update w/r/t
+        canvas-scale-shadowBlur.js
+
 2013-12-03  Dean Jackson  <dino@apple.com>
 
         [WebGL] Support for texImage2D/texSubImage2D of type HALF_FLOAT_OES
index f28b9db..1cb3140 100644 (file)
@@ -70,7 +70,7 @@ shouldBe('data[0]', '255');
 shouldBe('data[1]', '20');
 shouldBe('data[2]', '0');
 
-imageData = ctx.getImageData(380, 30, 1, 1);
+imageData = ctx.getImageData(381, 31, 1, 1);
 data = imageData.data;
 shouldBe('data[0]', '255');
 shouldBe('data[1]', '20');
index 6fdb73f..b36ab26 100644 (file)
@@ -67,7 +67,7 @@ shouldBe('d[1]', '0');
 shouldBe('d[2]', '0');
 shouldBeAround('d[3]', '255');
 
-d = ctx.getImageData(250, 175, 1, 1).data;
+d = ctx.getImageData(250, 174, 1, 1).data;
 shouldBe('d[0]', '0');
 shouldBe('d[1]', '0');
 shouldBe('d[2]', '0');
@@ -79,7 +79,7 @@ shouldBe('d[1]', '0');
 shouldBe('d[2]', '0');
 shouldBe('d[3]', '0');
 
-d = ctx.getImageData(175, 250, 1, 1).data;
+d = ctx.getImageData(174, 250, 1, 1).data;
 shouldBe('d[0]', '0');
 shouldBe('d[1]', '0');
 shouldBe('d[2]', '0');
@@ -135,7 +135,7 @@ shouldBe('d[1]', '0');
 shouldBe('d[2]', '0');
 shouldBeAround('d[3]', '255');
 
-d = ctx.getImageData(450, 175, 1, 1).data;
+d = ctx.getImageData(450, 174, 1, 1).data;
 shouldBe('d[0]', '0');
 shouldBe('d[1]', '0');
 shouldBe('d[2]', '0');
@@ -147,7 +147,7 @@ shouldBe('d[1]', '0');
 shouldBe('d[2]', '0');
 shouldBe('d[3]', '0');
 
-d = ctx.getImageData(375, 250, 1, 1).data;
+d = ctx.getImageData(374, 250, 1, 1).data;
 shouldBe('d[0]', '0');
 shouldBe('d[1]', '0');
 shouldBe('d[2]', '0');
index 2c3f0ba..0d41833 100644 (file)
@@ -10,7 +10,7 @@ function print(message, color)
     document.getElementById("console").appendChild(paragraph);
 }
 
-function shouldBeAround(a, b)
+function shouldBeAround(a, b, argdelta)
 {
     var evalA;
     try {
@@ -19,7 +19,11 @@ function shouldBeAround(a, b)
         evalA = e;
     }
 
-    if (Math.abs(evalA - b) < 20)
+    var delta = 20;
+    if (typeof argdelta != "undefined")
+        delta = argdelta;
+
+    if (Math.abs(evalA - b) < delta)
         print("PASS " + a + " is around " + b , "green")
     else
         print("FAIL " + a + " is not around " + b + " (actual: " + evalA + ")", "red");
@@ -119,7 +123,7 @@ d = ctx.getImageData(398, 210, 1, 1).data;
 shouldBe('d[0]', '255');
 shouldBe('d[1]', '0');
 shouldBe('d[2]', '0');
-shouldBeAround('d[3]', '200');
+shouldBeAround('d[3]', '200', 25);
 
 d = ctx.getImageData(508, 250, 1, 1).data;
 shouldBe('d[0]', '255');
@@ -131,7 +135,7 @@ d = ctx.getImageData(450, 198, 1, 1).data;
 shouldBe('d[0]', '255');
 shouldBe('d[1]', '0');
 shouldBe('d[2]', '0');
-shouldBeAround('d[3]', '199');
+shouldBeAround('d[3]', '199', 25);
 
 // Verify blurry alpha shadow.
 d = ctx.getImageData(505, 450, 1, 1).data;
index 27da9f0..87de7a3 100644 (file)
@@ -10,7 +10,7 @@ PASS d[3] is around 255
 FAIL d[0] should be 0. Was 255.
 PASS d[1] is 0
 PASS d[2] is 0
-FAIL d[3] should be 0. Was 6.
+FAIL d[3] should be 0. Was 5.
 FAIL d[0] should be 0. Was 255.
 PASS d[1] is 0
 PASS d[2] is 0
@@ -18,7 +18,7 @@ FAIL d[3] should be 0. Was 5.
 FAIL d[0] should be 0. Was 255.
 PASS d[1] is 0
 PASS d[2] is 0
-FAIL d[3] should be 0. Was 6.
+FAIL d[3] should be 0. Was 5.
 FAIL d[0] should be 0. Was 255.
 PASS d[1] is 0
 PASS d[2] is 0
@@ -54,7 +54,7 @@ PASS d[3] is around 255
 FAIL d[0] should be 0. Was 255.
 PASS d[1] is 0
 PASS d[2] is 0
-FAIL d[3] should be 0. Was 6.
+FAIL d[3] should be 0. Was 5.
 FAIL d[0] should be 0. Was 255.
 PASS d[1] is 0
 PASS d[2] is 0
@@ -62,7 +62,7 @@ FAIL d[3] should be 0. Was 5.
 FAIL d[0] should be 0. Was 255.
 PASS d[1] is 0
 PASS d[2] is 0
-FAIL d[3] should be 0. Was 6.
+FAIL d[3] should be 0. Was 5.
 FAIL d[0] should be 0. Was 255.
 PASS d[1] is 0
 PASS d[2] is 0
index 7cb546a..4b32b77 100644 (file)
@@ -1,3 +1,42 @@
+2013-12-04  Myles C. Maxfield  <mmaxfield@apple.com>
+
+        Allow ImageBuffer to use an IOSurface that is larger than necessary
+        https://bugs.webkit.org/show_bug.cgi?id=124626
+
+        Reviewed by Simon Fraser.
+
+        Because creating ImageBuffer's backing store can be so expensive, it
+        would be beneficial to have a pool of pre-created backing stores
+        available. However, this means that ImageBuffer might have to use a
+        backing store that is larger than the exact dimensions that it needs.
+        This patch adds a field, m_backingStoreSize, to CG's ImageBufferData
+        class, and uses this new field when performing ImageBuffer operations
+        to allow for larger-than-necessary backing stores. Content is always
+        drawn in the top left corner of the backing store.
+
+        No new tests are necessary because there is no behavior change.
+
+        * platform/graphics/ImageBuffer.h:
+        (WebCore::ImageBuffer::baseTransform): The base transform has to put
+        content at the top left corner instead of bottom left
+        * platform/graphics/cg/ImageBufferCG.cpp:
+        (WebCore::createCroppedImageIfNecessary): Convenience function to figure out
+        the dimensions of the backing texture in user space
+        (WebCore::ImageBuffer::ImageBuffer): Set up new m_backingStoreSize member
+        (WebCore::maybeCropToBounds): Some ImageBuffer API functions require
+        outputting an image with logical size. This function performs the cropping
+        (WebCore::ImageBuffer::copyImage): Updated for larger-than-necessary
+        backing stores
+        (WebCore::ImageBuffer::copyNativeImage): Ditto
+        (WebCore::ImageBuffer::draw): Ditto
+        (WebCore::ImageBuffer::clip): Ditto
+        (WebCore::ImageBuffer::putByteArray): Ditto
+        (WebCore::ImageBuffer::toDataURL): Ditto
+        * platform/graphics/cg/ImageBufferDataCG.cpp:
+        (WebCore::ImageBufferData::getData): Ditto
+        (WebCore::ImageBufferData::putData): Ditto
+        * platform/graphics/cg/ImageBufferDataCG.h: New m_backingStoreSize field
+
 2013-12-03  Dean Jackson  <dino@apple.com>
 
         [WebGL] Support for texImage2D of type HALF_FLOAT_OES
index abf8a6c..55311cf 100644 (file)
@@ -117,7 +117,7 @@ namespace WebCore {
         void transformColorSpace(ColorSpace srcColorSpace, ColorSpace dstColorSpace);
         void platformTransformColorSpace(const Vector<int>&);
 #else
-        AffineTransform baseTransform() const { return AffineTransform(1, 0, 0, -1, 0, internalSize().height()); }
+        AffineTransform baseTransform() const { return AffineTransform(1, 0, 0, -1, 0, m_data.m_backingStoreSize.height()); }
 #endif
 #if USE(ACCELERATED_COMPOSITING)
         PlatformLayer* platformLayer() const;
@@ -135,6 +135,8 @@ namespace WebCore {
 
     private:
 #if USE(CG)
+        // The returned image might be larger than the internalSize(). If you want the smaller
+        // image, crop the result.
         RetainPtr<CGImageRef> copyNativeImage(BackingStoreCopy = CopyBackingStore) const;
         void flushContext() const;
 #endif
index 607bf0f..b12bd4f 100644 (file)
@@ -98,6 +98,13 @@ 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();
+    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)
@@ -111,6 +118,7 @@ ImageBuffer::ImageBuffer(const IntSize& size, float resolutionScale, ColorSpace
         return;
 
     m_size = IntSize(scaledWidth, scaledHeight);
+    m_data.m_backingStoreSize = m_size;
 
     success = false;  // Make early return mean failure.
     bool accelerateRendering = renderingMode == Accelerated;
@@ -121,8 +129,8 @@ ImageBuffer::ImageBuffer(const IntSize& size, float resolutionScale, ColorSpace
     Checked<int, RecordOverflow> height = m_size.height();
 
     // Prevent integer overflows
-    m_data.m_bytesPerRow = 4 * width;
-    Checked<size_t, RecordOverflow> numBytes = height * m_data.m_bytesPerRow;
+    m_data.m_bytesPerRow = 4 * Checked<unsigned, RecordOverflow>(m_data.m_backingStoreSize.width());
+    Checked<size_t, RecordOverflow> numBytes = Checked<unsigned, RecordOverflow>(m_data.m_backingStoreSize.height()) * m_data.m_bytesPerRow;
     if (numBytes.hasOverflowed())
         return;
 
@@ -148,20 +156,21 @@ ImageBuffer::ImageBuffer(const IntSize& size, float resolutionScale, ColorSpace
     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));
+        m_data.m_surface = createIOSurface(m_data.m_backingStoreSize);
+        FloatSize userBounds = scaleSizeToUserSpace(FloatSize(width.unsafeGet(), height.unsafeGet()), m_data.m_backingStoreSize, m_size);
+        cgContext = adoptCF(wkIOSurfaceContextCreate(m_data.m_surface.get(), userBounds.width(), userBounds.height(), m_data.m_colorSpace));
 #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.m_backingStoreSize.height(), m_data.m_bytesPerRow.unsafeGet()).getValue(m_data.m_data))
             return;
         ASSERT(!(reinterpret_cast<intptr_t>(m_data.m_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));
+        cgContext = adoptCF(CGBitmapContextCreate(m_data.m_data, m_data.m_backingStoreSize.width(), m_data.m_backingStoreSize.height(), 8, m_data.m_bytesPerRow.unsafeGet(), m_data.m_colorSpace, m_data.m_bitmapInfo));
         // Create a live image that wraps the data.
         m_data.m_dataProvider = adoptCF(CGDataProviderCreateWithData(0, m_data.m_data, numBytes.unsafeGet(), releaseImageData));
     }
@@ -170,9 +179,9 @@ ImageBuffer::ImageBuffer(const IntSize& size, float resolutionScale, ColorSpace
         return;
 
     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->translate(0, -m_data.m_backingStoreSize.height());
+    m_context->applyDeviceScaleFactor(m_resolutionScale);
     m_context->setIsAcceleratedContext(accelerateRendering);
     success = true;
 }
@@ -191,6 +200,15 @@ void ImageBuffer::flushContext() const
     CGContextFlush(m_context->platformContext());
 }
 
+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()))) {
+        return adoptCF(CGImageCreateWithImageInRect(image, CGRectMake(0, static_cast<int>(CGImageGetHeight(image)) - bounds.height(), bounds.width(), bounds.height())));
+    }
+    return image;
+}
+
 PassRefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, ScaleBehavior scaleBehavior) const
 {
     RetainPtr<CGImageRef> image;
@@ -200,13 +218,18 @@ PassRefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, ScaleBeh
         image = copyNativeImage(DontCopyBackingStore);
         RetainPtr<CGContextRef> context = adoptCF(CGBitmapContextCreate(0, logicalSize().width(), logicalSize().height(), 8, 4 * logicalSize().width(), deviceRGBColorSpaceRef(), kCGImageAlphaPremultipliedLast));
         CGContextSetBlendMode(context.get(), kCGBlendModeCopy);
-        CGContextDrawImage(context.get(), CGRectMake(0, 0, logicalSize().width(), logicalSize().height()), image.get());
+        CGContextDrawImage(context.get(), CGRectMake(0, 0, m_data.m_backingStoreSize.width(), m_data.m_backingStoreSize.height()), image.get());
         image = adoptCF(CGBitmapContextCreateImage(context.get()));
     }
+    
+    image = createCroppedImageIfNecessary(image.get(), internalSize());
 
     if (!image)
         return 0;
 
+    ASSERT(CGImageGetWidth(image.get()) == static_cast<size_t>(m_logicalSize.width()));
+    ASSERT(CGImageGetHeight(image.get()) == static_cast<size_t>(m_logicalSize.height()));
+
     RefPtr<BitmapImage> bitmapImage = BitmapImage::create(image.get());
     bitmapImage->setSpaceSize(spaceSize());
 
@@ -224,7 +247,7 @@ RetainPtr<CGImageRef> ImageBuffer::copyNativeImage(BackingStoreCopy copyBehavior
     if (!m_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 = CGImageCreate(m_data.m_backingStoreSize.width(), m_data.m_backingStoreSize.height(), 8, 32, m_data.m_bytesPerRow.unsafeGet(), m_data.m_colorSpace, m_data.m_bitmapInfo, m_data.m_dataProvider.get(), 0, true, kCGRenderingIntentDefault);
             break;
         case CopyBackingStore:
             image = CGBitmapContextCreateImage(context()->platformContext());
@@ -279,12 +302,15 @@ void ImageBuffer::drawPattern(GraphicsContext* destContext, const FloatRect& src
 
 void ImageBuffer::clip(GraphicsContext* contextToClip, const FloatRect& rect) const
 {
+    FloatSize backingStoreSizeInUserSpace = scaleSizeToUserSpace(rect.size(), m_data.m_backingStoreSize, internalSize());
+
     CGContextRef platformContextToClip = contextToClip->platformContext();
     // FIXME: This image needs to be grayscale to be used as an alpha mask here.
     RetainPtr<CGImageRef> image = copyNativeImage(DontCopyBackingStore);
-    CGContextTranslateCTM(platformContextToClip, rect.x(), rect.y() + rect.height());
+    CGContextTranslateCTM(platformContextToClip, rect.x(), rect.y() + backingStoreSizeInUserSpace.height());
     CGContextScaleCTM(platformContextToClip, 1, -1);
-    CGContextClipToMask(platformContextToClip, FloatRect(FloatPoint(), rect.size()), image.get());
+    CGContextClipToRect(platformContextToClip, FloatRect(FloatPoint(0, backingStoreSizeInUserSpace.height() - rect.height()), rect.size()));
+    CGContextClipToMask(platformContextToClip, FloatRect(FloatPoint(), backingStoreSizeInUserSpace), image.get());
     CGContextScaleCTM(platformContextToClip, 1, -1);
     CGContextTranslateCTM(platformContextToClip, -rect.x(), -rect.y() - rect.height());
 }
@@ -335,10 +361,14 @@ void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, c
     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.m_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();
-    CGContextDrawImage(destContext, destRectInCGCoords, sourceCopyImage.get());
+    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
 }
@@ -442,13 +472,16 @@ String ImageBuffer::toDataURL(const String& mimeType, const double* quality, Coo
         image = adoptCF(CGImageCreate(logicalSize().width(), logicalSize().height(), 8, 32, 4 * logicalSize().width(),
                                     deviceRGBColorSpaceRef(), kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipLast,
                                     dataProvider.get(), 0, false, kCGRenderingIntentDefault));
-    } else if (m_resolutionScale == 1)
+    } else if (m_resolutionScale == 1) {
         image = copyNativeImage(CopyBackingStore);
-    else {
+        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));
         CGContextSetBlendMode(context.get(), kCGBlendModeCopy);
-        CGContextDrawImage(context.get(), CGRectMake(0, 0, logicalSize().width(), logicalSize().height()), image.get());
+        CGContextClipToRect(context.get(), CGRectMake(0, 0, logicalSize().width(), logicalSize().height()));
+        FloatSize imageRectInUserBounds = scaleSizeToUserSpace(logicalSize(), m_data.m_backingStoreSize, internalSize());
+        CGContextDrawImage(context.get(), CGRectMake(0, 0, imageRectInUserBounds.width(), imageRectInUserBounds.height()), image.get());
         image = adoptCF(CGBitmapContextCreateImage(context.get()));
     }
 
index 556ae54..de4c387 100644 (file)
@@ -158,7 +158,7 @@ PassRefPtr<Uint8ClampedArray> ImageBufferData::getData(const IntRect& rect, cons
     unsigned char* srcRows;
     
     if (!accelerateRendering) {
-        srcBytesPerRow = 4 * size.width();
+        srcBytesPerRow = m_bytesPerRow.unsafeGet();
         srcRows = reinterpret_cast<unsigned char*>(m_data) + originy * srcBytesPerRow + originx * 4;
         
 #if USE(ACCELERATE)
@@ -339,6 +339,10 @@ PassRefPtr<Uint8ClampedArray> ImageBufferData::getData(const IntRect& rect, cons
 
 void ImageBufferData::putData(Uint8ClampedArray*& source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, const IntSize& size, bool accelerateRendering, bool unmultiplied, float resolutionScale)
 {
+#if ASSERT_DISABLED
+    UNUSED_PARAM(size);
+#endif
+
     ASSERT(sourceRect.width() > 0);
     ASSERT(sourceRect.height() > 0);
     
@@ -381,7 +385,7 @@ void ImageBufferData::putData(Uint8ClampedArray*& source, const IntSize& sourceS
     unsigned char* destRows;
     
     if (!accelerateRendering) {
-        destBytesPerRow = 4 * size.width();
+        destBytesPerRow = m_bytesPerRow.unsafeGet();
         destRows = reinterpret_cast<unsigned char*>(m_data) + (desty * destBytesPerRow + destx * 4).unsafeGet();
         
 #if  USE(ACCELERATE)
index c399209..a41906b 100644 (file)
@@ -54,6 +54,7 @@ public:
     Checked<unsigned, RecordOverflow> m_bytesPerRow;
     CGColorSpaceRef m_colorSpace;
     RetainPtr<IOSurfaceRef> m_surface;
+    IntSize m_backingStoreSize;
 
     PassRefPtr<Uint8ClampedArray> getData(const IntRect&, const IntSize&, bool accelerateRendering, bool unmultiplied, float resolutionScale) const;
     void putData(Uint8ClampedArray*& source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, const IntSize&, bool accelerateRendering, bool unmultiplied, float resolutionScale);