Home | History | Annotate | Download | only in cairo
      1 /*
      2  * Copyright (C) 2006 Nikolas Zimmermann <zimmermann (at) kde.org>
      3  * Copyright (C) 2007 Holger Hans Peter Freyther <zecke (at) selfish.org>
      4  * Copyright (C) 2008, 2009 Dirk Schulze <krit (at) webkit.org>
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
     16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     18  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include "config.h"
     29 #include "ImageBuffer.h"
     30 
     31 #include "Base64.h"
     32 #include "BitmapImage.h"
     33 #include "Color.h"
     34 #include "GraphicsContext.h"
     35 #include "ImageData.h"
     36 #include "MIMETypeRegistry.h"
     37 #include "Pattern.h"
     38 #include "PlatformString.h"
     39 
     40 #include <cairo.h>
     41 #include <wtf/Vector.h>
     42 #include <math.h>
     43 
     44 using namespace std;
     45 
     46 // Cairo doesn't provide a way to copy a cairo_surface_t.
     47 // See http://lists.cairographics.org/archives/cairo/2007-June/010877.html
     48 // Once cairo provides the way, use the function instead of this.
     49 static inline cairo_surface_t* copySurface(cairo_surface_t* surface)
     50 {
     51     cairo_format_t format = cairo_image_surface_get_format(surface);
     52     int width = cairo_image_surface_get_width(surface);
     53     int height = cairo_image_surface_get_height(surface);
     54     cairo_surface_t* newsurface = cairo_image_surface_create(format, width, height);
     55 
     56     cairo_t* cr = cairo_create(newsurface);
     57     cairo_set_source_surface(cr, surface, 0, 0);
     58     cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
     59     cairo_paint(cr);
     60     cairo_destroy(cr);
     61 
     62     return newsurface;
     63 }
     64 
     65 namespace WebCore {
     66 
     67 ImageBufferData::ImageBufferData(const IntSize& size)
     68     : m_surface(0)
     69 {
     70 }
     71 
     72 ImageBuffer::ImageBuffer(const IntSize& size, ImageColorSpace imageColorSpace, bool& success)
     73     : m_data(size)
     74     , m_size(size)
     75 {
     76     success = false;  // Make early return mean error.
     77     m_data.m_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
     78                                                   size.width(),
     79                                                   size.height());
     80     if (cairo_surface_status(m_data.m_surface) != CAIRO_STATUS_SUCCESS)
     81         return;  // create will notice we didn't set m_initialized and fail.
     82 
     83     cairo_t* cr = cairo_create(m_data.m_surface);
     84     m_context.set(new GraphicsContext(cr));
     85     cairo_destroy(cr);  // The context is now owned by the GraphicsContext.
     86     success = true;
     87 }
     88 
     89 ImageBuffer::~ImageBuffer()
     90 {
     91     cairo_surface_destroy(m_data.m_surface);
     92 }
     93 
     94 GraphicsContext* ImageBuffer::context() const
     95 {
     96     return m_context.get();
     97 }
     98 
     99 Image* ImageBuffer::image() const
    100 {
    101     if (!m_image) {
    102         // It's assumed that if image() is called, the actual rendering to the
    103         // GraphicsContext must be done.
    104         ASSERT(context());
    105 
    106         // This creates a COPY of the image and will cache that copy. This means
    107         // that if subsequent operations take place on the context, neither the
    108         // currently-returned image, nor the results of future image() calls,
    109         // will contain that operation.
    110         //
    111         // This seems silly, but is the way the CG port works: image() is
    112         // intended to be used only when rendering is "complete."
    113         cairo_surface_t* newsurface = copySurface(m_data.m_surface);
    114 
    115         // BitmapImage will release the passed in surface on destruction
    116         m_image = BitmapImage::create(newsurface);
    117     }
    118     return m_image.get();
    119 }
    120 
    121 void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable)
    122 {
    123     ASSERT(cairo_surface_get_type(m_data.m_surface) == CAIRO_SURFACE_TYPE_IMAGE);
    124 
    125     unsigned char* dataSrc = cairo_image_surface_get_data(m_data.m_surface);
    126     int stride = cairo_image_surface_get_stride(m_data.m_surface);
    127     for (int y = 0; y < m_size.height(); ++y) {
    128         unsigned* row = reinterpret_cast<unsigned*>(dataSrc + stride * y);
    129         for (int x = 0; x < m_size.width(); x++) {
    130             unsigned* pixel = row + x;
    131             Color pixelColor = colorFromPremultipliedARGB(*pixel);
    132             pixelColor = Color(lookUpTable[pixelColor.red()],
    133                                lookUpTable[pixelColor.green()],
    134                                lookUpTable[pixelColor.blue()],
    135                                pixelColor.alpha());
    136             *pixel = premultipliedARGBFromColor(pixelColor);
    137         }
    138     }
    139     cairo_surface_mark_dirty_rectangle (m_data.m_surface, 0, 0, m_size.width(), m_size.height());
    140 }
    141 
    142 template <Multiply multiplied>
    143 PassRefPtr<ImageData> getImageData(const IntRect& rect, const ImageBufferData& data, const IntSize& size)
    144 {
    145     ASSERT(cairo_surface_get_type(data.m_surface) == CAIRO_SURFACE_TYPE_IMAGE);
    146 
    147     PassRefPtr<ImageData> result = ImageData::create(rect.width(), rect.height());
    148     unsigned char* dataSrc = cairo_image_surface_get_data(data.m_surface);
    149     unsigned char* dataDst = result->data()->data()->data();
    150 
    151     if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > size.width() || (rect.y() + rect.height()) > size.height())
    152         memset(dataDst, 0, result->data()->length());
    153 
    154     int originx = rect.x();
    155     int destx = 0;
    156     if (originx < 0) {
    157         destx = -originx;
    158         originx = 0;
    159     }
    160     int endx = rect.x() + rect.width();
    161     if (endx > size.width())
    162         endx = size.width();
    163     int numColumns = endx - originx;
    164 
    165     int originy = rect.y();
    166     int desty = 0;
    167     if (originy < 0) {
    168         desty = -originy;
    169         originy = 0;
    170     }
    171     int endy = rect.y() + rect.height();
    172     if (endy > size.height())
    173         endy = size.height();
    174     int numRows = endy - originy;
    175 
    176     int stride = cairo_image_surface_get_stride(data.m_surface);
    177     unsigned destBytesPerRow = 4 * rect.width();
    178 
    179     unsigned char* destRows = dataDst + desty * destBytesPerRow + destx * 4;
    180     for (int y = 0; y < numRows; ++y) {
    181         unsigned* row = reinterpret_cast<unsigned*>(dataSrc + stride * (y + originy));
    182         for (int x = 0; x < numColumns; x++) {
    183             int basex = x * 4;
    184             unsigned* pixel = row + x + originx;
    185             Color pixelColor;
    186             if (multiplied == Unmultiplied)
    187                 pixelColor = colorFromPremultipliedARGB(*pixel);
    188             else
    189                 pixelColor = Color(*pixel);
    190             destRows[basex]     = pixelColor.red();
    191             destRows[basex + 1] = pixelColor.green();
    192             destRows[basex + 2] = pixelColor.blue();
    193             destRows[basex + 3] = pixelColor.alpha();
    194         }
    195         destRows += destBytesPerRow;
    196     }
    197 
    198     return result;
    199 }
    200 
    201 PassRefPtr<ImageData> ImageBuffer::getUnmultipliedImageData(const IntRect& rect) const
    202 {
    203     return getImageData<Unmultiplied>(rect, m_data, m_size);
    204 }
    205 
    206 PassRefPtr<ImageData> ImageBuffer::getPremultipliedImageData(const IntRect& rect) const
    207 {
    208     return getImageData<Premultiplied>(rect, m_data, m_size);
    209 }
    210 
    211 template <Multiply multiplied>
    212 void putImageData(ImageData*& source, const IntRect& sourceRect, const IntPoint& destPoint, ImageBufferData& data, const IntSize& size)
    213 {
    214     ASSERT(cairo_surface_get_type(data.m_surface) == CAIRO_SURFACE_TYPE_IMAGE);
    215 
    216     unsigned char* dataDst = cairo_image_surface_get_data(data.m_surface);
    217 
    218     ASSERT(sourceRect.width() > 0);
    219     ASSERT(sourceRect.height() > 0);
    220 
    221     int originx = sourceRect.x();
    222     int destx = destPoint.x() + sourceRect.x();
    223     ASSERT(destx >= 0);
    224     ASSERT(destx < size.width());
    225     ASSERT(originx >= 0);
    226     ASSERT(originx <= sourceRect.right());
    227 
    228     int endx = destPoint.x() + sourceRect.right();
    229     ASSERT(endx <= size.width());
    230 
    231     int numColumns = endx - destx;
    232 
    233     int originy = sourceRect.y();
    234     int desty = destPoint.y() + sourceRect.y();
    235     ASSERT(desty >= 0);
    236     ASSERT(desty < size.height());
    237     ASSERT(originy >= 0);
    238     ASSERT(originy <= sourceRect.bottom());
    239 
    240     int endy = destPoint.y() + sourceRect.bottom();
    241     ASSERT(endy <= size.height());
    242     int numRows = endy - desty;
    243 
    244     unsigned srcBytesPerRow = 4 * source->width();
    245     int stride = cairo_image_surface_get_stride(data.m_surface);
    246 
    247     unsigned char* srcRows = source->data()->data()->data() + originy * srcBytesPerRow + originx * 4;
    248     for (int y = 0; y < numRows; ++y) {
    249         unsigned* row = reinterpret_cast<unsigned*>(dataDst + stride * (y + desty));
    250         for (int x = 0; x < numColumns; x++) {
    251             int basex = x * 4;
    252             unsigned* pixel = row + x + destx;
    253             Color pixelColor(srcRows[basex],
    254                     srcRows[basex + 1],
    255                     srcRows[basex + 2],
    256                     srcRows[basex + 3]);
    257             if (multiplied == Unmultiplied)
    258                 *pixel = premultipliedARGBFromColor(pixelColor);
    259             else
    260                 *pixel = pixelColor.rgb();
    261         }
    262         srcRows += srcBytesPerRow;
    263     }
    264     cairo_surface_mark_dirty_rectangle (data.m_surface,
    265                                         destx, desty,
    266                                         numColumns, numRows);
    267 }
    268 
    269 void ImageBuffer::putUnmultipliedImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint)
    270 {
    271     putImageData<Unmultiplied>(source, sourceRect, destPoint, m_data, m_size);
    272 }
    273 
    274 void ImageBuffer::putPremultipliedImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint)
    275 {
    276     putImageData<Premultiplied>(source, sourceRect, destPoint, m_data, m_size);
    277 }
    278 
    279 static cairo_status_t writeFunction(void* closure, const unsigned char* data, unsigned int length)
    280 {
    281     Vector<char>* in = reinterpret_cast<Vector<char>*>(closure);
    282     in->append(data, length);
    283     return CAIRO_STATUS_SUCCESS;
    284 }
    285 
    286 String ImageBuffer::toDataURL(const String& mimeType) const
    287 {
    288     cairo_surface_t* image = cairo_get_target(context()->platformContext());
    289     if (!image)
    290         return "data:,";
    291 
    292     String actualMimeType("image/png");
    293     if (MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType))
    294         actualMimeType = mimeType;
    295 
    296     Vector<char> in;
    297     // Only PNG output is supported for now.
    298     cairo_surface_write_to_png_stream(image, writeFunction, &in);
    299 
    300     Vector<char> out;
    301     base64Encode(in, out);
    302 
    303     return "data:" + actualMimeType + ";base64," + String(out.data(), out.size());
    304 }
    305 
    306 } // namespace WebCore
    307