Home | History | Annotate | Download | only in graphics
      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