File indexing completed on 2024-05-12 15:58:26

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_marker_painter.h"
0008 
0009 #include <KoColor.h>
0010 #include <KoColorSpace.h>
0011 
0012 #include "kis_paint_device.h"
0013 
0014 #include "kis_algebra_2d.h"
0015 #include "kis_sequential_iterator.h"
0016 
0017 
0018 struct KisMarkerPainter::Private
0019 {
0020     Private(KisPaintDeviceSP _device, const KoColor &_color) : device(_device), color(_color) {}
0021 
0022     KisPaintDeviceSP device;
0023     const KoColor &color;
0024 };
0025 
0026 KisMarkerPainter::KisMarkerPainter(KisPaintDeviceSP device, const KoColor &color)
0027     : m_d(new Private(device, color))
0028 {
0029 }
0030 
0031 KisMarkerPainter::~KisMarkerPainter()
0032 {
0033 }
0034 
0035 
0036 
0037 bool KisMarkerPainter::isNumberInValidRange(qint32 number)
0038 {
0039     if (number < -ValidNumberRangeValue || number > ValidNumberRangeValue)
0040         return false;
0041     return true;
0042 }
0043 
0044 bool KisMarkerPainter::isRectInValidRange(const QRect &rect)
0045 {
0046     return isNumberInValidRange(rect.x())
0047             && isNumberInValidRange(rect.y())
0048             && isNumberInValidRange(rect.width())
0049             && isNumberInValidRange(rect.height());
0050 }
0051 
0052 void KisMarkerPainter::fillHalfBrushDiff(const QPointF &p1, const QPointF &p2, const QPointF &p3,
0053                                          const QPointF &center, qreal radius)
0054 {
0055     KoColor currentColor(m_d->color);
0056 
0057     const int pixelSize = m_d->device->pixelSize();
0058     const KoColorSpace *cs = m_d->device->colorSpace();
0059 
0060     const qreal fadedRadius = radius + 1;
0061     QRectF boundRect(center.x() - fadedRadius, center.y() - fadedRadius,
0062                      2 * fadedRadius, 2 * fadedRadius);
0063 
0064     KisAlgebra2D::RightHalfPlane plane1(p1, p2);
0065     KisAlgebra2D::RightHalfPlane plane2(p2, p3);
0066     KisAlgebra2D::OuterCircle outer(center, radius);
0067 
0068     boundRect = KisAlgebra2D::cutOffRect(boundRect, plane1);
0069     boundRect = KisAlgebra2D::cutOffRect(boundRect, plane2);
0070 
0071     QRect alignedRect = boundRect.toAlignedRect();
0072 
0073     KIS_SAFE_ASSERT_RECOVER_RETURN(isRectInValidRange(alignedRect));
0074 
0075     KisSequentialIterator it(m_d->device, alignedRect);
0076 
0077     while (it.nextPixel()) {
0078         QPoint pt(it.x(), it.y());
0079 
0080         qreal value1 = plane1.value(pt);
0081         if (value1 < 0) continue;
0082 
0083         qreal value2 = plane2.value(pt);
0084         if (value2 < 0) continue;
0085 
0086         qreal value3 = outer.fadeSq(pt);
0087         if (value3 > 1.0) continue;
0088 
0089         // qreal fadePos =
0090         //     value1 < 0 || value2 < 0 ?
0091         //     qMax(-value1, -value2) : value3;
0092         qreal fadePos = value3;
0093 
0094         const quint8 srcAlpha = fadePos > 0 ? quint8((1.0 - fadePos) * 255.0) : 255;
0095         const quint8 dstAlpha = cs->opacityU8(it.rawData());
0096 
0097         if (srcAlpha > dstAlpha) {
0098             currentColor.setOpacity(srcAlpha);
0099             memcpy(it.rawData(), currentColor.data(), pixelSize);
0100         }
0101     }
0102 }
0103 
0104 void KisMarkerPainter::fillFullCircle(const QPointF &center, qreal radius)
0105 {
0106     KoColor currentColor(m_d->color);
0107 
0108     const int pixelSize = m_d->device->pixelSize();
0109     const KoColorSpace *cs = m_d->device->colorSpace();
0110 
0111     const qreal fadedRadius = radius + 1;
0112     QRectF boundRect(center.x() - fadedRadius, center.y() - fadedRadius,
0113                      2 * fadedRadius, 2 * fadedRadius);
0114 
0115     KisAlgebra2D::OuterCircle outer(center, radius);
0116 
0117     QRect alignedRect = boundRect.toAlignedRect();
0118 
0119     KIS_SAFE_ASSERT_RECOVER_RETURN(isRectInValidRange(alignedRect));
0120 
0121     KisSequentialIterator it(m_d->device, alignedRect);
0122     while (it.nextPixel()) {
0123         QPoint pt(it.x(), it.y());
0124 
0125         qreal value3 = outer.fadeSq(pt);
0126         if (value3 > 1.0) continue;
0127 
0128         const quint8 srcAlpha = value3 > 0 ? quint8((1.0 - value3) * 255.0) : 255;
0129         const quint8 dstAlpha = cs->opacityU8(it.rawData());
0130 
0131         if (srcAlpha > dstAlpha) {
0132             currentColor.setOpacity(srcAlpha);
0133             memcpy(it.rawData(), currentColor.data(), pixelSize);
0134         }
0135     }
0136 }
0137 
0138 void KisMarkerPainter::fillCirclesDiff(const QPointF &c1, qreal r1,
0139                                        const QPointF &c2, qreal r2)
0140 {
0141     QVector<QPointF> n = KisAlgebra2D::intersectTwoCircles(c1, r1, c2, r2);
0142 
0143     if (n.size() < 2) {
0144         fillFullCircle(c2, r2);
0145     } else {
0146         const QPointF diff = c2 - c1;
0147         const qreal normDiffInv = 1.0 / KisAlgebra2D::norm(diff);
0148         const QPointF direction = diff * normDiffInv;
0149         const QPointF p = c1 + r1 * direction;
0150         const QPointF q = c2 + r2 * direction;
0151 
0152         fillHalfBrushDiff(n[0], p, q, c2, r2);
0153         fillHalfBrushDiff(q, p, n[1], c2, r2);
0154     }
0155 }
0156