File indexing completed on 2024-05-12 16:02:06

0001 /*
0002  * SPDX-FileCopyrightText: 2016 Wolthera van Hovell tot Westerflier <griffinvalley@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 #include "KisVisualEllipticalSelectorShape.h"
0007 
0008 #include <QColor>
0009 #include <QPainter>
0010 #include <QRect>
0011 #include <QLineF>
0012 #include <QtMath>
0013 
0014 #include "kis_debug.h"
0015 #include "kis_global.h"
0016 
0017 KisVisualEllipticalSelectorShape::KisVisualEllipticalSelectorShape(QWidget *parent,
0018                                                                  Dimensions dimension,
0019                                                                  const KoColorSpace *cs,
0020                                                                  int channel1, int channel2,
0021                                                                  const KoColorDisplayRendererInterface *displayRenderer,
0022                                                                  int barWidth,
0023                                                                  singelDTypes d)
0024     : KisVisualColorSelectorShape(parent, dimension, cs, channel1, channel2, displayRenderer)
0025 {
0026     //qDebug() << "creating KisVisualEllipticalSelectorShape" << this;
0027     m_type = d;
0028     m_barWidth = barWidth;
0029 }
0030 
0031 KisVisualEllipticalSelectorShape::~KisVisualEllipticalSelectorShape()
0032 {
0033     //qDebug() << "deleting KisVisualEllipticalSelectorShape" << this;
0034 }
0035 
0036 QSize KisVisualEllipticalSelectorShape::sizeHint() const
0037 {
0038     return QSize(180,180);
0039 }
0040 
0041 void KisVisualEllipticalSelectorShape::setBorderWidth(int width)
0042 {
0043     m_barWidth = width;
0044     forceImageUpdate();
0045     update();
0046 }
0047 
0048 QRect KisVisualEllipticalSelectorShape::getSpaceForSquare(QRect geom)
0049 {
0050     int sizeValue = qMin(width(),height());
0051     QRect b(geom.left(), geom.top(), sizeValue, sizeValue);
0052     QLineF radius(b.center(), QPointF(b.left()+m_barWidth, b.center().y()) );
0053     radius.setAngle(135);
0054     QPointF tl = radius.p2();
0055     radius.setAngle(315);
0056     QPointF br = radius.p2();
0057     QRect r(tl.toPoint(), br.toPoint());
0058     return r;
0059 }
0060 
0061 QRect KisVisualEllipticalSelectorShape::getSpaceForCircle(QRect geom)
0062 {
0063     int sizeValue = qMin(width(),height());
0064     QRect b(geom.left(), geom.top(), sizeValue, sizeValue);
0065     QPointF tl = QPointF (b.topLeft().x()+m_barWidth, b.topLeft().y()+m_barWidth);
0066     QPointF br = QPointF (b.bottomRight().x()-m_barWidth, b.bottomRight().y()-m_barWidth);
0067     QRect r(tl.toPoint(), br.toPoint());
0068     return r;
0069 }
0070 
0071 QRect KisVisualEllipticalSelectorShape::getSpaceForTriangle(QRect geom)
0072 {
0073     int sizeValue = qMin(width(),height());
0074     QPointF center(0.5 * width(), 0.5 * height());
0075     qreal radius = 0.5 * sizeValue - (m_barWidth + 4);
0076     QLineF rLine(center, QPointF(center.x() + radius, center.y()));
0077     rLine.setAngle(330);
0078     QPoint br(rLine.p2().toPoint());
0079     //QPoint br(qCeil(rLine.p2().x()), qCeil(rLine.p2().y()));
0080     QPoint tl(width() - br.x(), m_barWidth + 4);
0081     QRect bound(tl, br);
0082     // adjust with triangle default margin for cursor rendering
0083     // it's not +5 because above calculation is for pixel center and ignores
0084     // the fact that dimensions are then effectively 1px smaller...
0085     bound.adjust(-5, -5, 4, 4);
0086     return bound.intersected(geom);
0087 }
0088 
0089 QPointF KisVisualEllipticalSelectorShape::convertShapeCoordinateToWidgetCoordinate(QPointF coordinate) const
0090 {
0091     qreal offset = 7.0;
0092     qreal a = (qreal)width()*0.5;
0093     QPointF center(a, a);
0094     QLineF line(center, QPoint((m_barWidth*0.5),a));
0095     qreal angle = coordinate.x()*360.0;
0096     angle = 360.0 - fmod(angle+180.0, 360.0);
0097     if (m_type==KisVisualEllipticalSelectorShape::borderMirrored) {
0098         angle = (coordinate.x()/2)*360.0;
0099         angle = fmod((angle+270.0), 360.0);
0100     }
0101     line.setAngle(angle);
0102     if (getDimensions()!=KisVisualColorSelectorShape::onedimensional) {
0103         line.setLength(qMin(coordinate.y()*(a-offset), a-offset));
0104     }
0105     return line.p2();
0106 }
0107 
0108 QPointF KisVisualEllipticalSelectorShape::convertWidgetCoordinateToShapeCoordinate(QPointF coordinate) const
0109 {
0110     //default implementation:
0111     qreal x = 0.5;
0112     qreal y = 1.0;
0113     qreal offset = 7.0;
0114     QPointF center = QRectF(QPointF(0.0, 0.0), this->size()).center();
0115     qreal a = (qreal(this->width()) / qreal(2));
0116     qreal xRel = center.x()-coordinate.x();
0117     qreal yRel = center.y()-coordinate.y();
0118     qreal radius = sqrt(xRel*xRel+yRel*yRel);
0119 
0120     if (m_type!=KisVisualEllipticalSelectorShape::borderMirrored){
0121         qreal angle = atan2(yRel, xRel);
0122         angle = kisRadiansToDegrees(angle);
0123         angle = fmod(angle+360, 360.0);
0124         x = angle/360.0;
0125         if (getDimensions()==KisVisualColorSelectorShape::twodimensional) {
0126             y = qBound(0.0,radius/(a-offset), 1.0);
0127         }
0128 
0129     } else {
0130         qreal angle = atan2(xRel, yRel);
0131         angle = kisRadiansToDegrees(angle);
0132         angle = fmod(angle+180, 360.0);
0133         if (angle>180.0) {
0134             angle = 360.0-angle;
0135         }
0136         x = (angle/360.0)*2;
0137         if (getDimensions()==KisVisualColorSelectorShape::twodimensional) {
0138             y = qBound(0.0,(radius+offset)/a, 1.0);
0139         }
0140     }
0141 
0142     return QPointF(x, y);
0143 }
0144 
0145 QPointF KisVisualEllipticalSelectorShape::mousePositionToShapeCoordinate(const QPointF &pos, const QPointF &dragStart) const
0146 {
0147     QPointF pos2(pos);
0148     if (m_type == KisVisualEllipticalSelectorShape::borderMirrored) {
0149         qreal h_center = width()/2.0;
0150         bool start_left = dragStart.x() < h_center;
0151         bool cursor_left = pos.x() < h_center;
0152         if (start_left != cursor_left) {
0153             pos2.setX(h_center);
0154         }
0155     }
0156     return convertWidgetCoordinateToShapeCoordinate(pos2);
0157 }
0158 
0159 QRegion KisVisualEllipticalSelectorShape::getMaskMap()
0160 {
0161     QRegion mask = QRegion(0,0,width(),height(), QRegion::Ellipse);
0162     if (getDimensions()==KisVisualColorSelectorShape::onedimensional) {
0163         mask = mask.subtracted(QRegion(m_barWidth, m_barWidth, width()-(m_barWidth*2), height()-(m_barWidth*2), QRegion::Ellipse));
0164     }
0165     return mask;
0166 }
0167 
0168 QImage KisVisualEllipticalSelectorShape::renderAlphaMask() const
0169 {
0170     // Hi-DPI aware rendering requires that we determine the device pixel dimension;
0171     // actual widget size in device pixels is not accessible unfortunately, it might be 1px smaller...
0172     const int deviceWidth = qCeil(width() * devicePixelRatioF());
0173     const int deviceHeight = qCeil(height() * devicePixelRatioF());
0174 
0175     QImage alphaMask(deviceWidth, deviceHeight, QImage::Format_Alpha8);
0176     alphaMask.fill(0);
0177     alphaMask.setDevicePixelRatio(devicePixelRatioF());
0178     QPainter painter(&alphaMask);
0179     painter.setRenderHint(QPainter::Antialiasing);
0180     painter.setBrush(Qt::white);
0181     painter.setPen(Qt::NoPen);
0182     painter.drawEllipse(2, 2, width() - 4, height() - 4);
0183     //painter.setBrush(Qt::black);
0184     if (getDimensions() == KisVisualColorSelectorShape::onedimensional) {
0185         painter.setCompositionMode(QPainter::CompositionMode_Clear);
0186         painter.drawEllipse(m_barWidth - 2, m_barWidth - 2, width() - 2*(m_barWidth-2), height() - 2*(m_barWidth-2));
0187     }
0188     return alphaMask;
0189 }
0190 
0191 void KisVisualEllipticalSelectorShape::drawCursor()
0192 {
0193     //qDebug() << this << "KisVisualEllipticalSelectorShape::drawCursor: image needs update" << imagesNeedUpdate();
0194     QPointF cursorPoint = convertShapeCoordinateToWidgetCoordinate(getCursorPosition());
0195     QImage fullSelector = getImageMap();
0196     QColor col = getColorFromConverter(getCurrentColor());
0197     QPainter painter;
0198     painter.begin(&fullSelector);
0199     painter.setRenderHint(QPainter::Antialiasing);
0200     QBrush fill;
0201     fill.setStyle(Qt::SolidPattern);
0202 
0203     int cursorwidth = 5;
0204 
0205     if (m_type==KisVisualEllipticalSelectorShape::borderMirrored) {
0206         painter.setPen(Qt::white);
0207         fill.setColor(Qt::white);
0208         painter.setBrush(fill);
0209         painter.drawEllipse(cursorPoint, cursorwidth, cursorwidth);
0210         QPointF mirror(width() - cursorPoint.x(), cursorPoint.y());
0211         painter.drawEllipse(mirror, cursorwidth, cursorwidth);
0212         fill.setColor(col);
0213         painter.setPen(Qt::black);
0214         painter.setBrush(fill);
0215         painter.drawEllipse(cursorPoint, cursorwidth-1, cursorwidth-1);
0216         painter.drawEllipse(mirror, cursorwidth-1, cursorwidth-1);
0217 
0218     } else {
0219         painter.setPen(Qt::white);
0220         fill.setColor(Qt::white);
0221         painter.setBrush(fill);
0222         painter.drawEllipse(cursorPoint, cursorwidth, cursorwidth);
0223         fill.setColor(col);
0224         painter.setPen(Qt::black);
0225         painter.setBrush(fill);
0226         painter.drawEllipse(cursorPoint, cursorwidth-1.0, cursorwidth-1.0);
0227     }
0228     painter.end();
0229     setFullImage(fullSelector);
0230 }