File indexing completed on 2024-05-12 16:01:31

0001 /*
0002  *  SPDX-FileCopyrightText: 2015 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_fps_decoration.h"
0008 
0009 #include <QApplication>
0010 #include <QPainter>
0011 #include "kis_canvas2.h"
0012 #include "kis_coordinates_converter.h"
0013 #include "opengl/kis_opengl_canvas_debugger.h"
0014 #include <KisStrokeSpeedMonitor.h>
0015 #include <QGraphicsScene>
0016 #include <QGraphicsPixmapItem>
0017 #include <QGraphicsDropShadowEffect>
0018 
0019 const QString KisFpsDecoration::idTag = "fps_decoration";
0020 
0021 KisFpsDecoration::KisFpsDecoration(QPointer<KisView> view)
0022     : KisCanvasDecoration(idTag, view)
0023     , m_font(QApplication::font())
0024     , m_pixmap(1, 1) // need non-zero pixmap for initial setup
0025 {
0026     setVisible(true);
0027 
0028     m_shadow = new QGraphicsDropShadowEffect(this);
0029     m_shadow->setBlurRadius(0.5);
0030     m_shadow->setOffset(0);
0031     m_shadow->setColor(QColor(0x30, 0x30, 0x30));
0032 
0033     m_scene = new QGraphicsScene(this);
0034     m_pixmapItem = m_scene->addPixmap(m_pixmap);
0035     m_pixmapItem->setGraphicsEffect(m_shadow);
0036 }
0037 
0038 KisFpsDecoration::~KisFpsDecoration()
0039 {
0040 }
0041 
0042 void KisFpsDecoration::drawDecoration(QPainter& gc, const QRectF& /*updateRect*/, const KisCoordinatesConverter */*converter*/, KisCanvas2* /*canvas*/)
0043 {
0044     // we always paint into a pixmap instead of directly into gc, as the latter
0045     // approach is known to cause garbled graphics on macOS, Windows, and even
0046     // sometimes Linux.
0047 
0048     const QString text = getText();
0049 
0050     // note that USUALLY the pixmap will have the right size. in very rare cases
0051     // (e.g. on the very first call) the computed bounding rect will not be right
0052     // and the pixmap will need a resize. it is faster to NOT usually calculate
0053     // the necessary bounds with an extra call like QFontMetrics::boundingRect()
0054     // here, as USUALLY the pixmap will be right and thus an extra call would be
0055     // unnecessary overhead.
0056 
0057     QSize size;
0058 
0059     if (!draw(text, size)) {
0060         // the pixmap is too small, we need to make it larger. make it 10% wider
0061         // than the measured width to avoid resizing again as soon as the text
0062         // gets a bit wider due to different content.
0063 
0064         m_pixmap = QPixmap(size.width() * 1.1f, size.height());
0065 
0066         KIS_ASSERT(draw(text, size));
0067     }
0068 
0069     QRectF r = m_pixmap.rect();
0070     r |= m_shadow->boundingRectFor(r);
0071 
0072     m_pixmapItem->setPixmap(m_pixmap);
0073     m_scene->render(&gc, r.translated(20, 20), r);
0074 }
0075 
0076 bool KisFpsDecoration::draw(const QString &text, QSize &outSize)
0077 {
0078     m_pixmap.fill(Qt::transparent);
0079 
0080     const int flags = Qt::AlignLeft | Qt::AlignTop | Qt::TextDontClip;
0081     QRect bounds;
0082 
0083     QPainter painter(&m_pixmap);
0084     painter.setFont(m_font);
0085 
0086     painter.setPen(QPen(QColor(0xF0, 0xF0, 0xF0)));
0087     painter.drawText(m_pixmap.rect().translated(1, 1), flags, text, &bounds);
0088 
0089     outSize = bounds.size() + QSize(1, 1);
0090 
0091     if (m_pixmap.width() < outSize.width() || m_pixmap.height() != outSize.height()) {
0092         return false; // pixmap is too small and needs a resize. rarely happens.
0093     }
0094 
0095     return true;
0096 }
0097 
0098 QString KisFpsDecoration::getText() const
0099 {
0100     QStringList lines;
0101 
0102     if (KisOpenglCanvasDebugger::instance()->showFpsOnCanvas()) {
0103         const qreal value = KisOpenglCanvasDebugger::instance()->accumulatedFps();
0104         lines << QString("Canvas FPS: %1").arg(QString::number(value, 'f', 1));
0105     }
0106 
0107     KisStrokeSpeedMonitor *monitor = KisStrokeSpeedMonitor::instance();
0108 
0109     if (monitor->haveStrokeSpeedMeasurement()) {
0110         lines << QString("Last cursor/brush speed (px/ms): %1/%2%3")
0111                 .arg(monitor->lastCursorSpeed(), 0, 'f', 1)
0112                 .arg(monitor->lastRenderingSpeed(), 0, 'f', 1)
0113                 .arg(monitor->lastStrokeSaturated() ? " (!)" : "");
0114         lines << QString("Last brush framerate: %1 fps")
0115                 .arg(monitor->lastFps(), 0, 'f', 1);
0116 
0117         lines << QString("Average cursor/brush speed (px/ms): %1/%2")
0118                 .arg(monitor->avgCursorSpeed(), 0, 'f', 1)
0119                 .arg(monitor->avgRenderingSpeed(), 0, 'f', 1);
0120         lines << QString("Average brush framerate: %1 fps")
0121                 .arg(monitor->avgFps(), 0, 'f', 1);
0122     }
0123 
0124     return lines.join('\n');
0125 }