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