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