Home | History | Annotate | Download | only in haiku
      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