File indexing completed on 2024-05-19 04:29:30

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 "KisVisualRectangleSelectorShape.h"
0007 
0008 #include <QColor>
0009 #include <QPainter>
0010 #include <QRect>
0011 #include <QList>
0012 #include <QLineF>
0013 #include <QtMath>
0014 
0015 #include "kis_debug.h"
0016 
0017 KisVisualRectangleSelectorShape::KisVisualRectangleSelectorShape(KisVisualColorSelector *parent,
0018                                                                  Dimensions dimension,
0019                                                                  int channel1, int channel2,
0020                                                                  int width,
0021                                                                  singelDTypes d)
0022     : KisVisualColorSelectorShape(parent, dimension, channel1, channel2)
0023 {
0024     //qDebug()  << "creating KisVisualRectangleSelectorShape" << this;
0025     m_type = d;
0026     m_barWidth = width;
0027 }
0028 
0029 KisVisualRectangleSelectorShape::~KisVisualRectangleSelectorShape()
0030 {
0031     //qDebug() << "deleting KisVisualRectangleSelectorShape" << this;
0032 }
0033 
0034 void KisVisualRectangleSelectorShape::setBorderWidth(int width)
0035 {
0036     m_barWidth = width;
0037 }
0038 
0039 void KisVisualRectangleSelectorShape::setOneDimensionalType(KisVisualRectangleSelectorShape::singelDTypes type)
0040 {
0041     if (type != m_type) {
0042         m_type = type;
0043         forceImageUpdate();
0044         update();
0045     }
0046 }
0047 
0048 QRect KisVisualRectangleSelectorShape::getSpaceForSquare(QRect geom)
0049 {
0050     return getAvailableSpace(geom, true);
0051 }
0052 
0053 QRect KisVisualRectangleSelectorShape::getSpaceForCircle(QRect geom)
0054 {
0055     return getAvailableSpace(geom, false);
0056 }
0057 
0058 QRect KisVisualRectangleSelectorShape::getSpaceForTriangle(QRect geom)
0059 {
0060     return getSpaceForSquare(geom);
0061 }
0062 
0063 QRect KisVisualRectangleSelectorShape::getAvailableSpace(QRect geom, bool stretch)
0064 {
0065     QPoint tl;
0066     QPoint br;
0067 
0068     if (m_type == KisVisualRectangleSelectorShape::vertical) {
0069         br = geom.bottomRight();
0070         tl = QPoint(geom.topLeft().x() + m_barWidth, geom.topLeft().y());
0071     } else if (m_type == KisVisualRectangleSelectorShape::horizontal) {
0072         br = geom.bottomRight();
0073         tl = QPoint(geom.topLeft().x(), geom.topLeft().y() + m_barWidth);
0074     } else {
0075         tl = QPoint(geom.topLeft().x() + m_barWidth, geom.topLeft().y() + m_barWidth);
0076         br = QPoint(geom.bottomRight().x() - m_barWidth, geom.bottomRight().y() - m_barWidth);
0077     }
0078     QRect available(tl, br);
0079     if (!stretch) {
0080         int size = qMin(available.height(), available.width());
0081         if (m_type == KisVisualRectangleSelectorShape::vertical) {
0082             available.moveTop(available.y() + (available.height() - size)/2);
0083         } else if (m_type == KisVisualRectangleSelectorShape::horizontal) {
0084             available.moveLeft(available.x() + (available.width() - size)/2);
0085         }
0086         available.setSize(QSize(size, size));
0087     }
0088 
0089     return available;
0090 }
0091 
0092 QPointF KisVisualRectangleSelectorShape::convertShapeCoordinateToWidgetCoordinate(QPointF coordinate) const
0093 {
0094     // Reminder: in Qt widget space, origin is top-left, but we want zero y to be at the bottom
0095     qreal x = 0.5 * width();
0096     qreal y = 0.5 * height();
0097     qreal offset = 5.0;
0098     KisVisualColorSelectorShape::Dimensions dimension = getDimensions();
0099     if (dimension == KisVisualColorSelectorShape::onedimensional) {
0100         if ( m_type == KisVisualRectangleSelectorShape::vertical) {
0101             y = qMin((1.0 - coordinate.x())*(height()-offset*2)+offset, (qreal)height());
0102         } else if (m_type == KisVisualRectangleSelectorShape::horizontal) {
0103             x = qMin(coordinate.x()*(width()-offset*2)+offset, (qreal)width());
0104         } else if (m_type == KisVisualRectangleSelectorShape::border) {
0105 
0106             QRectF innerRect(m_barWidth/2, m_barWidth/2, width()-m_barWidth, height()-m_barWidth);
0107             QPointF left (innerRect.left(),innerRect.center().y());
0108             QList <QLineF> polygonLines;
0109             polygonLines.append(QLineF(left, innerRect.topLeft()));
0110             polygonLines.append(QLineF(innerRect.topLeft(), innerRect.topRight()));
0111             polygonLines.append(QLineF(innerRect.topRight(), innerRect.bottomRight()));
0112             polygonLines.append(QLineF(innerRect.bottomRight(), innerRect.bottomLeft()));
0113             polygonLines.append(QLineF(innerRect.bottomLeft(), left));
0114 
0115             qreal totalLength =0.0;
0116             Q_FOREACH(QLineF line, polygonLines) {
0117                 totalLength += line.length();
0118             }
0119 
0120             qreal length = coordinate.x()*totalLength;
0121             QPointF intersect(x,y);
0122             Q_FOREACH(QLineF line, polygonLines) {
0123                 if (line.length()>length && length>0){
0124                     intersect = line.pointAt(length/line.length());
0125 
0126                 }
0127                 length-=line.length();
0128             }
0129             x = qRound(intersect.x());
0130             y = qRound(intersect.y());
0131 
0132         }
0133         else /*if (m_type == KisVisualRectangleSelectorShape::borderMirrored)*/  {
0134 
0135             QRectF innerRect(m_barWidth/2, m_barWidth/2, width()-m_barWidth, height()-m_barWidth);
0136             QPointF bottom (innerRect.center().x(), innerRect.bottom());
0137             QList <QLineF> polygonLines;
0138             polygonLines.append(QLineF(bottom, innerRect.bottomLeft()));
0139             polygonLines.append(QLineF(innerRect.bottomLeft(), innerRect.topLeft()));
0140             polygonLines.append(QLineF(innerRect.topLeft(), innerRect.topRight()));
0141             polygonLines.append(QLineF(innerRect.topRight(), innerRect.bottomRight()));
0142             polygonLines.append(QLineF(innerRect.bottomRight(), bottom));
0143 
0144             qreal totalLength =0.0;
0145             Q_FOREACH(QLineF line, polygonLines) {
0146                 totalLength += line.length();
0147             }
0148 
0149             qreal length = coordinate.x()*(totalLength/2);
0150             QPointF intersect(x,y);
0151             if (coordinate.y()==1) {
0152                 for (int i = polygonLines.size()-1; i==0; i--) {
0153                     QLineF line = polygonLines.at(i);
0154                     if (line.length()>length && length>0){
0155                         intersect = line.pointAt(length/line.length());
0156 
0157                     }
0158                     length-=line.length();
0159                 }
0160             } else {
0161                 Q_FOREACH(QLineF line, polygonLines) {
0162                     if (line.length()>length && length>0){
0163                         intersect = line.pointAt(length/line.length());
0164 
0165                     }
0166                     length-=line.length();
0167                 }
0168             }
0169             x = qRound(intersect.x());
0170             y = qRound(intersect.y());
0171 
0172         }
0173     } else {
0174         x = qMin(coordinate.x()*(width()-offset*2)+offset, (qreal)width());
0175         y = qMin((1.0 - coordinate.y())*(height()-offset*2)+offset, (qreal)height());
0176     }
0177     return QPointF(x,y);
0178 }
0179 
0180 QPointF KisVisualRectangleSelectorShape::convertWidgetCoordinateToShapeCoordinate(QPointF coordinate) const
0181 {
0182     // Reminder: in Qt widget space, origin is top-left, but we want zero y to be at the bottom
0183     //default values:
0184     qreal x = 0.5;
0185     qreal y = 0.5;
0186     qreal offset = 5.0;
0187     KisVisualColorSelectorShape::Dimensions dimension = getDimensions();
0188     if (dimension == KisVisualColorSelectorShape::onedimensional ) {
0189         if (m_type == KisVisualRectangleSelectorShape::vertical) {
0190             x = 1.0 - (coordinate.y()-offset)/(height()-offset*2);
0191         } else if (m_type == KisVisualRectangleSelectorShape::horizontal) {
0192             x = (coordinate.x()-offset)/(width()-offset*2);
0193         } else if (m_type == KisVisualRectangleSelectorShape::border) {
0194             //border
0195 
0196             QRectF innerRect(m_barWidth, m_barWidth, width()-(m_barWidth*2), height()-(m_barWidth*2));
0197             QPointF left (innerRect.left(),innerRect.center().y());
0198             QList <QLineF> polygonLines;
0199             polygonLines.append(QLineF(left, innerRect.topLeft()));
0200             polygonLines.append(QLineF(innerRect.topLeft(), innerRect.topRight()));
0201             polygonLines.append(QLineF(innerRect.topRight(), innerRect.bottomRight()));
0202             polygonLines.append(QLineF(innerRect.bottomRight(), innerRect.bottomLeft()));
0203             polygonLines.append(QLineF(innerRect.bottomLeft(), left));
0204 
0205             QLineF radius(coordinate, this->geometry().center());
0206             QPointF intersect(0.5,0.5);
0207             qreal length = 0.0;
0208             qreal totalLength = 0.0;
0209             bool foundIntersect = false;
0210             Q_FOREACH(QLineF line, polygonLines) {
0211                 if (line.intersect(radius,&intersect)==QLineF::BoundedIntersection && foundIntersect==false)
0212                 {
0213                     foundIntersect = true;
0214                     length+=QLineF(line.p1(), intersect).length();
0215 
0216                 }
0217                 if (foundIntersect==false) {
0218                     length+=line.length();
0219                 }
0220                 totalLength+=line.length();
0221             }
0222 
0223             x = length/totalLength;
0224 
0225         } else /*if (m_type == KisVisualRectangleSelectorShape::borderMirrored)*/  {
0226             //border
0227 
0228             QRectF innerRect(m_barWidth, m_barWidth, width()-(m_barWidth*2), height()-(m_barWidth*2));
0229             QPointF bottom (innerRect.center().x(), innerRect.bottom());
0230             QList <QLineF> polygonLines;
0231             polygonLines.append(QLineF(bottom, innerRect.bottomLeft()));
0232             polygonLines.append(QLineF(innerRect.bottomLeft(), innerRect.topLeft()));
0233             polygonLines.append(QLineF(innerRect.topLeft(), innerRect.topRight()));
0234             polygonLines.append(QLineF(innerRect.topRight(), innerRect.bottomRight()));
0235             polygonLines.append(QLineF(innerRect.bottomRight(), bottom));
0236 
0237             QLineF radius(coordinate, this->geometry().center());
0238             QPointF intersect(0.5,0.5);
0239             qreal length = 0.0;
0240             qreal totalLength = 0.0;
0241             bool foundIntersect = false;
0242             Q_FOREACH(QLineF line, polygonLines) {
0243                 if (line.intersect(radius,&intersect)==QLineF::BoundedIntersection && foundIntersect==false)
0244                 {
0245                     foundIntersect = true;
0246                     length+=QLineF(line.p1(), intersect).length();
0247 
0248                 }
0249                 if (foundIntersect==false) {
0250                     length+=line.length();
0251                 }
0252                 totalLength+=line.length();
0253             }
0254             int halflength = totalLength/2;
0255 
0256             if (length>halflength) {
0257                 x = (halflength - (length-halflength))/halflength;
0258                 y = 1.0;
0259             } else {
0260                 x = length/halflength;
0261                 y = 0.0;
0262             }
0263         }
0264     }
0265     else {
0266         x = (coordinate.x()-offset)/(width()-offset*2);
0267         y = 1.0 - (coordinate.y()-offset)/(height()-offset*2);
0268     }
0269     x = qBound((qreal)0.0, x, (qreal)1.0);
0270     y = qBound((qreal)0.0, y, (qreal)1.0);
0271     return QPointF(x, y);
0272 }
0273 
0274 QRegion KisVisualRectangleSelectorShape::getMaskMap()
0275 {
0276     QRegion mask = QRegion(0,0,width(),height());
0277     if (m_type==KisVisualRectangleSelectorShape::border || m_type==KisVisualRectangleSelectorShape::borderMirrored) {
0278         mask = mask.subtracted(QRegion(m_barWidth, m_barWidth, width()-(m_barWidth*2), height()-(m_barWidth*2)));
0279     }
0280     return mask;
0281 }
0282 
0283 QImage KisVisualRectangleSelectorShape::renderAlphaMask() const
0284 {
0285     // Hi-DPI aware rendering requires that we determine the device pixel dimension;
0286     // actual widget size in device pixels is not accessible unfortunately, it might be 1px smaller...
0287     const int deviceWidth = qCeil(width() * devicePixelRatioF());
0288     const int deviceHeight = qCeil(height() * devicePixelRatioF());
0289 
0290     QImage alphaMask(deviceWidth, deviceHeight, QImage::Format_Alpha8);
0291     alphaMask.fill(0);
0292     alphaMask.setDevicePixelRatio(devicePixelRatioF());
0293     QPainter painter(&alphaMask);
0294     painter.setRenderHint(QPainter::Antialiasing);
0295     painter.setBrush(Qt::white);
0296     painter.setPen(Qt::NoPen);
0297 
0298     if (getDimensions() == onedimensional) {
0299         if (m_type == KisVisualRectangleSelectorShape::vertical) {
0300             painter.drawRect(2, 3, width() - 4, height() - 6);
0301         } else if (m_type == KisVisualRectangleSelectorShape::horizontal) {
0302             painter.drawRect(3, 2, width() - 6, height() - 4);
0303         } else /*if (m_type == border || m_type == borderMirrored)*/ {
0304             painter.drawRect(2, 2, width() - 4, height() - 4);
0305             painter.setBrush(Qt::black);
0306             painter.drawRect(m_barWidth, m_barWidth, width() - 2 * m_barWidth, height() - 2 * m_barWidth);
0307         }
0308     } else {
0309         painter.drawRect(3, 3, width() - 6, height() - 6);
0310     }
0311 
0312     return alphaMask;
0313 }
0314 
0315 void KisVisualRectangleSelectorShape::drawCursor(QPainter &painter)
0316 {
0317     //qDebug() << this << "KisVisualRectangleSelectorShape::drawCursor: image needs update" << imagesNeedUpdate();
0318     QPointF cursorPoint = convertShapeCoordinateToWidgetCoordinate(getCursorPosition());
0319     QColor col = getColorFromConverter(getCurrentColor());
0320     QBrush fill(Qt::SolidPattern);
0321 
0322     int cursorwidth = 5;
0323 
0324     if (getDimensions() == KisVisualColorSelectorShape::onedimensional
0325             && m_type != KisVisualRectangleSelectorShape::border
0326             && m_type != KisVisualRectangleSelectorShape::borderMirrored) {
0327         QRectF rect;
0328         if (m_type==KisVisualRectangleSelectorShape::vertical) {
0329             qreal y = qRound(cursorPoint.y());
0330             rect.setCoords(1.5, y - cursorwidth + 0.5, width() - 1.5, y + cursorwidth - 0.5);
0331         } else {
0332             qreal x = qRound(cursorPoint.x());
0333             rect.setCoords(x - cursorwidth  + 0.5, 1.5, x + cursorwidth - 0.5, height() - 1.5);
0334         }
0335         painter.setPen(Qt::white);
0336         fill.setColor(Qt::white);
0337         painter.setBrush(fill);
0338         painter.drawRect(rect);
0339         fill.setColor(col);
0340         painter.setPen(Qt::black);
0341         painter.setBrush(fill);
0342         painter.drawRect(rect.adjusted(1, 1, -1, -1));
0343 
0344     }else if(m_type==KisVisualRectangleSelectorShape::borderMirrored){
0345         QRectF innerRect(m_barWidth, m_barWidth, width()-(m_barWidth*2), height()-(m_barWidth*2));
0346         painter.setPen(Qt::white);
0347         fill.setColor(Qt::white);
0348         painter.setBrush(fill);
0349         painter.drawEllipse(cursorPoint, cursorwidth, cursorwidth);
0350         QPoint mirror(innerRect.center().x()+(innerRect.center().x()-cursorPoint.x()),cursorPoint.y());
0351         painter.drawEllipse(mirror, cursorwidth, cursorwidth);
0352         fill.setColor(col);
0353         painter.setPen(Qt::black);
0354         painter.setBrush(fill);
0355         painter.drawEllipse(cursorPoint, cursorwidth-1, cursorwidth-1);
0356         painter.drawEllipse(mirror, cursorwidth-1, cursorwidth-1);
0357 
0358     } else {
0359         painter.setPen(Qt::white);
0360         fill.setColor(Qt::white);
0361         painter.setBrush(fill);
0362         painter.drawEllipse(cursorPoint, cursorwidth, cursorwidth);
0363         fill.setColor(col);
0364         painter.setPen(Qt::black);
0365         painter.setBrush(fill);
0366         painter.drawEllipse(cursorPoint, cursorwidth-1.0, cursorwidth-1.0);
0367     }
0368 }