1 /* 2 * Copyright (C) 2010 Stephan Amus <superstippi (at) 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 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 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 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 77 ImageBuffer::~ImageBuffer() 78 { 79 } 80 81 size_t ImageBuffer::dataSize() const 82 { 83 return m_size.width() * m_size.height() * 4; 84 } 85 86 GraphicsContext* ImageBuffer::context() const 87 { 88 ASSERT(m_data.m_view.Window()); 89 90 return m_context.get(); 91 } 92 93 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 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 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 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 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 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 258 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 265 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 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 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 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 319 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