File indexing completed on 2024-05-12 15:56:58

0001 /*
0002  *  SPDX-FileCopyrightText: 2014 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #ifndef KIS_LOD_TRANSFORM_BASE_H
0008 #define KIS_LOD_TRANSFORM_BASE_H
0009 
0010 
0011 #include <QtCore/qmath.h>
0012 #include <QTransform>
0013 
0014 
0015 #include <kritaglobal_export.h>
0016 
0017 class KRITAGLOBAL_EXPORT KisLodTransformBase {
0018 public:
0019     KisLodTransformBase(int levelOfDetail) {
0020         qreal scale = lodToScale(levelOfDetail);
0021         m_transform = QTransform::fromScale(scale, scale);
0022         m_levelOfDetail = levelOfDetail;
0023     }
0024 
0025 
0026 
0027     static int scaleToLod(qreal scale, int maxLod) {
0028         return qMin(maxLod, qMax(0, qFloor(std::log2(1.0 / scale))));
0029     }
0030 
0031     static qreal lodToScale(int levelOfDetail) {
0032         return levelOfDetail > 0 ? 1.0 / (1 << qMax(0, levelOfDetail)) : 1.0;
0033     }
0034 
0035     static qreal lodToInvScale(int levelOfDetail) {
0036         return 1 << qMax(0, levelOfDetail);
0037     }
0038 
0039     template <class PaintDeviceTypeSP>
0040     static qreal lodToScale(PaintDeviceTypeSP device) {
0041         return lodToScale(device->defaultBounds()->currentLevelOfDetail());
0042     }
0043 
0044     QRectF map(const QRectF &rc) const {
0045         return m_transform.mapRect(rc);
0046     }
0047 
0048     QRect map(const QRect &rc) const {
0049         return m_transform.mapRect(rc);
0050     }
0051 
0052     QRectF mapInverted(const QRectF &rc) const {
0053         return m_transform.inverted().mapRect(rc);
0054     }
0055 
0056     QRect mapInverted(const QRect &rc) const {
0057         return m_transform.inverted().mapRect(rc);
0058     }
0059 
0060 
0061 
0062     template <class T>
0063     T map(const T &object) const {
0064         return m_transform.map(object);
0065     }
0066 
0067     static inline QRect alignedRect(const QRect &srcRect, int lod)
0068     {
0069         qint32 alignment = 1 << lod;
0070 
0071         qint32 x1, y1, x2, y2;
0072         srcRect.getCoords(&x1, &y1, &x2, &y2);
0073 
0074         alignByPow2Lo(x1, alignment);
0075         alignByPow2Lo(y1, alignment);
0076 
0077         /**
0078          * Here is a workaround of Qt's QRect::right()/bottom()
0079          * "historical reasons". It should be one pixel smaller
0080          * than actual right/bottom position
0081          */
0082         alignByPow2ButOneHi(x2, alignment);
0083         alignByPow2ButOneHi(y2, alignment);
0084 
0085         QRect rect;
0086         rect.setCoords(x1, y1, x2, y2);
0087 
0088         return rect;
0089     }
0090 
0091     static inline QRect scaledRect(const QRect &srcRect, int lod) {
0092         qint32 x1, y1, x2, y2;
0093         srcRect.getCoords(&x1, &y1, &x2, &y2);
0094 
0095         KIS_ASSERT_RECOVER_NOOP(!(x1 & 1));
0096         KIS_ASSERT_RECOVER_NOOP(!(y1 & 1));
0097         KIS_ASSERT_RECOVER_NOOP(!((x2 + 1) & 1));
0098         KIS_ASSERT_RECOVER_NOOP(!((y2 + 1) & 1));
0099 
0100         x1 = divideSafe(x1, lod);
0101         y1 = divideSafe(y1, lod);
0102         x2 = divideSafe(x2 + 1, lod) - 1;
0103         y2 = divideSafe(y2 + 1, lod) - 1;
0104 
0105         QRect rect;
0106         rect.setCoords(x1, y1, x2, y2);
0107 
0108         return rect;
0109     }
0110 
0111     static QRect upscaledRect(const QRect &srcRect, int lod) {
0112         qint32 x1, y1, x2, y2;
0113         srcRect.getCoords(&x1, &y1, &x2, &y2);
0114 
0115         x2++;
0116         y2++;
0117 
0118         x1 <<= lod;
0119         y1 <<= lod;
0120         x2 <<= lod;
0121         y2 <<= lod;
0122 
0123         x2--;
0124         y2--;
0125 
0126         QRect rect;
0127         rect.setCoords(x1, y1, x2, y2);
0128 
0129         return rect;
0130     }
0131 
0132     static inline int coordToLodCoord(int x, int lod) {
0133         return divideSafe(x, lod);
0134     }
0135 
0136     QTransform transform() const {
0137         return m_transform;
0138     }
0139 
0140 private:
0141     friend class KisRectsGrid;
0142     /**
0143      * Aligns @value to the lowest integer not smaller than @value and
0144      * that is, increased by one, a divident of alignment
0145      */
0146     static inline void alignByPow2ButOneHi(qint32 &value, qint32 alignment)
0147     {
0148         qint32 mask = alignment - 1;
0149         value |= mask;
0150     }
0151 
0152     /**
0153      * Aligns @value to the highest integer not exceeding @value and
0154      * that is a divident of @alignment
0155      */
0156     static inline void alignByPow2Lo(qint32 &value, qint32 alignment)
0157     {
0158         qint32 mask = alignment - 1;
0159          value &= ~mask;
0160     }
0161 
0162     static inline int divideSafe(int x, int lod) {
0163         return x > 0 ? x >> lod : -( -x >> lod);
0164     }
0165 
0166 protected:
0167     QTransform m_transform;
0168     int m_levelOfDetail;
0169 };
0170 
0171 class KisLodTransformScalar {
0172 public:
0173     KisLodTransformScalar(int lod) {
0174         m_scale = KisLodTransformBase::lodToScale(lod);
0175     }
0176 
0177     template <class PaintDeviceTypeSP>
0178     KisLodTransformScalar(PaintDeviceTypeSP device) {
0179         m_scale = KisLodTransformBase::lodToScale(device->defaultBounds()->currentLevelOfDetail());
0180     }
0181 
0182     qreal scale(qreal value) const {
0183         return m_scale * value;
0184     }
0185 
0186 private:
0187     qreal m_scale;
0188 };
0189 
0190 #endif // KIS_LOD_TRANSFORM_BASE_H