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 }