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 "ImageBuffer.h" 35 36 #include "Base64.h" 37 #include "BitmapImage.h" 38 #include "BitmapImageSingleFrameSkia.h" 39 #include "DrawingBuffer.h" 40 #include "GLES2Canvas.h" 41 #include "GraphicsContext.h" 42 #include "ImageData.h" 43 #include "JPEGImageEncoder.h" 44 #include "MIMETypeRegistry.h" 45 #include "PNGImageEncoder.h" 46 #include "PlatformContextSkia.h" 47 #include "SkColorPriv.h" 48 #include "SkiaUtils.h" 49 50 #include <wtf/text/StringConcatenate.h> 51 52 using namespace std; 53 54 namespace WebCore { 55 56 // We pass a technically-uninitialized canvas to the platform context here since 57 // the canvas initialization completes in ImageBuffer::ImageBuffer. But 58 // PlatformContext doesn't actually need to use the object, and this makes all 59 // the ownership easier to manage. 60 ImageBufferData::ImageBufferData(const IntSize& size) 61 : m_platformContext(0) // Canvas is set in ImageBuffer constructor. 62 { 63 } 64 65 ImageBuffer::ImageBuffer(const IntSize& size, ColorSpace, RenderingMode, bool& success) 66 : m_data(size) 67 , m_size(size) 68 { 69 SkCanvas* canvas = skia::CreateBitmapCanvas(size.width(), size.height(), false); 70 if (!canvas) { 71 success = false; 72 return; 73 } 74 75 m_data.m_canvas = canvas; 76 m_data.m_platformContext.setCanvas(m_data.m_canvas.get()); 77 m_context.set(new GraphicsContext(&m_data.m_platformContext)); 78 m_context->platformContext()->setDrawingToImageBuffer(true); 79 80 // Make the background transparent. It would be nice if this wasn't 81 // required, but the canvas is currently filled with the magic transparency 82 // color. Can we have another way to manage this? 83 m_data.m_canvas->drawARGB(0, 0, 0, 0, SkXfermode::kClear_Mode); 84 success = true; 85 } 86 87 ImageBuffer::~ImageBuffer() 88 { 89 } 90 91 GraphicsContext* ImageBuffer::context() const 92 { 93 return m_context.get(); 94 } 95 96 size_t ImageBuffer::dataSize() const 97 { 98 return m_size.width() * m_size.height() * 4; 99 } 100 101 bool ImageBuffer::drawsUsingCopy() const 102 { 103 return false; 104 } 105 106 PassRefPtr<Image> ImageBuffer::copyImage() const 107 { 108 m_context->platformContext()->syncSoftwareCanvas(); 109 return BitmapImageSingleFrameSkia::create(*m_data.m_platformContext.bitmap(), true); 110 } 111 112 void ImageBuffer::clip(GraphicsContext* context, const FloatRect& rect) const 113 { 114 context->platformContext()->beginLayerClippedToImage(rect, this); 115 } 116 117 void ImageBuffer::draw(GraphicsContext* context, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, 118 CompositeOperator op, bool useLowQualityScale) 119 { 120 if (m_data.m_platformContext.useGPU() && context->platformContext()->useGPU()) { 121 if (context->platformContext()->canAccelerate()) { 122 m_data.m_platformContext.prepareForHardwareDraw(); 123 DrawingBuffer* sourceDrawingBuffer = m_data.m_platformContext.gpuCanvas()->drawingBuffer(); 124 unsigned sourceTexture = static_cast<unsigned>(sourceDrawingBuffer->platformColorBuffer()); 125 FloatRect destRectNormalized(normalizeRect(destRect)); 126 FloatRect srcRectFlipped(normalizeRect(srcRect)); 127 srcRectFlipped.setY(m_size.height() - srcRect.y()); 128 srcRectFlipped.setHeight(-srcRect.height()); 129 context->platformContext()->prepareForHardwareDraw(); 130 context->platformContext()->gpuCanvas()->drawTexturedRect(sourceTexture, m_size, srcRectFlipped, destRectNormalized, styleColorSpace, op); 131 return; 132 } 133 m_data.m_platformContext.syncSoftwareCanvas(); 134 } 135 136 RefPtr<Image> image = BitmapImageSingleFrameSkia::create(*m_data.m_platformContext.bitmap(), context == m_context); 137 context->drawImage(image.get(), styleColorSpace, destRect, srcRect, op, useLowQualityScale); 138 } 139 140 void ImageBuffer::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const AffineTransform& patternTransform, 141 const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect) 142 { 143 RefPtr<Image> image = BitmapImageSingleFrameSkia::create(*m_data.m_platformContext.bitmap(), context == m_context); 144 image->drawPattern(context, srcRect, patternTransform, phase, styleColorSpace, op, destRect); 145 } 146 147 void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable) 148 { 149 const SkBitmap& bitmap = *context()->platformContext()->bitmap(); 150 if (bitmap.isNull()) 151 return; 152 153 ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config); 154 SkAutoLockPixels bitmapLock(bitmap); 155 for (int y = 0; y < m_size.height(); ++y) { 156 uint32_t* srcRow = bitmap.getAddr32(0, y); 157 for (int x = 0; x < m_size.width(); ++x) { 158 SkColor color = SkPMColorToColor(srcRow[x]); 159 srcRow[x] = SkPreMultiplyARGB(SkColorGetA(color), 160 lookUpTable[SkColorGetR(color)], 161 lookUpTable[SkColorGetG(color)], 162 lookUpTable[SkColorGetB(color)]); 163 } 164 } 165 } 166 167 template <Multiply multiplied> 168 PassRefPtr<ByteArray> getImageData(const IntRect& rect, SkDevice& srcDevice, 169 const IntSize& size) 170 { 171 float area = 4.0f * rect.width() * rect.height(); 172 if (area > static_cast<float>(std::numeric_limits<int>::max())) 173 return 0; 174 175 RefPtr<ByteArray> result = ByteArray::create(rect.width() * rect.height() * 4); 176 177 SkBitmap::Config srcConfig = srcDevice.accessBitmap(false).config(); 178 179 if (srcConfig == SkBitmap::kNo_Config) { 180 // This is an empty SkBitmap that could not be configured. 181 ASSERT(!size.width() || !size.height()); 182 return result.release(); 183 } 184 185 unsigned char* data = result->data(); 186 187 if (rect.x() < 0 188 || rect.y() < 0 189 || rect.maxX() > size.width() 190 || rect.maxY() > size.height()) 191 memset(data, 0, result->length()); 192 193 int originX = rect.x(); 194 int destX = 0; 195 if (originX < 0) { 196 destX = -originX; 197 originX = 0; 198 } 199 int endX = rect.maxX(); 200 if (endX > size.width()) 201 endX = size.width(); 202 int numColumns = endX - originX; 203 204 if (numColumns <= 0) 205 return result.release(); 206 207 int originY = rect.y(); 208 int destY = 0; 209 if (originY < 0) { 210 destY = -originY; 211 originY = 0; 212 } 213 int endY = rect.maxY(); 214 if (endY > size.height()) 215 endY = size.height(); 216 int numRows = endY - originY; 217 218 if (numRows <= 0) 219 return result.release(); 220 221 ASSERT(srcConfig == SkBitmap::kARGB_8888_Config); 222 223 unsigned destBytesPerRow = 4 * rect.width(); 224 225 SkBitmap srcBitmap; 226 srcDevice.readPixels(SkIRect::MakeXYWH(originX, originY, numColumns, numRows), &srcBitmap); 227 228 unsigned char* destRow = data + destY * destBytesPerRow + destX * 4; 229 230 // Do conversion of byte order and alpha divide (if necessary) 231 for (int y = 0; y < numRows; ++y) { 232 SkPMColor* srcBitmapRow = srcBitmap.getAddr32(0, y); 233 for (int x = 0; x < numColumns; ++x) { 234 SkPMColor srcPMColor = srcBitmapRow[x]; 235 unsigned char* destPixel = &destRow[x * 4]; 236 if (multiplied == Unmultiplied) { 237 unsigned char a = SkGetPackedA32(srcPMColor); 238 destPixel[0] = a ? SkGetPackedR32(srcPMColor) * 255 / a : 0; 239 destPixel[1] = a ? SkGetPackedG32(srcPMColor) * 255 / a : 0; 240 destPixel[2] = a ? SkGetPackedB32(srcPMColor) * 255 / a : 0; 241 destPixel[3] = a; 242 } else { 243 // Input and output are both pre-multiplied, we just need to re-arrange the 244 // bytes from the bitmap format to RGBA. 245 destPixel[0] = SkGetPackedR32(srcPMColor); 246 destPixel[1] = SkGetPackedG32(srcPMColor); 247 destPixel[2] = SkGetPackedB32(srcPMColor); 248 destPixel[3] = SkGetPackedA32(srcPMColor); 249 } 250 } 251 destRow += destBytesPerRow; 252 } 253 254 return result.release(); 255 } 256 257 PassRefPtr<ByteArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect) const 258 { 259 context()->platformContext()->syncSoftwareCanvas(); 260 return getImageData<Unmultiplied>(rect, *context()->platformContext()->canvas()->getDevice(), m_size); 261 } 262 263 PassRefPtr<ByteArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect) const 264 { 265 context()->platformContext()->syncSoftwareCanvas(); 266 return getImageData<Premultiplied>(rect, *context()->platformContext()->canvas()->getDevice(), m_size); 267 } 268 269 template <Multiply multiplied> 270 void putImageData(ByteArray*& source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, 271 SkDevice* dstDevice, const IntSize& size) 272 { 273 ASSERT(sourceRect.width() > 0); 274 ASSERT(sourceRect.height() > 0); 275 276 int originX = sourceRect.x(); 277 int destX = destPoint.x() + sourceRect.x(); 278 ASSERT(destX >= 0); 279 ASSERT(destX < size.width()); 280 ASSERT(originX >= 0); 281 ASSERT(originX < sourceRect.maxX()); 282 283 int endX = destPoint.x() + sourceRect.maxX(); 284 ASSERT(endX <= size.width()); 285 286 int numColumns = endX - destX; 287 288 int originY = sourceRect.y(); 289 int destY = destPoint.y() + sourceRect.y(); 290 ASSERT(destY >= 0); 291 ASSERT(destY < size.height()); 292 ASSERT(originY >= 0); 293 ASSERT(originY < sourceRect.maxY()); 294 295 int endY = destPoint.y() + sourceRect.maxY(); 296 ASSERT(endY <= size.height()); 297 int numRows = endY - destY; 298 299 unsigned srcBytesPerRow = 4 * sourceSize.width(); 300 301 SkBitmap deviceBitmap = dstDevice->accessBitmap(true); 302 SkAutoLockPixels deviceAutoLock(deviceBitmap); 303 304 // If the device's bitmap doesn't have pixels we will make a temp and call writePixels on the device. 305 bool temporaryBitmap = !deviceBitmap.getPixels(); 306 SkBitmap destBitmap; 307 308 if (temporaryBitmap) { 309 destBitmap.setConfig(SkBitmap::kARGB_8888_Config, numColumns, numRows, srcBytesPerRow); 310 if (!destBitmap.allocPixels()) 311 CRASH(); 312 } else 313 deviceBitmap.extractSubset(&destBitmap, SkIRect::MakeXYWH(destX, destY, numColumns, numRows)); 314 315 // Whether we made a temporary or not destBitmap is always configured to be written at 0,0 316 SkAutoLockPixels destAutoLock(destBitmap); 317 const unsigned char* srcRow = source->data() + originY * srcBytesPerRow + originX * 4; 318 for (int y = 0; y < numRows; ++y) { 319 SkPMColor* destRow = destBitmap.getAddr32(0, y); 320 for (int x = 0; x < numColumns; ++x) { 321 const unsigned char* srcPixel = &srcRow[x * 4]; 322 if (multiplied == Unmultiplied) { 323 unsigned char alpha = srcPixel[3]; 324 unsigned char r = SkMulDiv255Ceiling(srcPixel[0], alpha); 325 unsigned char g = SkMulDiv255Ceiling(srcPixel[1], alpha); 326 unsigned char b = SkMulDiv255Ceiling(srcPixel[2], alpha); 327 destRow[x] = SkPackARGB32(alpha, r, g, b); 328 } else 329 destRow[x] = SkPackARGB32(srcPixel[3], srcPixel[0], srcPixel[1], srcPixel[2]); 330 } 331 srcRow += srcBytesPerRow; 332 } 333 334 // If we used a temporary then write it to the device 335 if (temporaryBitmap) 336 dstDevice->writePixels(destBitmap, destX, destY); 337 } 338 339 void ImageBuffer::putUnmultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint) 340 { 341 context()->platformContext()->syncSoftwareCanvas(); 342 putImageData<Unmultiplied>(source, sourceSize, sourceRect, destPoint, context()->platformContext()->canvas()->getDevice(), m_size); 343 } 344 345 void ImageBuffer::putPremultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint) 346 { 347 context()->platformContext()->syncSoftwareCanvas(); 348 putImageData<Premultiplied>(source, sourceSize, sourceRect, destPoint, context()->platformContext()->canvas()->getDevice(), m_size); 349 } 350 351 template <typename T> 352 static String ImageToDataURL(T& source, const String& mimeType, const double* quality) 353 { 354 ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)); 355 356 Vector<unsigned char> encodedImage; 357 if (mimeType == "image/jpeg") { 358 int compressionQuality = JPEGImageEncoder::DefaultCompressionQuality; 359 if (quality && *quality >= 0.0 && *quality <= 1.0) 360 compressionQuality = static_cast<int>(*quality * 100 + 0.5); 361 if (!JPEGImageEncoder::encode(source, compressionQuality, &encodedImage)) 362 return "data:,"; 363 } else { 364 if (!PNGImageEncoder::encode(source, &encodedImage)) 365 return "data:,"; 366 ASSERT(mimeType == "image/png"); 367 } 368 369 Vector<char> base64Data; 370 base64Encode(*reinterpret_cast<Vector<char>*>(&encodedImage), base64Data); 371 372 return makeString("data:", mimeType, ";base64,", base64Data); 373 } 374 375 String ImageBuffer::toDataURL(const String& mimeType, const double* quality) const 376 { 377 SkDevice* device = context()->platformContext()->canvas()->getDevice(); 378 SkBitmap bitmap = device->accessBitmap(false); 379 380 // if we can't see the pixels directly, call readPixels() to get a copy. 381 // this could happen if the device is backed by a GPU. 382 bitmap.lockPixels(); // balanced by our destructor, or explicitly if getPixels() fails 383 if (!bitmap.getPixels()) { 384 bitmap.unlockPixels(); 385 SkIRect bounds = SkIRect::MakeWH(device->width(), device->height()); 386 if (!device->readPixels(bounds, &bitmap)) 387 return "data:,"; 388 } 389 390 return ImageToDataURL(bitmap, mimeType, quality); 391 } 392 393 String ImageDataToDataURL(const ImageData& source, const String& mimeType, const double* quality) 394 { 395 return ImageToDataURL(source, mimeType, quality); 396 } 397 398 } // namespace WebCore 399