2009-06-17 Brent Fulgham <bfulgham@webkit.org>
[WebKit-https.git] / WebCore / platform / graphics / cairo / ImageCairo.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.  All rights reserved.
3  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
4  * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include "config.h"
29 #include "BitmapImage.h"
30
31 #if PLATFORM(CAIRO)
32
33 #include "Color.h"
34 #include "FloatRect.h"
35 #include "GraphicsContext.h"
36 #include "ImageBuffer.h"
37 #include "ImageObserver.h"
38 #include "TransformationMatrix.h"
39 #include <cairo.h>
40 #include <math.h>
41 #include <wtf/OwnPtr.h>
42
43 namespace WebCore {
44
45 bool FrameData::clear(bool clearMetadata)
46 {
47     if (clearMetadata)
48         m_haveMetadata = false;
49
50     if (m_frame) {
51         cairo_surface_destroy(m_frame);
52         m_frame = 0;
53         return true;
54     }
55     return false;
56 }
57
58 BitmapImage::BitmapImage(cairo_surface_t* surface, ImageObserver* observer)
59     : Image(observer)
60     , m_currentFrame(0)
61     , m_frames(0)
62     , m_frameTimer(0)
63     , m_repetitionCount(cAnimationNone)
64     , m_repetitionCountStatus(Unknown)
65     , m_repetitionsComplete(0)
66     , m_isSolidColor(false)
67     , m_checkedForSolidColor(false)
68     , m_animationFinished(true)
69     , m_allDataReceived(true)
70     , m_haveSize(true)
71     , m_sizeAvailable(true)
72     , m_decodedSize(0)
73     , m_haveFrameCount(true)
74     , m_frameCount(1)
75 {
76     initPlatformData();
77
78     // TODO: check to be sure this is an image surface
79
80     int width = cairo_image_surface_get_width(surface);
81     int height = cairo_image_surface_get_height(surface);
82     m_decodedSize = width * height * 4;
83     m_size = IntSize(width, height);
84
85     m_frames.grow(1);
86     m_frames[0].m_frame = surface;
87     m_frames[0].m_hasAlpha = cairo_surface_get_content(surface) != CAIRO_CONTENT_COLOR;
88     m_frames[0].m_haveMetadata = true;
89     checkForSolidColor();
90 }
91
92 void BitmapImage::draw(GraphicsContext* context, const FloatRect& dst, const FloatRect& src, CompositeOperator op)
93 {
94     FloatRect srcRect(src);
95     FloatRect dstRect(dst);
96
97     if (dstRect.width() == 0.0f || dstRect.height() == 0.0f ||
98         srcRect.width() == 0.0f || srcRect.height() == 0.0f)
99         return;
100
101     startAnimation();
102
103     cairo_surface_t* image = frameAtIndex(m_currentFrame);
104     if (!image) // If it's too early we won't have an image yet.
105         return;
106
107     if (mayFillWithSolidColor()) {
108         fillWithSolidColor(context, dstRect, solidColor(), op);
109         return;
110     }
111
112     IntSize selfSize = size();
113
114     cairo_t* cr = context->platformContext();
115     context->save();
116
117     // Set the compositing operation.
118     if (op == CompositeSourceOver && !frameHasAlphaAtIndex(m_currentFrame))
119         context->setCompositeOperation(CompositeCopy);
120     else
121         context->setCompositeOperation(op);
122
123     // If we're drawing a sub portion of the image or scaling then create
124     // a pattern transformation on the image and draw the transformed pattern.
125     // Test using example site at http://www.meyerweb.com/eric/css/edge/complexspiral/demo.html
126     cairo_pattern_t* pattern = cairo_pattern_create_for_surface(image);
127
128     // To avoid the unwanted gradient effect (#14017) we use
129     // CAIRO_FILTER_NEAREST now, but the real fix will be to have
130     // CAIRO_EXTEND_PAD implemented for surfaces in Cairo allowing us to still
131     // use bilinear filtering
132     cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST);
133
134     float scaleX = srcRect.width() / dstRect.width();
135     float scaleY = srcRect.height() / dstRect.height();
136     cairo_matrix_t matrix = { scaleX, 0, 0, scaleY, srcRect.x(), srcRect.y() };
137     cairo_pattern_set_matrix(pattern, &matrix);
138
139     // Draw the image.
140     cairo_translate(cr, dstRect.x(), dstRect.y());
141     cairo_set_source(cr, pattern);
142     cairo_pattern_destroy(pattern);
143     cairo_rectangle(cr, 0, 0, dstRect.width(), dstRect.height());
144     cairo_clip(cr);
145     cairo_paint_with_alpha(cr, context->getAlpha());
146
147     context->restore();
148
149     if (imageObserver())
150         imageObserver()->didDraw(this);
151 }
152
153 void Image::drawPattern(GraphicsContext* context, const FloatRect& tileRect, const TransformationMatrix& patternTransform,
154                         const FloatPoint& phase, CompositeOperator op, const FloatRect& destRect)
155 {
156     cairo_surface_t* image = nativeImageForCurrentFrame();
157     if (!image) // If it's too early we won't have an image yet.
158         return;
159
160     // Avoid NaN
161     if (!isfinite(phase.x()) || !isfinite(phase.y()))
162        return;
163
164     cairo_t* cr = context->platformContext();
165     context->save();
166
167     IntRect imageSize = enclosingIntRect(tileRect);
168     OwnPtr<ImageBuffer> imageSurface = ImageBuffer::create(imageSize.size(), false);
169
170     if (!imageSurface)
171         return;
172
173     if (tileRect.size() != size()) {
174         cairo_t* clippedImageContext = imageSurface->context()->platformContext();
175         cairo_set_source_surface(clippedImageContext, image, -tileRect.x(), -tileRect.y());
176         cairo_paint(clippedImageContext);
177         image = imageSurface->image()->nativeImageForCurrentFrame();
178     }
179
180     cairo_pattern_t* pattern = cairo_pattern_create_for_surface(image);
181     cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
182
183     // Workaround to avoid the unwanted gradient effect (#14017)
184     cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST);
185
186     cairo_matrix_t pattern_matrix = cairo_matrix_t(patternTransform);
187     cairo_matrix_t phase_matrix = {1, 0, 0, 1, phase.x() + tileRect.x() * patternTransform.a(), phase.y() + tileRect.y() * patternTransform.d()};
188     cairo_matrix_t combined;
189     cairo_matrix_multiply(&combined, &pattern_matrix, &phase_matrix);
190     cairo_matrix_invert(&combined);
191     cairo_pattern_set_matrix(pattern, &combined);
192
193     context->setCompositeOperation(op);
194     cairo_set_source(cr, pattern);
195     cairo_pattern_destroy(pattern);
196     cairo_rectangle(cr, destRect.x(), destRect.y(), destRect.width(), destRect.height());
197     cairo_fill(cr);
198
199     context->restore();
200
201     if (imageObserver())
202         imageObserver()->didDraw(this);
203 }
204
205 void BitmapImage::checkForSolidColor()
206 {
207     m_isSolidColor = false;
208     m_checkedForSolidColor = true;
209
210     if (frameCount() > 1)
211         return;
212
213     cairo_surface_t* frameSurface = frameAtIndex(0);
214     if (!frameSurface)
215         return;
216
217     ASSERT(cairo_surface_get_type(frameSurface) == CAIRO_SURFACE_TYPE_IMAGE);
218
219     int width = cairo_image_surface_get_width(frameSurface);
220     int height = cairo_image_surface_get_height(frameSurface);
221
222     if (width != 1 || height != 1)
223         return;
224
225     unsigned* pixelColor = reinterpret_cast<unsigned*>(cairo_image_surface_get_data(frameSurface));
226     m_solidColor = colorFromPremultipliedARGB(*pixelColor);
227
228     m_isSolidColor = true;
229 }
230
231 }
232
233 #endif // PLATFORM(CAIRO)