diff --git a/backend-shared/exportfuncs.cpp b/backend-shared/exportfuncs.cpp index 189e86f47..f5a1c9ccb 100644 --- a/backend-shared/exportfuncs.cpp +++ b/backend-shared/exportfuncs.cpp @@ -16,7 +16,7 @@ #include "core/selection.h" #include "core/taxonomy.h" #include "core/sample.h" -#include "profile-widget/profilewidget2.h" +#include "profile-widget/profilescene.h" #include #include #include @@ -39,10 +39,9 @@ static constexpr int profileScale = 4; static constexpr int profileWidth = 800 * profileScale; static constexpr int profileHeight = 600 * profileScale; -static void exportProfile(ProfileWidget2 *profile, const struct dive *dive, const QString &filename) +static void exportProfile(ProfileScene *profile, const struct dive *dive, const QString &filename) { - profile->setProfileState(dive, 0); - profile->plotDive(dive, 0, false, true); + profile->plotDive(dive, 0, nullptr, false, true); QImage image = QImage(QSize(profileWidth, profileHeight), QImage::Format_RGB32); QPainter paint; paint.begin(&image); @@ -50,11 +49,9 @@ static void exportProfile(ProfileWidget2 *profile, const struct dive *dive, cons image.save(filename); } -static std::unique_ptr getPrintProfile() +static std::unique_ptr getPrintProfile() { - auto profile = std::make_unique(nullptr, (double)profileScale, nullptr); - profile->setPrintMode(); - return profile; + return std::make_unique((double)profileScale, true, false); } void exportProfile(QString filename, bool selected_only, ExportCallback &cb) diff --git a/desktop-widgets/diveplanner.cpp b/desktop-widgets/diveplanner.cpp index 87edb59da..e3d267945 100644 --- a/desktop-widgets/diveplanner.cpp +++ b/desktop-widgets/diveplanner.cpp @@ -12,7 +12,7 @@ #include "qt-models/cylindermodel.h" #include "qt-models/models.h" -#include "profile-widget/profilewidget2.h" +#include "profile-widget/profilescene.h" #include "qt-models/diveplannermodel.h" #include @@ -609,10 +609,8 @@ void PlannerWidgets::printDecoPlan() painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::SmoothPixmapTransform); - auto profile = std::make_unique(DivePlannerPointsModel::instance(), 1.0, nullptr); - profile->setPlanState(&displayed_dive, 0); - profile->plotDive(&displayed_dive, 0, true, true); - profile->setPrintMode(); + auto profile = std::make_unique(1.0, true, false); + profile->plotDive(&displayed_dive, 0, DivePlannerPointsModel::instance(), true, true); profile->draw(&painter, QRect(0, 0, pixmap.width(), pixmap.height())); QByteArray byteArray; diff --git a/desktop-widgets/printer.cpp b/desktop-widgets/printer.cpp index 873e515be..2d0d24983 100644 --- a/desktop-widgets/printer.cpp +++ b/desktop-widgets/printer.cpp @@ -1,16 +1,18 @@ // SPDX-License-Identifier: GPL-2.0 #include "printer.h" #include "templatelayout.h" +#include "core/dive.h" // for get_dive_by_uniq_id() #include "core/statistics.h" #include "core/qthelper.h" +#include "profile-widget/profilescene.h" #include +#include #include #include #include #include #include -#include "profile-widget/profilewidget2.h" Printer::Printer(QPaintDevice *paintDevice, const print_options &printOptions, const template_options &templateOptions, PrintMode printMode, bool inPlanner) : paintDevice(paintDevice), @@ -29,15 +31,14 @@ Printer::~Printer() } void Printer::putProfileImage(const QRect &profilePlaceholder, const QRect &viewPort, QPainter *painter, - struct dive *dive, ProfileWidget2 *profile) + struct dive *dive, ProfileScene *profile) { int x = profilePlaceholder.x() - viewPort.x(); int y = profilePlaceholder.y() - viewPort.y(); // use the placeHolder and the viewPort position to calculate the relative position of the dive profile. QRect pos(x, y, profilePlaceholder.width(), profilePlaceholder.height()); - profile->setProfileState(dive, 0); - profile->plotDive(dive, 0, true); + profile->plotDive(dive, 0, nullptr, true); profile->draw(painter, pos); } @@ -109,11 +110,7 @@ void Printer::render(int pages) // Scale the fonts in the printed profile accordingly. // This is arbitrary, but it seems to work reasonably well. double printFontScale = collection.count() > 0 ? collection[0].geometry().size().height() / 600.0 : 1.0; - auto profile = std::make_unique(nullptr, printFontScale, nullptr); - - // apply printing settings to profile - profile->setFrameStyle(QFrame::NoFrame); - profile->setPrintMode(!printOptions.color_selected); + auto profile = std::make_unique(printFontScale, true, !printOptions.color_selected); // render the Qwebview QPainter painter; diff --git a/desktop-widgets/printer.h b/desktop-widgets/printer.h index c841da2c9..3d16a3d9a 100644 --- a/desktop-widgets/printer.h +++ b/desktop-widgets/printer.h @@ -5,7 +5,7 @@ #include "printoptions.h" #include "templateedit.h" -class ProfileWidget2; +class ProfileScene; class QPainter; class QPaintDevice; class QRect; @@ -32,7 +32,7 @@ private: void render(int Pages); void flowRender(); void putProfileImage(const QRect &box, const QRect &viewPort, QPainter *painter, - struct dive *dive, ProfileWidget2 *profile); + struct dive *dive, ProfileScene *profile); private slots: void templateProgessUpdated(int value); diff --git a/profile-widget/profilescene.cpp b/profile-widget/profilescene.cpp index cc556dffa..8164644c3 100644 --- a/profile-widget/profilescene.cpp +++ b/profile-widget/profilescene.cpp @@ -187,6 +187,8 @@ ProfileScene::ProfileScene(double fontPrintScale, bool printMode, bool isGraysca for (AbstractProfilePolygonItem *item: profileItems) addItem(item); + + updateAxes(true); } ProfileScene::~ProfileScene() @@ -663,3 +665,39 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM #endif diveComputerText->setText(dcText); } + +QImage ProfileScene::toImage(QSize size) +{ + // The size of chart with respect to the scene is fixed - by convention - to 100.0. + // We add 2% to the height so that the dive computer name is not cut off. + QRectF sceneRect(0.0, 0.0, 100.0, 102.0); + + QImage image(size, QImage::Format_ARGB32); + image.fill(getColor(::BACKGROUND, isGrayscale)); + + QPainter imgPainter(&image); + imgPainter.setRenderHint(QPainter::Antialiasing); + imgPainter.setRenderHint(QPainter::SmoothPixmapTransform); + render(&imgPainter, QRect(QPoint(), size), sceneRect, Qt::IgnoreAspectRatio); + imgPainter.end(); + + if (isGrayscale) { + // convert QImage to grayscale before rendering + for (int i = 0; i < image.height(); i++) { + QRgb *pixel = reinterpret_cast(image.scanLine(i)); + QRgb *end = pixel + image.width(); + for (; pixel != end; pixel++) { + int gray_val = qGray(*pixel); + *pixel = QColor(gray_val, gray_val, gray_val).rgb(); + } + } + } + + return image; +} + +void ProfileScene::draw(QPainter *painter, const QRect &pos) +{ + QImage img = toImage(pos.size()); + painter->drawImage(pos, img); +} diff --git a/profile-widget/profilescene.h b/profile-widget/profilescene.h index 665bb56d0..f99f13dff 100644 --- a/profile-widget/profilescene.h +++ b/profile-widget/profilescene.h @@ -8,6 +8,7 @@ #include "core/display.h" #include +#include class DivePlannerPointsModel; class DivePlotDataModel; @@ -46,6 +47,9 @@ public: void plotDive(const struct dive *d, int dc, DivePlannerPointsModel *plannerModel = nullptr, bool inPlanner = false, bool instant = false, bool calcMax = true); + void draw(QPainter *painter, const QRect &pos); + QImage toImage(QSize size); + const struct dive *d; int dc; private: diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp index 391dcbb6c..352651ea8 100644 --- a/profile-widget/profilewidget2.cpp +++ b/profile-widget/profilewidget2.cpp @@ -1428,39 +1428,3 @@ struct dive *ProfileWidget2::mutable_dive() const { return const_cast(d); } - -QImage ProfileWidget2::toImage(QSize size) -{ - // The size of chart with respect to the scene is fixed - by convention - to 100.0. - // We add 2% to the height so that the dive computer name is not cut off. - QRectF sceneRect(0.0, 0.0, 100.0, 102.0); - - QImage image(size, QImage::Format_ARGB32); - image.fill(getColor(::BACKGROUND, profileScene->isGrayscale)); - - QPainter imgPainter(&image); - imgPainter.setRenderHint(QPainter::Antialiasing); - imgPainter.setRenderHint(QPainter::SmoothPixmapTransform); - scene()->render(&imgPainter, QRect(QPoint(), size), sceneRect, Qt::IgnoreAspectRatio); - imgPainter.end(); - - if (profileScene->isGrayscale) { - // convert QImage to grayscale before rendering - for (int i = 0; i < image.height(); i++) { - QRgb *pixel = reinterpret_cast(image.scanLine(i)); - QRgb *end = pixel + image.width(); - for (; pixel != end; pixel++) { - int gray_val = qGray(*pixel); - *pixel = QColor(gray_val, gray_val, gray_val).rgb(); - } - } - } - - return image; -} - -void ProfileWidget2::draw(QPainter *painter, const QRect &pos) -{ - QImage img = toImage(pos.size()); - painter->drawImage(pos, img); -} diff --git a/profile-widget/profilewidget2.h b/profile-widget/profilewidget2.h index 86dc7eb24..a68181117 100644 --- a/profile-widget/profilewidget2.h +++ b/profile-widget/profilewidget2.h @@ -53,9 +53,7 @@ public: void setEditState(const struct dive *d, int dc); void setPrintMode(bool grayscale = false); bool isPlanner() const; - void draw(QPainter *painter, const QRect &pos); void clear(); - QImage toImage(QSize size); #ifndef SUBSURFACE_MOBILE bool eventFilter(QObject *, QEvent *) override; #endif diff --git a/profile-widget/qmlprofile.cpp b/profile-widget/qmlprofile.cpp index d3907db1f..e52c69ab2 100644 --- a/profile-widget/qmlprofile.cpp +++ b/profile-widget/qmlprofile.cpp @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include "qmlprofile.h" +#include "profilescene.h" #include "mobile-widgets/qmlmanager.h" #include "core/errorhelper.h" #include "core/subsurface-string.h" @@ -26,11 +27,13 @@ QMLProfile::QMLProfile(QQuickItem *parent) : setDevicePixelRatio(QMLManager::instance()->lastDevicePixelRatio()); } +QMLProfile::~QMLProfile() +{ +} + void QMLProfile::createProfileView() { - m_profileWidget.reset(new ProfileWidget2(nullptr, fontScale * m_devicePixelRatio, nullptr)); - m_profileWidget->setProfileState(nullptr, 0); - m_profileWidget->setPrintMode(); + m_profileWidget.reset(new ProfileScene(fontScale * m_devicePixelRatio, true, false)); } // we need this so we can connect update() to the scaleChanged() signal - which the connect above cannot do @@ -51,75 +54,7 @@ void QMLProfile::paint(QPainter *painter) // let's look at the intended size of the content and scale our scene accordingly QRect painterRect = painter->viewport(); - QRect profileRect = m_profileWidget->viewport()->rect(); - // qDebug() << "profile viewport and painter viewport" << profileRect << painterRect; - qreal sceneSize = 104; // that should give us 2% margin all around (100x100 scene) - qreal dprComp = devicePixelRatio() * painterRect.width() / profileRect.width(); - qreal sx = painterRect.width() / sceneSize / dprComp; - qreal sy = painterRect.height() / sceneSize / dprComp; - - // next figure out the weird magic by which we need to shift the painter so the profile is shown - double dpr = devicePixelRatio(); - double magicValues[] = { 0.0, 0.1, 0.25, 0.33, 0.375, 0.40, 0.42}; - qreal magicShiftFactor = 0.0; - if (dpr < 1.3) { - magicShiftFactor = magicValues[0]; - } else if (dpr > 6.0) { - magicShiftFactor = magicValues[6]; - } else if (IS_FP_SAME(dpr, rint(dpr))) { - magicShiftFactor = magicValues[lrint(dpr)]; - } else { - int lower = (int)dpr; - magicShiftFactor = (magicValues[lower] * (lower + 1 - dpr) + magicValues[lower + 1] * (dpr - lower)); - if (dpr < 1.45) - magicShiftFactor -= 0.03; - } - // now set up the transformations scale the profile and - // shift the painter (taking its existing transformation into account) - QTransform profileTransform = QTransform(); - profileTransform.scale(sx, sy); - - // this is a bit complex because of the interaction with the Qt widget scaling - // logic. We use the default setup where the scale happens with the center of the - // widget as transformation center. - // The QML code *should* already ensure that we don't shrink the profile, but for - // the math below to work, let's make sure - qreal profileScale = scale(); - if (profileScale < 1.0) { - profileScale = 1.0; - setScale(profileScale); - } - // When zooming and panning we want to ensure that we always show a subset of the - // profile and not the "empty space" around the profile. - // a bit of math on a piece of paper shows that our offsets need to stay within +/- dx and dy - qreal dx = width() * (profileScale - 1) / (2 * dpr * profileScale); - qreal dy = height() * (profileScale - 1) / (2 * dpr * profileScale); - m_xOffset = std::max(-dx, std::min(m_xOffset, dx)); - m_yOffset = std::max(-dy, std::min(m_yOffset, dy)); - - QTransform painterTransform = painter->transform(); - painterTransform.translate(dpr * m_xOffset - painterRect.width() * magicShiftFactor, dpr * m_yOffset - painterRect.height() * magicShiftFactor); - -#if defined(PROFILE_SCALING_DEBUG) - // some debugging messages to help adjust this in case the magic above is insufficient - qDebug() << QString("dpr %1 profile viewport %2 %3 painter viewport %4 %5").arg(dpr).arg(profileRect.width()).arg(profileRect.height()) - .arg(painterRect.width()).arg(painterRect.height()); - qDebug() << QString("profile matrix %1 %2 %3 %4 %5 %6 %7 %8 %9").arg(profileTransform.m11()).arg(profileTransform.m12()).arg(profileTransform.m13()) - .arg(profileTransform.m21()).arg(profileTransform.m22()).arg(profileTransform.m23()) - .arg(profileTransform.m31()).arg(profileTransform.m32()).arg(profileTransform.m33())); - qDebug() << QString("painter matrix %1 %2 %3 %4 %5 %6 %7 %8 %9").arg(painterTransform.m11()).arg(painterTransform.m12()).arg(painterTransform.m13()) - .arg(painterTransform.m21()).arg(painterTransform.m22()).arg(painterTransform.m23()) - .arg(painterTransform.m31()).arg(painterTransform.m32()).arg(painterTransform.m33())); - qDebug() << "exist profile transform" << m_profileWidget->transform() << "painter transform" << painter->transform(); -#endif - // apply the transformation - painter->setTransform(painterTransform); - m_profileWidget->setTransform(profileTransform); - - // finally, render the profile - m_profileWidget->render(painter); - if (verbose) - qDebug() << "finished rendering profile with offset" << QString::number(m_xOffset, 'f', 1) << "/" << QString::number(m_yOffset, 'f', 1) << "in" << timer.elapsed() << "ms"; + m_profileWidget->draw(painter, painterRect); } void QMLProfile::setMargin(int margin) diff --git a/profile-widget/qmlprofile.h b/profile-widget/qmlprofile.h index 4762c6d9c..29e1291ea 100644 --- a/profile-widget/qmlprofile.h +++ b/profile-widget/qmlprofile.h @@ -2,9 +2,11 @@ #ifndef QMLPROFILE_H #define QMLPROFILE_H -#include "profilewidget2.h" #include "core/subsurface-qt/divelistnotifier.h" #include +#include + +class ProfileScene; class QMLProfile : public QQuickPaintedItem { @@ -16,6 +18,7 @@ class QMLProfile : public QQuickPaintedItem public: explicit QMLProfile(QQuickItem *parent = 0); + ~QMLProfile(); void paint(QPainter *painter); @@ -36,7 +39,7 @@ private: qreal m_devicePixelRatio; int m_margin; qreal m_xOffset, m_yOffset; - QScopedPointer m_profileWidget; + std::unique_ptr m_profileWidget; void updateProfile(); void createProfileView();