File indexing completed on 2024-05-12 15:58:15
0001 /* 0002 * SPDX-FileCopyrightText: 2012 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #ifndef __KIS_FILTER_WEIGHTS_BUFFER_H 0008 #define __KIS_FILTER_WEIGHTS_BUFFER_H 0009 0010 #include "kis_fixed_point_maths.h" 0011 #include "kis_filter_strategy.h" 0012 #include "kis_debug.h" 0013 0014 #ifdef SANITY_CHECKS_ENABLED 0015 static bool checkForAsymmetricZeros = false; 0016 0017 #define SANITY_CENTER_POSITION() \ 0018 do { \ 0019 Q_ASSERT(scaledIter >= beginDst); \ 0020 Q_ASSERT(scaledIter <= endDst); \ 0021 \ 0022 if (j == centerIndex) { \ 0023 Q_ASSERT(scaledIter == centerSrc); \ 0024 } \ 0025 } while(0) 0026 0027 #define SANITY_ZEROS() \ 0028 do { \ 0029 if (checkForAsymmetricZeros) { \ 0030 for (int j = 0; j < span; j++) { \ 0031 int idx2 = span - j - 1; \ 0032 \ 0033 if ((m_filterWeights[i].weight[j] && !m_filterWeights[i].weight[idx2]) || \ 0034 (!m_filterWeights[i].weight[j] && m_filterWeights[i].weight[idx2])) { \ 0035 \ 0036 dbgKrita << "*******"; \ 0037 dbgKrita << "Non-symmetric zero found:" << centerSrc; \ 0038 dbgKrita << "Weight" << j << ":" << m_filterWeights[i].weight[j]; \ 0039 dbgKrita << "Weight" << idx2 << ":" << m_filterWeights[i].weight[idx2]; \ 0040 qFatal("Non-symmetric zero -> fail"); \ 0041 } \ 0042 } \ 0043 } \ 0044 } while (0) 0045 0046 #define SANITY_CHECKSUM() \ 0047 do { \ 0048 Q_ASSERT(sum == 255); \ 0049 } while (0) 0050 0051 #else 0052 #define SANITY_CENTER_POSITION() 0053 #define SANITY_ZEROS() 0054 #define SANITY_CHECKSUM() 0055 #endif 0056 0057 #ifdef DEBUG_ENABLED 0058 #define DEBUG_ALL() \ 0059 do { \ 0060 dbgKrita << "************** i =" << i; \ 0061 dbgKrita << ppVar(centerSrc); \ 0062 dbgKrita << ppVar(centerIndex); \ 0063 dbgKrita << ppVar(beginSrc) << ppVar(endSrc); \ 0064 dbgKrita << ppVar(beginDst) << ppVar(endDst); \ 0065 dbgKrita << ppVar(scaledIter) << ppVar(scaledInc); \ 0066 dbgKrita << ppVar(span); \ 0067 dbgKrita << "==="; \ 0068 } while (0) 0069 0070 #define DEBUG_SAMPLE() \ 0071 do { \ 0072 dbgKrita << ppVar(scaledIter) << ppVar(t); \ 0073 } while (0) 0074 #else 0075 #define DEBUG_ALL() Q_UNUSED(beginDst); Q_UNUSED(endDst); 0076 #define DEBUG_SAMPLE() 0077 #endif 0078 0079 0080 0081 /** 0082 * \class KisFilterWeightsBuffer 0083 * 0084 * Stores the cached values for the weights of neighbouring pixels 0085 * that would form the pixel in a result of resampling. The object of 0086 * this class is created before a pass of the transformation basing on 0087 * the desired scale factor and the filter strategy used for resampling. 0088 * 0089 * Here is an example of a calculation of the span for a pixel with 0090 * scale equal to 1.0. The result of the blending will be written into 0091 * the dst(0) pixel, which is marked with '*' sign. Note that all the 0092 * coordinates here are related to the center of the pixel, not to its 0093 * leftmost border as it is common in other systems. The centerSrc 0094 * coordinate represents the offset between the source and the 0095 * destination buffers. 0096 * 0097 * dst-coordinates: the coordinates in the resulting image. The values 0098 * of the filter strategy are calculated in these 0099 * coordinates. 0100 * 0101 * src-coordinates: the coordinates in the source image/buffer. We 0102 * pick integer values from there and calculate their 0103 * dst-position to know their weights. 0104 * 0105 * 0106 * +----+----+----+-- scaledIter (samples, measured in dst pixels, 0107 * | | | | correspond to integers in src) 0108 * 0109 * +---------+-- supportDst == filterStrategy->intSupport() 0110 * | | 0111 * +-- beginDst +-- endDst 0112 * | | | 0113 * | +-- centerDst (always zero) 0114 * | | | 0115 * 0116 * dst: ----|----|----|----|----*----|----|----|----|----|--> 0117 * -4 -3 -2 -1 0 1 2 3 4 5 0118 * 0119 * src: --|----|----|----|----|----|----|----|----|----|----> 0120 * -4 -3 -2 -1 0 1 2 3 4 5 0121 * 0122 * ^ ^ ^ 0123 * | | | 0124 * | +-- centerSrc 0125 * | | | 0126 * +-- beginSrc +endSrc 0127 * | | | 0128 * | +---------+-- supportSrc ~= supportDst / realScale 0129 * | | 0130 * +-------------------+-- span (number of integers in the region) 0131 */ 0132 0133 class KisFilterWeightsBuffer 0134 { 0135 public: 0136 struct FilterWeights { 0137 ~FilterWeights() { 0138 delete[] weight; 0139 } 0140 0141 qint16 *weight; 0142 int span; 0143 int centerIndex; 0144 }; 0145 0146 public: 0147 KisFilterWeightsBuffer(KisFilterStrategy *filterStrategy, qreal realScale) { 0148 Q_ASSERT(realScale > 0); 0149 0150 m_filterWeights = new FilterWeights[256]; 0151 m_maxSpan = 0; 0152 m_weightsPositionScale = 1; 0153 0154 KisFixedPoint supportSrc; 0155 KisFixedPoint supportDst; 0156 0157 if (realScale < 1.0 && realScale > (1.0 / (1 << 8))) { 0158 m_weightsPositionScale = KisFixedPoint(realScale); 0159 supportSrc.from256Frac(filterStrategy->intSupport(m_weightsPositionScale.toFloat()) / realScale); 0160 supportDst.from256Frac(filterStrategy->intSupport(m_weightsPositionScale.toFloat())); 0161 0162 } else { 0163 supportSrc.from256Frac(filterStrategy->intSupport(m_weightsPositionScale.toFloat())); 0164 supportDst.from256Frac(filterStrategy->intSupport(m_weightsPositionScale.toFloat())); 0165 } 0166 0167 for (int i = 0; i < 256; i++) { 0168 KisFixedPoint centerSrc; 0169 centerSrc.from256Frac(i); 0170 0171 KisFixedPoint beginDst = -supportDst; 0172 KisFixedPoint endDst = supportDst; 0173 0174 KisFixedPoint beginSrc = -supportSrc - centerSrc / m_weightsPositionScale; 0175 KisFixedPoint endSrc = supportSrc - centerSrc / m_weightsPositionScale; 0176 0177 int span = (2 * supportSrc).toInt() + 0178 (beginSrc.isInteger() && endSrc.isInteger()); 0179 0180 int centerIndex = -beginSrc.toInt(); 0181 0182 m_filterWeights[i].centerIndex = centerIndex; 0183 m_filterWeights[i].span = span; 0184 m_filterWeights[i].weight = new qint16[span]; 0185 m_maxSpan = qMax(m_maxSpan, span); 0186 0187 // in dst coordinate system: 0188 KisFixedPoint scaledIter = centerSrc + beginSrc.toInt() * m_weightsPositionScale; 0189 KisFixedPoint scaledInc = m_weightsPositionScale; 0190 0191 DEBUG_ALL(); 0192 0193 int sum = 0; 0194 for (int j = 0; j < span; j++) { 0195 int t = filterStrategy->intValueAt(scaledIter.to256Frac(), m_weightsPositionScale.toFloat()); 0196 m_filterWeights[i].weight[j] = t; 0197 sum += t; 0198 0199 DEBUG_SAMPLE(); 0200 SANITY_CENTER_POSITION(); 0201 0202 scaledIter += scaledInc; 0203 } 0204 0205 SANITY_ZEROS(); 0206 0207 if (sum != 255 && sum > 0) { 0208 qreal fixFactor = 255.0 / sum; 0209 sum = 0; 0210 0211 for (int j = 0; j < span; j++) { 0212 int t = qRound(m_filterWeights[i].weight[j] * fixFactor); 0213 0214 m_filterWeights[i].weight[j] = t; 0215 sum += t; 0216 } 0217 } 0218 0219 while (sum != 255) { 0220 int diff = sum < 255 ? 1 : -1; 0221 int index = findMaxIndex(m_filterWeights[i].weight, span); 0222 m_filterWeights[i].weight[index] += diff; 0223 sum += diff; 0224 } 0225 0226 SANITY_CHECKSUM(); 0227 } 0228 } 0229 0230 ~KisFilterWeightsBuffer() { 0231 delete[] m_filterWeights; 0232 } 0233 0234 /** 0235 * Return a weights buffer for a particular value of offset 0236 */ 0237 FilterWeights* weights(KisFixedPoint pos) const { 0238 return m_filterWeights + pos.to256Frac(); 0239 } 0240 0241 /** 0242 * The maximum width of the buffer that would be needed for 0243 * calculation of a pixel value. In other words, the maximum 0244 * number of support pixels that are needed for calculation of a 0245 * single result pixel 0246 */ 0247 int maxSpan() const { 0248 return m_maxSpan; 0249 } 0250 0251 /** 0252 * The scale of the support buffer. Note that it is not always 0253 * equal to the real scale of the transformation due to 0254 * interpolation/blending difference. 0255 */ 0256 KisFixedPoint weightsPositionScale() const { 0257 return m_weightsPositionScale; 0258 } 0259 0260 private: 0261 int findMaxIndex(qint16 *buf, int size) { 0262 int maxValue = buf[0]; 0263 int maxIndex = 0; 0264 0265 for (int i = 1; i < size; i++) { 0266 if (buf[i] > maxValue) { 0267 maxValue = buf[i]; 0268 maxIndex = i; 0269 } 0270 } 0271 0272 return maxIndex; 0273 } 0274 0275 private: 0276 FilterWeights *m_filterWeights; 0277 int m_maxSpan; 0278 KisFixedPoint m_weightsPositionScale; 0279 }; 0280 0281 #endif /* __KIS_FILTER_WEIGHTS_BUFFER_H */