eacdc7e9d57988b43190574ac36afc16979b7059
[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 "NativeImageQt.h"
36 #include "StillImageQt.h"
37 #include "TransparencyLayer.h"
38 #include <wtf/text/CString.h>
39 #include <wtf/text/WTFString.h>
40
41 #include <QBuffer>
42 #include <QColor>
43 #include <QImage>
44 #include <QImageWriter>
45 #include <QPainter>
46 #include <math.h>
47
48 namespace WebCore {
49
50 ImageBufferData::ImageBufferData(const IntSize& size)
51     : m_nativeImage(size, NativeImageQt::defaultFormatForAlphaEnabledImages())
52 {
53     if (m_nativeImage.isNull())
54         return;
55
56     m_nativeImage.fill(QColor(Qt::transparent));
57
58     QPainter* painter = new QPainter;
59     m_painter = adoptPtr(painter);
60
61     if (!painter->begin(&m_nativeImage))
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_nativeImage);
80 }
81
82 ImageBuffer::ImageBuffer(const IntSize& size, float /* resolutionScale */, ColorSpace, RenderingMode, DeferralMode, bool& success)
83     : m_data(size)
84     , m_size(size)
85     , m_logicalSize(size)
86 {
87     success = m_data.m_painter && m_data.m_painter->isActive();
88     if (!success)
89         return;
90
91     m_context = adoptPtr(new GraphicsContext(m_data.m_painter.get()));
92 }
93
94 ImageBuffer::~ImageBuffer()
95 {
96 }
97
98 GraphicsContext* ImageBuffer::context() const
99 {
100     ASSERT(m_data.m_painter->isActive());
101
102     return m_context.get();
103 }
104
105 PassRefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior) const
106 {
107     if (copyBehavior == CopyBackingStore)
108         return StillImage::create(m_data.m_nativeImage);
109
110     return StillImage::createForRendering(&m_data.m_nativeImage);
111 }
112
113 void ImageBuffer::draw(GraphicsContext* destContext, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect,
114                        CompositeOperator op, bool useLowQualityScale)
115 {
116     if (destContext == context()) {
117         // We're drawing into our own buffer.  In order for this to work, we need to copy the source buffer first.
118         RefPtr<Image> copy = copyImage(CopyBackingStore);
119         destContext->drawImage(copy.get(), ColorSpaceDeviceRGB, destRect, srcRect, op, DoNotRespectImageOrientation, useLowQualityScale);
120     } else
121         destContext->drawImage(m_data.m_image.get(), styleColorSpace, destRect, srcRect, op, DoNotRespectImageOrientation, useLowQualityScale);
122 }
123
124 void ImageBuffer::drawPattern(GraphicsContext* destContext, const FloatRect& srcRect, const AffineTransform& patternTransform,
125                               const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect)
126 {
127     if (destContext == context()) {
128         // We're drawing into our own buffer.  In order for this to work, we need to copy the source buffer first.
129         RefPtr<Image> copy = copyImage(CopyBackingStore);
130         copy->drawPattern(destContext, srcRect, patternTransform, phase, styleColorSpace, op, destRect);
131     } else
132         m_data.m_image->drawPattern(destContext, srcRect, patternTransform, phase, styleColorSpace, op, destRect);
133 }
134
135 void ImageBuffer::clip(GraphicsContext* context, const FloatRect& floatRect) const
136 {
137     QImage* nativeImage = m_data.m_image->nativeImageForCurrentFrame();
138     if (!nativeImage)
139         return;
140
141     IntRect rect = enclosingIntRect(floatRect);
142     QImage alphaMask = *nativeImage;
143
144     context->pushTransparencyLayerInternal(rect, 1.0, alphaMask);
145 }
146
147 void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable)
148 {
149     bool isPainting = m_data.m_painter->isActive();
150     if (isPainting)
151         m_data.m_painter->end();
152
153     QImage image = m_data.m_nativeImage;
154     ASSERT(!image.isNull());
155
156     uchar* bits = image.bits();
157     const int bytesPerLine = image.bytesPerLine();
158
159     for (int y = 0; y < m_size.height(); ++y) {
160         quint32* scanLine = reinterpret_cast_ptr<quint32*>(bits + y * bytesPerLine);
161         for (int x = 0; x < m_size.width(); ++x) {
162             QRgb& pixel = scanLine[x];
163             pixel = qRgba(lookUpTable[qRed(pixel)],
164                           lookUpTable[qGreen(pixel)],
165                           lookUpTable[qBlue(pixel)],
166                           qAlpha(pixel));
167         }
168     }
169
170     m_data.m_nativeImage = image;
171
172     if (isPainting)
173         m_data.m_painter->begin(&m_data.m_nativeImage);
174 }
175
176 template <Multiply multiplied>
177 PassRefPtr<Uint8ClampedArray> getImageData(const IntRect& rect, const ImageBufferData& imageData, const IntSize& size)
178 {
179     float area = 4.0f * rect.width() * rect.height();
180     if (area > static_cast<float>(std::numeric_limits<int>::max()))
181         return 0;
182
183     RefPtr<Uint8ClampedArray> result = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4);
184     unsigned char* data = result->data();
185
186     if (rect.x() < 0 || rect.y() < 0 || rect.maxX() > size.width() || rect.maxY() > size.height())
187         result->zeroFill();
188
189     int originx = rect.x();
190     int destx = 0;
191     if (originx < 0) {
192         destx = -originx;
193         originx = 0;
194     }
195     int endx = rect.maxX();
196     if (endx > size.width())
197         endx = size.width();
198     int numColumns = endx - originx;
199
200     int originy = rect.y();
201     int desty = 0;
202     if (originy < 0) {
203         desty = -originy;
204         originy = 0;
205     }
206     int endy = rect.maxY();
207     if (endy > size.height())
208         endy = size.height();
209     int numRows = endy - originy;
210
211     // NOTE: For unmultiplied data, we undo the premultiplication below.
212     QImage image = imageData.m_nativeImage.convertToFormat(NativeImageQt::defaultFormatForAlphaEnabledImages());
213
214     ASSERT(!image.isNull());
215
216     const int bytesPerLine = image.bytesPerLine();
217     const uchar* bits = image.constBits();
218
219     quint32* destRows = reinterpret_cast_ptr<quint32*>(&data[desty * rect.width() * 4 + destx * 4]);
220
221     if (multiplied == Unmultiplied) {
222         for (int y = 0; y < numRows; ++y) {
223             const quint32* scanLine = reinterpret_cast_ptr<const quint32*>(bits + (y + originy) * bytesPerLine);
224             for (int x = 0; x < numColumns; x++) {
225                 QRgb pixel = scanLine[x + originx];
226                 int alpha = qAlpha(pixel);
227                 // Un-premultiply and convert RGB to BGR.
228                 if (alpha == 255)
229                     destRows[x] = (0xFF000000
230                                 | (qBlue(pixel) << 16)
231                                 | (qGreen(pixel) << 8)
232                                 | (qRed(pixel)));
233                 else if (alpha > 0)
234                     destRows[x] = ((alpha << 24)
235                                 | (((255 * qBlue(pixel)) / alpha)) << 16)
236                                 | (((255 * qGreen(pixel)) / alpha) << 8)
237                                 | ((255 * qRed(pixel)) / alpha);
238                 else
239                     destRows[x] = 0;
240             }
241             destRows += rect.width();
242         }
243     } else {
244         for (int y = 0; y < numRows; ++y) {
245             const quint32* scanLine = reinterpret_cast_ptr<const quint32*>(bits + (y + originy) * bytesPerLine);
246             for (int x = 0; x < numColumns; x++) {
247                 QRgb pixel = scanLine[x + originx];
248                 // Convert RGB to BGR.
249                 destRows[x] = ((pixel << 16) & 0xff0000) | ((pixel >> 16) & 0xff) | (pixel & 0xff00ff00);
250
251             }
252             destRows += rect.width();
253         }
254     }
255
256     return result.release();
257 }
258
259 PassRefPtr<Uint8ClampedArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect, CoordinateSystem) const
260 {
261     return getImageData<Unmultiplied>(rect, m_data, m_size);
262 }
263
264 PassRefPtr<Uint8ClampedArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect, CoordinateSystem) const
265 {
266     return getImageData<Premultiplied>(rect, m_data, m_size);
267 }
268
269 static inline unsigned int premultiplyABGRtoARGB(unsigned int x)
270 {
271     unsigned int a = x >> 24;
272     if (a == 255)
273         return (x << 16) | ((x >> 16) & 0xff) | (x & 0xff00ff00);
274     unsigned int t = (x & 0xff00ff) * a;
275     t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8;
276     t = ((t << 16) | (t >> 16)) & 0xff00ff;
277
278     x = ((x >> 8) & 0xff) * a;
279     x = (x + ((x >> 8) & 0xff) + 0x80);
280     x &= 0xff00;
281     x |= t | (a << 24);
282     return x;
283 }
284
285 void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, CoordinateSystem)
286 {
287     ASSERT(sourceRect.width() > 0);
288     ASSERT(sourceRect.height() > 0);
289
290     int originx = sourceRect.x();
291     int destx = destPoint.x() + sourceRect.x();
292     ASSERT(destx >= 0);
293     ASSERT(destx < m_size.width());
294     ASSERT(originx >= 0);
295     ASSERT(originx <= sourceRect.maxX());
296
297     int endx = destPoint.x() + sourceRect.maxX();
298     ASSERT(endx <= m_size.width());
299
300     int numColumns = endx - destx;
301
302     int originy = sourceRect.y();
303     int desty = destPoint.y() + sourceRect.y();
304     ASSERT(desty >= 0);
305     ASSERT(desty < m_size.height());
306     ASSERT(originy >= 0);
307     ASSERT(originy <= sourceRect.maxY());
308
309     int endy = destPoint.y() + sourceRect.maxY();
310     ASSERT(endy <= m_size.height());
311     int numRows = endy - desty;
312
313     unsigned srcBytesPerRow = 4 * sourceSize.width();
314
315     // NOTE: For unmultiplied input data, we do the premultiplication below.
316     QImage image(numColumns, numRows, QImage::Format_ARGB32_Premultiplied);
317     uchar* bits = image.bits();
318     const int bytesPerLine = image.bytesPerLine();
319
320     const quint32* srcScanLine = reinterpret_cast_ptr<const quint32*>(source->data() + originy * srcBytesPerRow + originx * 4);
321
322     if (multiplied == Unmultiplied) {
323         for (int y = 0; y < numRows; ++y) {
324             quint32* destScanLine = reinterpret_cast_ptr<quint32*>(bits + y * bytesPerLine);
325             for (int x = 0; x < numColumns; x++) {
326                 // Premultiply and convert BGR to RGB.
327                 quint32 pixel = srcScanLine[x];
328                 destScanLine[x] = premultiplyABGRtoARGB(pixel);
329             }
330             srcScanLine += sourceSize.width();
331         }
332     } else {
333         for (int y = 0; y < numRows; ++y) {
334             quint32* destScanLine = reinterpret_cast_ptr<quint32*>(bits + y * bytesPerLine);
335             for (int x = 0; x < numColumns; x++) {
336                 // Convert BGR to RGB.
337                 quint32 pixel = srcScanLine[x];
338                 destScanLine[x] = ((pixel << 16) & 0xff0000) | ((pixel >> 16) & 0xff) | (pixel & 0xff00ff00);
339             }
340             srcScanLine += sourceSize.width();
341         }
342     }
343
344     bool isPainting = m_data.m_painter->isActive();
345     if (!isPainting)
346         m_data.m_painter->begin(&m_data.m_nativeImage);
347     else {
348         m_data.m_painter->save();
349
350         // putImageData() should be unaffected by painter state
351         m_data.m_painter->resetTransform();
352         m_data.m_painter->setOpacity(1.0);
353         m_data.m_painter->setClipping(false);
354     }
355
356     m_data.m_painter->setCompositionMode(QPainter::CompositionMode_Source);
357     m_data.m_painter->drawImage(destx, desty, image);
358
359     if (!isPainting)
360         m_data.m_painter->end();
361     else
362         m_data.m_painter->restore();
363 }
364
365 static bool encodeImage(const QImage& image, const String& format, const double* quality, QByteArray& data)
366 {
367     int compressionQuality = 100;
368     if (quality && *quality >= 0.0 && *quality <= 1.0)
369         compressionQuality = static_cast<int>(*quality * 100 + 0.5);
370
371     QBuffer buffer(&data);
372     buffer.open(QBuffer::WriteOnly);
373     bool success = image.save(&buffer, format.utf8().data(), compressionQuality);
374     buffer.close();
375
376     return success;
377 }
378
379 String ImageBuffer::toDataURL(const String& mimeType, const double* quality, CoordinateSystem) const
380 {
381     ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
382
383     // QImageWriter does not support mimetypes. It does support Qt image formats (png,
384     // gif, jpeg..., xpm) so skip the image/ to get the Qt image format used to encode
385     // the m_nativeImage image.
386
387     QByteArray data;
388     if (!encodeImage(m_data.m_nativeImage, mimeType.substring(sizeof "image"), quality, data))
389         return "data:,";
390
391     return "data:" + mimeType + ";base64," + data.toBase64().data();
392 }
393
394 }