diff options
author | Giulio Camuffo <giulio.camuffo@kdab.com> | 2016-03-28 12:12:04 +0300 |
---|---|---|
committer | Giulio Camuffo <giulio.camuffo@kdab.com> | 2016-04-18 10:48:11 +0000 |
commit | 68f40f95972b857433df424cc16809eebfd77b8f (patch) | |
tree | 815ad8e49e25d4c19a3b3d20395b3e89fa30df51 | |
parent | 95d2bff24bc2678da91cfbd210382c3a5af6af8a (diff) |
Add API to grab a surface's content
This patch adds a new QWaylandSurfaceGrabber class. The user can use it
to grab a surface's content, and get a QImage with the data.
Change-Id: I25dd72a8ba39201cd91addbfc976b93ca1e05112
Reviewed-by: Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
8 files changed, 316 insertions, 1 deletions
diff --git a/src/compositor/compositor_api/compositor_api.pri b/src/compositor/compositor_api/compositor_api.pri index d5ea9a9a4..1140efe34 100644 --- a/src/compositor/compositor_api/compositor_api.pri +++ b/src/compositor/compositor_api/compositor_api.pri @@ -23,6 +23,7 @@ HEADERS += \ compositor_api/qwaylandview.h \ compositor_api/qwaylandview_p.h \ compositor_api/qwaylandresource.h \ + compositor_api/qwaylandsurfacegrabber.h \ SOURCES += \ compositor_api/qwaylandcompositor.cpp \ @@ -39,6 +40,7 @@ SOURCES += \ compositor_api/qwaylanddestroylistener.cpp \ compositor_api/qwaylandview.cpp \ compositor_api/qwaylandresource.cpp \ + compositor_api/qwaylandsurfacegrabber.cpp \ QT += core-private diff --git a/src/compositor/compositor_api/qwaylandcompositor.cpp b/src/compositor/compositor_api/qwaylandcompositor.cpp index 57e8397b9..17b089dcf 100644 --- a/src/compositor/compositor_api/qwaylandcompositor.cpp +++ b/src/compositor/compositor_api/qwaylandcompositor.cpp @@ -46,6 +46,7 @@ #include <QtWaylandCompositor/qwaylandkeyboard.h> #include <QtWaylandCompositor/qwaylandpointer.h> #include <QtWaylandCompositor/qwaylandtouch.h> +#include <QtWaylandCompositor/qwaylandsurfacegrabber.h> #include <QtWaylandCompositor/private/qwaylandkeyboard_p.h> #include <QtWaylandCompositor/private/qwaylandsurface_p.h> @@ -75,6 +76,13 @@ #include <QtGui/qpa/qplatformnativeinterface.h> #include <QtGui/private/qguiapplication_p.h> +#ifdef QT_COMPOSITOR_WAYLAND_GL +# include <QtGui/private/qopengltextureblitter_p.h> +# include <QOpenGLContext> +# include <QOpenGLFramebufferObject> +# include <QMatrix4x4> +#endif + QT_BEGIN_NAMESPACE namespace QtWayland { @@ -819,4 +827,50 @@ void QWaylandCompositor::setUseHardwareIntegrationExtension(bool use) useHardwareIntegrationExtensionChanged(); } +/*! + * Grab the surface content from the given \a buffer. + * The default implementation requires a OpenGL context to be bound to the current thread + * to work. If this is not possible reimplement this function in your compositor subclass + * to implement custom logic. + * The default implementation only grabs SHM and OpenGL buffers, reimplement this in your + * compositor subclass to handle more buffer types. + * You should not call this manually, but rather use \a QWaylandSurfaceGrabber. + */ +void QWaylandCompositor::grabSurface(QWaylandSurfaceGrabber *grabber, const QWaylandBufferRef &buffer) +{ + if (buffer.isShm()) { + emit grabber->success(buffer.image()); + } else { +#ifdef QT_COMPOSITOR_WAYLAND_GL + if (QOpenGLContext::currentContext()) { + QOpenGLFramebufferObject fbo(buffer.size()); + fbo.bind(); + QOpenGLTextureBlitter blitter; + blitter.create(); + blitter.bind(); + + glViewport(0, 0, buffer.size().width(), buffer.size().height()); + + QOpenGLTextureBlitter::Origin surfaceOrigin = + buffer.origin() == QWaylandSurface::OriginTopLeft + ? QOpenGLTextureBlitter::OriginTopLeft + : QOpenGLTextureBlitter::OriginBottomLeft; + + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + buffer.bindToTexture(); + blitter.blit(texture, QMatrix4x4(), surfaceOrigin); + + blitter.release(); + glDeleteTextures(1, &texture); + + emit grabber->success(fbo.toImage()); + } else +#endif + emit grabber->failed(QWaylandSurfaceGrabber::UnknownBufferType); + } +} + QT_END_NAMESPACE diff --git a/src/compositor/compositor_api/qwaylandcompositor.h b/src/compositor/compositor_api/qwaylandcompositor.h index ab90e12f9..a30786eb2 100644 --- a/src/compositor/compositor_api/qwaylandcompositor.h +++ b/src/compositor/compositor_api/qwaylandcompositor.h @@ -63,6 +63,8 @@ class QWaylandView; class QWaylandPointer; class QWaylandKeyboard; class QWaylandTouch; +class QWaylandSurfaceGrabber; +class QWaylandBufferRef; class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandCompositor : public QWaylandObject { @@ -115,6 +117,8 @@ public: bool useHardwareIntegrationExtension() const; void setUseHardwareIntegrationExtension(bool use); + virtual void grabSurface(QWaylandSurfaceGrabber *grabber, const QWaylandBufferRef &buffer); + public Q_SLOTS: void processWaylandEvents(); diff --git a/src/compositor/compositor_api/qwaylandquickcompositor.cpp b/src/compositor/compositor_api/qwaylandquickcompositor.cpp index a30ea05b3..dc13368f2 100644 --- a/src/compositor/compositor_api/qwaylandquickcompositor.cpp +++ b/src/compositor/compositor_api/qwaylandquickcompositor.cpp @@ -36,6 +36,11 @@ ****************************************************************************/ #include <QtQml/QQmlEngine> +#include <QQuickWindow> +#include <QtGui/private/qopengltextureblitter_p.h> +#include <QOpenGLFramebufferObject> +#include <QMatrix4x4> +#include <QRunnable> #include "qwaylandclient.h" #include "qwaylandquickcompositor.h" @@ -44,6 +49,7 @@ #include "qwaylandquickitem.h" #include "qwaylandoutput.h" #include <QtWaylandCompositor/private/qwaylandcompositor_p.h> +#include "qwaylandsurfacegrabber.h" QT_BEGIN_NAMESPACE @@ -102,4 +108,64 @@ void QWaylandQuickCompositor::componentComplete() create(); } +/*! + * Grab the surface content from the given \a buffer. + * Reimplemented from QWaylandCompositor::grabSurface. + */ +void QWaylandQuickCompositor::grabSurface(QWaylandSurfaceGrabber *grabber, const QWaylandBufferRef &buffer) +{ + if (buffer.isShm()) { + QWaylandCompositor::grabSurface(grabber, buffer); + return; + } + + QWaylandQuickOutput *output = static_cast<QWaylandQuickOutput *>(defaultOutput()); + if (!output) { + emit grabber->failed(QWaylandSurfaceGrabber::RendererNotReady); + return; + } + + // We cannot grab the surface now, we need to have a current opengl context, so we + // need to be in the render thread + class GrabState : public QRunnable + { + public: + QWaylandSurfaceGrabber *grabber; + QWaylandBufferRef buffer; + + void run() Q_DECL_OVERRIDE + { + QOpenGLFramebufferObject fbo(buffer.size()); + fbo.bind(); + QOpenGLTextureBlitter blitter; + blitter.create(); + blitter.bind(); + + glViewport(0, 0, buffer.size().width(), buffer.size().height()); + + QOpenGLTextureBlitter::Origin surfaceOrigin = + buffer.origin() == QWaylandSurface::OriginTopLeft + ? QOpenGLTextureBlitter::OriginTopLeft + : QOpenGLTextureBlitter::OriginBottomLeft; + + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + buffer.bindToTexture(); + blitter.blit(texture, QMatrix4x4(), surfaceOrigin); + + blitter.release(); + glDeleteTextures(1, &texture); + + emit grabber->success(fbo.toImage()); + } + }; + + GrabState *state = new GrabState; + state->grabber = grabber; + state->buffer = buffer; + static_cast<QQuickWindow *>(output->window())->scheduleRenderJob(state, QQuickWindow::NoStage); +} + QT_END_NAMESPACE diff --git a/src/compositor/compositor_api/qwaylandquickcompositor.h b/src/compositor/compositor_api/qwaylandquickcompositor.h index bf25adfbb..6e0c5d315 100644 --- a/src/compositor/compositor_api/qwaylandquickcompositor.h +++ b/src/compositor/compositor_api/qwaylandquickcompositor.h @@ -54,6 +54,8 @@ public: QWaylandQuickCompositor(QObject *parent = 0); void create() Q_DECL_OVERRIDE; + void grabSurface(QWaylandSurfaceGrabber *grabber, const QWaylandBufferRef &buffer) Q_DECL_OVERRIDE; + protected: void classBegin() Q_DECL_OVERRIDE; void componentComplete() Q_DECL_OVERRIDE; diff --git a/src/compositor/compositor_api/qwaylandsurface_p.h b/src/compositor/compositor_api/qwaylandsurface_p.h index 73057c54e..635820931 100644 --- a/src/compositor/compositor_api/qwaylandsurface_p.h +++ b/src/compositor/compositor_api/qwaylandsurface_p.h @@ -137,7 +137,7 @@ protected: void setBackBuffer(QtWayland::SurfaceBuffer *buffer, const QRegion &damage); QtWayland::SurfaceBuffer *createSurfaceBuffer(struct ::wl_resource *buffer); -protected: //member variables +public: //member variables QWaylandCompositor *compositor; int refCount; QWaylandClient *client; diff --git a/src/compositor/compositor_api/qwaylandsurfacegrabber.cpp b/src/compositor/compositor_api/qwaylandsurfacegrabber.cpp new file mode 100644 index 000000000..ab3342269 --- /dev/null +++ b/src/compositor/compositor_api/qwaylandsurfacegrabber.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Klarälvdalens Datakonsult AB (KDAB). +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwaylandsurfacegrabber.h" + +#include <QtCore/private/qobject_p.h> +#include <QtWaylandCompositor/qwaylandsurface.h> +#include <QtWaylandCompositor/qwaylandcompositor.h> +#include <QtWaylandCompositor/private/qwaylandsurface_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QWaylandSurfaceGrabber + \inmodule QtWaylandCompositor + \since 5.7 + \brief The QWaylandSurfaceGrabber class allows to read the content of a QWaylandSurface + + Sometimes it is needed to get the contents of a surface, for example to provide a screenshot + to the user. The QWaylandSurfaceGrabber class provides a simple method to do so, without + having to care what type of buffer backs the surface, be it SHM, OpenGL or something else. +*/ + +/*! + \enum QWaylandSurfaceGrabber::Error + + The Error enum describes the reason for a grab failure. + + \value InvalidSurface The surface is null or otherwise not valid. + \value NoBufferAttached The client has not attached a buffer on the surface yet. + \value UnknownBufferType The buffer attached on the surface is of an unknown type. + \value RendererNotReady The compositor renderer is not ready to grab the surface content. + */ + +class QWaylandSurfaceGrabberPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QWaylandSurfaceGrabber) + + QWaylandSurface *surface; +}; + +/*! + * Create a QWaylandSurfaceGrabber object with the given \a surface and \a parent + */ +QWaylandSurfaceGrabber::QWaylandSurfaceGrabber(QWaylandSurface *surface, QObject *parent) + : QObject(*(new QWaylandSurfaceGrabberPrivate), parent) +{ + Q_D(QWaylandSurfaceGrabber); + d->surface = surface; +} + +/*! + * Returns the surface set on this object + */ +QWaylandSurface *QWaylandSurfaceGrabber::surface() const +{ + Q_D(const QWaylandSurfaceGrabber); + return d->surface; +} + +/*! + * Grab the content of the surface set on this object. + * It may not be possible to do that immediately so the \a success and \a failed signals + * should be used to be notified of when the grab is completed. + */ +void QWaylandSurfaceGrabber::grab() +{ + Q_D(QWaylandSurfaceGrabber); + if (!d->surface) { + emit failed(InvalidSurface); + return; + } + + QWaylandSurfacePrivate *surf = QWaylandSurfacePrivate::get(d->surface); + QWaylandBufferRef buf = surf->bufferRef; + if (!buf.hasBuffer()) { + emit failed(NoBufferAttached); + return; + } + + d->surface->compositor()->grabSurface(this, buf); +} diff --git a/src/compositor/compositor_api/qwaylandsurfacegrabber.h b/src/compositor/compositor_api/qwaylandsurfacegrabber.h new file mode 100644 index 000000000..28f984102 --- /dev/null +++ b/src/compositor/compositor_api/qwaylandsurfacegrabber.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Klarälvdalens Datakonsult AB (KDAB). +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDSURFACEGRABBER_H +#define QWAYLANDSURFACEGRABBER_H + +#include <QtWaylandCompositor/qwaylandexport.h> +#include <QtCore/QObject> + +QT_BEGIN_NAMESPACE + +class QWaylandSurface; +class QWaylandSurfaceGrabberPrivate; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandSurfaceGrabber : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QWaylandSurfaceGrabber) +public: + enum Error { + InvalidSurface, + NoBufferAttached, + UnknownBufferType, + RendererNotReady, + }; + Q_ENUM(Error) + explicit QWaylandSurfaceGrabber(QWaylandSurface *surface, QObject *parent = Q_NULLPTR); + + QWaylandSurface *surface() const; + void grab(); + +Q_SIGNALS: + void success(const QImage &image); + void failed(Error error); +}; + +QT_END_NAMESPACE + +#endif // QWAYLANDSURFACEGRABBER_H |