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.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
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
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.
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.
34 #include "platform/graphics/ImageBuffer.h"
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"
63 PassOwnPtr<ImageBuffer> ImageBuffer::create(PassOwnPtr<ImageBufferSurface> surface)
65 if (!surface->isValid())
67 return adoptPtr(new ImageBuffer(surface));
70 PassOwnPtr<ImageBuffer> ImageBuffer::create(const IntSize& size, OpacityMode opacityMode)
72 OwnPtr<ImageBufferSurface> surface = adoptPtr(new UnacceleratedImageBufferSurface(size, opacityMode));
73 if (!surface->isValid())
75 return adoptPtr(new ImageBuffer(surface.release()));
78 ImageBuffer::ImageBuffer(PassOwnPtr<ImageBufferSurface> surface)
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());
88 ImageBuffer::~ImageBuffer()
92 GraphicsContext* ImageBuffer::context() const
95 ASSERT(m_context.get());
96 return m_context.get();
99 const SkBitmap& ImageBuffer::bitmap() const
101 m_surface->willUse();
102 return m_surface->bitmap();
105 bool ImageBuffer::isValid() const
107 return m_surface->isValid();
110 static SkBitmap deepSkBitmapCopy(const SkBitmap& bitmap)
113 if (!bitmap.deepCopyTo(&tmp))
114 bitmap.copyTo(&tmp, bitmap.colorType());
119 PassRefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, ScaleBehavior) const
122 return BitmapImage::create(NativeImageSkia::create());
124 const SkBitmap& bitmap = m_surface->bitmap();
125 return BitmapImage::create(NativeImageSkia::create(copyBehavior == CopyBackingStore ? deepSkBitmapCopy(bitmap) : bitmap));
128 BackingStoreCopy ImageBuffer::fastCopyImageMode()
130 return DontCopyBackingStore;
133 blink::WebLayer* ImageBuffer::platformLayer() const
135 return m_surface->layer();
138 bool ImageBuffer::copyToPlatformTexture(blink::WebGraphicsContext3D* context, Platform3DObject texture, GLenum internalFormat, GLenum destType, GLint level, bool premultiplyAlpha, bool flipY)
140 if (!m_surface->isAccelerated() || !platformLayer() || !isValid())
143 if (!Extensions3DUtil::canUseCopyTextureCHROMIUM(internalFormat, destType, level))
146 OwnPtr<blink::WebGraphicsContext3DProvider> provider = adoptPtr(blink::Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
149 blink::WebGraphicsContext3D* sharedContext = provider->context3d();
150 if (!sharedContext || !sharedContext->makeContextCurrent())
153 OwnPtr<blink::WebExternalTextureMailbox> mailbox = adoptPtr(new blink::WebExternalTextureMailbox);
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();
161 mailbox->syncPoint = sharedContext->insertSyncPoint();
163 if (!context->makeContextCurrent())
166 Platform3DObject sourceTexture = context->createTexture();
168 context->bindTexture(GL_TEXTURE_2D, sourceTexture);
170 context->waitSyncPoint(mailbox->syncPoint);
171 context->consumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox->name);
173 // The canvas is stored in a premultiplied format, so unpremultiply if necessary.
174 context->pixelStorei(GC3D_UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, !premultiplyAlpha);
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);
180 context->pixelStorei(GC3D_UNPACK_FLIP_Y_CHROMIUM, false);
181 context->pixelStorei(GC3D_UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, false);
183 context->bindTexture(GL_TEXTURE_2D, 0);
184 context->deleteTexture(sourceTexture);
187 sharedContext->waitSyncPoint(context->insertSyncPoint());
189 // Undo grContext texture binding changes introduced in this function
190 provider->grContext()->resetContext(kTextureBinding_GrGLBackendState);
195 static bool drawNeedsCopy(GraphicsContext* src, GraphicsContext* dst)
201 Platform3DObject ImageBuffer::getBackingTexture()
203 return m_surface->getBackingTexture();
206 bool ImageBuffer::copyRenderingResultsFromDrawingBuffer(DrawingBuffer* drawingBuffer)
210 OwnPtr<blink::WebGraphicsContext3DProvider> provider = adoptPtr(blink::Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
213 blink::WebGraphicsContext3D* context3D = provider->context3d();
214 Platform3DObject tex = m_surface->getBackingTexture();
215 if (!context3D || !tex)
217 m_surface->invalidateCachedBitmap();
218 return drawingBuffer->copyToPlatformTexture(context3D, tex, GL_RGBA,
219 GL_UNSIGNED_BYTE, 0, true, false);
222 void ImageBuffer::draw(GraphicsContext* context, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, blink::WebBlendMode blendMode)
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();
235 RefPtr<Image> image = BitmapImage::create(NativeImageSkia::create(drawNeedsCopy(m_context.get(), context) ? deepSkBitmapCopy(bitmap) : bitmap));
237 context->drawImage(image.get(), destRect, srcRect, op, blendMode, DoNotRespectImageOrientation);
240 void ImageBuffer::flush()
242 if (m_surface->canvas()) {
243 m_surface->canvas()->flush();
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)
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);
258 void ImageBuffer::transformColorSpace(ColorSpace srcColorSpace, ColorSpace dstColorSpace)
260 const uint8_t* lookUpTable = ColorSpaceUtilities::getConversionLUT(dstColorSpace, srcColorSpace);
264 // FIXME: Disable color space conversions on accelerated canvases (for now).
265 if (context()->isAccelerated() || !isValid())
268 const SkBitmap& bitmap = m_surface->bitmap();
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(
281 lookUpTable[SkColorGetR(color)],
282 lookUpTable[SkColorGetG(color)],
283 lookUpTable[SkColorGetB(color)]);
288 PassRefPtr<SkColorFilter> ImageBuffer::createColorSpaceFilter(ColorSpace srcColorSpace,
289 ColorSpace dstColorSpace)
291 const uint8_t* lut = ColorSpaceUtilities::getConversionLUT(dstColorSpace, srcColorSpace);
295 return adoptRef(SkTableColorFilter::CreateARGB(0, lut, lut, lut));
298 template <Multiply multiplied>
299 PassRefPtr<Uint8ClampedArray> getImageData(const IntRect& rect, GraphicsContext* context, const IntSize& size)
301 float area = 4.0f * rect.width() * rect.height();
302 if (area > static_cast<float>(std::numeric_limits<int>::max()))
305 RefPtr<Uint8ClampedArray> result = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4);
309 || rect.maxX() > size.width()
310 || rect.maxY() > size.height())
313 SkAlphaType alphaType = (multiplied == Premultiplied) ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
314 SkImageInfo info = SkImageInfo::Make(rect.width(), rect.height(), kRGBA_8888_SkColorType, alphaType);
316 context->readPixels(info, result->data(), 4 * rect.width(), rect.x(), rect.y());
317 return result.release();
320 PassRefPtr<Uint8ClampedArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect) const
323 return Uint8ClampedArray::create(rect.width() * rect.height() * 4);
324 return getImageData<Unmultiplied>(rect, context(), m_surface->size());
327 PassRefPtr<Uint8ClampedArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect) const
330 return Uint8ClampedArray::create(rect.width() * rect.height() * 4);
331 return getImageData<Premultiplied>(rect, context(), m_surface->size());
334 void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint)
339 ASSERT(sourceRect.width() > 0);
340 ASSERT(sourceRect.height() > 0);
342 int originX = sourceRect.x();
343 int destX = destPoint.x() + sourceRect.x();
345 ASSERT(destX < m_surface->size().width());
346 ASSERT(originX >= 0);
347 ASSERT(originX < sourceRect.maxX());
349 int originY = sourceRect.y();
350 int destY = destPoint.y() + sourceRect.y();
352 ASSERT(destY < m_surface->size().height());
353 ASSERT(originY >= 0);
354 ASSERT(originY < sourceRect.maxY());
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);
361 context()->writePixels(info, srcAddr, srcBytesPerRow, destX, destY);
364 template <typename T>
365 static bool encodeImage(T& source, const String& mimeType, const double* quality, Vector<char>* output)
367 Vector<unsigned char>* encodedImage = reinterpret_cast<Vector<unsigned char>*>(output);
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))
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))
382 if (!PNGImageEncoder::encode(source, encodedImage))
384 ASSERT(mimeType == "image/png");
390 String ImageBuffer::toDataURL(const String& mimeType, const double* quality) const
392 ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
394 Vector<char> encodedImage;
395 if (!isValid() || !encodeImage(m_surface->bitmap(), mimeType, quality, &encodedImage))
397 Vector<char> base64Data;
398 base64Encode(encodedImage, base64Data);
400 return "data:" + mimeType + ";base64," + base64Data;
403 String ImageDataToDataURL(const ImageDataBuffer& imageData, const String& mimeType, const double* quality)
405 ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
407 Vector<char> encodedImage;
408 if (!encodeImage(imageData, mimeType, quality, &encodedImage))
411 Vector<char> base64Data;
412 base64Encode(encodedImage, base64Data);
414 return "data:" + mimeType + ";base64," + base64Data;
417 } // namespace WebCore