void compareSelectionNode(QSGNode *node, const QRectF &rect, int selectionId) { QSGGeometryNode *geometryNode = static_cast<QSGGeometryNode *>(node); QSGGeometry *geometry = geometryNode->geometry(); QCOMPARE(geometry->vertexCount(), 4); QCOMPARE(geometry->drawingMode(), (GLenum)GL_TRIANGLE_STRIP); OpaqueColoredPoint2DWithSize *data = static_cast<OpaqueColoredPoint2DWithSize *>(geometry->vertexData()); float *lowerLeft = reinterpret_cast<float *>(data); float *lowerRight = reinterpret_cast<float *>(++data); float *upperLeft = reinterpret_cast<float *>(++data); float *upperRight = reinterpret_cast<float *>(++data); QCOMPARE(QRectF(QPointF(upperLeft[0], upperLeft[1]), QPointF(lowerRight[0], lowerRight[1])), rect); QCOMPARE(lowerRight[0], upperRight[0]); QCOMPARE(lowerRight[1], lowerLeft[1]); QCOMPARE(upperLeft[0], lowerLeft[0]); QCOMPARE(upperLeft[1], upperRight[1]); QCOMPARE(int(lowerLeft[4]), selectionId); QCOMPARE(int(lowerRight[4]), selectionId); QCOMPARE(int(upperLeft[4]), selectionId); QCOMPARE(int(upperRight[4]), selectionId); TimelineItemsMaterial *material = static_cast<TimelineItemsMaterial *>( geometryNode->material()); QVERIFY(!(material->flags() & QSGMaterial::Blending)); }
void Renderer::buildRenderList(QSGNode *node, QSGClipNode *clip) { if (node->isSubtreeBlocked()) return; if (node->type() == QSGNode::GeometryNodeType || node->type() == QSGNode::ClipNodeType) { QSGBasicGeometryNode *gn = static_cast<QSGBasicGeometryNode *>(node); QSGGeometry *g = gn->geometry(); Element e; e.node = gn; if (g->vertexCount() > 0) { e.vboOffset = m_vboSize; int vertexSize = g->sizeOfVertex() * g->vertexCount(); m_vboSize += vertexSize; m_vboData.resize(m_vboSize); memcpy(m_vboData.data() + e.vboOffset, g->vertexData(), vertexSize); } else { e.vboOffset = -1; } if (g->indexCount() > 0) { e.iboOffset = m_iboSize; int indexSize = g->sizeOfIndex() * g->indexCount(); m_iboSize += indexSize; m_iboData.resize(m_iboSize); memcpy(m_iboData.data() + e.iboOffset, g->indexData(), indexSize); } else { e.iboOffset = -1; } m_renderList.add(e); static_cast<BasicGeometryNode_Accessor *>(node)->m_clip_list = clip; if (node->type() == QSGNode::ClipNodeType) clip = static_cast<QSGClipNode *>(node); } QSGNODE_TRAVERSE(node) buildRenderList(child, clip); }
void ShaderEffectItem::updateGeometry() { QRectF srcRect(0, 1, 1, -1); if (m_mirrored) srcRect = QRectF(0, 0, 1, 1); QRectF dstRect = QRectF(0,0, width(), height()); int vmesh = m_meshResolution.height(); int hmesh = m_meshResolution.width(); QSGGeometry *g = &m_geometry; if (vmesh == 1 && hmesh == 1) { if (g->vertexCount() != 4) g->allocate(4); QSGGeometry::updateTexturedRectGeometry(g, dstRect, srcRect); return; } g->allocate((vmesh + 1) * (hmesh + 1), vmesh * 2 * (hmesh + 2)); QSGGeometry::TexturedPoint2D *vdata = g->vertexDataAsTexturedPoint2D(); for (int iy = 0; iy <= vmesh; ++iy) { float fy = iy / float(vmesh); float y = float(dstRect.top()) + fy * float(dstRect.height()); float ty = float(srcRect.top()) + fy * float(srcRect.height()); for (int ix = 0; ix <= hmesh; ++ix) { float fx = ix / float(hmesh); vdata->x = float(dstRect.left()) + fx * float(dstRect.width()); vdata->y = y; vdata->tx = float(srcRect.left()) + fx * float(srcRect.width()); vdata->ty = ty; ++vdata; } } quint16 *indices = (quint16 *)g->indexDataAsUShort(); int i = 0; for (int iy = 0; iy < vmesh; ++iy) { *(indices++) = i + hmesh + 1; for (int ix = 0; ix <= hmesh; ++ix, ++i) { *(indices++) = i + hmesh + 1; *(indices++) = i; } *(indices++) = i - 1; } }
void QQuickShapeGenericRenderer::updateStrokeNode(ShapePathData *d, QQuickShapeGenericNode *node) { if (!node->m_strokeNode) return; if (!(d->effectiveDirty & (DirtyStrokeGeom | DirtyColor))) return; QQuickShapeGenericStrokeFillNode *n = node->m_strokeNode; QSGGeometry *g = n->geometry(); if (d->strokeVertices.isEmpty()) { if (g->vertexCount() || g->indexCount()) { g->allocate(0, 0); n->markDirty(QSGNode::DirtyGeometry); } return; } n->markDirty(QSGNode::DirtyGeometry); // Async loading runs update once, bails out above, then updates again once // ready. Set the material dirty then. This is in-line with fill where the // first activateMaterial() achieves the same. if (!g->vertexCount()) n->markDirty(QSGNode::DirtyMaterial); if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyStrokeGeom)) { ColoredVertex *vdst = reinterpret_cast<ColoredVertex *>(g->vertexData()); for (int i = 0; i < g->vertexCount(); ++i) vdst[i].set(vdst[i].x, vdst[i].y, d->strokeColor); return; } g->allocate(d->strokeVertices.count(), 0); g->setDrawingMode(QSGGeometry::DrawTriangleStrip); memcpy(g->vertexData(), d->strokeVertices.constData(), g->vertexCount() * g->sizeOfVertex()); }
void SceneGraphDeviceContext::DrawCircle(int x, int y, int radius) { // Note: No support for vertex antialiasing. Use a top-level QQuickView with multisample antialiasing. // TODO: Add vertex antialiasing, refer to // 1) Qt sources for "void QSGBasicInternalRectangleNode::updateGeometry()" in // qtdeclarative/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp // 2) https://stackoverflow.com/questions/28125425/smooth-painting-in-custom-qml-element vrv::Pen currentPen = m_penStack.top(); int segmentCount = 16; QSGGeometryNode *node = new QSGGeometryNode(); QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), segmentCount); geometry->setDrawingMode(QSGGeometry::DrawTriangleFan); node->setGeometry(geometry); node->setFlag(QSGNode::OwnsGeometry); QSGFlatColorMaterial *material = new QSGFlatColorMaterial; material->setColor(static_cast<QRgb>(currentPen.GetColour())); node->setMaterial(material); node->setFlag(QSGNode::OwnsMaterial); // This draws individual triangles from the first point (center) to every outer point by using DrawTriangleFan. QSGGeometry::Point2D *vertices = geometry->vertexDataAsPoint2D(); int numPoints = geometry->vertexCount(); vertices[0].x = translateX(x); vertices[0].y = translateY(y); for (int i = 1; i < numPoints; ++i) { double theta = i * 2 * M_PI / (numPoints - 2); vertices[i].x = translateX(x + radius * static_cast<float>(std::cos(theta))); vertices[i].y = translateY(y - radius * static_cast<float>(std::sin(theta))); } node->markDirty(QSGNode::DirtyGeometry); AddGeometryNode(node); }
void QSGDefaultImageNode::updateGeometry() { Q_ASSERT(!m_targetRect.isEmpty()); const QSGTexture *t = m_material.texture(); 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); #ifdef QT_OPENGL_ES_2 QOpenGLContext *ctx = QOpenGLContext::currentContext(); bool npotSupported = ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat); QSize size = t->textureSize(); bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height()); bool wrapSupported = npotSupported || !isNpot; #else bool wrapSupported = true; #endif // 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 && wrapSupported))) { 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; }
void QSGBasicInternalImageNode::updateGeometry() { Q_ASSERT(!m_targetRect.isEmpty()); const QSGTexture *t = materialTexture(); if (!t) { QSGGeometry *g = geometry(); g->allocate(4); g->setDrawingMode(QSGGeometry::DrawTriangleStrip); 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(QSGGeometry::DrawTriangleStrip); 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(QSGGeometry::DrawTriangleStrip); QSGGeometry::updateTexturedRectGeometry(&m_geometry, m_targetRect, sr); } } else { QSGGeometry *g = m_antialiasing ? geometry() : &m_geometry; updateGeometry(m_targetRect, m_innerTargetRect, sourceRect, innerSourceRect, m_subSourceRect, g, m_mirror, m_antialiasing); } } markDirty(DirtyGeometry); m_dirtyGeometry = false; }
QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect, const QRectF &innerTargetRect, const QRectF &sourceRect, const QRectF &innerSourceRect, const QRectF &subSourceRect, QSGGeometry *geometry, bool mirror, bool antialiasing) { int floorLeft = qFloor(subSourceRect.left()); int ceilRight = qCeil(subSourceRect.right()); int floorTop = qFloor(subSourceRect.top()); int ceilBottom = qCeil(subSourceRect.bottom()); int hTiles = ceilRight - floorLeft; int vTiles = ceilBottom - floorTop; int hCells = hTiles; int vCells = vTiles; if (innerTargetRect.width() == 0) hCells = 0; if (innerTargetRect.left() != targetRect.left()) ++hCells; if (innerTargetRect.right() != targetRect.right()) ++hCells; if (innerTargetRect.height() == 0) vCells = 0; if (innerTargetRect.top() != targetRect.top()) ++vCells; if (innerTargetRect.bottom() != targetRect.bottom()) ++vCells; QVarLengthArray<X, 32> xData(2 * hCells); QVarLengthArray<Y, 32> yData(2 * vCells); X *xs = xData.data(); Y *ys = yData.data(); if (innerTargetRect.left() != targetRect.left()) { xs[0].x = targetRect.left(); xs[0].tx = sourceRect.left(); xs[1].x = innerTargetRect.left(); xs[1].tx = innerSourceRect.left(); xs += 2; } if (innerTargetRect.width() != 0) { xs[0].x = innerTargetRect.left(); xs[0].tx = innerSourceRect.x() + (subSourceRect.left() - floorLeft) * innerSourceRect.width(); ++xs; float b = innerTargetRect.width() / subSourceRect.width(); float a = innerTargetRect.x() - 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 = innerTargetRect.right(); xs[0].tx = innerSourceRect.x() + (subSourceRect.right() - ceilRight + 1) * innerSourceRect.width(); ++xs; } if (innerTargetRect.right() != targetRect.right()) { xs[0].x = innerTargetRect.right(); xs[0].tx = innerSourceRect.right(); xs[1].x = targetRect.right(); xs[1].tx = sourceRect.right(); xs += 2; } Q_ASSERT(xs == xData.data() + xData.size()); if (mirror) { float leftPlusRight = targetRect.left() + 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 (innerTargetRect.top() != targetRect.top()) { ys[0].y = targetRect.top(); ys[0].ty = sourceRect.top(); ys[1].y = innerTargetRect.top(); ys[1].ty = innerSourceRect.top(); ys += 2; } if (innerTargetRect.height() != 0) { ys[0].y = innerTargetRect.top(); ys[0].ty = innerSourceRect.y() + (subSourceRect.top() - floorTop) * innerSourceRect.height(); ++ys; float b = innerTargetRect.height() / subSourceRect.height(); float a = innerTargetRect.y() - 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 = innerTargetRect.bottom(); ys[0].ty = innerSourceRect.y() + (subSourceRect.bottom() - ceilBottom + 1) * innerSourceRect.height(); ++ys; } if (innerTargetRect.bottom() != targetRect.bottom()) { ys[0].y = innerTargetRect.bottom(); ys[0].ty = innerSourceRect.bottom(); ys[1].y = targetRect.bottom(); ys[1].ty = sourceRect.bottom(); ys += 2; } Q_ASSERT(ys == yData.data() + yData.size()); if (antialiasing) { QSGGeometry *g = geometry; Q_ASSERT(g); g->allocate(hCells * vCells * 4 + (hCells + vCells - 1) * 4, hCells * vCells * 6 + (hCells + vCells) * 12); g->setDrawingMode(QSGGeometry::DrawTriangles); 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(targetRect.width()) < qAbs(targetRect.height()) ? targetRect.width() : 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 { if (!geometry) { geometry = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), hCells * vCells * 4, hCells * vCells * 6, QSGGeometry::UnsignedShortType); } else { geometry->allocate(hCells * vCells * 4, hCells * vCells * 6); } geometry->setDrawingMode(QSGGeometry::DrawTriangles); QSGGeometry::TexturedPoint2D *vertices = 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 = geometry->indexDataAsUShort(); for (int i = 0; i < 4 * vCells * hCells; i += 4) appendQuad(&indices, i, i + 1, i + 2, i + 3); } return geometry; }
void QQuickShapeGenericRenderer::updateFillNode(ShapePathData *d, QQuickShapeGenericNode *node) { if (!node->m_fillNode) return; if (!(d->effectiveDirty & (DirtyFillGeom | DirtyColor | DirtyFillGradient))) return; // Make a copy of the data that will be accessed by the material on // the render thread. This must be done even when we bail out below. QQuickShapeGenericStrokeFillNode *n = node->m_fillNode; updateShadowDataInNode(d, n); QSGGeometry *g = n->geometry(); if (d->fillVertices.isEmpty()) { if (g->vertexCount() || g->indexCount()) { g->allocate(0, 0); n->markDirty(QSGNode::DirtyGeometry); } return; } if (d->fillGradientActive) { QQuickShapeGenericStrokeFillNode::Material gradMat; switch (d->fillGradientActive) { case LinearGradient: gradMat = QQuickShapeGenericStrokeFillNode::MatLinearGradient; break; case RadialGradient: gradMat = QQuickShapeGenericStrokeFillNode::MatRadialGradient; break; case ConicalGradient: gradMat = QQuickShapeGenericStrokeFillNode::MatConicalGradient; break; default: Q_UNREACHABLE(); } n->activateMaterial(m_item->window(), gradMat); if (d->effectiveDirty & DirtyFillGradient) { // Gradients are implemented via a texture-based material. n->markDirty(QSGNode::DirtyMaterial); // stop here if only the gradient changed; no need to touch the geometry if (!(d->effectiveDirty & DirtyFillGeom)) return; } } else { n->activateMaterial(m_item->window(), QQuickShapeGenericStrokeFillNode::MatSolidColor); // fast path for updating only color values when no change in vertex positions if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyFillGeom)) { ColoredVertex *vdst = reinterpret_cast<ColoredVertex *>(g->vertexData()); for (int i = 0; i < g->vertexCount(); ++i) vdst[i].set(vdst[i].x, vdst[i].y, d->fillColor); n->markDirty(QSGNode::DirtyGeometry); return; } } const int indexCount = d->indexType == QSGGeometry::UnsignedShortType ? d->fillIndices.count() * 2 : d->fillIndices.count(); if (g->indexType() != d->indexType) { g = new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), d->fillVertices.count(), indexCount, d->indexType); n->setGeometry(g); } else { g->allocate(d->fillVertices.count(), indexCount); } g->setDrawingMode(QSGGeometry::DrawTriangles); memcpy(g->vertexData(), d->fillVertices.constData(), g->vertexCount() * g->sizeOfVertex()); memcpy(g->indexData(), d->fillIndices.constData(), g->indexCount() * g->sizeOfIndex()); n->markDirty(QSGNode::DirtyGeometry); }