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