• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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