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 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "config.h" 29 #include "ImageBuffer.h" 30 31 #include "CString.h" 32 #include "GraphicsContext.h" 33 #include "ImageData.h" 34 #include "MIMETypeRegistry.h" 35 #include "StillImageQt.h" 36 37 #include <QBuffer> 38 #include <QColor> 39 #include <QImage> 40 #include <QImageWriter> 41 #include <QPainter> 42 #include <QPixmap> 43 #include <math.h> 44 45 namespace WebCore { 46 47 ImageBufferData::ImageBufferData(const IntSize& size) 48 : m_pixmap(size) 49 { 50 m_pixmap.fill(QColor(Qt::transparent)); 51 52 QPainter* painter = new QPainter(&m_pixmap); 53 m_painter.set(painter); 54 55 // Since ImageBuffer is used mainly for Canvas, explicitly initialize 56 // its painter's pen and brush with the corresponding canvas defaults 57 // NOTE: keep in sync with CanvasRenderingContext2D::State 58 QPen pen = painter->pen(); 59 pen.setColor(Qt::black); 60 pen.setWidth(1); 61 pen.setCapStyle(Qt::FlatCap); 62 pen.setJoinStyle(Qt::MiterJoin); 63 pen.setMiterLimit(10); 64 painter->setPen(pen); 65 QBrush brush = painter->brush(); 66 brush.setColor(Qt::black); 67 painter->setBrush(brush); 68 painter->setCompositionMode(QPainter::CompositionMode_SourceOver); 69 } 70 71 ImageBuffer::ImageBuffer(const IntSize& size, ImageColorSpace, bool& success) 72 : m_data(size) 73 , m_size(size) 74 { 75 m_context.set(new GraphicsContext(m_data.m_painter.get())); 76 success = true; 77 } 78 79 ImageBuffer::~ImageBuffer() 80 { 81 } 82 83 GraphicsContext* ImageBuffer::context() const 84 { 85 ASSERT(m_data.m_painter->isActive()); 86 87 return m_context.get(); 88 } 89 90 Image* ImageBuffer::image() const 91 { 92 if (!m_image) { 93 // It's assumed that if image() is called, the actual rendering to the 94 // GraphicsContext must be done. 95 ASSERT(context()); 96 m_image = StillImage::create(m_data.m_pixmap); 97 } 98 99 return m_image.get(); 100 } 101 102 void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable) 103 { 104 bool isPainting = m_data.m_painter->isActive(); 105 if (isPainting) 106 m_data.m_painter->end(); 107 108 QImage image = m_data.m_pixmap.toImage().convertToFormat(QImage::Format_ARGB32); 109 ASSERT(!image.isNull()); 110 111 for (int y = 0; y < m_size.height(); ++y) { 112 for (int x = 0; x < m_size.width(); x++) { 113 QRgb value = image.pixel(x, y); 114 value = qRgba(lookUpTable[qRed(value)], 115 lookUpTable[qGreen(value)], 116 lookUpTable[qBlue(value)], 117 qAlpha(value)); 118 image.setPixel(x, y, value); 119 } 120 } 121 122 m_data.m_pixmap = QPixmap::fromImage(image); 123 124 if (isPainting) 125 m_data.m_painter->begin(&m_data.m_pixmap); 126 } 127 128 template <Multiply multiplied> 129 PassRefPtr<ImageData> getImageData(const IntRect& rect, const ImageBufferData& imageData, const IntSize& size) 130 { 131 PassRefPtr<ImageData> result = ImageData::create(rect.width(), rect.height()); 132 unsigned char* data = result->data()->data()->data(); 133 134 if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > size.width() || (rect.y() + rect.height()) > size.height()) 135 memset(data, 0, result->data()->length()); 136 137 int originx = rect.x(); 138 int destx = 0; 139 if (originx < 0) { 140 destx = -originx; 141 originx = 0; 142 } 143 int endx = rect.x() + rect.width(); 144 if (endx > size.width()) 145 endx = size.width(); 146 int numColumns = endx - originx; 147 148 int originy = rect.y(); 149 int desty = 0; 150 if (originy < 0) { 151 desty = -originy; 152 originy = 0; 153 } 154 int endy = rect.y() + rect.height(); 155 if (endy > size.height()) 156 endy = size.height(); 157 int numRows = endy - originy; 158 159 QImage image = imageData.m_pixmap.toImage(); 160 if (multiplied == Unmultiplied) 161 image = image.convertToFormat(QImage::Format_ARGB32); 162 else 163 image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); 164 165 ASSERT(!image.isNull()); 166 167 unsigned destBytesPerRow = 4 * rect.width(); 168 unsigned char* destRows = data + desty * destBytesPerRow + destx * 4; 169 for (int y = 0; y < numRows; ++y) { 170 for (int x = 0; x < numColumns; x++) { 171 QRgb value = image.pixel(x + originx, y + originy); 172 int basex = x * 4; 173 174 destRows[basex] = qRed(value); 175 destRows[basex + 1] = qGreen(value); 176 destRows[basex + 2] = qBlue(value); 177 destRows[basex + 3] = qAlpha(value); 178 } 179 destRows += destBytesPerRow; 180 } 181 182 return result; 183 } 184 185 PassRefPtr<ImageData> ImageBuffer::getUnmultipliedImageData(const IntRect& rect) const 186 { 187 return getImageData<Unmultiplied>(rect, m_data, m_size); 188 } 189 190 PassRefPtr<ImageData> ImageBuffer::getPremultipliedImageData(const IntRect& rect) const 191 { 192 return getImageData<Premultiplied>(rect, m_data, m_size); 193 } 194 195 template <Multiply multiplied> 196 void putImageData(ImageData*& source, const IntRect& sourceRect, const IntPoint& destPoint, ImageBufferData& data, const IntSize& size) 197 { 198 ASSERT(sourceRect.width() > 0); 199 ASSERT(sourceRect.height() > 0); 200 201 int originx = sourceRect.x(); 202 int destx = destPoint.x() + sourceRect.x(); 203 ASSERT(destx >= 0); 204 ASSERT(destx < size.width()); 205 ASSERT(originx >= 0); 206 ASSERT(originx <= sourceRect.right()); 207 208 int endx = destPoint.x() + sourceRect.right(); 209 ASSERT(endx <= size.width()); 210 211 int numColumns = endx - destx; 212 213 int originy = sourceRect.y(); 214 int desty = destPoint.y() + sourceRect.y(); 215 ASSERT(desty >= 0); 216 ASSERT(desty < size.height()); 217 ASSERT(originy >= 0); 218 ASSERT(originy <= sourceRect.bottom()); 219 220 int endy = destPoint.y() + sourceRect.bottom(); 221 ASSERT(endy <= size.height()); 222 int numRows = endy - desty; 223 224 unsigned srcBytesPerRow = 4 * source->width(); 225 226 bool isPainting = data.m_painter->isActive(); 227 if (isPainting) 228 data.m_painter->end(); 229 230 QImage image = data.m_pixmap.toImage(); 231 if (multiplied == Unmultiplied) 232 image = image.convertToFormat(QImage::Format_ARGB32); 233 else 234 image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); 235 236 unsigned char* srcRows = source->data()->data()->data() + originy * srcBytesPerRow + originx * 4; 237 for (int y = 0; y < numRows; ++y) { 238 quint32* scanLine = reinterpret_cast<quint32*>(image.scanLine(y + desty)); 239 for (int x = 0; x < numColumns; x++) { 240 // ImageData stores the pixels in RGBA while QImage is ARGB 241 quint32 pixel = reinterpret_cast<quint32*>(srcRows + 4 * x)[0]; 242 pixel = ((pixel << 16) & 0xff0000) | ((pixel >> 16) & 0xff) | (pixel & 0xff00ff00); 243 scanLine[x + destx] = pixel; 244 } 245 246 srcRows += srcBytesPerRow; 247 } 248 249 data.m_pixmap = QPixmap::fromImage(image); 250 251 if (isPainting) 252 data.m_painter->begin(&data.m_pixmap); 253 } 254 255 void ImageBuffer::putUnmultipliedImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint) 256 { 257 putImageData<Unmultiplied>(source, sourceRect, destPoint, m_data, m_size); 258 } 259 260 void ImageBuffer::putPremultipliedImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint) 261 { 262 putImageData<Premultiplied>(source, sourceRect, destPoint, m_data, m_size); 263 } 264 265 // We get a mimeType here but QImageWriter does not support mimetypes but 266 // only formats (png, gif, jpeg..., xpm). So assume we get image/ as image 267 // mimetypes and then remove the image/ to get the Qt format. 268 String ImageBuffer::toDataURL(const String& mimeType) const 269 { 270 ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)); 271 272 if (!mimeType.startsWith("image/")) 273 return "data:,"; 274 275 // prepare our target 276 QByteArray data; 277 QBuffer buffer(&data); 278 buffer.open(QBuffer::WriteOnly); 279 280 if (!m_data.m_pixmap.save(&buffer, mimeType.substring(sizeof "image").utf8().data())) 281 return "data:,"; 282 283 buffer.close(); 284 return String::format("data:%s;base64,%s", mimeType.utf8().data(), data.toBase64().data()); 285 } 286 287 } 288