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 */