File indexing completed on 2024-06-16 04:16:01
0001 /* 0002 * SPDX-FileCopyrightText: 2010 Adam Celarek <kdedev at xibo dot at> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "kis_color_selector_wheel.h" 0008 0009 #include <QImage> 0010 #include <QPainter> 0011 #include <QColor> 0012 #include <cmath> 0013 #include <kconfig.h> 0014 #include <kconfiggroup.h> 0015 #include <ksharedconfig.h> 0016 0017 #include "kis_display_color_converter.h" 0018 #include "kis_acs_pixel_cache_renderer.h" 0019 0020 0021 KisColorSelectorWheel::KisColorSelectorWheel(KisColorSelector *parent) : 0022 KisColorSelectorComponent(parent), 0023 m_lastClickPos(-1,-1), 0024 m_renderAreaSize(1,1), 0025 m_renderAreaOffsetX(0.0), 0026 m_renderAreaOffsetY(0.0), 0027 m_toRenderArea(QTransform()) 0028 { 0029 } 0030 0031 KoColor KisColorSelectorWheel::selectColor(int x, int y) 0032 { 0033 int xWheel = x-width()/2; 0034 int yWheel = y-height()/2; 0035 0036 qreal radius = sqrt((double)xWheel*xWheel+yWheel*yWheel); 0037 radius/=qMin(width(), height()); 0038 if(radius>0.5) 0039 radius=0.5; 0040 0041 radius*=2.; 0042 0043 qreal angle = std::atan2((qreal)yWheel, (qreal)xWheel); 0044 angle+=M_PI; 0045 angle/=2*M_PI; 0046 0047 switch (m_parameter) { 0048 case KisColorSelectorConfiguration::hsvSH: 0049 emit paramChanged(angle, radius, -1, -1, -1, -1, -1, -1, -1); 0050 break; 0051 case KisColorSelectorConfiguration::hslSH: 0052 emit paramChanged(angle, -1, -1, radius, -1, -1, -1, -1, -1); 0053 break; 0054 case KisColorSelectorConfiguration::hsiSH: 0055 emit paramChanged(angle, -1, -1, -1, -1, radius, -1, -1, -1); 0056 break; 0057 case KisColorSelectorConfiguration::hsySH: 0058 emit paramChanged(angle, -1, -1, -1, -1, -1, -1, radius, -1); 0059 break; 0060 case KisColorSelectorConfiguration::VH: 0061 emit paramChanged(angle, -1, radius, -1, -1, -1, -1, -1, -1); 0062 break; 0063 case KisColorSelectorConfiguration::LH: 0064 emit paramChanged(angle, -1, -1, -1, radius, -1, -1, -1, -1); 0065 break; 0066 case KisColorSelectorConfiguration::IH: 0067 emit paramChanged(angle, -1, -1, -1, -1, -1, radius, -1, -1); 0068 break; 0069 case KisColorSelectorConfiguration::YH: 0070 emit paramChanged(angle, -1, -1, -1, -1, -1, -1, -1, radius); 0071 break; 0072 default: 0073 Q_ASSERT(false); 0074 break; 0075 } 0076 0077 emit update(); 0078 0079 angle *= 2. * M_PI; 0080 angle -= M_PI; 0081 radius*=0.5; 0082 m_lastClickPos.setX(cos(angle)*radius+0.5); 0083 m_lastClickPos.setY(sin(angle)*radius+0.5); 0084 0085 return colorAt(x, y, true); 0086 } 0087 0088 void KisColorSelectorWheel::setColor(const KoColor &color) 0089 { 0090 qreal hsvH, hsvS, hsvV; 0091 qreal hslH, hslS, hslL; 0092 qreal hsiH, hsiS, hsiI; 0093 qreal hsyH, hsyS, hsyY; 0094 KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); 0095 R = cfg.readEntry("lumaR", 0.2126); 0096 G = cfg.readEntry("lumaG", 0.7152); 0097 B = cfg.readEntry("lumaB", 0.0722); 0098 Gamma = cfg.readEntry("gamma", 2.2); 0099 m_parent->converter()->getHsvF(color, &hsvH, &hsvS, &hsvV); 0100 m_parent->converter()->getHslF(color, &hslH, &hslS, &hslL); 0101 m_parent->converter()->getHsiF(color, &hsiH, &hsiS, &hsiI); 0102 m_parent->converter()->getHsyF(color, &hsyH, &hsyS, &hsyY, R, G, B, Gamma); 0103 0104 qreal angle = 0.0, radius = 0.0; 0105 switch (m_parameter) { 0106 case KisColorSelectorConfiguration::LH: 0107 emit paramChanged(hslH, -1, -1, -1, hslL, -1, -1, -1, -1); 0108 angle = hslH; 0109 radius = hslL; 0110 break; 0111 case KisColorSelectorConfiguration::VH: 0112 emit paramChanged(hsvH, -1, hsvV, -1, -1, -1, -1, -1, -1); 0113 angle = hsvH; 0114 radius = hsvV; 0115 break; 0116 case KisColorSelectorConfiguration::IH: 0117 emit paramChanged(hslH, -1, -1, -1, -1, -1, hsiI, -1, -1); 0118 angle = hsiH; 0119 radius = hsiI; 0120 break; 0121 case KisColorSelectorConfiguration::YH: 0122 emit paramChanged(hsyH, -1, -1, -1, -1, -1, -1, -1, hsyY); 0123 angle = hsyH; 0124 radius = hsyY; 0125 break; 0126 case KisColorSelectorConfiguration::hsvSH: 0127 emit paramChanged(hsvH, hsvS, -1, -1, -1, -1, -1, -1, -1); 0128 angle = hsvH; 0129 radius = hsvS; 0130 break; 0131 case KisColorSelectorConfiguration::hslSH: 0132 emit paramChanged(hslH, -1, -1, hslS, -1, -1, -1, -1, -1); 0133 angle = hslH; 0134 radius = hslS; 0135 break; 0136 case KisColorSelectorConfiguration::hsiSH: 0137 emit paramChanged(hsiH, -1, -1, -1, -1, hsiS, -1, -1, -1); 0138 angle = hsiH; 0139 radius = hsiS; 0140 break; 0141 case KisColorSelectorConfiguration::hsySH: 0142 emit paramChanged(hsyH, -1, -1, -1, -1, -1, -1, hsyS, -1); 0143 angle = hsyH; 0144 radius = hsyS; 0145 break; 0146 default: 0147 Q_ASSERT(false); 0148 break; 0149 } 0150 angle *= 2. * M_PI; 0151 angle -= M_PI; 0152 radius *= 0.5; 0153 0154 m_lastClickPos.setX(cos(angle)*radius+0.5); 0155 m_lastClickPos.setY(sin(angle)*radius+0.5); 0156 0157 //workaround for bug 279500 0158 0159 if(m_lastClickPos!=QPoint(-1,-1) && m_parent->displayBlip()) { 0160 QPoint pos = (m_lastClickPos*qMin(width(), height())).toPoint(); 0161 if(width() < height()) { 0162 pos.setY(pos.y()+height()/2-width()/2); 0163 } else { 0164 pos.setX(pos.x()+width()/2-height()/2); 0165 } 0166 0167 setLastMousePosition(pos.x(), pos.y()); 0168 } 0169 0170 KisColorSelectorComponent::setColor(color); 0171 } 0172 0173 void KisColorSelectorWheel::paint(QPainter* painter) 0174 { 0175 0176 if(isDirty()) { 0177 KisPaintDeviceSP realPixelCache; 0178 Acs::PixelCacheRenderer::render(this, m_parent->converter(), QRect(0, 0, width(), height()), realPixelCache, 0179 m_pixelCache, m_pixelCacheOffset, painter->device()->devicePixelRatioF()); 0180 0181 //antialiasing for wheel 0182 QPainter tmpPainter(&m_pixelCache); 0183 tmpPainter.setRenderHint(QPainter::Antialiasing); 0184 tmpPainter.setPen(QPen(QColor(0,0,0,0), 2.5)); 0185 tmpPainter.setCompositionMode(QPainter::CompositionMode_Clear); 0186 int size=qMin(width(), height()); 0187 0188 m_renderAreaSize = QSize(size,size); 0189 m_renderAreaOffsetX = ((qreal)width()-(qreal)m_renderAreaSize.width())*0.5; 0190 m_renderAreaOffsetY = ((qreal)height()-(qreal)m_renderAreaSize.height())*0.5; 0191 m_toRenderArea.reset(); 0192 m_toRenderArea.translate(-m_renderAreaOffsetX,-m_renderAreaOffsetY); 0193 0194 QPoint ellipseCenter(width() / 2 - size / 2, height() / 2 - size / 2); 0195 ellipseCenter -= m_pixelCacheOffset; 0196 0197 tmpPainter.drawEllipse(ellipseCenter.x(), ellipseCenter.y(), size, size); 0198 } 0199 0200 painter->drawImage(m_pixelCacheOffset.x(),m_pixelCacheOffset.y(), m_pixelCache); 0201 0202 // draw gamut mask 0203 if (m_gamutMaskOn && m_currentGamutMask) { 0204 QImage maskBuffer = QImage(m_renderAreaSize*painter->device()->devicePixelRatioF(), QImage::Format_ARGB32_Premultiplied); 0205 maskBuffer.setDevicePixelRatio(painter->device()->devicePixelRatioF()); 0206 maskBuffer.fill(0); 0207 QPainter maskPainter(&maskBuffer); 0208 0209 QRect rect = QRect(0, 0, m_renderAreaSize.width(), m_renderAreaSize.height()); 0210 maskPainter.setRenderHint(QPainter::Antialiasing, true); 0211 0212 maskPainter.resetTransform(); 0213 maskPainter.translate(rect.width()*0.5, rect.height()*0.5); 0214 maskPainter.scale(rect.width()*0.5, rect.height()*0.5); 0215 0216 maskPainter.setPen(QPen(QBrush(Qt::white), 0.002)); 0217 maskPainter.setBrush(QColor(128,128,128,255)); // middle gray 0218 0219 maskPainter.drawEllipse(QPointF(0,0), 1.0, 1.0); 0220 0221 maskPainter.resetTransform(); 0222 maskPainter.setTransform(m_currentGamutMask->maskToViewTransform(m_renderAreaSize.width())); 0223 0224 maskPainter.setCompositionMode(QPainter::CompositionMode_DestinationIn); 0225 m_currentGamutMask->paint(maskPainter, m_maskPreviewActive); 0226 0227 maskPainter.setCompositionMode(QPainter::CompositionMode_SourceOver); 0228 m_currentGamutMask->paintStroke(maskPainter, m_maskPreviewActive); 0229 0230 painter->drawImage(m_renderAreaOffsetX, m_renderAreaOffsetY, maskBuffer); 0231 } 0232 0233 // draw blips 0234 0235 if(m_lastClickPos!=QPoint(-1,-1) && m_parent->displayBlip()) { 0236 QPoint pos = (m_lastClickPos*qMin(width(), height())).toPoint(); 0237 if(width()<height()) 0238 pos.setY(pos.y()+height()/2-width()/2); 0239 else 0240 pos.setX(pos.x()+width()/2-height()/2); 0241 0242 painter->setPen(QColor(0,0,0)); 0243 painter->drawEllipse(pos, 5, 5); 0244 painter->setPen(QColor(255,255,255)); 0245 painter->drawEllipse(pos, 4, 4); 0246 } 0247 } 0248 0249 KoColor KisColorSelectorWheel::colorAt(float x, float y, bool forceValid) 0250 { 0251 KoColor color = KoColor::createTransparent(m_parent->colorSpace()); 0252 0253 Q_ASSERT(x>=0 && x<=width()); 0254 Q_ASSERT(y>=0 && y<=height()); 0255 0256 const qreal xRel = x - 0.5 * width(); 0257 const qreal yRel = y - 0.5 * height(); 0258 const qreal minDimension = qMin(width(), height()); 0259 0260 qreal radius = sqrt(xRel*xRel+yRel*yRel); 0261 if (radius > 0.5 * minDimension) { 0262 if (!forceValid) { 0263 return color; 0264 } else { 0265 radius = 0.5 * minDimension; 0266 } 0267 } 0268 radius /= 0.5 * minDimension; 0269 0270 qreal angle = std::atan2(yRel, xRel); 0271 angle += M_PI; 0272 angle /= 2 * M_PI; 0273 0274 switch(m_parameter) { 0275 case KisColorSelectorConfiguration::hsvSH: 0276 color = m_parent->converter()->fromHsvF(angle, radius, m_value); 0277 break; 0278 case KisColorSelectorConfiguration::hslSH: 0279 color = m_parent->converter()->fromHslF(angle, radius, m_lightness); 0280 break; 0281 case KisColorSelectorConfiguration::hsiSH: 0282 color = m_parent->converter()->fromHsiF(angle, radius, m_intensity); 0283 break; 0284 case KisColorSelectorConfiguration::hsySH: 0285 color = m_parent->converter()->fromHsyF(angle, radius, m_luma, R, G, B, Gamma); 0286 break; 0287 case KisColorSelectorConfiguration::VH: 0288 color = m_parent->converter()->fromHsvF(angle, m_hsvSaturation, radius); 0289 break; 0290 case KisColorSelectorConfiguration::LH: 0291 color = m_parent->converter()->fromHslF(angle, m_hslSaturation, radius); 0292 break; 0293 case KisColorSelectorConfiguration::IH: 0294 color = m_parent->converter()->fromHsiF(angle, m_hsiSaturation, radius); 0295 break; 0296 case KisColorSelectorConfiguration::YH: 0297 color = m_parent->converter()->fromHsyF(angle, m_hsySaturation, radius, R, G, B, Gamma); 0298 break; 0299 default: 0300 Q_ASSERT(false); 0301 0302 return color; 0303 } 0304 return color; 0305 } 0306 0307 bool KisColorSelectorWheel::allowsColorSelectionAtPoint(const QPoint &pt) const 0308 { 0309 if (!m_gamutMaskOn || !m_currentGamutMask) { 0310 return true; 0311 } 0312 0313 QPointF colorCoord = m_toRenderArea.map(QPointF(pt)); 0314 QPointF translatedPoint = m_currentGamutMask->viewToMaskTransform(m_renderAreaSize.width()).map(colorCoord); 0315 bool isClear = m_currentGamutMask->coordIsClear(translatedPoint, m_maskPreviewActive); 0316 0317 return isClear; 0318 }