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