aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@theqtcompany.com>2016-03-15 11:36:52 +0100
committerLaszlo Agocs <laszlo.agocs@theqtcompany.com>2016-03-17 13:37:24 +0000
commit2401162f340bb50b58de701eede0b55c43753185 (patch)
tree5e83bd383db26403231875bf1225ff850c1fbc54 /src
parent843f72bd317f195ee31921acf8132074a7c50d4a (diff)
D3D12: Text enablers
Enablers for native text rendering. Distance field support is postponed for the time being. The main feature here is the revised texture handling where it is now possible to schedule the update of multiple sub-regions from a list of QImages. BGRA is now used where appropriate, meaning for example that RGB32 needs no QImage conversion anymore. The skeletons of the glyph nodes, the basic text material and the glyph cache are added as well. Also fixes cleanup when closing the window by properly reimplementing invalidate() in the rendercontext. The default rectangle, image and glyph nodes are refactored a bit once again: QSGBasicSomethingNode is now the common (albeit optional), abstract class, living in its own file, whereas QSGDefaultSomethingNode is the GL-specific one, like before. This prepares the future renaming for QSGDefault -> QSGOpenGL. Change-Id: I6dbb4ece10be39fd214bb64082d79607389e3e6e Reviewed-by: Andy Nichols <andy.nichols@theqtcompany.com>
Diffstat (limited to 'src')
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/d3d12.pri8
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12builtinmaterials.cpp222
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12builtinmaterials_p.h37
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12context.cpp7
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine.cpp258
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p.h5
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p_p.h11
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12glyphcache.cpp111
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12glyphcache_p.h84
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12glyphnode.cpp90
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12glyphnode_p.h74
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12imagenode_p.h4
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12rectanglenode_p.h4
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext.cpp34
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext_p.h1
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderloop.cpp10
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/qsgd3d12texture.cpp3
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/shaders/shaders.pri13
-rw-r--r--src/quick/scenegraph/adaptations/d3d12/shaders/textmask.hlsl36
-rw-r--r--src/quick/scenegraph/qsgbasicglyphnode.cpp95
-rw-r--r--src/quick/scenegraph/qsgbasicglyphnode_p.h92
-rw-r--r--src/quick/scenegraph/qsgbasicimagenode.cpp519
-rw-r--r--src/quick/scenegraph/qsgbasicimagenode_p.h96
-rw-r--r--src/quick/scenegraph/qsgbasicrectanglenode.cpp687
-rw-r--r--src/quick/scenegraph/qsgbasicrectanglenode_p.h99
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode.cpp59
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode_p.h32
-rw-r--r--src/quick/scenegraph/qsgdefaultimagenode.cpp493
-rw-r--r--src/quick/scenegraph/qsgdefaultimagenode_p.h38
-rw-r--r--src/quick/scenegraph/qsgdefaultrectanglenode.cpp644
-rw-r--r--src/quick/scenegraph/qsgdefaultrectanglenode_p.h42
-rw-r--r--src/quick/scenegraph/scenegraph.pri6
32 files changed, 2555 insertions, 1359 deletions
diff --git a/src/quick/scenegraph/adaptations/d3d12/d3d12.pri b/src/quick/scenegraph/adaptations/d3d12/d3d12.pri
index ce0bced96c..8950734c56 100644
--- a/src/quick/scenegraph/adaptations/d3d12/d3d12.pri
+++ b/src/quick/scenegraph/adaptations/d3d12/d3d12.pri
@@ -8,7 +8,9 @@ SOURCES += \
$$PWD/qsgd3d12material.cpp \
$$PWD/qsgd3d12builtinmaterials.cpp \
$$PWD/qsgd3d12texture.cpp \
- $$PWD/qsgd3d12imagenode.cpp
+ $$PWD/qsgd3d12imagenode.cpp \
+ $$PWD/qsgd3d12glyphnode.cpp \
+ $$PWD/qsgd3d12glyphcache.cpp
NO_PCH_SOURCES += \
$$PWD/qsgd3d12engine.cpp
@@ -25,7 +27,9 @@ HEADERS += \
$$PWD/qsgd3d12material_p.h \
$$PWD/qsgd3d12builtinmaterials_p.h \
$$PWD/qsgd3d12texture_p.h \
- $$PWD/qsgd3d12imagenode_p.h
+ $$PWD/qsgd3d12imagenode_p.h \
+ $$PWD/qsgd3d12glyphnode_p.h \
+ $$PWD/qsgd3d12glyphcache_p.h
LIBS += -ldxgi -ld3d12
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12builtinmaterials.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12builtinmaterials.cpp
index d5f6246c5d..3a42008972 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12builtinmaterials.cpp
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12builtinmaterials.cpp
@@ -38,6 +38,8 @@
****************************************************************************/
#include "qsgd3d12builtinmaterials_p.h"
+#include "qsgd3d12rendercontext_p.h"
+#include <QQuickWindow>
#include "vs_vertexcolor.hlslh"
#include "ps_vertexcolor.hlslh"
@@ -47,6 +49,8 @@
#include "ps_texture.hlslh"
#include "vs_smoothtexture.hlslh"
#include "ps_smoothtexture.hlslh"
+#include "vs_textmask.hlslh"
+#include "ps_textmask24.hlslh"
QT_BEGIN_NAMESPACE
@@ -336,4 +340,222 @@ QSGD3D12Material::UpdateResults QSGD3D12SmoothTextureMaterial::updatePipeline(co
return r;
}
+static inline float qsg_device_pixel_ratio(QSGD3D12Engine *engine)
+{
+ // ### offscreen render target support will need changes
+ float devicePixelRatio = 1;
+ QWindow *w = engine->window();
+ if (QQuickWindow *qw = qobject_cast<QQuickWindow *>(w))
+ devicePixelRatio = qw->effectiveDevicePixelRatio();
+ else
+ devicePixelRatio = w->devicePixelRatio();
+
+ return devicePixelRatio;
+}
+
+static inline int qsg_colorDiff(const QVector4D &a, const QVector4D &b)
+{
+ if (a.x() != b.x())
+ return a.x() > b.x() ? 1 : -1;
+ if (a.y() != b.y())
+ return a.y() > b.y() ? 1 : -1;
+ if (a.z() != b.z())
+ return a.z() > b.z() ? 1 : -1;
+ if (a.w() != b.w())
+ return a.w() > b.w() ? 1 : -1;
+ return 0;
+}
+
+QSGD3D12TextMaterial::QSGD3D12TextMaterial(QSGD3D12RenderContext *rc, const QRawFont &font, QFontEngine::GlyphFormat glyphFormat)
+ : m_font(font)
+{
+ setFlag(Blending, true);
+
+ QRawFontPrivate *fontD = QRawFontPrivate::get(m_font);
+ if (QFontEngine *fontEngine = fontD->fontEngine) {
+ if (glyphFormat == QFontEngine::Format_None)
+ glyphFormat = fontEngine->glyphFormat != QFontEngine::Format_None
+ ? fontEngine->glyphFormat : QFontEngine::Format_A32;
+
+ QSGD3D12Engine *d3dengine = rc->engine();
+ const float devicePixelRatio = qsg_device_pixel_ratio(d3dengine);
+ QTransform glyphCacheTransform = QTransform::fromScale(devicePixelRatio, devicePixelRatio);
+ if (!fontEngine->supportsTransformation(glyphCacheTransform))
+ glyphCacheTransform = QTransform();
+
+ m_glyphCache = fontEngine->glyphCache(d3dengine, glyphFormat, glyphCacheTransform);
+ if (!m_glyphCache || int(m_glyphCache->glyphFormat()) != glyphFormat) {
+ m_glyphCache = new QSGD3D12GlyphCache(d3dengine, glyphFormat, glyphCacheTransform);
+ fontEngine->setGlyphCache(d3dengine, m_glyphCache.data());
+ rc->registerFontengineForCleanup(fontEngine);
+ }
+ }
+}
+
+QSGMaterialType QSGD3D12TextMaterial::mtype;
+
+QSGMaterialType *QSGD3D12TextMaterial::type() const
+{
+ return &QSGD3D12TextMaterial::mtype;
+}
+
+int QSGD3D12TextMaterial::compare(const QSGMaterial *other) const
+{
+ Q_ASSERT(other && type() == other->type());
+ const QSGD3D12TextMaterial *o = static_cast<const QSGD3D12TextMaterial *>(other);
+ if (m_glyphCache != o->m_glyphCache)
+ return m_glyphCache.data() < o->m_glyphCache.data() ? -1 : 1;
+ return qsg_colorDiff(color(), o->color());
+}
+
+static const int TEXT_CB_SIZE_0 = 16 * sizeof(float); // float4x4
+static const int TEXT_CB_SIZE_1 = 2 * sizeof(float); // float2
+static const int TEXT_CB_SIZE_2 = sizeof(float); // float
+static const int TEXT_CB_SIZE_3 = sizeof(float); // float
+static const int TEXT_CB_SIZE = TEXT_CB_SIZE_0 + TEXT_CB_SIZE_1 + TEXT_CB_SIZE_2 + TEXT_CB_SIZE_3;
+
+int QSGD3D12TextMaterial::constantBufferSize() const
+{
+ return QSGD3D12Engine::alignedConstantBufferSize(TEXT_CB_SIZE);
+}
+
+void QSGD3D12TextMaterial::preparePipeline(QSGD3D12ShaderState *shaders)
+{
+ shaders->vs = g_VS_TextMask;
+ shaders->vsSize = sizeof(g_VS_TextMask);
+ shaders->ps = g_PS_TextMask24;
+ shaders->psSize = sizeof(g_PS_TextMask24);
+
+ shaders->rootSig.textureViews.resize(1);
+}
+
+QSGD3D12Material::UpdateResults QSGD3D12TextMaterial::updatePipeline(const RenderState &state,
+ QSGD3D12ShaderState *shaders,
+ quint8 *constantBuffer)
+{
+ QSGD3D12Material::UpdateResults r = 0;
+#if 0
+ quint8 *p = constantBuffer;
+
+ if (state.isMatrixDirty()) {
+ memcpy(p, state.combinedMatrix().constData(), TEXT_CB_SIZE_0);
+ r |= UpdatedConstantBuffer;
+ }
+ p += TEXT_CB_SIZE_0;
+
+ if (state.isOpacityDirty()) {
+ const float opacity = state.opacity();
+ memcpy(p, &opacity, TEXT_CB_SIZE_1);
+ r |= UpdatedConstantBuffer;
+ }
+ p += TEXT_CB_SIZE_1;
+
+ if (state.isOpacityDirty()) {
+ const float opacity = state.opacity();
+ memcpy(p, &opacity, TEXT_CB_SIZE_2);
+ r |= UpdatedConstantBuffer;
+ }
+ p += TEXT_CB_SIZE_2;
+
+ if (state.isOpacityDirty()) {
+ const float opacity = state.opacity();
+ memcpy(p, &opacity, TEXT_CB_SIZE_3);
+ r |= UpdatedConstantBuffer;
+ }
+ p += TEXT_CB_SIZE_3;
+#endif
+
+ QSGD3D12TextureView &tv(shaders->rootSig.textureViews[0]);
+ tv.filter = QSGD3D12TextureView::FilterNearest;
+ tv.addressModeHoriz = QSGD3D12TextureView::AddressClamp;
+ tv.addressModeVert = QSGD3D12TextureView::AddressClamp;
+
+ glyphCache()->activateTexture();
+
+ return r;
+}
+
+void QSGD3D12TextMaterial::populate(const QPointF &p,
+ const QVector<quint32> &glyphIndexes,
+ const QVector<QPointF> &glyphPositions,
+ QSGGeometry *geometry,
+ QRectF *boundingRect,
+ QPointF *baseLine,
+ const QMargins &margins)
+{
+ Q_ASSERT(m_font.isValid());
+ QVector<QFixedPoint> fixedPointPositions;
+ const int glyphPositionsSize = glyphPositions.size();
+ fixedPointPositions.reserve(glyphPositionsSize);
+ for (int i=0; i < glyphPositionsSize; ++i)
+ fixedPointPositions.append(QFixedPoint::fromPointF(glyphPositions.at(i)));
+
+ QSGD3D12GlyphCache *cache = glyphCache();
+ QRawFontPrivate *fontD = QRawFontPrivate::get(m_font);
+ cache->populate(fontD->fontEngine, glyphIndexes.size(), glyphIndexes.constData(),
+ fixedPointPositions.data());
+ cache->fillInPendingGlyphs();
+
+ int margin = fontD->fontEngine->glyphMargin(cache->glyphFormat());
+
+ float glyphCacheScaleX = cache->transform().m11();
+ float glyphCacheScaleY = cache->transform().m22();
+ float glyphCacheInverseScaleX = 1.0 / glyphCacheScaleX;
+ float glyphCacheInverseScaleY = 1.0 / glyphCacheScaleY;
+
+ Q_ASSERT(geometry->indexType() == QSGGeometry::TypeUnsignedShort);
+ geometry->allocate(glyphIndexes.size() * 4, glyphIndexes.size() * 6);
+ QVector4D *vp = reinterpret_cast<QVector4D *>(geometry->vertexDataAsTexturedPoint2D());
+ Q_ASSERT(geometry->sizeOfVertex() == sizeof(QVector4D));
+ ushort *ip = geometry->indexDataAsUShort();
+
+ QPointF position(p.x(), p.y() - m_font.ascent());
+ bool supportsSubPixelPositions = fontD->fontEngine->supportsSubPixelPositions();
+ for (int i = 0; i < glyphIndexes.size(); ++i) {
+ QFixed subPixelPosition;
+ if (supportsSubPixelPositions)
+ subPixelPosition = fontD->fontEngine->subPixelPositionForX(QFixed::fromReal(glyphPositions.at(i).x()));
+
+ QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphIndexes.at(i), subPixelPosition);
+ const QTextureGlyphCache::Coord &c = cache->coords.value(glyph);
+
+ QPointF glyphPosition = glyphPositions.at(i) + position;
+ float x = (qFloor(glyphPosition.x() * glyphCacheScaleX) * glyphCacheInverseScaleX)
+ + (c.baseLineX * glyphCacheInverseScaleX) - margin;
+ float y = (qRound(glyphPosition.y() * glyphCacheScaleY) * glyphCacheInverseScaleY)
+ - (c.baseLineY * glyphCacheInverseScaleY) - margin;
+
+ float w = c.w * glyphCacheInverseScaleX;
+ float h = c.h * glyphCacheInverseScaleY;
+
+ *boundingRect |= QRectF(x + margin, y + margin, w, h);
+
+ float cx1 = x - margins.left();
+ float cx2 = x + w + margins.right();
+ float cy1 = y - margins.top();
+ float cy2 = y + h + margins.bottom();
+
+ float tx1 = c.x - margins.left();
+ float tx2 = c.x + c.w + margins.right();
+ float ty1 = c.y - margins.top();
+ float ty2 = c.y + c.h + margins.bottom();
+
+ if (baseLine->isNull())
+ *baseLine = glyphPosition;
+
+ vp[4 * i + 0] = QVector4D(cx1, cy1, tx1, ty1);
+ vp[4 * i + 1] = QVector4D(cx2, cy1, tx2, ty1);
+ vp[4 * i + 2] = QVector4D(cx1, cy2, tx1, ty2);
+ vp[4 * i + 3] = QVector4D(cx2, cy2, tx2, ty2);
+
+ int o = i * 4;
+ ip[6 * i + 0] = o + 0;
+ ip[6 * i + 1] = o + 2;
+ ip[6 * i + 2] = o + 3;
+ ip[6 * i + 3] = o + 3;
+ ip[6 * i + 4] = o + 1;
+ ip[6 * i + 5] = o + 0;
+ }
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12builtinmaterials_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12builtinmaterials_p.h
index 5ed65ed289..26119cc2e7 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12builtinmaterials_p.h
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12builtinmaterials_p.h
@@ -52,10 +52,14 @@
//
#include "qsgd3d12material_p.h"
+#include "qsgd3d12glyphcache_p.h"
#include <private/qsgtexture_p.h>
+#include <QRawFont>
QT_BEGIN_NAMESPACE
+class QSGD3D12RenderContext;
+
class QSGD3D12VertexColorMaterial : public QSGD3D12Material
{
public:
@@ -165,6 +169,39 @@ private:
QSGTexture::WrapMode m_vertical_wrap = QSGTexture::ClampToEdge;
};
+class QSGD3D12TextMaterial : public QSGD3D12Material
+{
+public:
+ QSGD3D12TextMaterial(QSGD3D12RenderContext *rc, const QRawFont &font,
+ QFontEngine::GlyphFormat glyphFormat = QFontEngine::Format_None);
+
+ QSGMaterialType *type() const override;
+ int compare(const QSGMaterial *other) const override;
+
+ virtual int constantBufferSize() const override;
+ void preparePipeline(QSGD3D12ShaderState *shaders) override;
+ UpdateResults updatePipeline(const RenderState &state,
+ QSGD3D12ShaderState *shaders,
+ quint8 *constantBuffer) override;
+
+ void setColor(const QColor &c) { m_color = QVector4D(c.redF(), c.greenF(), c.blueF(), c.alphaF()); }
+ void setColor(const QVector4D &color) { m_color = color; }
+ const QVector4D &color() const { return m_color; }
+
+ void populate(const QPointF &position,
+ const QVector<quint32> &glyphIndexes, const QVector<QPointF> &glyphPositions,
+ QSGGeometry *geometry, QRectF *boundingRect, QPointF *baseLine,
+ const QMargins &margins = QMargins(0, 0, 0, 0));
+
+ QSGD3D12GlyphCache *glyphCache() const { return static_cast<QSGD3D12GlyphCache *>(m_glyphCache.data()); }
+
+private:
+ static QSGMaterialType mtype;
+ QVector4D m_color;
+ QRawFont m_font;
+ QExplicitlySharedDataPointer<QFontEngineGlyphCache> m_glyphCache;
+};
+
QT_END_NAMESPACE
#endif // QSGD3D12BUILTINMATERIALS_P_H
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12context.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12context.cpp
index 727109cc9c..f54be683f5 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12context.cpp
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12context.cpp
@@ -41,6 +41,7 @@
#include "qsgd3d12rendercontext_p.h"
#include "qsgd3d12rectanglenode_p.h"
#include "qsgd3d12imagenode_p.h"
+#include "qsgd3d12glyphnode_p.h"
QT_BEGIN_NAMESPACE
@@ -68,10 +69,10 @@ QSGPainterNode *QSGD3D12Context::createPainterNode(QQuickPaintedItem *item)
QSGGlyphNode *QSGD3D12Context::createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode)
{
- Q_UNUSED(rc);
Q_UNUSED(preferNativeGlyphNode);
- Q_UNREACHABLE();
- return nullptr;
+ // ### distance field text rendering is not supported atm
+
+ return new QSGD3D12GlyphNode(static_cast<QSGD3D12RenderContext *>(rc));
}
QSGNinePatchNode *QSGD3D12Context::createNinePatchNode()
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine.cpp
index bb1071cc28..bb990b08e0 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine.cpp
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine.cpp
@@ -252,14 +252,28 @@ void QSGD3D12DeviceManager::ensureCreated()
if (warp) {
qDebug("Using WARP");
- ComPtr<IDXGIAdapter> warpAdapter;
- m_factory->EnumWarpAdapter(IID_PPV_ARGS(&warpAdapter));
- HRESULT hr = D3D12CreateDevice(warpAdapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device));
+ m_factory->EnumWarpAdapter(IID_PPV_ARGS(&adapter));
+ HRESULT hr = D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device));
if (FAILED(hr)) {
qWarning("Failed to create WARP device: 0x%x", hr);
return;
}
}
+
+ ComPtr<IDXGIAdapter3> adapter3;
+ if (SUCCEEDED(adapter.As(&adapter3))) {
+ DXGI_QUERY_VIDEO_MEMORY_INFO vidMemInfo;
+ if (SUCCEEDED(adapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &vidMemInfo))) {
+ qDebug("Video memory info: LOCAL: Budget %llu KB CurrentUsage %llu KB AvailableForReservation %llu KB CurrentReservation %llu KB",
+ vidMemInfo.Budget / 1024, vidMemInfo.CurrentUsage / 1024,
+ vidMemInfo.AvailableForReservation / 1024, vidMemInfo.CurrentReservation / 1024);
+ }
+ if (SUCCEEDED(adapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL, &vidMemInfo))) {
+ qDebug("Video memory info: NON-LOCAL: Budget %llu KB CurrentUsage %llu KB AvailableForReservation %llu KB CurrentReservation %llu KB",
+ vidMemInfo.Budget / 1024, vidMemInfo.CurrentUsage / 1024,
+ vidMemInfo.AvailableForReservation / 1024, vidMemInfo.CurrentReservation / 1024);
+ }
+ }
}
void QSGD3D12DeviceManager::registerDeviceLossObserver(DeviceLossObserver *observer)
@@ -302,6 +316,11 @@ void QSGD3D12Engine::resize()
d->resize();
}
+QWindow *QSGD3D12Engine::window() const
+{
+ return d->currentWindow();
+}
+
void QSGD3D12Engine::beginFrame()
{
d->beginFrame();
@@ -399,9 +418,19 @@ uint QSGD3D12Engine::genTexture()
return d->genTexture();
}
-void QSGD3D12Engine::createTextureAsync(uint id, const QImage &image, TextureCreateFlags flags)
+void QSGD3D12Engine::createTexture(uint id, const QSize &size, QImage::Format format, TextureCreateFlags flags)
+{
+ d->createTexture(id, size, format, flags);
+}
+
+void QSGD3D12Engine::queueTextureUpload(uint id, const QImage &image, const QPoint &dstPos)
{
- return d->createTextureAsync(id, image, flags);
+ d->queueTextureUpload(id, QVector<QImage>() << image, QVector<QPoint>() << dstPos);
+}
+
+void QSGD3D12Engine::queueTextureUpload(uint id, const QVector<QImage> &images, const QVector<QPoint> &dstPos)
+{
+ d->queueTextureUpload(id, images, dstPos);
}
void QSGD3D12Engine::releaseTexture(uint id)
@@ -1028,7 +1057,8 @@ void QSGD3D12EnginePrivate::beginFrame()
Texture &t(textures[idx]);
if (t.fenceValue) { // may have been cleared by the previous frame
t.fenceValue = 0;
- t.stagingBuffer = nullptr;
+ t.stagingBuffers.clear();
+ t.stagingHeap = nullptr;
if (Q_UNLIKELY(debug_render()))
qDebug("Cleaned staging data for texture %u", id);
}
@@ -1661,23 +1691,64 @@ uint QSGD3D12EnginePrivate::genTexture()
Texture &t(textures[id - 1]);
t.entryInUse = true;
t.fenceValue = 0;
- t.mipmap = t.waitAdded = false;
return id;
}
-void QSGD3D12EnginePrivate::createTextureAsync(uint id, const QImage &image, QSGD3D12Engine::TextureCreateFlags flags)
+static inline DXGI_FORMAT textureFormat(QImage::Format format, bool wantsAlpha, bool mipmap,
+ QImage::Format *imageFormat, int *bytesPerPixel)
+{
+ DXGI_FORMAT f = DXGI_FORMAT_R8G8B8A8_UNORM;
+ QImage::Format convFormat = format;
+ int bpp = 4;
+
+ if (!mipmap) {
+ switch (format) {
+ case QImage::Format_Indexed8:
+ f = DXGI_FORMAT_A8_UNORM;
+ bpp = 1;
+ break;
+ case QImage::Format_RGB32:
+ f = DXGI_FORMAT_B8G8R8A8_UNORM;
+ break;
+ case QImage::Format_ARGB32:
+ f = DXGI_FORMAT_B8G8R8A8_UNORM;
+ convFormat = wantsAlpha ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32;
+ break;
+ case QImage::Format_ARGB32_Premultiplied:
+ f = DXGI_FORMAT_B8G8R8A8_UNORM;
+ convFormat = wantsAlpha ? format : QImage::Format_RGB32;
+ break;
+ default:
+ convFormat = wantsAlpha ? QImage::Format_RGBA8888_Premultiplied : QImage::Format_RGBX8888;
+ break;
+ }
+ } else {
+ // Mipmap generation needs unordered access and BGRA is not an option for that. Stick to RGBA.
+ convFormat = wantsAlpha ? QImage::Format_RGBA8888_Premultiplied : QImage::Format_RGBX8888;
+ }
+
+ if (imageFormat)
+ *imageFormat = convFormat;
+
+ if (bytesPerPixel)
+ *bytesPerPixel = bpp;
+
+ return f;
+}
+
+void QSGD3D12EnginePrivate::createTexture(uint id, const QSize &size, QImage::Format format,
+ QSGD3D12Engine::TextureCreateFlags flags)
{
Q_ASSERT(id);
const int idx = id - 1;
Q_ASSERT(idx < textures.count() && textures[idx].entryInUse);
Texture &t(textures[idx]);
- const bool alpha = flags & QSGD3D12Engine::CreateWithAlpha;
+ t.alpha = flags & QSGD3D12Engine::CreateWithAlpha;
t.mipmap = flags & QSGD3D12Engine::CreateWithMipMaps;
- t.waitAdded = false;
- const QSize adjustedSize = !t.mipmap ? image.size() : QSGD3D12Engine::mipMapAdjustedSourceSize(image.size());
+ const QSize adjustedSize = !t.mipmap ? size : QSGD3D12Engine::mipMapAdjustedSourceSize(size);
D3D12_HEAP_PROPERTIES defaultHeapProp = {};
defaultHeapProp.Type = D3D12_HEAP_TYPE_DEFAULT;
@@ -1688,7 +1759,7 @@ void QSGD3D12EnginePrivate::createTextureAsync(uint id, const QImage &image, QSG
textureDesc.Height = adjustedSize.height();
textureDesc.DepthOrArraySize = 1;
textureDesc.MipLevels = !t.mipmap ? 1 : QSGD3D12Engine::mipMapLevels(adjustedSize);
- textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ textureDesc.Format = textureFormat(format, t.alpha, t.mipmap, nullptr, nullptr);
textureDesc.SampleDesc.Count = 1;
textureDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
if (t.mipmap)
@@ -1726,63 +1797,140 @@ void QSGD3D12EnginePrivate::createTextureAsync(uint id, const QImage &image, QSG
}
if (Q_UNLIKELY(debug_render()))
- qDebug("created texture %d, size %dx%d, miplevels %d", id, adjustedSize.width(), adjustedSize.height(), textureDesc.MipLevels);
+ qDebug("created texture %u, size %dx%d, miplevels %d", id, adjustedSize.width(), adjustedSize.height(), textureDesc.MipLevels);
+}
+
+void QSGD3D12EnginePrivate::queueTextureUpload(uint id, const QVector<QImage> &images, const QVector<QPoint> &dstPos)
+{
+ Q_ASSERT(id);
+ Q_ASSERT(images.count() == dstPos.count());
+ if (images.isEmpty())
+ return;
+
+ const int idx = id - 1;
+ Q_ASSERT(idx < textures.count() && textures[idx].entryInUse);
+ Texture &t(textures[idx]);
+ Q_ASSERT(t.texture);
+
+ // When mipmapping is not in use, image can be smaller than the size passed
+ // to createTexture() and dstPos can specify a non-zero destination position.
+
+ if (t.mipmap && (images.count() != 1 || dstPos.count() != 1 || !dstPos[0].isNull())) {
+ qWarning("Mipmapped textures (%u) do not support partial uploads", id);
+ return;
+ }
+
+ // This function cannot be called until the previous upload has finished,
+ // but one call can initiate the upload of multiple sub-regions which still
+ // counts as one upload from the viewpoint of the rest of the engine (and
+ // only the completion of the whole set can be waited on).
+
+ if (t.fenceValue) {
+ qWarning("Attempted to queue texture upload (%u) while a previous upload is still in progress", id);
+ return;
+ }
t.fenceValue = nextTextureUploadFenceValue.fetchAndAddAcquire(1) + 1;
+ t.waitAdded = false;
- UINT64 bufferSize;
- D3D12_PLACED_SUBRESOURCE_FOOTPRINT textureLayout;
- device->GetCopyableFootprints(&textureDesc, 0, 1, 0, &textureLayout, nullptr, nullptr, &bufferSize);
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("adding upload for texture %u on the copy queue", id);
- D3D12_RESOURCE_DESC bufDesc = {};
- bufDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
- bufDesc.Width = bufferSize;
- bufDesc.Height = 1;
- bufDesc.DepthOrArraySize = 1;
- bufDesc.MipLevels = 1;
- bufDesc.Format = DXGI_FORMAT_UNKNOWN;
- bufDesc.SampleDesc.Count = 1;
- bufDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+ D3D12_RESOURCE_DESC textureDesc = t.texture->GetDesc();
+ const QSize adjustedTextureSize(textureDesc.Width, textureDesc.Height);
+ int totalSize = 0;
+ for (const QImage &image : images) {
+ int bytesPerPixel;
+ textureFormat(image.format(), t.alpha, t.mipmap, nullptr, &bytesPerPixel);
+ const int w = !t.mipmap ? image.width() : adjustedTextureSize.width();
+ const int h = !t.mipmap ? image.height() : adjustedTextureSize.height();
+ const int stride = alignedSize(w * bytesPerPixel, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
+ totalSize += alignedSize(h * stride, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
+ }
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("%d sub-uploads, heap size %d bytes", images.count(), totalSize);
+
+ // Instead of individual committed resources for each upload buffer,
+ // allocate only once and use placed resources.
D3D12_HEAP_PROPERTIES uploadHeapProp = {};
uploadHeapProp.Type = D3D12_HEAP_TYPE_UPLOAD;
+ D3D12_HEAP_DESC uploadHeapDesc = {};
+ uploadHeapDesc.SizeInBytes = totalSize;
+ uploadHeapDesc.Properties = uploadHeapProp;
+ uploadHeapDesc.Flags = D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS;
- if (FAILED(device->CreateCommittedResource(&uploadHeapProp, D3D12_HEAP_FLAG_NONE, &bufDesc,
- D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&t.stagingBuffer)))) {
- qWarning("Failed to create texture upload buffer");
+ if (FAILED(device->CreateHeap(&uploadHeapDesc, IID_PPV_ARGS(&t.stagingHeap)))) {
+ qWarning("Failed to create texture upload heap of size %d", totalSize);
return;
}
- // ### conversion and scaling are slow. the latter goes away once npot
- // mipmap generation is supported. figure out something for the former.
- QImage convImage = image.convertToFormat(alpha ? QImage::Format_RGBA8888_Premultiplied : QImage::Format_RGBX8888);
- if (t.mipmap && adjustedSize != convImage.size())
- convImage = convImage.scaled(adjustedSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ int placedOffset = 0;
+ for (int i = 0; i < images.count(); ++i) {
+ QImage::Format convFormat;
+ int bytesPerPixel;
+ textureFormat(images[i].format(), t.alpha, t.mipmap, &convFormat, &bytesPerPixel);
- quint8 *p = nullptr;
- const D3D12_RANGE readRange = { 0, 0 };
- if (FAILED(t.stagingBuffer->Map(0, &readRange, reinterpret_cast<void **>(&p)))) {
- qWarning("Map failed (texture upload buffer)");
- return;
- }
- quint8 *lp = p + textureLayout.Offset;
- for (uint y = 0; y < textureDesc.Height; ++y) {
- memcpy(lp, convImage.scanLine(y), convImage.width() * 4);
- lp += textureLayout.Footprint.RowPitch;
- }
- t.stagingBuffer->Unmap(0, nullptr);
+ QImage convImage = images[i].format() == convFormat ? images[i] : images[i].convertToFormat(convFormat);
+
+ if (t.mipmap && adjustedTextureSize != convImage.size())
+ convImage = convImage.scaled(adjustedTextureSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+
+ const int stride = alignedSize(convImage.width() * bytesPerPixel, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
+
+ D3D12_RESOURCE_DESC bufDesc = {};
+ bufDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+ bufDesc.Width = stride * convImage.height();
+ bufDesc.Height = 1;
+ bufDesc.DepthOrArraySize = 1;
+ bufDesc.MipLevels = 1;
+ bufDesc.Format = DXGI_FORMAT_UNKNOWN;
+ bufDesc.SampleDesc.Count = 1;
+ bufDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+
+ Texture::StagingEntry sdata;
+ if (FAILED(device->CreatePlacedResource(t.stagingHeap.Get(), placedOffset,
+ &bufDesc, D3D12_RESOURCE_STATE_GENERIC_READ,
+ nullptr, IID_PPV_ARGS(&sdata.buffer)))) {
+ qWarning("Failed to create texture upload buffer");
+ return;
+ }
+
+ quint8 *p = nullptr;
+ const D3D12_RANGE readRange = { 0, 0 };
+ if (FAILED(sdata.buffer->Map(0, &readRange, reinterpret_cast<void **>(&p)))) {
+ qWarning("Map failed (texture upload buffer)");
+ return;
+ }
+ for (int y = 0, ye = convImage.height(); y < ye; ++y) {
+ memcpy(p, convImage.scanLine(y), convImage.width() * 4);
+ p += stride;
+ }
+ sdata.buffer->Unmap(0, nullptr);
+
+ copyCommandList->Reset(copyCommandAllocator.Get(), nullptr);
+
+ D3D12_TEXTURE_COPY_LOCATION dstLoc;
+ dstLoc.pResource = t.texture.Get();
+ dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
+ dstLoc.SubresourceIndex = 0;
- copyCommandList->Reset(copyCommandAllocator.Get(), nullptr);
+ D3D12_TEXTURE_COPY_LOCATION srcLoc;
+ srcLoc.pResource = sdata.buffer.Get();
+ srcLoc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
+ srcLoc.PlacedFootprint.Offset = 0;
+ srcLoc.PlacedFootprint.Footprint.Format = textureDesc.Format;
+ srcLoc.PlacedFootprint.Footprint.Width = convImage.width();
+ srcLoc.PlacedFootprint.Footprint.Height = convImage.height();
+ srcLoc.PlacedFootprint.Footprint.Depth = 1;
+ srcLoc.PlacedFootprint.Footprint.RowPitch = stride;
- D3D12_TEXTURE_COPY_LOCATION dstLoc;
- dstLoc.pResource = t.texture.Get();
- dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
- dstLoc.SubresourceIndex = 0;
- D3D12_TEXTURE_COPY_LOCATION srcLoc;
- srcLoc.pResource = t.stagingBuffer.Get();
- srcLoc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
- srcLoc.PlacedFootprint = textureLayout;
- copyCommandList->CopyTextureRegion(&dstLoc, 0, 0, 0, &srcLoc, nullptr);
+ copyCommandList->CopyTextureRegion(&dstLoc, dstPos[i].x(), dstPos[i].y(), 0, &srcLoc, nullptr);
+
+ t.stagingBuffers.append(sdata);
+ placedOffset += alignedSize(bufDesc.Width, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
+ }
copyCommandList->Close();
ID3D12CommandList *commandLists[] = { copyCommandList.Get() };
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p.h
index 49df43f3c0..0c6772e9ed 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p.h
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p.h
@@ -260,6 +260,7 @@ public:
bool attachToWindow(QWindow *window);
void releaseResources();
void resize();
+ QWindow *window() const;
void beginFrame();
void endFrame();
@@ -306,7 +307,9 @@ public:
Q_DECLARE_FLAGS(TextureCreateFlags, TextureCreateFlag)
uint genTexture();
- void createTextureAsync(uint id, const QImage &image, TextureCreateFlags flags);
+ void createTexture(uint id, const QSize &size, QImage::Format format, TextureCreateFlags flags);
+ void queueTextureUpload(uint id, const QImage &image, const QPoint &dstPos = QPoint());
+ void queueTextureUpload(uint id, const QVector<QImage> &images, const QVector<QPoint> &dstPos);
void releaseTexture(uint id);
SIZE_T textureSRV(uint id) const;
void activateTexture(uint id);
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p_p.h
index db73e0329d..1788a7b2c2 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p_p.h
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p_p.h
@@ -134,6 +134,7 @@ public:
bool isInitialized() const { return initialized; }
void releaseResources();
void resize();
+ QWindow *currentWindow() const { return window; }
void beginFrame();
void endFrame();
@@ -163,7 +164,8 @@ public:
void waitGPU();
uint genTexture();
- void createTextureAsync(uint id, const QImage &image, QSGD3D12Engine::TextureCreateFlags flags);
+ void createTexture(uint id, const QSize &size, QImage::Format format, QSGD3D12Engine::TextureCreateFlags flags);
+ void queueTextureUpload(uint id, const QVector<QImage> &images, const QVector<QPoint> &dstPos);
void releaseTexture(uint id);
SIZE_T textureSRV(uint id) const;
void activateTexture(uint id);
@@ -282,8 +284,13 @@ private:
D3D12_CPU_DESCRIPTOR_HANDLE srv;
quint64 fenceValue = 0;
bool waitAdded = false;
- ComPtr<ID3D12Resource> stagingBuffer;
+ ComPtr<ID3D12Heap> stagingHeap;
+ struct StagingEntry {
+ ComPtr<ID3D12Resource> buffer;
+ };
+ QVector<StagingEntry> stagingBuffers;
QVector<D3D12_CPU_DESCRIPTOR_HANDLE> mipUAVs;
+ bool alpha = true;
bool mipmap = false;
};
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12glyphcache.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12glyphcache.cpp
new file mode 100644
index 0000000000..b1c7c06054
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12glyphcache.cpp
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgd3d12glyphcache_p.h"
+#include "qsgd3d12engine_p.h"
+
+QT_BEGIN_NAMESPACE
+
+// Keep it simple: allocate a large texture and never resize.
+static const int TEXTURE_WIDTH = 2048;
+static const int TEXTURE_HEIGHT = 2048;
+
+QSGD3D12GlyphCache::QSGD3D12GlyphCache(QSGD3D12Engine *engine, QFontEngine::GlyphFormat format, const QTransform &matrix)
+ : QTextureGlyphCache(format, matrix),
+ m_engine(engine)
+{
+}
+
+QSGD3D12GlyphCache::~QSGD3D12GlyphCache()
+{
+ if (m_id)
+ m_engine->releaseTexture(m_id);
+}
+
+void QSGD3D12GlyphCache::createTextureData(int, int)
+{
+ qDebug("create");
+ m_id = m_engine->genTexture();
+ Q_ASSERT(m_id);
+ m_engine->createTexture(m_id, QSize(TEXTURE_WIDTH, TEXTURE_HEIGHT),
+ QImage::Format_ARGB32_Premultiplied, QSGD3D12Engine::CreateWithAlpha);
+}
+
+void QSGD3D12GlyphCache::resizeTextureData(int, int)
+{
+ // nothing to do here
+}
+
+void QSGD3D12GlyphCache::beginFillTexture()
+{
+ qDebug("begin");
+}
+
+void QSGD3D12GlyphCache::fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition)
+{
+ qDebug("fill %x", glyph);
+}
+
+void QSGD3D12GlyphCache::endFillTexture()
+{
+ qDebug("end");
+}
+
+int QSGD3D12GlyphCache::glyphPadding() const
+{
+ return 1;
+}
+
+int QSGD3D12GlyphCache::maxTextureWidth() const
+{
+ return TEXTURE_WIDTH;
+}
+
+int QSGD3D12GlyphCache::maxTextureHeight() const
+{
+ return TEXTURE_HEIGHT;
+}
+
+void QSGD3D12GlyphCache::activateTexture()
+{
+ if (m_id)
+ m_engine->activateTexture(m_id);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12glyphcache_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12glyphcache_p.h
new file mode 100644
index 0000000000..a92558171b
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12glyphcache_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGD3D12GLYPHCACHE_P_H
+#define QSGD3D12GLYPHCACHE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/private/qtextureglyphcache_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12Engine;
+
+class QSGD3D12GlyphCache : public QTextureGlyphCache
+{
+public:
+ QSGD3D12GlyphCache(QSGD3D12Engine *engine, QFontEngine::GlyphFormat format, const QTransform &matrix);
+ ~QSGD3D12GlyphCache();
+
+ void createTextureData(int width, int height) override;
+ void resizeTextureData(int width, int height) override;
+ void beginFillTexture() override;
+ void fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition) override;
+ void endFillTexture() override;
+ int glyphPadding() const override;
+ int maxTextureWidth() const override;
+ int maxTextureHeight() const override;
+
+ void activateTexture();
+
+private:
+ QSGD3D12Engine *m_engine;
+ uint m_id = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12GLYPHCACHE_P_H
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12glyphnode.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12glyphnode.cpp
new file mode 100644
index 0000000000..c5e53219fa
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12glyphnode.cpp
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgd3d12glyphnode_p.h"
+#include "qsgd3d12builtinmaterials_p.h"
+
+QT_BEGIN_NAMESPACE
+
+void QSGD3D12GlyphNode::setMaterialColor(const QColor &color)
+{
+ static_cast<QSGD3D12TextMaterial *>(m_material)->setColor(color);
+}
+
+void QSGD3D12GlyphNode::update()
+{
+ QRawFont font = m_glyphs.rawFont();
+ QMargins margins(0, 0, 0, 0);
+
+ if (m_style == QQuickText::Normal) {
+ // QSGBasicGlyphNode dtor will delete
+ m_material = new QSGD3D12TextMaterial(m_rc, font);
+ } else if (m_style == QQuickText::Outline) {
+ // ### not yet supported
+// QSGOutlinedTextMaterial *material = new QSGOutlinedTextMaterial(font);
+// material->setStyleColor(m_styleColor);
+// m_material = material;
+ margins = QMargins(1, 1, 1, 1);
+ } else {
+ // ### not yet supported
+// QSGStyledTextMaterial *material = new QSGStyledTextMaterial(font);
+ if (m_style == QQuickText::Sunken) {
+// material->setStyleShift(QVector2D(0, -1));
+ margins.setTop(1);
+ } else if (m_style == QQuickText::Raised) {
+// material->setStyleShift(QVector2D(0, 1));
+ margins.setBottom(1);
+ }
+// material->setStyleColor(m_styleColor);
+// m_material = material;
+ }
+
+ QSGD3D12TextMaterial *textMaterial = static_cast<QSGD3D12TextMaterial *>(m_material);
+ textMaterial->setColor(m_color);
+
+ QRectF boundingRect;
+ textMaterial->populate(m_position, m_glyphs.glyphIndexes(), m_glyphs.positions(), geometry(),
+ &boundingRect, &m_baseLine, margins);
+ setBoundingRect(boundingRect);
+
+ setMaterial(m_material);
+ markDirty(DirtyGeometry);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12glyphnode_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12glyphnode_p.h
new file mode 100644
index 0000000000..d04a8e8777
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12glyphnode_p.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGD3D12GLYPHNODE_P_H
+#define QSGD3D12GLYPHNODE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgbasicglyphnode_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12RenderContext;
+
+class QSGD3D12GlyphNode : public QSGBasicGlyphNode
+{
+public:
+ QSGD3D12GlyphNode(QSGD3D12RenderContext *rc) : m_rc(rc) { }
+
+ void setMaterialColor(const QColor &color) override;
+ void update() override;
+
+private:
+ QSGD3D12RenderContext *m_rc;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12GLYPHNODE_P_H
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12imagenode_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12imagenode_p.h
index 7f47f7daa4..ef4b38884a 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12imagenode_p.h
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12imagenode_p.h
@@ -51,12 +51,12 @@
// We mean it.
//
-#include <private/qsgdefaultimagenode_p.h>
+#include <private/qsgbasicimagenode_p.h>
#include "qsgd3d12builtinmaterials_p.h"
QT_BEGIN_NAMESPACE
-class QSGD3D12ImageNode : public QSGDefaultNoMaterialImageNode
+class QSGD3D12ImageNode : public QSGBasicImageNode
{
public:
QSGD3D12ImageNode();
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rectanglenode_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rectanglenode_p.h
index 95df900489..95f734bc4c 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rectanglenode_p.h
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rectanglenode_p.h
@@ -51,12 +51,12 @@
// We mean it.
//
-#include <private/qsgdefaultrectanglenode_p.h>
+#include <private/qsgbasicrectanglenode_p.h>
#include "qsgd3d12builtinmaterials_p.h"
QT_BEGIN_NAMESPACE
-class QSGD3D12RectangleNode : public QSGDefaultNoMaterialRectangleNode
+class QSGD3D12RectangleNode : public QSGBasicRectangleNode
{
public:
QSGD3D12RectangleNode();
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext.cpp
index 306a54901a..e2b91619cc 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext.cpp
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext.cpp
@@ -43,6 +43,14 @@
QT_BEGIN_NAMESPACE
+// NOTE: Avoid categorized logging. It is slow.
+
+#define DECLARE_DEBUG_VAR(variable) \
+ static bool debug_ ## variable() \
+ { static bool value = qgetenv("QSG_RENDERER_DEBUG").contains(QT_STRINGIFY(variable)); return value; }
+
+DECLARE_DEBUG_VAR(render)
+
QSGD3D12RenderContext::QSGD3D12RenderContext(QSGContext *ctx)
: QSGRenderContext(ctx)
{
@@ -53,6 +61,30 @@ void QSGD3D12RenderContext::initialize(QOpenGLContext *)
Q_UNREACHABLE();
}
+void QSGD3D12RenderContext::invalidate()
+{
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("rendercontext invalidate engine %p, %d/%d/%d", m_engine,
+ m_texturesToDelete.count(), m_textures.count(), m_fontEnginesToClean.count());
+
+ qDeleteAll(m_texturesToDelete);
+ m_texturesToDelete.clear();
+
+ qDeleteAll(m_textures);
+ m_textures.clear();
+
+ for (QSet<QFontEngine *>::const_iterator it = m_fontEnginesToClean.constBegin(),
+ end = m_fontEnginesToClean.constEnd(); it != end; ++it) {
+ (*it)->clearGlyphCache(m_engine);
+ if (!(*it)->ref.deref())
+ delete *it;
+ }
+ m_fontEnginesToClean.clear();
+
+ m_sg->renderContextInvalidated(this);
+ emit invalidated();
+}
+
QSGTexture *QSGD3D12RenderContext::createTexture(const QImage &image, uint flags) const
{
Q_ASSERT(m_engine);
@@ -61,7 +93,7 @@ QSGTexture *QSGD3D12RenderContext::createTexture(const QImage &image, uint flags
return t;
}
-QSGRenderer *QSGD3D12RenderContext::createRenderer()
+ QSGRenderer *QSGD3D12RenderContext::createRenderer()
{
return new QSGD3D12Renderer(this);
}
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext_p.h
index ec3bf81288..2e05632ad9 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext_p.h
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rendercontext_p.h
@@ -63,6 +63,7 @@ public:
QSGD3D12RenderContext(QSGContext *ctx);
void initialize(QOpenGLContext *) override; // not in use
+ void invalidate() override;
void renderNextFrame(QSGRenderer *renderer, GLuint fbo) override;
QSGTexture *createTexture(const QImage &image, uint flags) const override;
QSGRenderer *createRenderer() override;
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderloop.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderloop.cpp
index 504e87b2f5..aace6cba28 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderloop.cpp
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderloop.cpp
@@ -61,6 +61,9 @@ QSGD3D12RenderLoop::QSGD3D12RenderLoop()
qDebug("new d3d12 render loop");
sg = new QSGD3D12Context;
+
+ // One global rendercontext, it will be the renderloop's responsibility to
+ // set the correct window-specific engine via setEngine() later on.
rc = new QSGD3D12RenderContext(sg);
}
@@ -112,7 +115,7 @@ void QSGD3D12RenderLoop::windowDestroyed(QQuickWindow *window)
qDebug() << "window destroyed" << window;
WindowData &data(m_windows[window]);
- delete data.engine;
+ QSGD3D12Engine *engine = data.engine;
m_windows.remove(window);
hide(window);
@@ -121,9 +124,12 @@ void QSGD3D12RenderLoop::windowDestroyed(QQuickWindow *window)
wd->cleanupNodesOnShutdown();
if (m_windows.isEmpty()) {
+ rc->setEngine(engine);
rc->invalidate();
QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
}
+
+ delete engine;
}
void QSGD3D12RenderLoop::exposureChanged(QQuickWindow *window)
@@ -288,8 +294,6 @@ void QSGD3D12RenderLoop::renderWindow(QQuickWindow *window)
lastFrameTime = QTime::currentTime();
}
- rc->setEngine(nullptr);
-
// Might have been set during syncSceneGraph()
if (data.updatePending)
maybeUpdate(window);
diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12texture.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12texture.cpp
index c76e730a6a..c1164b5add 100644
--- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12texture.cpp
+++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12texture.cpp
@@ -125,7 +125,8 @@ void QSGD3D12Texture::bind()
if (m_createdWithMipMaps)
createFlags |= QSGD3D12Engine::CreateWithMipMaps;
- m_engine->createTextureAsync(m_id, m_image, createFlags);
+ m_engine->createTexture(m_id, m_image.size(), m_image.format(), createFlags);
+ m_engine->queueTextureUpload(m_id, m_image);
}
// Here we know that the texture is going to be used in the current frame
diff --git a/src/quick/scenegraph/adaptations/d3d12/shaders/shaders.pri b/src/quick/scenegraph/adaptations/d3d12/shaders/shaders.pri
index 96b0c94565..5999fadda6 100644
--- a/src/quick/scenegraph/adaptations/d3d12/shaders/shaders.pri
+++ b/src/quick/scenegraph/adaptations/d3d12/shaders/shaders.pri
@@ -54,12 +54,23 @@ mipmapgen_cshader.header = cs_mipmapgen.hlslh
mipmapgen_cshader.entry = CS_Generate4MipMaps
mipmapgen_cshader.type = cs_5_0
+textmask_VSPS = $$PWD/textmask.hlsl
+textmask_vshader.input = textmask_VSPS
+textmask_vshader.header = vs_textmask.hlslh
+textmask_vshader.entry = VS_TextMask
+textmask_vshader.type = vs_5_0
+textmask_pshader24.input = textmask_VSPS
+textmask_pshader24.header = ps_textmask24.hlslh
+textmask_pshader24.entry = PS_TextMask24
+textmask_pshader24.type = ps_5_0
+
HLSL_SHADERS = \
vertexcolor_vshader vertexcolor_pshader \
stencilclip_vshader stencilclip_pshader \
smoothcolor_vshader smoothcolor_pshader \
texture_vshader texture_pshader \
smoothtexture_vshader smoothtexture_pshader \
- mipmapgen_cshader
+ mipmapgen_cshader \
+ textmask_vshader textmask_pshader24
load(hlsl_bytecode_header)
diff --git a/src/quick/scenegraph/adaptations/d3d12/shaders/textmask.hlsl b/src/quick/scenegraph/adaptations/d3d12/shaders/textmask.hlsl
new file mode 100644
index 0000000000..b7963d92fe
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/d3d12/shaders/textmask.hlsl
@@ -0,0 +1,36 @@
+struct VSInput
+{
+ float4 position : POSITION;
+ float2 coord : TEXCOORD0;
+};
+
+cbuffer ConstantBuffer : register(b0)
+{
+ float4x4 mvp;
+ float2 textureScale;
+ float dpr;
+ float color;
+};
+
+struct PSInput
+{
+ float4 position : SV_POSITION;
+ float2 coord : TEXCOORD0;
+};
+
+Texture2D tex : register(t0);
+SamplerState samp : register(s0);
+
+PSInput VS_TextMask(VSInput input)
+{
+ PSInput result;
+ result.position = mul(mvp, floor(input.position * dpr + 0.5) / dpr);
+ result.coord = input.coord * textureScale;
+ return result;
+}
+
+float4 PS_TextMask24(PSInput input) : SV_TARGET
+{
+ float4 glyph = tex.Sample(samp, input.coord);
+ return float4(glyph.rgb * color, glyph.a);
+}
diff --git a/src/quick/scenegraph/qsgbasicglyphnode.cpp b/src/quick/scenegraph/qsgbasicglyphnode.cpp
new file mode 100644
index 0000000000..cecfcbfdf9
--- /dev/null
+++ b/src/quick/scenegraph/qsgbasicglyphnode.cpp
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgbasicglyphnode_p.h"
+#include <qsgmaterial.h> // just so that we can safely do delete m_material in the dtor
+
+QT_BEGIN_NAMESPACE
+
+QSGBasicGlyphNode::QSGBasicGlyphNode()
+ : m_style(QQuickText::Normal)
+ , m_material(0)
+ , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0)
+{
+ m_geometry.setDrawingMode(GL_TRIANGLES);
+ setGeometry(&m_geometry);
+}
+
+QSGBasicGlyphNode::~QSGBasicGlyphNode()
+{
+ delete m_material;
+}
+
+void QSGBasicGlyphNode::setColor(const QColor &color)
+{
+ m_color = color;
+ if (m_material != 0) {
+ setMaterialColor(color);
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGBasicGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs)
+{
+ if (m_material != 0)
+ delete m_material;
+
+ m_position = position;
+ m_glyphs = glyphs;
+
+#ifdef QSG_RUNTIME_DESCRIPTION
+ qsgnode_set_description(this, QLatin1String("glyphs"));
+#endif
+}
+
+void QSGBasicGlyphNode::setStyle(QQuickText::TextStyle style)
+{
+ if (m_style == style)
+ return;
+ m_style = style;
+}
+
+void QSGBasicGlyphNode::setStyleColor(const QColor &color)
+{
+ if (m_styleColor == color)
+ return;
+ m_styleColor = color;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgbasicglyphnode_p.h b/src/quick/scenegraph/qsgbasicglyphnode_p.h
new file mode 100644
index 0000000000..6c6e1d7cc6
--- /dev/null
+++ b/src/quick/scenegraph/qsgbasicglyphnode_p.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGBASICGLYPHNODE_P_H
+#define QSGBASICGLYPHNODE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgadaptationlayer_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGMaterial;
+
+class QSGBasicGlyphNode: public QSGGlyphNode
+{
+public:
+ QSGBasicGlyphNode();
+ virtual ~QSGBasicGlyphNode();
+
+ virtual QPointF baseLine() const { return m_baseLine; }
+ virtual void setGlyphs(const QPointF &position, const QGlyphRun &glyphs);
+ virtual void setColor(const QColor &color);
+
+ virtual void setPreferredAntialiasingMode(AntialiasingMode) { }
+ virtual void setStyle(QQuickText::TextStyle);
+ virtual void setStyleColor(const QColor &);
+
+ virtual void setMaterialColor(const QColor &color) = 0;
+ virtual void update() = 0;
+
+protected:
+ QGlyphRun m_glyphs;
+ QPointF m_position;
+ QColor m_color;
+ QQuickText::TextStyle m_style;
+ QColor m_styleColor;
+
+ QPointF m_baseLine;
+ QSGMaterial *m_material;
+
+ QSGGeometry m_geometry;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/scenegraph/qsgbasicimagenode.cpp b/src/quick/scenegraph/qsgbasicimagenode.cpp
new file mode 100644
index 0000000000..a76c788656
--- /dev/null
+++ b/src/quick/scenegraph/qsgbasicimagenode.cpp
@@ -0,0 +1,519 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgbasicimagenode_p.h"
+
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace
+{
+ struct SmoothVertex
+ {
+ float x, y, u, v;
+ float dx, dy, du, dv;
+ };
+
+ const QSGGeometry::AttributeSet &smoothAttributeSet()
+ {
+ static QSGGeometry::Attribute data[] = {
+ QSGGeometry::Attribute::createWithSemantic(0, 2, GL_FLOAT, QSGGeometry::Attribute::POSITION),
+ QSGGeometry::Attribute::createWithSemantic(1, 2, GL_FLOAT, QSGGeometry::Attribute::TEXCOORD),
+ QSGGeometry::Attribute::createWithSemantic(2, 2, GL_FLOAT, QSGGeometry::Attribute::TEXCOORD1),
+ QSGGeometry::Attribute::createWithSemantic(3, 2, GL_FLOAT, QSGGeometry::Attribute::TEXCOORD2)
+ };
+ static QSGGeometry::AttributeSet attrs = { 4, sizeof(SmoothVertex), data };
+ return attrs;
+ }
+}
+
+QSGBasicImageNode::QSGBasicImageNode()
+ : m_innerSourceRect(0, 0, 1, 1)
+ , m_subSourceRect(0, 0, 1, 1)
+ , m_antialiasing(false)
+ , m_mirror(false)
+ , m_dirtyGeometry(false)
+ , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4)
+{
+ setGeometry(&m_geometry);
+
+#ifdef QSG_RUNTIME_DESCRIPTION
+ qsgnode_set_description(this, QLatin1String("image"));
+#endif
+}
+
+void QSGBasicImageNode::setTargetRect(const QRectF &rect)
+{
+ if (rect == m_targetRect)
+ return;
+ m_targetRect = rect;
+ m_dirtyGeometry = true;
+}
+
+void QSGBasicImageNode::setInnerTargetRect(const QRectF &rect)
+{
+ if (rect == m_innerTargetRect)
+ return;
+ m_innerTargetRect = rect;
+ m_dirtyGeometry = true;
+}
+
+void QSGBasicImageNode::setInnerSourceRect(const QRectF &rect)
+{
+ if (rect == m_innerSourceRect)
+ return;
+ m_innerSourceRect = rect;
+ m_dirtyGeometry = true;
+}
+
+void QSGBasicImageNode::setSubSourceRect(const QRectF &rect)
+{
+ if (rect == m_subSourceRect)
+ return;
+ m_subSourceRect = rect;
+ m_dirtyGeometry = true;
+}
+
+void QSGBasicImageNode::setTexture(QSGTexture *texture)
+{
+ Q_ASSERT(texture);
+
+ setMaterialTexture(texture);
+ updateMaterialBlending();
+
+ markDirty(DirtyMaterial);
+
+ // Because the texture can be a different part of the atlas, we need to update it...
+ m_dirtyGeometry = true;
+}
+
+void QSGBasicImageNode::setAntialiasing(bool antialiasing)
+{
+ if (antialiasing == m_antialiasing)
+ return;
+ m_antialiasing = antialiasing;
+ if (m_antialiasing) {
+ setGeometry(new QSGGeometry(smoothAttributeSet(), 0));
+ setFlag(OwnsGeometry, true);
+ } else {
+ setGeometry(&m_geometry);
+ setFlag(OwnsGeometry, false);
+ }
+ updateMaterialAntialiasing();
+ m_dirtyGeometry = true;
+}
+
+void QSGBasicImageNode::setMirror(bool mirror)
+{
+ if (mirror == m_mirror)
+ return;
+ m_mirror = mirror;
+ m_dirtyGeometry = true;
+}
+
+
+void QSGBasicImageNode::update()
+{
+ if (m_dirtyGeometry)
+ updateGeometry();
+}
+
+void QSGBasicImageNode::preprocess()
+{
+ bool doDirty = false;
+ QSGDynamicTexture *t = qobject_cast<QSGDynamicTexture *>(materialTexture());
+ if (t) {
+ doDirty = t->updateTexture();
+ if (doDirty)
+ updateGeometry();
+ }
+
+ if (updateMaterialBlending())
+ doDirty = true;
+
+ if (doDirty)
+ markDirty(DirtyMaterial);
+}
+
+namespace {
+ struct X { float x, tx; };
+ struct Y { float y, ty; };
+}
+
+static inline void appendQuad(quint16 **indices, quint16 topLeft, quint16 topRight,
+ quint16 bottomLeft, quint16 bottomRight)
+{
+ *(*indices)++ = topLeft;
+ *(*indices)++ = bottomLeft;
+ *(*indices)++ = bottomRight;
+ *(*indices)++ = bottomRight;
+ *(*indices)++ = topRight;
+ *(*indices)++ = topLeft;
+}
+
+void QSGBasicImageNode::updateGeometry()
+{
+ Q_ASSERT(!m_targetRect.isEmpty());
+ const QSGTexture *t = materialTexture();
+ if (!t) {
+ QSGGeometry *g = geometry();
+ g->allocate(4);
+ g->setDrawingMode(GL_TRIANGLE_STRIP);
+ memset(g->vertexData(), 0, g->sizeOfVertex() * 4);
+ } else {
+ QRectF sourceRect = t->normalizedTextureSubRect();
+
+ QRectF innerSourceRect(sourceRect.x() + m_innerSourceRect.x() * sourceRect.width(),
+ sourceRect.y() + m_innerSourceRect.y() * sourceRect.height(),
+ m_innerSourceRect.width() * sourceRect.width(),
+ m_innerSourceRect.height() * sourceRect.height());
+
+ bool hasMargins = m_targetRect != m_innerTargetRect;
+
+ int floorLeft = qFloor(m_subSourceRect.left());
+ int ceilRight = qCeil(m_subSourceRect.right());
+ int floorTop = qFloor(m_subSourceRect.top());
+ int ceilBottom = qCeil(m_subSourceRect.bottom());
+ int hTiles = ceilRight - floorLeft;
+ int vTiles = ceilBottom - floorTop;
+
+ bool hasTiles = hTiles != 1 || vTiles != 1;
+ bool fullTexture = innerSourceRect == QRectF(0, 0, 1, 1);
+
+ // An image can be rendered as a single quad if:
+ // - There are no margins, and either:
+ // - the image isn't repeated
+ // - the source rectangle fills the entire texture so that texture wrapping can be used,
+ // and NPOT is supported
+ if (!hasMargins && (!hasTiles || (fullTexture && supportsWrap(t->textureSize())))) {
+ QRectF sr;
+ if (!fullTexture) {
+ sr = QRectF(innerSourceRect.x() + (m_subSourceRect.left() - floorLeft) * innerSourceRect.width(),
+ innerSourceRect.y() + (m_subSourceRect.top() - floorTop) * innerSourceRect.height(),
+ m_subSourceRect.width() * innerSourceRect.width(),
+ m_subSourceRect.height() * innerSourceRect.height());
+ } else {
+ sr = QRectF(m_subSourceRect.left() - floorLeft, m_subSourceRect.top() - floorTop,
+ m_subSourceRect.width(), m_subSourceRect.height());
+ }
+ if (m_mirror) {
+ qreal oldLeft = sr.left();
+ sr.setLeft(sr.right());
+ sr.setRight(oldLeft);
+ }
+
+ if (m_antialiasing) {
+ QSGGeometry *g = geometry();
+ Q_ASSERT(g != &m_geometry);
+ g->allocate(8, 14);
+ g->setDrawingMode(GL_TRIANGLE_STRIP);
+ SmoothVertex *vertices = reinterpret_cast<SmoothVertex *>(g->vertexData());
+ float delta = float(qAbs(m_targetRect.width()) < qAbs(m_targetRect.height())
+ ? m_targetRect.width() : m_targetRect.height()) * 0.5f;
+ float sx = float(sr.width() / m_targetRect.width());
+ float sy = float(sr.height() / m_targetRect.height());
+ for (int d = -1; d <= 1; d += 2) {
+ for (int j = 0; j < 2; ++j) {
+ for (int i = 0; i < 2; ++i, ++vertices) {
+ vertices->x = m_targetRect.x() + i * m_targetRect.width();
+ vertices->y = m_targetRect.y() + j * m_targetRect.height();
+ vertices->u = sr.x() + i * sr.width();
+ vertices->v = sr.y() + j * sr.height();
+ vertices->dx = (i == 0 ? delta : -delta) * d;
+ vertices->dy = (j == 0 ? delta : -delta) * d;
+ vertices->du = (d < 0 ? 0 : vertices->dx * sx);
+ vertices->dv = (d < 0 ? 0 : vertices->dy * sy);
+ }
+ }
+ }
+ Q_ASSERT(vertices - g->vertexCount() == g->vertexData());
+ static const quint16 indices[] = {
+ 0, 4, 1, 5, 3, 7, 2, 6, 0, 4,
+ 4, 6, 5, 7
+ };
+ Q_ASSERT(g->sizeOfIndex() * g->indexCount() == sizeof(indices));
+ memcpy(g->indexDataAsUShort(), indices, sizeof(indices));
+ } else {
+ m_geometry.allocate(4);
+ m_geometry.setDrawingMode(GL_TRIANGLE_STRIP);
+ QSGGeometry::updateTexturedRectGeometry(&m_geometry, m_targetRect, sr);
+ }
+ } else {
+ int hCells = hTiles;
+ int vCells = vTiles;
+ if (m_innerTargetRect.width() == 0)
+ hCells = 0;
+ if (m_innerTargetRect.left() != m_targetRect.left())
+ ++hCells;
+ if (m_innerTargetRect.right() != m_targetRect.right())
+ ++hCells;
+ if (m_innerTargetRect.height() == 0)
+ vCells = 0;
+ if (m_innerTargetRect.top() != m_targetRect.top())
+ ++vCells;
+ if (m_innerTargetRect.bottom() != m_targetRect.bottom())
+ ++vCells;
+ QVarLengthArray<X, 32> xData(2 * hCells);
+ QVarLengthArray<Y, 32> yData(2 * vCells);
+ X *xs = xData.data();
+ Y *ys = yData.data();
+
+ if (m_innerTargetRect.left() != m_targetRect.left()) {
+ xs[0].x = m_targetRect.left();
+ xs[0].tx = sourceRect.left();
+ xs[1].x = m_innerTargetRect.left();
+ xs[1].tx = innerSourceRect.left();
+ xs += 2;
+ }
+ if (m_innerTargetRect.width() != 0) {
+ xs[0].x = m_innerTargetRect.left();
+ xs[0].tx = innerSourceRect.x() + (m_subSourceRect.left() - floorLeft) * innerSourceRect.width();
+ ++xs;
+ float b = m_innerTargetRect.width() / m_subSourceRect.width();
+ float a = m_innerTargetRect.x() - m_subSourceRect.x() * b;
+ for (int i = floorLeft + 1; i <= ceilRight - 1; ++i) {
+ xs[0].x = xs[1].x = a + b * i;
+ xs[0].tx = innerSourceRect.right();
+ xs[1].tx = innerSourceRect.left();
+ xs += 2;
+ }
+ xs[0].x = m_innerTargetRect.right();
+ xs[0].tx = innerSourceRect.x() + (m_subSourceRect.right() - ceilRight + 1) * innerSourceRect.width();
+ ++xs;
+ }
+ if (m_innerTargetRect.right() != m_targetRect.right()) {
+ xs[0].x = m_innerTargetRect.right();
+ xs[0].tx = innerSourceRect.right();
+ xs[1].x = m_targetRect.right();
+ xs[1].tx = sourceRect.right();
+ xs += 2;
+ }
+ Q_ASSERT(xs == xData.data() + xData.size());
+ if (m_mirror) {
+ float leftPlusRight = m_targetRect.left() + m_targetRect.right();
+ int count = xData.size();
+ xs = xData.data();
+ for (int i = 0; i < count >> 1; ++i)
+ qSwap(xs[i], xs[count - 1 - i]);
+ for (int i = 0; i < count; ++i)
+ xs[i].x = leftPlusRight - xs[i].x;
+ }
+
+ if (m_innerTargetRect.top() != m_targetRect.top()) {
+ ys[0].y = m_targetRect.top();
+ ys[0].ty = sourceRect.top();
+ ys[1].y = m_innerTargetRect.top();
+ ys[1].ty = innerSourceRect.top();
+ ys += 2;
+ }
+ if (m_innerTargetRect.height() != 0) {
+ ys[0].y = m_innerTargetRect.top();
+ ys[0].ty = innerSourceRect.y() + (m_subSourceRect.top() - floorTop) * innerSourceRect.height();
+ ++ys;
+ float b = m_innerTargetRect.height() / m_subSourceRect.height();
+ float a = m_innerTargetRect.y() - m_subSourceRect.y() * b;
+ for (int i = floorTop + 1; i <= ceilBottom - 1; ++i) {
+ ys[0].y = ys[1].y = a + b * i;
+ ys[0].ty = innerSourceRect.bottom();
+ ys[1].ty = innerSourceRect.top();
+ ys += 2;
+ }
+ ys[0].y = m_innerTargetRect.bottom();
+ ys[0].ty = innerSourceRect.y() + (m_subSourceRect.bottom() - ceilBottom + 1) * innerSourceRect.height();
+ ++ys;
+ }
+ if (m_innerTargetRect.bottom() != m_targetRect.bottom()) {
+ ys[0].y = m_innerTargetRect.bottom();
+ ys[0].ty = innerSourceRect.bottom();
+ ys[1].y = m_targetRect.bottom();
+ ys[1].ty = sourceRect.bottom();
+ ys += 2;
+ }
+ Q_ASSERT(ys == yData.data() + yData.size());
+
+ if (m_antialiasing) {
+ QSGGeometry *g = geometry();
+ Q_ASSERT(g != &m_geometry);
+
+ g->allocate(hCells * vCells * 4 + (hCells + vCells - 1) * 4,
+ hCells * vCells * 6 + (hCells + vCells) * 12);
+ g->setDrawingMode(GL_TRIANGLES);
+ SmoothVertex *vertices = reinterpret_cast<SmoothVertex *>(g->vertexData());
+ memset(vertices, 0, g->vertexCount() * g->sizeOfVertex());
+ quint16 *indices = g->indexDataAsUShort();
+
+ // The deltas are how much the fuzziness can reach into the image.
+ // Only the border vertices are moved by the vertex shader, so the fuzziness
+ // can't reach further into the image than the closest interior vertices.
+ float leftDx = xData.at(1).x - xData.at(0).x;
+ float rightDx = xData.at(xData.size() - 1).x - xData.at(xData.size() - 2).x;
+ float topDy = yData.at(1).y - yData.at(0).y;
+ float bottomDy = yData.at(yData.size() - 1).y - yData.at(yData.size() - 2).y;
+
+ float leftDu = xData.at(1).tx - xData.at(0).tx;
+ float rightDu = xData.at(xData.size() - 1).tx - xData.at(xData.size() - 2).tx;
+ float topDv = yData.at(1).ty - yData.at(0).ty;
+ float bottomDv = yData.at(yData.size() - 1).ty - yData.at(yData.size() - 2).ty;
+
+ if (hCells == 1) {
+ leftDx = rightDx *= 0.5f;
+ leftDu = rightDu *= 0.5f;
+ }
+ if (vCells == 1) {
+ topDy = bottomDy *= 0.5f;
+ topDv = bottomDv *= 0.5f;
+ }
+
+ // This delta is how much the fuzziness can reach out from the image.
+ float delta = float(qAbs(m_targetRect.width()) < qAbs(m_targetRect.height())
+ ? m_targetRect.width() : m_targetRect.height()) * 0.5f;
+
+ quint16 index = 0;
+ ys = yData.data();
+ for (int j = 0; j < vCells; ++j, ys += 2) {
+ xs = xData.data();
+ bool isTop = j == 0;
+ bool isBottom = j == vCells - 1;
+ for (int i = 0; i < hCells; ++i, xs += 2) {
+ bool isLeft = i == 0;
+ bool isRight = i == hCells - 1;
+
+ SmoothVertex *v = vertices + index;
+
+ quint16 topLeft = index;
+ for (int k = (isTop || isLeft ? 2 : 1); k--; ++v, ++index) {
+ v->x = xs[0].x;
+ v->u = xs[0].tx;
+ v->y = ys[0].y;
+ v->v = ys[0].ty;
+ }
+
+ quint16 topRight = index;
+ for (int k = (isTop || isRight ? 2 : 1); k--; ++v, ++index) {
+ v->x = xs[1].x;
+ v->u = xs[1].tx;
+ v->y = ys[0].y;
+ v->v = ys[0].ty;
+ }
+
+ quint16 bottomLeft = index;
+ for (int k = (isBottom || isLeft ? 2 : 1); k--; ++v, ++index) {
+ v->x = xs[0].x;
+ v->u = xs[0].tx;
+ v->y = ys[1].y;
+ v->v = ys[1].ty;
+ }
+
+ quint16 bottomRight = index;
+ for (int k = (isBottom || isRight ? 2 : 1); k--; ++v, ++index) {
+ v->x = xs[1].x;
+ v->u = xs[1].tx;
+ v->y = ys[1].y;
+ v->v = ys[1].ty;
+ }
+
+ appendQuad(&indices, topLeft, topRight, bottomLeft, bottomRight);
+
+ if (isTop) {
+ vertices[topLeft].dy = vertices[topRight].dy = topDy;
+ vertices[topLeft].dv = vertices[topRight].dv = topDv;
+ vertices[topLeft + 1].dy = vertices[topRight + 1].dy = -delta;
+ appendQuad(&indices, topLeft + 1, topRight + 1, topLeft, topRight);
+ }
+
+ if (isBottom) {
+ vertices[bottomLeft].dy = vertices[bottomRight].dy = -bottomDy;
+ vertices[bottomLeft].dv = vertices[bottomRight].dv = -bottomDv;
+ vertices[bottomLeft + 1].dy = vertices[bottomRight + 1].dy = delta;
+ appendQuad(&indices, bottomLeft, bottomRight, bottomLeft + 1, bottomRight + 1);
+ }
+
+ if (isLeft) {
+ vertices[topLeft].dx = vertices[bottomLeft].dx = leftDx;
+ vertices[topLeft].du = vertices[bottomLeft].du = leftDu;
+ vertices[topLeft + 1].dx = vertices[bottomLeft + 1].dx = -delta;
+ appendQuad(&indices, topLeft + 1, topLeft, bottomLeft + 1, bottomLeft);
+ }
+
+ if (isRight) {
+ vertices[topRight].dx = vertices[bottomRight].dx = -rightDx;
+ vertices[topRight].du = vertices[bottomRight].du = -rightDu;
+ vertices[topRight + 1].dx = vertices[bottomRight + 1].dx = delta;
+ appendQuad(&indices, topRight, topRight + 1, bottomRight, bottomRight + 1);
+ }
+ }
+ }
+
+ Q_ASSERT(index == g->vertexCount());
+ Q_ASSERT(indices - g->indexCount() == g->indexData());
+ } else {
+ m_geometry.allocate(hCells * vCells * 4, hCells * vCells * 6);
+ m_geometry.setDrawingMode(GL_TRIANGLES);
+ QSGGeometry::TexturedPoint2D *vertices = m_geometry.vertexDataAsTexturedPoint2D();
+ ys = yData.data();
+ for (int j = 0; j < vCells; ++j, ys += 2) {
+ xs = xData.data();
+ for (int i = 0; i < hCells; ++i, xs += 2) {
+ vertices[0].x = vertices[2].x = xs[0].x;
+ vertices[0].tx = vertices[2].tx = xs[0].tx;
+ vertices[1].x = vertices[3].x = xs[1].x;
+ vertices[1].tx = vertices[3].tx = xs[1].tx;
+
+ vertices[0].y = vertices[1].y = ys[0].y;
+ vertices[0].ty = vertices[1].ty = ys[0].ty;
+ vertices[2].y = vertices[3].y = ys[1].y;
+ vertices[2].ty = vertices[3].ty = ys[1].ty;
+
+ vertices += 4;
+ }
+ }
+
+ quint16 *indices = m_geometry.indexDataAsUShort();
+ for (int i = 0; i < 4 * vCells * hCells; i += 4)
+ appendQuad(&indices, i, i + 1, i + 2, i + 3);
+ }
+ }
+ }
+ markDirty(DirtyGeometry);
+ m_dirtyGeometry = false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgbasicimagenode_p.h b/src/quick/scenegraph/qsgbasicimagenode_p.h
new file mode 100644
index 0000000000..6820db7fe3
--- /dev/null
+++ b/src/quick/scenegraph/qsgbasicimagenode_p.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGBASICIMAGENODE_P_H
+#define QSGBASICIMAGENODE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgadaptationlayer_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_PRIVATE_EXPORT QSGBasicImageNode : public QSGImageNode
+{
+public:
+ QSGBasicImageNode();
+
+ void setTargetRect(const QRectF &rect) override;
+ void setInnerTargetRect(const QRectF &rect) override;
+ void setInnerSourceRect(const QRectF &rect) override;
+ void setSubSourceRect(const QRectF &rect) override;
+ void setTexture(QSGTexture *texture) override;
+ void setAntialiasing(bool antialiasing) override;
+ void setMirror(bool mirror) override;
+ void update() override;
+ void preprocess() override;
+
+protected:
+ virtual void updateMaterialAntialiasing() = 0;
+ virtual void setMaterialTexture(QSGTexture *texture) = 0;
+ virtual QSGTexture *materialTexture() const = 0;
+ virtual bool updateMaterialBlending() = 0;
+ virtual bool supportsWrap(const QSize &size) const = 0;
+
+ void updateGeometry();
+
+ QRectF m_targetRect;
+ QRectF m_innerTargetRect;
+ QRectF m_innerSourceRect;
+ QRectF m_subSourceRect;
+
+ uint m_antialiasing : 1;
+ uint m_mirror : 1;
+ uint m_dirtyGeometry : 1;
+
+ QSGGeometry m_geometry;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/scenegraph/qsgbasicrectanglenode.cpp b/src/quick/scenegraph/qsgbasicrectanglenode.cpp
new file mode 100644
index 0000000000..14d2dc9677
--- /dev/null
+++ b/src/quick/scenegraph/qsgbasicrectanglenode.cpp
@@ -0,0 +1,687 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgbasicrectanglenode_p.h"
+
+#include <QtCore/qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace
+{
+ struct Color4ub
+ {
+ unsigned char r, g, b, a;
+ };
+
+ Color4ub operator *(Color4ub c, float t) { c.a *= t; c.r *= t; c.g *= t; c.b *= t; return c; }
+ Color4ub operator +(Color4ub a, Color4ub b) { a.a += b.a; a.r += b.r; a.g += b.g; a.b += b.b; return a; }
+
+ inline Color4ub colorToColor4ub(const QColor &c)
+ {
+ Color4ub color = { uchar(qRound(c.redF() * c.alphaF() * 255)),
+ uchar(qRound(c.greenF() * c.alphaF() * 255)),
+ uchar(qRound(c.blueF() * c.alphaF() * 255)),
+ uchar(qRound(c.alphaF() * 255))
+ };
+ return color;
+ }
+
+ // Same layout as QSGGeometry::ColoredPoint2D, but uses Color4ub for convenience.
+ struct Vertex
+ {
+ float x, y;
+ Color4ub color;
+ void set(float nx, float ny, Color4ub ncolor)
+ {
+ x = nx; y = ny; color = ncolor;
+ }
+ };
+
+ struct SmoothVertex : public Vertex
+ {
+ float dx, dy;
+ void set(float nx, float ny, Color4ub ncolor, float ndx, float ndy)
+ {
+ Vertex::set(nx, ny, ncolor);
+ dx = ndx; dy = ndy;
+ }
+ };
+
+ const QSGGeometry::AttributeSet &smoothAttributeSet()
+ {
+ static QSGGeometry::Attribute data[] = {
+ QSGGeometry::Attribute::createWithSemantic(0, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::POSITION),
+ QSGGeometry::Attribute::createWithSemantic(1, 4, QSGGeometry::TypeUnsignedByte, QSGGeometry::Attribute::COLOR),
+ QSGGeometry::Attribute::createWithSemantic(2, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::TEXCOORD)
+ };
+ static QSGGeometry::AttributeSet attrs = { 3, sizeof(SmoothVertex), data };
+ return attrs;
+ }
+}
+
+QSGBasicRectangleNode::QSGBasicRectangleNode()
+ : m_radius(0)
+ , m_pen_width(0)
+ , m_aligned(true)
+ , m_antialiasing(false)
+ , m_gradient_is_opaque(true)
+ , m_dirty_geometry(false)
+ , m_geometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0)
+{
+ setGeometry(&m_geometry);
+
+#ifdef QSG_RUNTIME_DESCRIPTION
+ qsgnode_set_description(this, QLatin1String("rectangle"));
+#endif
+}
+
+void QSGBasicRectangleNode::setRect(const QRectF &rect)
+{
+ if (rect == m_rect)
+ return;
+ m_rect = rect;
+ m_dirty_geometry = true;
+}
+
+void QSGBasicRectangleNode::setColor(const QColor &color)
+{
+ if (color == m_color)
+ return;
+ m_color = color;
+ if (m_gradient_stops.isEmpty())
+ m_dirty_geometry = true;
+}
+
+void QSGBasicRectangleNode::setPenColor(const QColor &color)
+{
+ if (color == m_border_color)
+ return;
+ m_border_color = color;
+ if (m_pen_width > 0)
+ m_dirty_geometry = true;
+}
+
+void QSGBasicRectangleNode::setPenWidth(qreal width)
+{
+ if (width == m_pen_width)
+ return;
+ m_pen_width = width;
+ m_dirty_geometry = true;
+}
+
+
+void QSGBasicRectangleNode::setGradientStops(const QGradientStops &stops)
+{
+ if (stops.constData() == m_gradient_stops.constData())
+ return;
+
+ m_gradient_stops = stops;
+
+ m_gradient_is_opaque = true;
+ for (int i = 0; i < stops.size(); ++i)
+ m_gradient_is_opaque &= stops.at(i).second.alpha() == 0xff;
+ m_dirty_geometry = true;
+}
+
+void QSGBasicRectangleNode::setRadius(qreal radius)
+{
+ if (radius == m_radius)
+ return;
+ m_radius = radius;
+ m_dirty_geometry = true;
+}
+
+void QSGBasicRectangleNode::setAntialiasing(bool antialiasing)
+{
+ if (!supportsAntialiasing())
+ return;
+
+ if (antialiasing == m_antialiasing)
+ return;
+ m_antialiasing = antialiasing;
+ if (m_antialiasing) {
+ setGeometry(new QSGGeometry(smoothAttributeSet(), 0));
+ setFlag(OwnsGeometry, true);
+ } else {
+ setGeometry(&m_geometry);
+ setFlag(OwnsGeometry, false);
+ }
+ updateMaterialAntialiasing();
+ m_dirty_geometry = true;
+}
+
+void QSGBasicRectangleNode::setAligned(bool aligned)
+{
+ if (aligned == m_aligned)
+ return;
+ m_aligned = aligned;
+ m_dirty_geometry = true;
+}
+
+void QSGBasicRectangleNode::update()
+{
+ if (m_dirty_geometry) {
+ updateGeometry();
+ m_dirty_geometry = false;
+
+ QSGNode::DirtyState state = QSGNode::DirtyGeometry;
+ updateMaterialBlending(&state);
+ markDirty(state);
+ }
+}
+
+void QSGBasicRectangleNode::updateGeometry()
+{
+ float width = float(m_rect.width());
+ float height = float(m_rect.height());
+ float penWidth = qMin(qMin(width, height) * 0.5f, float(m_pen_width));
+
+ if (m_aligned)
+ penWidth = qRound(penWidth);
+
+ QSGGeometry *g = geometry();
+ g->setDrawingMode(QSGGeometry::DrawTriangleStrip);
+ int vertexStride = g->sizeOfVertex();
+
+ union {
+ Vertex *vertices;
+ SmoothVertex *smoothVertices;
+ };
+
+ Color4ub fillColor = colorToColor4ub(m_color);
+ Color4ub borderColor = colorToColor4ub(m_border_color);
+ Color4ub transparent = { 0, 0, 0, 0 };
+ const QGradientStops &stops = m_gradient_stops;
+
+ int nextGradientStop = 0;
+ float gradientPos = penWidth / height;
+ while (nextGradientStop < stops.size() && stops.at(nextGradientStop).first <= gradientPos)
+ ++nextGradientStop;
+ int lastGradientStop = stops.size() - 1;
+ float lastGradientPos = 1.0f - penWidth / height;
+ while (lastGradientStop >= nextGradientStop && stops.at(lastGradientStop).first >= lastGradientPos)
+ --lastGradientStop;
+ int gradientIntersections = (lastGradientStop - nextGradientStop + 1);
+
+ if (m_radius > 0) {
+ // Rounded corners.
+
+ // Radius should never exceeds half of the width or half of the height
+ float radius = qMin(qMin(width, height) * 0.5f, float(m_radius));
+ QRectF innerRect = m_rect;
+ innerRect.adjust(radius, radius, -radius, -radius);
+
+ float innerRadius = radius - penWidth * 1.0f;
+ float outerRadius = radius;
+ float delta = qMin(width, height) * 0.5f;
+
+ // Number of segments per corner, approximately one per 3 pixels.
+ int segments = qBound(3, qCeil(outerRadius * (M_PI / 6)), 18);
+
+ /*
+
+ --+--__
+ --+--__--__
+ | --__--__
+ | seg --__--+
+ --+-__ ment _+ \
+ --+-__--__ - \ \
+ --__--+ se \ \
+ + \ g \ \
+ \ \ m \ \
+ -----------+--+ e \ \ <- gradient line
+ \ \ nt\ \
+ fill +--+----+--+
+ | | | |
+ border
+ inner AA outer AA (AA = antialiasing)
+
+ */
+
+ int innerVertexCount = (segments + 1) * 4 + gradientIntersections * 2;
+ int outerVertexCount = (segments + 1) * 4;
+ int vertexCount = innerVertexCount;
+ if (m_antialiasing || penWidth)
+ vertexCount += innerVertexCount;
+ if (penWidth)
+ vertexCount += outerVertexCount;
+ if (m_antialiasing && penWidth)
+ vertexCount += outerVertexCount;
+
+ int fillIndexCount = innerVertexCount;
+ int innerAAIndexCount = innerVertexCount * 2 + 2;
+ int borderIndexCount = innerVertexCount * 2 + 2;
+ int outerAAIndexCount = outerVertexCount * 2 + 2;
+ int indexCount = 0;
+ int fillHead = 0;
+ int innerAAHead = 0;
+ int innerAATail = 0;
+ int borderHead = 0;
+ int borderTail = 0;
+ int outerAAHead = 0;
+ int outerAATail = 0;
+ bool hasFill = m_color.alpha() > 0 || !stops.isEmpty();
+ if (hasFill)
+ indexCount += fillIndexCount;
+ if (m_antialiasing) {
+ innerAATail = innerAAHead = indexCount + (innerAAIndexCount >> 1) + 1;
+ indexCount += innerAAIndexCount;
+ }
+ if (penWidth) {
+ borderTail = borderHead = indexCount + (borderIndexCount >> 1) + 1;
+ indexCount += borderIndexCount;
+ }
+ if (m_antialiasing && penWidth) {
+ outerAATail = outerAAHead = indexCount + (outerAAIndexCount >> 1) + 1;
+ indexCount += outerAAIndexCount;
+ }
+
+ g->allocate(vertexCount, indexCount);
+ vertices = reinterpret_cast<Vertex *>(g->vertexData());
+ memset(vertices, 0, vertexCount * vertexStride);
+ quint16 *indices = g->indexDataAsUShort();
+ quint16 index = 0;
+
+ float py = 0; // previous inner y-coordinate.
+ float plx = 0; // previous inner left x-coordinate.
+ float prx = 0; // previous inner right x-coordinate.
+
+ float angle = 0.5f * float(M_PI) / segments;
+ float cosStep = qFastCos(angle);
+ float sinStep = qFastSin(angle);
+
+ for (int part = 0; part < 2; ++part) {
+ float c = 1 - part;
+ float s = part;
+ for (int i = 0; i <= segments; ++i) {
+ float y, lx, rx;
+ if (innerRadius > 0) {
+ y = (part ? innerRect.bottom() : innerRect.top()) - innerRadius * c; // current inner y-coordinate.
+ lx = innerRect.left() - innerRadius * s; // current inner left x-coordinate.
+ rx = innerRect.right() + innerRadius * s; // current inner right x-coordinate.
+ gradientPos = ((part ? innerRect.height() : 0) + radius - innerRadius * c) / height;
+ } else {
+ y = (part ? innerRect.bottom() + innerRadius : innerRect.top() - innerRadius); // current inner y-coordinate.
+ lx = innerRect.left() - innerRadius; // current inner left x-coordinate.
+ rx = innerRect.right() + innerRadius; // current inner right x-coordinate.
+ gradientPos = ((part ? innerRect.height() + innerRadius : -innerRadius) + radius) / height;
+ }
+ float Y = (part ? innerRect.bottom() : innerRect.top()) - outerRadius * c; // current outer y-coordinate.
+ float lX = innerRect.left() - outerRadius * s; // current outer left x-coordinate.
+ float rX = innerRect.right() + outerRadius * s; // current outer right x-coordinate.
+
+ while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) {
+ // Insert vertices at gradient stops.
+ float gy = (innerRect.top() - radius) + stops.at(nextGradientStop).first * height;
+ float t = (gy - py) / (y - py);
+ float glx = plx * (1 - t) + t * lx;
+ float grx = prx * (1 - t) + t * rx;
+
+ fillColor = colorToColor4ub(stops.at(nextGradientStop).second);
+
+ if (hasFill) {
+ indices[fillHead++] = index;
+ indices[fillHead++] = index + 1;
+ }
+
+ if (penWidth) {
+ --borderHead;
+ indices[borderHead] = indices[borderHead + 2];
+ indices[--borderHead] = index + 2;
+ indices[borderTail++] = index + 3;
+ indices[borderTail] = indices[borderTail - 2];
+ ++borderTail;
+ }
+
+ if (m_antialiasing) {
+ indices[--innerAAHead] = index + 2;
+ indices[--innerAAHead] = index;
+ indices[innerAATail++] = index + 1;
+ indices[innerAATail++] = index + 3;
+
+ bool lower = stops.at(nextGradientStop).first > 0.5f;
+ float dy = lower ? qMin(0.0f, height - gy - delta) : qMax(0.0f, delta - gy);
+ smoothVertices[index++].set(grx, gy, fillColor, width - grx - delta, dy);
+ smoothVertices[index++].set(glx, gy, fillColor, delta - glx, dy);
+ if (penWidth) {
+ smoothVertices[index++].set(grx, gy, borderColor, 0.49f * penWidth * s, -0.49f * penWidth * c);
+ smoothVertices[index++].set(glx, gy, borderColor, -0.49f * penWidth * s, -0.49f * penWidth * c);
+ } else {
+ dy = lower ? delta : -delta;
+ smoothVertices[index++].set(grx, gy, transparent, delta, dy);
+ smoothVertices[index++].set(glx, gy, transparent, -delta, dy);
+ }
+ } else {
+ vertices[index++].set(grx, gy, fillColor);
+ vertices[index++].set(glx, gy, fillColor);
+ if (penWidth) {
+ vertices[index++].set(grx, gy, borderColor);
+ vertices[index++].set(glx, gy, borderColor);
+ }
+ }
+ ++nextGradientStop;
+ }
+
+ if (!stops.isEmpty()) {
+ if (nextGradientStop == 0) {
+ fillColor = colorToColor4ub(stops.at(0).second);
+ } else if (nextGradientStop == stops.size()) {
+ fillColor = colorToColor4ub(stops.last().second);
+ } else {
+ const QGradientStop &prev = stops.at(nextGradientStop - 1);
+ const QGradientStop &next = stops.at(nextGradientStop);
+ float t = (gradientPos - prev.first) / (next.first - prev.first);
+ fillColor = colorToColor4ub(prev.second) * (1 - t) + colorToColor4ub(next.second) * t;
+ }
+ }
+
+ if (hasFill) {
+ indices[fillHead++] = index;
+ indices[fillHead++] = index + 1;
+ }
+
+ if (penWidth) {
+ indices[--borderHead] = index + 4;
+ indices[--borderHead] = index + 2;
+ indices[borderTail++] = index + 3;
+ indices[borderTail++] = index + 5;
+ }
+
+ if (m_antialiasing) {
+ indices[--innerAAHead] = index + 2;
+ indices[--innerAAHead] = index;
+ indices[innerAATail++] = index + 1;
+ indices[innerAATail++] = index + 3;
+
+ float dy = part ? qMin(0.0f, height - y - delta) : qMax(0.0f, delta - y);
+ smoothVertices[index++].set(rx, y, fillColor, width - rx - delta, dy);
+ smoothVertices[index++].set(lx, y, fillColor, delta - lx, dy);
+
+ dy = part ? delta : -delta;
+ if (penWidth) {
+ smoothVertices[index++].set(rx, y, borderColor, 0.49f * penWidth * s, -0.49f * penWidth * c);
+ smoothVertices[index++].set(lx, y, borderColor, -0.49f * penWidth * s, -0.49f * penWidth * c);
+ smoothVertices[index++].set(rX, Y, borderColor, -0.49f * penWidth * s, 0.49f * penWidth * c);
+ smoothVertices[index++].set(lX, Y, borderColor, 0.49f * penWidth * s, 0.49f * penWidth * c);
+ smoothVertices[index++].set(rX, Y, transparent, delta, dy);
+ smoothVertices[index++].set(lX, Y, transparent, -delta, dy);
+
+ indices[--outerAAHead] = index - 2;
+ indices[--outerAAHead] = index - 4;
+ indices[outerAATail++] = index - 3;
+ indices[outerAATail++] = index - 1;
+ } else {
+ smoothVertices[index++].set(rx, y, transparent, delta, dy);
+ smoothVertices[index++].set(lx, y, transparent, -delta, dy);
+ }
+ } else {
+ vertices[index++].set(rx, y, fillColor);
+ vertices[index++].set(lx, y, fillColor);
+ if (penWidth) {
+ vertices[index++].set(rx, y, borderColor);
+ vertices[index++].set(lx, y, borderColor);
+ vertices[index++].set(rX, Y, borderColor);
+ vertices[index++].set(lX, Y, borderColor);
+ }
+ }
+
+ py = y;
+ plx = lx;
+ prx = rx;
+
+ // Rotate
+ qreal tmp = c;
+ c = c * cosStep - s * sinStep;
+ s = s * cosStep + tmp * sinStep;
+ }
+ }
+ Q_ASSERT(index == vertexCount);
+
+ // Close the triangle strips.
+ if (m_antialiasing) {
+ indices[--innerAAHead] = indices[innerAATail - 1];
+ indices[--innerAAHead] = indices[innerAATail - 2];
+ Q_ASSERT(innerAATail <= indexCount);
+ }
+ if (penWidth) {
+ indices[--borderHead] = indices[borderTail - 1];
+ indices[--borderHead] = indices[borderTail - 2];
+ Q_ASSERT(borderTail <= indexCount);
+ }
+ if (m_antialiasing && penWidth) {
+ indices[--outerAAHead] = indices[outerAATail - 1];
+ indices[--outerAAHead] = indices[outerAATail - 2];
+ Q_ASSERT(outerAATail == indexCount);
+ }
+ } else {
+ // Straight corners.
+ QRectF innerRect = m_rect;
+ QRectF outerRect = m_rect;
+
+ if (penWidth)
+ innerRect.adjust(1.0f * penWidth, 1.0f * penWidth, -1.0f * penWidth, -1.0f * penWidth);
+
+ float delta = qMin(width, height) * 0.5f;
+ int innerVertexCount = 4 + gradientIntersections * 2;
+ int outerVertexCount = 4;
+ int vertexCount = innerVertexCount;
+ if (m_antialiasing || penWidth)
+ vertexCount += innerVertexCount;
+ if (penWidth)
+ vertexCount += outerVertexCount;
+ if (m_antialiasing && penWidth)
+ vertexCount += outerVertexCount;
+
+ int fillIndexCount = innerVertexCount;
+ int innerAAIndexCount = innerVertexCount * 2 + 2;
+ int borderIndexCount = innerVertexCount * 2 + 2;
+ int outerAAIndexCount = outerVertexCount * 2 + 2;
+ int indexCount = 0;
+ int fillHead = 0;
+ int innerAAHead = 0;
+ int innerAATail = 0;
+ int borderHead = 0;
+ int borderTail = 0;
+ int outerAAHead = 0;
+ int outerAATail = 0;
+ bool hasFill = m_color.alpha() > 0 || !stops.isEmpty();
+ if (hasFill)
+ indexCount += fillIndexCount;
+ if (m_antialiasing) {
+ innerAATail = innerAAHead = indexCount + (innerAAIndexCount >> 1) + 1;
+ indexCount += innerAAIndexCount;
+ }
+ if (penWidth) {
+ borderTail = borderHead = indexCount + (borderIndexCount >> 1) + 1;
+ indexCount += borderIndexCount;
+ }
+ if (m_antialiasing && penWidth) {
+ outerAATail = outerAAHead = indexCount + (outerAAIndexCount >> 1) + 1;
+ indexCount += outerAAIndexCount;
+ }
+
+ g->allocate(vertexCount, indexCount);
+ vertices = reinterpret_cast<Vertex *>(g->vertexData());
+ memset(vertices, 0, vertexCount * vertexStride);
+ quint16 *indices = g->indexDataAsUShort();
+ quint16 index = 0;
+
+ float lx = innerRect.left();
+ float rx = innerRect.right();
+ float lX = outerRect.left();
+ float rX = outerRect.right();
+
+ for (int part = -1; part <= 1; part += 2) {
+ float y = (part == 1 ? innerRect.bottom() : innerRect.top());
+ float Y = (part == 1 ? outerRect.bottom() : outerRect.top());
+ gradientPos = (y - innerRect.top() + penWidth) / height;
+
+ while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) {
+ // Insert vertices at gradient stops.
+ float gy = (innerRect.top() - penWidth) + stops.at(nextGradientStop).first * height;
+
+ fillColor = colorToColor4ub(stops.at(nextGradientStop).second);
+
+ if (hasFill) {
+ indices[fillHead++] = index;
+ indices[fillHead++] = index + 1;
+ }
+
+ if (penWidth) {
+ --borderHead;
+ indices[borderHead] = indices[borderHead + 2];
+ indices[--borderHead] = index + 2;
+ indices[borderTail++] = index + 3;
+ indices[borderTail] = indices[borderTail - 2];
+ ++borderTail;
+ }
+
+ if (m_antialiasing) {
+ indices[--innerAAHead] = index + 2;
+ indices[--innerAAHead] = index;
+ indices[innerAATail++] = index + 1;
+ indices[innerAATail++] = index + 3;
+
+ bool lower = stops.at(nextGradientStop).first > 0.5f;
+ float dy = lower ? qMin(0.0f, height - gy - delta) : qMax(0.0f, delta - gy);
+ smoothVertices[index++].set(rx, gy, fillColor, width - rx - delta, dy);
+ smoothVertices[index++].set(lx, gy, fillColor, delta - lx, dy);
+ if (penWidth) {
+ smoothVertices[index++].set(rx, gy, borderColor, 0.49f * penWidth, (lower ? 0.49f : -0.49f) * penWidth);
+ smoothVertices[index++].set(lx, gy, borderColor, -0.49f * penWidth, (lower ? 0.49f : -0.49f) * penWidth);
+ } else {
+ smoothVertices[index++].set(rx, gy, transparent, delta, lower ? delta : -delta);
+ smoothVertices[index++].set(lx, gy, transparent, -delta, lower ? delta : -delta);
+ }
+ } else {
+ vertices[index++].set(rx, gy, fillColor);
+ vertices[index++].set(lx, gy, fillColor);
+ if (penWidth) {
+ vertices[index++].set(rx, gy, borderColor);
+ vertices[index++].set(lx, gy, borderColor);
+ }
+ }
+ ++nextGradientStop;
+ }
+
+ if (!stops.isEmpty()) {
+ if (nextGradientStop == 0) {
+ fillColor = colorToColor4ub(stops.at(0).second);
+ } else if (nextGradientStop == stops.size()) {
+ fillColor = colorToColor4ub(stops.last().second);
+ } else {
+ const QGradientStop &prev = stops.at(nextGradientStop - 1);
+ const QGradientStop &next = stops.at(nextGradientStop);
+ float t = (gradientPos - prev.first) / (next.first - prev.first);
+ fillColor = colorToColor4ub(prev.second) * (1 - t) + colorToColor4ub(next.second) * t;
+ }
+ }
+
+ if (hasFill) {
+ indices[fillHead++] = index;
+ indices[fillHead++] = index + 1;
+ }
+
+ if (penWidth) {
+ indices[--borderHead] = index + 4;
+ indices[--borderHead] = index + 2;
+ indices[borderTail++] = index + 3;
+ indices[borderTail++] = index + 5;
+ }
+
+ if (m_antialiasing) {
+ indices[--innerAAHead] = index + 2;
+ indices[--innerAAHead] = index;
+ indices[innerAATail++] = index + 1;
+ indices[innerAATail++] = index + 3;
+
+ float dy = part == 1 ? qMin(0.0f, height - y - delta) : qMax(0.0f, delta - y);
+ smoothVertices[index++].set(rx, y, fillColor, width - rx - delta, dy);
+ smoothVertices[index++].set(lx, y, fillColor, delta - lx, dy);
+
+ if (penWidth) {
+ smoothVertices[index++].set(rx, y, borderColor, 0.49f * penWidth, 0.49f * penWidth * part);
+ smoothVertices[index++].set(lx, y, borderColor, -0.49f * penWidth, 0.49f * penWidth * part);
+ smoothVertices[index++].set(rX, Y, borderColor, -0.49f * penWidth, -0.49f * penWidth * part);
+ smoothVertices[index++].set(lX, Y, borderColor, 0.49f * penWidth, -0.49f * penWidth * part);
+ smoothVertices[index++].set(rX, Y, transparent, delta, delta * part);
+ smoothVertices[index++].set(lX, Y, transparent, -delta, delta * part);
+
+ indices[--outerAAHead] = index - 2;
+ indices[--outerAAHead] = index - 4;
+ indices[outerAATail++] = index - 3;
+ indices[outerAATail++] = index - 1;
+ } else {
+ smoothVertices[index++].set(rx, y, transparent, delta, delta * part);
+ smoothVertices[index++].set(lx, y, transparent, -delta, delta * part);
+ }
+ } else {
+ vertices[index++].set(rx, y, fillColor);
+ vertices[index++].set(lx, y, fillColor);
+ if (penWidth) {
+ vertices[index++].set(rx, y, borderColor);
+ vertices[index++].set(lx, y, borderColor);
+ vertices[index++].set(rX, Y, borderColor);
+ vertices[index++].set(lX, Y, borderColor);
+ }
+ }
+ }
+ Q_ASSERT(index == vertexCount);
+
+ // Close the triangle strips.
+ if (m_antialiasing) {
+ indices[--innerAAHead] = indices[innerAATail - 1];
+ indices[--innerAAHead] = indices[innerAATail - 2];
+ Q_ASSERT(innerAATail <= indexCount);
+ }
+ if (penWidth) {
+ indices[--borderHead] = indices[borderTail - 1];
+ indices[--borderHead] = indices[borderTail - 2];
+ Q_ASSERT(borderTail <= indexCount);
+ }
+ if (m_antialiasing && penWidth) {
+ indices[--outerAAHead] = indices[outerAATail - 1];
+ indices[--outerAAHead] = indices[outerAATail - 2];
+ Q_ASSERT(outerAATail == indexCount);
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgbasicrectanglenode_p.h b/src/quick/scenegraph/qsgbasicrectanglenode_p.h
new file mode 100644
index 0000000000..b1d1457590
--- /dev/null
+++ b/src/quick/scenegraph/qsgbasicrectanglenode_p.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#ifndef QSGBASICRECTANGLENODE_P_H
+#define QSGBASICRECTANGLENODE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgadaptationlayer_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_PRIVATE_EXPORT QSGBasicRectangleNode : public QSGRectangleNode
+{
+public:
+ QSGBasicRectangleNode();
+
+ void setRect(const QRectF &rect) override;
+ void setColor(const QColor &color) override;
+ void setPenColor(const QColor &color) override;
+ void setPenWidth(qreal width) override;
+ void setGradientStops(const QGradientStops &stops) override;
+ void setRadius(qreal radius) override;
+ void setAntialiasing(bool antialiasing) override;
+ void setAligned(bool aligned) override;
+ void update() override;
+
+protected:
+ virtual bool supportsAntialiasing() const { return true; }
+ virtual void updateMaterialAntialiasing() = 0;
+ virtual void updateMaterialBlending(QSGNode::DirtyState *state) = 0;
+
+ void updateGeometry();
+ void updateGradientTexture();
+
+ QRectF m_rect;
+ QGradientStops m_gradient_stops;
+ QColor m_color;
+ QColor m_border_color;
+ qreal m_radius;
+ qreal m_pen_width;
+
+ uint m_aligned : 1;
+ uint m_antialiasing : 1;
+ uint m_gradient_is_opaque : 1;
+ uint m_dirty_geometry : 1;
+
+ QSGGeometry m_geometry;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode.cpp b/src/quick/scenegraph/qsgdefaultglyphnode.cpp
index 082a4f8c09..b856d99bc1 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode.cpp
+++ b/src/quick/scenegraph/qsgdefaultglyphnode.cpp
@@ -40,59 +40,11 @@
#include "qsgdefaultglyphnode_p.h"
#include "qsgdefaultglyphnode_p_p.h"
-#include <qopenglshaderprogram.h>
-#include <private/qfont_p.h>
-
QT_BEGIN_NAMESPACE
-QSGDefaultGlyphNode::QSGDefaultGlyphNode()
- : m_style(QQuickText::Normal)
- , m_material(0)
- , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0)
-{
- m_geometry.setDrawingMode(GL_TRIANGLES);
- setGeometry(&m_geometry);
-}
-
-QSGDefaultGlyphNode::~QSGDefaultGlyphNode()
-{
- delete m_material;
-}
-
-void QSGDefaultGlyphNode::setColor(const QColor &color)
-{
- m_color = color;
- if (m_material != 0) {
- m_material->setColor(color);
- markDirty(DirtyMaterial);
- }
-}
-
-void QSGDefaultGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs)
-{
- if (m_material != 0)
- delete m_material;
-
- m_position = position;
- m_glyphs = glyphs;
-
-#ifdef QSG_RUNTIME_DESCRIPTION
- qsgnode_set_description(this, QLatin1String("glyphs"));
-#endif
-}
-
-void QSGDefaultGlyphNode::setStyle(QQuickText::TextStyle style)
-{
- if (m_style == style)
- return;
- m_style = style;
-}
-
-void QSGDefaultGlyphNode::setStyleColor(const QColor &color)
+void QSGDefaultGlyphNode::setMaterialColor(const QColor &color)
{
- if (m_styleColor == color)
- return;
- m_styleColor = color;
+ static_cast<QSGTextMaskMaterial *>(m_material)->setColor(color);
}
void QSGDefaultGlyphNode::update()
@@ -120,11 +72,12 @@ void QSGDefaultGlyphNode::update()
m_material = material;
}
- m_material->setColor(m_color);
+ QSGTextMaskMaterial *textMaskMaterial = static_cast<QSGTextMaskMaterial *>(m_material);
+ textMaskMaterial->setColor(m_color);
QRectF boundingRect;
- m_material->populate(m_position, m_glyphs.glyphIndexes(), m_glyphs.positions(), geometry(),
- &boundingRect, &m_baseLine, margins);
+ textMaskMaterial->populate(m_position, m_glyphs.glyphIndexes(), m_glyphs.positions(), geometry(),
+ &boundingRect, &m_baseLine, margins);
setBoundingRect(boundingRect);
setMaterial(m_material);
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.h b/src/quick/scenegraph/qsgdefaultglyphnode_p.h
index 4efeaea373..0eb7a4e4bd 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode_p.h
+++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.h
@@ -52,39 +52,15 @@
//
#include <private/qsgadaptationlayer_p.h>
-#include <QtQuick/qsgnode.h>
+#include <private/qsgbasicglyphnode_p.h>
QT_BEGIN_NAMESPACE
-class QGlyphs;
-class QSGTextMaskMaterial;
-class QSGDefaultGlyphNode: public QSGGlyphNode
+class QSGDefaultGlyphNode : public QSGBasicGlyphNode
{
public:
- QSGDefaultGlyphNode();
- virtual ~QSGDefaultGlyphNode();
-
- virtual QPointF baseLine() const { return m_baseLine; }
- virtual void setGlyphs(const QPointF &position, const QGlyphRun &glyphs);
- virtual void setColor(const QColor &color);
-
- virtual void setPreferredAntialiasingMode(AntialiasingMode) { }
- virtual void setStyle(QQuickText::TextStyle);
- virtual void setStyleColor(const QColor &);
-
- virtual void update();
-
-protected:
- QGlyphRun m_glyphs;
- QPointF m_position;
- QColor m_color;
- QQuickText::TextStyle m_style;
- QColor m_styleColor;
-
- QPointF m_baseLine;
- QSGTextMaskMaterial *m_material;
-
- QSGGeometry m_geometry;
+ void setMaterialColor(const QColor &color) override;
+ void update() override;
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdefaultimagenode.cpp b/src/quick/scenegraph/qsgdefaultimagenode.cpp
index 8e61bf9ec2..8ab5575b18 100644
--- a/src/quick/scenegraph/qsgdefaultimagenode.cpp
+++ b/src/quick/scenegraph/qsgdefaultimagenode.cpp
@@ -39,38 +39,12 @@
#include "qsgdefaultimagenode_p.h"
#include <private/qsgmaterialshader_p.h>
-
-#include <QtCore/qvarlengtharray.h>
-#include <QtCore/qmath.h>
-#include <QtGui/qopenglfunctions.h>
-
-#include <qsgtexturematerial.h>
#include <private/qsgtexturematerial_p.h>
-#include <qsgmaterial.h>
+#include <QtGui/qopenglfunctions.h>
+#include <QtCore/qmath.h>
QT_BEGIN_NAMESPACE
-namespace
-{
- struct SmoothVertex
- {
- float x, y, u, v;
- float dx, dy, du, dv;
- };
-
- const QSGGeometry::AttributeSet &smoothAttributeSet()
- {
- static QSGGeometry::Attribute data[] = {
- QSGGeometry::Attribute::createWithSemantic(0, 2, GL_FLOAT, QSGGeometry::Attribute::POSITION),
- QSGGeometry::Attribute::createWithSemantic(1, 2, GL_FLOAT, QSGGeometry::Attribute::TEXCOORD),
- QSGGeometry::Attribute::createWithSemantic(2, 2, GL_FLOAT, QSGGeometry::Attribute::TEXCOORD1),
- QSGGeometry::Attribute::createWithSemantic(3, 2, GL_FLOAT, QSGGeometry::Attribute::TEXCOORD2)
- };
- static QSGGeometry::AttributeSet attrs = { 4, sizeof(SmoothVertex), data };
- return attrs;
- }
-}
-
class SmoothTextureMaterialShader : public QSGTextureMaterialShader
{
public:
@@ -143,463 +117,6 @@ void SmoothTextureMaterialShader::initialize()
QSGTextureMaterialShader::initialize();
}
-QSGDefaultNoMaterialImageNode::QSGDefaultNoMaterialImageNode()
- : m_innerSourceRect(0, 0, 1, 1)
- , m_subSourceRect(0, 0, 1, 1)
- , m_antialiasing(false)
- , m_mirror(false)
- , m_dirtyGeometry(false)
- , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4)
-{
- setGeometry(&m_geometry);
-
-#ifdef QSG_RUNTIME_DESCRIPTION
- qsgnode_set_description(this, QLatin1String("image"));
-#endif
-}
-
-void QSGDefaultNoMaterialImageNode::setTargetRect(const QRectF &rect)
-{
- if (rect == m_targetRect)
- return;
- m_targetRect = rect;
- m_dirtyGeometry = true;
-}
-
-void QSGDefaultNoMaterialImageNode::setInnerTargetRect(const QRectF &rect)
-{
- if (rect == m_innerTargetRect)
- return;
- m_innerTargetRect = rect;
- m_dirtyGeometry = true;
-}
-
-void QSGDefaultNoMaterialImageNode::setInnerSourceRect(const QRectF &rect)
-{
- if (rect == m_innerSourceRect)
- return;
- m_innerSourceRect = rect;
- m_dirtyGeometry = true;
-}
-
-void QSGDefaultNoMaterialImageNode::setSubSourceRect(const QRectF &rect)
-{
- if (rect == m_subSourceRect)
- return;
- m_subSourceRect = rect;
- m_dirtyGeometry = true;
-}
-
-void QSGDefaultNoMaterialImageNode::setTexture(QSGTexture *texture)
-{
- Q_ASSERT(texture);
-
- setMaterialTexture(texture);
- updateMaterialBlending();
-
- markDirty(DirtyMaterial);
-
- // Because the texture can be a different part of the atlas, we need to update it...
- m_dirtyGeometry = true;
-}
-
-void QSGDefaultNoMaterialImageNode::setAntialiasing(bool antialiasing)
-{
- if (antialiasing == m_antialiasing)
- return;
- m_antialiasing = antialiasing;
- if (m_antialiasing) {
- setGeometry(new QSGGeometry(smoothAttributeSet(), 0));
- setFlag(OwnsGeometry, true);
- } else {
- setGeometry(&m_geometry);
- setFlag(OwnsGeometry, false);
- }
- updateMaterialAntialiasing();
- m_dirtyGeometry = true;
-}
-
-void QSGDefaultNoMaterialImageNode::setMirror(bool mirror)
-{
- if (mirror == m_mirror)
- return;
- m_mirror = mirror;
- m_dirtyGeometry = true;
-}
-
-
-void QSGDefaultNoMaterialImageNode::update()
-{
- if (m_dirtyGeometry)
- updateGeometry();
-}
-
-void QSGDefaultNoMaterialImageNode::preprocess()
-{
- bool doDirty = false;
- QSGDynamicTexture *t = qobject_cast<QSGDynamicTexture *>(materialTexture());
- if (t) {
- doDirty = t->updateTexture();
- if (doDirty)
- updateGeometry();
- }
-
- if (updateMaterialBlending())
- doDirty = true;
-
- if (doDirty)
- markDirty(DirtyMaterial);
-}
-
-inline static bool isPowerOfTwo(int x)
-{
- // Assumption: x >= 1
- return x == (x & -x);
-}
-
-namespace {
- struct X { float x, tx; };
- struct Y { float y, ty; };
-}
-
-static inline void appendQuad(quint16 **indices, quint16 topLeft, quint16 topRight,
- quint16 bottomLeft, quint16 bottomRight)
-{
- *(*indices)++ = topLeft;
- *(*indices)++ = bottomLeft;
- *(*indices)++ = bottomRight;
- *(*indices)++ = bottomRight;
- *(*indices)++ = topRight;
- *(*indices)++ = topLeft;
-}
-
-void QSGDefaultNoMaterialImageNode::updateGeometry()
-{
- Q_ASSERT(!m_targetRect.isEmpty());
- const QSGTexture *t = materialTexture();
- if (!t) {
- QSGGeometry *g = geometry();
- g->allocate(4);
- g->setDrawingMode(GL_TRIANGLE_STRIP);
- memset(g->vertexData(), 0, g->sizeOfVertex() * 4);
- } else {
- QRectF sourceRect = t->normalizedTextureSubRect();
-
- QRectF innerSourceRect(sourceRect.x() + m_innerSourceRect.x() * sourceRect.width(),
- sourceRect.y() + m_innerSourceRect.y() * sourceRect.height(),
- m_innerSourceRect.width() * sourceRect.width(),
- m_innerSourceRect.height() * sourceRect.height());
-
- bool hasMargins = m_targetRect != m_innerTargetRect;
-
- int floorLeft = qFloor(m_subSourceRect.left());
- int ceilRight = qCeil(m_subSourceRect.right());
- int floorTop = qFloor(m_subSourceRect.top());
- int ceilBottom = qCeil(m_subSourceRect.bottom());
- int hTiles = ceilRight - floorLeft;
- int vTiles = ceilBottom - floorTop;
-
- bool hasTiles = hTiles != 1 || vTiles != 1;
- bool fullTexture = innerSourceRect == QRectF(0, 0, 1, 1);
-
- // An image can be rendered as a single quad if:
- // - There are no margins, and either:
- // - the image isn't repeated
- // - the source rectangle fills the entire texture so that texture wrapping can be used,
- // and NPOT is supported
- if (!hasMargins && (!hasTiles || (fullTexture && supportsWrap(t->textureSize())))) {
- QRectF sr;
- if (!fullTexture) {
- sr = QRectF(innerSourceRect.x() + (m_subSourceRect.left() - floorLeft) * innerSourceRect.width(),
- innerSourceRect.y() + (m_subSourceRect.top() - floorTop) * innerSourceRect.height(),
- m_subSourceRect.width() * innerSourceRect.width(),
- m_subSourceRect.height() * innerSourceRect.height());
- } else {
- sr = QRectF(m_subSourceRect.left() - floorLeft, m_subSourceRect.top() - floorTop,
- m_subSourceRect.width(), m_subSourceRect.height());
- }
- if (m_mirror) {
- qreal oldLeft = sr.left();
- sr.setLeft(sr.right());
- sr.setRight(oldLeft);
- }
-
- if (m_antialiasing) {
- QSGGeometry *g = geometry();
- Q_ASSERT(g != &m_geometry);
- g->allocate(8, 14);
- g->setDrawingMode(GL_TRIANGLE_STRIP);
- SmoothVertex *vertices = reinterpret_cast<SmoothVertex *>(g->vertexData());
- float delta = float(qAbs(m_targetRect.width()) < qAbs(m_targetRect.height())
- ? m_targetRect.width() : m_targetRect.height()) * 0.5f;
- float sx = float(sr.width() / m_targetRect.width());
- float sy = float(sr.height() / m_targetRect.height());
- for (int d = -1; d <= 1; d += 2) {
- for (int j = 0; j < 2; ++j) {
- for (int i = 0; i < 2; ++i, ++vertices) {
- vertices->x = m_targetRect.x() + i * m_targetRect.width();
- vertices->y = m_targetRect.y() + j * m_targetRect.height();
- vertices->u = sr.x() + i * sr.width();
- vertices->v = sr.y() + j * sr.height();
- vertices->dx = (i == 0 ? delta : -delta) * d;
- vertices->dy = (j == 0 ? delta : -delta) * d;
- vertices->du = (d < 0 ? 0 : vertices->dx * sx);
- vertices->dv = (d < 0 ? 0 : vertices->dy * sy);
- }
- }
- }
- Q_ASSERT(vertices - g->vertexCount() == g->vertexData());
- static const quint16 indices[] = {
- 0, 4, 1, 5, 3, 7, 2, 6, 0, 4,
- 4, 6, 5, 7
- };
- Q_ASSERT(g->sizeOfIndex() * g->indexCount() == sizeof(indices));
- memcpy(g->indexDataAsUShort(), indices, sizeof(indices));
- } else {
- m_geometry.allocate(4);
- m_geometry.setDrawingMode(GL_TRIANGLE_STRIP);
- QSGGeometry::updateTexturedRectGeometry(&m_geometry, m_targetRect, sr);
- }
- } else {
- int hCells = hTiles;
- int vCells = vTiles;
- if (m_innerTargetRect.width() == 0)
- hCells = 0;
- if (m_innerTargetRect.left() != m_targetRect.left())
- ++hCells;
- if (m_innerTargetRect.right() != m_targetRect.right())
- ++hCells;
- if (m_innerTargetRect.height() == 0)
- vCells = 0;
- if (m_innerTargetRect.top() != m_targetRect.top())
- ++vCells;
- if (m_innerTargetRect.bottom() != m_targetRect.bottom())
- ++vCells;
- QVarLengthArray<X, 32> xData(2 * hCells);
- QVarLengthArray<Y, 32> yData(2 * vCells);
- X *xs = xData.data();
- Y *ys = yData.data();
-
- if (m_innerTargetRect.left() != m_targetRect.left()) {
- xs[0].x = m_targetRect.left();
- xs[0].tx = sourceRect.left();
- xs[1].x = m_innerTargetRect.left();
- xs[1].tx = innerSourceRect.left();
- xs += 2;
- }
- if (m_innerTargetRect.width() != 0) {
- xs[0].x = m_innerTargetRect.left();
- xs[0].tx = innerSourceRect.x() + (m_subSourceRect.left() - floorLeft) * innerSourceRect.width();
- ++xs;
- float b = m_innerTargetRect.width() / m_subSourceRect.width();
- float a = m_innerTargetRect.x() - m_subSourceRect.x() * b;
- for (int i = floorLeft + 1; i <= ceilRight - 1; ++i) {
- xs[0].x = xs[1].x = a + b * i;
- xs[0].tx = innerSourceRect.right();
- xs[1].tx = innerSourceRect.left();
- xs += 2;
- }
- xs[0].x = m_innerTargetRect.right();
- xs[0].tx = innerSourceRect.x() + (m_subSourceRect.right() - ceilRight + 1) * innerSourceRect.width();
- ++xs;
- }
- if (m_innerTargetRect.right() != m_targetRect.right()) {
- xs[0].x = m_innerTargetRect.right();
- xs[0].tx = innerSourceRect.right();
- xs[1].x = m_targetRect.right();
- xs[1].tx = sourceRect.right();
- xs += 2;
- }
- Q_ASSERT(xs == xData.data() + xData.size());
- if (m_mirror) {
- float leftPlusRight = m_targetRect.left() + m_targetRect.right();
- int count = xData.size();
- xs = xData.data();
- for (int i = 0; i < count >> 1; ++i)
- qSwap(xs[i], xs[count - 1 - i]);
- for (int i = 0; i < count; ++i)
- xs[i].x = leftPlusRight - xs[i].x;
- }
-
- if (m_innerTargetRect.top() != m_targetRect.top()) {
- ys[0].y = m_targetRect.top();
- ys[0].ty = sourceRect.top();
- ys[1].y = m_innerTargetRect.top();
- ys[1].ty = innerSourceRect.top();
- ys += 2;
- }
- if (m_innerTargetRect.height() != 0) {
- ys[0].y = m_innerTargetRect.top();
- ys[0].ty = innerSourceRect.y() + (m_subSourceRect.top() - floorTop) * innerSourceRect.height();
- ++ys;
- float b = m_innerTargetRect.height() / m_subSourceRect.height();
- float a = m_innerTargetRect.y() - m_subSourceRect.y() * b;
- for (int i = floorTop + 1; i <= ceilBottom - 1; ++i) {
- ys[0].y = ys[1].y = a + b * i;
- ys[0].ty = innerSourceRect.bottom();
- ys[1].ty = innerSourceRect.top();
- ys += 2;
- }
- ys[0].y = m_innerTargetRect.bottom();
- ys[0].ty = innerSourceRect.y() + (m_subSourceRect.bottom() - ceilBottom + 1) * innerSourceRect.height();
- ++ys;
- }
- if (m_innerTargetRect.bottom() != m_targetRect.bottom()) {
- ys[0].y = m_innerTargetRect.bottom();
- ys[0].ty = innerSourceRect.bottom();
- ys[1].y = m_targetRect.bottom();
- ys[1].ty = sourceRect.bottom();
- ys += 2;
- }
- Q_ASSERT(ys == yData.data() + yData.size());
-
- if (m_antialiasing) {
- QSGGeometry *g = geometry();
- Q_ASSERT(g != &m_geometry);
-
- g->allocate(hCells * vCells * 4 + (hCells + vCells - 1) * 4,
- hCells * vCells * 6 + (hCells + vCells) * 12);
- g->setDrawingMode(GL_TRIANGLES);
- SmoothVertex *vertices = reinterpret_cast<SmoothVertex *>(g->vertexData());
- memset(vertices, 0, g->vertexCount() * g->sizeOfVertex());
- quint16 *indices = g->indexDataAsUShort();
-
- // The deltas are how much the fuzziness can reach into the image.
- // Only the border vertices are moved by the vertex shader, so the fuzziness
- // can't reach further into the image than the closest interior vertices.
- float leftDx = xData.at(1).x - xData.at(0).x;
- float rightDx = xData.at(xData.size() - 1).x - xData.at(xData.size() - 2).x;
- float topDy = yData.at(1).y - yData.at(0).y;
- float bottomDy = yData.at(yData.size() - 1).y - yData.at(yData.size() - 2).y;
-
- float leftDu = xData.at(1).tx - xData.at(0).tx;
- float rightDu = xData.at(xData.size() - 1).tx - xData.at(xData.size() - 2).tx;
- float topDv = yData.at(1).ty - yData.at(0).ty;
- float bottomDv = yData.at(yData.size() - 1).ty - yData.at(yData.size() - 2).ty;
-
- if (hCells == 1) {
- leftDx = rightDx *= 0.5f;
- leftDu = rightDu *= 0.5f;
- }
- if (vCells == 1) {
- topDy = bottomDy *= 0.5f;
- topDv = bottomDv *= 0.5f;
- }
-
- // This delta is how much the fuzziness can reach out from the image.
- float delta = float(qAbs(m_targetRect.width()) < qAbs(m_targetRect.height())
- ? m_targetRect.width() : m_targetRect.height()) * 0.5f;
-
- quint16 index = 0;
- ys = yData.data();
- for (int j = 0; j < vCells; ++j, ys += 2) {
- xs = xData.data();
- bool isTop = j == 0;
- bool isBottom = j == vCells - 1;
- for (int i = 0; i < hCells; ++i, xs += 2) {
- bool isLeft = i == 0;
- bool isRight = i == hCells - 1;
-
- SmoothVertex *v = vertices + index;
-
- quint16 topLeft = index;
- for (int k = (isTop || isLeft ? 2 : 1); k--; ++v, ++index) {
- v->x = xs[0].x;
- v->u = xs[0].tx;
- v->y = ys[0].y;
- v->v = ys[0].ty;
- }
-
- quint16 topRight = index;
- for (int k = (isTop || isRight ? 2 : 1); k--; ++v, ++index) {
- v->x = xs[1].x;
- v->u = xs[1].tx;
- v->y = ys[0].y;
- v->v = ys[0].ty;
- }
-
- quint16 bottomLeft = index;
- for (int k = (isBottom || isLeft ? 2 : 1); k--; ++v, ++index) {
- v->x = xs[0].x;
- v->u = xs[0].tx;
- v->y = ys[1].y;
- v->v = ys[1].ty;
- }
-
- quint16 bottomRight = index;
- for (int k = (isBottom || isRight ? 2 : 1); k--; ++v, ++index) {
- v->x = xs[1].x;
- v->u = xs[1].tx;
- v->y = ys[1].y;
- v->v = ys[1].ty;
- }
-
- appendQuad(&indices, topLeft, topRight, bottomLeft, bottomRight);
-
- if (isTop) {
- vertices[topLeft].dy = vertices[topRight].dy = topDy;
- vertices[topLeft].dv = vertices[topRight].dv = topDv;
- vertices[topLeft + 1].dy = vertices[topRight + 1].dy = -delta;
- appendQuad(&indices, topLeft + 1, topRight + 1, topLeft, topRight);
- }
-
- if (isBottom) {
- vertices[bottomLeft].dy = vertices[bottomRight].dy = -bottomDy;
- vertices[bottomLeft].dv = vertices[bottomRight].dv = -bottomDv;
- vertices[bottomLeft + 1].dy = vertices[bottomRight + 1].dy = delta;
- appendQuad(&indices, bottomLeft, bottomRight, bottomLeft + 1, bottomRight + 1);
- }
-
- if (isLeft) {
- vertices[topLeft].dx = vertices[bottomLeft].dx = leftDx;
- vertices[topLeft].du = vertices[bottomLeft].du = leftDu;
- vertices[topLeft + 1].dx = vertices[bottomLeft + 1].dx = -delta;
- appendQuad(&indices, topLeft + 1, topLeft, bottomLeft + 1, bottomLeft);
- }
-
- if (isRight) {
- vertices[topRight].dx = vertices[bottomRight].dx = -rightDx;
- vertices[topRight].du = vertices[bottomRight].du = -rightDu;
- vertices[topRight + 1].dx = vertices[bottomRight + 1].dx = delta;
- appendQuad(&indices, topRight, topRight + 1, bottomRight, bottomRight + 1);
- }
- }
- }
-
- Q_ASSERT(index == g->vertexCount());
- Q_ASSERT(indices - g->indexCount() == g->indexData());
- } else {
- m_geometry.allocate(hCells * vCells * 4, hCells * vCells * 6);
- m_geometry.setDrawingMode(GL_TRIANGLES);
- QSGGeometry::TexturedPoint2D *vertices = m_geometry.vertexDataAsTexturedPoint2D();
- ys = yData.data();
- for (int j = 0; j < vCells; ++j, ys += 2) {
- xs = xData.data();
- for (int i = 0; i < hCells; ++i, xs += 2) {
- vertices[0].x = vertices[2].x = xs[0].x;
- vertices[0].tx = vertices[2].tx = xs[0].tx;
- vertices[1].x = vertices[3].x = xs[1].x;
- vertices[1].tx = vertices[3].tx = xs[1].tx;
-
- vertices[0].y = vertices[1].y = ys[0].y;
- vertices[0].ty = vertices[1].ty = ys[0].ty;
- vertices[2].y = vertices[3].y = ys[1].y;
- vertices[2].ty = vertices[3].ty = ys[1].ty;
-
- vertices += 4;
- }
- }
-
- quint16 *indices = m_geometry.indexDataAsUShort();
- for (int i = 0; i < 4 * vCells * hCells; i += 4)
- appendQuad(&indices, i, i + 1, i + 2, i + 3);
- }
- }
- }
- markDirty(DirtyGeometry);
- m_dirtyGeometry = false;
-}
-
QSGDefaultImageNode::QSGDefaultImageNode()
{
setMaterial(&m_materialO);
@@ -683,6 +200,12 @@ bool QSGDefaultImageNode::updateMaterialBlending()
return false;
}
+inline static bool isPowerOfTwo(int x)
+{
+ // Assumption: x >= 1
+ return x == (x & -x);
+}
+
bool QSGDefaultImageNode::supportsWrap(const QSize &size) const
{
bool wrapSupported = true;
diff --git a/src/quick/scenegraph/qsgdefaultimagenode_p.h b/src/quick/scenegraph/qsgdefaultimagenode_p.h
index 85de09b6eb..688c5a5039 100644
--- a/src/quick/scenegraph/qsgdefaultimagenode_p.h
+++ b/src/quick/scenegraph/qsgdefaultimagenode_p.h
@@ -53,6 +53,7 @@
//
#include <private/qsgadaptationlayer_p.h>
+#include <private/qsgbasicimagenode_p.h>
#include <QtQuick/qsgtexturematerial.h>
QT_BEGIN_NAMESPACE
@@ -69,42 +70,7 @@ protected:
QSGMaterialShader *createShader() const override;
};
-class Q_QUICK_PRIVATE_EXPORT QSGDefaultNoMaterialImageNode : public QSGImageNode
-{
-public:
- QSGDefaultNoMaterialImageNode();
- void setTargetRect(const QRectF &rect) override;
- void setInnerTargetRect(const QRectF &rect) override;
- void setInnerSourceRect(const QRectF &rect) override;
- void setSubSourceRect(const QRectF &rect) override;
- void setTexture(QSGTexture *texture) override;
- void setAntialiasing(bool antialiasing) override;
- void setMirror(bool mirror) override;
- void update() override;
- void preprocess() override;
-
-protected:
- virtual void updateMaterialAntialiasing() = 0;
- virtual void setMaterialTexture(QSGTexture *texture) = 0;
- virtual QSGTexture *materialTexture() const = 0;
- virtual bool updateMaterialBlending() = 0;
- virtual bool supportsWrap(const QSize &size) const = 0;
-
- void updateGeometry();
-
- QRectF m_targetRect;
- QRectF m_innerTargetRect;
- QRectF m_innerSourceRect;
- QRectF m_subSourceRect;
-
- uint m_antialiasing : 1;
- uint m_mirror : 1;
- uint m_dirtyGeometry : 1;
-
- QSGGeometry m_geometry;
-};
-
-class Q_QUICK_PRIVATE_EXPORT QSGDefaultImageNode : public QSGDefaultNoMaterialImageNode
+class Q_QUICK_PRIVATE_EXPORT QSGDefaultImageNode : public QSGBasicImageNode
{
public:
QSGDefaultImageNode();
diff --git a/src/quick/scenegraph/qsgdefaultrectanglenode.cpp b/src/quick/scenegraph/qsgdefaultrectanglenode.cpp
index e4a6648bbd..da1623e197 100644
--- a/src/quick/scenegraph/qsgdefaultrectanglenode.cpp
+++ b/src/quick/scenegraph/qsgdefaultrectanglenode.cpp
@@ -38,8 +38,6 @@
**
****************************************************************************/
-
-
#include "qsgdefaultrectanglenode_p.h"
#include <QtQuick/qsgvertexcolormaterial.h>
@@ -52,59 +50,6 @@
QT_BEGIN_NAMESPACE
-namespace
-{
- struct Color4ub
- {
- unsigned char r, g, b, a;
- };
-
- Color4ub operator *(Color4ub c, float t) { c.a *= t; c.r *= t; c.g *= t; c.b *= t; return c; }
- Color4ub operator +(Color4ub a, Color4ub b) { a.a += b.a; a.r += b.r; a.g += b.g; a.b += b.b; return a; }
-
- inline Color4ub colorToColor4ub(const QColor &c)
- {
- Color4ub color = { uchar(qRound(c.redF() * c.alphaF() * 255)),
- uchar(qRound(c.greenF() * c.alphaF() * 255)),
- uchar(qRound(c.blueF() * c.alphaF() * 255)),
- uchar(qRound(c.alphaF() * 255))
- };
- return color;
- }
-
- // Same layout as QSGGeometry::ColoredPoint2D, but uses Color4ub for convenience.
- struct Vertex
- {
- float x, y;
- Color4ub color;
- void set(float nx, float ny, Color4ub ncolor)
- {
- x = nx; y = ny; color = ncolor;
- }
- };
-
- struct SmoothVertex : public Vertex
- {
- float dx, dy;
- void set(float nx, float ny, Color4ub ncolor, float ndx, float ndy)
- {
- Vertex::set(nx, ny, ncolor);
- dx = ndx; dy = ndy;
- }
- };
-
- const QSGGeometry::AttributeSet &smoothAttributeSet()
- {
- static QSGGeometry::Attribute data[] = {
- QSGGeometry::Attribute::createWithSemantic(0, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::POSITION),
- QSGGeometry::Attribute::createWithSemantic(1, 4, QSGGeometry::TypeUnsignedByte, QSGGeometry::Attribute::COLOR),
- QSGGeometry::Attribute::createWithSemantic(2, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::TEXCOORD)
- };
- static QSGGeometry::AttributeSet attrs = { 3, sizeof(SmoothVertex), data };
- return attrs;
- }
-}
-
class SmoothColorMaterialShader : public QSGMaterialShader
{
public:
@@ -183,595 +128,6 @@ QSGMaterialShader *QSGSmoothColorMaterial::createShader() const
return new SmoothColorMaterialShader;
}
-
-QSGDefaultNoMaterialRectangleNode::QSGDefaultNoMaterialRectangleNode()
- : m_radius(0)
- , m_pen_width(0)
- , m_aligned(true)
- , m_antialiasing(false)
- , m_gradient_is_opaque(true)
- , m_dirty_geometry(false)
- , m_geometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0)
-{
- setGeometry(&m_geometry);
-
-#ifdef QSG_RUNTIME_DESCRIPTION
- qsgnode_set_description(this, QLatin1String("rectangle"));
-#endif
-}
-
-void QSGDefaultNoMaterialRectangleNode::setRect(const QRectF &rect)
-{
- if (rect == m_rect)
- return;
- m_rect = rect;
- m_dirty_geometry = true;
-}
-
-void QSGDefaultNoMaterialRectangleNode::setColor(const QColor &color)
-{
- if (color == m_color)
- return;
- m_color = color;
- if (m_gradient_stops.isEmpty())
- m_dirty_geometry = true;
-}
-
-void QSGDefaultNoMaterialRectangleNode::setPenColor(const QColor &color)
-{
- if (color == m_border_color)
- return;
- m_border_color = color;
- if (m_pen_width > 0)
- m_dirty_geometry = true;
-}
-
-void QSGDefaultNoMaterialRectangleNode::setPenWidth(qreal width)
-{
- if (width == m_pen_width)
- return;
- m_pen_width = width;
- m_dirty_geometry = true;
-}
-
-
-void QSGDefaultNoMaterialRectangleNode::setGradientStops(const QGradientStops &stops)
-{
- if (stops.constData() == m_gradient_stops.constData())
- return;
-
- m_gradient_stops = stops;
-
- m_gradient_is_opaque = true;
- for (int i = 0; i < stops.size(); ++i)
- m_gradient_is_opaque &= stops.at(i).second.alpha() == 0xff;
- m_dirty_geometry = true;
-}
-
-void QSGDefaultNoMaterialRectangleNode::setRadius(qreal radius)
-{
- if (radius == m_radius)
- return;
- m_radius = radius;
- m_dirty_geometry = true;
-}
-
-void QSGDefaultNoMaterialRectangleNode::setAntialiasing(bool antialiasing)
-{
- if (!supportsAntialiasing())
- return;
-
- if (antialiasing == m_antialiasing)
- return;
- m_antialiasing = antialiasing;
- if (m_antialiasing) {
- setGeometry(new QSGGeometry(smoothAttributeSet(), 0));
- setFlag(OwnsGeometry, true);
- } else {
- setGeometry(&m_geometry);
- setFlag(OwnsGeometry, false);
- }
- updateMaterialAntialiasing();
- m_dirty_geometry = true;
-}
-
-void QSGDefaultNoMaterialRectangleNode::setAligned(bool aligned)
-{
- if (aligned == m_aligned)
- return;
- m_aligned = aligned;
- m_dirty_geometry = true;
-}
-
-void QSGDefaultNoMaterialRectangleNode::update()
-{
- if (m_dirty_geometry) {
- updateGeometry();
- m_dirty_geometry = false;
-
- QSGNode::DirtyState state = QSGNode::DirtyGeometry;
- updateMaterialBlending(&state);
- markDirty(state);
- }
-}
-
-void QSGDefaultNoMaterialRectangleNode::updateGeometry()
-{
- float width = float(m_rect.width());
- float height = float(m_rect.height());
- float penWidth = qMin(qMin(width, height) * 0.5f, float(m_pen_width));
-
- if (m_aligned)
- penWidth = qRound(penWidth);
-
- QSGGeometry *g = geometry();
- g->setDrawingMode(QSGGeometry::DrawTriangleStrip);
- int vertexStride = g->sizeOfVertex();
-
- union {
- Vertex *vertices;
- SmoothVertex *smoothVertices;
- };
-
- Color4ub fillColor = colorToColor4ub(m_color);
- Color4ub borderColor = colorToColor4ub(m_border_color);
- Color4ub transparent = { 0, 0, 0, 0 };
- const QGradientStops &stops = m_gradient_stops;
-
- int nextGradientStop = 0;
- float gradientPos = penWidth / height;
- while (nextGradientStop < stops.size() && stops.at(nextGradientStop).first <= gradientPos)
- ++nextGradientStop;
- int lastGradientStop = stops.size() - 1;
- float lastGradientPos = 1.0f - penWidth / height;
- while (lastGradientStop >= nextGradientStop && stops.at(lastGradientStop).first >= lastGradientPos)
- --lastGradientStop;
- int gradientIntersections = (lastGradientStop - nextGradientStop + 1);
-
- if (m_radius > 0) {
- // Rounded corners.
-
- // Radius should never exceeds half of the width or half of the height
- float radius = qMin(qMin(width, height) * 0.5f, float(m_radius));
- QRectF innerRect = m_rect;
- innerRect.adjust(radius, radius, -radius, -radius);
-
- float innerRadius = radius - penWidth * 1.0f;
- float outerRadius = radius;
- float delta = qMin(width, height) * 0.5f;
-
- // Number of segments per corner, approximately one per 3 pixels.
- int segments = qBound(3, qCeil(outerRadius * (M_PI / 6)), 18);
-
- /*
-
- --+--__
- --+--__--__
- | --__--__
- | seg --__--+
- --+-__ ment _+ \
- --+-__--__ - \ \
- --__--+ se \ \
- + \ g \ \
- \ \ m \ \
- -----------+--+ e \ \ <- gradient line
- \ \ nt\ \
- fill +--+----+--+
- | | | |
- border
- inner AA outer AA (AA = antialiasing)
-
- */
-
- int innerVertexCount = (segments + 1) * 4 + gradientIntersections * 2;
- int outerVertexCount = (segments + 1) * 4;
- int vertexCount = innerVertexCount;
- if (m_antialiasing || penWidth)
- vertexCount += innerVertexCount;
- if (penWidth)
- vertexCount += outerVertexCount;
- if (m_antialiasing && penWidth)
- vertexCount += outerVertexCount;
-
- int fillIndexCount = innerVertexCount;
- int innerAAIndexCount = innerVertexCount * 2 + 2;
- int borderIndexCount = innerVertexCount * 2 + 2;
- int outerAAIndexCount = outerVertexCount * 2 + 2;
- int indexCount = 0;
- int fillHead = 0;
- int innerAAHead = 0;
- int innerAATail = 0;
- int borderHead = 0;
- int borderTail = 0;
- int outerAAHead = 0;
- int outerAATail = 0;
- bool hasFill = m_color.alpha() > 0 || !stops.isEmpty();
- if (hasFill)
- indexCount += fillIndexCount;
- if (m_antialiasing) {
- innerAATail = innerAAHead = indexCount + (innerAAIndexCount >> 1) + 1;
- indexCount += innerAAIndexCount;
- }
- if (penWidth) {
- borderTail = borderHead = indexCount + (borderIndexCount >> 1) + 1;
- indexCount += borderIndexCount;
- }
- if (m_antialiasing && penWidth) {
- outerAATail = outerAAHead = indexCount + (outerAAIndexCount >> 1) + 1;
- indexCount += outerAAIndexCount;
- }
-
- g->allocate(vertexCount, indexCount);
- vertices = reinterpret_cast<Vertex *>(g->vertexData());
- memset(vertices, 0, vertexCount * vertexStride);
- quint16 *indices = g->indexDataAsUShort();
- quint16 index = 0;
-
- float py = 0; // previous inner y-coordinate.
- float plx = 0; // previous inner left x-coordinate.
- float prx = 0; // previous inner right x-coordinate.
-
- float angle = 0.5f * float(M_PI) / segments;
- float cosStep = qFastCos(angle);
- float sinStep = qFastSin(angle);
-
- for (int part = 0; part < 2; ++part) {
- float c = 1 - part;
- float s = part;
- for (int i = 0; i <= segments; ++i) {
- float y, lx, rx;
- if (innerRadius > 0) {
- y = (part ? innerRect.bottom() : innerRect.top()) - innerRadius * c; // current inner y-coordinate.
- lx = innerRect.left() - innerRadius * s; // current inner left x-coordinate.
- rx = innerRect.right() + innerRadius * s; // current inner right x-coordinate.
- gradientPos = ((part ? innerRect.height() : 0) + radius - innerRadius * c) / height;
- } else {
- y = (part ? innerRect.bottom() + innerRadius : innerRect.top() - innerRadius); // current inner y-coordinate.
- lx = innerRect.left() - innerRadius; // current inner left x-coordinate.
- rx = innerRect.right() + innerRadius; // current inner right x-coordinate.
- gradientPos = ((part ? innerRect.height() + innerRadius : -innerRadius) + radius) / height;
- }
- float Y = (part ? innerRect.bottom() : innerRect.top()) - outerRadius * c; // current outer y-coordinate.
- float lX = innerRect.left() - outerRadius * s; // current outer left x-coordinate.
- float rX = innerRect.right() + outerRadius * s; // current outer right x-coordinate.
-
- while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) {
- // Insert vertices at gradient stops.
- float gy = (innerRect.top() - radius) + stops.at(nextGradientStop).first * height;
- float t = (gy - py) / (y - py);
- float glx = plx * (1 - t) + t * lx;
- float grx = prx * (1 - t) + t * rx;
-
- fillColor = colorToColor4ub(stops.at(nextGradientStop).second);
-
- if (hasFill) {
- indices[fillHead++] = index;
- indices[fillHead++] = index + 1;
- }
-
- if (penWidth) {
- --borderHead;
- indices[borderHead] = indices[borderHead + 2];
- indices[--borderHead] = index + 2;
- indices[borderTail++] = index + 3;
- indices[borderTail] = indices[borderTail - 2];
- ++borderTail;
- }
-
- if (m_antialiasing) {
- indices[--innerAAHead] = index + 2;
- indices[--innerAAHead] = index;
- indices[innerAATail++] = index + 1;
- indices[innerAATail++] = index + 3;
-
- bool lower = stops.at(nextGradientStop).first > 0.5f;
- float dy = lower ? qMin(0.0f, height - gy - delta) : qMax(0.0f, delta - gy);
- smoothVertices[index++].set(grx, gy, fillColor, width - grx - delta, dy);
- smoothVertices[index++].set(glx, gy, fillColor, delta - glx, dy);
- if (penWidth) {
- smoothVertices[index++].set(grx, gy, borderColor, 0.49f * penWidth * s, -0.49f * penWidth * c);
- smoothVertices[index++].set(glx, gy, borderColor, -0.49f * penWidth * s, -0.49f * penWidth * c);
- } else {
- dy = lower ? delta : -delta;
- smoothVertices[index++].set(grx, gy, transparent, delta, dy);
- smoothVertices[index++].set(glx, gy, transparent, -delta, dy);
- }
- } else {
- vertices[index++].set(grx, gy, fillColor);
- vertices[index++].set(glx, gy, fillColor);
- if (penWidth) {
- vertices[index++].set(grx, gy, borderColor);
- vertices[index++].set(glx, gy, borderColor);
- }
- }
- ++nextGradientStop;
- }
-
- if (!stops.isEmpty()) {
- if (nextGradientStop == 0) {
- fillColor = colorToColor4ub(stops.at(0).second);
- } else if (nextGradientStop == stops.size()) {
- fillColor = colorToColor4ub(stops.last().second);
- } else {
- const QGradientStop &prev = stops.at(nextGradientStop - 1);
- const QGradientStop &next = stops.at(nextGradientStop);
- float t = (gradientPos - prev.first) / (next.first - prev.first);
- fillColor = colorToColor4ub(prev.second) * (1 - t) + colorToColor4ub(next.second) * t;
- }
- }
-
- if (hasFill) {
- indices[fillHead++] = index;
- indices[fillHead++] = index + 1;
- }
-
- if (penWidth) {
- indices[--borderHead] = index + 4;
- indices[--borderHead] = index + 2;
- indices[borderTail++] = index + 3;
- indices[borderTail++] = index + 5;
- }
-
- if (m_antialiasing) {
- indices[--innerAAHead] = index + 2;
- indices[--innerAAHead] = index;
- indices[innerAATail++] = index + 1;
- indices[innerAATail++] = index + 3;
-
- float dy = part ? qMin(0.0f, height - y - delta) : qMax(0.0f, delta - y);
- smoothVertices[index++].set(rx, y, fillColor, width - rx - delta, dy);
- smoothVertices[index++].set(lx, y, fillColor, delta - lx, dy);
-
- dy = part ? delta : -delta;
- if (penWidth) {
- smoothVertices[index++].set(rx, y, borderColor, 0.49f * penWidth * s, -0.49f * penWidth * c);
- smoothVertices[index++].set(lx, y, borderColor, -0.49f * penWidth * s, -0.49f * penWidth * c);
- smoothVertices[index++].set(rX, Y, borderColor, -0.49f * penWidth * s, 0.49f * penWidth * c);
- smoothVertices[index++].set(lX, Y, borderColor, 0.49f * penWidth * s, 0.49f * penWidth * c);
- smoothVertices[index++].set(rX, Y, transparent, delta, dy);
- smoothVertices[index++].set(lX, Y, transparent, -delta, dy);
-
- indices[--outerAAHead] = index - 2;
- indices[--outerAAHead] = index - 4;
- indices[outerAATail++] = index - 3;
- indices[outerAATail++] = index - 1;
- } else {
- smoothVertices[index++].set(rx, y, transparent, delta, dy);
- smoothVertices[index++].set(lx, y, transparent, -delta, dy);
- }
- } else {
- vertices[index++].set(rx, y, fillColor);
- vertices[index++].set(lx, y, fillColor);
- if (penWidth) {
- vertices[index++].set(rx, y, borderColor);
- vertices[index++].set(lx, y, borderColor);
- vertices[index++].set(rX, Y, borderColor);
- vertices[index++].set(lX, Y, borderColor);
- }
- }
-
- py = y;
- plx = lx;
- prx = rx;
-
- // Rotate
- qreal tmp = c;
- c = c * cosStep - s * sinStep;
- s = s * cosStep + tmp * sinStep;
- }
- }
- Q_ASSERT(index == vertexCount);
-
- // Close the triangle strips.
- if (m_antialiasing) {
- indices[--innerAAHead] = indices[innerAATail - 1];
- indices[--innerAAHead] = indices[innerAATail - 2];
- Q_ASSERT(innerAATail <= indexCount);
- }
- if (penWidth) {
- indices[--borderHead] = indices[borderTail - 1];
- indices[--borderHead] = indices[borderTail - 2];
- Q_ASSERT(borderTail <= indexCount);
- }
- if (m_antialiasing && penWidth) {
- indices[--outerAAHead] = indices[outerAATail - 1];
- indices[--outerAAHead] = indices[outerAATail - 2];
- Q_ASSERT(outerAATail == indexCount);
- }
- } else {
- // Straight corners.
- QRectF innerRect = m_rect;
- QRectF outerRect = m_rect;
-
- if (penWidth)
- innerRect.adjust(1.0f * penWidth, 1.0f * penWidth, -1.0f * penWidth, -1.0f * penWidth);
-
- float delta = qMin(width, height) * 0.5f;
- int innerVertexCount = 4 + gradientIntersections * 2;
- int outerVertexCount = 4;
- int vertexCount = innerVertexCount;
- if (m_antialiasing || penWidth)
- vertexCount += innerVertexCount;
- if (penWidth)
- vertexCount += outerVertexCount;
- if (m_antialiasing && penWidth)
- vertexCount += outerVertexCount;
-
- int fillIndexCount = innerVertexCount;
- int innerAAIndexCount = innerVertexCount * 2 + 2;
- int borderIndexCount = innerVertexCount * 2 + 2;
- int outerAAIndexCount = outerVertexCount * 2 + 2;
- int indexCount = 0;
- int fillHead = 0;
- int innerAAHead = 0;
- int innerAATail = 0;
- int borderHead = 0;
- int borderTail = 0;
- int outerAAHead = 0;
- int outerAATail = 0;
- bool hasFill = m_color.alpha() > 0 || !stops.isEmpty();
- if (hasFill)
- indexCount += fillIndexCount;
- if (m_antialiasing) {
- innerAATail = innerAAHead = indexCount + (innerAAIndexCount >> 1) + 1;
- indexCount += innerAAIndexCount;
- }
- if (penWidth) {
- borderTail = borderHead = indexCount + (borderIndexCount >> 1) + 1;
- indexCount += borderIndexCount;
- }
- if (m_antialiasing && penWidth) {
- outerAATail = outerAAHead = indexCount + (outerAAIndexCount >> 1) + 1;
- indexCount += outerAAIndexCount;
- }
-
- g->allocate(vertexCount, indexCount);
- vertices = reinterpret_cast<Vertex *>(g->vertexData());
- memset(vertices, 0, vertexCount * vertexStride);
- quint16 *indices = g->indexDataAsUShort();
- quint16 index = 0;
-
- float lx = innerRect.left();
- float rx = innerRect.right();
- float lX = outerRect.left();
- float rX = outerRect.right();
-
- for (int part = -1; part <= 1; part += 2) {
- float y = (part == 1 ? innerRect.bottom() : innerRect.top());
- float Y = (part == 1 ? outerRect.bottom() : outerRect.top());
- gradientPos = (y - innerRect.top() + penWidth) / height;
-
- while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) {
- // Insert vertices at gradient stops.
- float gy = (innerRect.top() - penWidth) + stops.at(nextGradientStop).first * height;
-
- fillColor = colorToColor4ub(stops.at(nextGradientStop).second);
-
- if (hasFill) {
- indices[fillHead++] = index;
- indices[fillHead++] = index + 1;
- }
-
- if (penWidth) {
- --borderHead;
- indices[borderHead] = indices[borderHead + 2];
- indices[--borderHead] = index + 2;
- indices[borderTail++] = index + 3;
- indices[borderTail] = indices[borderTail - 2];
- ++borderTail;
- }
-
- if (m_antialiasing) {
- indices[--innerAAHead] = index + 2;
- indices[--innerAAHead] = index;
- indices[innerAATail++] = index + 1;
- indices[innerAATail++] = index + 3;
-
- bool lower = stops.at(nextGradientStop).first > 0.5f;
- float dy = lower ? qMin(0.0f, height - gy - delta) : qMax(0.0f, delta - gy);
- smoothVertices[index++].set(rx, gy, fillColor, width - rx - delta, dy);
- smoothVertices[index++].set(lx, gy, fillColor, delta - lx, dy);
- if (penWidth) {
- smoothVertices[index++].set(rx, gy, borderColor, 0.49f * penWidth, (lower ? 0.49f : -0.49f) * penWidth);
- smoothVertices[index++].set(lx, gy, borderColor, -0.49f * penWidth, (lower ? 0.49f : -0.49f) * penWidth);
- } else {
- smoothVertices[index++].set(rx, gy, transparent, delta, lower ? delta : -delta);
- smoothVertices[index++].set(lx, gy, transparent, -delta, lower ? delta : -delta);
- }
- } else {
- vertices[index++].set(rx, gy, fillColor);
- vertices[index++].set(lx, gy, fillColor);
- if (penWidth) {
- vertices[index++].set(rx, gy, borderColor);
- vertices[index++].set(lx, gy, borderColor);
- }
- }
- ++nextGradientStop;
- }
-
- if (!stops.isEmpty()) {
- if (nextGradientStop == 0) {
- fillColor = colorToColor4ub(stops.at(0).second);
- } else if (nextGradientStop == stops.size()) {
- fillColor = colorToColor4ub(stops.last().second);
- } else {
- const QGradientStop &prev = stops.at(nextGradientStop - 1);
- const QGradientStop &next = stops.at(nextGradientStop);
- float t = (gradientPos - prev.first) / (next.first - prev.first);
- fillColor = colorToColor4ub(prev.second) * (1 - t) + colorToColor4ub(next.second) * t;
- }
- }
-
- if (hasFill) {
- indices[fillHead++] = index;
- indices[fillHead++] = index + 1;
- }
-
- if (penWidth) {
- indices[--borderHead] = index + 4;
- indices[--borderHead] = index + 2;
- indices[borderTail++] = index + 3;
- indices[borderTail++] = index + 5;
- }
-
- if (m_antialiasing) {
- indices[--innerAAHead] = index + 2;
- indices[--innerAAHead] = index;
- indices[innerAATail++] = index + 1;
- indices[innerAATail++] = index + 3;
-
- float dy = part == 1 ? qMin(0.0f, height - y - delta) : qMax(0.0f, delta - y);
- smoothVertices[index++].set(rx, y, fillColor, width - rx - delta, dy);
- smoothVertices[index++].set(lx, y, fillColor, delta - lx, dy);
-
- if (penWidth) {
- smoothVertices[index++].set(rx, y, borderColor, 0.49f * penWidth, 0.49f * penWidth * part);
- smoothVertices[index++].set(lx, y, borderColor, -0.49f * penWidth, 0.49f * penWidth * part);
- smoothVertices[index++].set(rX, Y, borderColor, -0.49f * penWidth, -0.49f * penWidth * part);
- smoothVertices[index++].set(lX, Y, borderColor, 0.49f * penWidth, -0.49f * penWidth * part);
- smoothVertices[index++].set(rX, Y, transparent, delta, delta * part);
- smoothVertices[index++].set(lX, Y, transparent, -delta, delta * part);
-
- indices[--outerAAHead] = index - 2;
- indices[--outerAAHead] = index - 4;
- indices[outerAATail++] = index - 3;
- indices[outerAATail++] = index - 1;
- } else {
- smoothVertices[index++].set(rx, y, transparent, delta, delta * part);
- smoothVertices[index++].set(lx, y, transparent, -delta, delta * part);
- }
- } else {
- vertices[index++].set(rx, y, fillColor);
- vertices[index++].set(lx, y, fillColor);
- if (penWidth) {
- vertices[index++].set(rx, y, borderColor);
- vertices[index++].set(lx, y, borderColor);
- vertices[index++].set(rX, Y, borderColor);
- vertices[index++].set(lX, Y, borderColor);
- }
- }
- }
- Q_ASSERT(index == vertexCount);
-
- // Close the triangle strips.
- if (m_antialiasing) {
- indices[--innerAAHead] = indices[innerAATail - 1];
- indices[--innerAAHead] = indices[innerAATail - 2];
- Q_ASSERT(innerAATail <= indexCount);
- }
- if (penWidth) {
- indices[--borderHead] = indices[borderTail - 1];
- indices[--borderHead] = indices[borderTail - 2];
- Q_ASSERT(borderTail <= indexCount);
- }
- if (m_antialiasing && penWidth) {
- indices[--outerAAHead] = indices[outerAATail - 1];
- indices[--outerAAHead] = indices[outerAATail - 2];
- Q_ASSERT(outerAATail == indexCount);
- }
- }
-}
-
QSGDefaultRectangleNode::QSGDefaultRectangleNode()
{
setMaterial(&m_material);
diff --git a/src/quick/scenegraph/qsgdefaultrectanglenode_p.h b/src/quick/scenegraph/qsgdefaultrectanglenode_p.h
index f5f3c465b7..f30a3beed7 100644
--- a/src/quick/scenegraph/qsgdefaultrectanglenode_p.h
+++ b/src/quick/scenegraph/qsgdefaultrectanglenode_p.h
@@ -53,7 +53,7 @@
//
#include <private/qsgadaptationlayer_p.h>
-
+#include <private/qsgbasicrectanglenode_p.h>
#include <QtQuick/qsgvertexcolormaterial.h>
QT_BEGIN_NAMESPACE
@@ -72,45 +72,7 @@ protected:
QSGMaterialShader *createShader() const override;
};
-class Q_QUICK_PRIVATE_EXPORT QSGDefaultNoMaterialRectangleNode : public QSGRectangleNode
-{
-public:
- QSGDefaultNoMaterialRectangleNode();
-
- void setRect(const QRectF &rect) override;
- void setColor(const QColor &color) override;
- void setPenColor(const QColor &color) override;
- void setPenWidth(qreal width) override;
- void setGradientStops(const QGradientStops &stops) override;
- void setRadius(qreal radius) override;
- void setAntialiasing(bool antialiasing) override;
- void setAligned(bool aligned) override;
- void update() override;
-
-protected:
- virtual bool supportsAntialiasing() const { return true; }
- virtual void updateMaterialAntialiasing() = 0;
- virtual void updateMaterialBlending(QSGNode::DirtyState *state) = 0;
-
- void updateGeometry();
- void updateGradientTexture();
-
- QRectF m_rect;
- QGradientStops m_gradient_stops;
- QColor m_color;
- QColor m_border_color;
- qreal m_radius;
- qreal m_pen_width;
-
- uint m_aligned : 1;
- uint m_antialiasing : 1;
- uint m_gradient_is_opaque : 1;
- uint m_dirty_geometry : 1;
-
- QSGGeometry m_geometry;
-};
-
-class Q_QUICK_PRIVATE_EXPORT QSGDefaultRectangleNode : public QSGDefaultNoMaterialRectangleNode
+class Q_QUICK_PRIVATE_EXPORT QSGDefaultRectangleNode : public QSGBasicRectangleNode
{
public:
QSGDefaultRectangleNode();
diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri
index 33f6dff8cf..b4d8b5d231 100644
--- a/src/quick/scenegraph/scenegraph.pri
+++ b/src/quick/scenegraph/scenegraph.pri
@@ -79,6 +79,9 @@ HEADERS += \
$$PWD/qsgdefaultglyphnode_p_p.h \
$$PWD/qsgdefaultimagenode_p.h \
$$PWD/qsgdefaultrectanglenode_p.h \
+ $$PWD/qsgbasicrectanglenode_p.h \
+ $$PWD/qsgbasicimagenode_p.h \
+ $$PWD/qsgbasicglyphnode_p.h \
$$PWD/qsgrenderloop_p.h \
$$PWD/qsgthreadedrenderloop_p.h \
$$PWD/qsgwindowsrenderloop_p.h \
@@ -95,6 +98,9 @@ SOURCES += \
$$PWD/qsgdistancefieldglyphnode_p.cpp \
$$PWD/qsgdefaultimagenode.cpp \
$$PWD/qsgdefaultrectanglenode.cpp \
+ $$PWD/qsgbasicrectanglenode.cpp \
+ $$PWD/qsgbasicimagenode.cpp \
+ $$PWD/qsgbasicglyphnode.cpp \
$$PWD/qsgrenderloop.cpp \
$$PWD/qsgthreadedrenderloop.cpp \
$$PWD/qsgwindowsrenderloop.cpp \