⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 160121 in webkit


Ignore:
Timestamp:
Dec 4, 2013, 1:35:38 PM (13 years ago)
Author:
commit-queue@webkit.org
Message:

Allow ImageBuffer to use an IOSurface that is larger than necessary
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

Location:
trunk
Files:
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r160119 r160121  
     12013-12-04  Myles C. Maxfield  <mmaxfield@apple.com>
     2
     3        Allow ImageBuffer to use an IOSurface that is larger than necessary
     4        https://bugs.webkit.org/show_bug.cgi?id=124626
     5
     6        Reviewed by Simon Fraser.
     7
     8        Update tests to be more robust with respect to accelerated vs
     9        non-accelerated ImageBuffers.
     10
     11        * fast/canvas/script-tests/canvas-fillPath-shadow.js: Don't sample a canvas at exactly
     12        the corner of a drawn shape (because the corner might be antialiased). Instead, sample
     13        a single pixel inside the shape
     14        * fast/canvas/script-tests/canvas-scale-shadowBlur.js: Don't sample a canvas at exactly
     15        the edge of the blur radius. Instead, sample a single pixel past the blur radius.
     16        * fast/canvas/script-tests/canvas-scale-strokePath-shadow.js:
     17        (shouldBeAround): Allow this test to be less strict when sampling inside a blurred region
     18        * platform/mac/fast/canvas/canvas-scale-shadowBlur-expected.txt: Matching update w/r/t
     19        canvas-scale-shadowBlur.js
     20
    1212013-12-03  Dean Jackson  <dino@apple.com>
    222
  • trunk/LayoutTests/fast/canvas/script-tests/canvas-fillPath-shadow.js

    r98407 r160121  
    7171shouldBe('data[2]', '0');
    7272
    73 imageData = ctx.getImageData(380, 30, 1, 1);
     73imageData = ctx.getImageData(381, 31, 1, 1);
    7474data = imageData.data;
    7575shouldBe('data[0]', '255');
  • trunk/LayoutTests/fast/canvas/script-tests/canvas-scale-shadowBlur.js

    r98407 r160121  
    6868shouldBeAround('d[3]', '255');
    6969
    70 d = ctx.getImageData(250, 175, 1, 1).data;
     70d = ctx.getImageData(250, 174, 1, 1).data;
    7171shouldBe('d[0]', '0');
    7272shouldBe('d[1]', '0');
     
    8080shouldBe('d[3]', '0');
    8181
    82 d = ctx.getImageData(175, 250, 1, 1).data;
     82d = ctx.getImageData(174, 250, 1, 1).data;
    8383shouldBe('d[0]', '0');
    8484shouldBe('d[1]', '0');
     
    136136shouldBeAround('d[3]', '255');
    137137
    138 d = ctx.getImageData(450, 175, 1, 1).data;
     138d = ctx.getImageData(450, 174, 1, 1).data;
    139139shouldBe('d[0]', '0');
    140140shouldBe('d[1]', '0');
     
    148148shouldBe('d[3]', '0');
    149149
    150 d = ctx.getImageData(375, 250, 1, 1).data;
     150d = ctx.getImageData(374, 250, 1, 1).data;
    151151shouldBe('d[0]', '0');
    152152shouldBe('d[1]', '0');
  • trunk/LayoutTests/fast/canvas/script-tests/canvas-scale-strokePath-shadow.js

    r98407 r160121  
    1111}
    1212
    13 function shouldBeAround(a, b)
     13function shouldBeAround(a, b, argdelta)
    1414{
    1515    var evalA;
     
    2020    }
    2121
    22     if (Math.abs(evalA - b) < 20)
     22    var delta = 20;
     23    if (typeof argdelta != "undefined")
     24        delta = argdelta;
     25
     26    if (Math.abs(evalA - b) < delta)
    2327        print("PASS " + a + " is around " + b , "green")
    2428    else
     
    120124shouldBe('d[1]', '0');
    121125shouldBe('d[2]', '0');
    122 shouldBeAround('d[3]', '200');
     126shouldBeAround('d[3]', '200', 25);
    123127
    124128d = ctx.getImageData(508, 250, 1, 1).data;
     
    132136shouldBe('d[1]', '0');
    133137shouldBe('d[2]', '0');
    134 shouldBeAround('d[3]', '199');
     138shouldBeAround('d[3]', '199', 25);
    135139
    136140// Verify blurry alpha shadow.
  • trunk/LayoutTests/platform/mac/fast/canvas/canvas-scale-shadowBlur-expected.txt

    r126203 r160121  
    1111PASS d[1] is 0
    1212PASS d[2] is 0
    13 FAIL d[3] should be 0. Was 6.
     13FAIL d[3] should be 0. Was 5.
    1414FAIL d[0] should be 0. Was 255.
    1515PASS d[1] is 0
     
    1919PASS d[1] is 0
    2020PASS d[2] is 0
    21 FAIL d[3] should be 0. Was 6.
     21FAIL d[3] should be 0. Was 5.
    2222FAIL d[0] should be 0. Was 255.
    2323PASS d[1] is 0
     
    5555PASS d[1] is 0
    5656PASS d[2] is 0
    57 FAIL d[3] should be 0. Was 6.
     57FAIL d[3] should be 0. Was 5.
    5858FAIL d[0] should be 0. Was 255.
    5959PASS d[1] is 0
     
    6363PASS d[1] is 0
    6464PASS d[2] is 0
    65 FAIL d[3] should be 0. Was 6.
     65FAIL d[3] should be 0. Was 5.
    6666FAIL d[0] should be 0. Was 255.
    6767PASS d[1] is 0
  • trunk/Source/WebCore/ChangeLog

    r160119 r160121  
     12013-12-04  Myles C. Maxfield  <mmaxfield@apple.com>
     2
     3        Allow ImageBuffer to use an IOSurface that is larger than necessary
     4        https://bugs.webkit.org/show_bug.cgi?id=124626
     5
     6        Reviewed by Simon Fraser.
     7
     8        Because creating ImageBuffer's backing store can be so expensive, it
     9        would be beneficial to have a pool of pre-created backing stores
     10        available. However, this means that ImageBuffer might have to use a
     11        backing store that is larger than the exact dimensions that it needs.
     12        This patch adds a field, m_backingStoreSize, to CG's ImageBufferData
     13        class, and uses this new field when performing ImageBuffer operations
     14        to allow for larger-than-necessary backing stores. Content is always
     15        drawn in the top left corner of the backing store.
     16
     17        No new tests are necessary because there is no behavior change.
     18
     19        * platform/graphics/ImageBuffer.h:
     20        (WebCore::ImageBuffer::baseTransform): The base transform has to put
     21        content at the top left corner instead of bottom left
     22        * platform/graphics/cg/ImageBufferCG.cpp:
     23        (WebCore::createCroppedImageIfNecessary): Convenience function to figure out
     24        the dimensions of the backing texture in user space
     25        (WebCore::ImageBuffer::ImageBuffer): Set up new m_backingStoreSize member
     26        (WebCore::maybeCropToBounds): Some ImageBuffer API functions require
     27        outputting an image with logical size. This function performs the cropping
     28        (WebCore::ImageBuffer::copyImage): Updated for larger-than-necessary
     29        backing stores
     30        (WebCore::ImageBuffer::copyNativeImage): Ditto
     31        (WebCore::ImageBuffer::draw): Ditto
     32        (WebCore::ImageBuffer::clip): Ditto
     33        (WebCore::ImageBuffer::putByteArray): Ditto
     34        (WebCore::ImageBuffer::toDataURL): Ditto
     35        * platform/graphics/cg/ImageBufferDataCG.cpp:
     36        (WebCore::ImageBufferData::getData): Ditto
     37        (WebCore::ImageBufferData::putData): Ditto
     38        * platform/graphics/cg/ImageBufferDataCG.h: New m_backingStoreSize field
     39
    1402013-12-03  Dean Jackson  <dino@apple.com>
    241
  • trunk/Source/WebCore/platform/graphics/ImageBuffer.h

    r159791 r160121  
    118118        void platformTransformColorSpace(const Vector<int>&);
    119119#else
    120         AffineTransform baseTransform() const { return AffineTransform(1, 0, 0, -1, 0, internalSize().height()); }
     120        AffineTransform baseTransform() const { return AffineTransform(1, 0, 0, -1, 0, m_data.m_backingStoreSize.height()); }
    121121#endif
    122122#if USE(ACCELERATED_COMPOSITING)
     
    136136    private:
    137137#if USE(CG)
     138        // The returned image might be larger than the internalSize(). If you want the smaller
     139        // image, crop the result.
    138140        RetainPtr<CGImageRef> copyNativeImage(BackingStoreCopy = CopyBackingStore) const;
    139141        void flushContext() const;
  • trunk/Source/WebCore/platform/graphics/cg/ImageBufferCG.cpp

    r160021 r160121  
    9999}
    100100
     101static FloatSize scaleSizeToUserSpace(const FloatSize& logicalSize, const IntSize& backingStoreSize, const IntSize& internalSize)
     102{
     103    float xMagnification = static_cast<float>(backingStoreSize.width()) / internalSize.width();
     104    float yMagnification = static_cast<float>(backingStoreSize.height()) / internalSize.height();
     105    return FloatSize(logicalSize.width() * xMagnification, logicalSize.height() * yMagnification);
     106}
     107
    101108ImageBuffer::ImageBuffer(const IntSize& size, float resolutionScale, ColorSpace imageColorSpace, RenderingMode renderingMode, bool& success)
    102109    : m_data(size) // NOTE: The input here isn't important as ImageBufferDataCG's constructor just ignores it.
     
    112119
    113120    m_size = IntSize(scaledWidth, scaledHeight);
     121    m_data.m_backingStoreSize = m_size;
    114122
    115123    success = false;  // Make early return mean failure.
     
    122130
    123131    // Prevent integer overflows
    124     m_data.m_bytesPerRow = 4 * width;
    125     Checked<size_t, RecordOverflow> numBytes = height * m_data.m_bytesPerRow;
     132    m_data.m_bytesPerRow = 4 * Checked<unsigned, RecordOverflow>(m_data.m_backingStoreSize.width());
     133    Checked<size_t, RecordOverflow> numBytes = Checked<unsigned, RecordOverflow>(m_data.m_backingStoreSize.height()) * m_data.m_bytesPerRow;
    126134    if (numBytes.hasOverflowed())
    127135        return;
     
    149157    if (accelerateRendering) {
    150158#if USE(IOSURFACE_CANVAS_BACKING_STORE)
    151         m_data.m_surface = createIOSurface(m_size);
    152         cgContext = adoptCF(wkIOSurfaceContextCreate(m_data.m_surface.get(), width.unsafeGet(), height.unsafeGet(), m_data.m_colorSpace));
     159        m_data.m_surface = createIOSurface(m_data.m_backingStoreSize);
     160        FloatSize userBounds = scaleSizeToUserSpace(FloatSize(width.unsafeGet(), height.unsafeGet()), m_data.m_backingStoreSize, m_size);
     161        cgContext = adoptCF(wkIOSurfaceContextCreate(m_data.m_surface.get(), userBounds.width(), userBounds.height(), m_data.m_colorSpace));
    153162#endif
    154163        if (!cgContext)
     
    157166
    158167    if (!accelerateRendering) {
    159         if (!tryFastCalloc(height.unsafeGet(), m_data.m_bytesPerRow.unsafeGet()).getValue(m_data.m_data))
     168        if (!tryFastCalloc(m_data.m_backingStoreSize.height(), m_data.m_bytesPerRow.unsafeGet()).getValue(m_data.m_data))
    160169            return;
    161170        ASSERT(!(reinterpret_cast<intptr_t>(m_data.m_data) & 3));
    162171
    163172        m_data.m_bitmapInfo = kCGImageAlphaPremultipliedLast;
    164         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));
     173        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));
    165174        // Create a live image that wraps the data.
    166175        m_data.m_dataProvider = adoptCF(CGDataProviderCreateWithData(0, m_data.m_data, numBytes.unsafeGet(), releaseImageData));
     
    171180
    172181    m_context = adoptPtr(new GraphicsContext(cgContext.get()));
     182    m_context->scale(FloatSize(1, -1));
     183    m_context->translate(0, -m_data.m_backingStoreSize.height());
    173184    m_context->applyDeviceScaleFactor(m_resolutionScale);
    174     m_context->scale(FloatSize(1, -1));
    175     m_context->translate(0, -size.height());
    176185    m_context->setIsAcceleratedContext(accelerateRendering);
    177186    success = true;
     
    190199{
    191200    CGContextFlush(m_context->platformContext());
     201}
     202
     203static RetainPtr<CGImageRef> createCroppedImageIfNecessary(CGImageRef image, const IntSize& bounds)
     204{
     205    if (image && (CGImageGetWidth(image) != static_cast<size_t>(bounds.width())
     206        || CGImageGetHeight(image) != static_cast<size_t>(bounds.height()))) {
     207        return adoptCF(CGImageCreateWithImageInRect(image, CGRectMake(0, static_cast<int>(CGImageGetHeight(image)) - bounds.height(), bounds.width(), bounds.height())));
     208    }
     209    return image;
    192210}
    193211
     
    201219        RetainPtr<CGContextRef> context = adoptCF(CGBitmapContextCreate(0, logicalSize().width(), logicalSize().height(), 8, 4 * logicalSize().width(), deviceRGBColorSpaceRef(), kCGImageAlphaPremultipliedLast));
    202220        CGContextSetBlendMode(context.get(), kCGBlendModeCopy);
    203         CGContextDrawImage(context.get(), CGRectMake(0, 0, logicalSize().width(), logicalSize().height()), image.get());
     221        CGContextDrawImage(context.get(), CGRectMake(0, 0, m_data.m_backingStoreSize.width(), m_data.m_backingStoreSize.height()), image.get());
    204222        image = adoptCF(CGBitmapContextCreateImage(context.get()));
    205223    }
     224   
     225    image = createCroppedImageIfNecessary(image.get(), internalSize());
    206226
    207227    if (!image)
    208228        return 0;
     229
     230    ASSERT(CGImageGetWidth(image.get()) == static_cast<size_t>(m_logicalSize.width()));
     231    ASSERT(CGImageGetHeight(image.get()) == static_cast<size_t>(m_logicalSize.height()));
    209232
    210233    RefPtr<BitmapImage> bitmapImage = BitmapImage::create(image.get());
     
    225248        switch (copyBehavior) {
    226249        case DontCopyBackingStore:
    227             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);
     250            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);
    228251            break;
    229252        case CopyBackingStore:
     
    280303void ImageBuffer::clip(GraphicsContext* contextToClip, const FloatRect& rect) const
    281304{
     305    FloatSize backingStoreSizeInUserSpace = scaleSizeToUserSpace(rect.size(), m_data.m_backingStoreSize, internalSize());
     306
    282307    CGContextRef platformContextToClip = contextToClip->platformContext();
    283308    // FIXME: This image needs to be grayscale to be used as an alpha mask here.
    284309    RetainPtr<CGImageRef> image = copyNativeImage(DontCopyBackingStore);
    285     CGContextTranslateCTM(platformContextToClip, rect.x(), rect.y() + rect.height());
     310    CGContextTranslateCTM(platformContextToClip, rect.x(), rect.y() + backingStoreSizeInUserSpace.height());
    286311    CGContextScaleCTM(platformContextToClip, 1, -1);
    287     CGContextClipToMask(platformContextToClip, FloatRect(FloatPoint(), rect.size()), image.get());
     312    CGContextClipToRect(platformContextToClip, FloatRect(FloatPoint(0, backingStoreSizeInUserSpace.height() - rect.height()), rect.size()));
     313    CGContextClipToMask(platformContextToClip, FloatRect(FloatPoint(), backingStoreSizeInUserSpace), image.get());
    288314    CGContextScaleCTM(platformContextToClip, 1, -1);
    289315    CGContextTranslateCTM(platformContextToClip, -rect.x(), -rect.y() - rect.height());
     
    336362
    337363    // Draw the image in CG coordinate space
    338     IntPoint destPointInCGCoords(destPoint.x() + sourceRect.x(), (coordinateSystem == LogicalCoordinateSystem ? logicalSize() : internalSize()).height() - (destPoint.y() + sourceRect.y()) - sourceRect.height());
     364    FloatSize scaledDestSize = scaleSizeToUserSpace(coordinateSystem == LogicalCoordinateSystem ? logicalSize() : internalSize(), m_data.m_backingStoreSize, internalSize());
     365    IntPoint destPointInCGCoords(destPoint.x() + sourceRect.x(), scaledDestSize.height() - (destPoint.y() + sourceRect.y()) - sourceRect.height());
    339366    IntRect destRectInCGCoords(destPointInCGCoords, sourceCopySize);
     367    CGContextClipToRect(destContext, destRectInCGCoords);
     368
    340369    RetainPtr<CGImageRef> sourceCopyImage = sourceCopy->copyNativeImage();
    341     CGContextDrawImage(destContext, destRectInCGCoords, sourceCopyImage.get());
     370    FloatRect backingStoreInDestRect = FloatRect(FloatPoint(destPointInCGCoords.x(), destPointInCGCoords.y() + sourceCopySize.height() - (int)CGImageGetHeight(sourceCopyImage.get())), FloatSize(CGImageGetWidth(sourceCopyImage.get()), CGImageGetHeight(sourceCopyImage.get())));
     371    CGContextDrawImage(destContext, backingStoreInDestRect, sourceCopyImage.get());
    342372    CGContextRestoreGState(destContext);
    343373#endif
     
    443473                                    deviceRGBColorSpaceRef(), kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipLast,
    444474                                    dataProvider.get(), 0, false, kCGRenderingIntentDefault));
    445     } else if (m_resolutionScale == 1)
     475    } else if (m_resolutionScale == 1) {
    446476        image = copyNativeImage(CopyBackingStore);
    447     else {
     477        image = createCroppedImageIfNecessary(image.get(), internalSize());
     478    } else {
    448479        image = copyNativeImage(DontCopyBackingStore);
    449480        RetainPtr<CGContextRef> context = adoptCF(CGBitmapContextCreate(0, logicalSize().width(), logicalSize().height(), 8, 4 * logicalSize().width(), deviceRGBColorSpaceRef(), kCGImageAlphaPremultipliedLast));
    450481        CGContextSetBlendMode(context.get(), kCGBlendModeCopy);
    451         CGContextDrawImage(context.get(), CGRectMake(0, 0, logicalSize().width(), logicalSize().height()), image.get());
     482        CGContextClipToRect(context.get(), CGRectMake(0, 0, logicalSize().width(), logicalSize().height()));
     483        FloatSize imageRectInUserBounds = scaleSizeToUserSpace(logicalSize(), m_data.m_backingStoreSize, internalSize());
     484        CGContextDrawImage(context.get(), CGRectMake(0, 0, imageRectInUserBounds.width(), imageRectInUserBounds.height()), image.get());
    452485        image = adoptCF(CGBitmapContextCreateImage(context.get()));
    453486    }
  • trunk/Source/WebCore/platform/graphics/cg/ImageBufferDataCG.cpp

    r159027 r160121  
    159159   
    160160    if (!accelerateRendering) {
    161         srcBytesPerRow = 4 * size.width();
     161        srcBytesPerRow = m_bytesPerRow.unsafeGet();
    162162        srcRows = reinterpret_cast<unsigned char*>(m_data) + originy * srcBytesPerRow + originx * 4;
    163163       
     
    340340void ImageBufferData::putData(Uint8ClampedArray*& source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, const IntSize& size, bool accelerateRendering, bool unmultiplied, float resolutionScale)
    341341{
     342#if ASSERT_DISABLED
     343    UNUSED_PARAM(size);
     344#endif
     345
    342346    ASSERT(sourceRect.width() > 0);
    343347    ASSERT(sourceRect.height() > 0);
     
    382386   
    383387    if (!accelerateRendering) {
    384         destBytesPerRow = 4 * size.width();
     388        destBytesPerRow = m_bytesPerRow.unsafeGet();
    385389        destRows = reinterpret_cast<unsigned char*>(m_data) + (desty * destBytesPerRow + destx * 4).unsafeGet();
    386390       
  • trunk/Source/WebCore/platform/graphics/cg/ImageBufferDataCG.h

    r158330 r160121  
    5555    CGColorSpaceRef m_colorSpace;
    5656    RetainPtr<IOSurfaceRef> m_surface;
     57    IntSize m_backingStoreSize;
    5758
    5859    PassRefPtr<Uint8ClampedArray> getData(const IntRect&, const IntSize&, bool accelerateRendering, bool unmultiplied, float resolutionScale) const;
Note: See TracChangeset for help on using the changeset viewer.