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 "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