diff options
Diffstat (limited to 'Source/WebCore/platform/graphics/qt/ImageBufferDataQt.cpp')
-rw-r--r-- | Source/WebCore/platform/graphics/qt/ImageBufferDataQt.cpp | 538 |
1 files changed, 538 insertions, 0 deletions
diff --git a/Source/WebCore/platform/graphics/qt/ImageBufferDataQt.cpp b/Source/WebCore/platform/graphics/qt/ImageBufferDataQt.cpp new file mode 100644 index 000000000..b277fd421 --- /dev/null +++ b/Source/WebCore/platform/graphics/qt/ImageBufferDataQt.cpp @@ -0,0 +1,538 @@ +/* + * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> + * Copyright (C) 2008 Holger Hans Peter Freyther + * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> + * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved. + * Copyright (C) 2014 Digia Plc. and/or its subsidiary(-ies) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ImageBuffer.h" + +#include "GraphicsContext.h" +#include "GraphicsSurface.h" +#include "ImageData.h" +#include "IntRect.h" +#include "StillImageQt.h" + +#include <QImage> +#include <QPaintEngine> +#include <QPainter> +#include <QPixmap> + +#if ENABLE(ACCELERATED_2D_CANVAS) +#include "QFramebufferPaintDevice.h" +#include "TextureMapper.h" +#include "TextureMapperGL.h" +#include "TextureMapperPlatformLayer.h" +#include <QOffscreenSurface> +#include <QOpenGLContext> +#include <QOpenGLFramebufferObject> +#include <QOpenGLPaintDevice> +#include <QThreadStorage> +#include <private/qopenglpaintengine_p.h> +#endif + +namespace WebCore { + +#if ENABLE(ACCELERATED_2D_CANVAS) + +class QOpenGLContextThreadStorage { +public: + QOpenGLContext* context() + { + QOpenGLContext*& context = storage.localData(); + if (!context) { + context = new QOpenGLContext; + context->create(); + } + return context; + } + +private: + QThreadStorage<QOpenGLContext*> storage; +}; + +Q_GLOBAL_STATIC(QOpenGLContextThreadStorage, imagebuffer_opengl_context) + +// The owner of the surface needs to be separate from QFramebufferPaintDevice, since the surface +// must already be current with the QFramebufferObject constructor is called. +class ImageBufferContext { +public: + ImageBufferContext(QOpenGLContext* sharedContext) + : m_ownSurface(0) + { + if (sharedContext) + m_format = sharedContext->format(); + + m_context = sharedContext ? sharedContext : imagebuffer_opengl_context->context(); + + m_surface = m_context->surface(); + } + ~ImageBufferContext() + { + if (QOpenGLContext::currentContext() == m_context && m_context->surface() == m_ownSurface) + m_context->doneCurrent(); + delete m_ownSurface; + } + void createSurfaceIfNeeded() + { + if (m_surface) + return; + + m_ownSurface = new QOffscreenSurface; + m_ownSurface->setFormat(m_format); + m_ownSurface->create(); + + m_surface = m_ownSurface; + } + void makeCurrentIfNeeded() + { + if (QOpenGLContext::currentContext() != m_context) { + createSurfaceIfNeeded(); + + m_context->makeCurrent(m_surface); + } + } + QOpenGLContext* context() { return m_context; } + +private: + QSurface* m_surface; + QOffscreenSurface* m_ownSurface; + QOpenGLContext* m_context; + QSurfaceFormat m_format; +}; + +// ---------------------- ImageBufferDataPrivateAccelerated + +struct ImageBufferDataPrivateAccelerated final : public TextureMapperPlatformLayer, public ImageBufferDataPrivate { + ImageBufferDataPrivateAccelerated(const IntSize&, QOpenGLContext* sharedContext); + virtual ~ImageBufferDataPrivateAccelerated(); + + QPaintDevice* paintDevice() final { return m_paintDevice; } + QImage toQImage() const final; + RefPtr<Image> image() const final; + RefPtr<Image> copyImage() const final; + RefPtr<Image> takeImage() final; + bool isAccelerated() const final { return true; } + PlatformLayer* platformLayer() final { return this; } + + void invalidateState() const; + void draw(GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, + CompositeOperator, BlendMode, bool ownContext) final; + void drawPattern(GraphicsContext& destContext, const FloatRect& srcRect, const AffineTransform& patternTransform, + const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, + const FloatRect& destRect, BlendMode, bool ownContext) final; + void clip(GraphicsContext&, const IntRect& floatRect) const final; + void platformTransformColorSpace(const Vector<int>& lookUpTable) final; + + // TextureMapperPlatformLayer: + void paintToTextureMapper(TextureMapper&, const FloatRect&, const TransformationMatrix& modelViewMatrix = TransformationMatrix(), float opacity = 1.0) final; +#if USE(GRAPHICS_SURFACE) + IntSize platformLayerSize() const final; + uint32_t copyToGraphicsSurface() final; + GraphicsSurfaceToken graphicsSurfaceToken() const final; + RefPtr<GraphicsSurface> m_graphicsSurface; +#endif +private: + QFramebufferPaintDevice* m_paintDevice; + ImageBufferContext* m_context; +}; + +ImageBufferDataPrivateAccelerated::ImageBufferDataPrivateAccelerated(const IntSize& size, QOpenGLContext* sharedContext) +{ + m_context = new ImageBufferContext(sharedContext); + m_context->makeCurrentIfNeeded(); + + m_paintDevice = new QFramebufferPaintDevice(size); +} + +ImageBufferDataPrivateAccelerated::~ImageBufferDataPrivateAccelerated() +{ + if (client()) + client()->platformLayerWillBeDestroyed(); + delete m_paintDevice; + delete m_context; +} + +QImage ImageBufferDataPrivateAccelerated::toQImage() const +{ + invalidateState(); + return m_paintDevice->toImage(); +} + +RefPtr<Image> ImageBufferDataPrivateAccelerated::image() const +{ + return copyImage(); +} + +RefPtr<Image> ImageBufferDataPrivateAccelerated::copyImage() const +{ + return StillImage::create(QPixmap::fromImage(toQImage())); +} + +RefPtr<Image> ImageBufferDataPrivateAccelerated::takeImage() +{ + return StillImage::create(QPixmap::fromImage(toQImage())); +} + +void ImageBufferDataPrivateAccelerated::invalidateState() const +{ + // This will flush pending QPainter operations and force ensureActiveTarget() to be called on the next paint. + QOpenGL2PaintEngineEx* acceleratedPaintEngine = static_cast<QOpenGL2PaintEngineEx*>(m_paintDevice->paintEngine()); + acceleratedPaintEngine->invalidateState(); +} + +void ImageBufferDataPrivateAccelerated::draw(GraphicsContext& destContext, const FloatRect& destRect, + const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode, bool /*ownContext*/) +{ + if (destContext.isAcceleratedContext()) { + invalidateState(); + + // If accelerated compositing is disabled, this may be the painter of the QGLWidget, which is a QGL2PaintEngineEx. + QOpenGL2PaintEngineEx* acceleratedPaintEngine = dynamic_cast<QOpenGL2PaintEngineEx*>(destContext.platformContext()->paintEngine()); // toQOpenGL2PaintEngineEx(destContext.platformContext()->paintEngine()); + if (acceleratedPaintEngine) { + QPaintDevice* targetPaintDevice = acceleratedPaintEngine->paintDevice(); + + QRect rect(QPoint(), m_paintDevice->size()); + + // drawTexture's rendering is flipped relative to QtWebKit's convention, so we need to compensate + FloatRect srcRectFlipped = m_paintDevice->paintFlipped() + ? FloatRect(srcRect.x(), srcRect.maxY(), srcRect.width(), -srcRect.height()) + : FloatRect(srcRect.x(), rect.height() - srcRect.maxY(), srcRect.width(), srcRect.height()); + + // Using the same texture as source and target of a rendering operation is undefined in OpenGL, + // so if that's the case we need to use a temporary intermediate buffer. + if (m_paintDevice == targetPaintDevice) { + m_context->makeCurrentIfNeeded(); + + QFramebufferPaintDevice device(rect.size(), QOpenGLFramebufferObject::NoAttachment, false); + + // We disable flipping in order to do a pure blit into the intermediate buffer + device.setPaintFlipped(false); + + QPainter painter(&device); + QOpenGL2PaintEngineEx* pe = static_cast<QOpenGL2PaintEngineEx*>(painter.paintEngine()); + pe->drawTexture(rect, m_paintDevice->texture(), rect.size(), rect); + painter.end(); + + acceleratedPaintEngine->drawTexture(destRect, device.texture(), rect.size(), srcRectFlipped); + } else { + acceleratedPaintEngine->drawTexture(destRect, m_paintDevice->texture(), rect.size(), srcRectFlipped); + } + + return; + } + } + RefPtr<Image> image = StillImage::create(QPixmap::fromImage(toQImage())); + destContext.drawImage(*image, destRect, srcRect, ImagePaintingOptions(op, blendMode, DoNotRespectImageOrientation)); +} + + +void ImageBufferDataPrivateAccelerated::drawPattern(GraphicsContext& destContext, const FloatRect& srcRect, const AffineTransform& patternTransform, + const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, const FloatRect& destRect, BlendMode blendMode, bool /*ownContext*/) +{ + RefPtr<Image> image = StillImage::create(QPixmap::fromImage(toQImage())); + image->drawPattern(destContext, srcRect, patternTransform, phase, spacing, op, destRect, blendMode); +} + +void ImageBufferDataPrivateAccelerated::clip(GraphicsContext& context, const IntRect& rect) const +{ + QPixmap alphaMask = QPixmap::fromImage(toQImage()); + context.pushTransparencyLayerInternal(rect, 1.0, alphaMask); +} + +void ImageBufferDataPrivateAccelerated::platformTransformColorSpace(const Vector<int>& lookUpTable) +{ + QPainter* painter = paintDevice()->paintEngine()->painter(); + + QImage image = toQImage().convertToFormat(QImage::Format_ARGB32); + ASSERT(!image.isNull()); + + uchar* bits = image.bits(); + const int bytesPerLine = image.bytesPerLine(); + + for (int y = 0; y < image.height(); ++y) { + quint32* scanLine = reinterpret_cast_ptr<quint32*>(bits + y * bytesPerLine); + for (int x = 0; x < image.width(); ++x) { + QRgb& pixel = scanLine[x]; + pixel = qRgba(lookUpTable[qRed(pixel)], + lookUpTable[qGreen(pixel)], + lookUpTable[qBlue(pixel)], + qAlpha(pixel)); + } + } + + painter->save(); + painter->resetTransform(); + painter->setOpacity(1.0); + painter->setClipping(false); + painter->setCompositionMode(QPainter::CompositionMode_Source); + painter->drawImage(QPoint(0, 0), image); + painter->restore(); +} + +void ImageBufferDataPrivateAccelerated::paintToTextureMapper(TextureMapper& textureMapper, const FloatRect& targetRect, const TransformationMatrix& matrix, float opacity) +{ + bool canRenderDirectly = false; + if (textureMapper.accelerationMode() == TextureMapper::OpenGLMode) { + if (QOpenGLContext::areSharing(m_context->context(), + static_cast<TextureMapperGL&>(textureMapper).graphicsContext3D()->platformGraphicsContext3D())) + { + canRenderDirectly = true; + } + } + + if (!canRenderDirectly) { + QImage image = toQImage(); + TransformationMatrix oldTransform = textureMapper.graphicsContext()->get3DTransform(); + textureMapper.graphicsContext()->concat3DTransform(matrix); + textureMapper.graphicsContext()->platformContext()->drawImage(targetRect, image); + textureMapper.graphicsContext()->set3DTransform(oldTransform); + return; + } + + invalidateState(); +#if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0) + static_cast<TextureMapperGL&>(textureMapper).drawTexture(m_paintDevice->texture(), TextureMapperGL::ShouldBlend, m_paintDevice->size(), targetRect, matrix, opacity); +#else + static_cast<TextureMapperGL&>(textureMapper).drawTexture(m_paintDevice->texture(), TextureMapperGL::ShouldBlend | TextureMapperGL::ShouldFlipTexture, m_paintDevice->size(), targetRect, matrix, opacity); +#endif +} + +#if USE(GRAPHICS_SURFACE) +IntSize ImageBufferDataPrivateAccelerated::platformLayerSize() const +{ + return m_paintDevice->size(); +} + +uint32_t ImageBufferDataPrivateAccelerated::copyToGraphicsSurface() +{ + if (!m_graphicsSurface) { + GraphicsSurface::Flags flags = GraphicsSurface::SupportsAlpha | GraphicsSurface::SupportsTextureTarget | GraphicsSurface::SupportsSharing; + m_graphicsSurface = GraphicsSurface::create(m_paintDevice->size(), flags); + } + + invalidateState(); + + m_graphicsSurface->copyFromTexture(m_paintDevice->texture(), IntRect(IntPoint(), m_paintDevice->size())); + return m_graphicsSurface->swapBuffers(); +} + +GraphicsSurfaceToken ImageBufferDataPrivateAccelerated::graphicsSurfaceToken() const +{ + return m_graphicsSurface->exportToken(); +} +#endif // USE(GRAPHICS_SURFACE) + +#endif // ENABLE(ACCELERATED_2D_CANVAS) + +// ---------------------- ImageBufferDataPrivateUnaccelerated + +struct ImageBufferDataPrivateUnaccelerated final : public ImageBufferDataPrivate { + ImageBufferDataPrivateUnaccelerated(const IntSize&); + QPaintDevice* paintDevice() final { return m_pixmap.isNull() ? 0 : &m_pixmap; } + QImage toQImage() const final; + RefPtr<Image> image() const final; + RefPtr<Image> copyImage() const final; + RefPtr<Image> takeImage() final; + bool isAccelerated() const final { return false; } + PlatformLayer* platformLayer() final { return 0; } + void draw(GraphicsContext& destContext, const FloatRect& destRect, + const FloatRect& srcRect, CompositeOperator, BlendMode, bool ownContext) final; + void drawPattern(GraphicsContext& destContext, const FloatRect& srcRect, const AffineTransform& patternTransform, + const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, + const FloatRect& destRect, BlendMode, bool ownContext) final; + void clip(GraphicsContext&, const IntRect& floatRect) const final; + void platformTransformColorSpace(const Vector<int>& lookUpTable) final; + + QPixmap m_pixmap; + RefPtr<Image> m_image; +}; + +ImageBufferDataPrivateUnaccelerated::ImageBufferDataPrivateUnaccelerated(const IntSize& size) + : m_pixmap(size) + , m_image(StillImage::createForRendering(&m_pixmap)) +{ + m_pixmap.fill(QColor(Qt::transparent)); +} + +QImage ImageBufferDataPrivateUnaccelerated::toQImage() const +{ + QPaintEngine* paintEngine = m_pixmap.paintEngine(); + if (!paintEngine || paintEngine->type() != QPaintEngine::Raster) + return m_pixmap.toImage(); + + // QRasterPixmapData::toImage() will deep-copy the backing QImage if there's an active QPainter on it. + // For performance reasons, we don't want that here, so we temporarily redirect the paint engine. + QPaintDevice* currentPaintDevice = paintEngine->paintDevice(); + paintEngine->setPaintDevice(0); + QImage image = m_pixmap.toImage(); + paintEngine->setPaintDevice(currentPaintDevice); + return image; +} + +RefPtr<Image> ImageBufferDataPrivateUnaccelerated::image() const +{ + return StillImage::createForRendering(&m_pixmap); +} + +RefPtr<Image> ImageBufferDataPrivateUnaccelerated::copyImage() const +{ + return StillImage::create(m_pixmap); +} + +RefPtr<Image> ImageBufferDataPrivateUnaccelerated::takeImage() +{ + return StillImage::create(WTFMove(m_pixmap)); +} + +void ImageBufferDataPrivateUnaccelerated::draw(GraphicsContext& destContext, const FloatRect& destRect, + const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode, bool ownContext) +{ + if (ownContext) { + // We're drawing into our own buffer. In order for this to work, we need to copy the source buffer first. + RefPtr<Image> copy = copyImage(); + destContext.drawImage(*copy, destRect, srcRect, ImagePaintingOptions(op, blendMode, ImageOrientationDescription())); + } else + destContext.drawImage(*m_image, destRect, srcRect, ImagePaintingOptions(op, blendMode, ImageOrientationDescription())); +} + +void ImageBufferDataPrivateUnaccelerated::drawPattern(GraphicsContext& destContext, const FloatRect& srcRect, const AffineTransform& patternTransform, + const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, + const FloatRect& destRect, BlendMode blendMode, bool ownContext) +{ + if (ownContext) { + // We're drawing into our own buffer. In order for this to work, we need to copy the source buffer first. + RefPtr<Image> copy = copyImage(); + copy->drawPattern(destContext, srcRect, patternTransform, phase, spacing, op, destRect, blendMode); + } else + m_image->drawPattern(destContext, srcRect, patternTransform, phase, spacing, op, destRect, blendMode); +} + +void ImageBufferDataPrivateUnaccelerated::clip(GraphicsContext& context, const IntRect& rect) const +{ + QPixmap* nativeImage = m_image->nativeImageForCurrentFrame(); + if (!nativeImage) + return; + + QPixmap alphaMask = *nativeImage; + context.pushTransparencyLayerInternal(rect, 1.0, alphaMask); +} + +void ImageBufferDataPrivateUnaccelerated::platformTransformColorSpace(const Vector<int>& lookUpTable) +{ + QPainter* painter = paintDevice()->paintEngine()->painter(); + + bool isPainting = painter->isActive(); + if (isPainting) + painter->end(); + + QImage image = toQImage().convertToFormat(QImage::Format_ARGB32); + ASSERT(!image.isNull()); + + uchar* bits = image.bits(); + const int bytesPerLine = image.bytesPerLine(); + + for (int y = 0; y < m_pixmap.height(); ++y) { + quint32* scanLine = reinterpret_cast_ptr<quint32*>(bits + y * bytesPerLine); + for (int x = 0; x < m_pixmap.width(); ++x) { + QRgb& pixel = scanLine[x]; + pixel = qRgba(lookUpTable[qRed(pixel)], + lookUpTable[qGreen(pixel)], + lookUpTable[qBlue(pixel)], + qAlpha(pixel)); + } + } + + m_pixmap = QPixmap::fromImage(image); + + if (isPainting) + painter->begin(&m_pixmap); +} + +// ---------------------- ImageBufferData + +ImageBufferData::ImageBufferData(const IntSize& size) +{ + m_painter = new QPainter; + + m_impl = new ImageBufferDataPrivateUnaccelerated(size); + + if (!m_impl->paintDevice()) + return; + if (!m_painter->begin(m_impl->paintDevice())) + return; + + initPainter(); +} + +#if ENABLE(ACCELERATED_2D_CANVAS) +ImageBufferData::ImageBufferData(const IntSize& size, QOpenGLContext* compatibleContext) +{ + m_painter = new QPainter; + + m_impl = new ImageBufferDataPrivateAccelerated(size, compatibleContext); + + if (!m_impl->paintDevice()) + return; + if (!m_painter->begin(m_impl->paintDevice())) + return; + + initPainter(); +} +#endif + +ImageBufferData::~ImageBufferData() +{ +#if ENABLE(ACCELERATED_2D_CANVAS) + if (m_impl->isAccelerated()) + static_cast<QFramebufferPaintDevice*>(m_impl->paintDevice())->ensureActiveTarget(); +#endif + m_painter->end(); + delete m_painter; + delete m_impl; +} + +void ImageBufferData::initPainter() +{ + m_painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + + // Since ImageBuffer is used mainly for Canvas, explicitly initialize + // its painter's pen and brush with the corresponding canvas defaults + // NOTE: keep in sync with CanvasRenderingContext2D::State + QPen pen = m_painter->pen(); + pen.setColor(Qt::black); + pen.setWidth(1); + pen.setCapStyle(Qt::FlatCap); + pen.setJoinStyle(Qt::SvgMiterJoin); + pen.setMiterLimit(10); + m_painter->setPen(pen); + QBrush brush = m_painter->brush(); + brush.setColor(Qt::black); + m_painter->setBrush(brush); + m_painter->setCompositionMode(QPainter::CompositionMode_SourceOver); +} + +} |