Title: [158162] branches/safari-537.73-branch

Diff

Modified: branches/safari-537.73-branch/LayoutTests/ChangeLog (158161 => 158162)


--- branches/safari-537.73-branch/LayoutTests/ChangeLog	2013-10-29 02:45:26 UTC (rev 158161)
+++ branches/safari-537.73-branch/LayoutTests/ChangeLog	2013-10-29 02:52:49 UTC (rev 158162)
@@ -1,5 +1,21 @@
 2013-10-28  Lucas Forschler  <[email protected]>
 
+        Merge r155665
+
+    2013-09-12  Tim Horton  <[email protected]>
+
+            [mac] Cache rendered image in PDFDocumentImage
+            https://bugs.webkit.org/show_bug.cgi?id=121207
+
+            Reviewed by Simon Fraser.
+
+            Add a test ensuring that very large PDF-in-<img> elements don't crash.
+
+            * fast/images/pdf-as-image-too-big-expected.txt: Added.
+            * fast/images/pdf-as-image-too-big.html: Added.
+
+2013-10-28  Lucas Forschler  <[email protected]>
+
     Rollout 158067.
 
 2013-10-25  Lucas Forschler  <[email protected]>

Copied: branches/safari-537.73-branch/LayoutTests/fast/images/pdf-as-image-too-big-expected.txt (from rev 155665, trunk/LayoutTests/fast/images/pdf-as-image-too-big-expected.txt) (0 => 158162)


--- branches/safari-537.73-branch/LayoutTests/fast/images/pdf-as-image-too-big-expected.txt	                        (rev 0)
+++ branches/safari-537.73-branch/LayoutTests/fast/images/pdf-as-image-too-big-expected.txt	2013-10-29 02:52:49 UTC (rev 158162)
@@ -0,0 +1 @@
+This test should not crash.

Copied: branches/safari-537.73-branch/LayoutTests/fast/images/pdf-as-image-too-big.html (from rev 155665, trunk/LayoutTests/fast/images/pdf-as-image-too-big.html) (0 => 158162)


--- branches/safari-537.73-branch/LayoutTests/fast/images/pdf-as-image-too-big.html	                        (rev 0)
+++ branches/safari-537.73-branch/LayoutTests/fast/images/pdf-as-image-too-big.html	2013-10-29 02:52:49 UTC (rev 158162)
@@ -0,0 +1,14 @@
+<html>
+<head>
+    <script>
+    if (window.testRunner)
+        testRunner.dumpAsText();
+    </script>
+</head>
+<body style='margin: 0px'>
+<div style='background: red; height: 100000px; width: 100000px;'>
+    <img src='' width="100000px">
+    This test should not crash.
+</div>
+</body>
+</html>
\ No newline at end of file

Modified: branches/safari-537.73-branch/Source/WebCore/ChangeLog (158161 => 158162)


--- branches/safari-537.73-branch/Source/WebCore/ChangeLog	2013-10-29 02:45:26 UTC (rev 158161)
+++ branches/safari-537.73-branch/Source/WebCore/ChangeLog	2013-10-29 02:52:49 UTC (rev 158162)
@@ -1,5 +1,69 @@
 2013-10-28  Lucas Forschler  <[email protected]>
 
+        Merge r155665
+
+    2013-09-12  Tim Horton  <[email protected]>
+
+            [mac] Cache rendered image in PDFDocumentImage
+            https://bugs.webkit.org/show_bug.cgi?id=121207
+
+            Reviewed by Simon Fraser.
+
+            Tests: fast/images/pdf-as-image-too-big.html
+
+            * loader/cache/CachedImage.cpp:
+            (WebCore::CachedImage::createImage):
+            PDFDocumentImage takes a ImageObserver now so that it can report
+            decoded data size changes to the memory cache.
+
+            * platform/graphics/Image.h:
+            (WebCore::Image::isPDFDocumentImage): Added.
+
+            * platform/graphics/cg/PDFDocumentImage.cpp:
+            (WebCore::PDFDocumentImage::PDFDocumentImage):
+            PDFDocumentImage takes a ImageObserver now so that it can report
+            decoded data size changes to the memory cache.
+
+            (WebCore::PDFDocumentImage::applyRotationForPainting):
+            Fix up some comments, and use GraphicsContext instead of CG API.
+
+            (WebCore::PDFDocumentImage::cacheParametersMatch):
+            Determine whether our cached image is still valid, given the new
+            destination's size, CTM scale, and source rect.
+
+            (WebCore::transformContextForPainting): Added.
+
+            (WebCore::PDFDocumentImage::updateCachedImageIfNeeded):
+            Cache a rendered bitmap of the PDF. Invalidate the cache if cacheParametersMatch
+            decides that the parameters don't match, unless we're painting in low quality mode,
+            in which case we'll scale the existing image (and then fully repaint when the
+            high-quality repaint timer fires).
+            Inform the memory cache of our new size.
+
+            (WebCore::PDFDocumentImage::draw):
+            Update the cached image if needed.
+            Paint the cached image into the context if it's available (which it might not be,
+            if the image is way too big and the allocation fails). Otherwise, paint straight
+            into the context as we previously did.
+
+            (WebCore::PDFDocumentImage::destroyDecodedData):
+            Throw away the cached image if requested.
+
+            (WebCore::PDFDocumentImage::decodedSize):
+            (WebCore::PDFDocumentImage::drawPDFPage):
+            Drive-by use GraphicsContext instead of CG directly.
+
+            * platform/graphics/cg/PDFDocumentImage.h:
+            (WebCore::PDFDocumentImage::create):
+            Override isPDFDocumentImage().
+            Add storage for the cached image buffer and various cache parameters.
+
+            * rendering/ImageQualityController.cpp:
+            (WebCore::ImageQualityController::shouldPaintAtLowQuality):
+            PDFDocumentImage is also interested in/capable of low-quality painting now.
+
+2013-10-28  Lucas Forschler  <[email protected]>
+
         Merge r155664
 
     2013-09-12  Tim Horton  <[email protected]>

Modified: branches/safari-537.73-branch/Source/WebCore/loader/cache/CachedImage.cpp (158161 => 158162)


--- branches/safari-537.73-branch/Source/WebCore/loader/cache/CachedImage.cpp	2013-10-29 02:45:26 UTC (rev 158161)
+++ branches/safari-537.73-branch/Source/WebCore/loader/cache/CachedImage.cpp	2013-10-29 02:52:49 UTC (rev 158162)
@@ -313,7 +313,7 @@
         return;
 #if USE(CG) && !USE(WEBKIT_IMAGE_DECODERS)
     else if (m_response.mimeType() == "application/pdf")
-        m_image = PDFDocumentImage::create();
+        m_image = PDFDocumentImage::create(this);
 #endif
 #if ENABLE(SVG)
     else if (m_response.mimeType() == "image/svg+xml") {

Modified: branches/safari-537.73-branch/Source/WebCore/platform/graphics/Image.h (158161 => 158162)


--- branches/safari-537.73-branch/Source/WebCore/platform/graphics/Image.h	2013-10-29 02:45:26 UTC (rev 158161)
+++ branches/safari-537.73-branch/Source/WebCore/platform/graphics/Image.h	2013-10-29 02:52:49 UTC (rev 158162)
@@ -99,6 +99,7 @@
 
     virtual bool isSVGImage() const { return false; }
     virtual bool isBitmapImage() const { return false; }
+    virtual bool isPDFDocumentImage() const { return false; }
     virtual bool currentFrameKnownToBeOpaque() = 0;
 
     // Derived classes should override this if they can assure that 

Modified: branches/safari-537.73-branch/Source/WebCore/platform/graphics/cg/PDFDocumentImage.cpp (158161 => 158162)


--- branches/safari-537.73-branch/Source/WebCore/platform/graphics/cg/PDFDocumentImage.cpp	2013-10-29 02:45:26 UTC (rev 158161)
+++ branches/safari-537.73-branch/Source/WebCore/platform/graphics/cg/PDFDocumentImage.cpp	2013-10-29 02:52:49 UTC (rev 158162)
@@ -30,6 +30,7 @@
 #if USE(CG)
 
 #include "GraphicsContext.h"
+#include "ImageBuffer.h"
 #include "ImageObserver.h"
 #include "Length.h"
 #include "SharedBuffer.h"
@@ -44,8 +45,9 @@
 
 namespace WebCore {
 
-PDFDocumentImage::PDFDocumentImage()
-    : Image(0) // PDFs don't animate
+PDFDocumentImage::PDFDocumentImage(ImageObserver* observer)
+    : Image(observer)
+    , m_cachedBytes(0)
     , m_rotation(0.0f)
     , m_hasPage(false)
 {
@@ -95,53 +97,126 @@
 
 void PDFDocumentImage::applyRotationForPainting(GraphicsContext* context) const
 {
-    // rotate the crop box and calculate bounding box
+    // Rotate the crop box and calculate bounding box.
     float sina = sinf(-m_rotation);
     float cosa = cosf(-m_rotation);
     float width = m_cropBox.width();
     float height = m_cropBox.height();
 
-    // calculate rotated x and y edges of the crop box. if they're negative, it means part of the image has
-    // been rotated outside of the bounds and we need to shift over the image so it lies inside the bounds again
+    // Calculate rotated x and y edges of the crop box. If they're negative, it means part of the image has
+    // been rotated outside of the bounds and we need to shift over the image so it lies inside the bounds again.
     CGPoint rx = CGPointMake(width * cosa, width * sina);
     CGPoint ry = CGPointMake(-height * sina, height * cosa);
 
-    // adjust so we are at the crop box origin
+    // Adjust so we are at the crop box origin.
     const CGFloat zero = 0;
-    CGContextTranslateCTM(context->platformContext(), floorf(-std::min(zero, std::min(rx.x, ry.x))), floorf(-std::min(zero, std::min(rx.y, ry.y))));
+    context->translate(floorf(-std::min(zero, std::min(rx.x, ry.x))), floorf(-std::min(zero, std::min(rx.y, ry.y))));
 
-    // rotate -ve to remove rotation
-    CGContextRotateCTM(context->platformContext(), -m_rotation);
+    context->rotate(-m_rotation);
 }
 
+bool PDFDocumentImage::cacheParametersMatch(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect) const
+{
+    if (dstRect.size() != m_cachedDestinationSize)
+        return false;
+
+    if (srcRect != m_cachedSourceRect)
+        return false;
+
+    AffineTransform::DecomposedType decomposedTransform;
+    context->getCTM(GraphicsContext::DefinitelyIncludeDeviceScale).decompose(decomposedTransform);
+
+    AffineTransform::DecomposedType cachedDecomposedTransform;
+    m_cachedTransform.decompose(cachedDecomposedTransform);
+    if (decomposedTransform.scaleX != cachedDecomposedTransform.scaleX || decomposedTransform.scaleY != cachedDecomposedTransform.scaleY)
+        return false;
+
+    return true;
+}
+
+static void transformContextForPainting(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect)
+{
+    float hScale = dstRect.width() / srcRect.width();
+    float vScale = dstRect.height() / srcRect.height();
+    context->translate(srcRect.x() * hScale, srcRect.y() * vScale);
+    context->scale(FloatSize(hScale, -vScale));
+    context->translate(0, -srcRect.height());
+}
+
+void PDFDocumentImage::updateCachedImageIfNeeded(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect)
+{
+    // If we have an existing image, reuse it if we're doing a low-quality paint, even if cache parameters don't match;
+    // we'll rerender when we do the subsequent high-quality paint.
+    InterpolationQuality interpolationQuality = context->imageInterpolationQuality();
+    bool useLowQualityInterpolation = interpolationQuality == InterpolationNone || interpolationQuality == InterpolationLow;
+
+    if (!m_cachedImageBuffer || (!cacheParametersMatch(context, dstRect, srcRect) && !useLowQualityInterpolation)) {
+        m_cachedImageBuffer = context->createCompatibleBuffer(enclosingIntRect(dstRect).size());
+        if (!m_cachedImageBuffer)
+            return;
+        GraphicsContext* bufferContext = m_cachedImageBuffer->context();
+        if (!bufferContext) {
+            m_cachedImageBuffer = nullptr;
+            return;
+        }
+
+        transformContextForPainting(bufferContext, dstRect, srcRect);
+        drawPDFPage(bufferContext);
+
+        m_cachedTransform = context->getCTM(GraphicsContext::DefinitelyIncludeDeviceScale);
+        m_cachedDestinationSize = dstRect.size();
+        m_cachedSourceRect = srcRect;
+
+        IntSize internalSize = m_cachedImageBuffer->internalSize();
+        size_t oldCachedBytes = m_cachedBytes;
+        m_cachedBytes = internalSize.width() * internalSize.height() * 4;
+
+        if (imageObserver())
+            imageObserver()->decodedSizeChanged(this, safeCast<int>(m_cachedBytes) - safeCast<int>(oldCachedBytes));
+    }
+}
+
 void PDFDocumentImage::draw(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace, CompositeOperator op, BlendMode)
 {
     if (!m_document || !m_hasPage)
         return;
 
+    updateCachedImageIfNeeded(context, dstRect, srcRect);
+
     {
         GraphicsContextStateSaver stateSaver(*context);
-
         context->setCompositeOperation(op);
 
-        float hScale = dstRect.width() / srcRect.width();
-        float vScale = dstRect.height() / srcRect.height();
-
-        // Scale and translate so the document is rendered in the correct location,
-        // accounting for the fact that the GraphicsContext is flipped.
-        CGContextTranslateCTM(context->platformContext(), dstRect.x() - srcRect.x() * hScale, dstRect.y() - srcRect.y() * vScale);
-        CGContextScaleCTM(context->platformContext(), hScale, vScale);
-        CGContextScaleCTM(context->platformContext(), 1, -1);
-        CGContextTranslateCTM(context->platformContext(), 0, -srcRect.height());
-        CGContextClipToRect(context->platformContext(), CGRectIntegral(srcRect));
-
-        drawPDFPage(context);
+        if (m_cachedImageBuffer)
+            context->drawImageBuffer(m_cachedImageBuffer.get(), ColorSpaceDeviceRGB, dstRect);
+        else {
+            transformContextForPainting(context, dstRect, srcRect);
+            drawPDFPage(context);
+        }
     }
 
     if (imageObserver())
         imageObserver()->didDraw(this);
 }
 
+void PDFDocumentImage::destroyDecodedData(bool)
+{
+    m_cachedImageBuffer = nullptr;
+
+    if (imageObserver())
+        imageObserver()->decodedSizeChanged(this, -safeCast<int>(m_cachedBytes));
+
+    m_cachedBytes = 0;
+}
+
+unsigned PDFDocumentImage::decodedSize() const
+{
+    // FIXME: PDFDocumentImage is underreporting decoded sizes because this
+    // only includes the cached image and nothing else.
+
+    return m_cachedBytes;
+}
+
 #if !USE(PDFKIT_FOR_PDFDOCUMENTIMAGE)
 void PDFDocumentImage::createPDFDocument()
 {
@@ -175,7 +250,7 @@
 {
     applyRotationForPainting(context);
 
-    CGContextTranslateCTM(context->platformContext(), -m_cropBox.x(), -m_cropBox.y());
+    context->translate(-m_cropBox.x(), -m_cropBox.y());
 
     // CGPDF pages are indexed from 1.
     CGContextDrawPDFPage(context->platformContext(), CGPDFDocumentGetPage(m_document.get(), 1));

Modified: branches/safari-537.73-branch/Source/WebCore/platform/graphics/cg/PDFDocumentImage.h (158161 => 158162)


--- branches/safari-537.73-branch/Source/WebCore/platform/graphics/cg/PDFDocumentImage.h	2013-10-29 02:45:26 UTC (rev 158161)
+++ branches/safari-537.73-branch/Source/WebCore/platform/graphics/cg/PDFDocumentImage.h	2013-10-29 02:52:49 UTC (rev 158162)
@@ -26,6 +26,7 @@
 #ifndef PDFDocumentImage_h
 #define PDFDocumentImage_h
 
+#include "AffineTransform.h"
 #include "FloatRect.h"
 #include "GraphicsTypes.h"
 #include "Image.h"
@@ -42,28 +43,29 @@
 namespace WebCore {
 
 class GraphicsContext;
+class ImageBuffer;
 
-class PDFDocumentImage : public Image {
+class PDFDocumentImage FINAL : public Image {
 public:
-    static PassRefPtr<PDFDocumentImage> create()
+    static PassRefPtr<PDFDocumentImage> create(ImageObserver* observer)
     {
-        return adoptRef(new PDFDocumentImage);
+        return adoptRef(new PDFDocumentImage(observer));
     }
 
 private:
-    PDFDocumentImage();
+    PDFDocumentImage(ImageObserver*);
     virtual ~PDFDocumentImage();
 
+    virtual bool isPDFDocumentImage() const OVERRIDE { return true; }
+
     virtual String filenameExtension() const OVERRIDE;
 
     virtual bool hasSingleSecurityOrigin() const OVERRIDE { return true; }
 
     virtual bool dataChanged(bool allDataReceived) OVERRIDE;
 
-    // FIXME: PDF Images are underreporting decoded sizes and will be unable
-    // to prune because these functions are not implemented yet.
-    virtual void destroyDecodedData(bool /*destroyAll*/ = true) OVERRIDE { }
-    virtual unsigned decodedSize() const OVERRIDE { return 0; }
+    virtual void destroyDecodedData(bool /*destroyAll*/ = true) OVERRIDE;
+    virtual unsigned decodedSize() const OVERRIDE;
 
     virtual void computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio) OVERRIDE;
     virtual IntSize size() const OVERRIDE;
@@ -80,12 +82,21 @@
     unsigned pageCount() const;
     void drawPDFPage(GraphicsContext*);
 
+    void updateCachedImageIfNeeded(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect);
+    bool cacheParametersMatch(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect) const;
+
 #if USE(PDFKIT_FOR_PDFDOCUMENTIMAGE)
     RetainPtr<PDFDocument> m_document;
 #else
     RetainPtr<CGPDFDocumentRef> m_document;
 #endif
 
+    OwnPtr<ImageBuffer> m_cachedImageBuffer;
+    AffineTransform m_cachedTransform;
+    FloatSize m_cachedDestinationSize;
+    FloatRect m_cachedSourceRect;
+    size_t m_cachedBytes;
+
     FloatRect m_mediaBox;
     FloatRect m_cropBox;
     float m_rotation;

Modified: branches/safari-537.73-branch/Source/WebCore/rendering/RenderBoxModelObject.cpp (158161 => 158162)


--- branches/safari-537.73-branch/Source/WebCore/rendering/RenderBoxModelObject.cpp	2013-10-29 02:45:26 UTC (rev 158161)
+++ branches/safari-537.73-branch/Source/WebCore/rendering/RenderBoxModelObject.cpp	2013-10-29 02:52:49 UTC (rev 158162)
@@ -159,7 +159,7 @@
 {
     // If the image is not a bitmap image, then none of this is relevant and we just paint at high
     // quality.
-    if (!image || !image->isBitmapImage() || context->paintingDisabled())
+    if (!image || !(image->isBitmapImage() || image->isPDFDocumentImage()) || context->paintingDisabled())
         return false;
 
     switch (object->style()->imageRendering()) {
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to