1 /*
2 * Copyright (c) 2008, Google Inc. All rights reserved.
3 * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
4 * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include "config.h"
34 #include "ImageBuffer.h"
35
36 #include "Base64.h"
37 #include "BitmapImage.h"
38 #include "BitmapImageSingleFrameSkia.h"
39 #include "DrawingBuffer.h"
40 #include "GLES2Canvas.h"
41 #include "GraphicsContext.h"
42 #include "ImageData.h"
43 #include "JPEGImageEncoder.h"
44 #include "MIMETypeRegistry.h"
45 #include "PNGImageEncoder.h"
46 #include "PlatformContextSkia.h"
47 #include "SkColorPriv.h"
48 #include "SkiaUtils.h"
49
50 #include <wtf/text/StringConcatenate.h>
51
52 using namespace std;
53
54 namespace WebCore {
55
56 // We pass a technically-uninitialized canvas to the platform context here since
57 // the canvas initialization completes in ImageBuffer::ImageBuffer. But
58 // PlatformContext doesn't actually need to use the object, and this makes all
59 // the ownership easier to manage.
ImageBufferData(const IntSize & size)60 ImageBufferData::ImageBufferData(const IntSize& size)
61 : m_platformContext(0) // Canvas is set in ImageBuffer constructor.
62 {
63 }
64
ImageBuffer(const IntSize & size,ColorSpace,RenderingMode,bool & success)65 ImageBuffer::ImageBuffer(const IntSize& size, ColorSpace, RenderingMode, bool& success)
66 : m_data(size)
67 , m_size(size)
68 {
69 SkCanvas* canvas = skia::CreateBitmapCanvas(size.width(), size.height(), false);
70 if (!canvas) {
71 success = false;
72 return;
73 }
74
75 m_data.m_canvas = canvas;
76 m_data.m_platformContext.setCanvas(m_data.m_canvas.get());
77 m_context.set(new GraphicsContext(&m_data.m_platformContext));
78 m_context->platformContext()->setDrawingToImageBuffer(true);
79
80 // Make the background transparent. It would be nice if this wasn't
81 // required, but the canvas is currently filled with the magic transparency
82 // color. Can we have another way to manage this?
83 m_data.m_canvas->drawARGB(0, 0, 0, 0, SkXfermode::kClear_Mode);
84 success = true;
85 }
86
~ImageBuffer()87 ImageBuffer::~ImageBuffer()
88 {
89 }
90
context() const91 GraphicsContext* ImageBuffer::context() const
92 {
93 return m_context.get();
94 }
95
dataSize() const96 size_t ImageBuffer::dataSize() const
97 {
98 return m_size.width() * m_size.height() * 4;
99 }
100
drawsUsingCopy() const101 bool ImageBuffer::drawsUsingCopy() const
102 {
103 return false;
104 }
105
copyImage() const106 PassRefPtr<Image> ImageBuffer::copyImage() const
107 {
108 m_context->platformContext()->syncSoftwareCanvas();
109 return BitmapImageSingleFrameSkia::create(*m_data.m_platformContext.bitmap(), true);
110 }
111
clip(GraphicsContext * context,const FloatRect & rect) const112 void ImageBuffer::clip(GraphicsContext* context, const FloatRect& rect) const
113 {
114 context->platformContext()->beginLayerClippedToImage(rect, this);
115 }
116
draw(GraphicsContext * context,ColorSpace styleColorSpace,const FloatRect & destRect,const FloatRect & srcRect,CompositeOperator op,bool useLowQualityScale)117 void ImageBuffer::draw(GraphicsContext* context, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect,
118 CompositeOperator op, bool useLowQualityScale)
119 {
120 if (m_data.m_platformContext.useGPU() && context->platformContext()->useGPU()) {
121 if (context->platformContext()->canAccelerate()) {
122 m_data.m_platformContext.prepareForHardwareDraw();
123 DrawingBuffer* sourceDrawingBuffer = m_data.m_platformContext.gpuCanvas()->drawingBuffer();
124 unsigned sourceTexture = static_cast<unsigned>(sourceDrawingBuffer->platformColorBuffer());
125 FloatRect destRectNormalized(normalizeRect(destRect));
126 FloatRect srcRectFlipped(normalizeRect(srcRect));
127 srcRectFlipped.setY(m_size.height() - srcRect.y());
128 srcRectFlipped.setHeight(-srcRect.height());
129 context->platformContext()->prepareForHardwareDraw();
130 context->platformContext()->gpuCanvas()->drawTexturedRect(sourceTexture, m_size, srcRectFlipped, destRectNormalized, styleColorSpace, op);
131 return;
132 }
133 m_data.m_platformContext.syncSoftwareCanvas();
134 }
135
136 RefPtr<Image> image = BitmapImageSingleFrameSkia::create(*m_data.m_platformContext.bitmap(), context == m_context);
137 context->drawImage(image.get(), styleColorSpace, destRect, srcRect, op, useLowQualityScale);
138 }
139
drawPattern(GraphicsContext * context,const FloatRect & srcRect,const AffineTransform & patternTransform,const FloatPoint & phase,ColorSpace styleColorSpace,CompositeOperator op,const FloatRect & destRect)140 void ImageBuffer::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const AffineTransform& patternTransform,
141 const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect)
142 {
143 RefPtr<Image> image = BitmapImageSingleFrameSkia::create(*m_data.m_platformContext.bitmap(), context == m_context);
144 image->drawPattern(context, srcRect, patternTransform, phase, styleColorSpace, op, destRect);
145 }
146
platformTransformColorSpace(const Vector<int> & lookUpTable)147 void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable)
148 {
149 const SkBitmap& bitmap = *context()->platformContext()->bitmap();
150 if (bitmap.isNull())
151 return;
152
153 ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config);
154 SkAutoLockPixels bitmapLock(bitmap);
155 for (int y = 0; y < m_size.height(); ++y) {
156 uint32_t* srcRow = bitmap.getAddr32(0, y);
157 for (int x = 0; x < m_size.width(); ++x) {
158 SkColor color = SkPMColorToColor(srcRow[x]);
159 srcRow[x] = SkPreMultiplyARGB(SkColorGetA(color),
160 lookUpTable[SkColorGetR(color)],
161 lookUpTable[SkColorGetG(color)],
162 lookUpTable[SkColorGetB(color)]);
163 }
164 }
165 }
166
167 template <Multiply multiplied>
getImageData(const IntRect & rect,SkDevice & srcDevice,const IntSize & size)168 PassRefPtr<ByteArray> getImageData(const IntRect& rect, SkDevice& srcDevice,
169 const IntSize& size)
170 {
171 float area = 4.0f * rect.width() * rect.height();
172 if (area > static_cast<float>(std::numeric_limits<int>::max()))
173 return 0;
174
175 RefPtr<ByteArray> result = ByteArray::create(rect.width() * rect.height() * 4);
176
177 SkBitmap::Config srcConfig = srcDevice.accessBitmap(false).config();
178
179 if (srcConfig == SkBitmap::kNo_Config) {
180 // This is an empty SkBitmap that could not be configured.
181 ASSERT(!size.width() || !size.height());
182 return result.release();
183 }
184
185 unsigned char* data = result->data();
186
187 if (rect.x() < 0
188 || rect.y() < 0
189 || rect.maxX() > size.width()
190 || rect.maxY() > size.height())
191 memset(data, 0, result->length());
192
193 int originX = rect.x();
194 int destX = 0;
195 if (originX < 0) {
196 destX = -originX;
197 originX = 0;
198 }
199 int endX = rect.maxX();
200 if (endX > size.width())
201 endX = size.width();
202 int numColumns = endX - originX;
203
204 if (numColumns <= 0)
205 return result.release();
206
207 int originY = rect.y();
208 int destY = 0;
209 if (originY < 0) {
210 destY = -originY;
211 originY = 0;
212 }
213 int endY = rect.maxY();
214 if (endY > size.height())
215 endY = size.height();
216 int numRows = endY - originY;
217
218 if (numRows <= 0)
219 return result.release();
220
221 ASSERT(srcConfig == SkBitmap::kARGB_8888_Config);
222
223 unsigned destBytesPerRow = 4 * rect.width();
224
225 SkBitmap srcBitmap;
226 srcDevice.readPixels(SkIRect::MakeXYWH(originX, originY, numColumns, numRows), &srcBitmap);
227
228 unsigned char* destRow = data + destY * destBytesPerRow + destX * 4;
229
230 // Do conversion of byte order and alpha divide (if necessary)
231 for (int y = 0; y < numRows; ++y) {
232 SkPMColor* srcBitmapRow = srcBitmap.getAddr32(0, y);
233 for (int x = 0; x < numColumns; ++x) {
234 SkPMColor srcPMColor = srcBitmapRow[x];
235 unsigned char* destPixel = &destRow[x * 4];
236 if (multiplied == Unmultiplied) {
237 unsigned char a = SkGetPackedA32(srcPMColor);
238 destPixel[0] = a ? SkGetPackedR32(srcPMColor) * 255 / a : 0;
239 destPixel[1] = a ? SkGetPackedG32(srcPMColor) * 255 / a : 0;
240 destPixel[2] = a ? SkGetPackedB32(srcPMColor) * 255 / a : 0;
241 destPixel[3] = a;
242 } else {
243 // Input and output are both pre-multiplied, we just need to re-arrange the
244 // bytes from the bitmap format to RGBA.
245 destPixel[0] = SkGetPackedR32(srcPMColor);
246 destPixel[1] = SkGetPackedG32(srcPMColor);
247 destPixel[2] = SkGetPackedB32(srcPMColor);
248 destPixel[3] = SkGetPackedA32(srcPMColor);
249 }
250 }
251 destRow += destBytesPerRow;
252 }
253
254 return result.release();
255 }
256
getUnmultipliedImageData(const IntRect & rect) const257 PassRefPtr<ByteArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect) const
258 {
259 context()->platformContext()->syncSoftwareCanvas();
260 return getImageData<Unmultiplied>(rect, *context()->platformContext()->canvas()->getDevice(), m_size);
261 }
262
getPremultipliedImageData(const IntRect & rect) const263 PassRefPtr<ByteArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect) const
264 {
265 context()->platformContext()->syncSoftwareCanvas();
266 return getImageData<Premultiplied>(rect, *context()->platformContext()->canvas()->getDevice(), m_size);
267 }
268
269 template <Multiply multiplied>
putImageData(ByteArray * & source,const IntSize & sourceSize,const IntRect & sourceRect,const IntPoint & destPoint,SkDevice * dstDevice,const IntSize & size)270 void putImageData(ByteArray*& source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint,
271 SkDevice* dstDevice, const IntSize& size)
272 {
273 ASSERT(sourceRect.width() > 0);
274 ASSERT(sourceRect.height() > 0);
275
276 int originX = sourceRect.x();
277 int destX = destPoint.x() + sourceRect.x();
278 ASSERT(destX >= 0);
279 ASSERT(destX < size.width());
280 ASSERT(originX >= 0);
281 ASSERT(originX < sourceRect.maxX());
282
283 int endX = destPoint.x() + sourceRect.maxX();
284 ASSERT(endX <= size.width());
285
286 int numColumns = endX - destX;
287
288 int originY = sourceRect.y();
289 int destY = destPoint.y() + sourceRect.y();
290 ASSERT(destY >= 0);
291 ASSERT(destY < size.height());
292 ASSERT(originY >= 0);
293 ASSERT(originY < sourceRect.maxY());
294
295 int endY = destPoint.y() + sourceRect.maxY();
296 ASSERT(endY <= size.height());
297 int numRows = endY - destY;
298
299 unsigned srcBytesPerRow = 4 * sourceSize.width();
300
301 SkBitmap deviceBitmap = dstDevice->accessBitmap(true);
302 SkAutoLockPixels deviceAutoLock(deviceBitmap);
303
304 // If the device's bitmap doesn't have pixels we will make a temp and call writePixels on the device.
305 bool temporaryBitmap = !deviceBitmap.getPixels();
306 SkBitmap destBitmap;
307
308 if (temporaryBitmap) {
309 destBitmap.setConfig(SkBitmap::kARGB_8888_Config, numColumns, numRows, srcBytesPerRow);
310 if (!destBitmap.allocPixels())
311 CRASH();
312 } else
313 deviceBitmap.extractSubset(&destBitmap, SkIRect::MakeXYWH(destX, destY, numColumns, numRows));
314
315 // Whether we made a temporary or not destBitmap is always configured to be written at 0,0
316 SkAutoLockPixels destAutoLock(destBitmap);
317 const unsigned char* srcRow = source->data() + originY * srcBytesPerRow + originX * 4;
318 for (int y = 0; y < numRows; ++y) {
319 SkPMColor* destRow = destBitmap.getAddr32(0, y);
320 for (int x = 0; x < numColumns; ++x) {
321 const unsigned char* srcPixel = &srcRow[x * 4];
322 if (multiplied == Unmultiplied) {
323 unsigned char alpha = srcPixel[3];
324 unsigned char r = SkMulDiv255Ceiling(srcPixel[0], alpha);
325 unsigned char g = SkMulDiv255Ceiling(srcPixel[1], alpha);
326 unsigned char b = SkMulDiv255Ceiling(srcPixel[2], alpha);
327 destRow[x] = SkPackARGB32(alpha, r, g, b);
328 } else
329 destRow[x] = SkPackARGB32(srcPixel[3], srcPixel[0], srcPixel[1], srcPixel[2]);
330 }
331 srcRow += srcBytesPerRow;
332 }
333
334 // If we used a temporary then write it to the device
335 if (temporaryBitmap)
336 dstDevice->writePixels(destBitmap, destX, destY);
337 }
338
putUnmultipliedImageData(ByteArray * source,const IntSize & sourceSize,const IntRect & sourceRect,const IntPoint & destPoint)339 void ImageBuffer::putUnmultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint)
340 {
341 context()->platformContext()->syncSoftwareCanvas();
342 putImageData<Unmultiplied>(source, sourceSize, sourceRect, destPoint, context()->platformContext()->canvas()->getDevice(), m_size);
343 }
344
putPremultipliedImageData(ByteArray * source,const IntSize & sourceSize,const IntRect & sourceRect,const IntPoint & destPoint)345 void ImageBuffer::putPremultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint)
346 {
347 context()->platformContext()->syncSoftwareCanvas();
348 putImageData<Premultiplied>(source, sourceSize, sourceRect, destPoint, context()->platformContext()->canvas()->getDevice(), m_size);
349 }
350
351 template <typename T>
ImageToDataURL(T & source,const String & mimeType,const double * quality)352 static String ImageToDataURL(T& source, const String& mimeType, const double* quality)
353 {
354 ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
355
356 Vector<unsigned char> encodedImage;
357 if (mimeType == "image/jpeg") {
358 int compressionQuality = JPEGImageEncoder::DefaultCompressionQuality;
359 if (quality && *quality >= 0.0 && *quality <= 1.0)
360 compressionQuality = static_cast<int>(*quality * 100 + 0.5);
361 if (!JPEGImageEncoder::encode(source, compressionQuality, &encodedImage))
362 return "data:,";
363 } else {
364 if (!PNGImageEncoder::encode(source, &encodedImage))
365 return "data:,";
366 ASSERT(mimeType == "image/png");
367 }
368
369 Vector<char> base64Data;
370 base64Encode(*reinterpret_cast<Vector<char>*>(&encodedImage), base64Data);
371
372 return makeString("data:", mimeType, ";base64,", base64Data);
373 }
374
toDataURL(const String & mimeType,const double * quality) const375 String ImageBuffer::toDataURL(const String& mimeType, const double* quality) const
376 {
377 SkDevice* device = context()->platformContext()->canvas()->getDevice();
378 SkBitmap bitmap = device->accessBitmap(false);
379
380 // if we can't see the pixels directly, call readPixels() to get a copy.
381 // this could happen if the device is backed by a GPU.
382 bitmap.lockPixels(); // balanced by our destructor, or explicitly if getPixels() fails
383 if (!bitmap.getPixels()) {
384 bitmap.unlockPixels();
385 SkIRect bounds = SkIRect::MakeWH(device->width(), device->height());
386 if (!device->readPixels(bounds, &bitmap))
387 return "data:,";
388 }
389
390 return ImageToDataURL(bitmap, mimeType, quality);
391 }
392
ImageDataToDataURL(const ImageData & source,const String & mimeType,const double * quality)393 String ImageDataToDataURL(const ImageData& source, const String& mimeType, const double* quality)
394 {
395 return ImageToDataURL(source, mimeType, quality);
396 }
397
398 } // namespace WebCore
399