File indexing completed on 2024-12-22 04:16:07
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 "kis_deform_paintop.h" 0008 #include "kis_deform_paintop_settings.h" 0009 0010 #include <cmath> 0011 0012 #include <QtGlobal> 0013 #include <QRect> 0014 0015 #include <kis_image.h> 0016 #include <kis_debug.h> 0017 0018 #include "kis_global.h" 0019 #include "kis_paint_device.h" 0020 #include "kis_painter.h" 0021 #include "kis_selection.h" 0022 #include "kis_random_accessor_ng.h" 0023 #include "kis_lod_transform.h" 0024 0025 #include <kis_fixed_paint_device.h> 0026 0027 #include "KisDeformOptionData.h" 0028 #include "KisBrushSizeOptionData.h" 0029 #include "kis_paintop_plugin_utils.h" 0030 #include <KoColorSpaceRegistry.h> 0031 #include <KoCompositeOp.h> 0032 0033 #ifdef Q_OS_WIN 0034 // quoting DRAND48(3) man-page: 0035 // These functions are declared obsolete by SVID 3, 0036 // which states that rand(3) should be used instead. 0037 #define drand48() (static_cast<double>(qrand()) / static_cast<double>(RAND_MAX)) 0038 #endif 0039 0040 KisDeformPaintOp::KisDeformPaintOp(const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP image) 0041 : KisPaintOp(painter) 0042 , m_sizeOption(settings.data()) 0043 , m_opacityOption(settings.data()) 0044 , m_rotationOption(settings.data()) 0045 , m_rateOption(settings.data()) 0046 { 0047 Q_UNUSED(image); 0048 Q_UNUSED(node); 0049 Q_ASSERT(settings); 0050 0051 m_brushSizeData.read(settings.data()); 0052 m_deformData.read(settings.data()); 0053 m_airbrushData.read(settings.data()); 0054 0055 m_deformBrush.setProperties(&m_deformData); 0056 m_deformBrush.setSizeProperties(&m_brushSizeData); 0057 0058 m_deformBrush.initDeformAction(); 0059 0060 m_dev = source(); 0061 0062 if ((m_brushSizeData.brushDiameter * 0.5) > 1) { 0063 m_ySpacing = m_xSpacing = m_brushSizeData.brushDiameter * 0.5 * m_brushSizeData.brushSpacing; 0064 } 0065 else { 0066 m_ySpacing = m_xSpacing = 1.0; 0067 } 0068 m_spacing = m_xSpacing; 0069 0070 0071 0072 } 0073 0074 KisDeformPaintOp::~KisDeformPaintOp() 0075 { 0076 } 0077 0078 KisSpacingInformation KisDeformPaintOp::paintAt(const KisPaintInformation& info) 0079 { 0080 if (!painter()) return KisSpacingInformation(m_spacing); 0081 if (!m_dev) return KisSpacingInformation(m_spacing); 0082 0083 KisFixedPaintDeviceSP dab = cachedDab(source()->compositionSourceColorSpace()); 0084 0085 qint32 x; 0086 qreal subPixelX; 0087 qint32 y; 0088 qreal subPixelY; 0089 0090 QPointF pt = info.pos(); 0091 if (m_brushSizeData.brushJitterMovementEnabled) { 0092 pt.setX(pt.x() + ((m_brushSizeData.brushDiameter * info.randomSource()->generateNormalized()) - m_brushSizeData.brushDiameter * 0.5) * m_brushSizeData.brushJitterMovement); 0093 pt.setY(pt.y() + ((m_brushSizeData.brushDiameter * info.randomSource()->generateNormalized()) - m_brushSizeData.brushDiameter * 0.5) * m_brushSizeData.brushJitterMovement); 0094 } 0095 0096 qreal rotation = m_rotationOption.apply(info); 0097 0098 // Deform Brush is capable of working with zero scale, 0099 // so no additional checks for 'zero'ness are needed 0100 qreal scale = m_sizeOption.apply(info); 0101 0102 0103 rotation += m_brushSizeData.brushRotation; 0104 scale *= m_brushSizeData.brushScale; 0105 0106 QPointF pos = pt - m_deformBrush.hotSpot(scale, rotation); 0107 0108 splitCoordinate(pos.x(), &x, &subPixelX); 0109 splitCoordinate(pos.y(), &y, &subPixelY); 0110 0111 KisFixedPaintDeviceSP mask = m_deformBrush.paintMask(dab, m_dev, info.randomSource(), 0112 scale, rotation, 0113 info.pos(), 0114 subPixelX, subPixelY, 0115 x, y 0116 ); 0117 0118 // this happens for the first dab of the move mode, we need more information for being able to move 0119 if (!mask) { 0120 return updateSpacingImpl(info); 0121 } 0122 0123 quint8 origOpacity = m_opacityOption.apply(painter(), info); 0124 painter()->bltFixedWithFixedSelection(x, y, dab, mask, mask->bounds().width() , mask->bounds().height()); 0125 painter()->renderMirrorMask(QRect(QPoint(x, y), QSize(mask->bounds().width() , mask->bounds().height())), dab, mask); 0126 painter()->setOpacity(origOpacity); 0127 0128 return updateSpacingImpl(info); 0129 } 0130 0131 KisSpacingInformation KisDeformPaintOp::updateSpacingImpl(const KisPaintInformation &info) const 0132 { 0133 return KisPaintOpPluginUtils::effectiveSpacing(1.0, 1.0, true, 0.0, false, m_spacing, false, 0134 1.0, 0135 KisLodTransform::lodToScale(painter()->device()), 0136 &m_airbrushData, nullptr, info); 0137 } 0138 0139 KisTimingInformation KisDeformPaintOp::updateTimingImpl(const KisPaintInformation &info) const 0140 { 0141 return KisPaintOpPluginUtils::effectiveTiming(&m_airbrushData, &m_rateOption, info); 0142 }