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
52using namespace std;
53
54namespace 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.
60ImageBufferData::ImageBufferData(const IntSize& size)
61    : m_platformContext(0)  // Canvas is set in ImageBuffer constructor.
62{
63}
64
65ImageBuffer::ImageBuffer(const IntSize& size, ColorSpace, RenderingMode, bool& success)
66    : m_data(size)
67    , m_size(size)
68{
69    SkCanvas* canvas = skia::CreateBitmapCanvas(size.width(), size.height(), false);
70    if (!canvas) {
71        success = false;
72        return;
73    }
74
75    m_data.m_canvas = canvas;
76    m_data.m_platformContext.setCanvas(m_data.m_canvas.get());
77    m_context.set(new GraphicsContext(&m_data.m_platformContext));
78    m_context->platformContext()->setDrawingToImageBuffer(true);
79
80    // Make the background transparent. It would be nice if this wasn't
81    // required, but the canvas is currently filled with the magic transparency
82    // color. Can we have another way to manage this?
83    m_data.m_canvas->drawARGB(0, 0, 0, 0, SkXfermode::kClear_Mode);
84    success = true;
85}
86
87ImageBuffer::~ImageBuffer()
88{
89}
90
91GraphicsContext* ImageBuffer::context() const
92{
93    return m_context.get();
94}
95
96size_t ImageBuffer::dataSize() const
97{
98    return m_size.width() * m_size.height() * 4;
99}
100
101bool ImageBuffer::drawsUsingCopy() const
102{
103    return false;
104}
105
106PassRefPtr<Image> ImageBuffer::copyImage() const
107{
108    m_context->platformContext()->syncSoftwareCanvas();
109    return BitmapImageSingleFrameSkia::create(*m_data.m_platformContext.bitmap(), true);
110}
111
112void ImageBuffer::clip(GraphicsContext* context, const FloatRect& rect) const
113{
114    context->platformContext()->beginLayerClippedToImage(rect, this);
115}
116
117void ImageBuffer::draw(GraphicsContext* context, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect,
118                       CompositeOperator op, bool useLowQualityScale)
119{
120    if (m_data.m_platformContext.useGPU() && context->platformContext()->useGPU()) {
121        if (context->platformContext()->canAccelerate()) {
122            m_data.m_platformContext.prepareForHardwareDraw();
123            DrawingBuffer* sourceDrawingBuffer = m_data.m_platformContext.gpuCanvas()->drawingBuffer();
124            unsigned sourceTexture = static_cast<unsigned>(sourceDrawingBuffer->platformColorBuffer());
125            FloatRect destRectNormalized(normalizeRect(destRect));
126            FloatRect srcRectFlipped(normalizeRect(srcRect));
127            srcRectFlipped.setY(m_size.height() - srcRect.y());
128            srcRectFlipped.setHeight(-srcRect.height());
129            context->platformContext()->prepareForHardwareDraw();
130            context->platformContext()->gpuCanvas()->drawTexturedRect(sourceTexture, m_size, srcRectFlipped, destRectNormalized, styleColorSpace, op);
131            return;
132        }
133        m_data.m_platformContext.syncSoftwareCanvas();
134    }
135
136    RefPtr<Image> image = BitmapImageSingleFrameSkia::create(*m_data.m_platformContext.bitmap(), context == m_context);
137    context->drawImage(image.get(), styleColorSpace, destRect, srcRect, op, useLowQualityScale);
138}
139
140void ImageBuffer::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const AffineTransform& patternTransform,
141                              const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect)
142{
143    RefPtr<Image> image = BitmapImageSingleFrameSkia::create(*m_data.m_platformContext.bitmap(), context == m_context);
144    image->drawPattern(context, srcRect, patternTransform, phase, styleColorSpace, op, destRect);
145}
146
147void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable)
148{
149    const SkBitmap& bitmap = *context()->platformContext()->bitmap();
150    if (bitmap.isNull())
151        return;
152
153    ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config);
154    SkAutoLockPixels bitmapLock(bitmap);
155    for (int y = 0; y < m_size.height(); ++y) {
156        uint32_t* srcRow = bitmap.getAddr32(0, y);
157        for (int x = 0; x < m_size.width(); ++x) {
158            SkColor color = SkPMColorToColor(srcRow[x]);
159            srcRow[x] = SkPreMultiplyARGB(SkColorGetA(color),
160                                          lookUpTable[SkColorGetR(color)],
161                                          lookUpTable[SkColorGetG(color)],
162                                          lookUpTable[SkColorGetB(color)]);
163        }
164    }
165}
166
167template <Multiply multiplied>
168PassRefPtr<ByteArray> getImageData(const IntRect& rect, SkDevice& srcDevice,
169                                   const IntSize& size)
170{
171    float area = 4.0f * rect.width() * rect.height();
172    if (area > static_cast<float>(std::numeric_limits<int>::max()))
173        return 0;
174
175    RefPtr<ByteArray> result = ByteArray::create(rect.width() * rect.height() * 4);
176
177    SkBitmap::Config srcConfig = srcDevice.accessBitmap(false).config();
178
179    if (srcConfig == SkBitmap::kNo_Config) {
180        // This is an empty SkBitmap that could not be configured.
181        ASSERT(!size.width() || !size.height());
182        return result.release();
183    }
184
185    unsigned char* data = result->data();
186
187    if (rect.x() < 0
188        || rect.y() < 0
189        || rect.maxX() > size.width()
190        || rect.maxY() > size.height())
191        memset(data, 0, result->length());
192
193    int originX = rect.x();
194    int destX = 0;
195    if (originX < 0) {
196        destX = -originX;
197        originX = 0;
198    }
199    int endX = rect.maxX();
200    if (endX > size.width())
201        endX = size.width();
202    int numColumns = endX - originX;
203
204    if (numColumns <= 0)
205        return result.release();
206
207    int originY = rect.y();
208    int destY = 0;
209    if (originY < 0) {
210        destY = -originY;
211        originY = 0;
212    }
213    int endY = rect.maxY();
214    if (endY > size.height())
215        endY = size.height();
216    int numRows = endY - originY;
217
218    if (numRows <= 0)
219        return result.release();
220
221    ASSERT(srcConfig == SkBitmap::kARGB_8888_Config);
222
223    unsigned destBytesPerRow = 4 * rect.width();
224
225    SkBitmap srcBitmap;
226    srcDevice.readPixels(SkIRect::MakeXYWH(originX, originY, numColumns, numRows), &srcBitmap);
227
228    unsigned char* destRow = data + destY * destBytesPerRow + destX * 4;
229
230    // Do conversion of byte order and alpha divide (if necessary)
231    for (int y = 0; y < numRows; ++y) {
232        SkPMColor* srcBitmapRow = srcBitmap.getAddr32(0, y);
233        for (int x = 0; x < numColumns; ++x) {
234            SkPMColor srcPMColor = srcBitmapRow[x];
235            unsigned char* destPixel = &destRow[x * 4];
236            if (multiplied == Unmultiplied) {
237                unsigned char a = SkGetPackedA32(srcPMColor);
238                destPixel[0] = a ? SkGetPackedR32(srcPMColor) * 255 / a : 0;
239                destPixel[1] = a ? SkGetPackedG32(srcPMColor) * 255 / a : 0;
240                destPixel[2] = a ? SkGetPackedB32(srcPMColor) * 255 / a : 0;
241                destPixel[3] = a;
242            } else {
243                // Input and output are both pre-multiplied, we just need to re-arrange the
244                // bytes from the bitmap format to RGBA.
245                destPixel[0] = SkGetPackedR32(srcPMColor);
246                destPixel[1] = SkGetPackedG32(srcPMColor);
247                destPixel[2] = SkGetPackedB32(srcPMColor);
248                destPixel[3] = SkGetPackedA32(srcPMColor);
249            }
250        }
251        destRow += destBytesPerRow;
252    }
253
254    return result.release();
255}
256
257PassRefPtr<ByteArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect) const
258{
259    context()->platformContext()->syncSoftwareCanvas();
260    return getImageData<Unmultiplied>(rect, *context()->platformContext()->canvas()->getDevice(), m_size);
261}
262
263PassRefPtr<ByteArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect) const
264{
265    context()->platformContext()->syncSoftwareCanvas();
266    return getImageData<Premultiplied>(rect, *context()->platformContext()->canvas()->getDevice(), m_size);
267}
268
269template <Multiply multiplied>
270void putImageData(ByteArray*& source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint,
271                  SkDevice* dstDevice, const IntSize& size)
272{
273    ASSERT(sourceRect.width() > 0);
274    ASSERT(sourceRect.height() > 0);
275
276    int originX = sourceRect.x();
277    int destX = destPoint.x() + sourceRect.x();
278    ASSERT(destX >= 0);
279    ASSERT(destX < size.width());
280    ASSERT(originX >= 0);
281    ASSERT(originX < sourceRect.maxX());
282
283    int endX = destPoint.x() + sourceRect.maxX();
284    ASSERT(endX <= size.width());
285
286    int numColumns = endX - destX;
287
288    int originY = sourceRect.y();
289    int destY = destPoint.y() + sourceRect.y();
290    ASSERT(destY >= 0);
291    ASSERT(destY < size.height());
292    ASSERT(originY >= 0);
293    ASSERT(originY < sourceRect.maxY());
294
295    int endY = destPoint.y() + sourceRect.maxY();
296    ASSERT(endY <= size.height());
297    int numRows = endY - destY;
298
299    unsigned srcBytesPerRow = 4 * sourceSize.width();
300
301    SkBitmap deviceBitmap = dstDevice->accessBitmap(true);
302    SkAutoLockPixels deviceAutoLock(deviceBitmap);
303
304    // If the device's bitmap doesn't have pixels we will make a temp and call writePixels on the device.
305    bool temporaryBitmap = !deviceBitmap.getPixels();
306    SkBitmap destBitmap;
307
308    if (temporaryBitmap) {
309        destBitmap.setConfig(SkBitmap::kARGB_8888_Config, numColumns, numRows, srcBytesPerRow);
310        if (!destBitmap.allocPixels())
311            CRASH();
312    } else
313        deviceBitmap.extractSubset(&destBitmap, SkIRect::MakeXYWH(destX, destY, numColumns, numRows));
314
315    // Whether we made a temporary or not destBitmap is always configured to be written at 0,0
316    SkAutoLockPixels destAutoLock(destBitmap);
317    const unsigned char* srcRow = source->data() + originY * srcBytesPerRow + originX * 4;
318    for (int y = 0; y < numRows; ++y) {
319        SkPMColor* destRow = destBitmap.getAddr32(0, y);
320        for (int x = 0; x < numColumns; ++x) {
321            const unsigned char* srcPixel = &srcRow[x * 4];
322            if (multiplied == Unmultiplied) {
323                unsigned char alpha = srcPixel[3];
324                unsigned char r = SkMulDiv255Ceiling(srcPixel[0], alpha);
325                unsigned char g = SkMulDiv255Ceiling(srcPixel[1], alpha);
326                unsigned char b = SkMulDiv255Ceiling(srcPixel[2], alpha);
327                destRow[x] = SkPackARGB32(alpha, r, g, b);
328            } else
329                destRow[x] = SkPackARGB32(srcPixel[3], srcPixel[0], srcPixel[1], srcPixel[2]);
330        }
331        srcRow += srcBytesPerRow;
332    }
333
334    // If we used a temporary then write it to the device
335    if (temporaryBitmap)
336        dstDevice->writePixels(destBitmap, destX, destY);
337}
338
339void ImageBuffer::putUnmultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint)
340{
341    context()->platformContext()->syncSoftwareCanvas();
342    putImageData<Unmultiplied>(source, sourceSize, sourceRect, destPoint, context()->platformContext()->canvas()->getDevice(), m_size);
343}
344
345void ImageBuffer::putPremultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint)
346{
347    context()->platformContext()->syncSoftwareCanvas();
348    putImageData<Premultiplied>(source, sourceSize, sourceRect, destPoint, context()->platformContext()->canvas()->getDevice(), m_size);
349}
350
351template <typename T>
352static String ImageToDataURL(T& source, const String& mimeType, const double* quality)
353{
354    ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
355
356    Vector<unsigned char> encodedImage;
357    if (mimeType == "image/jpeg") {
358        int compressionQuality = JPEGImageEncoder::DefaultCompressionQuality;
359        if (quality && *quality >= 0.0 && *quality <= 1.0)
360            compressionQuality = static_cast<int>(*quality * 100 + 0.5);
361        if (!JPEGImageEncoder::encode(source, compressionQuality, &encodedImage))
362            return "data:,";
363    } else {
364        if (!PNGImageEncoder::encode(source, &encodedImage))
365            return "data:,";
366        ASSERT(mimeType == "image/png");
367    }
368
369    Vector<char> base64Data;
370    base64Encode(*reinterpret_cast<Vector<char>*>(&encodedImage), base64Data);
371
372    return makeString("data:", mimeType, ";base64,", base64Data);
373}
374
375String ImageBuffer::toDataURL(const String& mimeType, const double* quality) const
376{
377    SkDevice* device = context()->platformContext()->canvas()->getDevice();
378    SkBitmap bitmap = device->accessBitmap(false);
379
380    // if we can't see the pixels directly, call readPixels() to get a copy.
381    // this could happen if the device is backed by a GPU.
382    bitmap.lockPixels(); // balanced by our destructor, or explicitly if getPixels() fails
383    if (!bitmap.getPixels()) {
384        bitmap.unlockPixels();
385        SkIRect bounds = SkIRect::MakeWH(device->width(), device->height());
386        if (!device->readPixels(bounds, &bitmap))
387            return "data:,";
388    }
389
390    return ImageToDataURL(bitmap, mimeType, quality);
391}
392
393String ImageDataToDataURL(const ImageData& source, const String& mimeType, const double* quality)
394{
395    return ImageToDataURL(source, mimeType, quality);
396}
397
398} // namespace WebCore
399