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