Warning, file /office/calligra/libs/widgets/KoTriangleColorSelector.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 * Copyright (c) 2008 Cyrille Berger <cberger@cberger.net> 0003 * 0004 * This library is free software; you can redistribute it and/or modify 0005 * it under the terms of the GNU Lesser General Public License as published by 0006 * the Free Software Foundation; either version 2.1 of the License, 0007 * or (at your option) any later version. 0008 * 0009 * This library is distributed in the hope that it will be useful, 0010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0012 * GNU Lesser General Public License for more details. 0013 * 0014 * You should have received a copy of the GNU Lesser General Public License 0015 * along with this program; if not, write to the Free Software 0016 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 0017 */ 0018 0019 #include "KoTriangleColorSelector.h" 0020 #include <math.h> 0021 0022 #include <QMouseEvent> 0023 #include <QPainter> 0024 #include <QPixmap> 0025 #include <QTimer> 0026 #include <KoColorSpaceRegistry.h> 0027 #include <KoColorConversions.h> 0028 #include <KoColorDisplayRendererInterface.h> 0029 0030 0031 enum CurrentHandle { 0032 NoHandle, 0033 HueHandle, 0034 ValueSaturationHandle }; 0035 0036 struct Q_DECL_HIDDEN KoTriangleColorSelector::Private { 0037 Private(KoTriangleColorSelector *_q, const KoColorDisplayRendererInterface *_displayRenderer) 0038 : q(_q), 0039 displayRenderer(_displayRenderer), 0040 hue(0), 0041 saturation(0), 0042 value(0), 0043 updateAllowed(true), 0044 invalidTriangle(true), 0045 lastX(-1), 0046 lastY(-1) 0047 { 0048 } 0049 0050 KoTriangleColorSelector *q; 0051 const KoColorDisplayRendererInterface *displayRenderer; 0052 QPixmap wheelPixmap; 0053 QPixmap trianglePixmap; 0054 int hue; 0055 int saturation; 0056 int value; 0057 int sizeColorSelector; 0058 qreal centerColorSelector; 0059 qreal wheelWidthProportion; 0060 qreal wheelWidth; 0061 qreal wheelNormExt; 0062 qreal wheelNormInt; 0063 qreal wheelInnerRadius; 0064 qreal triangleRadius; 0065 qreal triangleLength; 0066 qreal triangleHeight; 0067 qreal triangleBottom; 0068 qreal triangleTop; 0069 qreal normExt; 0070 qreal normInt; 0071 bool updateAllowed; 0072 CurrentHandle handle; 0073 qreal triangleHandleSize; 0074 bool invalidTriangle; 0075 int lastX, lastY; 0076 QTimer updateTimer; 0077 0078 void init(); 0079 }; 0080 0081 void KoTriangleColorSelector::Private::init() 0082 { 0083 q->setMinimumHeight( 100 ); 0084 q->setMinimumWidth( 100 ); 0085 q->setMouseTracking( true ); 0086 q->updateTriangleCircleParameters(); 0087 updateTimer.setInterval(1); 0088 updateTimer.setSingleShot(true); 0089 q->connect(&updateTimer, SIGNAL(timeout()), q, SLOT(update())); 0090 } 0091 0092 KoTriangleColorSelector::KoTriangleColorSelector(QWidget* parent) 0093 : QWidget(parent), 0094 d(new Private(this, KoDumbColorDisplayRenderer::instance())) 0095 { 0096 d->init(); 0097 } 0098 0099 KoTriangleColorSelector::KoTriangleColorSelector(const KoColorDisplayRendererInterface *displayRenderer, QWidget *parent) 0100 : QWidget(parent), 0101 d(new Private(this, displayRenderer)) 0102 { 0103 d->init(); 0104 connect(displayRenderer, SIGNAL(displayConfigurationChanged()), this, SLOT(configurationChanged())); 0105 } 0106 0107 KoTriangleColorSelector::~KoTriangleColorSelector() 0108 { 0109 delete d; 0110 } 0111 0112 void KoTriangleColorSelector::updateTriangleCircleParameters() 0113 { 0114 d->sizeColorSelector = qMin(width(), height()); 0115 d->centerColorSelector = 0.5 * d->sizeColorSelector; 0116 d->wheelWidthProportion = 0.25; 0117 d->wheelWidth = d->centerColorSelector * d->wheelWidthProportion; 0118 d->wheelNormExt = qAbs( d->centerColorSelector ); 0119 d->wheelNormInt = qAbs( d->centerColorSelector * (1.0 - d->wheelWidthProportion)); 0120 d->wheelInnerRadius = d->centerColorSelector * (1.0 - d->wheelWidthProportion); 0121 d->triangleRadius = d->wheelInnerRadius * 0.9; 0122 d->triangleLength = 3.0 / sqrt(3.0) * d->triangleRadius; 0123 d->triangleHeight = d->triangleLength * sqrt(3.0) * 0.5; 0124 d->triangleTop = 0.5 * d->sizeColorSelector - d->triangleRadius; 0125 d->triangleBottom = d->triangleHeight + d->triangleTop; 0126 d->triangleHandleSize = 10.0; 0127 } 0128 0129 void KoTriangleColorSelector::paintEvent( QPaintEvent * event ) 0130 { 0131 0132 if( d->invalidTriangle ) 0133 { 0134 generateTriangle(); 0135 } 0136 Q_UNUSED(event); 0137 QPainter p(this); 0138 p.setRenderHint(QPainter::SmoothPixmapTransform); 0139 p.setRenderHint(QPainter::Antialiasing); 0140 QPointF pos(d->centerColorSelector, d->centerColorSelector); 0141 p.translate(QPointF( 0.5*width(), 0.5*height() ) ); 0142 // Draw the wheel 0143 p.drawPixmap( -pos, d->wheelPixmap ); 0144 // Draw the triangle 0145 p.save(); 0146 0147 p.rotate( hue() + 150 ); 0148 0149 0150 p.drawPixmap( -pos , d->trianglePixmap ); 0151 // Draw selectors 0152 p.restore(); 0153 // Draw value,saturation selector 0154 // Compute coordinates 0155 { 0156 qreal vs_selector_ypos_ = value() / 255.0; 0157 qreal ls_ = (vs_selector_ypos_) * d->triangleLength; // length of the saturation on the triangle 0158 qreal vs_selector_xpos_ = ls_ * (saturation() / 255.0 - 0.5); 0159 // Draw it 0160 p.save(); 0161 p.setPen( QPen( Qt::white, 1.0) ); 0162 0163 QColor currentColor = d->displayRenderer->toQColor(color()); 0164 0165 p.setBrush(currentColor); 0166 p.rotate( hue() + 150 ); 0167 p.drawEllipse( QRectF( -d->triangleHandleSize*0.5 + vs_selector_xpos_, 0168 -d->triangleHandleSize*0.5 - (d->centerColorSelector - d->triangleTop) + vs_selector_ypos_ * d->triangleHeight, 0169 d->triangleHandleSize , d->triangleHandleSize )); 0170 } 0171 p.restore(); 0172 // Draw Hue selector 0173 p.save(); 0174 p.setPen( QPen( Qt::white, 1.0) ); 0175 p.rotate( hue() - 90 ); 0176 qreal hueSelectorWidth_ = 0.8; 0177 qreal hueSelectorOffset_ = 0.5 *( 1.0 - hueSelectorWidth_) * d->wheelWidth; 0178 qreal hueSelectorSize_ = 0.8 * d->wheelWidth; 0179 p.drawRect( QRectF( -1.5, -d->centerColorSelector + hueSelectorOffset_, 3.0, hueSelectorSize_ )); 0180 p.restore(); 0181 p.end(); 0182 } 0183 0184 0185 // make sure to always use get/set functions when managing HSV properties( don't call directly like d->hue) 0186 // these settings get updated A LOT when the color picker is being used. You might get unexpected results 0187 int KoTriangleColorSelector::hue() const 0188 { 0189 return d->hue; 0190 } 0191 0192 void KoTriangleColorSelector::setHue(int h) 0193 { 0194 // setColor() will give you -1 when saturation is 0 0195 // ignore setting hue in this instance. otherwise it will mess up the hue ring 0196 if (h == -1) 0197 return; 0198 0199 0200 h = qBound(0, h, 359); 0201 d->hue = h; 0202 tellColorChanged(); 0203 d->invalidTriangle = true; 0204 d->updateTimer.start(); 0205 } 0206 0207 int KoTriangleColorSelector::value() const 0208 { 0209 return d->value; 0210 } 0211 0212 void KoTriangleColorSelector::setValue(int v) 0213 { 0214 v = qBound(0, v, 255); 0215 d->value = v; 0216 tellColorChanged(); 0217 d->invalidTriangle = true; 0218 d->updateTimer.start(); 0219 } 0220 0221 int KoTriangleColorSelector::saturation() const 0222 { 0223 return d->saturation; 0224 } 0225 0226 void KoTriangleColorSelector::setSaturation(int s) 0227 { 0228 s = qBound(0, s, 255); 0229 d->saturation = s; 0230 tellColorChanged(); 0231 d->invalidTriangle = true; 0232 d->updateTimer.start(); 0233 } 0234 0235 void KoTriangleColorSelector::setHSV(int h, int s, int v) 0236 { 0237 d->invalidTriangle = (hue() != h); 0238 setHue(h); 0239 setValue(v); 0240 setSaturation(s); 0241 } 0242 0243 KoColor KoTriangleColorSelector::color() const 0244 { 0245 return d->displayRenderer->fromHsv(hue(), saturation(), value()); 0246 } 0247 0248 void KoTriangleColorSelector::setColor(const KoColor & _color) 0249 { 0250 if ( color() == _color) 0251 return; 0252 0253 //displayrenderer->getHsv is what sets the foreground color in the application 0254 if(d->updateAllowed) { 0255 int hueRef = hue(); 0256 int saturationRef = saturation(); 0257 int valueRef = value(); 0258 0259 d->displayRenderer->getHsv(_color, &hueRef, &saturationRef, &valueRef); 0260 setHSV(hueRef, saturationRef, valueRef); 0261 0262 d->invalidTriangle = true; 0263 d->updateTimer.start(); 0264 } 0265 } 0266 0267 void KoTriangleColorSelector::resizeEvent( QResizeEvent * event ) 0268 { 0269 QWidget::resizeEvent( event ); 0270 updateTriangleCircleParameters(); 0271 generateWheel(); 0272 d->invalidTriangle = true; 0273 } 0274 0275 inline qreal pow2(qreal v) 0276 { 0277 return v*v; 0278 } 0279 0280 void KoTriangleColorSelector::tellColorChanged() 0281 { 0282 d->updateAllowed = false; 0283 emit colorChanged(color()); 0284 d->updateAllowed = true; 0285 } 0286 0287 void KoTriangleColorSelector::generateTriangle() 0288 { 0289 QImage image(d->sizeColorSelector, d->sizeColorSelector, QImage::Format_ARGB32); 0290 // Length of triangle 0291 int hue_ = hue(); 0292 0293 for(int y = 0; y < d->sizeColorSelector; ++y) 0294 { 0295 qreal ynormalize = ( d->triangleTop - y ) / ( d->triangleTop - d->triangleBottom ); 0296 qreal v = 255 * ynormalize; 0297 qreal ls_ = (ynormalize) * d->triangleLength; 0298 qreal startx_ = d->centerColorSelector - 0.5 * ls_; 0299 uint* data = reinterpret_cast<uint*>(image.scanLine(y)); 0300 for(int x = 0; x < d->sizeColorSelector; ++x, ++data) 0301 { 0302 qreal s = 255 * (x - startx_) / ls_; 0303 if(v < -1.0 || v > 256.0 || s < -1.0 || s > 256.0 ) 0304 { 0305 *data = qRgba(0,0,0,0); 0306 } else { 0307 qreal va = 1.0, sa = 1.0; 0308 if( v < 0.0) { va = 1.0 + v; v = 0; } 0309 else if( v > 255.0 ) { va = 256.0 - v; v = 255; } 0310 if( s < 0.0) { sa = 1.0 + s; s = 0; } 0311 else if( s > 255.0 ) { sa = 256.0 - s; s = 255; } 0312 qreal coeff = va * sa; 0313 0314 KoColor color = d->displayRenderer->fromHsv(hue_, s, v, int(coeff * 255.0)); 0315 QColor qcolor = d->displayRenderer->toQColor(color); 0316 0317 *data = qcolor.rgba(); 0318 } 0319 } 0320 } 0321 0322 d->trianglePixmap = QPixmap::fromImage(image); 0323 d->invalidTriangle = false; 0324 } 0325 0326 void KoTriangleColorSelector::generateWheel() 0327 { 0328 QImage image(d->sizeColorSelector, d->sizeColorSelector, QImage::Format_ARGB32); 0329 for(int y = 0; y < d->sizeColorSelector; y++) 0330 { 0331 qreal yc = y - d->centerColorSelector; 0332 qreal y2 = pow2( yc ); 0333 for(int x = 0; x < d->sizeColorSelector; x++) 0334 { 0335 qreal xc = x - d->centerColorSelector; 0336 qreal norm = sqrt(pow2( xc ) + y2); 0337 if( norm <= d->wheelNormExt + 1.0 && norm >= d->wheelNormInt - 1.0 ) 0338 { 0339 qreal acoef = 1.0; 0340 if(norm > d->wheelNormExt ) acoef = (1.0 + d->wheelNormExt - norm); 0341 else if(norm < d->wheelNormInt ) acoef = (1.0 - d->wheelNormInt + norm); 0342 qreal angle = atan2(yc, xc); 0343 int h = (int)((180 * angle / M_PI) + 180) % 360; 0344 0345 KoColor color = d->displayRenderer->fromHsv(h, 255, 255, int(acoef * 255.0)); 0346 QColor qcolor = d->displayRenderer->toQColor(color); 0347 0348 image.setPixel(x,y, qcolor.rgba()); 0349 } else { 0350 image.setPixel(x,y, qRgba(0,0,0,0)); 0351 } 0352 } 0353 } 0354 d->wheelPixmap = QPixmap::fromImage(image); 0355 } 0356 0357 void KoTriangleColorSelector::mouseReleaseEvent( QMouseEvent * event ) 0358 { 0359 if(event->button() == Qt::LeftButton) 0360 { 0361 selectColorAt( event->x(), event->y()); 0362 d->handle = NoHandle; 0363 } else { 0364 QWidget::mouseReleaseEvent( event ); 0365 } 0366 } 0367 0368 void KoTriangleColorSelector::mousePressEvent( QMouseEvent * event ) 0369 { 0370 if(event->button() == Qt::LeftButton) 0371 { 0372 d->handle = NoHandle; 0373 selectColorAt( event->x(), event->y()); 0374 } else { 0375 QWidget::mousePressEvent( event ); 0376 } 0377 } 0378 0379 void KoTriangleColorSelector::mouseMoveEvent( QMouseEvent * event ) 0380 { 0381 if(event->buttons() & Qt::LeftButton) 0382 { 0383 selectColorAt( event->x(), event->y(), false ); 0384 } else { 0385 QWidget::mouseMoveEvent( event); 0386 } 0387 } 0388 0389 void KoTriangleColorSelector::selectColorAt(int _x, int _y, bool checkInWheel) 0390 { 0391 Q_UNUSED( checkInWheel ); 0392 0393 if (d->lastX == _x && d->lastY == _y) 0394 { 0395 return; 0396 } 0397 d->lastX = _x; 0398 d->lastY = _y; 0399 0400 qreal x = _x - 0.5*width(); 0401 qreal y = _y - 0.5*height(); 0402 // Check if the click is inside the wheel 0403 qreal norm = sqrt( x * x + y * y); 0404 if ( ( (norm < d->wheelNormExt) && (norm > d->wheelNormInt) && d->handle == NoHandle ) 0405 || d->handle == HueHandle ) { 0406 d->handle = HueHandle; 0407 setHue( (int)(atan2(y, x) * 180 / M_PI ) + 180); 0408 d->updateTimer.start(); 0409 } 0410 else { 0411 // Compute the s and v value, if they are in range, use them 0412 qreal rotation = -(hue() + 150) * M_PI / 180; 0413 qreal cr = cos(rotation); 0414 qreal sr = sin(rotation); 0415 qreal x1 = x * cr - y * sr; // <- now x1 gives the saturation 0416 qreal y1 = x * sr + y * cr; // <- now y1 gives the value 0417 y1 += d->wheelNormExt; 0418 qreal ynormalize = (d->triangleTop - y1 ) / ( d->triangleTop - d->triangleBottom ); 0419 if( (ynormalize >= 0.0 && ynormalize <= 1.0 ) || d->handle == ValueSaturationHandle) 0420 { 0421 d->handle = ValueSaturationHandle; 0422 qreal ls_ = (ynormalize) * d->triangleLength; // length of the saturation on the triangle 0423 qreal sat = ( x1 / ls_ + 0.5) ; 0424 if((sat >= 0.0 && sat <= 1.0) || d->handle == ValueSaturationHandle) 0425 { 0426 setHSV( hue(), sat * 255, ynormalize * 255); 0427 } 0428 } 0429 d->updateTimer.start(); 0430 } 0431 } 0432 0433 void KoTriangleColorSelector::configurationChanged() 0434 { 0435 generateWheel(); 0436 d->invalidTriangle = true; 0437 update(); 0438 }