4022fad11e7a643f02ac4a15ea63f2a31593a1be
[WebKit-https.git] / Source / WebCore / platform / graphics / cg / PDFDocumentImage.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2006, 2013 Apple Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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. 
24  */
25
26 #define _USE_MATH_DEFINES 1
27 #include "config.h"
28 #include "PDFDocumentImage.h"
29
30 #if USE(CG)
31
32 #if PLATFORM(IOS)
33 #import <CoreGraphics/CGContextPrivate.h>
34 #import <CoreGraphics/CGContextGState.h>
35 #import <CoreGraphics/CoreGraphics.h>
36 #import <ImageIO/ImageIO.h>
37 #endif
38
39 #include "GraphicsContext.h"
40 #include "ImageBuffer.h"
41 #include "ImageObserver.h"
42 #include "IntRect.h"
43 #include "Length.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>
49
50 #if !PLATFORM(COCOA)
51 #include "ImageSourceCG.h"
52 #endif
53
54 namespace WebCore {
55
56 PDFDocumentImage::PDFDocumentImage(ImageObserver* observer)
57     : Image(observer)
58     , m_cachedBytes(0)
59     , m_rotationDegrees(0)
60     , m_hasPage(false)
61 {
62 }
63
64 PDFDocumentImage::~PDFDocumentImage()
65 {
66 }
67
68 String PDFDocumentImage::filenameExtension() const
69 {
70     return "pdf";
71 }
72
73 FloatSize PDFDocumentImage::size() const
74 {
75     FloatSize expandedCropBoxSize = FloatSize(expandedIntSize(m_cropBox.size()));
76
77     if (m_rotationDegrees == 90 || m_rotationDegrees == 270)
78         return expandedCropBoxSize.transposedSize();
79     return expandedCropBoxSize;
80 }
81
82 void PDFDocumentImage::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio)
83 {
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();
87 }
88
89 bool PDFDocumentImage::dataChanged(bool allDataReceived)
90 {
91     ASSERT(!m_document);
92     if (allDataReceived && !m_document) {
93         createPDFDocument();
94
95         if (pageCount()) {
96             m_hasPage = true;
97             computeBoundsForCurrentPage();
98         }
99     }
100     return m_document; // Return true if size is available.
101 }
102
103 bool PDFDocumentImage::cacheParametersMatch(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect) const
104 {
105     if (dstRect.size() != m_cachedDestinationSize)
106         return false;
107
108     if (srcRect != m_cachedSourceRect)
109         return false;
110
111     AffineTransform::DecomposedType decomposedTransform;
112     context->getCTM(GraphicsContext::DefinitelyIncludeDeviceScale).decompose(decomposedTransform);
113
114     AffineTransform::DecomposedType cachedDecomposedTransform;
115     m_cachedTransform.decompose(cachedDecomposedTransform);
116     if (decomposedTransform.scaleX != cachedDecomposedTransform.scaleX || decomposedTransform.scaleY != cachedDecomposedTransform.scaleY)
117         return false;
118
119     return true;
120 }
121
122 static void transformContextForPainting(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect)
123 {
124     float hScale = dstRect.width() / srcRect.width();
125     float vScale = dstRect.height() / srcRect.height();
126
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());
130
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);
136             vScale = hScale;
137         }
138     }
139
140     context->translate(srcRect.x() * hScale, srcRect.y() * vScale);
141     context->scale(FloatSize(hScale, -vScale));
142     context->translate(0, -srcRect.height());
143 }
144
145 void PDFDocumentImage::updateCachedImageIfNeeded(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect)
146 {
147     // If we have an existing image, reuse it if we're doing a low-quality paint, even if cache parameters don't match;
148     // we'll rerender when we do the subsequent high-quality paint.
149     InterpolationQuality interpolationQuality = context->imageInterpolationQuality();
150     bool useLowQualityInterpolation = interpolationQuality == InterpolationNone || interpolationQuality == InterpolationLow;
151
152     if (!m_cachedImageBuffer || (!cacheParametersMatch(context, dstRect, srcRect) && !useLowQualityInterpolation)) {
153         m_cachedImageBuffer = context->createCompatibleBuffer(FloatRect(enclosingIntRect(dstRect)).size());
154         if (!m_cachedImageBuffer)
155             return;
156         GraphicsContext* bufferContext = m_cachedImageBuffer->context();
157         if (!bufferContext) {
158             m_cachedImageBuffer = nullptr;
159             return;
160         }
161
162         transformContextForPainting(bufferContext, dstRect, srcRect);
163         drawPDFPage(bufferContext);
164
165         m_cachedTransform = context->getCTM(GraphicsContext::DefinitelyIncludeDeviceScale);
166         m_cachedDestinationSize = dstRect.size();
167         m_cachedSourceRect = srcRect;
168
169         IntSize internalSize = m_cachedImageBuffer->internalSize();
170         size_t oldCachedBytes = m_cachedBytes;
171         m_cachedBytes = internalSize.width() * internalSize.height() * 4;
172
173         if (imageObserver())
174             imageObserver()->decodedSizeChanged(this, safeCast<int>(m_cachedBytes) - safeCast<int>(oldCachedBytes));
175     }
176 }
177
178 void PDFDocumentImage::draw(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace, CompositeOperator op, BlendMode, ImageOrientationDescription)
179 {
180     if (!m_document || !m_hasPage)
181         return;
182
183     updateCachedImageIfNeeded(context, dstRect, srcRect);
184
185     {
186         GraphicsContextStateSaver stateSaver(*context);
187         context->setCompositeOperation(op);
188
189         if (m_cachedImageBuffer)
190             context->drawImageBuffer(m_cachedImageBuffer.get(), ColorSpaceDeviceRGB, dstRect);
191         else {
192             transformContextForPainting(context, dstRect, srcRect);
193             drawPDFPage(context);
194         }
195     }
196
197     if (imageObserver())
198         imageObserver()->didDraw(this);
199 }
200
201 void PDFDocumentImage::destroyDecodedData(bool)
202 {
203     m_cachedImageBuffer = nullptr;
204
205     if (imageObserver())
206         imageObserver()->decodedSizeChanged(this, -safeCast<int>(m_cachedBytes));
207
208     m_cachedBytes = 0;
209 }
210
211 #if !USE(PDFKIT_FOR_PDFDOCUMENTIMAGE)
212
213 void PDFDocumentImage::createPDFDocument()
214 {
215     RetainPtr<CGDataProviderRef> dataProvider = adoptCF(CGDataProviderCreateWithCFData(data()->createCFData().get()));
216     m_document = adoptCF(CGPDFDocumentCreateWithProvider(dataProvider.get()));
217 }
218
219 void PDFDocumentImage::computeBoundsForCurrentPage()
220 {
221     CGPDFPageRef cgPage = CGPDFDocumentGetPage(m_document.get(), 1);
222     CGRect mediaBox = CGPDFPageGetBoxRect(cgPage, kCGPDFMediaBox);
223
224     // Get crop box (not always there). If not, use media box.
225     CGRect r = CGPDFPageGetBoxRect(cgPage, kCGPDFCropBox);
226     if (!CGRectIsEmpty(r))
227         m_cropBox = r;
228     else
229         m_cropBox = mediaBox;
230
231     m_rotationDegrees = CGPDFPageGetRotationAngle(cgPage);
232 }
233
234 unsigned PDFDocumentImage::pageCount() const
235 {
236     return CGPDFDocumentGetNumberOfPages(m_document.get());
237 }
238
239 static void applyRotationForPainting(GraphicsContext* context, FloatSize size, int rotationDegrees)
240 {
241     if (rotationDegrees == 90)
242         context->translate(0, size.height());
243     else if (rotationDegrees == 180)
244         context->translate(size.width(), size.height());
245     else if (rotationDegrees == 270)
246         context->translate(size.width(), 0);
247
248     context->rotate(-deg2rad(static_cast<float>(rotationDegrees)));
249 }
250
251 void PDFDocumentImage::drawPDFPage(GraphicsContext* context)
252 {
253     applyRotationForPainting(context, size(), m_rotationDegrees);
254
255     context->translate(-m_cropBox.x(), -m_cropBox.y());
256
257     // CGPDF pages are indexed from 1.
258     CGContextDrawPDFPage(context->platformContext(), CGPDFDocumentGetPage(m_document.get(), 1));
259 }
260
261 #endif // !USE(PDFKIT_FOR_PDFDOCUMENTIMAGE)
262
263 }
264
265 #endif // USE(CG)