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 ¢er, 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 ¢er, 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