File indexing completed on 2024-05-12 15:58:42

0001 /*
0002  *  SPDX-FileCopyrightText: 2014 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #ifndef __KIS_SEQUENTIAL_ITERATOR_H
0008 #define __KIS_SEQUENTIAL_ITERATOR_H
0009 
0010 #include <KoAlwaysInline.h>
0011 
0012 #include "kis_types.h"
0013 #include "kis_paint_device.h"
0014 #include "kis_iterator_ng.h"
0015 
0016 
0017 struct DevicePolicy {
0018     DevicePolicy(KisPaintDeviceSP dev) : m_dev(dev) {}
0019 
0020     template <typename Convertible>
0021     DevicePolicy(Convertible sel) : m_dev(sel) {}
0022 
0023     KisHLineConstIteratorSP createConstIterator(const QRect &rect) {
0024         return m_dev->createHLineConstIteratorNG(rect.x(), rect.y(), rect.width());
0025     }
0026 
0027     KisHLineIteratorSP createIterator(const QRect &rect) {
0028         return m_dev->createHLineIteratorNG(rect.x(), rect.y(), rect.width());
0029     }
0030 
0031     int pixelSize() const {
0032         return m_dev->pixelSize();
0033     }
0034 
0035     KisPaintDeviceSP m_dev;
0036 };
0037 
0038 template <class SourcePolicy = DevicePolicy>
0039 struct ReadOnlyIteratorPolicy {
0040     typedef KisHLineConstIteratorSP IteratorTypeSP;
0041 
0042     ReadOnlyIteratorPolicy(SourcePolicy source, const QRect &rect) {
0043         m_iter = !rect.isEmpty() ? source.createConstIterator(rect) : 0;
0044     }
0045 
0046     ALWAYS_INLINE void updatePointersCache() {
0047         m_rawDataConst = m_iter ? m_iter->rawDataConst() : 0;
0048         m_oldRawData = m_iter ? m_iter->oldRawData() : 0;
0049     }
0050 
0051     ALWAYS_INLINE const quint8* rawDataConst() const {
0052         return m_rawDataConst;
0053     }
0054 
0055     ALWAYS_INLINE const quint8* oldRawData() const {
0056         return m_oldRawData;
0057     }
0058 
0059     IteratorTypeSP m_iter;
0060 
0061 private:
0062     const quint8 *m_rawDataConst {nullptr};
0063     const quint8 *m_oldRawData {nullptr};
0064 };
0065 
0066 template <class SourcePolicy = DevicePolicy>
0067 struct WritableIteratorPolicy {
0068     typedef KisHLineIteratorSP IteratorTypeSP;
0069 
0070     WritableIteratorPolicy(SourcePolicy source, const QRect &rect) {
0071         m_iter = !rect.isEmpty() ? source.createIterator(rect) : 0;
0072     }
0073 
0074     ALWAYS_INLINE void updatePointersCache() {
0075         m_rawData = m_iter ? m_iter->rawData() : 0;
0076         m_oldRawData = m_iter ? m_iter->oldRawData() : 0;
0077     }
0078 
0079     ALWAYS_INLINE quint8* rawData() {
0080         return m_rawData;
0081     }
0082 
0083     ALWAYS_INLINE const quint8* rawDataConst() const {
0084         return m_rawData;
0085     }
0086 
0087     ALWAYS_INLINE const quint8* oldRawData() const {
0088         return m_oldRawData;
0089     }
0090 
0091     IteratorTypeSP m_iter;
0092 
0093 private:
0094     quint8 *m_rawData {nullptr};
0095     const quint8 *m_oldRawData {nullptr};
0096 };
0097 
0098 struct NoProgressPolicy
0099 {
0100     ALWAYS_INLINE void setRange(int /* minimum */, int /* maximum */)
0101     {
0102     }
0103 
0104     ALWAYS_INLINE void setValue(int /* value */)
0105     {
0106     }
0107 
0108     ALWAYS_INLINE void setFinished()
0109     {
0110     }
0111 };
0112 
0113 /**
0114  * Sequential iterator is supposed to be used when you need to
0115  * read/write a rect of the image and you don't want to think about
0116  * row or column nested loops. For the sequential iterator you will
0117  * need a single loop: the data will be read line-by-line using an
0118  * internal hline iterator. Please note that thanks to inline
0119  * optimizations inside the sequential iterator when doing
0120  * pixel-by-pixel processing it is about twice faster(!)  than a usual
0121  * hline iterator.
0122  *
0123  * The follows the "java-style" iterators rules. Before requesting the
0124  * first pixel from the iterator you should call nextPixel() to "jump over"
0125  * this first pixel. After the jump is accomplished, you can easily request
0126  * the "jumped over" pixel data.
0127  *
0128  * The modified rules apply when the user wants accesses consequent pixels
0129  * in one go. The user first asks the iterator for the number of available
0130  * consequent pixels, and then calls nextPixels(numConseqPixels). In this
0131  * case, iterator inserts a "virtual" pixel that one should jump over before
0132  * doing any real iteration.
0133  *
0134  * Iteration in pixel-by-pixel manner:
0135  *
0136  * \code{.cpp}
0137  * KisSequentialConstIterator it(dev, rect);
0138  * while (it.nextPixel()) {
0139  *     quint *ptr = it.rawDataConst();
0140  *     // work with ptr...
0141  * }
0142  * \endcode
0143  *
0144  * Iteration with strides:
0145  *
0146  * \code{.cpp}
0147  * KisSequentialConstIterator it(dev, rect);
0148  *
0149  * // Here we jump over the first "virtual" pixel,
0150  * // which helps us to avoid an empty rect problem
0151  *
0152  * int numConseqPixels = it.nConseqPixels();
0153  * while (it.nextPixels(numConseqPixels)) {
0154  *
0155  *     // get real number of conseq pixels
0156  *
0157  *     numConseqPixels = it.nConseqPixels();
0158  *     quint *ptr = it.rawDataConst();
0159  *
0160  *     // process the data
0161  *     processPixelData(tr, numConseqPixels);
0162  * }
0163  * \endcode
0164  *
0165  *
0166  * Implementation:
0167  *
0168  * The iterator is implemented using a policy pattern. The class
0169  * itself is a template which accepts a special class (policy) that
0170  * defines: 1) which type of the hline iterator will be used; 2) what
0171  * methods of the internal hline iterator will be called. The choice
0172  * of the policy declares whether the iterator will be writable or
0173  * const.
0174  */
0175 
0176 template <class IteratorPolicy, class SourcePolicy = DevicePolicy, class ProgressPolicy = NoProgressPolicy>
0177 class KisSequentialIteratorBase
0178 {
0179 public:
0180     KisSequentialIteratorBase(SourcePolicy source, const QRect &rect, ProgressPolicy progressPolicy = ProgressPolicy())
0181         : m_policy(source, rect),
0182           m_progressPolicy(progressPolicy),
0183           m_pixelSize(source.pixelSize()),
0184           m_rowsLeft(rect.height() - 1),
0185           m_columnOffset(0),
0186           m_iteratorX(0),
0187           m_iteratorY(0),
0188           m_isStarted(false)
0189     {
0190         m_columnsLeft = m_numConseqPixels =
0191             m_policy.m_iter ? m_policy.m_iter->nConseqPixels() : 0;
0192 
0193         m_policy.updatePointersCache();
0194         m_iteratorX = m_policy.m_iter ? m_policy.m_iter->x() : 0;
0195         m_iteratorY = m_policy.m_iter ? m_policy.m_iter->y() : 0;
0196 
0197         m_progressPolicy.setRange(rect.top(), rect.top() + rect.height());
0198         m_progressPolicy.setValue(rect.top());
0199     }
0200 
0201     ~KisSequentialIteratorBase() {
0202         m_progressPolicy.setFinished();
0203     }
0204 
0205     inline int nConseqPixels() const {
0206         return m_isStarted ? m_columnsLeft : 1;
0207     }
0208 
0209     inline bool nextPixels(int numPixels) {
0210         // leave one step for the nextPixel() call
0211         numPixels--;
0212 
0213         m_columnsLeft -= numPixels;
0214         m_columnOffset += numPixels * m_pixelSize;
0215 
0216         return nextPixel();
0217     }
0218 
0219     inline bool nextPixel() {
0220         if (!m_isStarted) {
0221             m_isStarted = true;
0222             return m_policy.m_iter;
0223         }
0224 
0225         m_columnsLeft--;
0226 
0227         if (m_columnsLeft > 0) {
0228             m_columnOffset += m_pixelSize;
0229             return true;
0230         } else {
0231             bool result = m_policy.m_iter->nextPixels(m_numConseqPixels);
0232             if (result) {
0233                 m_columnOffset = 0;
0234                 m_columnsLeft = m_numConseqPixels = m_policy.m_iter->nConseqPixels();
0235                 m_policy.updatePointersCache();
0236             } else if (m_rowsLeft > 0) {
0237                 m_rowsLeft--;
0238                 m_policy.m_iter->nextRow();
0239                 m_columnOffset = 0;
0240                 m_columnsLeft = m_numConseqPixels = m_policy.m_iter->nConseqPixels();
0241                 m_policy.updatePointersCache();
0242                 m_progressPolicy.setValue(m_policy.m_iter->y());
0243             } else if (m_rowsLeft == 0) {
0244                 // report that we have completed iteration
0245                 m_progressPolicy.setValue(m_policy.m_iter->y() + 1);
0246             }
0247 
0248             m_iteratorX = m_policy.m_iter->x();
0249             m_iteratorY = m_policy.m_iter->y();
0250         }
0251         return m_columnsLeft > 0;
0252     }
0253 
0254 
0255     ALWAYS_INLINE int x() const {
0256         return m_iteratorX + m_numConseqPixels - m_columnsLeft;
0257     }
0258 
0259     ALWAYS_INLINE int y() const {
0260         return m_iteratorY;
0261     }
0262 
0263     // SFINAE: This method becomes undefined for const version of the
0264     //         iterator automatically
0265     ALWAYS_INLINE quint8* rawData() {
0266         return m_policy.rawData() + m_columnOffset;
0267     }
0268 
0269     ALWAYS_INLINE const quint8* rawDataConst() const {
0270         return m_policy.rawDataConst() + m_columnOffset;
0271     }
0272 
0273     ALWAYS_INLINE const quint8* oldRawData() const {
0274         return m_policy.oldRawData() + m_columnOffset;
0275     }
0276 
0277 private:
0278     Q_DISABLE_COPY(KisSequentialIteratorBase)
0279     IteratorPolicy m_policy;
0280     ProgressPolicy m_progressPolicy;
0281     const int m_pixelSize;
0282     int m_rowsLeft;
0283 
0284     int m_numConseqPixels;
0285     int m_columnsLeft;
0286 
0287     int m_columnOffset;
0288     int m_iteratorX;
0289     int m_iteratorY;
0290 
0291     bool m_isStarted;
0292 };
0293 
0294 typedef KisSequentialIteratorBase<ReadOnlyIteratorPolicy<> > KisSequentialConstIterator;
0295 typedef KisSequentialIteratorBase<WritableIteratorPolicy<> > KisSequentialIterator;
0296 
0297 #endif /* __KIS_SEQUENTIAL_ITERATOR_H */