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 }