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/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;