Home | History | Annotate | Download | only in qt
      1 /*
      2  * Copyright (C) 2006 Nikolas Zimmermann <zimmermann (at) kde.org>
      3  * Copyright (C) 2008 Holger Hans Peter Freyther
      4  * Copyright (C) 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 "CString.h"
     32 #include "GraphicsContext.h"
     33 #include "ImageData.h"
     34 #include "MIMETypeRegistry.h"
     35 #include "StillImageQt.h"
     36 
     37 #include <QBuffer>
     38 #include <QColor>
     39 #include <QImage>
     40 #include <QImageWriter>
     41 #include <QPainter>
     42 #include <QPixmap>
     43 #include <math.h>
     44 
     45 namespace WebCore {
     46 
     47 ImageBufferData::ImageBufferData(const IntSize& size)
     48     : m_pixmap(size)
     49 {
     50     m_pixmap.fill(QColor(Qt::transparent));
     51 
     52     QPainter* painter = new QPainter(&m_pixmap);
     53     m_painter.set(painter);
     54 
     55     // Since ImageBuffer is used mainly for Canvas, explicitly initialize
     56     // its painter's pen and brush with the corresponding canvas defaults
     57     // NOTE: keep in sync with CanvasRenderingContext2D::State
     58     QPen pen = painter->pen();
     59     pen.setColor(Qt::black);
     60     pen.setWidth(1);
     61     pen.setCapStyle(Qt::FlatCap);
     62     pen.setJoinStyle(Qt::MiterJoin);
     63     pen.setMiterLimit(10);
     64     painter->setPen(pen);
     65     QBrush brush = painter->brush();
     66     brush.setColor(Qt::black);
     67     painter->setBrush(brush);
     68     painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
     69 }
     70 
     71 ImageBuffer::ImageBuffer(const IntSize& size, ImageColorSpace, bool& success)
     72     : m_data(size)
     73     , m_size(size)
     74 {
     75     m_context.set(new GraphicsContext(m_data.m_painter.get()));
     76     success = true;
     77 }
     78 
     79 ImageBuffer::~ImageBuffer()
     80 {
     81 }
     82 
     83 GraphicsContext* ImageBuffer::context() const
     84 {
     85     ASSERT(m_data.m_painter->isActive());
     86 
     87     return m_context.get();
     88 }
     89 
     90 Image* ImageBuffer::image() const
     91 {
     92     if (!m_image) {
     93         // It's assumed that if image() is called, the actual rendering to the
     94         // GraphicsContext must be done.
     95         ASSERT(context());
     96         m_image = StillImage::create(m_data.m_pixmap);
     97     }
     98 
     99     return m_image.get();
    100 }
    101 
    102 void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable)
    103 {
    104     bool isPainting = m_data.m_painter->isActive();
    105     if (isPainting)
    106         m_data.m_painter->end();
    107 
    108     QImage image = m_data.m_pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
    109     ASSERT(!image.isNull());
    110 
    111     for (int y = 0; y < m_size.height(); ++y) {
    112         for (int x = 0; x < m_size.width(); x++) {
    113             QRgb value = image.pixel(x, y);
    114             value = qRgba(lookUpTable[qRed(value)],
    115                           lookUpTable[qGreen(value)],
    116                           lookUpTable[qBlue(value)],
    117                           qAlpha(value));
    118             image.setPixel(x, y, value);
    119         }
    120     }
    121 
    122     m_data.m_pixmap = QPixmap::fromImage(image);
    123 
    124     if (isPainting)
    125         m_data.m_painter->begin(&m_data.m_pixmap);
    126 }
    127 
    128 template <Multiply multiplied>
    129 PassRefPtr<ImageData> getImageData(const IntRect& rect, const ImageBufferData& imageData, const IntSize& size)
    130 {
    131     PassRefPtr<ImageData> result = ImageData::create(rect.width(), rect.height());
    132     unsigned char* data = result->data()->data()->data();
    133 
    134     if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > size.width() || (rect.y() + rect.height()) > size.height())
    135         memset(data, 0, result->data()->length());
    136 
    137     int originx = rect.x();
    138     int destx = 0;
    139     if (originx < 0) {
    140         destx = -originx;
    141         originx = 0;
    142     }
    143     int endx = rect.x() + rect.width();
    144     if (endx > size.width())
    145         endx = size.width();
    146     int numColumns = endx - originx;
    147 
    148     int originy = rect.y();
    149     int desty = 0;
    150     if (originy < 0) {
    151         desty = -originy;
    152         originy = 0;
    153     }
    154     int endy = rect.y() + rect.height();
    155     if (endy > size.height())
    156         endy = size.height();
    157     int numRows = endy - originy;
    158 
    159     QImage image = imageData.m_pixmap.toImage();
    160     if (multiplied == Unmultiplied)
    161         image = image.convertToFormat(QImage::Format_ARGB32);
    162     else
    163         image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
    164 
    165     ASSERT(!image.isNull());
    166 
    167     unsigned destBytesPerRow = 4 * rect.width();
    168     unsigned char* destRows = data + desty * destBytesPerRow + destx * 4;
    169     for (int y = 0; y < numRows; ++y) {
    170         for (int x = 0; x < numColumns; x++) {
    171             QRgb value = image.pixel(x + originx, y + originy);
    172             int basex = x * 4;
    173 
    174             destRows[basex] = qRed(value);
    175             destRows[basex + 1] = qGreen(value);
    176             destRows[basex + 2] = qBlue(value);
    177             destRows[basex + 3] = qAlpha(value);
    178         }
    179         destRows += destBytesPerRow;
    180     }
    181 
    182     return result;
    183 }
    184 
    185 PassRefPtr<ImageData> ImageBuffer::getUnmultipliedImageData(const IntRect& rect) const
    186 {
    187     return getImageData<Unmultiplied>(rect, m_data, m_size);
    188 }
    189 
    190 PassRefPtr<ImageData> ImageBuffer::getPremultipliedImageData(const IntRect& rect) const
    191 {
    192     return getImageData<Premultiplied>(rect, m_data, m_size);
    193 }
    194 
    195 template <Multiply multiplied>
    196 void putImageData(ImageData*& source, const IntRect& sourceRect, const IntPoint& destPoint, ImageBufferData& data, const IntSize& size)
    197 {
    198     ASSERT(sourceRect.width() > 0);
    199     ASSERT(sourceRect.height() > 0);
    200 
    201     int originx = sourceRect.x();
    202     int destx = destPoint.x() + sourceRect.x();
    203     ASSERT(destx >= 0);
    204     ASSERT(destx < size.width());
    205     ASSERT(originx >= 0);
    206     ASSERT(originx <= sourceRect.right());
    207 
    208     int endx = destPoint.x() + sourceRect.right();
    209     ASSERT(endx <= size.width());
    210 
    211     int numColumns = endx - destx;
    212 
    213     int originy = sourceRect.y();
    214     int desty = destPoint.y() + sourceRect.y();
    215     ASSERT(desty >= 0);
    216     ASSERT(desty < size.height());
    217     ASSERT(originy >= 0);
    218     ASSERT(originy <= sourceRect.bottom());
    219 
    220     int endy = destPoint.y() + sourceRect.bottom();
    221     ASSERT(endy <= size.height());
    222     int numRows = endy - desty;
    223 
    224     unsigned srcBytesPerRow = 4 * source->width();
    225 
    226     bool isPainting = data.m_painter->isActive();
    227     if (isPainting)
    228         data.m_painter->end();
    229 
    230     QImage image = data.m_pixmap.toImage();
    231     if (multiplied == Unmultiplied)
    232         image = image.convertToFormat(QImage::Format_ARGB32);
    233     else
    234         image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
    235 
    236     unsigned char* srcRows = source->data()->data()->data() + originy * srcBytesPerRow + originx * 4;
    237     for (int y = 0; y < numRows; ++y) {
    238         quint32* scanLine = reinterpret_cast<quint32*>(image.scanLine(y + desty));
    239         for (int x = 0; x < numColumns; x++) {
    240             // ImageData stores the pixels in RGBA while QImage is ARGB
    241             quint32 pixel = reinterpret_cast<quint32*>(srcRows + 4 * x)[0];
    242             pixel = ((pixel << 16) & 0xff0000) | ((pixel >> 16) & 0xff) | (pixel & 0xff00ff00);
    243             scanLine[x + destx] = pixel;
    244         }
    245 
    246         srcRows += srcBytesPerRow;
    247     }
    248 
    249     data.m_pixmap = QPixmap::fromImage(image);
    250 
    251     if (isPainting)
    252         data.m_painter->begin(&data.m_pixmap);
    253 }
    254 
    255 void ImageBuffer::putUnmultipliedImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint)
    256 {
    257     putImageData<Unmultiplied>(source, sourceRect, destPoint, m_data, m_size);
    258 }
    259 
    260 void ImageBuffer::putPremultipliedImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint)
    261 {
    262     putImageData<Premultiplied>(source, sourceRect, destPoint, m_data, m_size);
    263 }
    264 
    265 // We get a mimeType here but QImageWriter does not support mimetypes but
    266 // only formats (png, gif, jpeg..., xpm). So assume we get image/ as image
    267 // mimetypes and then remove the image/ to get the Qt format.
    268 String ImageBuffer::toDataURL(const String& mimeType) const
    269 {
    270     ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
    271 
    272     if (!mimeType.startsWith("image/"))
    273         return "data:,";
    274 
    275     // prepare our target
    276     QByteArray data;
    277     QBuffer buffer(&data);
    278     buffer.open(QBuffer::WriteOnly);
    279 
    280     if (!m_data.m_pixmap.save(&buffer, mimeType.substring(sizeof "image").utf8().data()))
    281         return "data:,";
    282 
    283     buffer.close();
    284     return String::format("data:%s;base64,%s", mimeType.utf8().data(), data.toBase64().data());
    285 }
    286 
    287 }
    288