1/*
2 * Copyright (C) 2010 Stephan AÃmus <superstippi@gmx.de>
3 * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "ImageBuffer.h"
29
30#include "Base64.h"
31#include "GraphicsContext.h"
32#include "ImageData.h"
33#include "MIMETypeRegistry.h"
34#include "StillImageHaiku.h"
35#include <wtf/text/CString.h>
36#include <wtf/text/StringConcatenate.h>
37#include <BitmapStream.h>
38#include <String.h>
39#include <TranslatorRoster.h>
40
41namespace WebCore {
42
43ImageBufferData::ImageBufferData(const IntSize& size)
44    : m_bitmap(BRect(0, 0, size.width() - 1, size.height() - 1), B_RGBA32, true)
45    , m_view(m_bitmap.Bounds(), "WebKit ImageBufferData", 0, 0)
46{
47    // Always keep the bitmap locked, we are the only client.
48    m_bitmap.Lock();
49    m_bitmap.AddChild(&m_view);
50
51    // Fill with completely transparent color.
52    memset(m_bitmap.Bits(), 0, m_bitmap.BitsLength());
53
54    // Since ImageBuffer is used mainly for Canvas, explicitly initialize
55    // its view's graphics state with the corresponding canvas defaults
56    // NOTE: keep in sync with CanvasRenderingContext2D::State
57    m_view.SetLineMode(B_BUTT_CAP, B_MITER_JOIN, 10);
58    m_view.SetDrawingMode(B_OP_ALPHA);
59    m_view.SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE);
60}
61
62ImageBufferData::~ImageBufferData()
63{
64    // Remove the view from the bitmap, keeping it from being free'd twice.
65    m_view.RemoveSelf();
66    m_bitmap.Unlock();
67}
68
69ImageBuffer::ImageBuffer(const IntSize& size, ImageColorSpace imageColorSpace, RenderingMode, bool& success)
70    : m_data(size)
71    , m_size(size)
72{
73    m_context.set(new GraphicsContext(&m_data.m_view));
74    success = true;
75}
76
77ImageBuffer::~ImageBuffer()
78{
79}
80
81size_t ImageBuffer::dataSize() const
82{
83    return m_size.width() * m_size.height() * 4;
84}
85
86GraphicsContext* ImageBuffer::context() const
87{
88    ASSERT(m_data.m_view.Window());
89
90    return m_context.get();
91}
92
93Image* ImageBuffer::image() const
94{
95    if (!m_image) {
96        // It's assumed that if image() is called, the actual rendering to the
97        // GraphicsContext must be done.
98        ASSERT(context());
99        m_data.m_view.Sync();
100        m_image = StillImage::create(m_data.m_bitmap);
101    }
102
103    return m_image.get();
104}
105
106void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable)
107{
108    uint8* rowData = reinterpret_cast<uint8*>(m_data.m_bitmap.Bits());
109    unsigned bytesPerRow = m_data.m_bitmap.BytesPerRow();
110    unsigned rows = m_size.height();
111    unsigned columns = m_size.width();
112    for (unsigned y = 0; y < rows; y++) {
113        uint8* pixel = rowData;
114        for (unsigned x = 0; x < columns; x++) {
115            // lookUpTable doesn't seem to support a LUT for each color channel
116            // separately (judging from the other ports). We don't need to
117            // convert from/to pre-multiplied color space since BBitmap storage
118            // is not pre-multiplied.
119            pixel[0] = lookUpTable[pixel[0]];
120            pixel[1] = lookUpTable[pixel[1]];
121            pixel[2] = lookUpTable[pixel[2]];
122            // alpha stays unmodified.
123            pixel += 4;
124        }
125        rowData += bytesPerRow;
126    }
127}
128
129static inline void convertFromData(const uint8* sourceRows, unsigned sourceBytesPerRow,
130                                   uint8* destRows, unsigned destBytesPerRow,
131                                   unsigned rows, unsigned columns)
132{
133    for (unsigned y = 0; y < rows; y++) {
134        const uint8* sourcePixel = sourceRows;
135        uint8* destPixel = destRows;
136        for (unsigned x = 0; x < columns; x++) {
137            // RGBA -> BGRA or BGRA -> RGBA
138            destPixel[0] = sourcePixel[2];
139            destPixel[1] = sourcePixel[1];
140            destPixel[2] = sourcePixel[0];
141            destPixel[3] = sourcePixel[3];
142            destPixel += 4;
143            sourcePixel += 4;
144        }
145        sourceRows += sourceBytesPerRow;
146        destRows += destBytesPerRow;
147    }
148}
149
150static inline void convertFromInternalData(const uint8* sourceRows, unsigned sourceBytesPerRow,
151                                           uint8* destRows, unsigned destBytesPerRow,
152                                           unsigned rows, unsigned columns,
153                                           bool premultiplied)
154{
155    if (premultiplied) {
156        // Internal storage is not pre-multiplied, pre-multiply on the fly.
157        for (unsigned y = 0; y < rows; y++) {
158            const uint8* sourcePixel = sourceRows;
159            uint8* destPixel = destRows;
160            for (unsigned x = 0; x < columns; x++) {
161                // RGBA -> BGRA or BGRA -> RGBA
162                destPixel[0] = static_cast<uint16>(sourcePixel[2]) * sourcePixel[3] / 255;
163                destPixel[1] = static_cast<uint16>(sourcePixel[1]) * sourcePixel[3] / 255;
164                destPixel[2] = static_cast<uint16>(sourcePixel[0]) * sourcePixel[3] / 255;
165                destPixel[3] = sourcePixel[3];
166                destPixel += 4;
167                sourcePixel += 4;
168            }
169            sourceRows += sourceBytesPerRow;
170            destRows += destBytesPerRow;
171        }
172    } else {
173        convertFromData(sourceRows, sourceBytesPerRow,
174                        destRows, destBytesPerRow,
175                        rows, columns);
176    }
177}
178
179static inline void convertToInternalData(const uint8* sourceRows, unsigned sourceBytesPerRow,
180                                         uint8* destRows, unsigned destBytesPerRow,
181                                         unsigned rows, unsigned columns,
182                                         bool premultiplied)
183{
184    if (premultiplied) {
185        // Internal storage is not pre-multiplied, de-multiply source data.
186        for (unsigned y = 0; y < rows; y++) {
187            const uint8* sourcePixel = sourceRows;
188            uint8* destPixel = destRows;
189            for (unsigned x = 0; x < columns; x++) {
190                // RGBA -> BGRA or BGRA -> RGBA
191                if (sourcePixel[3]) {
192                    destPixel[0] = static_cast<uint16>(sourcePixel[2]) * 255 / sourcePixel[3];
193                    destPixel[1] = static_cast<uint16>(sourcePixel[1]) * 255 / sourcePixel[3];
194                    destPixel[2] = static_cast<uint16>(sourcePixel[0]) * 255 / sourcePixel[3];
195                    destPixel[3] = sourcePixel[3];
196                } else {
197                    destPixel[0] = 0;
198                    destPixel[1] = 0;
199                    destPixel[2] = 0;
200                    destPixel[3] = 0;
201                }
202                destPixel += 4;
203                sourcePixel += 4;
204            }
205            sourceRows += sourceBytesPerRow;
206            destRows += destBytesPerRow;
207        }
208    } else {
209        convertFromData(sourceRows, sourceBytesPerRow,
210                        destRows, destBytesPerRow,
211                        rows, columns);
212    }
213}
214
215static PassRefPtr<ImageData> getImageData(const IntRect& rect, const ImageBufferData& imageData, const IntSize& size, bool premultiplied)
216{
217    RefPtr<ImageData> result = ImageData::create(IntSize(rect.width(), rect.height()));
218    unsigned char* data = result->data()->data()->data();
219
220    // If the destination image is larger than the source image, the outside
221    // regions need to be transparent. This way is simply, although with a
222    // a slight overhead for the inside region.
223    if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > size.width() || (rect.y() + rect.height()) > size.height())
224        memset(data, 0, result->data()->length());
225
226    // If the requested image is outside the source image, we can return at
227    // this point.
228    if (rect.x() > size.width() || rect.y() > size.height() || rect.right() < 0 || rect.bottom() < 0)
229        return result.release();
230
231    // Now we know there must be an intersection rect which we need to extract.
232    BRect sourceRect(0, 0, size.width() - 1, size.height() - 1);
233    sourceRect = BRect(rect) & sourceRect;
234
235    unsigned destBytesPerRow = 4 * rect.width();
236    unsigned char* destRows = data;
237    // Offset the destination pointer to point at the first pixel of the
238    // intersection rect.
239    destRows += (rect.x() - static_cast<int>(sourceRect.left)) * 4
240        + (rect.y() - static_cast<int>(sourceRect.top)) * destBytesPerRow;
241
242    const uint8* sourceRows = reinterpret_cast<const uint8*>(imageData.m_bitmap.Bits());
243    uint32 sourceBytesPerRow = imageData.m_bitmap.BytesPerRow();
244    // Offset the source pointer to point at the first pixel of the
245    // intersection rect.
246    sourceRows += static_cast<int>(sourceRect.left) * 4
247        + static_cast<int>(sourceRect.top) * sourceBytesPerRow;
248
249    unsigned rows = sourceRect.IntegerHeight() + 1;
250    unsigned columns = sourceRect.IntegerWidth() + 1;
251    convertFromInternalData(sourceRows, sourceBytesPerRow, destRows, destBytesPerRow,
252        rows, columns, premultiplied);
253
254    return result.release();
255}
256
257
258PassRefPtr<ImageData> ImageBuffer::getUnmultipliedImageData(const IntRect& rect) const
259{
260    // Make sure all asynchronous drawing has finished
261    m_data.m_view.Sync();
262    return getImageData(rect, m_data, m_size, false);
263}
264
265PassRefPtr<ImageData> ImageBuffer::getPremultipliedImageData(const IntRect& rect) const
266{
267    // Make sure all asynchronous drawing has finished
268    m_data.m_view.Sync();
269    return getImageData(rect, m_data, m_size, true);
270}
271
272static void putImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint, ImageBufferData& imageData, const IntSize& size, bool premultiplied)
273{
274    // If the source image is outside the destination image, we can return at
275    // this point.
276    // FIXME: Check if this isn't already done in WebCore.
277    if (destPoint.x() > size.width() || destPoint.y() > size.height()
278        || destPoint.x() + sourceRect.width() < 0
279        || destPoint.y() + sourceRect.height() < 0) {
280        return;
281    }
282
283    const unsigned char* sourceRows = source->data()->data()->data();
284    unsigned sourceBytesPerRow = 4 * source->width();
285    // Offset the source pointer to the first pixel of the source rect.
286    sourceRows += sourceRect.x() * 4 + sourceRect.y() * sourceBytesPerRow;
287
288    // We know there must be an intersection rect.
289    BRect destRect(destPoint.x(), destPoint.y(), sourceRect.width() - 1, sourceRect.height() - 1);
290    destRect = destRect & BRect(0, 0, size.width() - 1, size.height() - 1);
291
292    unsigned char* destRows = reinterpret_cast<unsigned char*>(imageData.m_bitmap.Bits());
293    uint32 destBytesPerRow = imageData.m_bitmap.BytesPerRow();
294    // Offset the source pointer to point at the first pixel of the
295    // intersection rect.
296    destRows += static_cast<int>(destRect.left) * 4
297        + static_cast<int>(destRect.top) * destBytesPerRow;
298
299    unsigned rows = sourceRect.height();
300    unsigned columns = sourceRect.width();
301    convertToInternalData(sourceRows, sourceBytesPerRow, destRows, destBytesPerRow,
302        rows, columns, premultiplied);
303}
304
305void ImageBuffer::putUnmultipliedImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint)
306{
307    // Make sure all asynchronous drawing has finished
308    m_data.m_view.Sync();
309    putImageData(source, sourceRect, destPoint, m_data, m_size, false);
310}
311
312void ImageBuffer::putPremultipliedImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint)
313{
314    // Make sure all asynchronous drawing has finished
315    m_data.m_view.Sync();
316    putImageData(source, sourceRect, destPoint, m_data, m_size, true);
317}
318
319String ImageBuffer::toDataURL(const String& mimeType, const double*) const
320{
321    if (!MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType))
322        return "data:,";
323
324    BString mimeTypeString(mimeType);
325
326    uint32 translatorType = 0;
327
328    BTranslatorRoster* roster = BTranslatorRoster::Default();
329    translator_id* translators;
330    int32 translatorCount;
331    roster->GetAllTranslators(&translators, &translatorCount);
332    for (int32 i = 0; i < translatorCount; i++) {
333        // Skip translators that don't support archived BBitmaps as input data.
334        const translation_format* inputFormats;
335        int32 formatCount;
336        roster->GetInputFormats(translators[i], &inputFormats, &formatCount);
337        bool supportsBitmaps = false;
338        for (int32 j = 0; j < formatCount; j++) {
339            if (inputFormats[j].type == B_TRANSLATOR_BITMAP) {
340                supportsBitmaps = true;
341                break;
342            }
343        }
344        if (!supportsBitmaps)
345            continue;
346
347        const translation_format* outputFormats;
348        roster->GetOutputFormats(translators[i], &outputFormats, &formatCount);
349        for (int32 j = 0; j < formatCount; j++) {
350            if (outputFormats[j].group == B_TRANSLATOR_BITMAP
351                && mimeTypeString == outputFormats[j].MIME) {
352                translatorType = outputFormats[j].type;
353            }
354        }
355        if (translatorType)
356            break;
357    }
358
359
360    BMallocIO translatedStream;
361    BBitmap* bitmap = const_cast<BBitmap*>(&m_data.m_bitmap);
362        // BBitmapStream doesn't take "const Bitmap*"...
363    BBitmapStream bitmapStream(bitmap);
364    if (roster->Translate(&bitmapStream, 0, 0, &translatedStream, translatorType,
365                          B_TRANSLATOR_BITMAP, mimeType.utf8().data()) != B_OK) {
366        bitmapStream.DetachBitmap(&bitmap);
367        return "data:,";
368    }
369
370    bitmapStream.DetachBitmap(&bitmap);
371
372    Vector<char> encodedBuffer;
373    base64Encode(reinterpret_cast<const char*>(translatedStream.Buffer()),
374                 translatedStream.BufferLength(), encodedBuffer);
375
376    return makeString("data:", mimeType, ";base64,", encodedBuffer);
377}
378
379} // namespace WebCore
380
381