Extend platform layer so it can pass blend modes to the compositing calls
[WebKit-https.git] / Source / WebCore / platform / graphics / cg / PDFDocumentImage.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2006 Apple Computer, 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 COMPUTER, 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 COMPUTER, 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 #include "GraphicsContext.h"
33 #include "ImageObserver.h"
34 #include "Length.h"
35 #include "SharedBuffer.h"
36 #include <CoreGraphics/CGContext.h>
37 #include <CoreGraphics/CGPDFDocument.h>
38 #include <wtf/MathExtras.h>
39 #include <wtf/RetainPtr.h>
40
41 #if !PLATFORM(MAC)
42 #include "ImageSourceCG.h"
43 #endif
44
45 using namespace std;
46
47 namespace WebCore {
48
49 PDFDocumentImage::PDFDocumentImage()
50     : Image(0) // PDFs don't animate
51     , m_document(0)
52     , m_rotation(0.0f)
53     , m_currentPage(-1)
54 {
55 }
56
57 PDFDocumentImage::~PDFDocumentImage()
58 {
59     CGPDFDocumentRelease(m_document);
60 }
61
62 String PDFDocumentImage::filenameExtension() const
63 {
64     return "pdf";
65 }
66
67 IntSize PDFDocumentImage::size() const
68 {
69     const float sina = sinf(-m_rotation);
70     const float cosa = cosf(-m_rotation);
71     const float width = m_mediaBox.size().width();
72     const float height = m_mediaBox.size().height();
73     const float rotWidth = width * cosa - height * sina;
74     const float rotHeight = width * sina + height * cosa;
75     
76     return IntSize((int)(fabsf(rotWidth) + 0.5f), (int)(fabsf(rotHeight) + 0.5f));
77 }
78
79 void PDFDocumentImage::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio)
80 {
81     // 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).
82     Image::computeIntrinsicDimensions(intrinsicWidth, intrinsicHeight, intrinsicRatio);
83     intrinsicRatio = FloatSize();
84 }
85
86 bool PDFDocumentImage::dataChanged(bool allDataReceived)
87 {
88     if (allDataReceived && !m_document) {
89 #if PLATFORM(MAC)
90         // On Mac the NSData inside the SharedBuffer can be secretly appended to without the SharedBuffer's knowledge.  We use SharedBuffer's ability
91         // to wrap itself inside CFData to get around this, ensuring that ImageIO is really looking at the SharedBuffer.
92         RetainPtr<CFDataRef> data(AdoptCF, this->data()->createCFData());
93         RetainPtr<CGDataProviderRef> dataProvider(AdoptCF, CGDataProviderCreateWithCFData(data.get()));
94 #else
95         // Create a CGDataProvider to wrap the SharedBuffer.
96         // We use the GetBytesAtPosition callback rather than the GetBytePointer one because SharedBuffer
97         // does not provide a way to lock down the byte pointer and guarantee that it won't move, which
98         // is a requirement for using the GetBytePointer callback.
99         CGDataProviderDirectCallbacks providerCallbacks = { 0, 0, 0, sharedBufferGetBytesAtPosition, 0 };
100         RetainPtr<CGDataProviderRef> dataProvider(AdoptCF, CGDataProviderCreateDirect(this->data(), this->data()->size(), &providerCallbacks));
101 #endif
102         m_document = CGPDFDocumentCreateWithProvider(dataProvider.get());
103         setCurrentPage(0);
104     }
105     return m_document; // return true if size is available
106 }
107
108 void PDFDocumentImage::adjustCTM(GraphicsContext* context) const
109 {
110     // rotate the crop box and calculate bounding box
111     float sina = sinf(-m_rotation);
112     float cosa = cosf(-m_rotation);
113     float width = m_cropBox.width();
114     float height = m_cropBox.height();
115
116     // calculate rotated x and y edges of the corp box. if they're negative, it means part of the image has
117     // been rotated outside of the bounds and we need to shift over the image so it lies inside the bounds again
118     CGPoint rx = CGPointMake(width * cosa, width * sina);
119     CGPoint ry = CGPointMake(-height * sina, height * cosa);
120
121     // adjust so we are at the crop box origin
122     const CGFloat zero = 0;
123     CGContextTranslateCTM(context->platformContext(), floorf(-min(zero, min(rx.x, ry.x))), floorf(-min(zero, min(rx.y, ry.y))));
124
125     // rotate -ve to remove rotation
126     CGContextRotateCTM(context->platformContext(), -m_rotation);
127
128     // shift so we are completely within media box
129     CGContextTranslateCTM(context->platformContext(), m_mediaBox.x() - m_cropBox.x(), m_mediaBox.y() - m_cropBox.y());
130 }
131
132 void PDFDocumentImage::setCurrentPage(int page)
133 {
134     if (!m_document)
135         return;
136
137     if (page == m_currentPage)
138         return;
139
140     if (!(page >= 0 && page < pageCount()))
141         return;
142
143     m_currentPage = page;
144
145     CGPDFPageRef cgPage = CGPDFDocumentGetPage(m_document, page + 1);
146
147     // get media box (guaranteed)
148     m_mediaBox = CGPDFPageGetBoxRect(cgPage, kCGPDFMediaBox);
149
150     // get crop box (not always there). if not, use media box
151     CGRect r = CGPDFPageGetBoxRect(cgPage, kCGPDFCropBox);
152     if (!CGRectIsEmpty(r))
153         m_cropBox = r;
154     else
155         m_cropBox = m_mediaBox;
156
157     // get page rotation angle
158     m_rotation = CGPDFPageGetRotationAngle(cgPage) * piFloat / 180.0f; // to radians
159 }
160
161 int PDFDocumentImage::pageCount() const
162 {
163     return m_document ? CGPDFDocumentGetNumberOfPages(m_document) : 0;
164 }
165
166 void PDFDocumentImage::draw(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace, CompositeOperator op, BlendMode)
167 {
168     if (!m_document || m_currentPage == -1)
169         return;
170
171     {
172         GraphicsContextStateSaver stateSaver(*context);
173
174         context->setCompositeOperation(op);
175
176         float hScale = dstRect.width() / srcRect.width();
177         float vScale = dstRect.height() / srcRect.height();
178
179         // Scale and translate so the document is rendered in the correct location,
180         // including accounting for the fact that a GraphicsContext is always flipped
181         // and doing appropriate flipping.
182         CGContextTranslateCTM(context->platformContext(), dstRect.x() - srcRect.x() * hScale, dstRect.y() - srcRect.y() * vScale);
183         CGContextScaleCTM(context->platformContext(), hScale, vScale);
184         CGContextScaleCTM(context->platformContext(), 1, -1);
185         CGContextTranslateCTM(context->platformContext(), 0, -srcRect.height());
186         CGContextClipToRect(context->platformContext(), CGRectIntegral(srcRect));
187
188         // Rotate translate image into position according to doc properties.
189         adjustCTM(context);
190
191         CGContextTranslateCTM(context->platformContext(), -m_mediaBox.x(), -m_mediaBox.y());
192         CGContextDrawPDFPage(context->platformContext(), CGPDFDocumentGetPage(m_document, m_currentPage + 1));
193     }
194
195     if (imageObserver())
196         imageObserver()->didDraw(this);
197 }
198
199 }
200
201 #endif // USE(CG)