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 "core/platform/graphics/ImageBuffer.h" 35 36 #include "core/html/ImageData.h" 37 #include "core/platform/MIMETypeRegistry.h" 38 #include "core/platform/graphics/BitmapImage.h" 39 #include "core/platform/graphics/Extensions3D.h" 40 #include "core/platform/graphics/GraphicsContext.h" 41 #include "core/platform/graphics/GraphicsContext3D.h" 42 #include "core/platform/graphics/IntRect.h" 43 #include "core/platform/graphics/chromium/Canvas2DLayerBridge.h" 44 #include "core/platform/graphics/gpu/SharedGraphicsContext3D.h" 45 #include "core/platform/graphics/skia/NativeImageSkia.h" 46 #include "core/platform/graphics/skia/SkiaUtils.h" 47 #include "core/platform/image-encoders/skia/JPEGImageEncoder.h" 48 #include "core/platform/image-encoders/skia/PNGImageEncoder.h" 49 #include "core/platform/image-encoders/skia/WEBPImageEncoder.h" 50 #include "public/platform/Platform.h" 51 #include "skia/ext/platform_canvas.h" 52 #include "third_party/skia/include/core/SkColorPriv.h" 53 #include "third_party/skia/include/core/SkSurface.h" 54 #include "third_party/skia/include/gpu/GrContext.h" 55 #include "third_party/skia/include/gpu/SkGpuDevice.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 static SkCanvas* createAcceleratedCanvas(const IntSize& size, OwnPtr<Canvas2DLayerBridge>* outLayerBridge, OpacityMode opacityMode) 65 { 66 RefPtr<GraphicsContext3D> context3D = SharedGraphicsContext3D::get(); 67 if (!context3D) 68 return 0; 69 Canvas2DLayerBridge::OpacityMode bridgeOpacityMode = opacityMode == Opaque ? Canvas2DLayerBridge::Opaque : Canvas2DLayerBridge::NonOpaque; 70 *outLayerBridge = Canvas2DLayerBridge::create(context3D.release(), size, bridgeOpacityMode); 71 // If canvas buffer allocation failed, debug build will have asserted 72 // For release builds, we must verify whether the device has a render target 73 return (*outLayerBridge) ? (*outLayerBridge)->getCanvas() : 0; 74 } 75 76 static SkCanvas* createNonPlatformCanvas(const IntSize& size) 77 { 78 SkAutoTUnref<SkDevice> device(new SkDevice(SkBitmap::kARGB_8888_Config, size.width(), size.height())); 79 SkPixelRef* pixelRef = device->accessBitmap(false).pixelRef(); 80 return pixelRef ? new SkCanvas(device) : 0; 81 } 82 83 PassOwnPtr<ImageBuffer> ImageBuffer::createCompatibleBuffer(const IntSize& size, float resolutionScale, const GraphicsContext* context, bool hasAlpha) 84 { 85 bool success = false; 86 OwnPtr<ImageBuffer> buf = adoptPtr(new ImageBuffer(size, resolutionScale, context, hasAlpha, success)); 87 if (!success) 88 return nullptr; 89 return buf.release(); 90 } 91 92 ImageBuffer::ImageBuffer(const IntSize& size, float resolutionScale, const GraphicsContext* compatibleContext, bool hasAlpha, bool& success) 93 : m_size(size) 94 , m_logicalSize(size) 95 , m_resolutionScale(resolutionScale) 96 { 97 if (!compatibleContext) { 98 success = false; 99 return; 100 } 101 102 SkAutoTUnref<SkDevice> device(compatibleContext->createCompatibleDevice(size, hasAlpha)); 103 if (!device.get()) { 104 success = false; 105 return; 106 } 107 108 SkPixelRef* pixelRef = device->accessBitmap(false).pixelRef(); 109 if (!pixelRef) { 110 success = false; 111 return; 112 } 113 114 m_canvas = adoptPtr(new SkCanvas(device)); 115 m_context = adoptPtr(new GraphicsContext(m_canvas.get())); 116 m_context->setCertainlyOpaque(!hasAlpha); 117 m_context->scale(FloatSize(m_resolutionScale, m_resolutionScale)); 118 119 success = true; 120 } 121 122 ImageBuffer::ImageBuffer(const IntSize& size, float resolutionScale, RenderingMode renderingMode, OpacityMode opacityMode, bool& success) 123 : m_size(size) 124 , m_logicalSize(size) 125 , m_resolutionScale(resolutionScale) 126 { 127 if (renderingMode == Accelerated) { 128 m_canvas = adoptPtr(createAcceleratedCanvas(size, &m_layerBridge, opacityMode)); 129 if (!m_canvas) 130 renderingMode = UnacceleratedNonPlatformBuffer; 131 } 132 133 if (renderingMode == UnacceleratedNonPlatformBuffer) 134 m_canvas = adoptPtr(createNonPlatformCanvas(size)); 135 136 if (!m_canvas) 137 m_canvas = adoptPtr(skia::TryCreateBitmapCanvas(size.width(), size.height(), false)); 138 139 if (!m_canvas) { 140 success = false; 141 return; 142 } 143 144 m_context = adoptPtr(new GraphicsContext(m_canvas.get())); 145 m_context->setCertainlyOpaque(opacityMode == Opaque); 146 m_context->setAccelerated(renderingMode == Accelerated); 147 m_context->scale(FloatSize(m_resolutionScale, m_resolutionScale)); 148 149 // Clear the background transparent or opaque, as required. It would be nice if this wasn't 150 // required, but the canvas is currently filled with the magic transparency 151 // color. Can we have another way to manage this? 152 if (opacityMode == Opaque) 153 m_canvas->drawARGB(255, 0, 0, 0, SkXfermode::kSrc_Mode); 154 else 155 m_canvas->drawARGB(0, 0, 0, 0, SkXfermode::kClear_Mode); 156 157 success = true; 158 } 159 160 ImageBuffer::~ImageBuffer() 161 { 162 } 163 164 GraphicsContext* ImageBuffer::context() const 165 { 166 if (m_layerBridge) { 167 // We're using context acquisition as a signal that someone is about to render into our buffer and we need 168 // to be ready. This isn't logically const-correct, hence the cast. 169 const_cast<Canvas2DLayerBridge*>(m_layerBridge.get())->contextAcquired(); 170 } 171 return m_context.get(); 172 } 173 174 175 bool ImageBuffer::isValid() const 176 { 177 if (m_layerBridge.get()) 178 return const_cast<Canvas2DLayerBridge*>(m_layerBridge.get())->isValid(); 179 return true; 180 } 181 182 static SkBitmap deepSkBitmapCopy(const SkBitmap& bitmap) 183 { 184 SkBitmap tmp; 185 if (!bitmap.deepCopyTo(&tmp, bitmap.config())) 186 bitmap.copyTo(&tmp, bitmap.config()); 187 188 return tmp; 189 } 190 191 PassRefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, ScaleBehavior) const 192 { 193 if (!isValid()) 194 return BitmapImage::create(NativeImageSkia::create()); 195 196 const SkBitmap& bitmap = *context()->bitmap(); 197 // FIXME: Start honoring ScaleBehavior to scale 2x buffers down to 1x. 198 return BitmapImage::create(NativeImageSkia::create(copyBehavior == CopyBackingStore ? deepSkBitmapCopy(bitmap) : bitmap, m_resolutionScale)); 199 } 200 201 BackingStoreCopy ImageBuffer::fastCopyImageMode() 202 { 203 return DontCopyBackingStore; 204 } 205 206 WebKit::WebLayer* ImageBuffer::platformLayer() const 207 { 208 return m_layerBridge ? m_layerBridge->layer() : 0; 209 } 210 211 bool ImageBuffer::copyToPlatformTexture(GraphicsContext3D& context, Platform3DObject texture, GC3Denum internalFormat, GC3Denum destType, GC3Dint level, bool premultiplyAlpha, bool flipY) 212 { 213 if (!m_layerBridge || !platformLayer() || !isValid()) 214 return false; 215 216 Platform3DObject sourceTexture = m_layerBridge->backBufferTexture(); 217 218 if (!context.makeContextCurrent()) 219 return false; 220 221 Extensions3D* extensions = context.getExtensions(); 222 if (!extensions->supports("GL_CHROMIUM_copy_texture") || !extensions->supports("GL_CHROMIUM_flipy") 223 || !extensions->canUseCopyTextureCHROMIUM(internalFormat, destType, level)) 224 return false; 225 226 // The canvas is stored in a premultiplied format, so unpremultiply if necessary. 227 context.pixelStorei(Extensions3D::UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, !premultiplyAlpha); 228 229 // The canvas is stored in an inverted position, so the flip semantics are reversed. 230 context.pixelStorei(Extensions3D::UNPACK_FLIP_Y_CHROMIUM, !flipY); 231 232 extensions->copyTextureCHROMIUM(GraphicsContext3D::TEXTURE_2D, sourceTexture, texture, level, internalFormat, destType); 233 234 context.pixelStorei(Extensions3D::UNPACK_FLIP_Y_CHROMIUM, false); 235 context.pixelStorei(Extensions3D::UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, false); 236 context.flush(); 237 return true; 238 } 239 240 static bool drawNeedsCopy(GraphicsContext* src, GraphicsContext* dst) 241 { 242 return (src == dst); 243 } 244 245 void ImageBuffer::draw(GraphicsContext* context, const FloatRect& destRect, const FloatRect& srcRect, 246 CompositeOperator op, BlendMode blendMode, bool useLowQualityScale) 247 { 248 if (!isValid()) 249 return; 250 251 const SkBitmap& bitmap = *m_context->bitmap(); 252 RefPtr<Image> image = BitmapImage::create(NativeImageSkia::create(drawNeedsCopy(m_context.get(), context) ? deepSkBitmapCopy(bitmap) : bitmap)); 253 context->drawImage(image.get(), destRect, srcRect, op, blendMode, DoNotRespectImageOrientation, useLowQualityScale); 254 } 255 256 void ImageBuffer::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const FloatSize& scale, 257 const FloatPoint& phase, CompositeOperator op, const FloatRect& destRect, BlendMode blendMode) 258 { 259 if (!isValid()) 260 return; 261 262 const SkBitmap& bitmap = *m_context->bitmap(); 263 RefPtr<Image> image = BitmapImage::create(NativeImageSkia::create(drawNeedsCopy(m_context.get(), context) ? deepSkBitmapCopy(bitmap) : bitmap)); 264 image->drawPattern(context, srcRect, scale, phase, op, destRect, blendMode); 265 } 266 267 void ImageBuffer::transformColorSpace(ColorSpace srcColorSpace, ColorSpace dstColorSpace) 268 { 269 if (srcColorSpace == dstColorSpace) 270 return; 271 272 // only sRGB <-> linearRGB are supported at the moment 273 if ((srcColorSpace != ColorSpaceLinearRGB && srcColorSpace != ColorSpaceDeviceRGB) 274 || (dstColorSpace != ColorSpaceLinearRGB && dstColorSpace != ColorSpaceDeviceRGB)) 275 return; 276 277 // FIXME: Disable color space conversions on accelerated canvases (for now). 278 if (context()->isAccelerated() || !isValid()) 279 return; 280 281 const SkBitmap& bitmap = *context()->bitmap(); 282 if (bitmap.isNull()) 283 return; 284 285 const Vector<uint8_t>& lookUpTable = dstColorSpace == ColorSpaceLinearRGB ? 286 getLinearRgbLUT() : getDeviceRgbLUT(); 287 288 ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config); 289 SkAutoLockPixels bitmapLock(bitmap); 290 for (int y = 0; y < m_size.height(); ++y) { 291 uint32_t* srcRow = bitmap.getAddr32(0, y); 292 for (int x = 0; x < m_size.width(); ++x) { 293 SkColor color = SkPMColorToColor(srcRow[x]); 294 srcRow[x] = SkPreMultiplyARGB( 295 SkColorGetA(color), 296 lookUpTable[SkColorGetR(color)], 297 lookUpTable[SkColorGetG(color)], 298 lookUpTable[SkColorGetB(color)]); 299 } 300 } 301 } 302 303 const Vector<uint8_t>& ImageBuffer::getLinearRgbLUT() 304 { 305 DEFINE_STATIC_LOCAL(Vector<uint8_t>, linearRgbLUT, ()); 306 if (linearRgbLUT.isEmpty()) { 307 for (unsigned i = 0; i < 256; i++) { 308 float color = i / 255.0f; 309 color = (color <= 0.04045f ? color / 12.92f : pow((color + 0.055f) / 1.055f, 2.4f)); 310 color = std::max(0.0f, color); 311 color = std::min(1.0f, color); 312 linearRgbLUT.append(static_cast<uint8_t>(round(color * 255))); 313 } 314 } 315 return linearRgbLUT; 316 } 317 318 const Vector<uint8_t>& ImageBuffer::getDeviceRgbLUT() 319 { 320 DEFINE_STATIC_LOCAL(Vector<uint8_t>, deviceRgbLUT, ()); 321 if (deviceRgbLUT.isEmpty()) { 322 for (unsigned i = 0; i < 256; i++) { 323 float color = i / 255.0f; 324 color = (powf(color, 1.0f / 2.4f) * 1.055f) - 0.055f; 325 color = std::max(0.0f, color); 326 color = std::min(1.0f, color); 327 deviceRgbLUT.append(static_cast<uint8_t>(round(color * 255))); 328 } 329 } 330 return deviceRgbLUT; 331 } 332 333 334 template <Multiply multiplied> 335 PassRefPtr<Uint8ClampedArray> getImageData(const IntRect& rect, GraphicsContext* context, const IntSize& size) 336 { 337 float area = 4.0f * rect.width() * rect.height(); 338 if (area > static_cast<float>(std::numeric_limits<int>::max())) 339 return 0; 340 341 RefPtr<Uint8ClampedArray> result = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4); 342 343 unsigned char* data = result->data(); 344 345 if (rect.x() < 0 346 || rect.y() < 0 347 || rect.maxX() > size.width() 348 || rect.maxY() > size.height()) 349 result->zeroFill(); 350 351 unsigned destBytesPerRow = 4 * rect.width(); 352 SkBitmap destBitmap; 353 destBitmap.setConfig(SkBitmap::kARGB_8888_Config, rect.width(), rect.height(), destBytesPerRow); 354 destBitmap.setPixels(data); 355 356 SkCanvas::Config8888 config8888; 357 if (multiplied == Premultiplied) 358 config8888 = SkCanvas::kRGBA_Premul_Config8888; 359 else 360 config8888 = SkCanvas::kRGBA_Unpremul_Config8888; 361 362 context->readPixels(&destBitmap, rect.x(), rect.y(), config8888); 363 return result.release(); 364 } 365 366 PassRefPtr<Uint8ClampedArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect, CoordinateSystem) const 367 { 368 if (!isValid()) 369 return Uint8ClampedArray::create(rect.width() * rect.height() * 4); 370 return getImageData<Unmultiplied>(rect, context(), m_size); 371 } 372 373 PassRefPtr<Uint8ClampedArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect, CoordinateSystem) const 374 { 375 if (!isValid()) 376 return Uint8ClampedArray::create(rect.width() * rect.height() * 4); 377 return getImageData<Premultiplied>(rect, context(), m_size); 378 } 379 380 void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, CoordinateSystem) 381 { 382 if (!isValid()) 383 return; 384 385 ASSERT(sourceRect.width() > 0); 386 ASSERT(sourceRect.height() > 0); 387 388 int originX = sourceRect.x(); 389 int destX = destPoint.x() + sourceRect.x(); 390 ASSERT(destX >= 0); 391 ASSERT(destX < m_size.width()); 392 ASSERT(originX >= 0); 393 ASSERT(originX < sourceRect.maxX()); 394 395 int endX = destPoint.x() + sourceRect.maxX(); 396 ASSERT(endX <= m_size.width()); 397 398 int numColumns = endX - destX; 399 400 int originY = sourceRect.y(); 401 int destY = destPoint.y() + sourceRect.y(); 402 ASSERT(destY >= 0); 403 ASSERT(destY < m_size.height()); 404 ASSERT(originY >= 0); 405 ASSERT(originY < sourceRect.maxY()); 406 407 int endY = destPoint.y() + sourceRect.maxY(); 408 ASSERT(endY <= m_size.height()); 409 int numRows = endY - destY; 410 411 unsigned srcBytesPerRow = 4 * sourceSize.width(); 412 SkBitmap srcBitmap; 413 srcBitmap.setConfig(SkBitmap::kARGB_8888_Config, numColumns, numRows, srcBytesPerRow); 414 srcBitmap.setPixels(source->data() + originY * srcBytesPerRow + originX * 4); 415 416 SkCanvas::Config8888 config8888; 417 if (multiplied == Premultiplied) 418 config8888 = SkCanvas::kRGBA_Premul_Config8888; 419 else 420 config8888 = SkCanvas::kRGBA_Unpremul_Config8888; 421 422 context()->writePixels(srcBitmap, destX, destY, config8888); 423 } 424 425 void ImageBuffer::convertToLuminanceMask() 426 { 427 IntRect luminanceRect(IntPoint(), internalSize()); 428 RefPtr<Uint8ClampedArray> srcPixelArray = getUnmultipliedImageData(luminanceRect); 429 430 unsigned pixelArrayLength = srcPixelArray->length(); 431 for (unsigned pixelOffset = 0; pixelOffset < pixelArrayLength; pixelOffset += 4) { 432 unsigned char a = srcPixelArray->item(pixelOffset + 3); 433 if (!a) 434 continue; 435 unsigned char r = srcPixelArray->item(pixelOffset); 436 unsigned char g = srcPixelArray->item(pixelOffset + 1); 437 unsigned char b = srcPixelArray->item(pixelOffset + 2); 438 439 double luma = (r * 0.2125 + g * 0.7154 + b * 0.0721) * ((double)a / 255.0); 440 srcPixelArray->set(pixelOffset + 3, luma); 441 } 442 putByteArray(Unmultiplied, srcPixelArray.get(), luminanceRect.size(), luminanceRect, IntPoint()); 443 } 444 445 template <typename T> 446 static bool encodeImage(T& source, const String& mimeType, const double* quality, Vector<char>* output) 447 { 448 Vector<unsigned char>* encodedImage = reinterpret_cast<Vector<unsigned char>*>(output); 449 450 if (mimeType == "image/jpeg") { 451 int compressionQuality = JPEGImageEncoder::DefaultCompressionQuality; 452 if (quality && *quality >= 0.0 && *quality <= 1.0) 453 compressionQuality = static_cast<int>(*quality * 100 + 0.5); 454 if (!JPEGImageEncoder::encode(source, compressionQuality, encodedImage)) 455 return false; 456 } else if (mimeType == "image/webp") { 457 int compressionQuality = WEBPImageEncoder::DefaultCompressionQuality; 458 if (quality && *quality >= 0.0 && *quality <= 1.0) 459 compressionQuality = static_cast<int>(*quality * 100 + 0.5); 460 if (!WEBPImageEncoder::encode(source, compressionQuality, encodedImage)) 461 return false; 462 } else { 463 if (!PNGImageEncoder::encode(source, encodedImage)) 464 return false; 465 ASSERT(mimeType == "image/png"); 466 } 467 468 return true; 469 } 470 471 String ImageBuffer::toDataURL(const String& mimeType, const double* quality, CoordinateSystem) const 472 { 473 ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)); 474 475 Vector<char> encodedImage; 476 if (!isValid() || !encodeImage(*context()->bitmap(), mimeType, quality, &encodedImage)) 477 return "data:,"; 478 Vector<char> base64Data; 479 base64Encode(encodedImage, base64Data); 480 481 return "data:" + mimeType + ";base64," + base64Data; 482 } 483 484 String ImageDataToDataURL(const ImageData& imageData, const String& mimeType, const double* quality) 485 { 486 ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)); 487 488 Vector<char> encodedImage; 489 if (!encodeImage(imageData, mimeType, quality, &encodedImage)) 490 return "data:,"; 491 492 Vector<char> base64Data; 493 base64Encode(encodedImage, base64Data); 494 495 return "data:" + mimeType + ";base64," + base64Data; 496 } 497 498 } // namespace WebCore 499