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