File indexing completed on 2024-12-22 04:16:22

0001 /*
0002  *  SPDX-FileCopyrightText: 2008-2012 Lukáš Tvrdý <lukast.dev@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_spray_paintop.h"
0008 #include "kis_spray_paintop_settings.h"
0009 
0010 #include <cmath>
0011 
0012 #include <QRect>
0013 #include <kis_global.h>
0014 #include <kis_paint_device.h>
0015 #include <kis_painter.h>
0016 #include <kis_types.h>
0017 #include <brushengine/kis_paintop.h>
0018 #include <kis_node.h>
0019 
0020 #include <kis_fixed_paint_device.h>
0021 #include <kis_brush_option.h>
0022 #include <kis_lod_transform.h>
0023 #include <kis_paintop_plugin_utils.h>
0024 #include <KoResourceLoadResult.h>
0025 
0026 
0027 KisSprayPaintOp::KisSprayPaintOp(const KisPaintOpSettingsSP settings, KisPainter *painter, KisNodeSP node, KisImageSP image)
0028     : KisPaintOp(painter)
0029     , m_sprayOpOption(settings.data())
0030     , m_isPresetValid(true)
0031     , m_rotationOption(settings.data())
0032     , m_sizeOption(settings.data())
0033     , m_opacityOption(settings.data())
0034     , m_rateOption(settings.data())
0035     , m_node(node)
0036     
0037 {
0038     Q_ASSERT(settings);
0039     Q_ASSERT(painter);
0040     Q_UNUSED(image);
0041 
0042     m_airbrushData.read(settings.data());
0043 
0044     m_brushOption.readOptionSetting(settings, settings->resourcesInterface(), settings->canvasResourcesInterface());
0045 
0046     m_colorProperties.read(settings.data());
0047     // create the actual distribution objects
0048     m_sprayOpOption.updateDistributions();
0049     // first load tip properties as shape properties are dependent on diameter/scale/aspect
0050     m_shapeProperties.read(settings.data());
0051 
0052     // TODO: what to do with proportional sizes?
0053     m_shapeDynamicsProperties.read(settings.data());
0054 
0055     if (!m_shapeProperties.enabled && !m_brushOption.brush()) {
0056         // in case the preset does not contain the definition for KisBrush
0057         m_isPresetValid = false;
0058         dbgKrita << "Preset is not valid. Painting is not possible. Use the preset editor to fix current brush engine preset.";
0059     }
0060 
0061     m_sprayBrush.setProperties(&m_sprayOpOption.data, &m_colorProperties,
0062                                &m_shapeProperties, &m_shapeDynamicsProperties, m_brushOption.brush());
0063 
0064     m_sprayBrush.setFixedDab(cachedDab());
0065 
0066     // spacing
0067     if ((m_sprayOpOption.data.diameter * 0.5) > 1) {
0068         m_ySpacing = m_xSpacing = m_sprayOpOption.data.diameter * 0.5 * m_sprayOpOption.data.spacing;
0069     }
0070     else {
0071         m_ySpacing = m_xSpacing = 1.0;
0072     }
0073     m_spacing = m_xSpacing;
0074 }
0075 
0076 KisSprayPaintOp::~KisSprayPaintOp()
0077 {
0078 }
0079 
0080 QList<KoResourceLoadResult> KisSprayPaintOp::prepareLinkedResources(const KisPaintOpSettingsSP settings, KisResourcesInterfaceSP resourcesInterface)
0081 {
0082     KisBrushOptionProperties brushOption;
0083     return brushOption.prepareLinkedResources(settings, resourcesInterface);
0084 }
0085 
0086 KisSpacingInformation KisSprayPaintOp::paintAt(const KisPaintInformation& info)
0087 {
0088     if (!painter() || !m_isPresetValid) {
0089         return KisSpacingInformation(m_spacing);
0090     }
0091 
0092     if (!m_dab) {
0093         m_dab = source()->createCompositionSourceDevice();
0094     }
0095     else {
0096         m_dab->clear();
0097     }
0098 
0099     qreal rotation = m_rotationOption.apply(info);
0100     quint8 origOpacity = m_opacityOption.apply(painter(), info);
0101     // Spray Brush is capable of working with zero scale,
0102     // so no additional checks for 'zero'ness are needed
0103     const qreal scale = m_sizeOption.apply(info);
0104     const qreal lodScale = KisLodTransform::lodToScale(painter()->device());
0105 
0106 
0107     m_sprayBrush.paint(m_dab,
0108                        m_node->paintDevice(),
0109                        info,
0110                        rotation,
0111                        scale, lodScale,
0112                        painter()->paintColor(),
0113                        painter()->backgroundColor());
0114 
0115     QRect rc = m_dab->extent();
0116     painter()->bitBlt(rc.topLeft(), m_dab, rc);
0117     painter()->renderMirrorMask(rc, m_dab);
0118     painter()->setOpacity(origOpacity);
0119 
0120     return computeSpacing(info, lodScale);
0121 }
0122 
0123 KisSpacingInformation KisSprayPaintOp::updateSpacingImpl(const KisPaintInformation &info) const
0124 {
0125     return computeSpacing(info, KisLodTransform::lodToScale(painter()->device()));
0126 }
0127 
0128 KisTimingInformation KisSprayPaintOp::updateTimingImpl(const KisPaintInformation &info) const
0129 {
0130     return KisPaintOpPluginUtils::effectiveTiming(&m_airbrushData, &m_rateOption, info);
0131 }
0132 
0133 KisSpacingInformation KisSprayPaintOp::computeSpacing(const KisPaintInformation &info,
0134                                                       qreal lodScale) const
0135 {
0136     return KisPaintOpPluginUtils::effectiveSpacing(1.0, 1.0, true, 0.0, false,
0137                                                    m_spacing * lodScale, false, 1.0, lodScale,
0138                                                    &m_airbrushData, nullptr, info);
0139 }