aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
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 \