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 }