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 "platform/MIMETypeRegistry.h" 37 #include "platform/geometry/IntRect.h" 38 #include "platform/graphics/BitmapImage.h" 39 #include "platform/graphics/Extensions3D.h" 40 #include "platform/graphics/GraphicsContext.h" 41 #include "platform/graphics/GraphicsContext3D.h" 42 #include "platform/graphics/UnacceleratedImageBufferSurface.h" 43 #include "platform/graphics/gpu/DrawingBuffer.h" 44 #include "platform/graphics/gpu/SharedGraphicsContext3D.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 "third_party/skia/include/effects/SkTableColorFilter.h" 52 #include "wtf/MathExtras.h" 53 #include "wtf/text/Base64.h" 54 #include "wtf/text/WTFString.h" 55 56 using namespace std; 57 58 namespace WebCore { 59 60 PassOwnPtr<ImageBuffer> ImageBuffer::create(PassOwnPtr<ImageBufferSurface> surface) 61 { 62 if (!surface->isValid()) 63 return nullptr; 64 return adoptPtr(new ImageBuffer(surface)); 65 } 66 67 PassOwnPtr<ImageBuffer> ImageBuffer::create(const IntSize& size, OpacityMode opacityMode) 68 { 69 OwnPtr<ImageBufferSurface> surface = adoptPtr(new UnacceleratedImageBufferSurface(size, opacityMode)); 70 if (!surface->isValid()) 71 return nullptr; 72 return adoptPtr(new ImageBuffer(surface.release())); 73 } 74 75 ImageBuffer::ImageBuffer(PassOwnPtr<ImageBufferSurface> surface) 76 : m_surface(surface) 77 { 78 if (m_surface->canvas()) { 79 m_context = adoptPtr(new GraphicsContext(m_surface->canvas())); 80 m_context->setCertainlyOpaque(m_surface->opacityMode() == Opaque); 81 m_context->setAccelerated(m_surface->isAccelerated()); 82 } 83 } 84 85 ImageBuffer::~ImageBuffer() 86 { 87 } 88 89 GraphicsContext* ImageBuffer::context() const 90 { 91 m_surface->willUse(); 92 ASSERT(m_context.get()); 93 return m_context.get(); 94 } 95 96 const SkBitmap& ImageBuffer::bitmap() const 97 { 98 m_surface->willUse(); 99 return m_surface->bitmap(); 100 } 101 102 bool ImageBuffer::isValid() const 103 { 104 return m_surface->isValid(); 105 } 106 107 static SkBitmap deepSkBitmapCopy(const SkBitmap& bitmap) 108 { 109 SkBitmap tmp; 110 if (!bitmap.deepCopyTo(&tmp, bitmap.config())) 111 bitmap.copyTo(&tmp, bitmap.config()); 112 113 return tmp; 114 } 115 116 PassRefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, ScaleBehavior) const 117 { 118 if (!isValid()) 119 return BitmapImage::create(NativeImageSkia::create()); 120 121 const SkBitmap& bitmap = m_surface->bitmap(); 122 return BitmapImage::create(NativeImageSkia::create(copyBehavior == CopyBackingStore ? deepSkBitmapCopy(bitmap) : bitmap)); 123 } 124 125 BackingStoreCopy ImageBuffer::fastCopyImageMode() 126 { 127 return DontCopyBackingStore; 128 } 129 130 blink::WebLayer* ImageBuffer::platformLayer() const 131 { 132 return m_surface->layer(); 133 } 134 135 bool ImageBuffer::copyToPlatformTexture(GraphicsContext3D& context, Platform3DObject texture, GC3Denum internalFormat, GC3Denum destType, GC3Dint level, bool premultiplyAlpha, bool flipY) 136 { 137 if (!m_surface->isAccelerated() || !platformLayer() || !isValid()) 138 return false; 139 140 if (!context.makeContextCurrent()) 141 return false; 142 143 Extensions3D* extensions = context.extensions(); 144 if (!extensions->supports("GL_CHROMIUM_copy_texture") || !extensions->supports("GL_CHROMIUM_flipy") 145 || !extensions->canUseCopyTextureCHROMIUM(internalFormat, destType, level)) 146 return false; 147 148 // The canvas is stored in a premultiplied format, so unpremultiply if necessary. 149 context.pixelStorei(Extensions3D::UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, !premultiplyAlpha); 150 151 // The canvas is stored in an inverted position, so the flip semantics are reversed. 152 context.pixelStorei(Extensions3D::UNPACK_FLIP_Y_CHROMIUM, !flipY); 153 extensions->copyTextureCHROMIUM(GL_TEXTURE_2D, getBackingTexture(), texture, level, internalFormat, destType); 154 155 context.pixelStorei(Extensions3D::UNPACK_FLIP_Y_CHROMIUM, false); 156 context.pixelStorei(Extensions3D::UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, false); 157 context.flush(); 158 return true; 159 } 160 161 static bool drawNeedsCopy(GraphicsContext* src, GraphicsContext* dst) 162 { 163 ASSERT(dst); 164 return (src == dst); 165 } 166 167 Platform3DObject ImageBuffer::getBackingTexture() 168 { 169 return m_surface->getBackingTexture(); 170 } 171 172 bool ImageBuffer::copyRenderingResultsFromDrawingBuffer(DrawingBuffer* drawingBuffer) 173 { 174 if (!drawingBuffer) 175 return false; 176 RefPtr<GraphicsContext3D> context3D = SharedGraphicsContext3D::get(); 177 Platform3DObject tex = m_surface->getBackingTexture(); 178 if (!context3D || !tex) 179 return false; 180 181 return drawingBuffer->copyToPlatformTexture(*(context3D.get()), tex, GL_RGBA, 182 GL_UNSIGNED_BYTE, 0, true, false); 183 } 184 185 void ImageBuffer::draw(GraphicsContext* context, const FloatRect& destRect, const FloatRect& srcRect, 186 CompositeOperator op, blink::WebBlendMode blendMode, bool useLowQualityScale) 187 { 188 if (!isValid()) 189 return; 190 191 const SkBitmap& bitmap = m_surface->bitmap(); 192 RefPtr<Image> image = BitmapImage::create(NativeImageSkia::create(drawNeedsCopy(m_context.get(), context) ? deepSkBitmapCopy(bitmap) : bitmap)); 193 context->drawImage(image.get(), destRect, srcRect, op, blendMode, DoNotRespectImageOrientation, useLowQualityScale); 194 } 195 196 void ImageBuffer::flush() 197 { 198 if (m_surface->canvas()) { 199 m_surface->canvas()->flush(); 200 } 201 } 202 203 void ImageBuffer::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const FloatSize& scale, 204 const FloatPoint& phase, CompositeOperator op, const FloatRect& destRect, blink::WebBlendMode blendMode, const IntSize& repeatSpacing) 205 { 206 if (!isValid()) 207 return; 208 209 const SkBitmap& bitmap = m_surface->bitmap(); 210 RefPtr<Image> image = BitmapImage::create(NativeImageSkia::create(drawNeedsCopy(m_context.get(), context) ? deepSkBitmapCopy(bitmap) : bitmap)); 211 image->drawPattern(context, srcRect, scale, phase, op, destRect, blendMode, repeatSpacing); 212 } 213 214 static const Vector<uint8_t>& getLinearRgbLUT() 215 { 216 DEFINE_STATIC_LOCAL(Vector<uint8_t>, linearRgbLUT, ()); 217 if (linearRgbLUT.isEmpty()) { 218 linearRgbLUT.reserveCapacity(256); 219 for (unsigned i = 0; i < 256; i++) { 220 float color = i / 255.0f; 221 color = (color <= 0.04045f ? color / 12.92f : pow((color + 0.055f) / 1.055f, 2.4f)); 222 color = std::max(0.0f, color); 223 color = std::min(1.0f, color); 224 linearRgbLUT.append(static_cast<uint8_t>(round(color * 255))); 225 } 226 } 227 return linearRgbLUT; 228 } 229 230 static const Vector<uint8_t>& getDeviceRgbLUT() 231 { 232 DEFINE_STATIC_LOCAL(Vector<uint8_t>, deviceRgbLUT, ()); 233 if (deviceRgbLUT.isEmpty()) { 234 deviceRgbLUT.reserveCapacity(256); 235 for (unsigned i = 0; i < 256; i++) { 236 float color = i / 255.0f; 237 color = (powf(color, 1.0f / 2.4f) * 1.055f) - 0.055f; 238 color = std::max(0.0f, color); 239 color = std::min(1.0f, color); 240 deviceRgbLUT.append(static_cast<uint8_t>(round(color * 255))); 241 } 242 } 243 return deviceRgbLUT; 244 } 245 246 void ImageBuffer::transformColorSpace(ColorSpace srcColorSpace, ColorSpace dstColorSpace) 247 { 248 if (srcColorSpace == dstColorSpace) 249 return; 250 251 // only sRGB <-> linearRGB are supported at the moment 252 if ((srcColorSpace != ColorSpaceLinearRGB && srcColorSpace != ColorSpaceDeviceRGB) 253 || (dstColorSpace != ColorSpaceLinearRGB && dstColorSpace != ColorSpaceDeviceRGB)) 254 return; 255 256 // FIXME: Disable color space conversions on accelerated canvases (for now). 257 if (context()->isAccelerated() || !isValid()) 258 return; 259 260 const SkBitmap& bitmap = m_surface->bitmap(); 261 if (bitmap.isNull()) 262 return; 263 264 const Vector<uint8_t>& lookUpTable = dstColorSpace == ColorSpaceLinearRGB ? 265 getLinearRgbLUT() : getDeviceRgbLUT(); 266 267 ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config); 268 IntSize size = m_surface->size(); 269 SkAutoLockPixels bitmapLock(bitmap); 270 for (int y = 0; y < size.height(); ++y) { 271 uint32_t* srcRow = bitmap.getAddr32(0, y); 272 for (int x = 0; x < size.width(); ++x) { 273 SkColor color = SkPMColorToColor(srcRow[x]); 274 srcRow[x] = SkPreMultiplyARGB( 275 SkColorGetA(color), 276 lookUpTable[SkColorGetR(color)], 277 lookUpTable[SkColorGetG(color)], 278 lookUpTable[SkColorGetB(color)]); 279 } 280 } 281 } 282 283 PassRefPtr<SkColorFilter> ImageBuffer::createColorSpaceFilter(ColorSpace srcColorSpace, 284 ColorSpace dstColorSpace) 285 { 286 if ((srcColorSpace == dstColorSpace) 287 || (srcColorSpace != ColorSpaceLinearRGB && srcColorSpace != ColorSpaceDeviceRGB) 288 || (dstColorSpace != ColorSpaceLinearRGB && dstColorSpace != ColorSpaceDeviceRGB)) 289 return 0; 290 291 const uint8_t* lut = 0; 292 if (dstColorSpace == ColorSpaceLinearRGB) 293 lut = &getLinearRgbLUT()[0]; 294 else if (dstColorSpace == ColorSpaceDeviceRGB) 295 lut = &getDeviceRgbLUT()[0]; 296 else 297 return 0; 298 299 return adoptRef(SkTableColorFilter::CreateARGB(0, lut, lut, lut)); 300 } 301 302 template <Multiply multiplied> 303 PassRefPtr<Uint8ClampedArray> getImageData(const IntRect& rect, GraphicsContext* context, const IntSize& size) 304 { 305 float area = 4.0f * rect.width() * rect.height(); 306 if (area > static_cast<float>(std::numeric_limits<int>::max())) 307 return 0; 308 309 RefPtr<Uint8ClampedArray> result = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4); 310 311 unsigned char* data = result->data(); 312 313 if (rect.x() < 0 314 || rect.y() < 0 315 || rect.maxX() > size.width() 316 || rect.maxY() > size.height()) 317 result->zeroFill(); 318 319 unsigned destBytesPerRow = 4 * rect.width(); 320 SkBitmap destBitmap; 321 destBitmap.setConfig(SkBitmap::kARGB_8888_Config, rect.width(), rect.height(), destBytesPerRow); 322 destBitmap.setPixels(data); 323 324 SkCanvas::Config8888 config8888; 325 if (multiplied == Premultiplied) 326 config8888 = SkCanvas::kRGBA_Premul_Config8888; 327 else 328 config8888 = SkCanvas::kRGBA_Unpremul_Config8888; 329 330 context->readPixels(&destBitmap, rect.x(), rect.y(), config8888); 331 return result.release(); 332 } 333 334 PassRefPtr<Uint8ClampedArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect) const 335 { 336 if (!isValid()) 337 return Uint8ClampedArray::create(rect.width() * rect.height() * 4); 338 return getImageData<Unmultiplied>(rect, context(), m_surface->size()); 339 } 340 341 PassRefPtr<Uint8ClampedArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect) const 342 { 343 if (!isValid()) 344 return Uint8ClampedArray::create(rect.width() * rect.height() * 4); 345 return getImageData<Premultiplied>(rect, context(), m_surface->size()); 346 } 347 348 void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint) 349 { 350 if (!isValid()) 351 return; 352 353 ASSERT(sourceRect.width() > 0); 354 ASSERT(sourceRect.height() > 0); 355 356 int originX = sourceRect.x(); 357 int destX = destPoint.x() + sourceRect.x(); 358 ASSERT(destX >= 0); 359 ASSERT(destX < m_surface->size().width()); 360 ASSERT(originX >= 0); 361 ASSERT(originX < sourceRect.maxX()); 362 363 int endX = destPoint.x() + sourceRect.maxX(); 364 ASSERT(endX <= m_surface->size().width()); 365 366 int numColumns = endX - destX; 367 368 int originY = sourceRect.y(); 369 int destY = destPoint.y() + sourceRect.y(); 370 ASSERT(destY >= 0); 371 ASSERT(destY < m_surface->size().height()); 372 ASSERT(originY >= 0); 373 ASSERT(originY < sourceRect.maxY()); 374 375 int endY = destPoint.y() + sourceRect.maxY(); 376 ASSERT(endY <= m_surface->size().height()); 377 int numRows = endY - destY; 378 379 unsigned srcBytesPerRow = 4 * sourceSize.width(); 380 SkBitmap srcBitmap; 381 srcBitmap.setConfig(SkBitmap::kARGB_8888_Config, numColumns, numRows, srcBytesPerRow); 382 srcBitmap.setPixels(source->data() + originY * srcBytesPerRow + originX * 4); 383 384 SkCanvas::Config8888 config8888; 385 if (multiplied == Premultiplied) 386 config8888 = SkCanvas::kRGBA_Premul_Config8888; 387 else 388 config8888 = SkCanvas::kRGBA_Unpremul_Config8888; 389 390 context()->writePixels(srcBitmap, destX, destY, config8888); 391 } 392 393 template <typename T> 394 static bool encodeImage(T& source, const String& mimeType, const double* quality, Vector<char>* output) 395 { 396 Vector<unsigned char>* encodedImage = reinterpret_cast<Vector<unsigned char>*>(output); 397 398 if (mimeType == "image/jpeg") { 399 int compressionQuality = JPEGImageEncoder::DefaultCompressionQuality; 400 if (quality && *quality >= 0.0 && *quality <= 1.0) 401 compressionQuality = static_cast<int>(*quality * 100 + 0.5); 402 if (!JPEGImageEncoder::encode(source, compressionQuality, encodedImage)) 403 return false; 404 } else if (mimeType == "image/webp") { 405 int compressionQuality = WEBPImageEncoder::DefaultCompressionQuality; 406 if (quality && *quality >= 0.0 && *quality <= 1.0) 407 compressionQuality = static_cast<int>(*quality * 100 + 0.5); 408 if (!WEBPImageEncoder::encode(source, compressionQuality, encodedImage)) 409 return false; 410 } else { 411 if (!PNGImageEncoder::encode(source, encodedImage)) 412 return false; 413 ASSERT(mimeType == "image/png"); 414 } 415 416 return true; 417 } 418 419 String ImageBuffer::toDataURL(const String& mimeType, const double* quality) const 420 { 421 ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)); 422 423 Vector<char> encodedImage; 424 if (!isValid() || !encodeImage(m_surface->bitmap(), mimeType, quality, &encodedImage)) 425 return "data:,"; 426 Vector<char> base64Data; 427 base64Encode(encodedImage, base64Data); 428 429 return "data:" + mimeType + ";base64," + base64Data; 430 } 431 432 String ImageDataToDataURL(const ImageDataBuffer& imageData, const String& mimeType, const double* quality) 433 { 434 ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)); 435 436 Vector<char> encodedImage; 437 if (!encodeImage(imageData, mimeType, quality, &encodedImage)) 438 return "data:,"; 439 440 Vector<char> base64Data; 441 base64Encode(encodedImage, base64Data); 442 443 return "data:" + mimeType + ";base64," + base64Data; 444 } 445 446 } // namespace WebCore 447