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 }