File indexing completed on 2024-05-12 15:58:15
0001 /* 0002 * SPDX-FileCopyrightText: 2004 Michael Thaler <michael.thaler@physik.tu-muenchen.de> 0003 * SPDX-FileCopyrightText: 2005 C. Boemann <cbo@boemann.dk> 0004 * SPDX-FileCopyrightText: 2013 Juan Palacios <jpalaciosdev@gmail.com> 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "kis_filter_strategy.h" 0010 0011 #include <math.h> 0012 0013 #include <klocalizedstring.h> 0014 #include <QGlobalStatic> 0015 0016 #include "kis_debug.h" 0017 #include <QtMath> 0018 #include <QSize> 0019 0020 Q_GLOBAL_STATIC(KisFilterStrategyRegistry, s_instance) 0021 0022 qreal KisHermiteFilterStrategy::valueAt(qreal t, qreal weightsPositionScale) const 0023 { 0024 Q_UNUSED(weightsPositionScale); 0025 /* f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1 */ 0026 if (t < 0.0) t = -t; 0027 if (t < 1.0) return((2.0 * t - 3.0) * t * t + 1.0); 0028 return(0.0); 0029 } 0030 0031 qint32 KisHermiteFilterStrategy::intValueAt(qint32 t, qreal weightsPositionScale) const 0032 { 0033 Q_UNUSED(weightsPositionScale); 0034 /* f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1 */ 0035 if (t < 0) t = -t; 0036 if (t < 256) { 0037 t = (2 * t - 3 * 256) * t * t + (256 << 16); 0038 0039 //go from .24 fixed point to .8 fixedpoint (hack only works with positive numbers, which it is) 0040 t = (t + 0x8000) >> 16; 0041 0042 // go from .8 fixed point to 8bitscale. ie t = (t*255)/256; 0043 if (t >= 128) 0044 return t - 1; 0045 return t; 0046 } 0047 return(0); 0048 } 0049 0050 qint32 KisBicubicFilterStrategy::intValueAt(qint32 t, qreal weightsPositionScale) const 0051 { 0052 Q_UNUSED(weightsPositionScale); 0053 /* f(t) = 1.5|t|^3 - 2.5|t|^2 + 1, -1 <= t <= 1 */ 0054 if (t < 0) t = -t; 0055 if (t < 256) { 0056 t = (3 * t - 5 * 256) * t * t / 2 + (256 << 16); 0057 0058 //go from .24 fixed point to .8 fixedpoint (hack only works with positive numbers, which it is) 0059 t = (t + 0x8000) >> 16; 0060 0061 // go from .8 fixed point to 8bitscale. ie t = (t*255)/256; 0062 if (t >= 128) 0063 return t - 1; 0064 return t; 0065 } 0066 if (t < 512) { 0067 /* f(t) = -0.5|t|^3 + 2.5|t|^2 + 4|t| - 2, -2 <= t <= 2 */ 0068 t = ((-t + 5 * 256) * t / 2 - 4 * 256 * 256) * t + (2 * 256 << 16); 0069 0070 //go from .24 fixed point to .8 fixedpoint (hack only works with positive numbers, which it is) 0071 t = (t + 0x8000) >> 16; 0072 0073 // go from .8 fixed point to 8bitscale. ie t = (t*255)/256; 0074 if (t >= 128) 0075 return t - 1; 0076 return t; 0077 } 0078 return(0); 0079 } 0080 0081 qreal KisBoxFilterStrategy::valueAt(qreal t, qreal weightsPositionScale) const 0082 { 0083 if ((t >= -0.5 * weightsPositionScale) && (t < 0.5 * weightsPositionScale)) return(1.0); 0084 return(0.0); 0085 } 0086 0087 qint32 KisBoxFilterStrategy::intValueAt(qint32 t, qreal weightsPositionScale) const 0088 { 0089 /* f(t) = 1, -0.5 < t <= 0.5 */ 0090 if ((t >= -128 * weightsPositionScale) && (t < 128 * weightsPositionScale)) 0091 return 255; 0092 return 0; 0093 } 0094 0095 0096 qreal KisBoxFilterStrategy::support(qreal weightsPositionScale) 0097 { 0098 return supportVal*weightsPositionScale; 0099 } 0100 0101 qint32 KisBoxFilterStrategy::intSupport(qreal weightsPositionScale) 0102 { 0103 return qCeil(intSupportVal*weightsPositionScale); 0104 } 0105 0106 qreal KisBilinearFilterStrategy::valueAt(qreal t, qreal weightsPositionScale) const 0107 { 0108 Q_UNUSED(weightsPositionScale); 0109 if (t < 0.0) t = -t; 0110 if (t < 1.0) return(1.0 - t); 0111 return(0.0); 0112 } 0113 0114 qint32 KisBilinearFilterStrategy::intValueAt(qint32 t, qreal weightsPositionScale) const 0115 { 0116 Q_UNUSED(weightsPositionScale); 0117 /* f(t) = |t|, -1 <= t <= 1 */ 0118 if (t < 0) t = -t; 0119 if (t < 256) { 0120 // calc 256-1 but also go from .8 fixed point to 8bitscale. ie t = (t*255)/256; ie: if(t>=128) return t-1; 0121 if (t >= 128) return 256 - t; 0122 return 255 - t; 0123 } 0124 return(0); 0125 } 0126 0127 0128 qreal KisBellFilterStrategy::valueAt(qreal t, qreal weightsPositionScale) const 0129 { 0130 Q_UNUSED(weightsPositionScale); 0131 if (t < 0) t = -t; 0132 if (t < .5) return(.75 - (t * t)); 0133 if (t < 1.5) { 0134 t = (t - 1.5); 0135 return(.5 *(t * t)); 0136 } 0137 return(0.0); 0138 } 0139 0140 qreal KisBSplineFilterStrategy::valueAt(qreal t, qreal weightsPositionScale) const 0141 { 0142 Q_UNUSED(weightsPositionScale); 0143 qreal tt; 0144 0145 if (t < 0) t = -t; 0146 if (t < 1) { 0147 tt = t * t; 0148 return((.5 * tt * t) - tt + (2.0 / 3.0)); 0149 } else if (t < 2) { 0150 t = 2 - t; 0151 return((1.0 / 6.0) *(t * t * t)); 0152 } 0153 return(0.0); 0154 } 0155 0156 qreal KisLanczos3FilterStrategy::valueAt(qreal t, qreal weightsPositionScale) const 0157 { 0158 Q_UNUSED(weightsPositionScale); 0159 if (t < 0) t = -t; 0160 if (t < 3.0) return(sinc(t) * sinc(t / 3.0)); 0161 return(0.0); 0162 } 0163 0164 qreal KisLanczos3FilterStrategy::sinc(qreal x) const 0165 { 0166 const qreal pi = 3.1415926535897932385; 0167 x *= pi; 0168 if (x != 0) return(sin(x) / x); 0169 return(1.0); 0170 } 0171 0172 qreal KisMitchellFilterStrategy::valueAt(qreal t, qreal weightsPositionScale) const 0173 { 0174 Q_UNUSED(weightsPositionScale); 0175 const qreal B = 1.0 / 3.0; 0176 const qreal C = 1.0 / 3.0; 0177 qreal tt; 0178 0179 tt = t * t; 0180 if (t < 0) t = -t; 0181 if (t < 1.0) { 0182 t = (((12.0 - 9.0 * B - 6.0 * C) * (t * tt)) + ((-18.0 + 12.0 * B + 6.0 * C) * tt) + (6.0 - 2 * B)); 0183 return(t / 6.0); 0184 } else if (t < 2.0) { 0185 t = (((-1.0 * B - 6.0 * C) * (t * tt)) + ((6.0 * B + 30.0 * C) * tt) + ((-12.0 * B - 48.0 * C) * t) + (8.0 * B + 24 * C)); 0186 return(t / 6.0); 0187 } 0188 return(0.0); 0189 } 0190 0191 KisFilterStrategyRegistry::KisFilterStrategyRegistry() 0192 { 0193 } 0194 0195 KisFilterStrategyRegistry::~KisFilterStrategyRegistry() 0196 { 0197 Q_FOREACH (const QString &id, keys()) { 0198 delete get(id); 0199 } 0200 dbgRegistry << "deleting KisFilterStrategyRegistry"; 0201 } 0202 0203 KisFilterStrategyRegistry* KisFilterStrategyRegistry::instance() 0204 { 0205 if (!s_instance.exists()) { 0206 s_instance->add(new KisBoxFilterStrategy); 0207 s_instance->addAlias("Box", "NearestNeighbor"); 0208 0209 s_instance->add(new KisHermiteFilterStrategy); 0210 s_instance->add(new KisBicubicFilterStrategy); 0211 s_instance->add(new KisBilinearFilterStrategy); 0212 s_instance->add(new KisBellFilterStrategy); 0213 s_instance->add(new KisBSplineFilterStrategy); 0214 s_instance->add(new KisLanczos3FilterStrategy); 0215 s_instance->add(new KisMitchellFilterStrategy); 0216 } 0217 return s_instance; 0218 } 0219 0220 QList<KoID> KisFilterStrategyRegistry::listKeys() const 0221 { 0222 QList<KoID> answer; 0223 Q_FOREACH (const QString key, keys()) { 0224 answer.append(KoID(key, get(key)->name())); 0225 } 0226 0227 return answer; 0228 } 0229 0230 QString KisFilterStrategyRegistry::formattedDescriptions() const 0231 { 0232 QString formatedDescription("<html><head/><body>"); 0233 0234 Q_FOREACH (const QString key, keys()) { 0235 KisFilterStrategy *strategy = get(key); 0236 QString description = strategy->description(); 0237 0238 if (!description.isEmpty()) { 0239 formatedDescription.append("<p><span style=\"font-weight:600;\">"); 0240 formatedDescription.append(strategy->name()); 0241 formatedDescription.append("</span>: "); 0242 formatedDescription.append(description); 0243 formatedDescription.append("</p>"); 0244 } 0245 } 0246 formatedDescription.append("</body></html>"); 0247 0248 return formatedDescription; 0249 } 0250 0251 KisFilterStrategy *KisFilterStrategyRegistry::autoFilterStrategy(QSize originalSize, QSize desiredSize) const 0252 { 0253 // Default to nearest neighbor scaling for tiny source images. (i.e: icons or small sprite sheets.) 0254 const int pixelArtThreshold = 256; 0255 if (originalSize.width() <= pixelArtThreshold || 0256 originalSize.height() <= pixelArtThreshold) { 0257 return KisFilterStrategyRegistry::instance()->value("NearestNeighbor"); 0258 } 0259 0260 const float xScaleFactor = (float)desiredSize.width() / originalSize.width(); 0261 const float yScaleFactor = (float)desiredSize.height() / originalSize.height(); 0262 0263 if (xScaleFactor > 1.f || yScaleFactor > 1.f) { // Enlargement. 0264 return KisFilterStrategyRegistry::instance()->value("Bicubic"); 0265 } else if (xScaleFactor < 1.f || yScaleFactor < 1.f) { // Reduction. 0266 return KisFilterStrategyRegistry::instance()->value("Bicubic"); 0267 } 0268 0269 return KisFilterStrategyRegistry::instance()->value("NearestNeighbor"); 0270 }