026f4491d028521a58e71ac736a0aafc72265935
[WebKit-https.git] / Source / WebCore / platform / graphics / cairo / ImageBufferCairo.cpp
1 /*
2  * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2007 Holger Hans Peter Freyther <zecke@selfish.org>
4  * Copyright (C) 2008, 2009 Dirk Schulze <krit@webkit.org>
5  * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
20  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "ImageBuffer.h"
31
32 #include "BitmapImage.h"
33 #include "CairoUtilities.h"
34 #include "Color.h"
35 #include "GraphicsContext.h"
36 #include "ImageData.h"
37 #include "MIMETypeRegistry.h"
38 #include "NotImplemented.h"
39 #include "Pattern.h"
40 #include "PlatformContextCairo.h"
41 #include "RefPtrCairo.h"
42 #include <cairo.h>
43 #include <wtf/Vector.h>
44 #include <wtf/text/Base64.h>
45 #include <wtf/text/WTFString.h>
46
47 using namespace std;
48
49 namespace WebCore {
50
51 ImageBufferData::ImageBufferData(const IntSize&)
52     : m_surface(0)
53     , m_platformContext(0)
54 {
55 }
56
57 ImageBuffer::ImageBuffer(const IntSize& size, float /* resolutionScale */, ColorSpace, RenderingMode, DeferralMode, bool& success)
58     : m_data(size)
59     , m_size(size)
60     , m_logicalSize(size)
61 {
62     success = false;  // Make early return mean error.
63     m_data.m_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
64                                                   size.width(),
65                                                   size.height());
66     if (cairo_surface_status(m_data.m_surface) != CAIRO_STATUS_SUCCESS)
67         return;  // create will notice we didn't set m_initialized and fail.
68
69     RefPtr<cairo_t> cr = adoptRef(cairo_create(m_data.m_surface));
70     m_data.m_platformContext.setCr(cr.get());
71     m_context = adoptPtr(new GraphicsContext(&m_data.m_platformContext));
72     success = true;
73 }
74
75 ImageBuffer::~ImageBuffer()
76 {
77     cairo_surface_destroy(m_data.m_surface);
78 }
79
80 GraphicsContext* ImageBuffer::context() const
81 {
82     return m_context.get();
83 }
84
85 PassRefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, ScaleBehavior) const
86 {
87     if (copyBehavior == CopyBackingStore)
88         return BitmapImage::create(copyCairoImageSurface(m_data.m_surface).leakRef());
89
90     // BitmapImage will release the passed in surface on destruction
91     return BitmapImage::create(cairo_surface_reference(m_data.m_surface));
92 }
93
94 void ImageBuffer::clip(GraphicsContext* context, const FloatRect& maskRect) const
95 {
96     context->platformContext()->pushImageMask(m_data.m_surface, maskRect);
97 }
98
99 void ImageBuffer::draw(GraphicsContext* destinationContext, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect,
100                        CompositeOperator op , bool useLowQualityScale)
101 {
102     BackingStoreCopy copyMode = destinationContext == context() ? CopyBackingStore : DontCopyBackingStore;
103     RefPtr<Image> image = copyImage(copyMode);
104     destinationContext->drawImage(image.get(), styleColorSpace, destRect, srcRect, op, DoNotRespectImageOrientation, useLowQualityScale);
105 }
106
107 void ImageBuffer::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const AffineTransform& patternTransform,
108                               const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect)
109 {
110     RefPtr<Image> image = copyImage(DontCopyBackingStore);
111     image->drawPattern(context, srcRect, patternTransform, phase, styleColorSpace, op, destRect);
112 }
113
114 void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable)
115 {
116     ASSERT(cairo_surface_get_type(m_data.m_surface) == CAIRO_SURFACE_TYPE_IMAGE);
117
118     unsigned char* dataSrc = cairo_image_surface_get_data(m_data.m_surface);
119     int stride = cairo_image_surface_get_stride(m_data.m_surface);
120     for (int y = 0; y < m_size.height(); ++y) {
121         unsigned* row = reinterpret_cast<unsigned*>(dataSrc + stride * y);
122         for (int x = 0; x < m_size.width(); x++) {
123             unsigned* pixel = row + x;
124             Color pixelColor = colorFromPremultipliedARGB(*pixel);
125             pixelColor = Color(lookUpTable[pixelColor.red()],
126                                lookUpTable[pixelColor.green()],
127                                lookUpTable[pixelColor.blue()],
128                                pixelColor.alpha());
129             *pixel = premultipliedARGBFromColor(pixelColor);
130         }
131     }
132     cairo_surface_mark_dirty_rectangle (m_data.m_surface, 0, 0, m_size.width(), m_size.height());
133 }
134
135 template <Multiply multiplied>
136 PassRefPtr<Uint8ClampedArray> getImageData(const IntRect& rect, const ImageBufferData& data, const IntSize& size)
137 {
138     ASSERT(cairo_surface_get_type(data.m_surface) == CAIRO_SURFACE_TYPE_IMAGE);
139
140     RefPtr<Uint8ClampedArray> result = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4);
141     unsigned char* dataSrc = cairo_image_surface_get_data(data.m_surface);
142     unsigned char* dataDst = result->data();
143
144     if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > size.width() || (rect.y() + rect.height()) > size.height())
145         result->zeroFill();
146
147     int originx = rect.x();
148     int destx = 0;
149     if (originx < 0) {
150         destx = -originx;
151         originx = 0;
152     }
153     int endx = rect.maxX();
154     if (endx > size.width())
155         endx = size.width();
156     int numColumns = endx - originx;
157
158     int originy = rect.y();
159     int desty = 0;
160     if (originy < 0) {
161         desty = -originy;
162         originy = 0;
163     }
164     int endy = rect.maxY();
165     if (endy > size.height())
166         endy = size.height();
167     int numRows = endy - originy;
168
169     int stride = cairo_image_surface_get_stride(data.m_surface);
170     unsigned destBytesPerRow = 4 * rect.width();
171
172     unsigned char* destRows = dataDst + desty * destBytesPerRow + destx * 4;
173     for (int y = 0; y < numRows; ++y) {
174         unsigned* row = reinterpret_cast<unsigned*>(dataSrc + stride * (y + originy));
175         for (int x = 0; x < numColumns; x++) {
176             int basex = x * 4;
177             unsigned* pixel = row + x + originx;
178
179             // Avoid calling Color::colorFromPremultipliedARGB() because one
180             // function call per pixel is too expensive.
181             unsigned alpha = (*pixel & 0xFF000000) >> 24;
182             unsigned red = (*pixel & 0x00FF0000) >> 16;
183             unsigned green = (*pixel & 0x0000FF00) >> 8;
184             unsigned blue = (*pixel & 0x000000FF);
185
186             if (multiplied == Unmultiplied) {
187                 if (alpha && alpha != 255) {
188                     red = red * 255 / alpha;
189                     green = green * 255 / alpha;
190                     blue = blue * 255 / alpha;
191                 }
192             }
193
194             destRows[basex]     = red;
195             destRows[basex + 1] = green;
196             destRows[basex + 2] = blue;
197             destRows[basex + 3] = alpha;
198         }
199         destRows += destBytesPerRow;
200     }
201
202     return result.release();
203 }
204
205 PassRefPtr<Uint8ClampedArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect, CoordinateSystem) const
206 {
207     return getImageData<Unmultiplied>(rect, m_data, m_size);
208 }
209
210 PassRefPtr<Uint8ClampedArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect, CoordinateSystem) const
211 {
212     return getImageData<Premultiplied>(rect, m_data, m_size);
213 }
214
215 void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, CoordinateSystem)
216 {
217     ASSERT(cairo_surface_get_type(m_data.m_surface) == CAIRO_SURFACE_TYPE_IMAGE);
218
219     unsigned char* dataDst = cairo_image_surface_get_data(m_data.m_surface);
220
221     ASSERT(sourceRect.width() > 0);
222     ASSERT(sourceRect.height() > 0);
223
224     int originx = sourceRect.x();
225     int destx = destPoint.x() + sourceRect.x();
226     ASSERT(destx >= 0);
227     ASSERT(destx < m_size.width());
228     ASSERT(originx >= 0);
229     ASSERT(originx <= sourceRect.maxX());
230
231     int endx = destPoint.x() + sourceRect.maxX();
232     ASSERT(endx <= m_size.width());
233
234     int numColumns = endx - destx;
235
236     int originy = sourceRect.y();
237     int desty = destPoint.y() + sourceRect.y();
238     ASSERT(desty >= 0);
239     ASSERT(desty < m_size.height());
240     ASSERT(originy >= 0);
241     ASSERT(originy <= sourceRect.maxY());
242
243     int endy = destPoint.y() + sourceRect.maxY();
244     ASSERT(endy <= m_size.height());
245     int numRows = endy - desty;
246
247     unsigned srcBytesPerRow = 4 * sourceSize.width();
248     int stride = cairo_image_surface_get_stride(m_data.m_surface);
249
250     unsigned char* srcRows = source->data() + originy * srcBytesPerRow + originx * 4;
251     for (int y = 0; y < numRows; ++y) {
252         unsigned* row = reinterpret_cast<unsigned*>(dataDst + stride * (y + desty));
253         for (int x = 0; x < numColumns; x++) {
254             int basex = x * 4;
255             unsigned* pixel = row + x + destx;
256
257             // Avoid calling Color::premultipliedARGBFromColor() because one
258             // function call per pixel is too expensive.
259             unsigned red = srcRows[basex];
260             unsigned green = srcRows[basex + 1];
261             unsigned blue = srcRows[basex + 2];
262             unsigned alpha = srcRows[basex + 3];
263
264             if (multiplied == Unmultiplied) {
265                 if (alpha && alpha != 255) {
266                     red = (red * alpha + 254) / 255;
267                     green = (green * alpha + 254) / 255;
268                     blue = (blue * alpha + 254) / 255;
269                 }
270             }
271
272             *pixel = (alpha << 24) | red  << 16 | green  << 8 | blue;
273         }
274         srcRows += srcBytesPerRow;
275     }
276     cairo_surface_mark_dirty_rectangle(m_data.m_surface,
277                                         destx, desty,
278                                         numColumns, numRows);
279 }
280
281 #if !PLATFORM(GTK)
282 static cairo_status_t writeFunction(void* output, const unsigned char* data, unsigned int length)
283 {
284     if (!reinterpret_cast<Vector<unsigned char>*>(output)->tryAppend(data, length))
285         return CAIRO_STATUS_WRITE_ERROR;
286     return CAIRO_STATUS_SUCCESS;
287 }
288
289 static bool encodeImage(cairo_surface_t* image, const String& mimeType, Vector<char>* output)
290 {
291     ASSERT_UNUSED(mimeType, mimeType == "image/png"); // Only PNG output is supported for now.
292
293     return cairo_surface_write_to_png_stream(image, writeFunction, output) == CAIRO_STATUS_SUCCESS;
294 }
295
296 String ImageBuffer::toDataURL(const String& mimeType, const double*, CoordinateSystem) const
297 {
298     ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
299
300     cairo_surface_t* image = cairo_get_target(context()->platformContext()->cr());
301
302     Vector<char> encodedImage;
303     if (!image || !encodeImage(image, mimeType, &encodedImage))
304         return "data:,";
305
306     Vector<char> base64Data;
307     base64Encode(encodedImage, base64Data);
308
309     return "data:" + mimeType + ";base64," + base64Data;
310 }
311 #endif
312
313 } // namespace WebCore