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