File indexing completed on 2024-05-26 04:34:01
0001 /* 0002 * SPDX-FileCopyrightText: 2012 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "kis_dab_cache_base.h" 0008 0009 #include <KoColor.h> 0010 #include "kis_color_source.h" 0011 #include "kis_paint_device.h" 0012 #include "kis_brush.h" 0013 #include <KisMirrorOption.h> 0014 #include <KisSharpnessOption.h> 0015 #include <kis_texture_option.h> 0016 #include <kis_precision_option.h> 0017 #include <kis_fixed_paint_device.h> 0018 #include <brushengine/kis_paintop.h> 0019 0020 #include <kundo2command.h> 0021 0022 struct PrecisionValues { 0023 qreal angle; 0024 qreal sizeFrac; 0025 qreal subPixel; 0026 qreal softnessFactor; 0027 qreal lightnessStrength; 0028 qreal ratio; 0029 }; 0030 0031 const qreal eps = 1e-6; 0032 static const PrecisionValues precisionLevels[] = { 0033 {M_PI / 180, 0.05, 1, 0.01, 0.01, 0.05}, 0034 {M_PI / 180, 0.01, 1, 0.01, 0.01, 0.01}, 0035 {M_PI / 180, 0, 1, 0.01, 0.01, eps}, 0036 {M_PI / 180, 0, 0.5, 0.01, 0.01, eps}, 0037 { eps, 0, eps, eps, eps, eps} 0038 }; 0039 0040 struct KisDabCacheBase::SavedDabParameters { 0041 KoColor color; 0042 qreal angle; 0043 int width; 0044 int height; 0045 qreal subPixelX; 0046 qreal subPixelY; 0047 qreal softnessFactor; 0048 qreal lightnessStrength; 0049 qreal ratio; 0050 int index; 0051 MirrorProperties mirrorProperties; 0052 0053 bool compare(const SavedDabParameters &rhs, int precisionLevel) const { 0054 const PrecisionValues &prec = precisionLevels[precisionLevel]; 0055 0056 return color == rhs.color && 0057 qAbs(angle - rhs.angle) <= prec.angle && 0058 qAbs(width - rhs.width) <= (int)(prec.sizeFrac * width) && 0059 qAbs(height - rhs.height) <= (int)(prec.sizeFrac * height) && 0060 qAbs(subPixelX - rhs.subPixelX) <= prec.subPixel && 0061 qAbs(subPixelY - rhs.subPixelY) <= prec.subPixel && 0062 qAbs(softnessFactor - rhs.softnessFactor) <= prec.softnessFactor && 0063 qAbs(lightnessStrength - rhs.lightnessStrength) <= prec.lightnessStrength && 0064 qAbs(ratio - rhs.ratio) <= prec.ratio && 0065 index == rhs.index && 0066 mirrorProperties.horizontalMirror == rhs.mirrorProperties.horizontalMirror && 0067 mirrorProperties.verticalMirror == rhs.mirrorProperties.verticalMirror; 0068 } 0069 }; 0070 0071 struct KisDabCacheBase::Private { 0072 0073 Private() 0074 : mirrorOption(0), 0075 precisionOption(0), 0076 subPixelPrecisionDisabled(false) 0077 {} 0078 0079 KisMirrorOption *mirrorOption; 0080 KisPrecisionOption *precisionOption; 0081 bool subPixelPrecisionDisabled; 0082 0083 SavedDabParameters lastSavedDabParameters; 0084 0085 static qreal positiveFraction(qreal x); 0086 }; 0087 0088 0089 0090 KisDabCacheBase::KisDabCacheBase() 0091 : m_d(new Private()) 0092 { 0093 } 0094 0095 KisDabCacheBase::~KisDabCacheBase() 0096 { 0097 delete m_d; 0098 } 0099 0100 void KisDabCacheBase::setMirrorPostprocessing(KisMirrorOption *option) 0101 { 0102 m_d->mirrorOption = option; 0103 } 0104 0105 void KisDabCacheBase::setPrecisionOption(KisPrecisionOption *option) 0106 { 0107 m_d->precisionOption = option; 0108 } 0109 0110 void KisDabCacheBase::disableSubpixelPrecision() 0111 { 0112 m_d->subPixelPrecisionDisabled = true; 0113 } 0114 0115 inline KisDabCacheBase::SavedDabParameters 0116 KisDabCacheBase::getDabParameters(KisBrushSP brush, 0117 const KoColor& color, 0118 KisDabShape const& shape, 0119 const KisPaintInformation& info, 0120 double subPixelX, double subPixelY, 0121 qreal softnessFactor, 0122 qreal lightnessStrength, 0123 MirrorProperties mirrorProperties) 0124 { 0125 SavedDabParameters params; 0126 0127 params.color = color; 0128 params.angle = shape.rotation(); 0129 params.width = brush->maskWidth(shape, subPixelX, subPixelY, info); 0130 params.height = brush->maskHeight(shape, subPixelX, subPixelY, info); 0131 params.subPixelX = subPixelX; 0132 params.subPixelY = subPixelY; 0133 params.softnessFactor = softnessFactor; 0134 params.lightnessStrength = lightnessStrength; 0135 params.index = brush->brushIndex(); 0136 params.mirrorProperties = mirrorProperties; 0137 params.ratio = shape.ratio(); 0138 0139 return params; 0140 } 0141 0142 bool KisDabCacheBase::needSeparateOriginal(KisTextureOption *textureOption, 0143 KisSharpnessOption *sharpnessOption) const 0144 { 0145 return (textureOption && textureOption->m_enabled) || 0146 (sharpnessOption && sharpnessOption->isChecked()); 0147 } 0148 0149 struct KisDabCacheBase::DabPosition { 0150 DabPosition(const QRect &_rect, 0151 const QPointF &_subPixel, 0152 qreal _realAngle) 0153 : rect(_rect), 0154 subPixel(_subPixel), 0155 realAngle(_realAngle) { 0156 } 0157 0158 QRect rect; 0159 QPointF subPixel; 0160 qreal realAngle; 0161 }; 0162 0163 qreal KisDabCacheBase::Private::positiveFraction(qreal x) { 0164 qint32 unused = 0; 0165 qreal fraction = 0.0; 0166 KisPaintOp::splitCoordinate(x, &unused, &fraction); 0167 0168 return fraction; 0169 } 0170 0171 inline 0172 KisDabCacheBase::DabPosition 0173 KisDabCacheBase::calculateDabRect(KisBrushSP brush, 0174 const QPointF &cursorPoint, 0175 KisDabShape shape, 0176 const KisPaintInformation& info, 0177 const MirrorProperties &mirrorProperties, 0178 KisSharpnessOption *sharpnessOption) 0179 { 0180 qint32 x = 0, y = 0; 0181 qreal subPixelX = 0.0, subPixelY = 0.0; 0182 0183 if (mirrorProperties.coordinateSystemFlipped) { 0184 shape = KisDabShape(shape.scale(), shape.ratio(), 2 * M_PI - shape.rotation()); 0185 } 0186 0187 QPointF hotSpot = brush->hotSpot(shape, info); 0188 QPointF pt = cursorPoint - hotSpot; 0189 0190 if (sharpnessOption) { 0191 sharpnessOption->apply(info, pt, x, y, subPixelX, subPixelY); 0192 } 0193 else { 0194 KisPaintOp::splitCoordinate(pt.x(), &x, &subPixelX); 0195 KisPaintOp::splitCoordinate(pt.y(), &y, &subPixelY); 0196 } 0197 0198 if (m_d->subPixelPrecisionDisabled) { 0199 subPixelX = 0; 0200 subPixelY = 0; 0201 } 0202 0203 if (qIsNaN(subPixelX)) { 0204 subPixelX = 0; 0205 } 0206 0207 if (qIsNaN(subPixelY)) { 0208 subPixelY = 0; 0209 } 0210 0211 int width = brush->maskWidth(shape, subPixelX, subPixelY, info); 0212 int height = brush->maskHeight(shape, subPixelX, subPixelY, info); 0213 0214 if (mirrorProperties.horizontalMirror) { 0215 subPixelX = Private::positiveFraction(-(cursorPoint.x() + hotSpot.x())); 0216 width = brush->maskWidth(shape, subPixelX, subPixelY, info); 0217 x = qRound(cursorPoint.x() + subPixelX + hotSpot.x()) - width; 0218 } 0219 0220 if (mirrorProperties.verticalMirror) { 0221 subPixelY = Private::positiveFraction(-(cursorPoint.y() + hotSpot.y())); 0222 height = brush->maskHeight(shape, subPixelX, subPixelY, info); 0223 y = qRound(cursorPoint.y() + subPixelY + hotSpot.y()) - height; 0224 } 0225 0226 return DabPosition(QRect(x, y, width, height), 0227 QPointF(subPixelX, subPixelY), 0228 shape.rotation()); 0229 } 0230 0231 void KisDabCacheBase::fetchDabGenerationInfo(bool hasDabInCache, 0232 KisDabCacheUtils::DabRenderingResources *resources, 0233 const KisDabCacheUtils::DabRequestInfo &request, 0234 KisDabCacheUtils::DabGenerationInfo *di, 0235 bool *shouldUseCache) 0236 { 0237 di->info = request.info; 0238 di->softnessFactor = request.softnessFactor; 0239 di->lightnessStrength = request.lightnessStrength; 0240 0241 if (m_d->mirrorOption) { 0242 di->mirrorProperties = m_d->mirrorOption->apply(request.info); 0243 } 0244 0245 DabPosition position = calculateDabRect(resources->brush, 0246 request.cursorPoint, 0247 request.shape, 0248 request.info, 0249 di->mirrorProperties, 0250 resources->sharpnessOption.data()); 0251 di->shape = KisDabShape(request.shape.scale(), request.shape.ratio(), position.realAngle); 0252 di->dstDabRect = position.rect; 0253 di->subPixel = position.subPixel; 0254 0255 const bool supportsCaching = resources->brush->supportsCaching(); 0256 0257 const KisUniformColorSource *uniformColorSource = 0258 resources->colorSource ? dynamic_cast<const KisUniformColorSource*>(resources->colorSource.data()) : 0; 0259 0260 di->solidColorFill = !resources->colorSource || uniformColorSource; 0261 di->paintColor = uniformColorSource ? 0262 uniformColorSource->uniformColor() : request.color; 0263 0264 SavedDabParameters newParams = getDabParameters(resources->brush, 0265 di->paintColor, 0266 di->shape, 0267 di->info, 0268 di->subPixel.x(), 0269 di->subPixel.y(), 0270 di->softnessFactor, 0271 di->lightnessStrength, 0272 di->mirrorProperties); 0273 0274 int precisionLevel = 4; 0275 if (m_d->precisionOption) { 0276 const int effectiveDabSize = qMin(newParams.width, newParams.height); 0277 precisionLevel = m_d->precisionOption->effectivePrecisionLevel(effectiveDabSize) - 1; 0278 } 0279 *shouldUseCache = hasDabInCache && supportsCaching && di->solidColorFill && 0280 newParams.compare(m_d->lastSavedDabParameters, precisionLevel); 0281 0282 if (!*shouldUseCache) { 0283 m_d->lastSavedDabParameters = newParams; 0284 } 0285 0286 di->needsPostprocessing = needSeparateOriginal(resources->textureOption.data(), resources->sharpnessOption.data()); 0287 } 0288