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