File indexing completed on 2024-05-12 15:57:03

0001 /*
0002  *  SPDX-FileCopyrightText: 2021 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "KisRectsGrid.h"
0008 #include "kis_assert.h"
0009 #include <QtCore/qmath.h>
0010 #include "kis_lod_transform_base.h"
0011 #include "kis_global.h"
0012 #include "kis_algebra_2d.h"
0013 #include <KisUsageLogger.h>
0014 
0015 #include "kis_debug.h"
0016 
0017 
0018 KisRectsGrid::KisRectsGrid(int gridSize)
0019     : m_gridSize(gridSize)
0020     , m_logGridSize(qFloor(std::log2(gridSize)))
0021 {
0022     if (!qFuzzyCompare(std::log2(gridSize), qreal(m_logGridSize))) {
0023         KisUsageLogger::log(QString("Invalid grid configuration. Grid size: %1, log grid size: %2. Resetting to 64 and 6").arg(gridSize, m_logGridSize));
0024         m_gridSize = 64;
0025         m_logGridSize = 6;
0026     }
0027 }
0028 
0029 void KisRectsGrid::resize(const QRect &newMappedAreaSize)
0030 {
0031     KIS_SAFE_ASSERT_RECOVER_NOOP(m_mappedAreaSize.isEmpty() || newMappedAreaSize.contains(m_mappedAreaSize));
0032 
0033     QVector<quint8> newMapping(newMappedAreaSize.width() * newMappedAreaSize.height());
0034 
0035     const int xDiff = m_mappedAreaSize.x() - newMappedAreaSize.x();
0036     const int yDiff = m_mappedAreaSize.y() - newMappedAreaSize.y();
0037 
0038     int dstRowStride = newMappedAreaSize.width();
0039     int dstRowStart = xDiff + yDiff * dstRowStride;
0040 
0041     for (int y = 0; y < m_mappedAreaSize.height(); y++) {
0042         int dstRowIndex = dstRowStart + dstRowStride * y;
0043         int srcRowIndex = m_mappedAreaSize.width() * y;
0044 
0045         memcpy(&newMapping[dstRowIndex], &m_mapping[srcRowIndex], m_mappedAreaSize.width());
0046     }
0047 
0048     std::swap(newMapping, m_mapping);
0049     m_mappedAreaSize = newMappedAreaSize;
0050 }
0051 
0052 QRect KisRectsGrid::alignRect(const QRect &rc) const
0053 {
0054     return KisLodTransformBase::alignedRect(rc, m_logGridSize);
0055 }
0056 
0057 QVector<QRect> KisRectsGrid::addRect(const QRect &rc)
0058 {
0059     return addAlignedRect(alignRect(rc));
0060 }
0061 
0062 QVector<QRect> KisRectsGrid::addAlignedRect(const QRect &rc)
0063 {
0064     if (rc.isEmpty()) return QVector<QRect>();
0065 
0066     const QRect mappedRect = KisLodTransformBase::scaledRect(rc, m_logGridSize);
0067 
0068     if (!m_mappedAreaSize.contains(mappedRect)) {
0069         QRect nextMappingSize = m_mappedAreaSize | mappedRect;
0070         nextMappingSize = KisAlgebra2D::blowRect(nextMappingSize, 0.2);
0071         resize(nextMappingSize);
0072     }
0073 
0074     QVector<QRect> addedRects;
0075 
0076     for (int y = mappedRect.y(); y <= mappedRect.bottom(); y++) {
0077         for (int x = mappedRect.x(); x <= mappedRect.right(); x++) {
0078             quint8 *ptr = &m_mapping[m_mappedAreaSize.width() * (y - m_mappedAreaSize.y()) + (x - m_mappedAreaSize.x())];
0079             if (!*ptr) {
0080                 *ptr = 1;
0081                 addedRects.append(KisLodTransformBase::upscaledRect(QRect(x, y, 1, 1), m_logGridSize));
0082             }
0083         }
0084     }
0085     return addedRects;
0086 }
0087 
0088 inline QRect KisRectsGrid::shrinkRectToAlignedGrid(const QRect &srcRect, int lod)
0089 {
0090     qint32 alignment = 1 << lod;
0091 
0092     qint32 x1, y1, x2, y2;
0093     srcRect.getCoords(&x1, &y1, &x2, &y2);
0094 
0095     x1--;
0096     y1--;
0097     x2++;
0098     y2++;
0099 
0100     KisLodTransformBase::alignByPow2ButOneHi(x1, alignment);
0101     KisLodTransformBase::alignByPow2ButOneHi(y1, alignment);
0102 
0103     KisLodTransformBase::alignByPow2Lo(x2, alignment);
0104     KisLodTransformBase::alignByPow2Lo(y2, alignment);
0105 
0106     x1++;
0107     y1++;
0108     x2--;
0109     y2--;
0110 
0111     QRect rect;
0112     rect.setCoords(x1, y1, x2, y2);
0113 
0114     return rect;
0115 }
0116 
0117 QVector<QRect> KisRectsGrid::removeRect(const QRect &rc)
0118 {
0119     const QRect alignedRect = shrinkRectToAlignedGrid(rc, m_logGridSize);
0120     return !alignedRect.isEmpty() ? removeAlignedRect(alignedRect) : QVector<QRect>();
0121 }
0122 
0123 QVector<QRect> KisRectsGrid::removeAlignedRect(const QRect &rc)
0124 {
0125     const QRect mappedRect = KisLodTransformBase::scaledRect(rc, m_logGridSize);
0126 
0127     // NOTE: we never shrink the size of the grid, just keep it as big as
0128     //       it ever was
0129 
0130     QVector<QRect> removedRects;
0131 
0132     for (int y = mappedRect.y(); y <= mappedRect.bottom(); y++) {
0133         for (int x = mappedRect.x(); x <= mappedRect.right(); x++) {
0134             quint8 *ptr = &m_mapping[m_mappedAreaSize.width() * (y - m_mappedAreaSize.y()) + (x - m_mappedAreaSize.x())];
0135             if (*ptr) {
0136                 *ptr = 0;
0137                 removedRects.append(KisLodTransformBase::upscaledRect(QRect(x, y, 1, 1), m_logGridSize));
0138             }
0139         }
0140     }
0141     return removedRects;
0142 }
0143 
0144 bool KisRectsGrid::contains(const QRect &rc) const
0145 {
0146     const QRect mappedRect = KisLodTransformBase::scaledRect(alignRect(rc), m_logGridSize);
0147 
0148     if (!m_mappedAreaSize.contains(mappedRect)) return false;
0149 
0150     for (int y = mappedRect.y(); y <= mappedRect.bottom(); y++) {
0151         for (int x = mappedRect.x(); x <= mappedRect.right(); x++) {
0152             const quint8 *ptr = &m_mapping[m_mappedAreaSize.width() * (y - m_mappedAreaSize.y()) + (x - m_mappedAreaSize.x())];
0153             if (!*ptr) return false;
0154         }
0155     }
0156 
0157     return true;
0158 }
0159 
0160 QRect KisRectsGrid::boundingRect() const {
0161     QRect gridBounds;
0162 
0163     for (int y = m_mappedAreaSize.y(); y <= m_mappedAreaSize.bottom(); y++) {
0164         for (int x = m_mappedAreaSize.x(); x <= m_mappedAreaSize.right(); x++) {
0165             gridBounds |= QRect(x, y, 1, 1);
0166         }
0167     }
0168 
0169     return KisLodTransformBase::upscaledRect(gridBounds, m_logGridSize);
0170 }