File indexing completed on 2024-06-16 04:17:19

0001 /*
0002  *  SPDX-FileCopyrightText: 2008, 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "deform_brush.h"
0008 #include "kis_painter.h"
0009 
0010 #include "kis_fixed_paint_device.h"
0011 
0012 #include <KoColor.h>
0013 #include <KoColorSpace.h>
0014 
0015 #include <QRect>
0016 
0017 #include <kis_types.h>
0018 #include <kis_iterator_ng.h>
0019 #include <kis_cross_device_color_sampler.h>
0020 
0021 #include <cmath>
0022 #include <ctime>
0023 #include <KoColorSpaceRegistry.h>
0024 
0025 const qreal degToRad = M_PI / 180.0;
0026 
0027 
0028 DeformBrush::DeformBrush()
0029 {
0030     m_firstPaint = false;
0031     m_counter = 1;
0032     m_deformAction = 0;
0033 }
0034 
0035 DeformBrush::~DeformBrush()
0036 {
0037     delete m_deformAction;
0038 }
0039 
0040 void DeformBrush::initDeformAction()
0041 {
0042     DeformModes mode = DeformModes(m_properties->deformAction);
0043 
0044     switch (mode) {
0045     case GROW:
0046     case SHRINK: {
0047         m_deformAction = new DeformScale();
0048         break;
0049     }
0050     case SWIRL_CW:
0051     case SWIRL_CCW: {
0052         m_deformAction = new DeformRotation();
0053         break;
0054     }
0055 
0056     case MOVE: {
0057         m_deformAction = new DeformMove();
0058         static_cast<DeformMove*>(m_deformAction)->setFactor(m_properties->deformAmount);
0059         break;
0060     }
0061     case LENS_IN:
0062     case LENS_OUT: {
0063         m_deformAction = new DeformLens();
0064         static_cast<DeformLens*>(m_deformAction)->setLensFactor(m_properties->deformAmount, 0.0);
0065         static_cast<DeformLens*>(m_deformAction)->setMode(mode == LENS_OUT);
0066         break;
0067     }
0068     case DEFORM_COLOR: {
0069         m_deformAction = new DeformColor();
0070         static_cast<DeformColor*>(m_deformAction)->setFactor(m_properties->deformAmount);
0071         break;
0072     }
0073     default: {
0074         m_deformAction = new DeformBase();
0075         break;
0076     }
0077     }
0078 }
0079 
0080 bool DeformBrush::setupAction(
0081     DeformModes mode, const QPointF& pos, QTransform const& rotation)
0082 {
0083 
0084     switch (mode) {
0085     case GROW:
0086     case SHRINK: {
0087         // grow or shrink, the sign decide
0088         qreal sign = (mode == GROW) ? 1.0 : -1.0;
0089         qreal factor;
0090         if (m_properties->deformUseCounter) {
0091             factor = (1.0 + sign * (m_counter * m_counter / 100.0));
0092         }
0093         else {
0094             factor = (1.0 + sign * (m_properties->deformAmount));
0095         }
0096         DeformScale* deformScale = dynamic_cast<DeformScale*>(m_deformAction);
0097         KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(deformScale, false);
0098         deformScale->setFactor(factor);
0099         break;
0100     }
0101     case SWIRL_CW:
0102     case SWIRL_CCW: {
0103         // CW or CCW, the sign decide
0104         qreal sign = (mode == SWIRL_CW) ? 1.0 : -1.0;
0105         qreal factor;
0106         if (m_properties->deformUseCounter) {
0107             factor = m_counter * sign * degToRad;
0108         }
0109         else {
0110             factor = (360 * m_properties->deformAmount * 0.5) * sign * degToRad;
0111         }
0112         DeformRotation* deformRotation = dynamic_cast<DeformRotation*>(m_deformAction);
0113         KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(deformRotation, false);
0114         deformRotation->setAlpha(factor);
0115         break;
0116     }
0117     case MOVE: {
0118         if (m_firstPaint == false) {
0119             m_prevX = pos.x();
0120             m_prevY = pos.y();
0121             DeformMove* deformMove = static_cast<DeformMove*>(m_deformAction);
0122             KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(deformMove, false);
0123             deformMove->setDistance(0.0, 0.0);
0124             m_firstPaint = true;
0125             return false;
0126         }
0127         else {
0128             qreal xDistance = pos.x() - m_prevX;
0129             qreal yDistance = pos.y() - m_prevY;
0130             rotation.map(xDistance, yDistance, &xDistance, &yDistance);
0131             DeformMove* deformMove = static_cast<DeformMove*>(m_deformAction);
0132             KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(deformMove, false);
0133             deformMove->setDistance(xDistance, yDistance);
0134             m_prevX = pos.x();
0135             m_prevY = pos.y();
0136         }
0137         break;
0138     }
0139     case LENS_IN:
0140     case LENS_OUT: {
0141         DeformLens* deformLens = static_cast<DeformLens*>(m_deformAction);
0142         KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(deformLens, false);
0143         deformLens->setMaxDistance(m_sizeProperties->brushDiameter * 0.5, m_sizeProperties->brushDiameter * 0.5);
0144         break;
0145     }
0146     case DEFORM_COLOR: {
0147         // no run-time setup
0148         break;
0149     }
0150     default: {
0151         break;
0152     }
0153     }
0154     return true;
0155 }
0156 
0157 KisFixedPaintDeviceSP DeformBrush::paintMask(KisFixedPaintDeviceSP dab,
0158         KisPaintDeviceSP layer,
0159         KisRandomSourceSP randomSource,
0160         qreal scale,
0161         qreal rotation,
0162         QPointF pos, qreal subPixelX, qreal subPixelY, int dabX, int dabY)
0163 {
0164     KisFixedPaintDeviceSP mask = new KisFixedPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
0165     KisCrossDeviceColorSampler colorSampler(layer, dab);
0166 
0167     qreal fWidth = maskWidth(scale);
0168     qreal fHeight = maskHeight(scale);
0169 
0170     int dstWidth =  qRound(m_maskRect.width());
0171     int dstHeight = qRound(m_maskRect.height());
0172 
0173     // clear
0174     if (dab->bounds().width() != dstWidth || dab->bounds().height() != dstHeight) {
0175         dab->setRect(m_maskRect.toRect());
0176         dab->lazyGrowBufferWithoutInitialization();
0177     }
0178 
0179     qreal const centerX = dstWidth  * 0.5  + subPixelX;
0180     qreal const centerY = dstHeight * 0.5  + subPixelY;
0181 
0182     qreal const majorAxis = 2.0 / fWidth;
0183     qreal const minorAxis = 2.0 / fHeight;
0184 
0185     qreal distance;
0186 
0187     QTransform forwardRotationMatrix;
0188     forwardRotationMatrix.rotate(rotation);
0189     QTransform reverseRotationMatrix;
0190     reverseRotationMatrix.rotate(-rotation);
0191 
0192     // if can't paint, stop
0193     if (!setupAction(DeformModes(m_properties->deformAction),
0194                      pos, forwardRotationMatrix))
0195     {
0196         return 0;
0197     }
0198 
0199     mask->setRect(dab->bounds());
0200     mask->lazyGrowBufferWithoutInitialization();
0201     quint8* maskPointer = mask->data();
0202     qint8 maskPixelSize = mask->pixelSize();
0203 
0204     quint8* dabPointer = dab->data();
0205     int dabPixelSize = dab->colorSpace()->pixelSize();
0206 
0207     for (int y = 0; y <  dstHeight; y++) {
0208         for (int x = 0; x < dstWidth; x++) {
0209             qreal maskX = x - centerX;
0210             qreal maskY = y - centerY;
0211             forwardRotationMatrix.map(maskX, maskY, &maskX, &maskY);
0212             distance = norme(maskX * majorAxis, maskY * minorAxis);
0213 
0214             if (distance > 1.0) {
0215                 // leave there OPACITY TRANSPARENT pixel (default pixel)
0216 
0217                 colorSampler.sampleOldColor(x + dabX, y + dabY, dabPointer);
0218                 dabPointer += dabPixelSize;
0219 
0220                 *maskPointer = OPACITY_TRANSPARENT_U8;
0221                 maskPointer += maskPixelSize;
0222                 continue;
0223             }
0224 
0225             if (m_sizeProperties->brushDensity != 1.0) {
0226                 if (m_sizeProperties->brushDensity < randomSource->generateNormalized()) {
0227                     dabPointer += dabPixelSize;
0228                     *maskPointer = OPACITY_TRANSPARENT_U8;
0229                     maskPointer += maskPixelSize;
0230                     continue;
0231                 }
0232             }
0233 
0234             m_deformAction->transform(&maskX, &maskY, distance, randomSource);
0235             reverseRotationMatrix.map(maskX, maskY, &maskX, &maskY);
0236 
0237             maskX += pos.x();
0238             maskY += pos.y();
0239 
0240             if (!m_properties->deformUseBilinear) {
0241                 maskX = qRound(maskX);
0242                 maskY = qRound(maskY);
0243             }
0244 
0245             if (m_properties->deformUseOldData) {
0246                 colorSampler.sampleOldColor(maskX, maskY, dabPointer);
0247             }
0248             else {
0249                 colorSampler.sampleColor(maskX, maskY, dabPointer);
0250             }
0251 
0252             dabPointer += dabPixelSize;
0253 
0254             *maskPointer = OPACITY_OPAQUE_U8;
0255             maskPointer += maskPixelSize;
0256 
0257         }
0258     }
0259     m_counter++;
0260 
0261     return mask;
0262 
0263 }
0264 
0265 void DeformBrush::debugColor(const quint8* data, KoColorSpace * cs)
0266 {
0267     QColor rgbcolor;
0268     cs->toQColor(data, &rgbcolor);
0269     dbgPlugins << "RGBA: ("
0270                << rgbcolor.red()
0271                << ", " << rgbcolor.green()
0272                << ", " << rgbcolor.blue()
0273                << ", " << rgbcolor.alpha() << ")";
0274 }
0275 
0276 QPointF DeformBrush::hotSpot(qreal scale, qreal rotation)
0277 {
0278     qreal fWidth = maskWidth(scale);
0279     qreal fHeight = maskHeight(scale);
0280 
0281     QTransform m;
0282     m.reset();
0283     m.rotate(-rotation);
0284 
0285     m_maskRect = QRect(0, 0, fWidth, fHeight);
0286     m_maskRect.translate(-m_maskRect.center());
0287     m_maskRect = m.mapRect(m_maskRect);
0288     m_maskRect.translate(-m_maskRect.topLeft());
0289     return m_maskRect.center();
0290 }