File indexing completed on 2024-05-12 15:58:48
0001 /* 0002 * SPDX-FileCopyrightText: 2013 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #ifndef __KIS_WRAPPED_RECT_H 0008 #define __KIS_WRAPPED_RECT_H 0009 0010 #include <QVector> 0011 #include <QRect> 0012 #include <QtMath> 0013 0014 struct KisWrappedRect : public QVector<QRect> { 0015 static inline int xToWrappedX(int x, const QRect &wrapRect) { 0016 x = (x - wrapRect.x()) % wrapRect.width(); 0017 if (x < 0) x += wrapRect.width(); 0018 return x; 0019 } 0020 0021 static inline int yToWrappedY(int y, const QRect &wrapRect) { 0022 y = (y - wrapRect.y()) % wrapRect.height(); 0023 if (y < 0) y += wrapRect.height(); 0024 return y; 0025 } 0026 0027 static inline QPoint ptToWrappedPt(QPoint pt, const QRect &wrapRect) { 0028 pt.rx() = xToWrappedX(pt.x(), wrapRect); 0029 pt.ry() = yToWrappedY(pt.y(), wrapRect); 0030 return pt; 0031 } 0032 0033 /** 0034 * Return origins at which we should paint \p rc with crop rect set to \p wrapRect, 0035 * so that the final image would look "wrapped". 0036 */ 0037 static inline QVector<QPoint> normalizationOriginsForRect(const QRect &rc, const QRect &wrapRect) { 0038 QVector<QPoint> result; 0039 0040 if (wrapRect.contains(rc)) { 0041 result.append(rc.topLeft()); 0042 } else { 0043 int x = xToWrappedX(rc.x(), wrapRect); 0044 int y = yToWrappedY(rc.y(), wrapRect); 0045 int w = qMin(rc.width(), wrapRect.width()); 0046 int h = qMin(rc.height(), wrapRect.height()); 0047 0048 // we ensure that the topleft of the rect belongs to the 0049 // visible rectangle 0050 Q_ASSERT(x >= 0 && x < wrapRect.width()); 0051 Q_ASSERT(y >= 0 && y < wrapRect.height()); 0052 0053 QRect newRect(x, y, w, h); 0054 0055 if (!(newRect & wrapRect).isEmpty()) { // tl 0056 result.append(QPoint(x, y)); 0057 } 0058 0059 if (!(newRect.translated(-wrapRect.width(), 0) & wrapRect).isEmpty()) { // tr 0060 result.append(QPoint(x - wrapRect.width(), y)); 0061 } 0062 0063 if (!(newRect.translated(0, -wrapRect.height()) & wrapRect).isEmpty()) { // bl 0064 result.append(QPoint(x, y - wrapRect.height())); 0065 } 0066 0067 if (!(newRect.translated(-wrapRect.width(), -wrapRect.height()) & wrapRect).isEmpty()) { // br 0068 result.append(QPoint(x - wrapRect.width(), y - wrapRect.height())); 0069 } 0070 } 0071 0072 return result; 0073 } 0074 0075 static QVector<QRect> multiplyWrappedRect(const QRect &rc, 0076 const QRect &wrapRect, 0077 const QRect &limitRect) { 0078 0079 QVector<QRect> result; 0080 0081 const int firstCol = qFloor(qreal(limitRect.x() - wrapRect.x()) / wrapRect.width()); 0082 const int firstRow = qFloor(qreal(limitRect.y() - wrapRect.y()) / wrapRect.height()); 0083 0084 const int lastCol = qFloor(qreal(limitRect.right() - wrapRect.x()) / wrapRect.width()); 0085 const int lastRow = qFloor(qreal(limitRect.bottom() - wrapRect.y()) / wrapRect.height()); 0086 0087 KisWrappedRect wrappedRect(rc, wrapRect); 0088 0089 Q_FOREACH (const QRect &rect, wrappedRect) { 0090 if (rect.isEmpty()) continue; 0091 0092 for (int y = firstRow; y <= lastRow; y++) { 0093 for (int x = firstCol; x <= lastCol; x++) { 0094 const QPoint offset(x * wrapRect.width(), y * wrapRect.height()); 0095 const QRect currentRect = 0096 rect.translated(offset + wrapRect.topLeft()) & limitRect; 0097 0098 if (!currentRect.isEmpty()) { 0099 result << currentRect; 0100 } 0101 } 0102 } 0103 } 0104 0105 return result; 0106 } 0107 0108 public: 0109 0110 enum { 0111 TOPLEFT = 0, 0112 TOPRIGHT, 0113 BOTTOMLEFT, 0114 BOTTOMRIGHT 0115 }; 0116 0117 KisWrappedRect(const QRect &rc, const QRect &wrapRect) 0118 : m_wrapRect(wrapRect), 0119 m_originalRect(rc) 0120 { 0121 if (wrapRect.contains(rc)) { 0122 append(rc); 0123 } else { 0124 int x = xToWrappedX(rc.x(), wrapRect); 0125 int y = yToWrappedY(rc.y(), wrapRect); 0126 int w = qMin(rc.width(), wrapRect.width()); 0127 int h = qMin(rc.height(), wrapRect.height()); 0128 0129 // we ensure that the topleft of the rect belongs to the 0130 // visible rectangle 0131 Q_ASSERT(x >= 0 && x < wrapRect.width()); 0132 Q_ASSERT(y >= 0 && y < wrapRect.height()); 0133 0134 QRect newRect(x, y, w, h); 0135 0136 append(newRect & wrapRect); // tl 0137 append(newRect.translated(-wrapRect.width(), 0) & wrapRect); // tr 0138 append(newRect.translated(0, -wrapRect.height()) & wrapRect); // bl 0139 append(newRect.translated(-wrapRect.width(), -wrapRect.height()) & wrapRect); // br 0140 } 0141 } 0142 0143 bool isSplit() const { 0144 int size = this->size(); 0145 0146 // we can either split or not split only 0147 Q_ASSERT(size == 1 || size == 4); 0148 0149 return size > 1; 0150 } 0151 0152 QRect topLeft() const { 0153 return this->at(TOPLEFT); 0154 } 0155 0156 QRect topRight() const { 0157 return this->at(TOPRIGHT); 0158 } 0159 0160 QRect bottomLeft() const { 0161 return this->at(BOTTOMLEFT); 0162 } 0163 0164 QRect bottomRight() const { 0165 return this->at(BOTTOMRIGHT); 0166 } 0167 0168 QRect wrapRect() const { 0169 return m_wrapRect; 0170 } 0171 0172 QRect originalRect() const { 0173 return m_originalRect; 0174 } 0175 0176 private: 0177 QRect m_wrapRect; 0178 QRect m_originalRect; 0179 }; 0180 0181 #endif /* __KIS_WRAPPED_RECT_H */