File indexing completed on 2024-05-12 15:57:02
0001 /* 0002 * SPDX-FileCopyrightText: 2016 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "KisHandlePainterHelper.h" 0008 0009 #include <QPainter> 0010 #include <QPainterPath> 0011 #include "kis_algebra_2d.h" 0012 #include "kis_painting_tweaks.h" 0013 0014 using KisPaintingTweaks::PenBrushSaver; 0015 0016 KisHandlePainterHelper::KisHandlePainterHelper(QPainter *_painter, qreal handleRadius) 0017 : m_painter(_painter), 0018 m_originalPainterTransform(m_painter->transform()), 0019 m_painterTransform(m_painter->transform()), 0020 m_handleRadius(handleRadius), 0021 m_decomposedMatrix(m_painterTransform) 0022 { 0023 init(); 0024 } 0025 0026 KisHandlePainterHelper::KisHandlePainterHelper(QPainter *_painter, const QTransform &originalPainterTransform, qreal handleRadius) 0027 : m_painter(_painter), 0028 m_originalPainterTransform(originalPainterTransform), 0029 m_painterTransform(m_painter->transform()), 0030 m_handleRadius(handleRadius), 0031 m_decomposedMatrix(m_painterTransform) 0032 { 0033 init(); 0034 } 0035 0036 KisHandlePainterHelper::KisHandlePainterHelper(KisHandlePainterHelper &&rhs) 0037 : m_painter(rhs.m_painter), 0038 m_originalPainterTransform(rhs.m_originalPainterTransform), 0039 m_painterTransform(rhs.m_painterTransform), 0040 m_handleRadius(rhs.m_handleRadius), 0041 m_decomposedMatrix(rhs.m_decomposedMatrix), 0042 m_handleTransform(rhs.m_handleTransform), 0043 m_handlePolygon(rhs.m_handlePolygon), 0044 m_handleStyle(rhs.m_handleStyle) 0045 { 0046 // disable the source helper 0047 rhs.m_painter = 0; 0048 } 0049 0050 void KisHandlePainterHelper::init() 0051 { 0052 m_handleStyle = KisHandleStyle::inheritStyle(); 0053 0054 m_painter->setTransform(QTransform()); 0055 m_handleTransform = m_decomposedMatrix.shearTransform() * m_decomposedMatrix.rotateTransform(); 0056 0057 if (m_handleRadius > 0.0) { 0058 const QRectF handleRect(-m_handleRadius, -m_handleRadius, 2 * m_handleRadius, 2 * m_handleRadius); 0059 m_handlePolygon = m_handleTransform.map(QPolygonF(handleRect)); 0060 } 0061 } 0062 0063 KisHandlePainterHelper::~KisHandlePainterHelper() { 0064 if (m_painter) { 0065 m_painter->setTransform(m_originalPainterTransform); 0066 } 0067 } 0068 0069 void KisHandlePainterHelper::setHandleStyle(const KisHandleStyle &style) 0070 { 0071 m_handleStyle = style; 0072 } 0073 0074 void KisHandlePainterHelper::drawHandleRect(const QPointF ¢er, qreal radius, QPoint offset = QPoint(0,0)) 0075 { 0076 KIS_SAFE_ASSERT_RECOVER_RETURN(m_painter); 0077 0078 QRectF handleRect(-radius, -radius, 2 * radius, 2 * radius); 0079 QPolygonF handlePolygon = m_handleTransform.map(QPolygonF(handleRect)); 0080 handlePolygon.translate(m_painterTransform.map(center)); 0081 0082 handlePolygon.translate(offset); 0083 0084 const QPen originalPen = m_painter->pen(); 0085 0086 // temporarily set the pen width to 2 to avoid pixel shifting dropping pixels the border 0087 QPen *tempPen = new QPen(m_painter->pen()); 0088 tempPen->setWidth(4); 0089 const QPen customPen = *tempPen; 0090 m_painter->setPen(customPen); 0091 0092 0093 Q_FOREACH (KisHandleStyle::IterationStyle it, m_handleStyle.handleIterations) { 0094 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop); 0095 m_painter->drawPolygon(handlePolygon); 0096 } 0097 0098 m_painter->setPen(originalPen); 0099 } 0100 0101 void KisHandlePainterHelper::drawHandleCircle(const QPointF ¢er, qreal radius) { 0102 KIS_SAFE_ASSERT_RECOVER_RETURN(m_painter); 0103 0104 QRectF handleRect(-radius, -radius, 2 * radius, 2 * radius); 0105 handleRect.translate(m_painterTransform.map(center)); 0106 0107 Q_FOREACH (KisHandleStyle::IterationStyle it, m_handleStyle.handleIterations) { 0108 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop); 0109 m_painter->drawEllipse(handleRect); 0110 } 0111 } 0112 0113 void KisHandlePainterHelper::drawHandleCircle(const QPointF ¢er) 0114 { 0115 drawHandleCircle(center, m_handleRadius); 0116 } 0117 0118 void KisHandlePainterHelper::drawHandleSmallCircle(const QPointF ¢er) 0119 { 0120 drawHandleCircle(center, 0.7 * m_handleRadius); 0121 } 0122 0123 void KisHandlePainterHelper::drawHandleRect(const QPointF ¢er) { 0124 KIS_SAFE_ASSERT_RECOVER_RETURN(m_painter); 0125 QPolygonF paintingPolygon = m_handlePolygon.translated(m_painterTransform.map(center)); 0126 0127 Q_FOREACH (KisHandleStyle::IterationStyle it, m_handleStyle.handleIterations) { 0128 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop); 0129 m_painter->drawPolygon(paintingPolygon); 0130 } 0131 } 0132 0133 void KisHandlePainterHelper::drawGradientHandle(const QPointF ¢er, qreal radius) { 0134 KIS_SAFE_ASSERT_RECOVER_RETURN(m_painter); 0135 0136 QPolygonF handlePolygon; 0137 0138 handlePolygon << QPointF(-radius, 0); 0139 handlePolygon << QPointF(0, radius); 0140 handlePolygon << QPointF(radius, 0); 0141 handlePolygon << QPointF(0, -radius); 0142 0143 handlePolygon = m_handleTransform.map(handlePolygon); 0144 handlePolygon.translate(m_painterTransform.map(center)); 0145 0146 Q_FOREACH (KisHandleStyle::IterationStyle it, m_handleStyle.handleIterations) { 0147 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop); 0148 m_painter->drawPolygon(handlePolygon); 0149 } 0150 } 0151 0152 void KisHandlePainterHelper::drawGradientHandle(const QPointF ¢er) 0153 { 0154 drawGradientHandle(center, 1.41 * m_handleRadius); 0155 } 0156 0157 void KisHandlePainterHelper::drawGradientCrossHandle(const QPointF ¢er, qreal radius) { 0158 KIS_SAFE_ASSERT_RECOVER_RETURN(m_painter); 0159 0160 { // Draw a cross 0161 QPainterPath p; 0162 p.moveTo(-radius, -radius); 0163 p.lineTo(radius, radius); 0164 p.moveTo(radius, -radius); 0165 p.lineTo(-radius, radius); 0166 0167 p = m_handleTransform.map(p); 0168 p.translate(m_painterTransform.map(center)); 0169 0170 Q_FOREACH (KisHandleStyle::IterationStyle it, m_handleStyle.handleIterations) { 0171 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop); 0172 m_painter->drawPath(p); 0173 } 0174 } 0175 0176 { // Draw a square 0177 const qreal halfRadius = 0.5 * radius; 0178 0179 QPolygonF handlePolygon; 0180 handlePolygon << QPointF(-halfRadius, 0); 0181 handlePolygon << QPointF(0, halfRadius); 0182 handlePolygon << QPointF(halfRadius, 0); 0183 handlePolygon << QPointF(0, -halfRadius); 0184 0185 handlePolygon = m_handleTransform.map(handlePolygon); 0186 handlePolygon.translate(m_painterTransform.map(center)); 0187 0188 Q_FOREACH (KisHandleStyle::IterationStyle it, m_handleStyle.handleIterations) { 0189 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop); 0190 m_painter->drawPolygon(handlePolygon); 0191 } 0192 } 0193 } 0194 0195 void KisHandlePainterHelper::drawArrow(const QPointF &pos, const QPointF &from, qreal radius) 0196 { 0197 KIS_SAFE_ASSERT_RECOVER_RETURN(m_painter); 0198 0199 QPainterPath p; 0200 0201 QLineF line(pos, from); 0202 line.setLength(radius); 0203 0204 QPointF norm = KisAlgebra2D::leftUnitNormal(pos - from); 0205 norm *= 0.34 * radius; 0206 0207 p.moveTo(line.p2() + norm); 0208 p.lineTo(line.p1()); 0209 p.lineTo(line.p2() - norm); 0210 0211 p.translate(-pos); 0212 0213 p = m_handleTransform.map(p).translated(m_painterTransform.map(pos)); 0214 0215 Q_FOREACH (KisHandleStyle::IterationStyle it, m_handleStyle.handleIterations) { 0216 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop); 0217 m_painter->drawPath(p); 0218 } 0219 } 0220 0221 void KisHandlePainterHelper::drawGradientArrow(const QPointF &start, const QPointF &end, qreal radius) 0222 { 0223 KIS_SAFE_ASSERT_RECOVER_RETURN(m_painter); 0224 0225 QPainterPath p; 0226 p.moveTo(start); 0227 p.lineTo(end); 0228 p = m_painterTransform.map(p); 0229 0230 Q_FOREACH (KisHandleStyle::IterationStyle it, m_handleStyle.lineIterations) { 0231 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop); 0232 m_painter->drawPath(p); 0233 } 0234 0235 const qreal length = kisDistance(start, end); 0236 const QPointF diff = end - start; 0237 0238 if (length > 5 * radius) { 0239 drawArrow(start + 0.33 * diff, start, radius); 0240 drawArrow(start + 0.66 * diff, start, radius); 0241 } else if (length > 3 * radius) { 0242 drawArrow(start + 0.5 * diff, start, radius); 0243 } 0244 } 0245 0246 void KisHandlePainterHelper::drawRubberLine(const QPolygonF &poly) { 0247 KIS_SAFE_ASSERT_RECOVER_RETURN(m_painter); 0248 0249 QPolygonF paintingPolygon = m_painterTransform.map(poly); 0250 0251 Q_FOREACH (KisHandleStyle::IterationStyle it, m_handleStyle.lineIterations) { 0252 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop); 0253 m_painter->drawPolygon(paintingPolygon); 0254 } 0255 } 0256 0257 void KisHandlePainterHelper::drawConnectionLine(const QLineF &line) 0258 { 0259 drawConnectionLine(line.p1(), line.p2()); 0260 } 0261 0262 void KisHandlePainterHelper::drawConnectionLine(const QPointF &p1, const QPointF &p2) 0263 { 0264 KIS_SAFE_ASSERT_RECOVER_RETURN(m_painter); 0265 0266 QPointF realP1 = m_painterTransform.map(p1); 0267 QPointF realP2 = m_painterTransform.map(p2); 0268 0269 Q_FOREACH (KisHandleStyle::IterationStyle it, m_handleStyle.lineIterations) { 0270 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop); 0271 m_painter->drawLine(realP1, realP2); 0272 } 0273 } 0274 0275 void KisHandlePainterHelper::drawPath(const QPainterPath &path) 0276 { 0277 const QPainterPath realPath = m_painterTransform.map(path); 0278 0279 Q_FOREACH (KisHandleStyle::IterationStyle it, m_handleStyle.lineIterations) { 0280 PenBrushSaver saver(it.isValid ? m_painter : 0, it.stylePair, PenBrushSaver::allow_noop); 0281 m_painter->drawPath(realPath); 0282 } 0283 } 0284 0285 void KisHandlePainterHelper::drawPixmap(const QPixmap &pixmap, QPointF position, int size, QRectF sourceRect) 0286 { 0287 QPointF handlePolygon = m_painterTransform.map(position); 0288 0289 QPoint offsetPosition(0, 40); 0290 handlePolygon += offsetPosition; 0291 0292 handlePolygon -= QPointF(size*0.5,size*0.5); 0293 0294 m_painter->drawPixmap(QRect(handlePolygon.x(), handlePolygon.y(), 0295 size, size), 0296 pixmap, 0297 sourceRect); 0298 } 0299 0300 void KisHandlePainterHelper::fillHandleRect(const QPointF ¢er, qreal radius, QColor fillColor, QPoint offset = QPoint(0,0)) 0301 { 0302 KIS_SAFE_ASSERT_RECOVER_RETURN(m_painter); 0303 0304 QRectF handleRect(-radius, -radius, 2 * radius, 2 * radius); 0305 QPolygonF handlePolygon = m_handleTransform.map(QPolygonF(handleRect)); 0306 handlePolygon.translate(m_painterTransform.map(center)); 0307 0308 QPainterPath painterPath; 0309 painterPath.addPolygon(handlePolygon); 0310 0311 // offset that happens after zoom transform. This means the offset will be the same, no matter the zoom level 0312 // this is good for UI elements that need to be below the bounding box 0313 painterPath.translate(offset); 0314 0315 const QPainterPath pathToSend = painterPath; 0316 const QBrush brushStyle(fillColor); 0317 m_painter->fillPath(pathToSend, brushStyle); 0318 }