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

0001 /*
0002  *  SPDX-FileCopyrightText: 2016 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_roundmarkerop.h"
0008 
0009 #include <cmath>
0010 #include <memory>
0011 #include <QRect>
0012 
0013 #include <KoColorSpaceRegistry.h>
0014 #include <KoColor.h>
0015 #include <KoColorProfile.h>
0016 #include <KoCompositeOpRegistry.h>
0017 
0018 #include <kis_brush.h>
0019 #include <kis_global.h>
0020 #include <kis_paint_device.h>
0021 #include <kis_painter.h>
0022 #include <kis_image.h>
0023 #include <kis_selection.h>
0024 #include <kis_brush_based_paintop_settings.h>
0025 #include <kis_cross_device_color_sampler.h>
0026 #include <kis_fixed_paint_device.h>
0027 #include <kis_lod_transform.h>
0028 #include <kis_spacing_information.h>
0029 #include "kis_marker_painter.h"
0030 #include "kis_paintop_utils.h"
0031 
0032 
0033 
0034 KisRoundMarkerOp::KisRoundMarkerOp(KisPaintOpSettingsSP settings, KisPainter* painter, KisNodeSP node, KisImageSP /*image*/)
0035     : KisPaintOp(painter)
0036     , m_sizeOption(settings.data())
0037     , m_spacingOption(settings.data())
0038 {
0039     Q_UNUSED(node);
0040 
0041     Q_ASSERT(settings);
0042     Q_ASSERT(painter);
0043 
0044     m_firstRun = true;
0045     m_lastRadius = 1.0;
0046 
0047     m_markerOption.read(settings.data());
0048 }
0049 
0050 KisRoundMarkerOp::~KisRoundMarkerOp()
0051 {
0052 }
0053 
0054 KisSpacingInformation KisRoundMarkerOp::paintAt(const KisPaintInformation& info)
0055 {
0056     // Simple error catching
0057     if (!painter()->device()) {
0058         return KisSpacingInformation(1.0);
0059     }
0060 
0061     // get the scaling factor calculated by the size option
0062     const qreal lodScale = KisLodTransform::lodToScale(painter()->device());
0063     const qreal scale = m_sizeOption.apply(info) * lodScale;
0064     const qreal rotation = 0; // TODO
0065 
0066     const qreal diameter = m_markerOption.diameter * scale;
0067     qreal radius = 0.5 * diameter;
0068 
0069     if (KisPaintOpUtils::checkSizeTooSmall(scale, diameter, diameter)) return KisSpacingInformation();
0070     KisDabShape shape(scale, 1.0, rotation);
0071 
0072     // The position will need subtracting 0.5, but you cannot do this here
0073     // because then the mirroring tools get wrong position to mirror
0074     // and the mirroring doesn't work well.
0075     // Subtracting must happen just before the painting.
0076     QPointF pos = info.pos();
0077 
0078     KisMarkerPainter gc(painter()->device(), painter()->paintColor());
0079 
0080     if (m_firstRun) {
0081         const QVector<QPointF> points =
0082             painter()->calculateAllMirroredPoints(pos);
0083 
0084         Q_FOREACH(const QPointF &pt, points) {
0085             // Subtracting .5 from both dimensions, because the final dab tends to exaggerate towards the lower right.
0086             // This aligns it with the brush cursor.
0087             gc.fillFullCircle(pt - QPointF(0.5, 0.5), radius);
0088         }
0089     } else {
0090         const QVector<QPair<QPointF, QPointF>> pairs =
0091             painter()->calculateAllMirroredPoints(qMakePair(m_lastPaintPos, pos));
0092 
0093         Q_FOREACH(const auto &pair, pairs) {
0094             // Subtracting .5 from both dimensions, because the final dab tends to exaggerate towards the lower right.
0095             // This aligns it with the brush cursor.
0096             gc.fillCirclesDiff(pair.first - QPointF(0.5, 0.5), m_lastRadius,
0097                                pair.second - QPointF(0.5, 0.5), radius);
0098         }
0099     }
0100 
0101     m_firstRun = false;
0102     m_lastPaintPos = pos;
0103     m_lastRadius = radius;
0104 
0105     QRectF dirtyRect(pos.x() - radius, pos.y() - radius,
0106                      2 * radius, 2 * radius);
0107     dirtyRect = kisGrowRect(dirtyRect, 1);
0108 
0109     const QVector<QRect> allDirtyRects =
0110         painter()->calculateAllMirroredRects(dirtyRect.toAlignedRect());
0111 
0112     painter()->addDirtyRects(allDirtyRects);
0113 
0114     // QPointF scatteredPos =
0115     //     m_scatterOption.apply(info,
0116     //                           brush->maskWidth(shape, 0, 0, info),
0117     //                           brush->maskHeight(shape, 0, 0, info));
0118 
0119 
0120 
0121     //updateMask(info, scale, rotation, scatteredPos);
0122 
0123     //QPointF newCenterPos = QRectF(m_dstDabRect).center();
0124     /**
0125      * Save the center of the current dab to know where to read the
0126      * data during the next pass. We do not save scatteredPos here,
0127      * because it may differ slightly from the real center of the
0128      * brush (due to rounding effects), which will result in a
0129      * really weird quality.
0130      */
0131     //QRect srcDabRect = m_dstDabRect.translated((m_lastPaintPos - newCenterPos).toPoint());
0132 
0133     //m_lastPaintPos = newCenterPos;
0134 
0135     KisSpacingInformation spacingInfo = computeSpacing(info, diameter);
0136 
0137     if (m_firstRun) {
0138         m_firstRun = false;
0139         return spacingInfo;
0140     }
0141 
0142 
0143     return spacingInfo;
0144 }
0145 
0146 KisSpacingInformation KisRoundMarkerOp::updateSpacingImpl(const KisPaintInformation &info) const
0147 {
0148     const qreal lodScale = KisLodTransform::lodToScale(painter()->device());
0149     const qreal diameter = m_markerOption.diameter * m_sizeOption.apply(info) * lodScale;
0150 
0151     return computeSpacing(info, diameter);
0152 }
0153 
0154 KisSpacingInformation KisRoundMarkerOp::computeSpacing(const KisPaintInformation &info,
0155                                                        qreal diameter) const
0156 {
0157     const qreal rotation = 0; // TODO
0158     const bool axesFlipped = false; // TODO
0159 
0160     qreal extraSpacingScale = 1.0;
0161     if (m_spacingOption.isChecked()) {
0162         extraSpacingScale = m_spacingOption.apply(info);
0163     }
0164 
0165     return KisPaintOpUtils::effectiveSpacing(diameter, diameter,
0166                                              extraSpacingScale, true, true, rotation, axesFlipped,
0167                                              m_markerOption.spacing,
0168                                              m_markerOption.useAutoSpacing,
0169                                              m_markerOption.autoSpacingCoeff,
0170                                              KisLodTransform::lodToScale(painter()->device()));
0171 }