2 * Copyright (C) 2004, 2005, 2006, 2013 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #define _USE_MATH_DEFINES 1
28 #include "PDFDocumentImage.h"
33 #import <CoreGraphics/CGContextPrivate.h>
34 #import <CoreGraphics/CGContextGState.h>
35 #import <CoreGraphics/CoreGraphics.h>
36 #import <ImageIO/ImageIO.h>
39 #include "GraphicsContext.h"
40 #include "ImageBuffer.h"
41 #include "ImageObserver.h"
44 #include "SharedBuffer.h"
45 #include <CoreGraphics/CGContext.h>
46 #include <CoreGraphics/CGPDFDocument.h>
47 #include <wtf/MathExtras.h>
48 #include <wtf/RetainPtr.h>
51 #include "ImageSourceCG.h"
56 PDFDocumentImage::PDFDocumentImage(ImageObserver* observer)
59 , m_rotationDegrees(0)
64 PDFDocumentImage::~PDFDocumentImage()
68 String PDFDocumentImage::filenameExtension() const
73 FloatSize PDFDocumentImage::size() const
75 FloatSize expandedCropBoxSize = FloatSize(expandedIntSize(m_cropBox.size()));
77 if (m_rotationDegrees == 90 || m_rotationDegrees == 270)
78 return expandedCropBoxSize.transposedSize();
79 return expandedCropBoxSize;
82 void PDFDocumentImage::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio)
84 // FIXME: If we want size negotiation with PDF documents as-image, this is the place to implement it (https://bugs.webkit.org/show_bug.cgi?id=12095).
85 Image::computeIntrinsicDimensions(intrinsicWidth, intrinsicHeight, intrinsicRatio);
86 intrinsicRatio = FloatSize();
89 bool PDFDocumentImage::dataChanged(bool allDataReceived)
92 if (allDataReceived && !m_document) {
97 computeBoundsForCurrentPage();
100 return m_document; // Return true if size is available.
103 bool PDFDocumentImage::cacheParametersMatch(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect) const
105 if (dstRect.size() != m_cachedDestinationSize)
108 if (srcRect != m_cachedSourceRect)
111 AffineTransform::DecomposedType decomposedTransform;
112 context->getCTM(GraphicsContext::DefinitelyIncludeDeviceScale).decompose(decomposedTransform);
114 AffineTransform::DecomposedType cachedDecomposedTransform;
115 m_cachedTransform.decompose(cachedDecomposedTransform);
116 if (decomposedTransform.scaleX != cachedDecomposedTransform.scaleX || decomposedTransform.scaleY != cachedDecomposedTransform.scaleY)
122 static void transformContextForPainting(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect)
124 float hScale = dstRect.width() / srcRect.width();
125 float vScale = dstRect.height() / srcRect.height();
127 if (hScale != vScale) {
128 float minimumScale = std::max((dstRect.width() - 0.5) / srcRect.width(), (dstRect.height() - 0.5) / srcRect.height());
129 float maximumScale = std::min((dstRect.width() + 0.5) / srcRect.width(), (dstRect.height() + 0.5) / srcRect.height());
131 // If the difference between the two scales is due to integer rounding of image sizes,
132 // use the smaller of the two original scales to ensure that the image fits inside the
133 // space originally allocated for it.
134 if (minimumScale <= maximumScale) {
135 hScale = std::min(hScale, vScale);
140 context->translate(srcRect.x() * hScale, srcRect.y() * vScale);
141 context->scale(FloatSize(hScale, -vScale));
142 context->translate(0, -srcRect.height());
145 void PDFDocumentImage::updateCachedImageIfNeeded(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect)
148 // On iOS, some clients use low-quality image interpolation always, which throws off this optimization,
149 // as we never get the subsequent high-quality paint. Since live resize is rare on iOS, disable the optimization.
150 // FIXME (136593): It's also possible to do the wrong thing here if CSS specifies low-quality interpolation via the "image-rendering"
151 // property, on all platforms. We should only do this optimization if we're actually in a ImageQualityController live resize,
152 // and are guaranteed to do a high-quality paint later.
153 bool repaintIfNecessary = true;
155 // If we have an existing image, reuse it if we're doing a low-quality paint, even if cache parameters don't match;
156 // we'll rerender when we do the subsequent high-quality paint.
157 InterpolationQuality interpolationQuality = context->imageInterpolationQuality();
158 bool repaintIfNecessary = interpolationQuality != InterpolationNone && interpolationQuality != InterpolationLow;
161 if (!m_cachedImageBuffer || (!cacheParametersMatch(context, dstRect, srcRect) && repaintIfNecessary)) {
162 m_cachedImageBuffer = context->createCompatibleBuffer(FloatRect(enclosingIntRect(dstRect)).size());
163 if (!m_cachedImageBuffer)
165 GraphicsContext* bufferContext = m_cachedImageBuffer->context();
166 if (!bufferContext) {
167 m_cachedImageBuffer = nullptr;
171 transformContextForPainting(bufferContext, dstRect, srcRect);
172 drawPDFPage(bufferContext);
174 m_cachedTransform = context->getCTM(GraphicsContext::DefinitelyIncludeDeviceScale);
175 m_cachedDestinationSize = dstRect.size();
176 m_cachedSourceRect = srcRect;
178 IntSize internalSize = m_cachedImageBuffer->internalSize();
179 size_t oldCachedBytes = m_cachedBytes;
180 m_cachedBytes = internalSize.width() * internalSize.height() * 4;
183 imageObserver()->decodedSizeChanged(this, safeCast<int>(m_cachedBytes) - safeCast<int>(oldCachedBytes));
187 void PDFDocumentImage::draw(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace, CompositeOperator op, BlendMode, ImageOrientationDescription)
189 if (!m_document || !m_hasPage)
192 updateCachedImageIfNeeded(context, dstRect, srcRect);
195 GraphicsContextStateSaver stateSaver(*context);
196 context->setCompositeOperation(op);
198 if (m_cachedImageBuffer)
199 context->drawImageBuffer(m_cachedImageBuffer.get(), ColorSpaceDeviceRGB, dstRect);
201 transformContextForPainting(context, dstRect, srcRect);
202 drawPDFPage(context);
207 imageObserver()->didDraw(this);
210 void PDFDocumentImage::destroyDecodedData(bool)
212 m_cachedImageBuffer = nullptr;
215 imageObserver()->decodedSizeChanged(this, -safeCast<int>(m_cachedBytes));
220 #if !USE(PDFKIT_FOR_PDFDOCUMENTIMAGE)
222 void PDFDocumentImage::createPDFDocument()
224 RetainPtr<CGDataProviderRef> dataProvider = adoptCF(CGDataProviderCreateWithCFData(data()->createCFData().get()));
225 m_document = adoptCF(CGPDFDocumentCreateWithProvider(dataProvider.get()));
228 void PDFDocumentImage::computeBoundsForCurrentPage()
230 CGPDFPageRef cgPage = CGPDFDocumentGetPage(m_document.get(), 1);
231 CGRect mediaBox = CGPDFPageGetBoxRect(cgPage, kCGPDFMediaBox);
233 // Get crop box (not always there). If not, use media box.
234 CGRect r = CGPDFPageGetBoxRect(cgPage, kCGPDFCropBox);
235 if (!CGRectIsEmpty(r))
238 m_cropBox = mediaBox;
240 m_rotationDegrees = CGPDFPageGetRotationAngle(cgPage);
243 unsigned PDFDocumentImage::pageCount() const
245 return CGPDFDocumentGetNumberOfPages(m_document.get());
248 static void applyRotationForPainting(GraphicsContext* context, FloatSize size, int rotationDegrees)
250 if (rotationDegrees == 90)
251 context->translate(0, size.height());
252 else if (rotationDegrees == 180)
253 context->translate(size.width(), size.height());
254 else if (rotationDegrees == 270)
255 context->translate(size.width(), 0);
257 context->rotate(-deg2rad(static_cast<float>(rotationDegrees)));
260 void PDFDocumentImage::drawPDFPage(GraphicsContext* context)
262 applyRotationForPainting(context, size(), m_rotationDegrees);
264 context->translate(-m_cropBox.x(), -m_cropBox.y());
266 // CGPDF pages are indexed from 1.
267 CGContextDrawPDFPage(context->platformContext(), CGPDFDocumentGetPage(m_document.get(), 1));
270 #endif // !USE(PDFKIT_FOR_PDFDOCUMENTIMAGE)