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