2011-03-15 John Bauman <jbauman@chromium.org>
[WebKit-https.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, SkDevice& srcDevice,
166                                    const IntSize& size)
167 {
168     RefPtr<ByteArray> result = ByteArray::create(rect.width() * rect.height() * 4);
169
170     SkBitmap::Config srcConfig = srcDevice.accessBitmap(false).config();
171
172     if (srcConfig == SkBitmap::kNo_Config) {
173         // This is an empty SkBitmap that could not be configured.
174         ASSERT(!size.width() || !size.height());
175         return result.release();
176     }
177
178     unsigned char* data = result->data();
179
180     if (rect.x() < 0
181         || rect.y() < 0
182         || rect.maxX() > size.width()
183         || rect.maxY() > size.height())
184         memset(data, 0, result->length());
185
186     int originX = rect.x();
187     int destX = 0;
188     if (originX < 0) {
189         destX = -originX;
190         originX = 0;
191     }
192     int endX = rect.maxX();
193     if (endX > size.width())
194         endX = size.width();
195     int numColumns = endX - originX;
196
197     if (numColumns <= 0)
198         return result.release();
199
200     int originY = rect.y();
201     int destY = 0;
202     if (originY < 0) {
203         destY = -originY;
204         originY = 0;
205     }
206     int endY = rect.maxY();
207     if (endY > size.height())
208         endY = size.height();
209     int numRows = endY - originY;
210
211     if (numRows <= 0)
212         return result.release();
213
214     ASSERT(srcConfig == SkBitmap::kARGB_8888_Config);
215
216     unsigned destBytesPerRow = 4 * rect.width();
217
218     SkBitmap srcBitmap;
219     srcDevice.readPixels(SkIRect::MakeXYWH(originX, originY, numColumns, numRows), &srcBitmap);
220
221     unsigned char* destRow = data + destY * destBytesPerRow + destX * 4;
222
223     // Do conversion of byte order and alpha divide (if necessary)
224     for (int y = 0; y < numRows; ++y) {
225         SkPMColor* srcBitmapRow = srcBitmap.getAddr32(0, y);
226         for (int x = 0; x < numColumns; ++x) {
227             SkPMColor srcPMColor = srcBitmapRow[x];
228             unsigned char* destPixel = &destRow[x * 4];
229             if (multiplied == Unmultiplied) {
230                 unsigned char a = SkGetPackedA32(srcPMColor);
231                 destPixel[0] = a ? SkGetPackedR32(srcPMColor) * 255 / a : 0;
232                 destPixel[1] = a ? SkGetPackedG32(srcPMColor) * 255 / a : 0;
233                 destPixel[2] = a ? SkGetPackedB32(srcPMColor) * 255 / a : 0;
234                 destPixel[3] = a;
235             } else {
236                 // Input and output are both pre-multiplied, we just need to re-arrange the
237                 // bytes from the bitmap format to RGBA.
238                 destPixel[0] = SkGetPackedR32(srcPMColor);
239                 destPixel[1] = SkGetPackedG32(srcPMColor);
240                 destPixel[2] = SkGetPackedB32(srcPMColor);
241                 destPixel[3] = SkGetPackedA32(srcPMColor);
242             }
243         }
244         destRow += destBytesPerRow;
245     }
246
247     return result.release();
248 }
249
250 PassRefPtr<ByteArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect) const
251 {
252     context()->platformContext()->syncSoftwareCanvas();
253     return getImageData<Unmultiplied>(rect, *context()->platformContext()->canvas()->getDevice(), m_size);
254 }
255
256 PassRefPtr<ByteArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect) const
257 {
258     context()->platformContext()->syncSoftwareCanvas();
259     return getImageData<Premultiplied>(rect, *context()->platformContext()->canvas()->getDevice(), m_size);
260 }
261
262 template <Multiply multiplied>
263 void putImageData(ByteArray*& source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint,
264                   SkDevice* dstDevice, const IntSize& size)
265 {
266     ASSERT(sourceRect.width() > 0);
267     ASSERT(sourceRect.height() > 0);
268
269     int originX = sourceRect.x();
270     int destX = destPoint.x() + sourceRect.x();
271     ASSERT(destX >= 0);
272     ASSERT(destX < size.width());
273     ASSERT(originX >= 0);
274     ASSERT(originX < sourceRect.maxX());
275
276     int endX = destPoint.x() + sourceRect.maxX();
277     ASSERT(endX <= size.width());
278
279     int numColumns = endX - destX;
280
281     int originY = sourceRect.y();
282     int destY = destPoint.y() + sourceRect.y();
283     ASSERT(destY >= 0);
284     ASSERT(destY < size.height());
285     ASSERT(originY >= 0);
286     ASSERT(originY < sourceRect.maxY());
287
288     int endY = destPoint.y() + sourceRect.maxY();
289     ASSERT(endY <= size.height());
290     int numRows = endY - destY;
291
292     unsigned srcBytesPerRow = 4 * sourceSize.width();
293
294     SkBitmap deviceBitmap = dstDevice->accessBitmap(true);
295     SkAutoLockPixels deviceAutoLock(deviceBitmap);
296
297     // If the device's bitmap doesn't have pixels we will make a temp and call writePixels on the device.
298     bool temporaryBitmap = !deviceBitmap.getPixels();
299     SkBitmap destBitmap;
300
301     if (temporaryBitmap) {
302         destBitmap.setConfig(SkBitmap::kARGB_8888_Config, numColumns, numRows, srcBytesPerRow);
303         if (!destBitmap.allocPixels())
304             CRASH();
305     } else
306         deviceBitmap.extractSubset(&destBitmap, SkIRect::MakeXYWH(destX, destY, numColumns, numRows));
307
308     // Whether we made a temporary or not destBitmap is always configured to be written at 0,0
309     SkAutoLockPixels destAutoLock(destBitmap);
310     const unsigned char* srcRow = source->data() + originY * srcBytesPerRow + originX * 4;
311     for (int y = 0; y < numRows; ++y) {
312         SkPMColor* destRow = destBitmap.getAddr32(0, y);
313         for (int x = 0; x < numColumns; ++x) {
314             const unsigned char* srcPixel = &srcRow[x * 4];
315             if (multiplied == Unmultiplied) {
316                 unsigned char alpha = srcPixel[3];
317                 unsigned char r = SkMulDiv255Ceiling(srcPixel[0], alpha);
318                 unsigned char g = SkMulDiv255Ceiling(srcPixel[1], alpha);
319                 unsigned char b = SkMulDiv255Ceiling(srcPixel[2], alpha);
320                 destRow[x] = SkPackARGB32(alpha, r, g, b);
321             } else
322                 destRow[x] = SkPackARGB32(srcPixel[3], srcPixel[0], srcPixel[1], srcPixel[2]);
323         }
324         srcRow += srcBytesPerRow;
325     }
326
327     // If we used a temporary then write it to the device
328     if (temporaryBitmap)
329         dstDevice->writePixels(destBitmap, destX, destY);
330 }
331
332 void ImageBuffer::putUnmultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint)
333 {
334     context()->platformContext()->syncSoftwareCanvas();
335     putImageData<Unmultiplied>(source, sourceSize, sourceRect, destPoint, context()->platformContext()->canvas()->getDevice(), m_size);
336 }
337
338 void ImageBuffer::putPremultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint)
339 {
340     context()->platformContext()->syncSoftwareCanvas();
341     putImageData<Premultiplied>(source, sourceSize, sourceRect, destPoint, context()->platformContext()->canvas()->getDevice(), m_size);
342 }
343
344 template <typename T>
345 static String ImageToDataURL(T& source, const String& mimeType, const double* quality)
346 {
347     Vector<unsigned char> encodedImage;
348     if (mimeType == "image/jpeg") {
349         int compressionQuality = JPEGImageEncoder::DefaultCompressionQuality;
350         if (quality && *quality >= 0.0 && *quality <= 1.0)
351             compressionQuality = static_cast<int>(*quality * 100 + 0.5);
352         if (!JPEGImageEncoder::encode(source, compressionQuality, &encodedImage))
353             return "data:,";
354     } else {
355         if (!PNGImageEncoder::encode(source, &encodedImage))
356             return "data:,";
357         ASSERT(mimeType == "image/png");
358     }
359
360     Vector<char> base64Data;
361     base64Encode(*reinterpret_cast<Vector<char>*>(&encodedImage), base64Data);
362
363     return makeString("data:", mimeType, ";base64,", base64Data);
364 }
365
366 String ImageBuffer::toDataURL(const String& mimeType, const double* quality) const
367 {
368     ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
369
370     Vector<unsigned char> encodedImage;
371     SkDevice* device = context()->platformContext()->canvas()->getDevice();
372     SkBitmap bitmap = device->accessBitmap(false);
373
374     // if we can't see the pixels directly, call readPixels() to get a copy.
375     // this could happen if the device is backed by a GPU.
376     bitmap.lockPixels(); // balanced by our destructor, or explicitly if getPixels() fails
377     if (!bitmap.getPixels()) {
378         bitmap.unlockPixels();
379         SkIRect bounds = SkIRect::MakeWH(device->width(), device->height());
380         if (!device->readPixels(bounds, &bitmap))
381             return "data:,";
382     }
383
384     return ImageToDataURL(bitmap, mimeType, quality);
385 }
386
387 String ImageDataToDataURL(const ImageData& source, const String& mimeType, const double* quality)
388 {
389     return ImageToDataURL(source, mimeType, quality);
390 }
391
392 } // namespace WebCore