1 /* 2 * Copyright (c) 2008, Google Inc. All rights reserved. 3 * Copyright (C) 2009 Dirk Schulze <krit (at) 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 namespace blink { 63 64 PassOwnPtr<ImageBuffer> ImageBuffer::create(PassOwnPtr<ImageBufferSurface> surface) 65 { 66 if (!surface->isValid()) 67 return nullptr; 68 return adoptPtr(new ImageBuffer(surface)); 69 } 70 71 PassOwnPtr<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 79 ImageBuffer::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 91 ImageBuffer::~ImageBuffer() 92 { 93 } 94 95 GraphicsContext* ImageBuffer::context() const 96 { 97 if (!isSurfaceValid()) 98 return 0; 99 ASSERT(m_context.get()); 100 return m_context.get(); 101 } 102 103 const SkBitmap& ImageBuffer::bitmap() const 104 { 105 return m_surface->bitmap(); 106 } 107 108 bool ImageBuffer::isSurfaceValid() const 109 { 110 return m_surface->isValid(); 111 } 112 113 bool ImageBuffer::isDirty() 114 { 115 return m_client ? m_client->isDirty() : false; 116 } 117 118 void ImageBuffer::didFinalizeFrame() 119 { 120 if (m_client) 121 m_client->didFinalizeFrame(); 122 } 123 124 void ImageBuffer::finalizeFrame(const FloatRect &dirtyRect) 125 { 126 m_surface->finalizeFrame(dirtyRect); 127 didFinalizeFrame(); 128 } 129 130 bool ImageBuffer::restoreSurface() const 131 { 132 return m_surface->isValid() || m_surface->restore(); 133 } 134 135 void ImageBuffer::notifySurfaceInvalid() 136 { 137 if (m_client) 138 m_client->notifySurfaceInvalid(); 139 } 140 141 static 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 150 PassRefPtr<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 159 BackingStoreCopy ImageBuffer::fastCopyImageMode() 160 { 161 return DontCopyBackingStore; 162 } 163 164 WebLayer* ImageBuffer::platformLayer() const 165 { 166 return m_surface->layer(); 167 } 168 169 bool 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 217 static bool drawNeedsCopy(GraphicsContext* src, GraphicsContext* dst) 218 { 219 ASSERT(dst); 220 return (src == dst); 221 } 222 223 Platform3DObject ImageBuffer::getBackingTexture() 224 { 225 return m_surface->getBackingTexture(); 226 } 227 228 void ImageBuffer::didModifyBackingTexture() 229 { 230 m_surface->didModifyBackingTexture(); 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 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 256 void 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 281 void ImageBuffer::flush() 282 { 283 if (m_surface->canvas()) { 284 m_surface->canvas()->flush(); 285 } 286 } 287 288 void 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 299 void 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 329 PassRefPtr<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 339 PassRefPtr<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 364 void 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 396 template <typename T> 397 static 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 422 String 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 433 String 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