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