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/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 
ImageBufferData(const IntSize & size)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 
toQImage() const82 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 
ImageBuffer(const IntSize & size,ColorSpace,RenderingMode,bool & success)97 ImageBuffer::ImageBuffer(const IntSize& size, ColorSpace, RenderingMode, bool& success)
98     : m_data(size)
99     , m_size(size)
100 {
101     success = m_data.m_painter && m_data.m_painter->isActive();
102     if (!success)
103         return;
104 
105     m_context = adoptPtr(new GraphicsContext(m_data.m_painter.get()));
106 }
107 
~ImageBuffer()108 ImageBuffer::~ImageBuffer()
109 {
110 }
111 
dataSize() const112 size_t ImageBuffer::dataSize() const
113 {
114     return m_size.width() * m_size.height() * 4;
115 }
116 
context() const117 GraphicsContext* ImageBuffer::context() const
118 {
119     ASSERT(m_data.m_painter->isActive());
120 
121     return m_context.get();
122 }
123 
drawsUsingCopy() const124 bool ImageBuffer::drawsUsingCopy() const
125 {
126     return false;
127 }
128 
copyImage() const129 PassRefPtr<Image> ImageBuffer::copyImage() const
130 {
131     return StillImage::create(m_data.m_pixmap);
132 }
133 
draw(GraphicsContext * destContext,ColorSpace styleColorSpace,const FloatRect & destRect,const FloatRect & srcRect,CompositeOperator op,bool useLowQualityScale)134 void ImageBuffer::draw(GraphicsContext* destContext, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect,
135                        CompositeOperator op, bool useLowQualityScale)
136 {
137     if (destContext == context()) {
138         // We're drawing into our own buffer.  In order for this to work, we need to copy the source buffer first.
139         RefPtr<Image> copy = copyImage();
140         destContext->drawImage(copy.get(), ColorSpaceDeviceRGB, destRect, srcRect, op, useLowQualityScale);
141     } else
142         destContext->drawImage(m_data.m_image.get(), styleColorSpace, destRect, srcRect, op, useLowQualityScale);
143 }
144 
drawPattern(GraphicsContext * destContext,const FloatRect & srcRect,const AffineTransform & patternTransform,const FloatPoint & phase,ColorSpace styleColorSpace,CompositeOperator op,const FloatRect & destRect)145 void ImageBuffer::drawPattern(GraphicsContext* destContext, const FloatRect& srcRect, const AffineTransform& patternTransform,
146                               const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect)
147 {
148     if (destContext == context()) {
149         // We're drawing into our own buffer.  In order for this to work, we need to copy the source buffer first.
150         RefPtr<Image> copy = copyImage();
151         copy->drawPattern(destContext, srcRect, patternTransform, phase, styleColorSpace, op, destRect);
152     } else
153         m_data.m_image->drawPattern(destContext, srcRect, patternTransform, phase, styleColorSpace, op, destRect);
154 }
155 
clip(GraphicsContext * context,const FloatRect & floatRect) const156 void ImageBuffer::clip(GraphicsContext* context, const FloatRect& floatRect) const
157 {
158     QPixmap* nativeImage = m_data.m_image->nativeImageForCurrentFrame();
159     if (!nativeImage)
160         return;
161 
162     IntRect rect = enclosingIntRect(floatRect);
163     QPixmap alphaMask = *nativeImage;
164     if (alphaMask.width() != rect.width() || alphaMask.height() != rect.height())
165         alphaMask = alphaMask.scaled(rect.width(), rect.height());
166 
167     context->pushTransparencyLayerInternal(rect, 1.0, alphaMask);
168 }
169 
platformTransformColorSpace(const Vector<int> & lookUpTable)170 void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable)
171 {
172     bool isPainting = m_data.m_painter->isActive();
173     if (isPainting)
174         m_data.m_painter->end();
175 
176     QImage image = m_data.toQImage().convertToFormat(QImage::Format_ARGB32);
177     ASSERT(!image.isNull());
178 
179     uchar* bits = image.bits();
180     const int bytesPerLine = image.bytesPerLine();
181 
182     for (int y = 0; y < m_size.height(); ++y) {
183         quint32* scanLine = reinterpret_cast_ptr<quint32*>(bits + y * bytesPerLine);
184         for (int x = 0; x < m_size.width(); ++x) {
185             QRgb& pixel = scanLine[x];
186             pixel = qRgba(lookUpTable[qRed(pixel)],
187                           lookUpTable[qGreen(pixel)],
188                           lookUpTable[qBlue(pixel)],
189                           qAlpha(pixel));
190         }
191     }
192 
193     m_data.m_pixmap = QPixmap::fromImage(image);
194 
195     if (isPainting)
196         m_data.m_painter->begin(&m_data.m_pixmap);
197 }
198 
199 template <Multiply multiplied>
getImageData(const IntRect & rect,const ImageBufferData & imageData,const IntSize & size)200 PassRefPtr<ByteArray> getImageData(const IntRect& rect, const ImageBufferData& imageData, const IntSize& size)
201 {
202     RefPtr<ByteArray> result = ByteArray::create(rect.width() * rect.height() * 4);
203     unsigned char* data = result->data();
204 
205     if (rect.x() < 0 || rect.y() < 0 || rect.maxX() > size.width() || rect.maxY() > size.height())
206         memset(data, 0, result->length());
207 
208     int originx = rect.x();
209     int destx = 0;
210     if (originx < 0) {
211         destx = -originx;
212         originx = 0;
213     }
214     int endx = rect.maxX();
215     if (endx > size.width())
216         endx = size.width();
217     int numColumns = endx - originx;
218 
219     int originy = rect.y();
220     int desty = 0;
221     if (originy < 0) {
222         desty = -originy;
223         originy = 0;
224     }
225     int endy = rect.maxY();
226     if (endy > size.height())
227         endy = size.height();
228     int numRows = endy - originy;
229 
230     // NOTE: For unmultiplied data, we undo the premultiplication below.
231     QImage image = imageData.toQImage().convertToFormat(QImage::Format_ARGB32_Premultiplied);
232 
233     ASSERT(!image.isNull());
234 
235     const int bytesPerLine = image.bytesPerLine();
236     const uchar* bits = image.constBits();
237 
238     quint32* destRows = reinterpret_cast_ptr<quint32*>(&data[desty * rect.width() * 4 + destx * 4]);
239 
240     if (multiplied == Unmultiplied) {
241         for (int y = 0; y < numRows; ++y) {
242             const quint32* scanLine = reinterpret_cast_ptr<const quint32*>(bits + (y + originy) * bytesPerLine);
243             for (int x = 0; x < numColumns; x++) {
244                 QRgb pixel = scanLine[x + originx];
245                 int alpha = qAlpha(pixel);
246                 // Un-premultiply and convert RGB to BGR.
247                 if (alpha == 255)
248                     destRows[x] = (0xFF000000
249                                 | (qBlue(pixel) << 16)
250                                 | (qGreen(pixel) << 8)
251                                 | (qRed(pixel)));
252                 else if (alpha > 0)
253                     destRows[x] = ((alpha << 24)
254                                 | (((255 * qBlue(pixel)) / alpha)) << 16)
255                                 | (((255 * qGreen(pixel)) / alpha) << 8)
256                                 | ((255 * qRed(pixel)) / alpha);
257                 else
258                     destRows[x] = 0;
259             }
260             destRows += rect.width();
261         }
262     } else {
263         for (int y = 0; y < numRows; ++y) {
264             const quint32* scanLine = reinterpret_cast_ptr<const quint32*>(bits + (y + originy) * bytesPerLine);
265             for (int x = 0; x < numColumns; x++) {
266                 QRgb pixel = scanLine[x + originx];
267                 // Convert RGB to BGR.
268                 destRows[x] = ((pixel << 16) & 0xff0000) | ((pixel >> 16) & 0xff) | (pixel & 0xff00ff00);
269 
270             }
271             destRows += rect.width();
272         }
273     }
274 
275     return result.release();
276 }
277 
getUnmultipliedImageData(const IntRect & rect) const278 PassRefPtr<ByteArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect) const
279 {
280     return getImageData<Unmultiplied>(rect, m_data, m_size);
281 }
282 
getPremultipliedImageData(const IntRect & rect) const283 PassRefPtr<ByteArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect) const
284 {
285     return getImageData<Premultiplied>(rect, m_data, m_size);
286 }
287 
premultiplyABGRtoARGB(unsigned int x)288 static inline unsigned int premultiplyABGRtoARGB(unsigned int x)
289 {
290     unsigned int a = x >> 24;
291     if (a == 255)
292         return (x << 16) | ((x >> 16) & 0xff) | (x & 0xff00ff00);
293     unsigned int t = (x & 0xff00ff) * a;
294     t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8;
295     t = ((t << 16) | (t >> 16)) & 0xff00ff;
296 
297     x = ((x >> 8) & 0xff) * a;
298     x = (x + ((x >> 8) & 0xff) + 0x80);
299     x &= 0xff00;
300     x |= t | (a << 24);
301     return x;
302 }
303 
304 template <Multiply multiplied>
putImageData(ByteArray * & source,const IntSize & sourceSize,const IntRect & sourceRect,const IntPoint & destPoint,ImageBufferData & data,const IntSize & size)305 void putImageData(ByteArray*& source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, ImageBufferData& data, const IntSize& size)
306 {
307     ASSERT(sourceRect.width() > 0);
308     ASSERT(sourceRect.height() > 0);
309 
310     int originx = sourceRect.x();
311     int destx = destPoint.x() + sourceRect.x();
312     ASSERT(destx >= 0);
313     ASSERT(destx < size.width());
314     ASSERT(originx >= 0);
315     ASSERT(originx <= sourceRect.maxX());
316 
317     int endx = destPoint.x() + sourceRect.maxX();
318     ASSERT(endx <= size.width());
319 
320     int numColumns = endx - destx;
321 
322     int originy = sourceRect.y();
323     int desty = destPoint.y() + sourceRect.y();
324     ASSERT(desty >= 0);
325     ASSERT(desty < size.height());
326     ASSERT(originy >= 0);
327     ASSERT(originy <= sourceRect.maxY());
328 
329     int endy = destPoint.y() + sourceRect.maxY();
330     ASSERT(endy <= size.height());
331     int numRows = endy - desty;
332 
333     unsigned srcBytesPerRow = 4 * sourceSize.width();
334 
335     // NOTE: For unmultiplied input data, we do the premultiplication below.
336     QImage image(numColumns, numRows, QImage::Format_ARGB32_Premultiplied);
337     uchar* bits = image.bits();
338     const int bytesPerLine = image.bytesPerLine();
339 
340     const quint32* srcScanLine = reinterpret_cast_ptr<const quint32*>(source->data() + originy * srcBytesPerRow + originx * 4);
341 
342     if (multiplied == Unmultiplied) {
343         for (int y = 0; y < numRows; ++y) {
344             quint32* destScanLine = reinterpret_cast_ptr<quint32*>(bits + y * bytesPerLine);
345             for (int x = 0; x < numColumns; x++) {
346                 // Premultiply and convert BGR to RGB.
347                 quint32 pixel = srcScanLine[x];
348                 destScanLine[x] = premultiplyABGRtoARGB(pixel);
349             }
350             srcScanLine += sourceSize.width();
351         }
352     } else {
353         for (int y = 0; y < numRows; ++y) {
354             quint32* destScanLine = reinterpret_cast_ptr<quint32*>(bits + y * bytesPerLine);
355             for (int x = 0; x < numColumns; x++) {
356                 // Convert BGR to RGB.
357                 quint32 pixel = srcScanLine[x];
358                 destScanLine[x] = ((pixel << 16) & 0xff0000) | ((pixel >> 16) & 0xff) | (pixel & 0xff00ff00);
359             }
360             srcScanLine += sourceSize.width();
361         }
362     }
363 
364     bool isPainting = data.m_painter->isActive();
365     if (!isPainting)
366         data.m_painter->begin(&data.m_pixmap);
367     else {
368         data.m_painter->save();
369 
370         // putImageData() should be unaffected by painter state
371         data.m_painter->resetTransform();
372         data.m_painter->setOpacity(1.0);
373         data.m_painter->setClipping(false);
374     }
375 
376     data.m_painter->setCompositionMode(QPainter::CompositionMode_Source);
377     data.m_painter->drawImage(destx, desty, image);
378 
379     if (!isPainting)
380         data.m_painter->end();
381     else
382         data.m_painter->restore();
383 }
384 
putUnmultipliedImageData(ByteArray * source,const IntSize & sourceSize,const IntRect & sourceRect,const IntPoint & destPoint)385 void ImageBuffer::putUnmultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint)
386 {
387     putImageData<Unmultiplied>(source, sourceSize, sourceRect, destPoint, m_data, m_size);
388 }
389 
putPremultipliedImageData(ByteArray * source,const IntSize & sourceSize,const IntRect & sourceRect,const IntPoint & destPoint)390 void ImageBuffer::putPremultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint)
391 {
392     putImageData<Premultiplied>(source, sourceSize, sourceRect, destPoint, m_data, m_size);
393 }
394 
395 // We get a mimeType here but QImageWriter does not support mimetypes but
396 // only formats (png, gif, jpeg..., xpm). So assume we get image/ as image
397 // mimetypes and then remove the image/ to get the Qt format.
toDataURL(const String & mimeType,const double * quality) const398 String ImageBuffer::toDataURL(const String& mimeType, const double* quality) const
399 {
400     ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
401 
402     if (!mimeType.startsWith("image/"))
403         return "data:,";
404 
405     // prepare our target
406     QByteArray data;
407     QBuffer buffer(&data);
408     buffer.open(QBuffer::WriteOnly);
409 
410     if (quality && *quality >= 0.0 && *quality <= 1.0) {
411         if (!m_data.m_pixmap.save(&buffer, mimeType.substring(sizeof "image").utf8().data(), *quality * 100 + 0.5)) {
412             buffer.close();
413             return "data:,";
414         }
415     } else {
416         if (!m_data.m_pixmap.save(&buffer, mimeType.substring(sizeof "image").utf8().data(), 100)) {
417             buffer.close();
418             return "data:,";
419         }
420     }
421 
422     buffer.close();
423 
424     return makeString("data:", mimeType, ";base64,", data.toBase64().data());
425 }
426 
427 }
428