diff options
Diffstat (limited to 'Source/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp')
-rw-r--r-- | Source/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp | 178 |
1 files changed, 144 insertions, 34 deletions
diff --git a/Source/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp b/Source/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp index 026f4491d..3cfc2f4d1 100644 --- a/Source/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp +++ b/Source/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp @@ -33,7 +33,6 @@ #include "CairoUtilities.h" #include "Color.h" #include "GraphicsContext.h" -#include "ImageData.h" #include "MIMETypeRegistry.h" #include "NotImplemented.h" #include "Pattern.h" @@ -44,29 +43,72 @@ #include <wtf/text/Base64.h> #include <wtf/text/WTFString.h> +#if ENABLE(ACCELERATED_2D_CANVAS) +#include "GLContext.h" +#include "OpenGLShims.h" +#include "TextureMapperGL.h" +#include <cairo-gl.h> +#endif + using namespace std; namespace WebCore { -ImageBufferData::ImageBufferData(const IntSize&) - : m_surface(0) - , m_platformContext(0) +ImageBufferData::ImageBufferData(const IntSize& size) + : m_platformContext(0) + , m_size(size) +#if ENABLE(ACCELERATED_2D_CANVAS) + , m_texture(0) +#endif +{ +} + +#if ENABLE(ACCELERATED_2D_CANVAS) +PassRefPtr<cairo_surface_t> createCairoGLSurface(const IntSize& size, uint32_t& texture) { + GLContext::sharingContext()->makeContextCurrent(); + + // We must generate the texture ourselves, because there is no Cairo API for extracting it + // from a pre-existing surface. + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glTexImage2D(GL_TEXTURE_2D, 0 /* level */, GL_RGBA8, size.width(), size.height(), 0 /* border */, GL_RGBA, GL_UNSIGNED_BYTE, 0); + + GLContext* context = GLContext::sharingContext(); + cairo_device_t* device = context->cairoDevice(); + + // Thread-awareness is a huge performance hit on non-Intel drivers. + cairo_gl_device_set_thread_aware(device, FALSE); + + return adoptRef(cairo_gl_surface_create_for_texture(device, CAIRO_CONTENT_COLOR_ALPHA, texture, size.width(), size.height())); } +#endif -ImageBuffer::ImageBuffer(const IntSize& size, float /* resolutionScale */, ColorSpace, RenderingMode, DeferralMode, bool& success) +ImageBuffer::ImageBuffer(const IntSize& size, float /* resolutionScale */, ColorSpace, RenderingMode renderingMode, bool& success) : m_data(size) , m_size(size) , m_logicalSize(size) { success = false; // Make early return mean error. - m_data.m_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, - size.width(), - size.height()); - if (cairo_surface_status(m_data.m_surface) != CAIRO_STATUS_SUCCESS) + +#if ENABLE(ACCELERATED_2D_CANVAS) + if (renderingMode == Accelerated) + m_data.m_surface = createCairoGLSurface(size, m_data.m_texture); + else +#endif + m_data.m_surface = adoptRef(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, size.width(), size.height())); + + if (cairo_surface_status(m_data.m_surface.get()) != CAIRO_STATUS_SUCCESS) return; // create will notice we didn't set m_initialized and fail. - RefPtr<cairo_t> cr = adoptRef(cairo_create(m_data.m_surface)); + RefPtr<cairo_t> cr = adoptRef(cairo_create(m_data.m_surface.get())); m_data.m_platformContext.setCr(cr.get()); m_context = adoptPtr(new GraphicsContext(&m_data.m_platformContext)); success = true; @@ -74,7 +116,6 @@ ImageBuffer::ImageBuffer(const IntSize& size, float /* resolutionScale */, Color ImageBuffer::~ImageBuffer() { - cairo_surface_destroy(m_data.m_surface); } GraphicsContext* ImageBuffer::context() const @@ -85,23 +126,28 @@ GraphicsContext* ImageBuffer::context() const PassRefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, ScaleBehavior) const { if (copyBehavior == CopyBackingStore) - return BitmapImage::create(copyCairoImageSurface(m_data.m_surface).leakRef()); + return BitmapImage::create(copyCairoImageSurface(m_data.m_surface.get())); // BitmapImage will release the passed in surface on destruction - return BitmapImage::create(cairo_surface_reference(m_data.m_surface)); + return BitmapImage::create(m_data.m_surface); +} + +BackingStoreCopy ImageBuffer::fastCopyImageMode() +{ + return DontCopyBackingStore; } void ImageBuffer::clip(GraphicsContext* context, const FloatRect& maskRect) const { - context->platformContext()->pushImageMask(m_data.m_surface, maskRect); + context->platformContext()->pushImageMask(m_data.m_surface.get(), maskRect); } void ImageBuffer::draw(GraphicsContext* destinationContext, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, - CompositeOperator op , bool useLowQualityScale) + CompositeOperator op, BlendMode blendMode, bool useLowQualityScale) { BackingStoreCopy copyMode = destinationContext == context() ? CopyBackingStore : DontCopyBackingStore; RefPtr<Image> image = copyImage(copyMode); - destinationContext->drawImage(image.get(), styleColorSpace, destRect, srcRect, op, DoNotRespectImageOrientation, useLowQualityScale); + destinationContext->drawImage(image.get(), styleColorSpace, destRect, srcRect, op, blendMode, DoNotRespectImageOrientation, useLowQualityScale); } void ImageBuffer::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const AffineTransform& patternTransform, @@ -113,12 +159,14 @@ void ImageBuffer::drawPattern(GraphicsContext* context, const FloatRect& srcRect void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable) { - ASSERT(cairo_surface_get_type(m_data.m_surface) == CAIRO_SURFACE_TYPE_IMAGE); + // FIXME: Enable color space conversions on accelerated canvases. + if (cairo_surface_get_type(m_data.m_surface.get()) != CAIRO_SURFACE_TYPE_IMAGE) + return; - unsigned char* dataSrc = cairo_image_surface_get_data(m_data.m_surface); - int stride = cairo_image_surface_get_stride(m_data.m_surface); + unsigned char* dataSrc = cairo_image_surface_get_data(m_data.m_surface.get()); + int stride = cairo_image_surface_get_stride(m_data.m_surface.get()); for (int y = 0; y < m_size.height(); ++y) { - unsigned* row = reinterpret_cast<unsigned*>(dataSrc + stride * y); + unsigned* row = reinterpret_cast_ptr<unsigned*>(dataSrc + stride * y); for (int x = 0; x < m_size.width(); x++) { unsigned* pixel = row + x; Color pixelColor = colorFromPremultipliedARGB(*pixel); @@ -129,16 +177,49 @@ void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable) *pixel = premultipliedARGBFromColor(pixelColor); } } - cairo_surface_mark_dirty_rectangle (m_data.m_surface, 0, 0, m_size.width(), m_size.height()); + cairo_surface_mark_dirty_rectangle(m_data.m_surface.get(), 0, 0, m_size.width(), m_size.height()); +} + +static cairo_surface_t* mapSurfaceToImage(cairo_surface_t* surface, const IntSize& size) +{ + if (cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_IMAGE) + return surface; + + cairo_surface_t* imageSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, size.width(), size.height()); + RefPtr<cairo_t> cr = adoptRef(cairo_create(imageSurface)); + cairo_set_source_surface(cr.get(), surface, 0, 0); + cairo_paint(cr.get()); + return imageSurface; +} + +static void unmapSurfaceFromImage(cairo_surface_t* surface, cairo_surface_t* imageSurface, const IntRect& dirtyRectangle = IntRect()) +{ + if (surface == imageSurface && dirtyRectangle.isEmpty()) + return; + + if (dirtyRectangle.isEmpty()) { + cairo_surface_destroy(imageSurface); + return; + } + + if (surface == imageSurface) { + cairo_surface_mark_dirty_rectangle(surface, dirtyRectangle.x(), dirtyRectangle.y(), dirtyRectangle.width(), dirtyRectangle.height()); + return; + } + + RefPtr<cairo_t> cr = adoptRef(cairo_create(surface)); + cairo_set_source_surface(cr.get(), imageSurface, 0, 0); + cairo_rectangle(cr.get(), dirtyRectangle.x(), dirtyRectangle.y(), dirtyRectangle.width(), dirtyRectangle.height()); + cairo_fill(cr.get()); + cairo_surface_destroy(imageSurface); } template <Multiply multiplied> PassRefPtr<Uint8ClampedArray> getImageData(const IntRect& rect, const ImageBufferData& data, const IntSize& size) { - ASSERT(cairo_surface_get_type(data.m_surface) == CAIRO_SURFACE_TYPE_IMAGE); - RefPtr<Uint8ClampedArray> result = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4); - unsigned char* dataSrc = cairo_image_surface_get_data(data.m_surface); + cairo_surface_t* imageSurface = mapSurfaceToImage(data.m_surface.get(), size); + unsigned char* dataSrc = cairo_image_surface_get_data(imageSurface); unsigned char* dataDst = result->data(); if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > size.width() || (rect.y() + rect.height()) > size.height()) @@ -166,12 +247,12 @@ PassRefPtr<Uint8ClampedArray> getImageData(const IntRect& rect, const ImageBuffe endy = size.height(); int numRows = endy - originy; - int stride = cairo_image_surface_get_stride(data.m_surface); + int stride = cairo_image_surface_get_stride(imageSurface); unsigned destBytesPerRow = 4 * rect.width(); unsigned char* destRows = dataDst + desty * destBytesPerRow + destx * 4; for (int y = 0; y < numRows; ++y) { - unsigned* row = reinterpret_cast<unsigned*>(dataSrc + stride * (y + originy)); + unsigned* row = reinterpret_cast_ptr<unsigned*>(dataSrc + stride * (y + originy)); for (int x = 0; x < numColumns; x++) { int basex = x * 4; unsigned* pixel = row + x + originx; @@ -199,6 +280,7 @@ PassRefPtr<Uint8ClampedArray> getImageData(const IntRect& rect, const ImageBuffe destRows += destBytesPerRow; } + unmapSurfaceFromImage(data.m_surface.get(), imageSurface); return result.release(); } @@ -214,9 +296,8 @@ PassRefPtr<Uint8ClampedArray> ImageBuffer::getPremultipliedImageData(const IntRe void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, CoordinateSystem) { - ASSERT(cairo_surface_get_type(m_data.m_surface) == CAIRO_SURFACE_TYPE_IMAGE); - - unsigned char* dataDst = cairo_image_surface_get_data(m_data.m_surface); + cairo_surface_t* imageSurface = mapSurfaceToImage(m_data.m_surface.get(), sourceSize); + unsigned char* dataDst = cairo_image_surface_get_data(imageSurface); ASSERT(sourceRect.width() > 0); ASSERT(sourceRect.height() > 0); @@ -245,11 +326,11 @@ void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, c int numRows = endy - desty; unsigned srcBytesPerRow = 4 * sourceSize.width(); - int stride = cairo_image_surface_get_stride(m_data.m_surface); + int stride = cairo_image_surface_get_stride(imageSurface); unsigned char* srcRows = source->data() + originy * srcBytesPerRow + originx * 4; for (int y = 0; y < numRows; ++y) { - unsigned* row = reinterpret_cast<unsigned*>(dataDst + stride * (y + desty)); + unsigned* row = reinterpret_cast_ptr<unsigned*>(dataDst + stride * (y + desty)); for (int x = 0; x < numColumns; x++) { int basex = x * 4; unsigned* pixel = row + x + destx; @@ -273,9 +354,8 @@ void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, c } srcRows += srcBytesPerRow; } - cairo_surface_mark_dirty_rectangle(m_data.m_surface, - destx, desty, - numColumns, numRows); + + unmapSurfaceFromImage(m_data.m_surface.get(), imageSurface, IntRect(destx, desty, numColumns, numRows)); } #if !PLATFORM(GTK) @@ -310,4 +390,34 @@ String ImageBuffer::toDataURL(const String& mimeType, const double*, CoordinateS } #endif +#if ENABLE(ACCELERATED_2D_CANVAS) +void ImageBufferData::paintToTextureMapper(TextureMapper* textureMapper, const FloatRect& targetRect, const TransformationMatrix& matrix, float opacity) +{ + if (textureMapper->accelerationMode() != TextureMapper::OpenGLMode) { + notImplemented(); + return; + } + + ASSERT(m_texture); + + // Cairo may change the active context, so we make sure to change it back after flushing. + GLContext* previousActiveContext = GLContext::getCurrent(); + cairo_surface_flush(m_surface.get()); + previousActiveContext->makeContextCurrent(); + + static_cast<TextureMapperGL*>(textureMapper)->drawTexture(m_texture, TextureMapperGL::ShouldBlend, m_size, targetRect, matrix, opacity); +} +#endif + +#if USE(ACCELERATED_COMPOSITING) +PlatformLayer* ImageBuffer::platformLayer() const +{ +#if ENABLE(ACCELERATED_2D_CANVAS) + if (m_data.m_texture) + return const_cast<ImageBufferData*>(&m_data); +#endif + return 0; +} +#endif + } // namespace WebCore |