File indexing completed on 2024-05-19 04:26:40
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 #include "KisWraparoundAxis.h" 0015 0016 struct KisWrappedRect : public QVector<QRect> { 0017 static inline int xToWrappedX(int x, const QRect &wrapRect, WrapAroundAxis wrapAxis) { 0018 if (wrapAxis == WRAPAROUND_VERTICAL) { 0019 return x; 0020 } 0021 x = (x - wrapRect.x()) % wrapRect.width(); 0022 if (x < 0) x += wrapRect.width(); 0023 return x; 0024 } 0025 0026 static inline int yToWrappedY(int y, const QRect &wrapRect, WrapAroundAxis wrapAxis) { 0027 if (wrapAxis == WRAPAROUND_HORIZONTAL) { 0028 return y; 0029 } 0030 y = (y - wrapRect.y()) % wrapRect.height(); 0031 if (y < 0) y += wrapRect.height(); 0032 return y; 0033 } 0034 0035 static inline QPoint ptToWrappedPt(QPoint pt, const QRect &wrapRect, WrapAroundAxis wrapAxis) { 0036 pt.rx() = xToWrappedX(pt.x(), wrapRect, wrapAxis); 0037 pt.ry() = yToWrappedY(pt.y(), wrapRect, wrapAxis); 0038 return pt; 0039 } 0040 0041 static inline QRect clipToWrapRect(QRect rc, const QRect &wrapRect, WrapAroundAxis wrapAxis) { 0042 switch (wrapAxis) { 0043 case WRAPAROUND_HORIZONTAL: 0044 { 0045 if (rc.left() < wrapRect.left()) { 0046 rc.setLeft(wrapRect.left()); 0047 } 0048 if (rc.right() > wrapRect.right()) { 0049 rc.setRight(wrapRect.right()); 0050 } 0051 return rc; 0052 } 0053 case WRAPAROUND_VERTICAL: 0054 { 0055 if (rc.top() < wrapRect.top()) { 0056 rc.setTop(wrapRect.top()); 0057 } 0058 if (rc.bottom() > wrapRect.bottom()) { 0059 rc.setBottom(wrapRect.bottom()); 0060 } 0061 return rc; 0062 } 0063 default /*WRAPAROUND_BOTH*/: 0064 return rc & wrapRect; 0065 } 0066 } 0067 0068 static inline bool wrapRectContains(const QRect &rc, const QRect &wrapRect, WrapAroundAxis wrapAxis) { 0069 switch (wrapAxis) { 0070 case WRAPAROUND_HORIZONTAL: 0071 return (rc.left() >= wrapRect.left() && rc.right() <= wrapRect.right()); 0072 case WRAPAROUND_VERTICAL: 0073 return (rc.top() >= wrapRect.top() && rc.bottom() <= wrapRect.bottom()); 0074 default /*WRAPAROUND_BOTH*/: 0075 return wrapRect.contains(rc); 0076 } 0077 } 0078 static inline bool wrapRectContains(QPoint pt, const QRect &wrapRect, WrapAroundAxis wrapAxis) { 0079 switch (wrapAxis) { 0080 case WRAPAROUND_HORIZONTAL: 0081 return (pt.x() >= wrapRect.left() && pt.x() <= wrapRect.right()); 0082 case WRAPAROUND_VERTICAL: 0083 return (pt.y() >= wrapRect.top() && pt.y() <= wrapRect.bottom()); 0084 default /*WRAPAROUND_BOTH*/: 0085 return wrapRect.contains(pt); 0086 } 0087 } 0088 0089 /** 0090 * Return origins at which we should paint \p rc with crop rect set to \p wrapRect, 0091 * so that the final image would look "wrapped". 0092 */ 0093 static inline QVector<QPoint> normalizationOriginsForRect(const QRect &rc, const QRect &wrapRect, WrapAroundAxis wrapAxis) { 0094 QVector<QPoint> result; 0095 0096 if (wrapRectContains(rc, wrapRect, wrapAxis)) { 0097 result.append(rc.topLeft()); 0098 } 0099 else { 0100 int x = xToWrappedX(rc.x(), wrapRect, wrapAxis); 0101 int y = yToWrappedY(rc.y(), wrapRect, wrapAxis); 0102 int w = wrapAxis != WRAPAROUND_VERTICAL ? qMin(rc.width(), wrapRect.width()) : rc.width(); 0103 int h = wrapAxis != WRAPAROUND_HORIZONTAL ? qMin(rc.height(), wrapRect.height()) : rc.height(); 0104 0105 // we ensure that the top/left of the rect belongs to the 0106 // visible rectangle 0107 Q_ASSERT(wrapRectContains(QPoint(x,y), wrapRect, wrapAxis)); 0108 0109 QRect newRect(x, y, w, h); 0110 0111 if (!clipToWrapRect(newRect, wrapRect, wrapAxis).isEmpty()) { 0112 result.append(QPoint(x, y)); // tl 0113 } 0114 0115 if (wrapAxis != WRAPAROUND_VERTICAL && 0116 !clipToWrapRect(newRect.translated(-wrapRect.width(), 0), wrapRect, wrapAxis).isEmpty()) { // tr 0117 result.append(QPoint(x - wrapRect.width(), y)); 0118 } 0119 0120 if (wrapAxis != WRAPAROUND_HORIZONTAL && 0121 !clipToWrapRect(newRect.translated(0, -wrapRect.height()), wrapRect, wrapAxis).isEmpty()) { // bl 0122 result.append(QPoint(x, y - wrapRect.height())); 0123 } 0124 0125 if (wrapAxis == WRAPAROUND_BOTH && 0126 !clipToWrapRect(newRect.translated(-wrapRect.width(), -wrapRect.height()), wrapRect, wrapAxis).isEmpty()) { // br 0127 result.append(QPoint(x - wrapRect.width(), y - wrapRect.height())); 0128 } 0129 } 0130 0131 return result; 0132 } 0133 0134 static QVector<QRect> multiplyWrappedRect(const QRect &rc, 0135 const QRect &wrapRect, 0136 const QRect &limitRect, 0137 WrapAroundAxis wrapAxis) { 0138 0139 QVector<QRect> result; 0140 0141 const int firstCol = 0142 wrapAxis != WRAPAROUND_VERTICAL ? qFloor(qreal(limitRect.x() - wrapRect.x()) / wrapRect.width()) : 0; 0143 const int firstRow = 0144 wrapAxis != WRAPAROUND_HORIZONTAL ? qFloor(qreal(limitRect.y() - wrapRect.y()) / wrapRect.height()) : 0; 0145 0146 const int lastCol = 0147 wrapAxis != WRAPAROUND_VERTICAL ? qFloor(qreal(limitRect.right() - wrapRect.x()) / wrapRect.width()) : 0; 0148 const int lastRow = 0149 wrapAxis != WRAPAROUND_HORIZONTAL ? qFloor(qreal(limitRect.bottom() - wrapRect.y()) / wrapRect.height()) : 0; 0150 0151 KisWrappedRect wrappedRect(rc, wrapRect, wrapAxis); 0152 0153 Q_FOREACH (const QRect &rect, wrappedRect) { 0154 if (rect.isEmpty()) continue; 0155 0156 for (int y = firstRow; y <= lastRow; y++) { 0157 for (int x = firstCol; x <= lastCol; x++) { 0158 const QPoint offset(x * wrapRect.width(), y * wrapRect.height()); 0159 const QRect currentRect = 0160 rect.translated(offset + wrapRect.topLeft()) & limitRect; 0161 0162 if (!currentRect.isEmpty()) { 0163 result << currentRect; 0164 } 0165 } 0166 } 0167 } 0168 0169 return result; 0170 } 0171 0172 public: 0173 0174 enum { 0175 TOPLEFT = 0, 0176 TOPRIGHT, 0177 BOTTOMLEFT, 0178 BOTTOMRIGHT 0179 }; 0180 0181 KisWrappedRect(const QRect &rc, const QRect &wrapRect, WrapAroundAxis wrapAxis) 0182 : m_wrapRect(wrapRect), 0183 m_originalRect(rc) 0184 { 0185 if (wrapRectContains(rc, wrapRect, wrapAxis)) { 0186 append(rc); 0187 } else { 0188 int x = xToWrappedX(rc.x(), wrapRect, wrapAxis); 0189 int y = yToWrappedY(rc.y(), wrapRect, wrapAxis); 0190 int w = wrapAxis != WRAPAROUND_VERTICAL ? qMin(rc.width(), wrapRect.width()) : rc.width(); 0191 int h = wrapAxis != WRAPAROUND_HORIZONTAL ? qMin(rc.height(), wrapRect.height()) : rc.height(); 0192 0193 // we ensure that the top/left of the rect belongs to the 0194 // visible rectangle 0195 Q_ASSERT(wrapRectContains(QPoint(x,y), wrapRect, wrapAxis)); 0196 0197 QRect newRect(x, y, w, h); 0198 0199 // We add empty QRects here because a "splitRect" is expected to contain exactly 4 rects. 0200 // Functions such as KisPaintDeviceWrappedStrategy::readBytes() and 0201 // KisWrappedLineIteratorBase() will not work properly (read: crash) otherwise. 0202 append(clipToWrapRect(newRect, wrapRect, wrapAxis)); // tl 0203 append(wrapAxis != WRAPAROUND_VERTICAL ? 0204 clipToWrapRect(newRect.translated(-wrapRect.width(), 0), wrapRect, wrapAxis) : QRect()); // tr 0205 append(wrapAxis != WRAPAROUND_HORIZONTAL ? 0206 clipToWrapRect(newRect.translated(0, -wrapRect.height()), wrapRect, wrapAxis) : QRect()); // bl 0207 append(wrapAxis == WRAPAROUND_BOTH ? 0208 clipToWrapRect(newRect.translated(-wrapRect.width(), -wrapRect.height()), wrapRect, wrapAxis) : QRect()); // br 0209 } 0210 } 0211 0212 bool isSplit() const { 0213 int size = this->size(); 0214 0215 // we can either split or not split only 0216 Q_ASSERT(size == 1 || size == 4); 0217 0218 return size > 1; 0219 } 0220 0221 QRect topLeft() const { 0222 return this->at(TOPLEFT); 0223 } 0224 0225 QRect topRight() const { 0226 return this->at(TOPRIGHT); 0227 } 0228 0229 QRect bottomLeft() const { 0230 return this->at(BOTTOMLEFT); 0231 } 0232 0233 QRect bottomRight() const { 0234 return this->at(BOTTOMRIGHT); 0235 } 0236 0237 QRect wrapRect() const { 0238 return m_wrapRect; 0239 } 0240 0241 QRect originalRect() const { 0242 return m_originalRect; 0243 } 0244 0245 private: 0246 QRect m_wrapRect; 0247 QRect m_originalRect; 0248 }; 0249 0250 #endif /* __KIS_WRAPPED_RECT_H */