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 }