[Qt] Make ImageBufferQt use premultiplied converting functions in Color.h instead...
[WebKit-https.git] / Source / WebCore / platform / graphics / qt / ImageBufferQt.cpp
1 /*
2  * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2008 Holger Hans Peter Freyther
4  * Copyright (C) 2009 Dirk Schulze <krit@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/WTFString.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 {
53     if (m_pixmap.isNull())
54         return;
55
56     m_pixmap.fill(QColor(Qt::transparent));
57
58     QPainter* painter = new QPainter;
59     m_painter = adoptPtr(painter);
60
61     if (!painter->begin(&m_pixmap))
62         return;
63
64     // Since ImageBuffer is used mainly for Canvas, explicitly initialize
65     // its painter's pen and brush with the corresponding canvas defaults
66     // NOTE: keep in sync with CanvasRenderingContext2D::State
67     QPen pen = painter->pen();
68     pen.setColor(Qt::black);
69     pen.setWidth(1);
70     pen.setCapStyle(Qt::FlatCap);
71     pen.setJoinStyle(Qt::SvgMiterJoin);
72     pen.setMiterLimit(10);
73     painter->setPen(pen);
74     QBrush brush = painter->brush();
75     brush.setColor(Qt::black);
76     painter->setBrush(brush);
77     painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
78     
79     m_image = StillImage::createForRendering(&m_pixmap);
80 }
81
82 QImage ImageBufferData::toQImage() const
83 {
84     QPaintEngine* paintEngine = m_pixmap.paintEngine();
85     if (!paintEngine || paintEngine->type() != QPaintEngine::Raster)
86         return m_pixmap.toImage();
87
88     // QRasterPixmapData::toImage() will deep-copy the backing QImage if there's an active QPainter on it.
89     // For performance reasons, we don't want that here, so we temporarily redirect the paint engine.
90     QPaintDevice* currentPaintDevice = paintEngine->paintDevice();
91     paintEngine->setPaintDevice(0);
92     QImage image = m_pixmap.toImage();
93     paintEngine->setPaintDevice(currentPaintDevice);
94     return image;
95 }
96
97 ImageBuffer::ImageBuffer(const IntSize& size, float /* resolutionScale */, ColorSpace, RenderingMode, DeferralMode, bool& success)
98     : m_data(size)
99     , m_size(size)
100     , m_logicalSize(size)
101 {
102     success = m_data.m_painter && m_data.m_painter->isActive();
103     if (!success)
104         return;
105
106     m_context = adoptPtr(new GraphicsContext(m_data.m_painter.get()));
107 }
108
109 ImageBuffer::~ImageBuffer()
110 {
111 }
112
113 GraphicsContext* ImageBuffer::context() const
114 {
115     ASSERT(m_data.m_painter->isActive());
116
117     return m_context.get();
118 }
119
120 PassRefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior) const
121 {
122     if (copyBehavior == CopyBackingStore)
123         return StillImage::create(m_data.m_pixmap);
124
125     return StillImage::createForRendering(&m_data.m_pixmap);
126 }
127
128 void ImageBuffer::draw(GraphicsContext* destContext, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect,
129                        CompositeOperator op, bool useLowQualityScale)
130 {
131     if (destContext == context()) {
132         // We're drawing into our own buffer.  In order for this to work, we need to copy the source buffer first.
133         RefPtr<Image> copy = copyImage(CopyBackingStore);
134         destContext->drawImage(copy.get(), ColorSpaceDeviceRGB, destRect, srcRect, op, DoNotRespectImageOrientation, useLowQualityScale);
135     } else
136         destContext->drawImage(m_data.m_image.get(), styleColorSpace, destRect, srcRect, op, DoNotRespectImageOrientation, useLowQualityScale);
137 }
138
139 void ImageBuffer::drawPattern(GraphicsContext* destContext, const FloatRect& srcRect, const AffineTransform& patternTransform,
140                               const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect)
141 {
142     if (destContext == context()) {
143         // We're drawing into our own buffer.  In order for this to work, we need to copy the source buffer first.
144         RefPtr<Image> copy = copyImage(CopyBackingStore);
145         copy->drawPattern(destContext, srcRect, patternTransform, phase, styleColorSpace, op, destRect);
146     } else
147         m_data.m_image->drawPattern(destContext, srcRect, patternTransform, phase, styleColorSpace, op, destRect);
148 }
149
150 void ImageBuffer::clip(GraphicsContext* context, const FloatRect& floatRect) const
151 {
152     QPixmap* nativeImage = m_data.m_image->nativeImageForCurrentFrame();
153     if (!nativeImage)
154         return;
155
156     IntRect rect = enclosingIntRect(floatRect);
157     QPixmap alphaMask = *nativeImage;
158
159     context->pushTransparencyLayerInternal(rect, 1.0, alphaMask);
160 }
161
162 void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable)
163 {
164     bool isPainting = m_data.m_painter->isActive();
165     if (isPainting)
166         m_data.m_painter->end();
167
168     QImage image = m_data.toQImage().convertToFormat(QImage::Format_ARGB32);
169     ASSERT(!image.isNull());
170
171     uchar* bits = image.bits();
172     const int bytesPerLine = image.bytesPerLine();
173
174     for (int y = 0; y < m_size.height(); ++y) {
175         quint32* scanLine = reinterpret_cast_ptr<quint32*>(bits + y * bytesPerLine);
176         for (int x = 0; x < m_size.width(); ++x) {
177             QRgb& pixel = scanLine[x];
178             pixel = qRgba(lookUpTable[qRed(pixel)],
179                           lookUpTable[qGreen(pixel)],
180                           lookUpTable[qBlue(pixel)],
181                           qAlpha(pixel));
182         }
183     }
184
185     m_data.m_pixmap = QPixmap::fromImage(image);
186
187     if (isPainting)
188         m_data.m_painter->begin(&m_data.m_pixmap);
189 }
190
191 static inline quint32 convertABGRToARGB(quint32 pixel)
192 {
193     return ((pixel << 16) & 0xff0000) | ((pixel >> 16) & 0xff) | (pixel & 0xff00ff00);
194 }
195
196 static inline quint32 convertARGBToABGR(quint32 pixel)
197 {
198     return convertABGRToARGB(pixel);
199 }
200
201 template <Multiply multiplied>
202 PassRefPtr<Uint8ClampedArray> getImageData(const IntRect& rect, const ImageBufferData& imageData, const IntSize& size)
203 {
204     float area = 4.0f * rect.width() * rect.height();
205     if (area > static_cast<float>(std::numeric_limits<int>::max()))
206         return 0;
207
208     RefPtr<Uint8ClampedArray> result = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4);
209     unsigned char* data = result->data();
210
211     if (rect.x() < 0 || rect.y() < 0 || rect.maxX() > size.width() || rect.maxY() > size.height())
212         result->zeroFill();
213
214     int originx = rect.x();
215     int destx = 0;
216     if (originx < 0) {
217         destx = -originx;
218         originx = 0;
219     }
220     int endx = rect.maxX();
221     if (endx > size.width())
222         endx = size.width();
223     int numColumns = endx - originx;
224
225     int originy = rect.y();
226     int desty = 0;
227     if (originy < 0) {
228         desty = -originy;
229         originy = 0;
230     }
231     int endy = rect.maxY();
232     if (endy > size.height())
233         endy = size.height();
234     int numRows = endy - originy;
235
236     // NOTE: For unmultiplied data, we undo the premultiplication below.
237     QImage image = imageData.toQImage().convertToFormat(QImage::Format_ARGB32_Premultiplied);
238
239     ASSERT(!image.isNull());
240
241     const int bytesPerLine = image.bytesPerLine();
242     const uchar* bits = image.constBits();
243
244     quint32* destRows = reinterpret_cast_ptr<quint32*>(&data[desty * rect.width() * 4 + destx * 4]);
245     for (int y = 0; y < numRows; ++y) {
246         const quint32* scanLine = reinterpret_cast_ptr<const quint32*>(bits + (y + originy) * bytesPerLine);
247         for (int x = 0; x < numColumns; x++) {
248             QRgb pixel = scanLine[x + originx];
249             Color pixelColor;
250             if (multiplied == Unmultiplied)
251                 pixelColor = colorFromPremultipliedARGB(Color(qRed(pixel), qGreen(pixel), qBlue(pixel), qAlpha(pixel)).rgb());
252             else
253                 pixelColor = Color(qRed(pixel), qGreen(pixel), qBlue(pixel), qAlpha(pixel));
254             destRows[x] = convertARGBToABGR(pixelColor.rgb());
255         }
256         destRows += rect.width();
257     }
258
259     return result.release();
260 }
261
262 PassRefPtr<Uint8ClampedArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect, CoordinateSystem) const
263 {
264     return getImageData<Unmultiplied>(rect, m_data, m_size);
265 }
266
267 PassRefPtr<Uint8ClampedArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect, CoordinateSystem) const
268 {
269     return getImageData<Premultiplied>(rect, m_data, m_size);
270 }
271
272 void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, CoordinateSystem)
273 {
274     ASSERT(sourceRect.width() > 0);
275     ASSERT(sourceRect.height() > 0);
276
277     int originx = sourceRect.x();
278     int destx = destPoint.x() + sourceRect.x();
279     ASSERT(destx >= 0);
280     ASSERT(destx < m_size.width());
281     ASSERT(originx >= 0);
282     ASSERT(originx <= sourceRect.maxX());
283
284     int endx = destPoint.x() + sourceRect.maxX();
285     ASSERT(endx <= m_size.width());
286
287     int numColumns = endx - destx;
288
289     int originy = sourceRect.y();
290     int desty = destPoint.y() + sourceRect.y();
291     ASSERT(desty >= 0);
292     ASSERT(desty < m_size.height());
293     ASSERT(originy >= 0);
294     ASSERT(originy <= sourceRect.maxY());
295
296     int endy = destPoint.y() + sourceRect.maxY();
297     ASSERT(endy <= m_size.height());
298     int numRows = endy - desty;
299
300     unsigned srcBytesPerRow = 4 * sourceSize.width();
301
302     // NOTE: For unmultiplied input data, we do the premultiplication below.
303     QImage image(numColumns, numRows, QImage::Format_ARGB32_Premultiplied);
304     uchar* bits = image.bits();
305     const int bytesPerLine = image.bytesPerLine();
306
307     const quint32* srcScanLine = reinterpret_cast_ptr<const quint32*>(source->data() + originy * srcBytesPerRow + originx * 4);
308
309     for (int y = 0; y < numRows; ++y) {
310         quint32* destScanLine = reinterpret_cast_ptr<quint32*>(bits + y * bytesPerLine);
311         for (int x = 0; x < numColumns; x++) {
312             quint32 pixel = convertABGRToARGB(srcScanLine[x]);
313             if (multiplied == Unmultiplied)
314                 destScanLine[x] = premultipliedARGBFromColor(Color(pixel));
315             else
316                 destScanLine[x] = pixel;
317         }
318         srcScanLine += sourceSize.width();
319     }
320
321     bool isPainting = m_data.m_painter->isActive();
322     if (!isPainting)
323         m_data.m_painter->begin(&m_data.m_pixmap);
324     else {
325         m_data.m_painter->save();
326
327         // putImageData() should be unaffected by painter state
328         m_data.m_painter->resetTransform();
329         m_data.m_painter->setOpacity(1.0);
330         m_data.m_painter->setClipping(false);
331     }
332
333     m_data.m_painter->setCompositionMode(QPainter::CompositionMode_Source);
334     m_data.m_painter->drawImage(destx, desty, image);
335
336     if (!isPainting)
337         m_data.m_painter->end();
338     else
339         m_data.m_painter->restore();
340 }
341
342 static bool encodeImage(const QPixmap& pixmap, const String& format, const double* quality, QByteArray& data)
343 {
344     int compressionQuality = 100;
345     if (quality && *quality >= 0.0 && *quality <= 1.0)
346         compressionQuality = static_cast<int>(*quality * 100 + 0.5);
347
348     QBuffer buffer(&data);
349     buffer.open(QBuffer::WriteOnly);
350     bool success = pixmap.save(&buffer, format.utf8().data(), compressionQuality);
351     buffer.close();
352
353     return success;
354 }
355
356 String ImageBuffer::toDataURL(const String& mimeType, const double* quality, CoordinateSystem) const
357 {
358     ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
359
360     // QImageWriter does not support mimetypes. It does support Qt image formats (png,
361     // gif, jpeg..., xpm) so skip the image/ to get the Qt image format used to encode
362     // the m_pixmap image.
363
364     QByteArray data;
365     if (!encodeImage(m_data.m_pixmap, mimeType.substring(sizeof "image"), quality, data))
366         return "data:,";
367
368     return "data:" + mimeType + ";base64," + data.toBase64().data();
369 }
370
371 }