1/*
2 * Copyright 2007, The Android Open Source Project
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *  * Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 *  * Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "ImageBuffer.h"
28
29#include "Base64.h"
30#include "BitmapImage.h"
31#include "ColorSpace.h"
32#include "GraphicsContext.h"
33#include "NotImplemented.h"
34#include "PlatformBridge.h"
35#include "PlatformGraphicsContext.h"
36#include "PlatformGraphicsContextSkia.h"
37#include "SkBitmapRef.h"
38#include "SkCanvas.h"
39#include "SkColorPriv.h"
40#include "SkData.h"
41#include "SkDevice.h"
42#include "SkImageEncoder.h"
43#include "SkStream.h"
44#include "SkUnPreMultiply.h"
45
46using namespace std;
47
48namespace WebCore {
49
50SkCanvas* imageBufferCanvas(const ImageBuffer* buffer)
51{
52    // We know that our PlatformGraphicsContext is a PlatformGraphicsContextSkia
53    // because that is what we create in GraphicsContext::createOffscreenContext
54    if (!buffer || !buffer->context())
55        return 0;
56    PlatformGraphicsContext* pc = buffer->context()->platformContext();
57    return static_cast<PlatformGraphicsContextSkia*>(pc)->canvas();
58}
59
60ImageBufferData::ImageBufferData(const IntSize&)
61{
62}
63
64ImageBuffer::ImageBuffer(const IntSize& size, ColorSpace, RenderingMode, bool& success)
65    : m_data(size)
66    , m_size(size)
67{
68    // GraphicsContext creates a 32bpp SkBitmap, so 4 bytes per pixel.
69    if (!PlatformBridge::canSatisfyMemoryAllocation(size.width() * size.height() * 4))
70        success = false;
71    else {
72        m_context.set(GraphicsContext::createOffscreenContext(size.width(), size.height()));
73        success = true;
74    }
75}
76
77ImageBuffer::~ImageBuffer()
78{
79}
80
81GraphicsContext* ImageBuffer::context() const
82{
83    return m_context.get();
84}
85
86bool ImageBuffer::drawsUsingCopy() const
87{
88    return true;
89}
90
91PassRefPtr<Image> ImageBuffer::copyImage() const
92{
93    ASSERT(context());
94
95    SkCanvas* canvas = imageBufferCanvas(this);
96    if (!canvas)
97      return 0;
98
99    SkDevice* device = canvas->getDevice();
100    const SkBitmap& orig = device->accessBitmap(false);
101
102    SkBitmap copy;
103    if (PlatformBridge::canSatisfyMemoryAllocation(orig.getSize()))
104        orig.copyTo(&copy, orig.config());
105
106    SkBitmapRef* ref = new SkBitmapRef(copy);
107    RefPtr<Image> image = BitmapImage::create(ref, 0);
108    ref->unref();
109    return image;
110}
111
112void ImageBuffer::clip(GraphicsContext* context, const FloatRect& rect) const
113{
114    SkDebugf("xxxxxxxxxxxxxxxxxx clip not implemented\n");
115}
116
117void ImageBuffer::draw(GraphicsContext* context, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, bool useLowQualityScale)
118{
119    RefPtr<Image> imageCopy = copyImage();
120    context->drawImage(imageCopy.get(), styleColorSpace, destRect, srcRect, op, useLowQualityScale);
121}
122
123void ImageBuffer::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect)
124{
125    RefPtr<Image> imageCopy = copyImage();
126    imageCopy->drawPattern(context, srcRect, patternTransform, phase, styleColorSpace, op, destRect);
127}
128
129PassRefPtr<ByteArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect) const
130{
131    GraphicsContext* gc = this->context();
132    if (!gc) {
133        return 0;
134    }
135
136    const SkBitmap& src = imageBufferCanvas(this)->getDevice()->accessBitmap(false);
137    SkAutoLockPixels alp(src);
138    if (!src.getPixels()) {
139        return 0;
140    }
141
142    RefPtr<ByteArray> result = ByteArray::create(rect.width() * rect.height() * 4);
143    unsigned char* data = result->data();
144
145    if (rect.x() < 0 || rect.y() < 0 || rect.maxX() > m_size.width() || rect.maxY() > m_size.height())
146        memset(data, 0, result->length());
147
148    int originx = rect.x();
149    int destx = 0;
150    if (originx < 0) {
151        destx = -originx;
152        originx = 0;
153    }
154    int endx = rect.x() + rect.width();
155    if (endx > m_size.width())
156        endx = m_size.width();
157    int numColumns = endx - originx;
158
159    int originy = rect.y();
160    int desty = 0;
161    if (originy < 0) {
162        desty = -originy;
163        originy = 0;
164    }
165    int endy = rect.y() + rect.height();
166    if (endy > m_size.height())
167        endy = m_size.height();
168    int numRows = endy - originy;
169
170    unsigned srcPixelsPerRow = src.rowBytesAsPixels();
171    unsigned destBytesPerRow = 4 * rect.width();
172
173    const SkPMColor* srcRows = src.getAddr32(originx, originy);
174    unsigned char* destRows = data + desty * destBytesPerRow + destx * 4;
175    for (int y = 0; y < numRows; ++y) {
176        for (int x = 0; x < numColumns; x++) {
177            // ugh, it appears they want unpremultiplied pixels
178            SkColor c = SkUnPreMultiply::PMColorToColor(srcRows[x]);
179            int basex = x * 4;
180            destRows[basex + 0] = SkColorGetR(c);
181            destRows[basex + 1] = SkColorGetG(c);
182            destRows[basex + 2] = SkColorGetB(c);
183            destRows[basex + 3] = SkColorGetA(c);
184        }
185        srcRows += srcPixelsPerRow;
186        destRows += destBytesPerRow;
187    }
188    return result.release();
189}
190
191void ImageBuffer::putUnmultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint)
192{
193    GraphicsContext* gc = this->context();
194    if (!gc) {
195        return;
196    }
197
198    const SkBitmap& dst = imageBufferCanvas(this)->getDevice()->accessBitmap(true);
199    SkAutoLockPixels alp(dst);
200    if (!dst.getPixels()) {
201        return;
202    }
203
204    ASSERT(sourceRect.width() > 0);
205    ASSERT(sourceRect.height() > 0);
206
207    int originx = sourceRect.x();
208    int destx = destPoint.x() + sourceRect.x();
209    ASSERT(destx >= 0);
210    ASSERT(destx < m_size.width());
211    ASSERT(originx >= 0);
212    ASSERT(originx <= sourceRect.maxX());
213
214    int endx = destPoint.x() + sourceRect.maxX();
215    ASSERT(endx <= m_size.width());
216
217    int numColumns = endx - destx;
218
219    int originy = sourceRect.y();
220    int desty = destPoint.y() + sourceRect.y();
221    ASSERT(desty >= 0);
222    ASSERT(desty < m_size.height());
223    ASSERT(originy >= 0);
224    ASSERT(originy <= sourceRect.maxY());
225
226    int endy = destPoint.y() + sourceRect.maxY();
227    ASSERT(endy <= m_size.height());
228    int numRows = endy - desty;
229
230    unsigned srcBytesPerRow = 4 * sourceSize.width();
231    unsigned dstPixelsPerRow = dst.rowBytesAsPixels();
232
233    unsigned char* srcRows = source->data() + originy * srcBytesPerRow + originx * 4;
234    SkPMColor* dstRows = dst.getAddr32(destx, desty);
235    for (int y = 0; y < numRows; ++y) {
236        for (int x = 0; x < numColumns; x++) {
237            int basex = x * 4;
238            dstRows[x] = SkPackARGB32(srcRows[basex + 3],
239                                      srcRows[basex + 0],
240                                      srcRows[basex + 1],
241                                      srcRows[basex + 2]);
242        }
243        dstRows += dstPixelsPerRow;
244        srcRows += srcBytesPerRow;
245    }
246}
247
248
249String ImageBuffer::toDataURL(const String&, const double*) const
250{
251    // Encode the image into a vector.
252    SkDynamicMemoryWStream pngStream;
253    const SkBitmap& dst = imageBufferCanvas(this)->getDevice()->accessBitmap(true);
254    SkImageEncoder::EncodeStream(&pngStream, dst, SkImageEncoder::kPNG_Type, 100);
255
256    // Convert it into base64.
257    Vector<char> pngEncodedData;
258    SkData* streamData = pngStream.copyToData();
259    pngEncodedData.append((char*)streamData->data(), streamData->size());
260    streamData->unref();
261    Vector<char> base64EncodedData;
262    base64Encode(pngEncodedData, base64EncodedData);
263    // Append with a \0 so that it's a valid string.
264    base64EncodedData.append('\0');
265
266    // And the resulting string.
267    return String::format("data:image/png;base64,%s", base64EncodedData.data());
268}
269
270void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookupTable)
271{
272    notImplemented();
273}
274
275size_t ImageBuffer::dataSize() const
276{
277    return m_size.width() * m_size.height() * 4;
278}
279
280}
281