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