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