Revision control
Copy as Markdown
// Copyright (C) 2025 The Qt Company Ltd.
// Copyright (C) 2015 QUIt Coding <info@quitcoding.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "demoqpitem.h"
#include <QPainter>
#include <QPainterPath>
#include <QPen>
#include <QPointF>
#include <QFontDatabase>
#define _USE_MATH_DEFINES
#include <qmath.h>
#include <algorithm>
DemoQPItem::DemoQPItem(QQuickItem *parent)
: QQuickPaintedItem(parent)
{
// Setup colors
m_colorWhite = QColor(255,255,255,255);
m_colorGray = QColor(180,180,180,255);
m_colorBlack = QColor(0,0,0,255);
m_color1 = QColor(180,190,40,20);
m_color2 = QColor(255,255,255,150);
m_color3 = QColor(255,255,255,80);
m_circleImage = QImage(":/qml/images/circle.png");
// Load custom font
QStringList s = QFontDatabase::applicationFontFamilies(g_customFontId);
if (!s.isEmpty())
m_testFont = QFont(s.first());
#ifdef USE_STATIC_TEXTS
m_texts.resize(50);
for (int i = 0; i < m_texts.size(); i++) {
auto &t = m_texts[i];
t.setText(QString::number(i + 1));
t.setPerformanceHint(QStaticText::AggressiveCaching);
}
#endif
// Set antialiasing and render target
setAntialiasing(m_qpAntialiasing);
setRenderTarget(m_qpRenderTargetFBO ? QQuickPaintedItem::FramebufferObject : QQuickPaintedItem::Image);
// Enable this to try FastFBOResizing
//setPerformanceHint(QQuickPaintedItem::FastFBOResizing, true);
}
void DemoQPItem::paint(QPainter *painter)
{
m_painter = painter;
float w = boundingRect().width();
float h = boundingRect().height();
float s = std::min(w, h);
float t = m_animationTime;
// These painting commands are identical with both renderers
for (int i=0 ; i < m_testCount ; i++) {
// Paint ruler
if (m_enabledTests & 1) {
drawRuler(0, h*0.02f, w, h*0.05f, t);
}
// Paint circles
if (m_enabledTests & 2) {
float bigCircle = 50.0f + s*0.5f;
float smallCircle = 20.0f + s*0.2f;
drawGraphCircles(w/2-bigCircle/2, h*0.1f, bigCircle, bigCircle, 8, t*2);
drawGraphCircles(w*0.05f, h*0.55f, smallCircle, smallCircle, 6, t*3);
drawGraphCircles(w-smallCircle-w*0.05f, h*0.55f, smallCircle, smallCircle, 3, t);
}
// Paint lines
if (m_enabledTests & 4) {
drawGraphLine(0, h, w, -h, 4, t);
drawGraphLine(0, h, w, -h*0.8f, 6, t+10);
drawGraphLine(0, h, w, -h*0.6f, 12, t/2);
}
// Paint bars
if (m_enabledTests & 8) {
drawGraphBars(0, h, w, -h*0.8f, 6, t*3);
drawGraphBars(0, h, w, -h*0.4f, 10, t+2);
drawGraphBars(0, h, w, -h*0.3f, 20, t*2+2);
drawGraphBars(0, h, w, -h*0.2f, 40, t*3+2);
}
// Paint icons
int icons = 20;
if (m_enabledTests & 16) {
drawIcons(0, h*0.2f, w, h*0.2f, icons, t);
}
// Paint flower
if (m_enabledTests & 32) {
float flowerSize = 80.0f + s*0.6f;
drawFlower(w/2-flowerSize/2, h-flowerSize, flowerSize, flowerSize, t);
}
// Increase animation time when m_testCount > 1
t += 0.3f;
}
}
void DemoQPItem::drawGraphLine(float x, float y, float w, float h, int items, float t)
{
QVarLengthArray<float, 1024> samples(items);
QVarLengthArray<float, 1024> sx(items); QVarLengthArray<float, 1024> sy(items);
float dx = w/(items-1);
float dotSize = 4.0f + w*0.005;
int i;
// Generate positions
for (i = 0; i<items; i++) {
samples[i] = 0.5f + sinf((i+1) * t * 0.2f) * 0.1f;
}
for (i = 0; i < items; i++) {
sx[i] = x+i*dx;
sy[i] = y+h*samples[i]*0.8f;
}
// Draw graph background area
QLinearGradient bg = QLinearGradient(x, y, x, y+h);
bg.setColorAt(0.0, m_color1);
bg.setColorAt(1.0, m_color2);
QPainterPath path;
path.moveTo(sx[0], sy[0]);
for (i = 1; i < items; i++)
path.cubicTo(sx[i-1]+dx*0.5f,sy[i-1], sx[i]-dx*0.5f,sy[i], sx[i],sy[i]);
path.lineTo(x+w, y);
path.lineTo(x, y);
m_painter->fillPath(path, bg);
// Draw graph line
QPainterPath path2;
path2.moveTo(sx[0], sy[0]);
for (i = 1; i < items; i++)
path2.cubicTo(sx[i-1]+dx*0.5f,sy[i-1], sx[i]-dx*0.5f,sy[i], sx[i],sy[i]);
m_painter->strokePath(path2, QPen(m_colorGray, 1.0f+dotSize*0.2f));
// Draw dots
m_painter->setBrush(m_colorWhite);
m_painter->setPen(QPen(m_colorBlack, dotSize*0.2));
for (i = 0; i < items; i++)
m_painter->drawEllipse(QPointF(sx[i], sy[i]), dotSize*0.8, dotSize*0.8);
}
void DemoQPItem::drawGraphBars(float x, float y, float w, float h, int items, float t) {
QVarLengthArray<float, 1024> samples(items);
QVarLengthArray<float, 1024> sx(items); QVarLengthArray<float, 1024> sy(items);
float dx = w/items;
float barWidth = dx * 0.8f;
float margin = dx - barWidth;
int i;
// Generate positions
for (i = 0; i<items; i++) {
samples[i] = 0.5f + sinf(i*0.1f+t)*0.5f;
}
for (i = 0; i < items; i++) {
sx[i] = x+i*dx + margin/2;
sy[i] = h*samples[i];
}
// Draw graph bars
m_painter->setBrush(m_color3);
float lineWidth = 1.0f;
QPen pen(m_colorBlack, lineWidth);
pen.setJoinStyle(Qt::MiterJoin);
m_painter->setPen(pen);
// TODO: Consider drawRects() instead if it's faster?
for (i = 0; i < items; i++) {
m_painter->drawRect((int)sx[i]+0.5, (int)y+2.5, (int)barWidth, (int)sy[i]);
}
}
void DemoQPItem::drawGraphCircles(float x, float y, float w, float h, int items, float t)
{
float barWidth = 0.3f * w/items;
float lineMargin = 0.2f * barWidth;
float showAnimationProgress = 0.1f + 0.4f*sinf(t*0.8f)+0.5f;
float lineWidth = barWidth*showAnimationProgress;
float cx = x+w/2;
float cy = y+h/2;
float radius1 = w/2 - lineWidth;
// Draw cicle backgrounds
float r = radius1;
QColor c_background = QColor(215,215,215,50);
m_painter->setPen(QPen(c_background, lineWidth));
m_painter->setBrush(Qt::NoBrush);
for (int i=0 ; i<items ; i++) {
m_painter->drawEllipse(QPointF(cx, cy), r, r);
r -= (lineWidth + lineMargin);
}
// Draw circle bars
r = radius1;
float r2 = radius1;
QPen pen(c_background, lineWidth, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
const int a1 = 90*16;
for (int i=0 ; i<items ; i++) {
float s = (float)i/items;
QColor c(200-150*s,200-50*s,100+50*s,255*showAnimationProgress);
pen.setColor(c);
m_painter->setPen(pen);
QRectF area(cx-r, cy-r, cx-x+r2-lineWidth, cy-y+r2-lineWidth);
int a0 = -360*16*(((float)items-i)/items)*showAnimationProgress;
m_painter->drawArc(area, a1, a0);
r -= (lineWidth + lineMargin);
r2 -= 2*(lineWidth + lineMargin);
}
}
void DemoQPItem::drawIcons(float x, float y, float w, float h, int items, float t)
{
float s = std::min(width(), height());
float size = 16.0f + s*0.05f;
float fontSize = size * 0.5f;
m_testFont.setPixelSize(fontSize);
m_painter->setFont(m_testFont);
m_painter->setPen("#FFFFFF");
m_painter->setBrush(Qt::NoBrush);
for (int i=0 ; i<items ; i++) {
float xp = x + (w-size)/items*i;
float yp = y + h*0.5f + h * sinf((i+1) * t * 0.1f) * 0.5f;
const QRectF rect(xp, yp, size, size);
m_painter->drawImage(rect, m_circleImage);
#ifdef USE_STATIC_TEXTS
const auto st = m_texts.at(i);
QPointF pos(rect.center().x() - 0.5f * st.size().width(),
rect.center().y() - 0.5f * st.size().height());
m_painter->drawStaticText(pos, st);
#else
m_painter->drawText(rect, Qt::AlignHCenter|Qt::AlignCenter, QString::number(i+1));
#endif
}
}
void DemoQPItem::drawRuler(float x, float y, float w, float h, float t)
{
float posX = x + w*0.05f;
double space = w*0.03f + sinf(t)*w*0.02f;
float fontSize = 10.0f + w*0.01f;
m_testFont.setPixelSize(fontSize);
m_painter->setFont(m_testFont);
m_painter->setPen("#E0E0B0");
m_painter->setBrush(Qt::NoBrush);
int i = 0;
QVector<QPointF> pointPairs;
while (posX < w) {
pointPairs.append(QPointF(posX, y));
float height = h*0.2f;
float ts = w*0.05f;
QRectF textRect(posX-ts, y+h-ts, ts*2, ts*2);
if (i%10==0) {
height = h*0.5f;
m_painter->drawText(textRect, Qt::AlignHCenter|Qt::AlignCenter, QString::number(i));
} else if (i%5==0) {
height = h*0.3f;
if (space > w*0.02f) m_painter->drawText(textRect, Qt::AlignHCenter|Qt::AlignCenter, QString::number(i));
}
pointPairs.append(QPointF(posX, y+height));
posX += space;
i++;
}
QPen pen(QColor("#E0E0E0"), 1);
m_painter->setPen(pen);
m_painter->drawLines(pointPairs);
}
float DemoQPItem::_flowerPos(int i) {
const int items = 12;
return (2*M_PI) * (1 - (float)i/items) - M_PI/2;
}
void DemoQPItem::drawFlower(float x, float y, float w, float h, float t)
{
const float cx = x + w/2;
const float cy = y + h/2;
const float leafSize = w/2;
m_painter->save(); // Required to prevent rotation multiply
QTransform transform = m_painter->transform();
transform.translate(cx, cy);
transform.rotate(sinf(t) * 20);
transform.translate(-cx, -cy);
m_painter->setTransform(transform);
m_painter->setPen(QPen(QColor("#40000000"), 4));
QRadialGradient gradient1(cx, cy, leafSize);
QColor startColor((0.5f + sinf(t*2)*0.5f)*255, 0, (0.5f + sinf(t+M_PI)*0.5f)*255);
gradient1.setColorAt(0, startColor);
gradient1.setColorAt(1, QColor("#ffffff"));
m_painter->setBrush(gradient1);
QPainterPath path;
path.moveTo(cx, cy);
const int items = 12;
for (int i=0; i<items; i+=2) {
path.quadTo(cx + cosf(_flowerPos(i))*leafSize, cy + sinf(_flowerPos(i))*leafSize, cx + cosf(_flowerPos(i+1))*leafSize, cy + sinf(_flowerPos(i+1))*leafSize);
path.quadTo(cx + cosf(_flowerPos(i+2))*leafSize, cy + sinf(_flowerPos(i+2))*leafSize, cx, cy);
}
m_painter->drawPath(path);
m_painter->setPen(Qt::NoPen);
m_painter->setBrush(QColor("#ffffff"));
m_painter->drawEllipse(QPointF(cx, cy), 0.1f*w, 0.1f*w);
m_painter->restore();
}