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 "GrContext.h"
     37 #include "platform/MIMETypeRegistry.h"
     38 #include "platform/geometry/IntRect.h"
     39 #include "platform/graphics/BitmapImage.h"
     40 #include "platform/graphics/GraphicsContext.h"
     41 #include "platform/graphics/GraphicsTypes3D.h"
     42 #include "platform/graphics/ImageBufferClient.h"
     43 #include "platform/graphics/UnacceleratedImageBufferSurface.h"
     44 #include "platform/graphics/gpu/DrawingBuffer.h"
     45 #include "platform/graphics/gpu/Extensions3DUtil.h"
     46 #include "platform/graphics/skia/NativeImageSkia.h"
     47 #include "platform/graphics/skia/SkiaUtils.h"
     48 #include "platform/image-encoders/skia/JPEGImageEncoder.h"
     49 #include "platform/image-encoders/skia/PNGImageEncoder.h"
     50 #include "platform/image-encoders/skia/WEBPImageEncoder.h"
     51 #include "public/platform/Platform.h"
     52 #include "public/platform/WebExternalTextureMailbox.h"
     53 #include "public/platform/WebGraphicsContext3D.h"
     54 #include "public/platform/WebGraphicsContext3DProvider.h"
     55 #include "third_party/skia/include/core/SkPicture.h"
     56 #include "third_party/skia/include/effects/SkTableColorFilter.h"
     57 #include "wtf/MathExtras.h"
     58 #include "wtf/Vector.h"
     59 #include "wtf/text/Base64.h"
     60 #include "wtf/text/WTFString.h"
     61 
     62 namespace blink {
     63 
     64 PassOwnPtr<ImageBuffer> ImageBuffer::create(PassOwnPtr<ImageBufferSurface> surface)
     65 {
     66     if (!surface->isValid())
     67         return nullptr;
     68     return adoptPtr(new ImageBuffer(surface));
     69 }
     70 
     71 PassOwnPtr<ImageBuffer> ImageBuffer::create(const IntSize& size, OpacityMode opacityMode)
     72 {
     73     OwnPtr<ImageBufferSurface> surface = adoptPtr(new UnacceleratedImageBufferSurface(size, opacityMode));
     74     if (!surface->isValid())
     75         return nullptr;
     76     return adoptPtr(new ImageBuffer(surface.release()));
     77 }
     78 
     79 ImageBuffer::ImageBuffer(PassOwnPtr<ImageBufferSurface> surface)
     80     : m_surface(surface)
     81     , m_client(0)
     82 {
     83     if (m_surface->canvas()) {
     84         m_context = adoptPtr(new GraphicsContext(m_surface->canvas()));
     85         m_context->setCertainlyOpaque(m_surface->opacityMode() == Opaque);
     86         m_context->setAccelerated(m_surface->isAccelerated());
     87     }
     88     m_surface->setImageBuffer(this);
     89 }
     90 
     91 ImageBuffer::~ImageBuffer()
     92 {
     93 }
     94 
     95 GraphicsContext* ImageBuffer::context() const
     96 {
     97     if (!isSurfaceValid())
     98         return 0;
     99     ASSERT(m_context.get());
    100     return m_context.get();
    101 }
    102 
    103 const SkBitmap& ImageBuffer::bitmap() const
    104 {
    105     return m_surface->bitmap();
    106 }
    107 
    108 bool ImageBuffer::isSurfaceValid() const
    109 {
    110     return m_surface->isValid();
    111 }
    112 
    113 bool ImageBuffer::isDirty()
    114 {
    115     return m_client ? m_client->isDirty() : false;
    116 }
    117 
    118 void ImageBuffer::didFinalizeFrame()
    119 {
    120     if (m_client)
    121         m_client->didFinalizeFrame();
    122 }
    123 
    124 void ImageBuffer::finalizeFrame(const FloatRect &dirtyRect)
    125 {
    126     m_surface->finalizeFrame(dirtyRect);
    127     didFinalizeFrame();
    128 }
    129 
    130 bool ImageBuffer::restoreSurface() const
    131 {
    132     return m_surface->isValid() || m_surface->restore();
    133 }
    134 
    135 void ImageBuffer::notifySurfaceInvalid()
    136 {
    137     if (m_client)
    138         m_client->notifySurfaceInvalid();
    139 }
    140 
    141 static SkBitmap deepSkBitmapCopy(const SkBitmap& bitmap)
    142 {
    143     SkBitmap tmp;
    144     if (!bitmap.deepCopyTo(&tmp))
    145         bitmap.copyTo(&tmp, bitmap.colorType());
    146 
    147     return tmp;
    148 }
    149 
    150 PassRefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, ScaleBehavior) const
    151 {
    152     if (!isSurfaceValid())
    153         return BitmapImage::create(NativeImageSkia::create());
    154 
    155     const SkBitmap& bitmap = m_surface->bitmap();
    156     return BitmapImage::create(NativeImageSkia::create(copyBehavior == CopyBackingStore ? deepSkBitmapCopy(bitmap) : bitmap));
    157 }
    158 
    159 BackingStoreCopy ImageBuffer::fastCopyImageMode()
    160 {
    161     return DontCopyBackingStore;
    162 }
    163 
    164 WebLayer* ImageBuffer::platformLayer() const
    165 {
    166     return m_surface->layer();
    167 }
    168 
    169 bool ImageBuffer::copyToPlatformTexture(WebGraphicsContext3D* context, Platform3DObject texture, GLenum internalFormat, GLenum destType, GLint level, bool premultiplyAlpha, bool flipY)
    170 {
    171     if (!m_surface->isAccelerated() || !getBackingTexture() || !isSurfaceValid())
    172         return false;
    173 
    174     if (!Extensions3DUtil::canUseCopyTextureCHROMIUM(internalFormat, destType, level))
    175         return false;
    176 
    177     OwnPtr<WebGraphicsContext3DProvider> provider = adoptPtr(Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
    178     if (!provider)
    179         return false;
    180     WebGraphicsContext3D* sharedContext = provider->context3d();
    181     if (!sharedContext)
    182         return false;
    183 
    184     OwnPtr<WebExternalTextureMailbox> mailbox = adoptPtr(new WebExternalTextureMailbox);
    185 
    186     // Contexts may be in a different share group. We must transfer the texture through a mailbox first
    187     sharedContext->genMailboxCHROMIUM(mailbox->name);
    188     sharedContext->produceTextureDirectCHROMIUM(getBackingTexture(), GL_TEXTURE_2D, mailbox->name);
    189     sharedContext->flush();
    190 
    191     mailbox->syncPoint = sharedContext->insertSyncPoint();
    192 
    193     context->waitSyncPoint(mailbox->syncPoint);
    194     Platform3DObject sourceTexture = context->createAndConsumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox->name);
    195 
    196     // The canvas is stored in a premultiplied format, so unpremultiply if necessary.
    197     context->pixelStorei(GC3D_UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, !premultiplyAlpha);
    198 
    199     // The canvas is stored in an inverted position, so the flip semantics are reversed.
    200     context->pixelStorei(GC3D_UNPACK_FLIP_Y_CHROMIUM, !flipY);
    201     context->copyTextureCHROMIUM(GL_TEXTURE_2D, sourceTexture, texture, level, internalFormat, destType);
    202 
    203     context->pixelStorei(GC3D_UNPACK_FLIP_Y_CHROMIUM, false);
    204     context->pixelStorei(GC3D_UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, false);
    205 
    206     context->deleteTexture(sourceTexture);
    207 
    208     context->flush();
    209     sharedContext->waitSyncPoint(context->insertSyncPoint());
    210 
    211     // Undo grContext texture binding changes introduced in this function
    212     provider->grContext()->resetContext(kTextureBinding_GrGLBackendState);
    213 
    214     return true;
    215 }
    216 
    217 static bool drawNeedsCopy(GraphicsContext* src, GraphicsContext* dst)
    218 {
    219     ASSERT(dst);
    220     return (src == dst);
    221 }
    222 
    223 Platform3DObject ImageBuffer::getBackingTexture()
    224 {
    225     return m_surface->getBackingTexture();
    226 }
    227 
    228 void ImageBuffer::didModifyBackingTexture()
    229 {
    230     m_surface->didModifyBackingTexture();
    231 }
    232 
    233 bool ImageBuffer::copyRenderingResultsFromDrawingBuffer(DrawingBuffer* drawingBuffer, bool fromFrontBuffer)
    234 {
    235     if (!drawingBuffer)
    236         return false;
    237     OwnPtr<WebGraphicsContext3DProvider> provider = adoptPtr(Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
    238     if (!provider)
    239         return false;
    240     WebGraphicsContext3D* context3D = provider->context3d();
    241     Platform3DObject tex = m_surface->getBackingTexture();
    242     if (!context3D || !tex)
    243         return false;
    244 
    245     m_surface->invalidateCachedBitmap();
    246     bool result = drawingBuffer->copyToPlatformTexture(context3D, tex, GL_RGBA,
    247         GL_UNSIGNED_BYTE, 0, true, false, fromFrontBuffer);
    248 
    249     if (result) {
    250         m_surface->didModifyBackingTexture();
    251     }
    252 
    253     return result;
    254 }
    255 
    256 void ImageBuffer::draw(GraphicsContext* context, const FloatRect& destRect, const FloatRect* srcPtr, CompositeOperator op, WebBlendMode blendMode)
    257 {
    258     if (!isSurfaceValid())
    259         return;
    260 
    261     FloatRect srcRect = srcPtr ? *srcPtr : FloatRect(FloatPoint(), size());
    262     RefPtr<SkPicture> picture = m_surface->getPicture();
    263     if (picture) {
    264         context->drawPicture(picture.release(), destRect, srcRect, op, blendMode);
    265         return;
    266     }
    267 
    268     SkBitmap bitmap = m_surface->bitmap();
    269     // For ImageBufferSurface that enables cachedBitmap, Use the cached Bitmap for CPU side usage
    270     // if it is available, otherwise generate and use it.
    271     if (!context->isAccelerated() && m_surface->isAccelerated() && m_surface->cachedBitmapEnabled() && isSurfaceValid()) {
    272         m_surface->updateCachedBitmapIfNeeded();
    273         bitmap = m_surface->cachedBitmap();
    274     }
    275 
    276     RefPtr<Image> image = BitmapImage::create(NativeImageSkia::create(drawNeedsCopy(m_context.get(), context) ? deepSkBitmapCopy(bitmap) : bitmap));
    277 
    278     context->drawImage(image.get(), destRect, srcRect, op, blendMode, DoNotRespectImageOrientation);
    279 }
    280 
    281 void ImageBuffer::flush()
    282 {
    283     if (m_surface->canvas()) {
    284         m_surface->canvas()->flush();
    285     }
    286 }
    287 
    288 void ImageBuffer::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const FloatSize& scale,
    289     const FloatPoint& phase, CompositeOperator op, const FloatRect& destRect, WebBlendMode blendMode, const IntSize& repeatSpacing)
    290 {
    291     if (!isSurfaceValid())
    292         return;
    293 
    294     const SkBitmap& bitmap = m_surface->bitmap();
    295     RefPtr<Image> image = BitmapImage::create(NativeImageSkia::create(drawNeedsCopy(m_context.get(), context) ? deepSkBitmapCopy(bitmap) : bitmap));
    296     image->drawPattern(context, srcRect, scale, phase, op, destRect, blendMode, repeatSpacing);
    297 }
    298 
    299 void ImageBuffer::transformColorSpace(ColorSpace srcColorSpace, ColorSpace dstColorSpace)
    300 {
    301     const uint8_t* lookUpTable = ColorSpaceUtilities::getConversionLUT(dstColorSpace, srcColorSpace);
    302     if (!lookUpTable)
    303         return;
    304 
    305     // FIXME: Disable color space conversions on accelerated canvases (for now).
    306     if (context()->isAccelerated() || !isSurfaceValid())
    307         return;
    308 
    309     const SkBitmap& bitmap = m_surface->bitmap();
    310     if (bitmap.isNull())
    311         return;
    312 
    313     ASSERT(bitmap.colorType() == kN32_SkColorType);
    314     IntSize size = m_surface->size();
    315     SkAutoLockPixels bitmapLock(bitmap);
    316     for (int y = 0; y < size.height(); ++y) {
    317         uint32_t* srcRow = bitmap.getAddr32(0, y);
    318         for (int x = 0; x < size.width(); ++x) {
    319             SkColor color = SkPMColorToColor(srcRow[x]);
    320             srcRow[x] = SkPreMultiplyARGB(
    321                 SkColorGetA(color),
    322                 lookUpTable[SkColorGetR(color)],
    323                 lookUpTable[SkColorGetG(color)],
    324                 lookUpTable[SkColorGetB(color)]);
    325         }
    326     }
    327 }
    328 
    329 PassRefPtr<SkColorFilter> ImageBuffer::createColorSpaceFilter(ColorSpace srcColorSpace,
    330     ColorSpace dstColorSpace)
    331 {
    332     const uint8_t* lut = ColorSpaceUtilities::getConversionLUT(dstColorSpace, srcColorSpace);
    333     if (!lut)
    334         return nullptr;
    335 
    336     return adoptRef(SkTableColorFilter::CreateARGB(0, lut, lut, lut));
    337 }
    338 
    339 PassRefPtr<Uint8ClampedArray> ImageBuffer::getImageData(Multiply multiplied, const IntRect& rect) const
    340 {
    341     if (!isSurfaceValid())
    342         return Uint8ClampedArray::create(rect.width() * rect.height() * 4);
    343 
    344     float area = 4.0f * rect.width() * rect.height();
    345     if (area > static_cast<float>(std::numeric_limits<int>::max()))
    346         return nullptr;
    347 
    348     RefPtr<Uint8ClampedArray> result = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4);
    349 
    350     if (rect.x() < 0
    351         || rect.y() < 0
    352         || rect.maxX() > m_surface->size().width()
    353         || rect.maxY() > m_surface->size().height())
    354         result->zeroFill();
    355 
    356     SkAlphaType alphaType = (multiplied == Premultiplied) ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
    357     SkImageInfo info = SkImageInfo::Make(rect.width(), rect.height(), kRGBA_8888_SkColorType, alphaType);
    358 
    359     m_surface->willAccessPixels();
    360     context()->readPixels(info, result->data(), 4 * rect.width(), rect.x(), rect.y());
    361     return result.release();
    362 }
    363 
    364 void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint)
    365 {
    366     if (!isSurfaceValid())
    367         return;
    368 
    369     ASSERT(sourceRect.width() > 0);
    370     ASSERT(sourceRect.height() > 0);
    371 
    372     int originX = sourceRect.x();
    373     int destX = destPoint.x() + sourceRect.x();
    374     ASSERT(destX >= 0);
    375     ASSERT(destX < m_surface->size().width());
    376     ASSERT(originX >= 0);
    377     ASSERT(originX < sourceRect.maxX());
    378 
    379     int originY = sourceRect.y();
    380     int destY = destPoint.y() + sourceRect.y();
    381     ASSERT(destY >= 0);
    382     ASSERT(destY < m_surface->size().height());
    383     ASSERT(originY >= 0);
    384     ASSERT(originY < sourceRect.maxY());
    385 
    386     const size_t srcBytesPerRow = 4 * sourceSize.width();
    387     const void* srcAddr = source->data() + originY * srcBytesPerRow + originX * 4;
    388     SkAlphaType alphaType = (multiplied == Premultiplied) ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
    389     SkImageInfo info = SkImageInfo::Make(sourceRect.width(), sourceRect.height(), kRGBA_8888_SkColorType, alphaType);
    390 
    391     m_surface->willAccessPixels();
    392 
    393     context()->writePixels(info, srcAddr, srcBytesPerRow, destX, destY);
    394 }
    395 
    396 template <typename T>
    397 static bool encodeImage(T& source, const String& mimeType, const double* quality, Vector<char>* output)
    398 {
    399     Vector<unsigned char>* encodedImage = reinterpret_cast<Vector<unsigned char>*>(output);
    400 
    401     if (mimeType == "image/jpeg") {
    402         int compressionQuality = JPEGImageEncoder::DefaultCompressionQuality;
    403         if (quality && *quality >= 0.0 && *quality <= 1.0)
    404             compressionQuality = static_cast<int>(*quality * 100 + 0.5);
    405         if (!JPEGImageEncoder::encode(source, compressionQuality, encodedImage))
    406             return false;
    407     } else if (mimeType == "image/webp") {
    408         int compressionQuality = WEBPImageEncoder::DefaultCompressionQuality;
    409         if (quality && *quality >= 0.0 && *quality <= 1.0)
    410             compressionQuality = static_cast<int>(*quality * 100 + 0.5);
    411         if (!WEBPImageEncoder::encode(source, compressionQuality, encodedImage))
    412             return false;
    413     } else {
    414         if (!PNGImageEncoder::encode(source, encodedImage))
    415             return false;
    416         ASSERT(mimeType == "image/png");
    417     }
    418 
    419     return true;
    420 }
    421 
    422 String ImageBuffer::toDataURL(const String& mimeType, const double* quality) const
    423 {
    424     ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
    425 
    426     Vector<char> encodedImage;
    427     if (!isSurfaceValid() || !encodeImage(m_surface->bitmap(), mimeType, quality, &encodedImage))
    428         return "data:,";
    429 
    430     return "data:" + mimeType + ";base64," + base64Encode(encodedImage);
    431 }
    432 
    433 String ImageDataToDataURL(const ImageDataBuffer& imageData, const String& mimeType, const double* quality)
    434 {
    435     ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
    436 
    437     Vector<char> encodedImage;
    438     if (!encodeImage(imageData, mimeType, quality, &encodedImage))
    439         return "data:,";
    440 
    441     return "data:" + mimeType + ";base64," + base64Encode(encodedImage);
    442 }
    443 
    444 } // namespace blink
    445