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 *
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
ImageBufferData(const IntSize & size)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
ImageBuffer(const IntSize & size,ImageColorSpace imageColorSpace,bool & success)71 ImageBuffer::ImageBuffer(const IntSize& size, ImageColorSpace 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
~ImageBuffer()79 ImageBuffer::~ImageBuffer()
80 {
81 }
82
context() const83 GraphicsContext* ImageBuffer::context() const
84 {
85 ASSERT(m_data.m_painter->isActive());
86
87 return m_context.get();
88 }
89
image() const90 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
platformTransformColorSpace(const Vector<int> & lookUpTable)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 lookUpTable[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
getImageData(const IntRect & rect) const128 PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const
129 {
130 PassRefPtr<ImageData> result = ImageData::create(rect.width(), rect.height());
131 unsigned char* data = result->data()->data()->data();
132
133 if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > m_size.width() || (rect.y() + rect.height()) > m_size.height())
134 memset(data, 0, result->data()->length());
135
136 int originx = rect.x();
137 int destx = 0;
138 if (originx < 0) {
139 destx = -originx;
140 originx = 0;
141 }
142 int endx = rect.x() + rect.width();
143 if (endx > m_size.width())
144 endx = m_size.width();
145 int numColumns = endx - originx;
146
147 int originy = rect.y();
148 int desty = 0;
149 if (originy < 0) {
150 desty = -originy;
151 originy = 0;
152 }
153 int endy = rect.y() + rect.height();
154 if (endy > m_size.height())
155 endy = m_size.height();
156 int numRows = endy - originy;
157
158 QImage image = m_data.m_pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
159 ASSERT(!image.isNull());
160
161 unsigned destBytesPerRow = 4 * rect.width();
162 unsigned char* destRows = data + desty * destBytesPerRow + destx * 4;
163 for (int y = 0; y < numRows; ++y) {
164 for (int x = 0; x < numColumns; x++) {
165 QRgb value = image.pixel(x + originx, y + originy);
166 int basex = x * 4;
167
168 destRows[basex] = qRed(value);
169 destRows[basex + 1] = qGreen(value);
170 destRows[basex + 2] = qBlue(value);
171 destRows[basex + 3] = qAlpha(value);
172 }
173 destRows += destBytesPerRow;
174 }
175
176 return result;
177 }
178
putImageData(ImageData * source,const IntRect & sourceRect,const IntPoint & destPoint)179 void ImageBuffer::putImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint)
180 {
181 ASSERT(sourceRect.width() > 0);
182 ASSERT(sourceRect.height() > 0);
183
184 int originx = sourceRect.x();
185 int destx = destPoint.x() + sourceRect.x();
186 ASSERT(destx >= 0);
187 ASSERT(destx < m_size.width());
188 ASSERT(originx >= 0);
189 ASSERT(originx <= sourceRect.right());
190
191 int endx = destPoint.x() + sourceRect.right();
192 ASSERT(endx <= m_size.width());
193
194 int numColumns = endx - destx;
195
196 int originy = sourceRect.y();
197 int desty = destPoint.y() + sourceRect.y();
198 ASSERT(desty >= 0);
199 ASSERT(desty < m_size.height());
200 ASSERT(originy >= 0);
201 ASSERT(originy <= sourceRect.bottom());
202
203 int endy = destPoint.y() + sourceRect.bottom();
204 ASSERT(endy <= m_size.height());
205 int numRows = endy - desty;
206
207 unsigned srcBytesPerRow = 4 * source->width();
208
209 bool isPainting = m_data.m_painter->isActive();
210 if (isPainting)
211 m_data.m_painter->end();
212
213 QImage image = m_data.m_pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
214
215 unsigned char* srcRows = source->data()->data()->data() + originy * srcBytesPerRow + originx * 4;
216 for (int y = 0; y < numRows; ++y) {
217 quint32* scanLine = reinterpret_cast<quint32*>(image.scanLine(y + desty));
218 for (int x = 0; x < numColumns; x++) {
219 int basex = x * 4;
220 scanLine[x + destx] = reinterpret_cast<quint32*>(srcRows + basex)[0];
221 }
222
223 srcRows += srcBytesPerRow;
224 }
225
226 m_data.m_pixmap = QPixmap::fromImage(image);
227
228 if (isPainting)
229 m_data.m_painter->begin(&m_data.m_pixmap);
230 }
231
232 // We get a mimeType here but QImageWriter does not support mimetypes but
233 // only formats (png, gif, jpeg..., xpm). So assume we get image/ as image
234 // mimetypes and then remove the image/ to get the Qt format.
toDataURL(const String & mimeType) const235 String ImageBuffer::toDataURL(const String& mimeType) const
236 {
237 ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
238
239 if (!mimeType.startsWith("image/"))
240 return "data:,";
241
242 // prepare our target
243 QByteArray data;
244 QBuffer buffer(&data);
245 buffer.open(QBuffer::WriteOnly);
246
247 if (!m_data.m_pixmap.save(&buffer, mimeType.substring(sizeof "image").utf8().data()))
248 return "data:,";
249
250 buffer.close();
251 return String::format("data:%s;base64,%s", mimeType.utf8().data(), data.toBase64().data());
252 }
253
254 }
255