aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@digia.com>2014-08-07 15:15:38 +0200
committerLars Knoll <lars.knoll@digia.com>2014-08-08 15:56:55 +0300
commitda54d17352dfc4070eb6fa105ce853e3d35490ef (patch)
tree1076956f67cb0355c3b525e271c17a24f36906a5
parent78a6d2d7340fbfdc5b5dbfd9974435714bb786fb (diff)
First implementation of images and rectangles
Change-Id: Ia905d6dfe3d9922ef820085fedc5195be8ace1da Reviewed-by: Lars Knoll <lars.knoll@digia.com>
-rw-r--r--.gitignore5
-rw-r--r--softwarecontext/context.cpp84
-rw-r--r--softwarecontext/context.h22
-rw-r--r--softwarecontext/imagenode.cpp366
-rw-r--r--softwarecontext/imagenode.h35
-rw-r--r--softwarecontext/pixmaptexture.cpp32
-rw-r--r--softwarecontext/pixmaptexture.h24
-rw-r--r--softwarecontext/pluginmain.cpp5
-rw-r--r--softwarecontext/pluginmain.h2
-rw-r--r--softwarecontext/rectanglenode.cpp50
-rw-r--r--softwarecontext/rectanglenode.h33
-rw-r--r--softwarecontext/renderloop.cpp230
-rw-r--r--softwarecontext/renderloop.h53
-rw-r--r--softwarecontext/softwarecontext.pro12
14 files changed, 944 insertions, 9 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000..3230b75d6b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+*.o
+moc_*
+Makefile
+*.pro.user
+*.so
diff --git a/softwarecontext/context.cpp b/softwarecontext/context.cpp
index e65b854af8..7de0aa5cde 100644
--- a/softwarecontext/context.cpp
+++ b/softwarecontext/context.cpp
@@ -41,6 +41,10 @@
#include "context.h"
+#include "rectanglenode.h"
+#include "imagenode.h"
+#include "pixmaptexture.h"
+
#include <QtCore/QCoreApplication>
#include <QtCore/QElapsedTimer>
@@ -62,8 +66,72 @@ static bool qsg_render_timing = !qgetenv("QSG_RENDER_TIMING").isEmpty();
namespace SoftwareContext
{
+Renderer::Renderer(QSGRenderContext *context)
+ : QSGRenderer(context)
+{
+}
+
+void Renderer::renderScene(GLuint fboId)
+{
+ class B : public QSGBindable
+ {
+ public:
+ void bind() const { }
+ } bindable;
+ QSGRenderer::renderScene(bindable);
+}
+
+void Renderer::render()
+{
+ QWindow *currentWindow = static_cast<RenderContext*>(m_context)->currentWindow;
+ if (!backingStore)
+ backingStore.reset(new QBackingStore(currentWindow));
+
+ if (backingStore->size() != currentWindow->size())
+ backingStore->resize(currentWindow->size());
+
+ const QRect rect(0, 0, currentWindow->width(), currentWindow->height());
+ backingStore->beginPaint(rect);
+
+ QPaintDevice *device = backingStore->paintDevice();
+ QPainter painter(device);
+ painter.setRenderHint(QPainter::Antialiasing);
+
+ painter.fillRect(rect, Qt::white);
+ renderNode(&painter, rootNode());
+
+ backingStore->endPaint();
+ backingStore->flush(rect);
+}
+
+void Renderer::renderNode(QPainter *painter, QSGNode *node)
+{
+ bool restore = false;
+
+ if (node->type() == QSGNode::TransformNodeType) {
+ QSGTransformNode *tn = static_cast<QSGTransformNode*>(node);
+ painter->save();
+ restore = true;
+ painter->setTransform(tn->matrix().toTransform(), /*combine*/true);
+ } else if (node->type() == QSGNode::ClipNodeType) {
+ QSGClipNode *cn = static_cast<QSGClipNode*>(node);
+ painter->save();
+ restore = true;
+ painter->setClipRect(cn->clipRect(), Qt::IntersectClip);
+ }
+
+ node->paint(painter);
+
+ for (QSGNode *child = node->firstChild(); child; child = child->nextSibling())
+ renderNode(painter, child);
+
+ if (restore)
+ painter->restore();
+}
+
RenderContext::RenderContext(QSGContext *ctx)
: QSGRenderContext(ctx)
+ , currentWindow(0)
{
}
Context::Context(QObject *parent)
@@ -71,6 +139,16 @@ Context::Context(QObject *parent)
{
}
+QSGRectangleNode *Context::createRectangleNode()
+{
+ return new RectangleNode();
+}
+
+QSGImageNode *Context::createImageNode()
+{
+ return new ImageNode();
+}
+
void RenderContext::initialize(QOpenGLContext *context)
{
QSGRenderContext::initialize(context);
@@ -83,7 +161,7 @@ void RenderContext::invalidate()
QSGTexture *RenderContext::createTexture(const QImage &image) const
{
- return QSGRenderContext::createTexture(image);
+ return new PixmapTexture(image);
}
QSGTexture *RenderContext::createTextureNoAtlas(const QImage &image) const
@@ -93,7 +171,7 @@ QSGTexture *RenderContext::createTextureNoAtlas(const QImage &image) const
QSGRenderer *RenderContext::createRenderer()
{
- return QSGRenderContext::createRenderer();
+ return new Renderer(this);
}
@@ -103,6 +181,4 @@ void RenderContext::renderNextFrame(QSGRenderer *renderer, GLuint fbo)
}
-
-
} // namespace
diff --git a/softwarecontext/context.h b/softwarecontext/context.h
index 6ab405c890..75cf02d767 100644
--- a/softwarecontext/context.h
+++ b/softwarecontext/context.h
@@ -44,12 +44,29 @@
#define CONTEXT_H
#include <private/qsgcontext_p.h>
+#include <private/qsgrenderer_p.h>
#include <QtCore/QElapsedTimer>
#include <QtGui/QOpenGLShaderProgram>
+#include <QtGui/QBackingStore>
namespace SoftwareContext
{
+class Renderer : public QSGRenderer
+{
+public:
+ Renderer(QSGRenderContext *context);
+
+ virtual void renderScene(GLuint fboId = 0);
+
+ virtual void render();
+
+private:
+ void renderNode(QPainter *painter, QSGNode *node);
+
+ QScopedPointer<QBackingStore> backingStore;
+};
+
class RenderContext : public QSGRenderContext
{
public:
@@ -60,6 +77,8 @@ public:
QSGTexture *createTexture(const QImage &image) const;
QSGTexture *createTextureNoAtlas(const QImage &image) const;
QSGRenderer *createRenderer();
+
+ QWindow *currentWindow;
};
class Context : public QSGContext
@@ -70,6 +89,9 @@ public:
QSGRenderContext *createRenderContext() { return new RenderContext(this); }
+ virtual QSGRectangleNode *createRectangleNode();
+ virtual QSGImageNode *createImageNode();
+
private:
};
diff --git a/softwarecontext/imagenode.cpp b/softwarecontext/imagenode.cpp
new file mode 100644
index 0000000000..1dddbaef10
--- /dev/null
+++ b/softwarecontext/imagenode.cpp
@@ -0,0 +1,366 @@
+#include "imagenode.h"
+
+#include "pixmaptexture.h"
+#include <QPainter>
+#include <qmath.h>
+
+// Helper from widgets/styles/qdrawutil.cpp
+
+typedef QVarLengthArray<QPainter::PixmapFragment, 16> QPixmapFragmentsArray;
+
+struct QTileRules
+{
+ inline QTileRules(Qt::TileRule horizontalRule, Qt::TileRule verticalRule)
+ : horizontal(horizontalRule), vertical(verticalRule) {}
+ inline QTileRules(Qt::TileRule rule = Qt::StretchTile)
+ : horizontal(rule), vertical(rule) {}
+ Qt::TileRule horizontal;
+ Qt::TileRule vertical;
+};
+
+#ifndef Q_QDOC
+// For internal use only.
+namespace QDrawBorderPixmap
+{
+ enum DrawingHint
+ {
+ OpaqueTopLeft = 0x0001,
+ OpaqueTop = 0x0002,
+ OpaqueTopRight = 0x0004,
+ OpaqueLeft = 0x0008,
+ OpaqueCenter = 0x0010,
+ OpaqueRight = 0x0020,
+ OpaqueBottomLeft = 0x0040,
+ OpaqueBottom = 0x0080,
+ OpaqueBottomRight = 0x0100,
+ OpaqueCorners = OpaqueTopLeft | OpaqueTopRight | OpaqueBottomLeft | OpaqueBottomRight,
+ OpaqueEdges = OpaqueTop | OpaqueLeft | OpaqueRight | OpaqueBottom,
+ OpaqueFrame = OpaqueCorners | OpaqueEdges,
+ OpaqueAll = OpaqueCenter | OpaqueFrame
+ };
+
+ Q_DECLARE_FLAGS(DrawingHints, DrawingHint)
+}
+#endif
+
+static void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargins &targetMargins,
+ const QPixmap &pixmap, const QRect &sourceRect,const QMargins &sourceMargins,
+ const QTileRules &rules, QDrawBorderPixmap::DrawingHints hints)
+{
+ QPainter::PixmapFragment d;
+ d.opacity = 1.0;
+ d.rotation = 0.0;
+
+ QPixmapFragmentsArray opaqueData;
+ QPixmapFragmentsArray translucentData;
+
+ // source center
+ const int sourceCenterTop = sourceRect.top() + sourceMargins.top();
+ const int sourceCenterLeft = sourceRect.left() + sourceMargins.left();
+ const int sourceCenterBottom = sourceRect.bottom() - sourceMargins.bottom() + 1;
+ const int sourceCenterRight = sourceRect.right() - sourceMargins.right() + 1;
+ const int sourceCenterWidth = sourceCenterRight - sourceCenterLeft;
+ const int sourceCenterHeight = sourceCenterBottom - sourceCenterTop;
+ // target center
+ const int targetCenterTop = targetRect.top() + targetMargins.top();
+ const int targetCenterLeft = targetRect.left() + targetMargins.left();
+ const int targetCenterBottom = targetRect.bottom() - targetMargins.bottom() + 1;
+ const int targetCenterRight = targetRect.right() - targetMargins.right() + 1;
+ const int targetCenterWidth = targetCenterRight - targetCenterLeft;
+ const int targetCenterHeight = targetCenterBottom - targetCenterTop;
+
+ QVarLengthArray<qreal, 16> xTarget; // x-coordinates of target rectangles
+ QVarLengthArray<qreal, 16> yTarget; // y-coordinates of target rectangles
+
+ int columns = 3;
+ int rows = 3;
+ if (rules.horizontal != Qt::StretchTile && sourceCenterWidth != 0)
+ columns = qMax(3, 2 + qCeil(targetCenterWidth / qreal(sourceCenterWidth)));
+ if (rules.vertical != Qt::StretchTile && sourceCenterHeight != 0)
+ rows = qMax(3, 2 + qCeil(targetCenterHeight / qreal(sourceCenterHeight)));
+
+ xTarget.resize(columns + 1);
+ yTarget.resize(rows + 1);
+
+ bool oldAA = painter->testRenderHint(QPainter::Antialiasing);
+ if (painter->paintEngine()->type() != QPaintEngine::OpenGL
+ && painter->paintEngine()->type() != QPaintEngine::OpenGL2
+ && oldAA && painter->combinedTransform().type() != QTransform::TxNone) {
+ painter->setRenderHint(QPainter::Antialiasing, false);
+ }
+
+ xTarget[0] = targetRect.left();
+ xTarget[1] = targetCenterLeft;
+ xTarget[columns - 1] = targetCenterRight;
+ xTarget[columns] = targetRect.left() + targetRect.width();
+
+ yTarget[0] = targetRect.top();
+ yTarget[1] = targetCenterTop;
+ yTarget[rows - 1] = targetCenterBottom;
+ yTarget[rows] = targetRect.top() + targetRect.height();
+
+ qreal dx = targetCenterWidth;
+ qreal dy = targetCenterHeight;
+
+ switch (rules.horizontal) {
+ case Qt::StretchTile:
+ dx = targetCenterWidth;
+ break;
+ case Qt::RepeatTile:
+ dx = sourceCenterWidth;
+ break;
+ case Qt::RoundTile:
+ dx = targetCenterWidth / qreal(columns - 2);
+ break;
+ }
+
+ for (int i = 2; i < columns - 1; ++i)
+ xTarget[i] = xTarget[i - 1] + dx;
+
+ switch (rules.vertical) {
+ case Qt::StretchTile:
+ dy = targetCenterHeight;
+ break;
+ case Qt::RepeatTile:
+ dy = sourceCenterHeight;
+ break;
+ case Qt::RoundTile:
+ dy = targetCenterHeight / qreal(rows - 2);
+ break;
+ }
+
+ for (int i = 2; i < rows - 1; ++i)
+ yTarget[i] = yTarget[i - 1] + dy;
+
+ // corners
+ if (targetMargins.top() > 0 && targetMargins.left() > 0 && sourceMargins.top() > 0 && sourceMargins.left() > 0) { // top left
+ d.x = (0.5 * (xTarget[1] + xTarget[0]));
+ d.y = (0.5 * (yTarget[1] + yTarget[0]));
+ d.sourceLeft = sourceRect.left();
+ d.sourceTop = sourceRect.top();
+ d.width = sourceMargins.left();
+ d.height = sourceMargins.top();
+ d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width;
+ d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height;
+ if (hints & QDrawBorderPixmap::OpaqueTopLeft)
+ opaqueData.append(d);
+ else
+ translucentData.append(d);
+ }
+ if (targetMargins.top() > 0 && targetMargins.right() > 0 && sourceMargins.top() > 0 && sourceMargins.right() > 0) { // top right
+ d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1]));
+ d.y = (0.5 * (yTarget[1] + yTarget[0]));
+ d.sourceLeft = sourceCenterRight;
+ d.sourceTop = sourceRect.top();
+ d.width = sourceMargins.right();
+ d.height = sourceMargins.top();
+ d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width;
+ d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height;
+ if (hints & QDrawBorderPixmap::OpaqueTopRight)
+ opaqueData.append(d);
+ else
+ translucentData.append(d);
+ }
+ if (targetMargins.bottom() > 0 && targetMargins.left() > 0 && sourceMargins.bottom() > 0 && sourceMargins.left() > 0) { // bottom left
+ d.x = (0.5 * (xTarget[1] + xTarget[0]));
+ d.y =(0.5 * (yTarget[rows] + yTarget[rows - 1]));
+ d.sourceLeft = sourceRect.left();
+ d.sourceTop = sourceCenterBottom;
+ d.width = sourceMargins.left();
+ d.height = sourceMargins.bottom();
+ d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width;
+ d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height;
+ if (hints & QDrawBorderPixmap::OpaqueBottomLeft)
+ opaqueData.append(d);
+ else
+ translucentData.append(d);
+ }
+ if (targetMargins.bottom() > 0 && targetMargins.right() > 0 && sourceMargins.bottom() > 0 && sourceMargins.right() > 0) { // bottom right
+ d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1]));
+ d.y = (0.5 * (yTarget[rows] + yTarget[rows - 1]));
+ d.sourceLeft = sourceCenterRight;
+ d.sourceTop = sourceCenterBottom;
+ d.width = sourceMargins.right();
+ d.height = sourceMargins.bottom();
+ d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width;
+ d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height;
+ if (hints & QDrawBorderPixmap::OpaqueBottomRight)
+ opaqueData.append(d);
+ else
+ translucentData.append(d);
+ }
+
+ // horizontal edges
+ if (targetCenterWidth > 0 && sourceCenterWidth > 0) {
+ if (targetMargins.top() > 0 && sourceMargins.top() > 0) { // top
+ QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueTop ? opaqueData : translucentData;
+ d.sourceLeft = sourceCenterLeft;
+ d.sourceTop = sourceRect.top();
+ d.width = sourceCenterWidth;
+ d.height = sourceMargins.top();
+ d.y = (0.5 * (yTarget[1] + yTarget[0]));
+ d.scaleX = dx / d.width;
+ d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height;
+ for (int i = 1; i < columns - 1; ++i) {
+ d.x = (0.5 * (xTarget[i + 1] + xTarget[i]));
+ data.append(d);
+ }
+ if (rules.horizontal == Qt::RepeatTile)
+ data[data.size() - 1].width = ((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX);
+ }
+ if (targetMargins.bottom() > 0 && sourceMargins.bottom() > 0) { // bottom
+ QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueBottom ? opaqueData : translucentData;
+ d.sourceLeft = sourceCenterLeft;
+ d.sourceTop = sourceCenterBottom;
+ d.width = sourceCenterWidth;
+ d.height = sourceMargins.bottom();
+ d.y = (0.5 * (yTarget[rows] + yTarget[rows - 1]));
+ d.scaleX = dx / d.width;
+ d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height;
+ for (int i = 1; i < columns - 1; ++i) {
+ d.x = (0.5 * (xTarget[i + 1] + xTarget[i]));
+ data.append(d);
+ }
+ if (rules.horizontal == Qt::RepeatTile)
+ data[data.size() - 1].width = ((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX);
+ }
+ }
+
+ // vertical edges
+ if (targetCenterHeight > 0 && sourceCenterHeight > 0) {
+ if (targetMargins.left() > 0 && sourceMargins.left() > 0) { // left
+ QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueLeft ? opaqueData : translucentData;
+ d.sourceLeft = sourceRect.left();
+ d.sourceTop = sourceCenterTop;
+ d.width = sourceMargins.left();
+ d.height = sourceCenterHeight;
+ d.x = (0.5 * (xTarget[1] + xTarget[0]));
+ d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width;
+ d.scaleY = dy / d.height;
+ for (int i = 1; i < rows - 1; ++i) {
+ d.y = (0.5 * (yTarget[i + 1] + yTarget[i]));
+ data.append(d);
+ }
+ if (rules.vertical == Qt::RepeatTile)
+ data[data.size() - 1].height = ((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY);
+ }
+ if (targetMargins.right() > 0 && sourceMargins.right() > 0) { // right
+ QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueRight ? opaqueData : translucentData;
+ d.sourceLeft = sourceCenterRight;
+ d.sourceTop = sourceCenterTop;
+ d.width = sourceMargins.right();
+ d.height = sourceCenterHeight;
+ d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1]));
+ d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width;
+ d.scaleY = dy / d.height;
+ for (int i = 1; i < rows - 1; ++i) {
+ d.y = (0.5 * (yTarget[i + 1] + yTarget[i]));
+ data.append(d);
+ }
+ if (rules.vertical == Qt::RepeatTile)
+ data[data.size() - 1].height = ((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY);
+ }
+ }
+
+ // center
+ if (targetCenterWidth > 0 && targetCenterHeight > 0 && sourceCenterWidth > 0 && sourceCenterHeight > 0) {
+ QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueCenter ? opaqueData : translucentData;
+ d.sourceLeft = sourceCenterLeft;
+ d.sourceTop = sourceCenterTop;
+ d.width = sourceCenterWidth;
+ d.height = sourceCenterHeight;
+ d.scaleX = dx / d.width;
+ d.scaleY = dy / d.height;
+
+ qreal repeatWidth = (xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX;
+ qreal repeatHeight = (yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY;
+
+ for (int j = 1; j < rows - 1; ++j) {
+ d.y = (0.5 * (yTarget[j + 1] + yTarget[j]));
+ for (int i = 1; i < columns - 1; ++i) {
+ d.x = (0.5 * (xTarget[i + 1] + xTarget[i]));
+ data.append(d);
+ }
+ if (rules.horizontal == Qt::RepeatTile)
+ data[data.size() - 1].width = repeatWidth;
+ }
+ if (rules.vertical == Qt::RepeatTile) {
+ for (int i = 1; i < columns - 1; ++i)
+ data[data.size() - i].height = repeatHeight;
+ }
+ }
+
+ if (opaqueData.size())
+ painter->drawPixmapFragments(opaqueData.data(), opaqueData.size(), pixmap, QPainter::OpaqueHint);
+ if (translucentData.size())
+ painter->drawPixmapFragments(translucentData.data(), translucentData.size(), pixmap);
+
+ if (oldAA)
+ painter->setRenderHint(QPainter::Antialiasing, true);
+}
+
+ImageNode::ImageNode()
+{
+ setMaterial((QSGMaterial*)1);
+ setGeometry((QSGGeometry*)1);
+}
+
+
+void ImageNode::setTargetRect(const QRectF &rect)
+{
+ m_targetRect = rect;
+}
+
+void ImageNode::setInnerTargetRect(const QRectF &rect)
+{
+ m_innerTargetRect = rect;
+}
+
+void ImageNode::setInnerSourceRect(const QRectF &rect)
+{
+ m_innerSourceRect = rect;
+}
+
+void ImageNode::setSubSourceRect(const QRectF &rect)
+{
+ m_subSourceRect = rect;
+}
+
+void ImageNode::setTexture(QSGTexture *texture)
+{
+ PixmapTexture *pt = qobject_cast<PixmapTexture*>(texture);
+ if (!pt) {
+ qWarning() << "Image used with invalid texture format.";
+ return;
+ }
+ m_pixmap = pt->pixmap();
+}
+
+void ImageNode::setMirror(bool mirror)
+{
+}
+
+void ImageNode::setMipmapFiltering(QSGTexture::Filtering filtering)
+{
+}
+
+void ImageNode::setFiltering(QSGTexture::Filtering filtering)
+{
+}
+
+void ImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode)
+{
+}
+
+void ImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode)
+{
+}
+
+void ImageNode::update()
+{
+}
+
+void ImageNode::paint(QPainter *painter)
+{
+ painter->drawPixmap(m_targetRect, m_pixmap, m_innerSourceRect);
+}
diff --git a/softwarecontext/imagenode.h b/softwarecontext/imagenode.h
new file mode 100644
index 0000000000..956e4f75d8
--- /dev/null
+++ b/softwarecontext/imagenode.h
@@ -0,0 +1,35 @@
+#ifndef IMAGENODE_H
+#define IMAGENODE_H
+
+#include <private/qsgadaptationlayer_p.h>
+#include <private/qsgtexturematerial_p.h>
+
+class ImageNode : public QSGImageNode
+{
+public:
+ ImageNode();
+
+ virtual void setTargetRect(const QRectF &rect);
+ virtual void setInnerTargetRect(const QRectF &rect);
+ virtual void setInnerSourceRect(const QRectF &rect);
+ virtual void setSubSourceRect(const QRectF &rect);
+ virtual void setTexture(QSGTexture *texture);
+ virtual void setMirror(bool mirror);
+ virtual void setMipmapFiltering(QSGTexture::Filtering filtering);
+ virtual void setFiltering(QSGTexture::Filtering filtering);
+ virtual void setHorizontalWrapMode(QSGTexture::WrapMode wrapMode);
+ virtual void setVerticalWrapMode(QSGTexture::WrapMode wrapMode);
+ virtual void update();
+
+ virtual void paint(QPainter *painter);
+
+private:
+ QRectF m_targetRect;
+ QRectF m_innerTargetRect;
+ QRectF m_innerSourceRect;
+ QRectF m_subSourceRect;
+
+ QPixmap m_pixmap;
+};
+
+#endif // IMAGENODE_H
diff --git a/softwarecontext/pixmaptexture.cpp b/softwarecontext/pixmaptexture.cpp
new file mode 100644
index 0000000000..48befd3e7c
--- /dev/null
+++ b/softwarecontext/pixmaptexture.cpp
@@ -0,0 +1,32 @@
+#include "pixmaptexture.h"
+
+PixmapTexture::PixmapTexture(const QImage &image)
+ : m_pixmap(QPixmap::fromImage(image))
+{
+}
+
+
+int PixmapTexture::textureId() const
+{
+ return 0;
+}
+
+QSize PixmapTexture::textureSize() const
+{
+ return m_pixmap.size();
+}
+
+bool PixmapTexture::hasAlphaChannel() const
+{
+ return m_pixmap.hasAlphaChannel();
+}
+
+bool PixmapTexture::hasMipmaps() const
+{
+ return false;
+}
+
+void PixmapTexture::bind()
+{
+ Q_UNREACHABLE();
+}
diff --git a/softwarecontext/pixmaptexture.h b/softwarecontext/pixmaptexture.h
new file mode 100644
index 0000000000..314005f82a
--- /dev/null
+++ b/softwarecontext/pixmaptexture.h
@@ -0,0 +1,24 @@
+#ifndef PIXMAPTEXTURE_H
+#define PIXMAPTEXTURE_H
+
+#include <private/qsgtexture_p.h>
+
+class PixmapTexture : public QSGTexture
+{
+ Q_OBJECT
+public:
+ PixmapTexture(const QImage &image);
+
+ virtual int textureId() const;
+ virtual QSize textureSize() const;
+ virtual bool hasAlphaChannel() const;
+ virtual bool hasMipmaps() const;
+ virtual void bind();
+
+ QPixmap pixmap() const { return m_pixmap; }
+
+private:
+ QPixmap m_pixmap;
+};
+
+#endif // PIXMAPTEXTURE_H
diff --git a/softwarecontext/pluginmain.cpp b/softwarecontext/pluginmain.cpp
index 192e6225bc..4fcfef0859 100644
--- a/softwarecontext/pluginmain.cpp
+++ b/softwarecontext/pluginmain.cpp
@@ -42,6 +42,7 @@
#include "pluginmain.h"
#include "context.h"
+#include "renderloop.h"
ContextPlugin::ContextPlugin(QObject *parent)
: QSGContextPlugin(parent)
@@ -60,9 +61,9 @@ QSGContext *ContextPlugin::create(const QString &) const
return instance;
}
-QQuickTextureFactory *ContextPlugin::createTextureFactoryFromImage(const QImage &image)
+QSGRenderLoop *ContextPlugin::createWindowManager()
{
- return 0;
+ return new RenderLoop();
}
SoftwareContext::Context *ContextPlugin::instance = 0;
diff --git a/softwarecontext/pluginmain.h b/softwarecontext/pluginmain.h
index f8ebf3cb9b..940f1de595 100644
--- a/softwarecontext/pluginmain.h
+++ b/softwarecontext/pluginmain.h
@@ -60,7 +60,7 @@ public:
QStringList keys() const;
QSGContext *create(const QString &key) const;
- QQuickTextureFactory *createTextureFactoryFromImage(const QImage &image);
+ QSGRenderLoop *createWindowManager();
static SoftwareContext::Context *instance;
};
diff --git a/softwarecontext/rectanglenode.cpp b/softwarecontext/rectanglenode.cpp
new file mode 100644
index 0000000000..6dde7058c2
--- /dev/null
+++ b/softwarecontext/rectanglenode.cpp
@@ -0,0 +1,50 @@
+#include "rectanglenode.h"
+
+RectangleNode::RectangleNode()
+{
+ setMaterial((QSGMaterial*)1);
+ setGeometry((QSGGeometry*)1);
+}
+
+void RectangleNode::setRect(const QRectF &rect)
+{
+ m_rect = rect;
+}
+
+void RectangleNode::setColor(const QColor &color)
+{
+ m_brush = color;
+}
+
+void RectangleNode::setPenColor(const QColor &color)
+{
+ m_pen.setColor(color);
+}
+
+void RectangleNode::setPenWidth(qreal width)
+{
+ m_pen.setWidthF(width);
+}
+
+void RectangleNode::setGradientStops(const QGradientStops &stops)
+{
+}
+
+void RectangleNode::setRadius(qreal radius)
+{
+}
+
+void RectangleNode::setAligned(bool aligned)
+{
+}
+
+void RectangleNode::update()
+{
+}
+
+void RectangleNode::paint(QPainter *painter)
+{
+ painter->setPen(m_pen);
+ painter->setBrush(m_brush);
+ painter->drawRect(m_rect);
+}
diff --git a/softwarecontext/rectanglenode.h b/softwarecontext/rectanglenode.h
new file mode 100644
index 0000000000..1b5f1af0a0
--- /dev/null
+++ b/softwarecontext/rectanglenode.h
@@ -0,0 +1,33 @@
+#ifndef RECTANGLENODE_H
+#define RECTANGLENODE_H
+
+#include <private/qsgadaptationlayer_p.h>
+
+#include <QPen>
+#include <QBrush>
+
+class RectangleNode : public QSGRectangleNode
+{
+public:
+ RectangleNode();
+
+ virtual void setRect(const QRectF &rect);
+ virtual void setColor(const QColor &color);
+ virtual void setPenColor(const QColor &color);
+ virtual void setPenWidth(qreal width);
+ virtual void setGradientStops(const QGradientStops &stops);
+ virtual void setRadius(qreal radius);
+ virtual void setAntialiasing(bool antialiasing) { Q_UNUSED(antialiasing) }
+ virtual void setAligned(bool aligned);
+
+ virtual void update();
+
+ virtual void paint(QPainter *);
+
+private:
+ QPen m_pen;
+ QBrush m_brush;
+ QRectF m_rect;
+};
+
+#endif // RECTANGLENODE_H
diff --git a/softwarecontext/renderloop.cpp b/softwarecontext/renderloop.cpp
new file mode 100644
index 0000000000..fd5137e945
--- /dev/null
+++ b/softwarecontext/renderloop.cpp
@@ -0,0 +1,230 @@
+#include "renderloop.h"
+
+#include "context.h"
+#include <private/qquickwindow_p.h>
+#include <QElapsedTimer>
+#include <private/qquickprofiler_p.h>
+
+// Used for very high-level info about the renderering and gl context
+// Includes GL_VERSION, type of render loop, atlas size, etc.
+Q_LOGGING_CATEGORY(QSG_LOG_INFO, "qt.scenegraph.info")
+
+// Used to debug the renderloop logic. Primarily useful for platform integrators
+// and when investigating the render loop logic.
+Q_LOGGING_CATEGORY(QSG_LOG_RENDERLOOP, "qt.scenegraph.renderloop")
+
+
+// GLSL shader compilation
+Q_LOGGING_CATEGORY(QSG_LOG_TIME_COMPILATION, "qt.scenegraph.time.compilation")
+
+// polish, animations, sync, render and swap in the render loop
+Q_LOGGING_CATEGORY(QSG_LOG_TIME_RENDERLOOP, "qt.scenegraph.time.renderloop")
+
+// Texture uploads and swizzling
+Q_LOGGING_CATEGORY(QSG_LOG_TIME_TEXTURE, "qt.scenegraph.time.texture")
+
+// Glyph preparation (only for distance fields atm)
+Q_LOGGING_CATEGORY(QSG_LOG_TIME_GLYPH, "qt.scenegraph.time.glyph")
+
+// Timing inside the renderer base class
+Q_LOGGING_CATEGORY(QSG_LOG_TIME_RENDERER, "qt.scenegraph.time.renderer")
+
+RenderLoop::RenderLoop()
+ : eventPending(false)
+{
+ sg = QSGContext::createDefaultContext();
+ rc = sg->createRenderContext();
+}
+
+RenderLoop::~RenderLoop()
+{
+ delete rc;
+ delete sg;
+}
+
+void RenderLoop::show(QQuickWindow *window)
+{
+ WindowData data;
+ data.updatePending = false;
+ data.grabOnly = false;
+ m_windows[window] = data;
+
+ maybeUpdate(window);
+}
+
+void RenderLoop::hide(QQuickWindow *window)
+{
+ if (!m_windows.contains(window))
+ return;
+
+ m_windows.remove(window);
+ QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
+ cd->fireAboutToStop();
+ cd->cleanupNodesOnShutdown();
+
+ if (m_windows.size() == 0) {
+ if (!cd->persistentSceneGraph) {
+ rc->invalidate();
+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
+ }
+ }
+}
+
+void RenderLoop::windowDestroyed(QQuickWindow *window)
+{
+ hide(window);
+ if (m_windows.size() == 0) {
+ rc->invalidate();
+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
+ }
+}
+
+void RenderLoop::renderWindow(QQuickWindow *window)
+{
+ QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
+ if (!cd->isRenderable() || !m_windows.contains(window))
+ return;
+
+ WindowData &data = const_cast<WindowData &>(m_windows[window]);
+
+ // ### create QPainter and set up pointer to current window/painter
+ static_cast<SoftwareContext::RenderContext*>(cd->context)->currentWindow = window;
+
+ bool alsoSwap = data.updatePending;
+ data.updatePending = false;
+
+ if (!data.grabOnly) {
+ cd->flushDelayedTouchEvent();
+ // Event delivery/processing triggered the window to be deleted or stop rendering.
+ if (!m_windows.contains(window))
+ return;
+ }
+ QElapsedTimer renderTimer;
+ qint64 renderTime = 0, syncTime = 0, polishTime = 0;
+ bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled() || QQuickProfiler::enabled;
+ if (profileFrames)
+ renderTimer.start();
+
+ cd->polishItems();
+
+ if (profileFrames) {
+ polishTime = renderTimer.nsecsElapsed();
+ Q_QUICK_SG_PROFILE(QQuickProfiler::SceneGraphPolishFrame, (polishTime));
+ }
+
+ emit window->afterAnimating();
+
+ cd->syncSceneGraph();
+
+ if (profileFrames)
+ syncTime = renderTimer.nsecsElapsed();
+
+ cd->renderSceneGraph(window->size());
+
+ if (profileFrames)
+ renderTime = renderTimer.nsecsElapsed();
+
+ if (data.grabOnly) {
+ // #### grabContent = qt_gl_read_framebuffer(window->size() * window->devicePixelRatio(), false, false);
+ data.grabOnly = false;
+ }
+
+ if (alsoSwap && window->isVisible()) {
+// ### gl->swapBuffers(window);
+ cd->fireFrameSwapped();
+ }
+
+ qint64 swapTime = 0;
+ if (profileFrames)
+ swapTime = renderTimer.nsecsElapsed();
+
+ if (QSG_LOG_TIME_RENDERLOOP().isDebugEnabled()) {
+ static QTime lastFrameTime = QTime::currentTime();
+ qCDebug(QSG_LOG_TIME_RENDERLOOP,
+ "Frame rendered with 'basic' renderloop in %dms, polish=%d, sync=%d, render=%d, swap=%d, frameDelta=%d",
+ int(swapTime / 1000000),
+ int(polishTime / 1000000),
+ int((syncTime - polishTime) / 1000000),
+ int((renderTime - syncTime) / 1000000),
+ int((swapTime - renderTime) / 10000000),
+ int(lastFrameTime.msecsTo(QTime::currentTime())));
+ lastFrameTime = QTime::currentTime();
+ }
+
+ Q_QUICK_SG_PROFILE(QQuickProfiler::SceneGraphRenderLoopFrame, (
+ syncTime - polishTime,
+ renderTime - syncTime,
+ swapTime - renderTime));
+
+ // Might have been set during syncSceneGraph()
+ if (data.updatePending)
+ maybeUpdate(window);
+}
+
+void RenderLoop::exposureChanged(QQuickWindow *window)
+{
+ if (window->isExposed()) {
+ m_windows[window].updatePending = true;
+ renderWindow(window);
+ }
+}
+
+QImage RenderLoop::grab(QQuickWindow *window)
+{
+ if (!m_windows.contains(window))
+ return QImage();
+
+ m_windows[window].grabOnly = true;
+
+ renderWindow(window);
+
+ QImage grabbed = grabContent;
+ grabContent = QImage();
+ return grabbed;
+}
+
+
+
+void RenderLoop::maybeUpdate(QQuickWindow *window)
+{
+ if (!m_windows.contains(window))
+ return;
+
+ m_windows[window].updatePending = true;
+
+ if (!eventPending) {
+ const int exhaust_delay = 5;
+ m_update_timer = startTimer(exhaust_delay, Qt::PreciseTimer);
+ eventPending = true;
+ }
+}
+
+QSurface::SurfaceType RenderLoop::windowSurfaceType() const
+{
+ return QSurface::RasterSurface;
+}
+
+
+
+QSGContext *RenderLoop::sceneGraphContext() const
+{
+ return sg;
+}
+
+
+bool RenderLoop::event(QEvent *e)
+{
+ if (e->type() == QEvent::Timer) {
+ eventPending = false;
+ killTimer(m_update_timer);
+ m_update_timer = 0;
+ for (QHash<QQuickWindow *, WindowData>::const_iterator it = m_windows.constBegin();
+ it != m_windows.constEnd(); ++it) {
+ const WindowData &data = it.value();
+ if (data.updatePending)
+ renderWindow(it.key());
+ }
+ return true;
+ }
+ return QObject::event(e);
+}
diff --git a/softwarecontext/renderloop.h b/softwarecontext/renderloop.h
new file mode 100644
index 0000000000..8475a0b65b
--- /dev/null
+++ b/softwarecontext/renderloop.h
@@ -0,0 +1,53 @@
+#ifndef RENDERLOOP_H
+#define RENDERLOOP_H
+
+#include <private/qsgrenderloop_p.h>
+
+class RenderLoop : public QSGRenderLoop
+{
+ Q_OBJECT
+public:
+ RenderLoop();
+ ~RenderLoop();
+
+ void show(QQuickWindow *window);
+ void hide(QQuickWindow *window);
+
+ void windowDestroyed(QQuickWindow *window);
+
+ void renderWindow(QQuickWindow *window);
+ void exposureChanged(QQuickWindow *window);
+ QImage grab(QQuickWindow *window);
+
+ void maybeUpdate(QQuickWindow *window);
+ void update(QQuickWindow *window) { maybeUpdate(window); } // identical for this implementation.
+
+ void releaseResources(QQuickWindow *) { }
+
+ virtual QSurface::SurfaceType windowSurfaceType() const;
+
+ QAnimationDriver *animationDriver() const { return 0; }
+
+ QSGContext *sceneGraphContext() const;
+ QSGRenderContext *createRenderContext(QSGContext *) const { return rc; }
+
+ bool event(QEvent *);
+
+ struct WindowData {
+ bool updatePending : 1;
+ bool grabOnly : 1;
+ };
+
+ QHash<QQuickWindow *, WindowData> m_windows;
+
+ QSGContext *sg;
+ QSGRenderContext *rc;
+
+ QImage grabContent;
+ int m_update_timer;
+
+ bool eventPending;
+
+};
+
+#endif // RENDERLOOP_H
diff --git a/softwarecontext/softwarecontext.pro b/softwarecontext/softwarecontext.pro
index 1d1aea4df1..0c6486fc8b 100644
--- a/softwarecontext/softwarecontext.pro
+++ b/softwarecontext/softwarecontext.pro
@@ -7,11 +7,19 @@ QT += gui-private core-private quick-private qml-private
SOURCES += \
context.cpp \
- pluginmain.cpp
+ pluginmain.cpp \
+ renderloop.cpp \
+ rectanglenode.cpp \
+ imagenode.cpp \
+ pixmaptexture.cpp
HEADERS += \
context.h \
- pluginmain.h
+ pluginmain.h \
+ renderloop.h \
+ rectanglenode.h \
+ imagenode.h \
+ pixmaptexture.h
OTHER_FILES += softwarecontext.json