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