Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / platform / graphics / ImageBuffer.cpp
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 "platform/graphics/ImageBuffer.h"
35
36 #include "platform/MIMETypeRegistry.h"
37 #include "platform/geometry/IntRect.h"
38 #include "platform/graphics/BitmapImage.h"
39 #include "platform/graphics/GraphicsContext.h"
40 #include "platform/graphics/GraphicsTypes3D.h"
41 #include "platform/graphics/UnacceleratedImageBufferSurface.h"
42 #include "platform/graphics/gpu/DrawingBuffer.h"
43 #include "platform/graphics/gpu/Extensions3DUtil.h"
44 #include "platform/graphics/skia/NativeImageSkia.h"
45 #include "platform/graphics/skia/SkiaUtils.h"
46 #include "platform/image-encoders/skia/JPEGImageEncoder.h"
47 #include "platform/image-encoders/skia/PNGImageEncoder.h"
48 #include "platform/image-encoders/skia/WEBPImageEncoder.h"
49 #include "public/platform/Platform.h"
50 #include "public/platform/WebGraphicsContext3D.h"
51 #include "public/platform/WebGraphicsContext3DProvider.h"
52 #include "third_party/skia/include/effects/SkTableColorFilter.h"
53 #include "wtf/MathExtras.h"
54 #include "wtf/text/Base64.h"
55 #include "wtf/text/WTFString.h"
56
57 using namespace std;
58
59 namespace WebCore {
60
61 PassOwnPtr<ImageBuffer> ImageBuffer::create(PassOwnPtr<ImageBufferSurface> surface)
62 {
63     if (!surface->isValid())
64         return nullptr;
65     return adoptPtr(new ImageBuffer(surface));
66 }
67
68 PassOwnPtr<ImageBuffer> ImageBuffer::create(const IntSize& size, OpacityMode opacityMode)
69 {
70     OwnPtr<ImageBufferSurface> surface = adoptPtr(new UnacceleratedImageBufferSurface(size, opacityMode));
71     if (!surface->isValid())
72         return nullptr;
73     return adoptPtr(new ImageBuffer(surface.release()));
74 }
75
76 ImageBuffer::ImageBuffer(PassOwnPtr<ImageBufferSurface> surface)
77     : m_surface(surface)
78 {
79     if (m_surface->canvas()) {
80         m_context = adoptPtr(new GraphicsContext(m_surface->canvas()));
81         m_context->setCertainlyOpaque(m_surface->opacityMode() == Opaque);
82         m_context->setAccelerated(m_surface->isAccelerated());
83     }
84 }
85
86 ImageBuffer::~ImageBuffer()
87 {
88 }
89
90 GraphicsContext* ImageBuffer::context() const
91 {
92     m_surface->willUse();
93     ASSERT(m_context.get());
94     return m_context.get();
95 }
96
97 const SkBitmap& ImageBuffer::bitmap() const
98 {
99     m_surface->willUse();
100     return m_surface->bitmap();
101 }
102
103 bool ImageBuffer::isValid() const
104 {
105     return m_surface->isValid();
106 }
107
108 static SkBitmap deepSkBitmapCopy(const SkBitmap& bitmap)
109 {
110     SkBitmap tmp;
111     if (!bitmap.deepCopyTo(&tmp, bitmap.config()))
112         bitmap.copyTo(&tmp, bitmap.config());
113
114     return tmp;
115 }
116
117 PassRefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, ScaleBehavior) const
118 {
119     if (!isValid())
120         return BitmapImage::create(NativeImageSkia::create());
121
122     const SkBitmap& bitmap = m_surface->bitmap();
123     return BitmapImage::create(NativeImageSkia::create(copyBehavior == CopyBackingStore ? deepSkBitmapCopy(bitmap) : bitmap));
124 }
125
126 BackingStoreCopy ImageBuffer::fastCopyImageMode()
127 {
128     return DontCopyBackingStore;
129 }
130
131 blink::WebLayer* ImageBuffer::platformLayer() const
132 {
133     return m_surface->layer();
134 }
135
136 bool ImageBuffer::copyToPlatformTexture(blink::WebGraphicsContext3D* context, Platform3DObject texture, GLenum internalFormat, GLenum destType, GLint level, bool premultiplyAlpha, bool flipY)
137 {
138     if (!m_surface->isAccelerated() || !platformLayer() || !isValid())
139         return false;
140
141     if (!context->makeContextCurrent())
142         return false;
143
144     if (!Extensions3DUtil::canUseCopyTextureCHROMIUM(internalFormat, destType, level))
145         return false;
146
147     // The canvas is stored in a premultiplied format, so unpremultiply if necessary.
148     context->pixelStorei(GC3D_UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, !premultiplyAlpha);
149
150     // The canvas is stored in an inverted position, so the flip semantics are reversed.
151     context->pixelStorei(GC3D_UNPACK_FLIP_Y_CHROMIUM, !flipY);
152     context->copyTextureCHROMIUM(GL_TEXTURE_2D, getBackingTexture(), texture, level, internalFormat, destType);
153
154     context->pixelStorei(GC3D_UNPACK_FLIP_Y_CHROMIUM, false);
155     context->pixelStorei(GC3D_UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, false);
156     context->flush();
157     return true;
158 }
159
160 static bool drawNeedsCopy(GraphicsContext* src, GraphicsContext* dst)
161 {
162     ASSERT(dst);
163     return (src == dst);
164 }
165
166 Platform3DObject ImageBuffer::getBackingTexture()
167 {
168     return m_surface->getBackingTexture();
169 }
170
171 bool ImageBuffer::copyRenderingResultsFromDrawingBuffer(DrawingBuffer* drawingBuffer)
172 {
173     if (!drawingBuffer)
174         return false;
175     OwnPtr<blink::WebGraphicsContext3DProvider> provider = adoptPtr(blink::Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
176     if (!provider)
177         return false;
178     blink::WebGraphicsContext3D* context3D = provider->context3d();
179     Platform3DObject tex = m_surface->getBackingTexture();
180     if (!context3D || !tex)
181         return false;
182     m_surface->invalidateCachedBitmap();
183     return drawingBuffer->copyToPlatformTexture(context3D, tex, GL_RGBA,
184         GL_UNSIGNED_BYTE, 0, true, false);
185 }
186
187 void ImageBuffer::draw(GraphicsContext* context, const FloatRect& destRect, const FloatRect& srcRect,
188     CompositeOperator op, blink::WebBlendMode blendMode, bool useLowQualityScale)
189 {
190     if (!isValid())
191         return;
192
193     SkBitmap bitmap = m_surface->bitmap();
194     // For ImageBufferSurface that enables cachedBitmap, Use the cached Bitmap for CPU side usage
195     // if it is available, otherwise generate and use it.
196     if (!context->isAccelerated() && m_surface->isAccelerated() && m_surface->cachedBitmapEnabled() && m_surface->isValid()) {
197         m_surface->updateCachedBitmapIfNeeded();
198         bitmap = m_surface->cachedBitmap();
199     }
200
201     RefPtr<Image> image = BitmapImage::create(NativeImageSkia::create(drawNeedsCopy(m_context.get(), context) ? deepSkBitmapCopy(bitmap) : bitmap));
202
203     context->drawImage(image.get(), destRect, srcRect, op, blendMode, DoNotRespectImageOrientation, useLowQualityScale);
204 }
205
206 void ImageBuffer::flush()
207 {
208     if (m_surface->canvas()) {
209         m_surface->canvas()->flush();
210     }
211 }
212
213 void ImageBuffer::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const FloatSize& scale,
214     const FloatPoint& phase, CompositeOperator op, const FloatRect& destRect, blink::WebBlendMode blendMode, const IntSize& repeatSpacing)
215 {
216     if (!isValid())
217         return;
218
219     const SkBitmap& bitmap = m_surface->bitmap();
220     RefPtr<Image> image = BitmapImage::create(NativeImageSkia::create(drawNeedsCopy(m_context.get(), context) ? deepSkBitmapCopy(bitmap) : bitmap));
221     image->drawPattern(context, srcRect, scale, phase, op, destRect, blendMode, repeatSpacing);
222 }
223
224 void ImageBuffer::transformColorSpace(ColorSpace srcColorSpace, ColorSpace dstColorSpace)
225 {
226     const uint8_t* lookUpTable = ColorSpaceUtilities::getConversionLUT(dstColorSpace, srcColorSpace);
227     if (!lookUpTable)
228         return;
229
230     // FIXME: Disable color space conversions on accelerated canvases (for now).
231     if (context()->isAccelerated() || !isValid())
232         return;
233
234     const SkBitmap& bitmap = m_surface->bitmap();
235     if (bitmap.isNull())
236         return;
237
238     ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config);
239     IntSize size = m_surface->size();
240     SkAutoLockPixels bitmapLock(bitmap);
241     for (int y = 0; y < size.height(); ++y) {
242         uint32_t* srcRow = bitmap.getAddr32(0, y);
243         for (int x = 0; x < size.width(); ++x) {
244             SkColor color = SkPMColorToColor(srcRow[x]);
245             srcRow[x] = SkPreMultiplyARGB(
246                 SkColorGetA(color),
247                 lookUpTable[SkColorGetR(color)],
248                 lookUpTable[SkColorGetG(color)],
249                 lookUpTable[SkColorGetB(color)]);
250         }
251     }
252 }
253
254 PassRefPtr<SkColorFilter> ImageBuffer::createColorSpaceFilter(ColorSpace srcColorSpace,
255     ColorSpace dstColorSpace)
256 {
257     const uint8_t* lut = ColorSpaceUtilities::getConversionLUT(dstColorSpace, srcColorSpace);
258     if (!lut)
259         return 0;
260
261     return adoptRef(SkTableColorFilter::CreateARGB(0, lut, lut, lut));
262 }
263
264 template <Multiply multiplied>
265 PassRefPtr<Uint8ClampedArray> getImageData(const IntRect& rect, GraphicsContext* context, const IntSize& size)
266 {
267     float area = 4.0f * rect.width() * rect.height();
268     if (area > static_cast<float>(std::numeric_limits<int>::max()))
269         return 0;
270
271     RefPtr<Uint8ClampedArray> result = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4);
272
273     unsigned char* data = result->data();
274
275     if (rect.x() < 0
276         || rect.y() < 0
277         || rect.maxX() > size.width()
278         || rect.maxY() > size.height())
279         result->zeroFill();
280
281     unsigned destBytesPerRow = 4 * rect.width();
282     SkBitmap destBitmap;
283     destBitmap.setConfig(SkBitmap::kARGB_8888_Config, rect.width(), rect.height(), destBytesPerRow);
284     destBitmap.setPixels(data);
285
286     SkCanvas::Config8888 config8888;
287     if (multiplied == Premultiplied)
288         config8888 = SkCanvas::kRGBA_Premul_Config8888;
289     else
290         config8888 = SkCanvas::kRGBA_Unpremul_Config8888;
291
292     context->readPixels(&destBitmap, rect.x(), rect.y(), config8888);
293     return result.release();
294 }
295
296 PassRefPtr<Uint8ClampedArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect) const
297 {
298     if (!isValid())
299         return Uint8ClampedArray::create(rect.width() * rect.height() * 4);
300     return getImageData<Unmultiplied>(rect, context(), m_surface->size());
301 }
302
303 PassRefPtr<Uint8ClampedArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect) const
304 {
305     if (!isValid())
306         return Uint8ClampedArray::create(rect.width() * rect.height() * 4);
307     return getImageData<Premultiplied>(rect, context(), m_surface->size());
308 }
309
310 void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint)
311 {
312     if (!isValid())
313         return;
314
315     ASSERT(sourceRect.width() > 0);
316     ASSERT(sourceRect.height() > 0);
317
318     int originX = sourceRect.x();
319     int destX = destPoint.x() + sourceRect.x();
320     ASSERT(destX >= 0);
321     ASSERT(destX < m_surface->size().width());
322     ASSERT(originX >= 0);
323     ASSERT(originX < sourceRect.maxX());
324
325     int endX = destPoint.x() + sourceRect.maxX();
326     ASSERT(endX <= m_surface->size().width());
327
328     int numColumns = endX - destX;
329
330     int originY = sourceRect.y();
331     int destY = destPoint.y() + sourceRect.y();
332     ASSERT(destY >= 0);
333     ASSERT(destY < m_surface->size().height());
334     ASSERT(originY >= 0);
335     ASSERT(originY < sourceRect.maxY());
336
337     int endY = destPoint.y() + sourceRect.maxY();
338     ASSERT(endY <= m_surface->size().height());
339     int numRows = endY - destY;
340
341     unsigned srcBytesPerRow = 4 * sourceSize.width();
342     SkBitmap srcBitmap;
343     srcBitmap.setConfig(SkBitmap::kARGB_8888_Config, numColumns, numRows, srcBytesPerRow);
344     srcBitmap.setPixels(source->data() + originY * srcBytesPerRow + originX * 4);
345
346     SkCanvas::Config8888 config8888;
347     if (multiplied == Premultiplied)
348         config8888 = SkCanvas::kRGBA_Premul_Config8888;
349     else
350         config8888 = SkCanvas::kRGBA_Unpremul_Config8888;
351
352     context()->writePixels(srcBitmap, destX, destY, config8888);
353 }
354
355 template <typename T>
356 static bool encodeImage(T& source, const String& mimeType, const double* quality, Vector<char>* output)
357 {
358     Vector<unsigned char>* encodedImage = reinterpret_cast<Vector<unsigned char>*>(output);
359
360     if (mimeType == "image/jpeg") {
361         int compressionQuality = JPEGImageEncoder::DefaultCompressionQuality;
362         if (quality && *quality >= 0.0 && *quality <= 1.0)
363             compressionQuality = static_cast<int>(*quality * 100 + 0.5);
364         if (!JPEGImageEncoder::encode(source, compressionQuality, encodedImage))
365             return false;
366     } else if (mimeType == "image/webp") {
367         int compressionQuality = WEBPImageEncoder::DefaultCompressionQuality;
368         if (quality && *quality >= 0.0 && *quality <= 1.0)
369             compressionQuality = static_cast<int>(*quality * 100 + 0.5);
370         if (!WEBPImageEncoder::encode(source, compressionQuality, encodedImage))
371             return false;
372     } else {
373         if (!PNGImageEncoder::encode(source, encodedImage))
374             return false;
375         ASSERT(mimeType == "image/png");
376     }
377
378     return true;
379 }
380
381 String ImageBuffer::toDataURL(const String& mimeType, const double* quality) const
382 {
383     ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
384
385     Vector<char> encodedImage;
386     if (!isValid() || !encodeImage(m_surface->bitmap(), mimeType, quality, &encodedImage))
387         return "data:,";
388     Vector<char> base64Data;
389     base64Encode(encodedImage, base64Data);
390
391     return "data:" + mimeType + ";base64," + base64Data;
392 }
393
394 String ImageDataToDataURL(const ImageDataBuffer& imageData, const String& mimeType, const double* quality)
395 {
396     ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
397
398     Vector<char> encodedImage;
399     if (!encodeImage(imageData, mimeType, quality, &encodedImage))
400         return "data:,";
401
402     Vector<char> base64Data;
403     base64Encode(encodedImage, base64Data);
404
405     return "data:" + mimeType + ";base64," + base64Data;
406 }
407
408 } // namespace WebCore