• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
41 namespace WebCore {
42 
ImageBufferData(const IntSize & size)43 ImageBufferData::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 
~ImageBufferData()62 ImageBufferData::~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 
ImageBuffer(const IntSize & size,ImageColorSpace imageColorSpace,RenderingMode,bool & success)69 ImageBuffer::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 
~ImageBuffer()77 ImageBuffer::~ImageBuffer()
78 {
79 }
80 
dataSize() const81 size_t ImageBuffer::dataSize() const
82 {
83     return m_size.width() * m_size.height() * 4;
84 }
85 
context() const86 GraphicsContext* ImageBuffer::context() const
87 {
88     ASSERT(m_data.m_view.Window());
89 
90     return m_context.get();
91 }
92 
image() const93 Image* 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 
platformTransformColorSpace(const Vector<int> & lookUpTable)106 void 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 
convertFromData(const uint8 * sourceRows,unsigned sourceBytesPerRow,uint8 * destRows,unsigned destBytesPerRow,unsigned rows,unsigned columns)129 static 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 
convertFromInternalData(const uint8 * sourceRows,unsigned sourceBytesPerRow,uint8 * destRows,unsigned destBytesPerRow,unsigned rows,unsigned columns,bool premultiplied)150 static 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 
convertToInternalData(const uint8 * sourceRows,unsigned sourceBytesPerRow,uint8 * destRows,unsigned destBytesPerRow,unsigned rows,unsigned columns,bool premultiplied)179 static 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 
getImageData(const IntRect & rect,const ImageBufferData & imageData,const IntSize & size,bool premultiplied)215 static 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 
getUnmultipliedImageData(const IntRect & rect) const258 PassRefPtr<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 
getPremultipliedImageData(const IntRect & rect) const265 PassRefPtr<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 
putImageData(ImageData * source,const IntRect & sourceRect,const IntPoint & destPoint,ImageBufferData & imageData,const IntSize & size,bool premultiplied)272 static 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 
putUnmultipliedImageData(ImageData * source,const IntRect & sourceRect,const IntPoint & destPoint)305 void 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 
putPremultipliedImageData(ImageData * source,const IntRect & sourceRect,const IntPoint & destPoint)312 void 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 
toDataURL(const String & mimeType,const double *) const319 String 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