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  * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
     17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     20  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     21  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     24  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include "config.h"
     30 #include "ImageBuffer.h"
     31 
     32 #include "GraphicsContext.h"
     33 #include "ImageData.h"
     34 #include "MIMETypeRegistry.h"
     35 #include "StillImageQt.h"
     36 #include "TransparencyLayer.h"
     37 #include <wtf/text/CString.h>
     38 #include <wtf/text/StringConcatenate.h>
     39 
     40 #include <QBuffer>
     41 #include <QColor>
     42 #include <QImage>
     43 #include <QImageWriter>
     44 #include <QPainter>
     45 #include <QPixmap>
     46 #include <math.h>
     47 
     48 namespace WebCore {
     49 
     50 ImageBufferData::ImageBufferData(const IntSize& size)
     51     : m_pixmap(size)
     52     , m_painter(0)
     53 {
     54     if (m_pixmap.isNull())
     55         return;
     56 
     57     m_pixmap.fill(QColor(Qt::transparent));
     58 
     59     QPainter* painter = new QPainter;
     60     m_painter.set(painter);
     61 
     62     if (!painter->begin(&m_pixmap))
     63         return;
     64 
     65     // Since ImageBuffer is used mainly for Canvas, explicitly initialize
     66     // its painter's pen and brush with the corresponding canvas defaults
     67     // NOTE: keep in sync with CanvasRenderingContext2D::State
     68     QPen pen = painter->pen();
     69     pen.setColor(Qt::black);
     70     pen.setWidth(1);
     71     pen.setCapStyle(Qt::FlatCap);
     72     pen.setJoinStyle(Qt::SvgMiterJoin);
     73     pen.setMiterLimit(10);
     74     painter->setPen(pen);
     75     QBrush brush = painter->brush();
     76     brush.setColor(Qt::black);
     77     painter->setBrush(brush);
     78     painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
     79 
     80     m_image = StillImage::createForRendering(&m_pixmap);
     81 }
     82 
     83 QImage ImageBufferData::toQImage() const
     84 {
     85     QPaintEngine* paintEngine = m_pixmap.paintEngine();
     86     if (!paintEngine || paintEngine->type() != QPaintEngine::Raster)
     87         return m_pixmap.toImage();
     88 
     89     // QRasterPixmapData::toImage() will deep-copy the backing QImage if there's an active QPainter on it.
     90     // For performance reasons, we don't want that here, so we temporarily redirect the paint engine.
     91     QPaintDevice* currentPaintDevice = paintEngine->paintDevice();
     92     paintEngine->setPaintDevice(0);
     93     QImage image = m_pixmap.toImage();
     94     paintEngine->setPaintDevice(currentPaintDevice);
     95     return image;
     96 }
     97 
     98 ImageBuffer::ImageBuffer(const IntSize& size, ColorSpace, RenderingMode, bool& success)
     99     : m_data(size)
    100     , m_size(size)
    101 {
    102     success = m_data.m_painter && m_data.m_painter->isActive();
    103     if (!success)
    104         return;
    105 
    106     m_context.set(new GraphicsContext(m_data.m_painter.get()));
    107 }
    108 
    109 ImageBuffer::~ImageBuffer()
    110 {
    111 }
    112 
    113 size_t ImageBuffer::dataSize() const
    114 {
    115     return m_size.width() * m_size.height() * 4;
    116 }
    117 
    118 GraphicsContext* ImageBuffer::context() const
    119 {
    120     ASSERT(m_data.m_painter->isActive());
    121 
    122     return m_context.get();
    123 }
    124 
    125 bool ImageBuffer::drawsUsingCopy() const
    126 {
    127     return false;
    128 }
    129 
    130 PassRefPtr<Image> ImageBuffer::copyImage() const
    131 {
    132     return StillImage::create(m_data.m_pixmap);
    133 }
    134 
    135 void ImageBuffer::draw(GraphicsContext* destContext, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect,
    136                        CompositeOperator op, bool useLowQualityScale)
    137 {
    138     if (destContext == context()) {
    139         // We're drawing into our own buffer.  In order for this to work, we need to copy the source buffer first.
    140         RefPtr<Image> copy = copyImage();
    141         destContext->drawImage(copy.get(), ColorSpaceDeviceRGB, destRect, srcRect, op, useLowQualityScale);
    142     } else
    143         destContext->drawImage(m_data.m_image.get(), styleColorSpace, destRect, srcRect, op, useLowQualityScale);
    144 }
    145 
    146 void ImageBuffer::drawPattern(GraphicsContext* destContext, const FloatRect& srcRect, const AffineTransform& patternTransform,
    147                               const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect)
    148 {
    149     if (destContext == context()) {
    150         // We're drawing into our own buffer.  In order for this to work, we need to copy the source buffer first.
    151         RefPtr<Image> copy = copyImage();
    152         copy->drawPattern(destContext, srcRect, patternTransform, phase, styleColorSpace, op, destRect);
    153     } else
    154         m_data.m_image->drawPattern(destContext, srcRect, patternTransform, phase, styleColorSpace, op, destRect);
    155 }
    156 
    157 void ImageBuffer::clip(GraphicsContext* context, const FloatRect& floatRect) const
    158 {
    159     QPixmap* nativeImage = m_data.m_image->nativeImageForCurrentFrame();
    160     if (!nativeImage)
    161         return;
    162 
    163     IntRect rect = enclosingIntRect(floatRect);
    164     QPixmap alphaMask = *nativeImage;
    165     if (alphaMask.width() != rect.width() || alphaMask.height() != rect.height())
    166         alphaMask = alphaMask.scaled(rect.width(), rect.height());
    167 
    168     context->pushTransparencyLayerInternal(rect, 1.0, alphaMask);
    169 }
    170 
    171 void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable)
    172 {
    173     bool isPainting = m_data.m_painter->isActive();
    174     if (isPainting)
    175         m_data.m_painter->end();
    176 
    177     QImage image = m_data.toQImage().convertToFormat(QImage::Format_ARGB32);
    178     ASSERT(!image.isNull());
    179 
    180     uchar* bits = image.bits();
    181     const int bytesPerLine = image.bytesPerLine();
    182 
    183     for (int y = 0; y < m_size.height(); ++y) {
    184         quint32* scanLine = reinterpret_cast_ptr<quint32*>(bits + y * bytesPerLine);
    185         for (int x = 0; x < m_size.width(); ++x) {
    186             QRgb& pixel = scanLine[x];
    187             pixel = qRgba(lookUpTable[qRed(pixel)],
    188                           lookUpTable[qGreen(pixel)],
    189                           lookUpTable[qBlue(pixel)],
    190                           qAlpha(pixel));
    191         }
    192     }
    193 
    194     m_data.m_pixmap = QPixmap::fromImage(image);
    195 
    196     if (isPainting)
    197         m_data.m_painter->begin(&m_data.m_pixmap);
    198 }
    199 
    200 template <Multiply multiplied>
    201 PassRefPtr<ByteArray> getImageData(const IntRect& rect, const ImageBufferData& imageData, const IntSize& size)
    202 {
    203     RefPtr<ByteArray> result = ByteArray::create(rect.width() * rect.height() * 4);
    204     unsigned char* data = result->data();
    205 
    206     if (rect.x() < 0 || rect.y() < 0 || rect.maxX() > size.width() || rect.maxY() > size.height())
    207         memset(data, 0, result->length());
    208 
    209     int originx = rect.x();
    210     int destx = 0;
    211     if (originx < 0) {
    212         destx = -originx;
    213         originx = 0;
    214     }
    215     int endx = rect.maxX();
    216     if (endx > size.width())
    217         endx = size.width();
    218     int numColumns = endx - originx;
    219 
    220     int originy = rect.y();
    221     int desty = 0;
    222     if (originy < 0) {
    223         desty = -originy;
    224         originy = 0;
    225     }
    226     int endy = rect.maxY();
    227     if (endy > size.height())
    228         endy = size.height();
    229     int numRows = endy - originy;
    230 
    231     // NOTE: For unmultiplied data, we undo the premultiplication below.
    232     QImage image = imageData.toQImage().convertToFormat(QImage::Format_ARGB32_Premultiplied);
    233 
    234     ASSERT(!image.isNull());
    235 
    236     const int bytesPerLine = image.bytesPerLine();
    237     const uchar* bits = image.constBits();
    238 
    239     quint32* destRows = reinterpret_cast_ptr<quint32*>(&data[desty * rect.width() * 4 + destx * 4]);
    240 
    241     if (multiplied == Unmultiplied) {
    242         for (int y = 0; y < numRows; ++y) {
    243             const quint32* scanLine = reinterpret_cast_ptr<const quint32*>(bits + (y + originy) * bytesPerLine);
    244             for (int x = 0; x < numColumns; x++) {
    245                 QRgb pixel = scanLine[x + originx];
    246                 int alpha = qAlpha(pixel);
    247                 // Un-premultiply and convert RGB to BGR.
    248                 if (alpha == 255)
    249                     destRows[x] = (0xFF000000
    250                                 | (qBlue(pixel) << 16)
    251                                 | (qGreen(pixel) << 8)
    252                                 | (qRed(pixel)));
    253                 else if (alpha > 0)
    254                     destRows[x] = ((alpha << 24)
    255                                 | (((255 * qBlue(pixel)) / alpha)) << 16)
    256                                 | (((255 * qGreen(pixel)) / alpha) << 8)
    257                                 | ((255 * qRed(pixel)) / alpha);
    258                 else
    259                     destRows[x] = 0;
    260             }
    261             destRows += rect.width();
    262         }
    263     } else {
    264         for (int y = 0; y < numRows; ++y) {
    265             const quint32* scanLine = reinterpret_cast_ptr<const quint32*>(bits + (y + originy) * bytesPerLine);
    266             for (int x = 0; x < numColumns; x++) {
    267                 QRgb pixel = scanLine[x + originx];
    268                 // Convert RGB to BGR.
    269                 destRows[x] = ((pixel << 16) & 0xff0000) | ((pixel >> 16) & 0xff) | (pixel & 0xff00ff00);
    270 
    271             }
    272             destRows += rect.width();
    273         }
    274     }
    275 
    276     return result.release();
    277 }
    278 
    279 PassRefPtr<ByteArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect) const
    280 {
    281     return getImageData<Unmultiplied>(rect, m_data, m_size);
    282 }
    283 
    284 PassRefPtr<ByteArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect) const
    285 {
    286     return getImageData<Premultiplied>(rect, m_data, m_size);
    287 }
    288 
    289 static inline unsigned int premultiplyABGRtoARGB(unsigned int x)
    290 {
    291     unsigned int a = x >> 24;
    292     if (a == 255)
    293         return (x << 16) | ((x >> 16) & 0xff) | (x & 0xff00ff00);
    294     unsigned int t = (x & 0xff00ff) * a;
    295     t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8;
    296     t = ((t << 16) | (t >> 16)) & 0xff00ff;
    297 
    298     x = ((x >> 8) & 0xff) * a;
    299     x = (x + ((x >> 8) & 0xff) + 0x80);
    300     x &= 0xff00;
    301     x |= t | (a << 24);
    302     return x;
    303 }
    304 
    305 template <Multiply multiplied>
    306 void putImageData(ByteArray*& source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, ImageBufferData& data, const IntSize& size)
    307 {
    308     ASSERT(sourceRect.width() > 0);
    309     ASSERT(sourceRect.height() > 0);
    310 
    311     int originx = sourceRect.x();
    312     int destx = destPoint.x() + sourceRect.x();
    313     ASSERT(destx >= 0);
    314     ASSERT(destx < size.width());
    315     ASSERT(originx >= 0);
    316     ASSERT(originx <= sourceRect.maxX());
    317 
    318     int endx = destPoint.x() + sourceRect.maxX();
    319     ASSERT(endx <= size.width());
    320 
    321     int numColumns = endx - destx;
    322 
    323     int originy = sourceRect.y();
    324     int desty = destPoint.y() + sourceRect.y();
    325     ASSERT(desty >= 0);
    326     ASSERT(desty < size.height());
    327     ASSERT(originy >= 0);
    328     ASSERT(originy <= sourceRect.maxY());
    329 
    330     int endy = destPoint.y() + sourceRect.maxY();
    331     ASSERT(endy <= size.height());
    332     int numRows = endy - desty;
    333 
    334     unsigned srcBytesPerRow = 4 * sourceSize.width();
    335 
    336     // NOTE: For unmultiplied input data, we do the premultiplication below.
    337     QImage image(numColumns, numRows, QImage::Format_ARGB32_Premultiplied);
    338     uchar* bits = image.bits();
    339     const int bytesPerLine = image.bytesPerLine();
    340 
    341     const quint32* srcScanLine = reinterpret_cast_ptr<const quint32*>(source->data() + originy * srcBytesPerRow + originx * 4);
    342 
    343     if (multiplied == Unmultiplied) {
    344         for (int y = 0; y < numRows; ++y) {
    345             quint32* destScanLine = reinterpret_cast_ptr<quint32*>(bits + y * bytesPerLine);
    346             for (int x = 0; x < numColumns; x++) {
    347                 // Premultiply and convert BGR to RGB.
    348                 quint32 pixel = srcScanLine[x];
    349                 destScanLine[x] = premultiplyABGRtoARGB(pixel);
    350             }
    351             srcScanLine += sourceSize.width();
    352         }
    353     } else {
    354         for (int y = 0; y < numRows; ++y) {
    355             quint32* destScanLine = reinterpret_cast_ptr<quint32*>(bits + y * bytesPerLine);
    356             for (int x = 0; x < numColumns; x++) {
    357                 // Convert BGR to RGB.
    358                 quint32 pixel = srcScanLine[x];
    359                 destScanLine[x] = ((pixel << 16) & 0xff0000) | ((pixel >> 16) & 0xff) | (pixel & 0xff00ff00);
    360             }
    361             srcScanLine += sourceSize.width();
    362         }
    363     }
    364 
    365     bool isPainting = data.m_painter->isActive();
    366     if (!isPainting)
    367         data.m_painter->begin(&data.m_pixmap);
    368     else {
    369         data.m_painter->save();
    370 
    371         // putImageData() should be unaffected by painter state
    372         data.m_painter->resetTransform();
    373         data.m_painter->setOpacity(1.0);
    374         data.m_painter->setClipping(false);
    375     }
    376 
    377     data.m_painter->setCompositionMode(QPainter::CompositionMode_Source);
    378     data.m_painter->drawImage(destx, desty, image);
    379 
    380     if (!isPainting)
    381         data.m_painter->end();
    382     else
    383         data.m_painter->restore();
    384 }
    385 
    386 void ImageBuffer::putUnmultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint)
    387 {
    388     putImageData<Unmultiplied>(source, sourceSize, sourceRect, destPoint, m_data, m_size);
    389 }
    390 
    391 void ImageBuffer::putPremultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint)
    392 {
    393     putImageData<Premultiplied>(source, sourceSize, sourceRect, destPoint, m_data, m_size);
    394 }
    395 
    396 // We get a mimeType here but QImageWriter does not support mimetypes but
    397 // only formats (png, gif, jpeg..., xpm). So assume we get image/ as image
    398 // mimetypes and then remove the image/ to get the Qt format.
    399 String ImageBuffer::toDataURL(const String& mimeType, const double* quality) const
    400 {
    401     ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
    402 
    403     if (!mimeType.startsWith("image/"))
    404         return "data:,";
    405 
    406     // prepare our target
    407     QByteArray data;
    408     QBuffer buffer(&data);
    409     buffer.open(QBuffer::WriteOnly);
    410 
    411     if (quality && *quality >= 0.0 && *quality <= 1.0) {
    412         if (!m_data.m_pixmap.save(&buffer, mimeType.substring(sizeof "image").utf8().data(), *quality * 100 + 0.5)) {
    413             buffer.close();
    414             return "data:,";
    415         }
    416     } else {
    417         if (!m_data.m_pixmap.save(&buffer, mimeType.substring(sizeof "image").utf8().data(), 100)) {
    418             buffer.close();
    419             return "data:,";
    420         }
    421     }
    422 
    423     buffer.close();
    424 
    425     return makeString("data:", mimeType, ";base64,", data.toBase64().data());
    426 }
    427 
    428 }
    429