2011-02-03 Dirk Pranke <dpranke@chromium.org>
[WebKit.git] / Source / WebCore / platform / graphics / skia / ImageBufferSkia.cpp
1 /*
2  * Copyright (c) 2008, Google Inc. All rights reserved.
3  * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
4  * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved.
5  * 
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  * 
10  *     * Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *     * Redistributions in binary form must reproduce the above
13  * copyright notice, this list of conditions and the following disclaimer
14  * in the documentation and/or other materials provided with the
15  * distribution.
16  *     * Neither the name of Google Inc. nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  * 
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "config.h"
34 #include "ImageBuffer.h"
35
36 #include "Base64.h"
37 #include "BitmapImage.h"
38 #include "BitmapImageSingleFrameSkia.h"
39 #include "DrawingBuffer.h"
40 #include "GLES2Canvas.h"
41 #include "GraphicsContext.h"
42 #include "ImageData.h"
43 #include "JPEGImageEncoder.h"
44 #include "MIMETypeRegistry.h"
45 #include "PNGImageEncoder.h"
46 #include "PlatformContextSkia.h"
47 #include "SkColorPriv.h"
48 #include "SkiaUtils.h"
49
50 #include <wtf/text/StringConcatenate.h>
51
52 using namespace std;
53
54 namespace WebCore {
55
56 // We pass a technically-uninitialized canvas to the platform context here since
57 // the canvas initialization completes in ImageBuffer::ImageBuffer. But
58 // PlatformContext doesn't actually need to use the object, and this makes all
59 // the ownership easier to manage.
60 ImageBufferData::ImageBufferData(const IntSize& size)
61     : m_platformContext(0)  // Canvas is set in ImageBuffer constructor.
62 {
63 }
64
65 ImageBuffer::ImageBuffer(const IntSize& size, ColorSpace, RenderingMode, bool& success)
66     : m_data(size)
67     , m_size(size)
68 {
69     if (!m_data.m_canvas.initialize(size.width(), size.height(), false)) {
70         success = false;
71         return;
72     }
73
74     m_data.m_platformContext.setCanvas(&m_data.m_canvas);
75     m_context.set(new GraphicsContext(&m_data.m_platformContext));
76     m_context->platformContext()->setDrawingToImageBuffer(true);
77
78     // Make the background transparent. It would be nice if this wasn't
79     // required, but the canvas is currently filled with the magic transparency
80     // color. Can we have another way to manage this?
81     m_data.m_canvas.drawARGB(0, 0, 0, 0, SkXfermode::kClear_Mode);
82     success = true;
83 }
84
85 ImageBuffer::~ImageBuffer()
86 {
87 }
88
89 GraphicsContext* ImageBuffer::context() const
90 {
91     return m_context.get();
92 }
93
94 size_t ImageBuffer::dataSize() const
95 {
96     return m_size.width() * m_size.height() * 4;
97 }
98
99 bool ImageBuffer::drawsUsingCopy() const
100 {
101     return false;
102 }
103
104 PassRefPtr<Image> ImageBuffer::copyImage() const
105 {
106     m_context->platformContext()->syncSoftwareCanvas();
107     return BitmapImageSingleFrameSkia::create(*m_data.m_platformContext.bitmap(), true);
108 }
109
110 void ImageBuffer::clip(GraphicsContext* context, const FloatRect& rect) const
111 {
112     context->platformContext()->beginLayerClippedToImage(rect, this);
113 }
114
115 void ImageBuffer::draw(GraphicsContext* context, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect,
116                        CompositeOperator op, bool useLowQualityScale)
117 {
118     if (m_data.m_platformContext.useGPU() && context->platformContext()->useGPU()) {
119         if (context->platformContext()->canAccelerate()) {
120             DrawingBuffer* sourceDrawingBuffer = m_data.m_platformContext.gpuCanvas()->drawingBuffer();
121             unsigned sourceTexture = static_cast<unsigned>(sourceDrawingBuffer->platformColorBuffer());
122             FloatRect destRectNormalized(normalizeRect(destRect));
123             FloatRect srcRectFlipped(normalizeRect(srcRect));
124             srcRectFlipped.setY(m_size.height() - srcRect.y());
125             srcRectFlipped.setHeight(-srcRect.height());
126             context->platformContext()->prepareForHardwareDraw();
127             context->platformContext()->gpuCanvas()->drawTexturedRect(sourceTexture, m_size, srcRectFlipped, destRectNormalized, styleColorSpace, op);
128             return;
129         }
130         m_data.m_platformContext.syncSoftwareCanvas();
131     }
132
133     RefPtr<Image> image = BitmapImageSingleFrameSkia::create(*m_data.m_platformContext.bitmap(), context == m_context);
134     context->drawImage(image.get(), styleColorSpace, destRect, srcRect, op, useLowQualityScale);
135 }
136
137 void ImageBuffer::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const AffineTransform& patternTransform,
138                               const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect)
139 {
140     RefPtr<Image> image = BitmapImageSingleFrameSkia::create(*m_data.m_platformContext.bitmap(), context == m_context);
141     image->drawPattern(context, srcRect, patternTransform, phase, styleColorSpace, op, destRect);
142 }
143
144 void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable)
145 {
146     const SkBitmap& bitmap = *context()->platformContext()->bitmap();
147     if (bitmap.isNull())
148         return;
149
150     ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config);
151     SkAutoLockPixels bitmapLock(bitmap);
152     for (int y = 0; y < m_size.height(); ++y) {
153         uint32_t* srcRow = bitmap.getAddr32(0, y);
154         for (int x = 0; x < m_size.width(); ++x) {
155             SkColor color = SkPMColorToColor(srcRow[x]);
156             srcRow[x] = SkPreMultiplyARGB(SkColorGetA(color),
157                                           lookUpTable[SkColorGetR(color)],
158                                           lookUpTable[SkColorGetG(color)],
159                                           lookUpTable[SkColorGetB(color)]);
160         }
161     }
162 }
163
164 template <Multiply multiplied>
165 PassRefPtr<ByteArray> getImageData(const IntRect& rect, const SkBitmap& bitmap, 
166                                    const IntSize& size)
167 {
168     RefPtr<ByteArray> result = ByteArray::create(rect.width() * rect.height() * 4);
169
170     if (bitmap.config() == SkBitmap::kNo_Config) {
171         // This is an empty SkBitmap that could not be configured.
172         ASSERT(!size.width() || !size.height());
173         return result.release();
174     }
175
176     unsigned char* data = result->data();
177
178     if (rect.x() < 0
179         || rect.y() < 0
180         || rect.maxX() > size.width()
181         || rect.maxY() > size.height())
182         memset(data, 0, result->length());
183
184     int originX = rect.x();
185     int destX = 0;
186     if (originX < 0) {
187         destX = -originX;
188         originX = 0;
189     }
190     int endX = rect.maxX();
191     if (endX > size.width())
192         endX = size.width();
193     int numColumns = endX - originX;
194
195     if (numColumns <= 0) 
196         return result.release();
197
198     int originY = rect.y();
199     int destY = 0;
200     if (originY < 0) {
201         destY = -originY;
202         originY = 0;
203     }
204     int endY = rect.maxY();
205     if (endY > size.height())
206         endY = size.height();
207     int numRows = endY - originY;
208
209     if (numRows <= 0) 
210         return result.release();
211
212     ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config);
213     SkAutoLockPixels bitmapLock(bitmap);
214
215     unsigned destBytesPerRow = 4 * rect.width();
216     unsigned char* destRow = data + destY * destBytesPerRow + destX * 4;
217
218     for (int y = 0; y < numRows; ++y) {
219         uint32_t* srcRow = bitmap.getAddr32(originX, originY + y);
220         for (int x = 0; x < numColumns; ++x) {
221             unsigned char* destPixel = &destRow[x * 4];
222             if (multiplied == Unmultiplied) {
223                 SkColor color = srcRow[x];
224                 unsigned a = SkColorGetA(color);
225                 destPixel[0] = a ? SkColorGetR(color) * 255 / a : 0;
226                 destPixel[1] = a ? SkColorGetG(color) * 255 / a : 0;
227                 destPixel[2] = a ? SkColorGetB(color) * 255 / a : 0;
228                 destPixel[3] = a;
229             } else {
230                 // Input and output are both pre-multiplied, we just need to re-arrange the
231                 // bytes from the bitmap format to RGBA.
232                 destPixel[0] = SkGetPackedR32(srcRow[x]);
233                 destPixel[1] = SkGetPackedG32(srcRow[x]);
234                 destPixel[2] = SkGetPackedB32(srcRow[x]);
235                 destPixel[3] = SkGetPackedA32(srcRow[x]);
236             }
237         }
238         destRow += destBytesPerRow;
239     }
240
241     return result.release();
242 }
243
244 PassRefPtr<ByteArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect) const
245 {
246     context()->platformContext()->syncSoftwareCanvas();
247     return getImageData<Unmultiplied>(rect, *context()->platformContext()->bitmap(), m_size);
248 }
249
250 PassRefPtr<ByteArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect) const
251 {
252     context()->platformContext()->syncSoftwareCanvas();
253     return getImageData<Premultiplied>(rect, *context()->platformContext()->bitmap(), m_size);
254 }
255
256 template <Multiply multiplied>
257 void putImageData(ByteArray*& source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, 
258                   const SkBitmap& bitmap, const IntSize& size)
259 {
260     ASSERT(sourceRect.width() > 0);
261     ASSERT(sourceRect.height() > 0);
262
263     int originX = sourceRect.x();
264     int destX = destPoint.x() + sourceRect.x();
265     ASSERT(destX >= 0);
266     ASSERT(destX < size.width());
267     ASSERT(originX >= 0);
268     ASSERT(originX < sourceRect.maxX());
269
270     int endX = destPoint.x() + sourceRect.maxX();
271     ASSERT(endX <= size.width());
272
273     int numColumns = endX - destX;
274
275     int originY = sourceRect.y();
276     int destY = destPoint.y() + sourceRect.y();
277     ASSERT(destY >= 0);
278     ASSERT(destY < size.height());
279     ASSERT(originY >= 0);
280     ASSERT(originY < sourceRect.maxY());
281
282     int endY = destPoint.y() + sourceRect.maxY();
283     ASSERT(endY <= size.height());
284     int numRows = endY - destY;
285
286     ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config);
287     SkAutoLockPixels bitmapLock(bitmap);
288
289     unsigned srcBytesPerRow = 4 * sourceSize.width();
290
291     const unsigned char* srcRow = source->data() + originY * srcBytesPerRow + originX * 4;
292
293     for (int y = 0; y < numRows; ++y) {
294         uint32_t* destRow = bitmap.getAddr32(destX, destY + y);
295         for (int x = 0; x < numColumns; ++x) {
296             const unsigned char* srcPixel = &srcRow[x * 4];
297             if (multiplied == Unmultiplied) {
298                 unsigned char alpha = srcPixel[3];
299                 unsigned char r = SkMulDiv255Ceiling(srcPixel[0], alpha);
300                 unsigned char g = SkMulDiv255Ceiling(srcPixel[1], alpha);
301                 unsigned char b = SkMulDiv255Ceiling(srcPixel[2], alpha);
302                 destRow[x] = SkPackARGB32(alpha, r, g, b);
303             } else
304                 destRow[x] = SkPackARGB32(srcPixel[3], srcPixel[0],
305                                           srcPixel[1], srcPixel[2]);
306         }
307         srcRow += srcBytesPerRow;
308     }
309 }
310
311 void ImageBuffer::putUnmultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint)
312 {
313     context()->platformContext()->prepareForSoftwareDraw();
314     putImageData<Unmultiplied>(source, sourceSize, sourceRect, destPoint, *context()->platformContext()->bitmap(), m_size);
315 }
316
317 void ImageBuffer::putPremultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint)
318 {
319     putImageData<Premultiplied>(source, sourceSize, sourceRect, destPoint, *context()->platformContext()->bitmap(), m_size);
320 }
321
322 String ImageBuffer::toDataURL(const String& mimeType, const double* quality) const
323 {
324     ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
325
326     Vector<unsigned char> encodedImage;
327     if (mimeType == "image/jpeg") {
328         int compressionQuality = JPEGImageEncoder::DefaultCompressionQuality;
329         if (quality && *quality >= 0.0 && *quality <= 1.0)
330             compressionQuality = static_cast<int>(*quality * 100 + 0.5);
331         if (!JPEGImageEncoder::encode(*context()->platformContext()->bitmap(), compressionQuality, &encodedImage))
332             return "data:,";
333     } else {
334         if (!PNGImageEncoder::encode(*context()->platformContext()->bitmap(), &encodedImage))
335             return "data:,";
336         ASSERT(mimeType == "image/png");
337     }
338
339     Vector<char> base64Data;
340     base64Encode(*reinterpret_cast<Vector<char>*>(&encodedImage), base64Data);
341
342     return makeString("data:", mimeType, ";base64,", base64Data);
343 }
344
345 } // namespace WebCore