File indexing completed on 2025-04-27 03:58:24

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2010-04-30
0007  * Description : Image zoom settings
0008  *
0009  * SPDX-FileCopyrightText: 2010-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0010  *
0011  * SPDX-License-Identifier: GPL-2.0-or-later
0012  *
0013  * ============================================================ */
0014 
0015 #include "imagezoomsettings.h"
0016 
0017 // C++ includes
0018 
0019 #include <cmath>
0020 
0021 // Qt includes
0022 
0023 #include <QList>
0024 
0025 // Local includes
0026 
0027 #include "digikam_debug.h"
0028 
0029 namespace Digikam
0030 {
0031 
0032 ImageZoomSettings::ImageZoomSettings()
0033     : m_zoom         (1.0),
0034       m_zoomConst    (1.0),
0035       m_displayWidget(nullptr)
0036 {
0037 }
0038 
0039 ImageZoomSettings::ImageZoomSettings(const QSize& imageSize, const QSize& originalSize)
0040     : m_zoom         (1.0),
0041       m_zoomConst    (1.0),
0042       m_displayWidget(nullptr)
0043 {
0044     setImageSize(imageSize, originalSize);
0045 }
0046 
0047 void ImageZoomSettings::setImageSize(const QSize& size, const QSize& originalSize)
0048 {
0049     m_size = size;
0050 
0051     if (!originalSize.isNull() && originalSize.isValid())
0052     {
0053         m_zoomConst = m_size.width() / double(originalSize.width());
0054     }
0055     else
0056     {
0057         m_zoomConst = 1.0;
0058     }
0059 }
0060 
0061 void ImageZoomSettings::setDisplayWidget(QWidget* const widget)
0062 {
0063     m_displayWidget = widget;
0064 }
0065 
0066 double ImageZoomSettings::zoomFactor() const
0067 {
0068     return m_zoom;
0069 }
0070 
0071 double ImageZoomSettings::realZoomFactor() const
0072 {
0073     return (m_zoom / displayRatio());
0074 }
0075 
0076 QSizeF ImageZoomSettings::imageSize() const
0077 {
0078     return m_size;
0079 }
0080 
0081 QSizeF ImageZoomSettings::originalImageSize() const
0082 {
0083     return (m_size / m_zoomConst);
0084 }
0085 
0086 QSizeF ImageZoomSettings::zoomedSize() const
0087 {
0088     return (m_size / (m_zoomConst * displayRatio()) * m_zoom);
0089 }
0090 
0091 QRectF ImageZoomSettings::sourceRect(const QRectF& imageRect) const
0092 {
0093     return mapZoomToImage(imageRect);
0094 }
0095 
0096 bool ImageZoomSettings::isFitToSize(const QSizeF& frameSize) const
0097 {
0098     return (zoomFactor() == fitToSizeZoomFactor(frameSize));
0099 }
0100 
0101 void ImageZoomSettings::setZoomFactor(double zoom)
0102 {
0103     m_zoom = zoom;
0104 }
0105 
0106 void ImageZoomSettings::fitToSize(const QSizeF& frameSize, FitToSizeMode mode)
0107 {
0108     setZoomFactor(fitToSizeZoomFactor(frameSize, mode));
0109 }
0110 
0111 double ImageZoomSettings::fitToSizeZoomFactor(const QSizeF& frameSize, FitToSizeMode mode) const
0112 {
0113     if (!frameSize.isValid() || !m_size.isValid())
0114     {
0115         return 1.0;
0116     }
0117 
0118     double zoom;
0119 
0120     if ((frameSize.width() / frameSize.height()) < (m_size.width() / m_size.height()))
0121     {
0122         zoom = m_zoomConst * displayRatio() * frameSize.width() / m_size.width();
0123     }
0124     else
0125     {
0126         zoom = m_zoomConst * displayRatio() * frameSize.height() / m_size.height();
0127     }
0128 
0129     // Zoom rounding down and scroll bars are never activated.
0130 
0131     zoom = floor(zoom * 100000 - 0.1) / 100000.0;
0132 
0133     if (mode == OnlyScaleDown)
0134     {
0135         // OnlyScaleDown: accept that an image is smaller than available space, don't scale up
0136 
0137         if ((frameSize.width() > originalImageSize().width()) && (frameSize.height() > originalImageSize().height()))
0138         {
0139             zoom = 1.0;
0140         }
0141     }
0142 
0143     return zoom;
0144 }
0145 
0146 QRectF ImageZoomSettings::mapZoomToImage(const QRectF& zoomedRect) const
0147 {
0148     return QRectF(zoomedRect.topLeft() / (m_zoom / (m_zoomConst * displayRatio())),
0149                   zoomedRect.size()    / (m_zoom / (m_zoomConst * displayRatio())));
0150 }
0151 
0152 QRectF ImageZoomSettings::mapImageToZoom(const QRectF& imageRect) const
0153 {
0154     return QRectF(imageRect.topLeft() * (m_zoom / (m_zoomConst * displayRatio())),
0155                   imageRect.size()    * (m_zoom / (m_zoomConst * displayRatio())));
0156 }
0157 
0158 QPointF ImageZoomSettings::mapZoomToImage(const QPointF& zoomedPoint) const
0159 {
0160     return (zoomedPoint / (m_zoom / (m_zoomConst * displayRatio())));
0161 }
0162 
0163 QPointF ImageZoomSettings::mapImageToZoom(const QPointF& imagePoint) const
0164 {
0165     return (imagePoint * (m_zoom / (m_zoomConst * displayRatio())));
0166 }
0167 
0168 inline static bool lessThanLimitedPrecision(double a, double b)
0169 {
0170     return lround(a * 100000) < lround(b * 100000);
0171 }
0172 
0173 double ImageZoomSettings::snappedZoomStep(double nextZoom, const QSizeF& frameSize) const
0174 {
0175     // If the zoom value gets changed from d->zoom to zoom
0176     // across 50%, 100% or fit-to-window, then return the
0177     // the corresponding special value. Otherwise zoom is returned unchanged.
0178 
0179     QList<double> snapValues;
0180     snapValues << 0.5;
0181     snapValues << 1.0;
0182 
0183     if (frameSize.isValid())
0184     {
0185         snapValues << fitToSizeZoomFactor(frameSize);
0186     }
0187 
0188     double currentZoom = zoomFactor();
0189 
0190     if (currentZoom < nextZoom)
0191     {
0192         Q_FOREACH (double z, snapValues)
0193         {
0194             if (lessThanLimitedPrecision(currentZoom, z) && lessThanLimitedPrecision(z, nextZoom))
0195             {    // cppcheck-suppress useStlAlgorithm
0196                 return z;
0197             }
0198         }
0199     }
0200     else
0201     {
0202         Q_FOREACH (double z, snapValues)
0203         {
0204             if (lessThanLimitedPrecision(z, currentZoom) && lessThanLimitedPrecision(nextZoom, z))
0205             {   // cppcheck-suppress useStlAlgorithm
0206                 return z;
0207             }
0208         }
0209     }
0210 
0211     return nextZoom;
0212 }
0213 
0214 double ImageZoomSettings::snappedZoomFactor(double zoom, const QSizeF& frameSize) const
0215 {
0216     QList<double> snapValues;
0217     snapValues << 0.5;
0218     snapValues << 1.0;
0219 
0220     if (frameSize.isValid())
0221     {
0222         snapValues << fitToSizeZoomFactor(frameSize);
0223     }
0224 
0225     Q_FOREACH (double z, snapValues)
0226     {
0227         if (fabs(zoom - z) < 0.05)
0228         {   // cppcheck-suppress useStlAlgorithm
0229             return z;
0230         }
0231     }
0232 
0233     return zoom;
0234 }
0235 
0236 double ImageZoomSettings::displayRatio() const
0237 {
0238     if (m_displayWidget)
0239     {
0240         return m_displayWidget->devicePixelRatio();
0241     }
0242 
0243     qCWarning(DIGIKAM_GENERAL_LOG) << "ImageZoomSettings: display widget not set";
0244 
0245     return 1.0;
0246 }
0247 
0248 } // namespace Digikam