File indexing completed on 2025-02-02 04:15:56
0001 /* 0002 * SPDX-FileCopyrightText: 2002 Patrick Julien <freak@codepimps.org> 0003 * SPDX-FileCopyrightText: 2004 Boudewijn Rempt <boud@valdyas.org> 0004 * SPDX-FileCopyrightText: 2004 Clarence Dang <dang@kde.org> 0005 * SPDX-FileCopyrightText: 2004 Adrian Page <adrian@pagenet.plus.com> 0006 * SPDX-FileCopyrightText: 2004, 2007, 2010 Cyrille Berger <cberger@cberger.net> 0007 * 0008 * SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 0011 #include "kis_paintop.h" 0012 0013 #include <QtMath> 0014 0015 #include <KoColor.h> 0016 #include <KoColorSpace.h> 0017 #include <KoPointerEvent.h> 0018 0019 #include "kis_painter.h" 0020 #include "kis_layer.h" 0021 0022 #include "kis_image.h" 0023 #include "kis_paint_device.h" 0024 #include "kis_global.h" 0025 #include "kis_datamanager.h" 0026 #include <brushengine/kis_paintop_preset.h> 0027 #include <brushengine/kis_paint_information.h> 0028 #include "kis_vec.h" 0029 #include "kis_perspective_math.h" 0030 #include "kis_fixed_paint_device.h" 0031 #include "kis_paintop_utils.h" 0032 0033 0034 #define BEZIER_FLATNESS_THRESHOLD 0.5 0035 #include <kis_distance_information.h> 0036 0037 #include <qnumeric.h> 0038 0039 struct Q_DECL_HIDDEN KisPaintOp::Private { 0040 Private(KisPaintOp *_q) 0041 : q(_q) {} 0042 0043 KisPaintOp *q {nullptr}; 0044 0045 KisFixedPaintDeviceSP dab; 0046 KisPainter* painter {nullptr}; 0047 0048 bool fanCornersEnabled {false}; 0049 qreal fanCornersStep {1.0}; 0050 }; 0051 0052 0053 KisPaintOp::KisPaintOp(KisPainter * painter) : d(new Private(this)) 0054 { 0055 d->painter = painter; 0056 } 0057 0058 KisPaintOp::~KisPaintOp() 0059 { 0060 d->dab.clear(); 0061 delete d; 0062 } 0063 0064 KisFixedPaintDeviceSP KisPaintOp::cachedDab() 0065 { 0066 return cachedDab(d->painter->device()->colorSpace()); 0067 } 0068 0069 KisFixedPaintDeviceSP KisPaintOp::cachedDab(const KoColorSpace *cs) 0070 { 0071 if (!d->dab || *d->dab->colorSpace() != *cs) { 0072 d->dab = new KisFixedPaintDevice(cs); 0073 } 0074 return d->dab; 0075 } 0076 0077 void KisPaintOp::setFanCornersInfo(bool fanCornersEnabled, qreal fanCornersStep) 0078 { 0079 d->fanCornersEnabled = fanCornersEnabled; 0080 d->fanCornersStep = fanCornersStep; 0081 } 0082 0083 void KisPaintOp::splitCoordinate(qreal coordinate, qint32 *whole, qreal *fraction) 0084 { 0085 const qint32 i = qFloor(coordinate); 0086 const qreal f = coordinate - i; 0087 0088 *whole = i; 0089 *fraction = f; 0090 } 0091 0092 std::pair<int, bool> KisPaintOp::doAsynchronousUpdate(QVector<KisRunnableStrokeJobData *> &jobs) 0093 { 0094 Q_UNUSED(jobs); 0095 return std::make_pair(40, false); 0096 } 0097 0098 static void paintBezierCurve(KisPaintOp *paintOp, 0099 const KisPaintInformation &pi1, 0100 const KisVector2D &control1, 0101 const KisVector2D &control2, 0102 const KisPaintInformation &pi2, 0103 KisDistanceInformation *currentDistance) 0104 { 0105 LineEquation line = LineEquation::Through(toKisVector2D(pi1.pos()), toKisVector2D(pi2.pos())); 0106 qreal d1 = line.absDistance(control1); 0107 qreal d2 = line.absDistance(control2); 0108 0109 if ((d1 < BEZIER_FLATNESS_THRESHOLD && d2 < BEZIER_FLATNESS_THRESHOLD) 0110 || qIsNaN(d1) || qIsNaN(d2)) { 0111 paintOp->paintLine(pi1, pi2, currentDistance); 0112 } else { 0113 // Midpoint subdivision. See Foley & Van Dam Computer Graphics P.508 0114 KisVector2D l2 = (toKisVector2D(pi1.pos()) + control1) / 2; 0115 KisVector2D h = (control1 + control2) / 2; 0116 KisVector2D l3 = (l2 + h) / 2; 0117 KisVector2D r3 = (control2 + toKisVector2D(pi2.pos())) / 2; 0118 KisVector2D r2 = (h + r3) / 2; 0119 KisVector2D l4 = (l3 + r2) / 2; 0120 0121 KisPaintInformation middlePI = KisPaintInformation::mix(toQPointF(l4), 0.5, pi1, pi2); 0122 0123 paintBezierCurve(paintOp, pi1, l2, l3, middlePI, currentDistance); 0124 paintBezierCurve(paintOp, middlePI, r2, r3, pi2, currentDistance); 0125 } 0126 } 0127 0128 void KisPaintOp::paintBezierCurve(const KisPaintInformation &pi1, 0129 const QPointF &control1, 0130 const QPointF &control2, 0131 const KisPaintInformation &pi2, 0132 KisDistanceInformation *currentDistance) 0133 { 0134 return ::paintBezierCurve(this, pi1, toKisVector2D(control1), toKisVector2D(control2), pi2, currentDistance); 0135 } 0136 0137 0138 void KisPaintOp::paintLine(const KisPaintInformation &pi1, 0139 const KisPaintInformation &pi2, 0140 KisDistanceInformation *currentDistance) 0141 { 0142 KisPaintOpUtils::paintLine(*this, pi1, pi2, currentDistance, 0143 d->fanCornersEnabled, 0144 d->fanCornersStep); 0145 } 0146 0147 void KisPaintOp::paintAt(const KisPaintInformation& info, KisDistanceInformation *currentDistance) 0148 { 0149 Q_ASSERT(currentDistance); 0150 0151 KisPaintInformation pi(info); 0152 pi.paintAt(*this, currentDistance); 0153 } 0154 0155 void KisPaintOp::updateSpacing(const KisPaintInformation &info, 0156 KisDistanceInformation ¤tDistance) const 0157 { 0158 KisPaintInformation pi(info); 0159 KisSpacingInformation spacingInfo; 0160 { 0161 KisPaintInformation::DistanceInformationRegistrar r 0162 = pi.registerDistanceInformation(¤tDistance); 0163 spacingInfo = updateSpacingImpl(pi); 0164 } 0165 0166 currentDistance.updateSpacing(spacingInfo); 0167 } 0168 0169 void KisPaintOp::updateTiming(const KisPaintInformation &info, 0170 KisDistanceInformation ¤tDistance) const 0171 { 0172 KisPaintInformation pi(info); 0173 KisTimingInformation timingInfo; 0174 { 0175 KisPaintInformation::DistanceInformationRegistrar r 0176 = pi.registerDistanceInformation(¤tDistance); 0177 timingInfo = updateTimingImpl(pi); 0178 } 0179 0180 currentDistance.updateTiming(timingInfo); 0181 } 0182 0183 KisTimingInformation KisPaintOp::updateTimingImpl(const KisPaintInformation &info) const 0184 { 0185 Q_UNUSED(info); 0186 return KisTimingInformation(); 0187 } 0188 0189 KisPainter* KisPaintOp::painter() const 0190 { 0191 return d->painter; 0192 } 0193 0194 KisPaintDeviceSP KisPaintOp::source() const 0195 { 0196 return d->painter->device(); 0197 }