diff --git a/libs/ui/kis_popup_palette.cpp b/libs/ui/kis_popup_palette.cpp index acb72d10ad..a03e5636f2 100644 --- a/libs/ui/kis_popup_palette.cpp +++ b/libs/ui/kis_popup_palette.cpp @@ -1,972 +1,973 @@ /* This file is part of the KDE project Copyright 2009 Vera Lukman Copyright 2011 Sven Langkamp Copyright 2016 Scott Petrovic This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_canvas2.h" #include "kis_config.h" #include "kis_popup_palette.h" #include "kis_favorite_resource_manager.h" #include "kis_icon_utils.h" #include "KisResourceServerProvider.h" #include #include +#include "KoColorDisplayRendererInterface.h" #include #include #include #include #include #include #include #include "kis_signal_compressor.h" #include "brushhud/kis_brush_hud.h" #include "brushhud/kis_round_hud_button.h" #include "kis_signals_blocker.h" #include "kis_canvas_controller.h" #include "kis_acyclic_signal_connector.h" #include "KisMouseClickEater.h" class PopupColorTriangle : public KoTriangleColorSelector { public: PopupColorTriangle(const KoColorDisplayRendererInterface *displayRenderer, QWidget* parent) : KoTriangleColorSelector(displayRenderer, parent) , m_dragging(false) { } ~PopupColorTriangle() override {} void tabletEvent(QTabletEvent* event) override { event->accept(); QMouseEvent* mouseEvent = 0; // this will tell the pop-up palette widget to close if(event->button() == Qt::RightButton) { emit requestCloseContainer(); } // ignore any tablet events that are done with the right click // Tablet move events don't return a "button", so catch that too if(event->button() == Qt::LeftButton || event->type() == QEvent::TabletMove) { switch (event->type()) { case QEvent::TabletPress: mouseEvent = new QMouseEvent(QEvent::MouseButtonPress, event->pos(), Qt::LeftButton, Qt::LeftButton, event->modifiers()); m_dragging = true; mousePressEvent(mouseEvent); break; case QEvent::TabletMove: mouseEvent = new QMouseEvent(QEvent::MouseMove, event->pos(), (m_dragging) ? Qt::LeftButton : Qt::NoButton, (m_dragging) ? Qt::LeftButton : Qt::NoButton, event->modifiers()); mouseMoveEvent(mouseEvent); break; case QEvent::TabletRelease: mouseEvent = new QMouseEvent(QEvent::MouseButtonRelease, event->pos(), Qt::LeftButton, Qt::LeftButton, event->modifiers()); m_dragging = false; mouseReleaseEvent(mouseEvent); break; default: break; } } delete mouseEvent; } private: bool m_dragging; }; KisPopupPalette::KisPopupPalette(KisViewManager* viewManager, KisCoordinatesConverter* coordinatesConverter ,KisFavoriteResourceManager* manager, const KoColorDisplayRendererInterface *displayRenderer, KisCanvasResourceProvider *provider, QWidget *parent) : QWidget(parent, Qt::FramelessWindowHint) , m_coordinatesConverter(coordinatesConverter) , m_viewManager(viewManager) , m_actionManager(viewManager->actionManager()) , m_resourceManager(manager) , m_displayRenderer(displayRenderer) , m_colorChangeCompressor(new KisSignalCompressor(50, KisSignalCompressor::POSTPONE)) , m_actionCollection(viewManager->actionCollection()) , m_acyclicConnector(new KisAcyclicSignalConnector(this)) , m_clicksEater(new KisMouseClickEater(Qt::RightButton, 1, this)) { // some UI controls are defined and created based off these variables const int borderWidth = 3; if (KisConfig(true).readEntry("popuppalette/usevisualcolorselector", false)) { KisVisualColorSelector *selector = new KisVisualColorSelector(this); selector->setAcceptTabletEvents(true); m_triangleColorSelector = selector; } else { m_triangleColorSelector = new PopupColorTriangle(displayRenderer, this); connect(m_triangleColorSelector, SIGNAL(requestCloseContainer()), this, SLOT(slotHide())); } m_triangleColorSelector->setDisplayRenderer(displayRenderer); m_triangleColorSelector->setConfig(true,false); m_triangleColorSelector->move(m_popupPaletteSize/2-m_colorHistoryInnerRadius+borderWidth, m_popupPaletteSize/2-m_colorHistoryInnerRadius+borderWidth); m_triangleColorSelector->resize(m_popupPaletteSize - 2*m_triangleColorSelector->x(), m_popupPaletteSize - 2*m_triangleColorSelector->y()); m_triangleColorSelector->setVisible(true); KoColor fgcolor(Qt::black, KoColorSpaceRegistry::instance()->rgb8()); if (m_resourceManager) { fgcolor = provider->fgColor(); } m_triangleColorSelector->slotSetColor(fgcolor); /** * Tablet support code generates a spurious right-click right after opening * the window, so we should ignore it. Next right-click will be used for * closing the popup palette */ this->installEventFilter(m_clicksEater); m_triangleColorSelector->installEventFilter(m_clicksEater); QRegion maskedRegion(0, 0, m_triangleColorSelector->width(), m_triangleColorSelector->height(), QRegion::Ellipse ); m_triangleColorSelector->setMask(maskedRegion); //setAttribute(Qt::WA_TranslucentBackground, true); connect(m_triangleColorSelector, SIGNAL(sigNewColor(KoColor)), m_colorChangeCompressor.data(), SLOT(start())); connect(m_colorChangeCompressor.data(), SIGNAL(timeout()), SLOT(slotEmitColorChanged())); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), m_triangleColorSelector, SLOT(configurationChanged())); connect(m_displayRenderer, SIGNAL(displayConfigurationChanged()), this, SLOT(slotDisplayConfigurationChanged())); m_acyclicConnector->connectForwardKoColor(m_resourceManager, SIGNAL(sigChangeFGColorSelector(KoColor)), this, SLOT(slotExternalFgColorChanged(KoColor))); m_acyclicConnector->connectBackwardKoColor(this, SIGNAL(sigChangefGColor(KoColor)), m_resourceManager, SIGNAL(sigSetFGColor(KoColor))); connect(this, SIGNAL(sigChangeActivePaintop(int)), m_resourceManager, SLOT(slotChangeActivePaintop(int))); connect(this, SIGNAL(sigUpdateRecentColor(int)), m_resourceManager, SLOT(slotUpdateRecentColor(int))); connect(m_resourceManager, SIGNAL(setSelectedColor(int)), SLOT(slotSetSelectedColor(int))); connect(m_resourceManager, SIGNAL(updatePalettes()), SLOT(slotUpdate())); connect(m_resourceManager, SIGNAL(hidePalettes()), SLOT(slotHide())); setCursor(Qt::ArrowCursor); setMouseTracking(true); setHoveredPreset(-1); setHoveredColor(-1); setSelectedColor(-1); m_brushHud = new KisBrushHud(provider, parent); m_brushHud->setFixedHeight(int(m_popupPaletteSize)); m_brushHud->setVisible(false); const int auxButtonSize = 35; m_settingsButton = new KisRoundHudButton(this); m_settingsButton->setGeometry(m_popupPaletteSize - 2.2 * auxButtonSize, m_popupPaletteSize - auxButtonSize, auxButtonSize, auxButtonSize); connect(m_settingsButton, SIGNAL(clicked()), SLOT(slotShowTagsPopup())); KisConfig cfg(true); m_brushHudButton = new KisRoundHudButton(this); m_brushHudButton->setCheckable(true); m_brushHudButton->setGeometry(m_popupPaletteSize - 1.0 * auxButtonSize, m_popupPaletteSize - auxButtonSize, auxButtonSize, auxButtonSize); connect(m_brushHudButton, SIGNAL(toggled(bool)), SLOT(showHudWidget(bool))); m_brushHudButton->setChecked(cfg.showBrushHud()); // add some stuff below the pop-up palette that will make it easier to use for tablet people QVBoxLayout* vLayout = new QVBoxLayout(this); // main layout QSpacerItem* verticalSpacer = new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding); vLayout->addSpacerItem(verticalSpacer); // this should push the box to the bottom QHBoxLayout* hLayout = new QHBoxLayout(); vLayout->addLayout(hLayout); mirrorMode = new KisHighlightedToolButton(this); mirrorMode->setFixedSize(35, 35); mirrorMode->setToolTip(i18n("Mirror Canvas")); mirrorMode->setDefaultAction(m_actionCollection->action("mirror_canvas")); canvasOnlyButton = new KisHighlightedToolButton(this); canvasOnlyButton->setFixedSize(35, 35); canvasOnlyButton->setToolTip(i18n("Canvas Only")); canvasOnlyButton->setDefaultAction(m_actionCollection->action("view_show_canvas_only")); zoomToOneHundredPercentButton = new QPushButton(this); zoomToOneHundredPercentButton->setText(i18n("100%")); zoomToOneHundredPercentButton->setFixedHeight(35); zoomToOneHundredPercentButton->setToolTip(i18n("Zoom to 100%")); connect(zoomToOneHundredPercentButton, SIGNAL(clicked(bool)), this, SLOT(slotZoomToOneHundredPercentClicked())); zoomCanvasSlider = new QSlider(Qt::Horizontal, this); zoomSliderMinValue = 10; // set in % zoomSliderMaxValue = 200; // set in % zoomCanvasSlider->setRange(zoomSliderMinValue, zoomSliderMaxValue); zoomCanvasSlider->setFixedHeight(35); zoomCanvasSlider->setValue(m_coordinatesConverter->zoomInPercent()); zoomCanvasSlider->setSingleStep(1); zoomCanvasSlider->setPageStep(1); connect(zoomCanvasSlider, SIGNAL(valueChanged(int)), this, SLOT(slotZoomSliderChanged(int))); connect(zoomCanvasSlider, SIGNAL(sliderPressed()), this, SLOT(slotZoomSliderPressed())); connect(zoomCanvasSlider, SIGNAL(sliderReleased()), this, SLOT(slotZoomSliderReleased())); slotUpdateIcons(); hLayout->addWidget(mirrorMode); hLayout->addWidget(canvasOnlyButton); hLayout->addWidget(zoomToOneHundredPercentButton); hLayout->addWidget(zoomCanvasSlider); setVisible(true); setVisible(false); opacityChange = new QGraphicsOpacityEffect(this); setGraphicsEffect(opacityChange); // Prevent tablet events from being captured by the canvas setAttribute(Qt::WA_NoMousePropagation, true); } void KisPopupPalette::slotDisplayConfigurationChanged() { // Visual Color Selector picks up color space from input KoColor col = m_viewManager->canvasResourceProvider()->fgColor(); const KoColorSpace *paintingCS = m_displayRenderer->getPaintingColorSpace(); //hack to get around cmyk for now. if (paintingCS->colorChannelCount()>3) { paintingCS = KoColorSpaceRegistry::instance()->rgb8(); } m_triangleColorSelector->slotSetColorSpace(paintingCS); m_triangleColorSelector->slotSetColor(col); } void KisPopupPalette::slotExternalFgColorChanged(const KoColor &color) { m_triangleColorSelector->slotSetColor(color); } void KisPopupPalette::slotEmitColorChanged() { if (isVisible()) { update(); emit sigChangefGColor(m_triangleColorSelector->getCurrentColor()); } } //setting KisPopupPalette properties int KisPopupPalette::hoveredPreset() const { return m_hoveredPreset; } void KisPopupPalette::setHoveredPreset(int x) { m_hoveredPreset = x; } int KisPopupPalette::hoveredColor() const { return m_hoveredColor; } void KisPopupPalette::setHoveredColor(int x) { m_hoveredColor = x; } int KisPopupPalette::selectedColor() const { return m_selectedColor; } void KisPopupPalette::setSelectedColor(int x) { m_selectedColor = x; } void KisPopupPalette::slotZoomSliderChanged(int zoom) { emit zoomLevelChanged(zoom); } void KisPopupPalette::slotZoomSliderPressed() { m_isZoomingCanvas = true; } void KisPopupPalette::slotZoomSliderReleased() { m_isZoomingCanvas = false; } void KisPopupPalette::adjustLayout(const QPoint &p) { KIS_ASSERT_RECOVER_RETURN(m_brushHud); if (isVisible() && parentWidget()) { float hudMargin = 30.0; const QRect fitRect = kisGrowRect(parentWidget()->rect(), -20.0); // -20 is widget margin const QPoint paletteCenterOffset(m_popupPaletteSize / 2, m_popupPaletteSize / 2); QRect paletteRect = rect(); paletteRect.moveTo(p - paletteCenterOffset); if (m_brushHudButton->isChecked()) { m_brushHud->updateGeometry(); paletteRect.adjust(0, 0, m_brushHud->width() + hudMargin, 0); } paletteRect = kisEnsureInRect(paletteRect, fitRect); move(paletteRect.topLeft()); m_brushHud->move(paletteRect.topLeft() + QPoint(m_popupPaletteSize + hudMargin, 0)); m_lastCenterPoint = p; } } void KisPopupPalette::slotUpdateIcons() { this->setPalette(qApp->palette()); for(int i=0; ichildren().size(); i++) { QWidget *w = qobject_cast(this->children().at(i)); if (w) { w->setPalette(qApp->palette()); } } zoomToOneHundredPercentButton->setIcon(m_actionCollection->action("zoom_to_100pct")->icon()); m_brushHud->updateIcons(); m_settingsButton->setIcon(KisIconUtils::loadIcon("tag")); m_brushHudButton->setOnOffIcons(KisIconUtils::loadIcon("arrow-left"), KisIconUtils::loadIcon("arrow-right")); } void KisPopupPalette::showHudWidget(bool visible) { KIS_ASSERT_RECOVER_RETURN(m_brushHud); const bool reallyVisible = visible && m_brushHudButton->isChecked(); if (reallyVisible) { m_brushHud->updateProperties(); } m_brushHud->setVisible(reallyVisible); adjustLayout(m_lastCenterPoint); KisConfig cfg(false); cfg.setShowBrushHud(visible); } void KisPopupPalette::showPopupPalette(const QPoint &p) { showPopupPalette(!isVisible()); adjustLayout(p); } void KisPopupPalette::showPopupPalette(bool show) { if (show) { // don't set the zoom slider if we are outside of the zoom slider bounds. It will change the zoom level to within // the bounds and cause the canvas to jump between the slider's min and max if (m_coordinatesConverter->zoomInPercent() > zoomSliderMinValue && m_coordinatesConverter->zoomInPercent() < zoomSliderMaxValue ){ KisSignalsBlocker b(zoomCanvasSlider); zoomCanvasSlider->setValue(m_coordinatesConverter->zoomInPercent()); // sync the zoom slider } } setVisible(show); m_brushHud->setVisible(show && m_brushHudButton->isChecked()); } //redefinition of setVariable function to change the scope to private void KisPopupPalette::setVisible(bool b) { QWidget::setVisible(b); } void KisPopupPalette::setParent(QWidget *parent) { m_brushHud->setParent(parent); QWidget::setParent(parent); } QSize KisPopupPalette::sizeHint() const { return QSize(m_popupPaletteSize, m_popupPaletteSize + 50); // last number is the space for the toolbar below } void KisPopupPalette::resizeEvent(QResizeEvent*) { } void KisPopupPalette::paintEvent(QPaintEvent* e) { Q_UNUSED(e); QPainter painter(this); QPen pen(palette().color(QPalette::Text)); pen.setWidth(3); painter.setPen(pen); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::SmoothPixmapTransform); // painting background color indicator QPainterPath bgColor; bgColor.addEllipse(QPoint( 50, 80), 30, 30); painter.fillPath(bgColor, m_displayRenderer->toQColor(m_resourceManager->bgColor())); painter.drawPath(bgColor); // painting foreground color indicator QPainterPath fgColor; fgColor.addEllipse(QPoint( 60, 50), 30, 30); painter.fillPath(fgColor, m_displayRenderer->toQColor(m_triangleColorSelector->getCurrentColor())); painter.drawPath(fgColor); // create a circle background that everything else will go into QPainterPath backgroundContainer; float shrinkCircleAmount = 3;// helps the circle when the stroke is put around it QRectF circleRect(shrinkCircleAmount, shrinkCircleAmount, m_popupPaletteSize - shrinkCircleAmount*2,m_popupPaletteSize - shrinkCircleAmount*2); backgroundContainer.addEllipse( circleRect ); painter.fillPath(backgroundContainer,palette().brush(QPalette::Background)); painter.drawPath(backgroundContainer); // create a path slightly inside the container circle. this will create a 'track' to indicate that we can rotate the canvas // with the indicator QPainterPath rotationTrackPath; shrinkCircleAmount = 18; QRectF circleRect2(shrinkCircleAmount, shrinkCircleAmount, m_popupPaletteSize - shrinkCircleAmount*2,m_popupPaletteSize - shrinkCircleAmount*2); rotationTrackPath.addEllipse( circleRect2 ); pen.setWidth(1); painter.setPen(pen); painter.drawPath(rotationTrackPath); // this thing will help indicate where the starting brush preset is at. // also what direction they go to give sor order to the presets populated /* pen.setWidth(6); pen.setCapStyle(Qt::RoundCap); painter.setPen(pen); painter.drawArc(circleRect, (16*90), (16*-30)); // span angle (last parameter) is in 16th of degrees QPainterPath brushDir; brushDir.arcMoveTo(circleRect, 60); brushDir.lineTo(brushDir.currentPosition().x()-5, brushDir.currentPosition().y() - 14); painter.drawPath(brushDir); brushDir.lineTo(brushDir.currentPosition().x()-2, brushDir.currentPosition().y() + 6); painter.drawPath(brushDir); */ // the following things needs to be based off the center, so let's translate the painter painter.translate(m_popupPaletteSize / 2, m_popupPaletteSize / 2); // create the canvas rotation handle QPainterPath rotationIndicator = drawRotationIndicator(m_coordinatesConverter->rotationAngle(), true); painter.fillPath(rotationIndicator,palette().brush(QPalette::Text)); // hover indicator for the canvas rotation if (m_isOverCanvasRotationIndicator == true) { painter.save(); QPen pen(palette().color(QPalette::Highlight)); pen.setWidth(2); painter.setPen(pen); painter.drawPath(rotationIndicator); painter.restore(); } // create a reset canvas rotation indicator to bring the canvas back to 0 degrees QPainterPath resetRotationIndicator = drawRotationIndicator(0, false); QPen resetPen(palette().color(QPalette::Text)); resetPen.setWidth(1); painter.save(); painter.setPen(resetPen); painter.drawPath(resetRotationIndicator); painter.restore(); // painting favorite brushes QList images(m_resourceManager->favoritePresetImages()); // painting favorite brushes pixmap/icon QPainterPath presetPath; for (int pos = 0; pos < numSlots(); pos++) { painter.save(); presetPath = createPathFromPresetIndex(pos); if (pos < images.size()) { painter.setClipPath(presetPath); QRect bounds = presetPath.boundingRect().toAlignedRect(); painter.drawImage(bounds.topLeft() , images.at(pos).scaled(bounds.size() , Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation)); } else { painter.fillPath(presetPath, palette().brush(QPalette::Window)); // brush slot that has no brush in it } QPen pen = painter.pen(); pen.setWidth(1); painter.setPen(pen); painter.drawPath(presetPath); painter.restore(); } if (hoveredPreset() > -1) { presetPath = createPathFromPresetIndex(hoveredPreset()); QPen pen(palette().color(QPalette::Highlight)); pen.setWidth(3); painter.setPen(pen); painter.drawPath(presetPath); } // paint recent colors area. painter.setPen(Qt::NoPen); float rotationAngle = -360.0 / m_resourceManager->recentColorsTotal(); // there might be no recent colors at the start, so paint a placeholder if (m_resourceManager->recentColorsTotal() == 0) { painter.setBrush(Qt::transparent); QPainterPath emptyRecentColorsPath(drawDonutPathFull(0, 0, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius)); painter.setPen(QPen(palette().color(QPalette::Background).lighter(150), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin)); painter.drawPath(emptyRecentColorsPath); } else { for (int pos = 0; pos < m_resourceManager->recentColorsTotal(); pos++) { QPainterPath recentColorsPath(drawDonutPathAngle(m_colorHistoryInnerRadius, m_colorHistoryOuterRadius, m_resourceManager->recentColorsTotal())); //accessing recent color of index pos painter.fillPath(recentColorsPath, m_displayRenderer->toQColor( m_resourceManager->recentColorAt(pos) )); painter.drawPath(recentColorsPath); painter.rotate(rotationAngle); } } // painting hovered color if (hoveredColor() > -1) { painter.setPen(QPen(palette().color(QPalette::Highlight), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin)); if (m_resourceManager->recentColorsTotal() == 1) { QPainterPath path_ColorDonut(drawDonutPathFull(0, 0, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius)); painter.drawPath(path_ColorDonut); } else { painter.rotate((m_resourceManager->recentColorsTotal() + hoveredColor()) *rotationAngle); QPainterPath path(drawDonutPathAngle(m_colorHistoryInnerRadius, m_colorHistoryOuterRadius, m_resourceManager->recentColorsTotal())); painter.drawPath(path); painter.rotate(hoveredColor() * -1 * rotationAngle); } } // painting selected color if (selectedColor() > -1) { painter.setPen(QPen(palette().color(QPalette::Highlight).darker(130), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin)); if (m_resourceManager->recentColorsTotal() == 1) { QPainterPath path_ColorDonut(drawDonutPathFull(0, 0, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius)); painter.drawPath(path_ColorDonut); } else { painter.rotate((m_resourceManager->recentColorsTotal() + selectedColor()) *rotationAngle); QPainterPath path(drawDonutPathAngle(m_colorHistoryInnerRadius, m_colorHistoryOuterRadius, m_resourceManager->recentColorsTotal())); painter.drawPath(path); painter.rotate(selectedColor() * -1 * rotationAngle); } } // if we are actively rotating the canvas or zooming, make the panel slightly transparent to see the canvas better if(m_isRotatingCanvasIndicator || m_isZoomingCanvas) { opacityChange->setOpacity(0.4); } else { opacityChange->setOpacity(1.0); } } QPainterPath KisPopupPalette::drawDonutPathFull(int x, int y, int inner_radius, int outer_radius) { QPainterPath path; path.addEllipse(QPointF(x, y), outer_radius, outer_radius); path.addEllipse(QPointF(x, y), inner_radius, inner_radius); path.setFillRule(Qt::OddEvenFill); return path; } QPainterPath KisPopupPalette::drawDonutPathAngle(int inner_radius, int outer_radius, int limit) { QPainterPath path; path.moveTo(-0.999 * outer_radius * sin(M_PI / limit), 0.999 * outer_radius * cos(M_PI / limit)); path.arcTo(-1 * outer_radius, -1 * outer_radius, 2 * outer_radius, 2 * outer_radius, -90.0 - 180.0 / limit, 360.0 / limit); path.arcTo(-1 * inner_radius, -1 * inner_radius, 2 * inner_radius, 2 * inner_radius, -90.0 + 180.0 / limit, - 360.0 / limit); path.closeSubpath(); return path; } QPainterPath KisPopupPalette::drawRotationIndicator(qreal rotationAngle, bool canDrag) { // used for canvas rotation. This function gets called twice. Once by the canvas rotation indicator, // and another time by the reset canvas position float canvasRotationRadians = qDegreesToRadians(rotationAngle - 90); // -90 will make 0 degrees be at the top float rotationDialXPosition = qCos(canvasRotationRadians) * (m_popupPaletteSize/2 - 10); // m_popupPaletteSize/2 = radius float rotationDialYPosition = qSin(canvasRotationRadians) * (m_popupPaletteSize/2 - 10); QPainterPath canvasRotationIndicator; int canvasIndicatorSize = 15; int canvasIndicatorMiddle = canvasIndicatorSize / 2; QRect indicatorRectangle = QRect( rotationDialXPosition - canvasIndicatorMiddle, rotationDialYPosition - canvasIndicatorMiddle, canvasIndicatorSize, canvasIndicatorSize ); if (canDrag) { m_canvasRotationIndicatorRect = indicatorRectangle; } else { m_resetCanvasRotationIndicatorRect = indicatorRectangle; } canvasRotationIndicator.addEllipse(indicatorRectangle.x(), indicatorRectangle.y(), indicatorRectangle.width(), indicatorRectangle.height() ); return canvasRotationIndicator; } void KisPopupPalette::mouseMoveEvent(QMouseEvent *event) { QPointF point = event->localPos(); event->accept(); setToolTip(QString()); setHoveredPreset(-1); setHoveredColor(-1); // calculate if we are over the canvas rotation knob // before we started painting, we moved the painter to the center of the widget, so the X/Y positions are offset. we need to // correct them first before looking for a click event intersection float rotationCorrectedXPos = m_canvasRotationIndicatorRect.x() + (m_popupPaletteSize / 2); float rotationCorrectedYPos = m_canvasRotationIndicatorRect.y() + (m_popupPaletteSize / 2); QRect correctedCanvasRotationIndicator = QRect(rotationCorrectedXPos, rotationCorrectedYPos, m_canvasRotationIndicatorRect.width(), m_canvasRotationIndicatorRect.height()); if (correctedCanvasRotationIndicator.contains(point.x(), point.y())) { m_isOverCanvasRotationIndicator = true; } else { m_isOverCanvasRotationIndicator = false; } if (m_isRotatingCanvasIndicator) { // we are rotating the canvas, so calculate the rotation angle based off the center // calculate the angle we are at first QPoint widgetCenterPoint = QPoint(m_popupPaletteSize/2, m_popupPaletteSize/2); float dX = point.x() - widgetCenterPoint.x(); float dY = point.y() - widgetCenterPoint.y(); float finalAngle = qAtan2(dY,dX) * 180 / M_PI; // what we need if we have two points, but don't know the angle finalAngle = finalAngle + 90; // add 90 degrees so 0 degree position points up float angleDifference = finalAngle - m_coordinatesConverter->rotationAngle(); // the rotation function accepts diffs, so find it out KisCanvasController *canvasController = dynamic_cast(m_viewManager->canvasBase()->canvasController()); canvasController->rotateCanvas(angleDifference); emit sigUpdateCanvas(); } // don't highlight the presets if we are in the middle of rotating the canvas if (m_isRotatingCanvasIndicator == false) { QPainterPath pathColor(drawDonutPathFull(m_popupPaletteSize / 2, m_popupPaletteSize / 2, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius)); { int pos = calculatePresetIndex(point, m_resourceManager->numFavoritePresets()); if (pos >= 0 && pos < m_resourceManager->numFavoritePresets()) { setToolTip(m_resourceManager->favoritePresetList().at(pos).data()->name()); setHoveredPreset(pos); } } if (pathColor.contains(point)) { int pos = calculateIndex(point, m_resourceManager->recentColorsTotal()); if (pos >= 0 && pos < m_resourceManager->recentColorsTotal()) { setHoveredColor(pos); } } } update(); } void KisPopupPalette::mousePressEvent(QMouseEvent *event) { QPointF point = event->localPos(); event->accept(); if (event->button() == Qt::LeftButton) { //in favorite brushes area int pos = calculateIndex(point, m_resourceManager->numFavoritePresets()); if (pos >= 0 && pos < m_resourceManager->numFavoritePresets() && isPointInPixmap(point, pos)) { //setSelectedBrush(pos); update(); } if (m_isOverCanvasRotationIndicator) { m_isRotatingCanvasIndicator = true; } // reset the canvas if we are over the reset canvas rotation indicator float rotationCorrectedXPos = m_resetCanvasRotationIndicatorRect.x() + (m_popupPaletteSize / 2); float rotationCorrectedYPos = m_resetCanvasRotationIndicatorRect.y() + (m_popupPaletteSize / 2); QRect correctedResetCanvasRotationIndicator = QRect(rotationCorrectedXPos, rotationCorrectedYPos, m_resetCanvasRotationIndicatorRect.width(), m_resetCanvasRotationIndicatorRect.height()); if (correctedResetCanvasRotationIndicator.contains(point.x(), point.y())) { float angleDifference = -m_coordinatesConverter->rotationAngle(); // the rotation function accepts diffs KisCanvasController *canvasController = dynamic_cast(m_viewManager->canvasBase()->canvasController()); canvasController->rotateCanvas(angleDifference); emit sigUpdateCanvas(); } } } void KisPopupPalette::slotShowTagsPopup() { KisPaintOpPresetResourceServer *rServer = KisResourceServerProvider::instance()->paintOpPresetServer(); QStringList tags = rServer->tagNamesList(); std::sort(tags.begin(), tags.end()); if (!tags.isEmpty()) { QMenu menu; Q_FOREACH (const QString& tag, tags) { menu.addAction(tag); } QAction *action = menu.exec(QCursor::pos()); if (action) { m_resourceManager->setCurrentTag(action->text()); } } else { QWhatsThis::showText(QCursor::pos(), i18n("There are no tags available to show in this popup. To add presets, you need to tag them and then select the tag here.")); } } void KisPopupPalette::slotZoomToOneHundredPercentClicked() { QAction *action = m_actionCollection->action("zoom_to_100pct"); if (action) { action->trigger(); } // also move the zoom slider to 100% position so they are in sync zoomCanvasSlider->setValue(100); } void KisPopupPalette::tabletEvent(QTabletEvent *event) { event->ignore(); } void KisPopupPalette::showEvent(QShowEvent *event) { m_clicksEater->reset(); QWidget::showEvent(event); } void KisPopupPalette::mouseReleaseEvent(QMouseEvent *event) { QPointF point = event->localPos(); event->accept(); if (event->buttons() == Qt::NoButton && event->button() == Qt::RightButton) { showPopupPalette(false); return; } m_isOverCanvasRotationIndicator = false; m_isRotatingCanvasIndicator = false; if (event->button() == Qt::LeftButton) { QPainterPath pathColor(drawDonutPathFull(m_popupPaletteSize / 2, m_popupPaletteSize / 2, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius)); //in favorite brushes area if (hoveredPreset() > -1) { //setSelectedBrush(hoveredBrush()); emit sigChangeActivePaintop(hoveredPreset()); } if (pathColor.contains(point)) { int pos = calculateIndex(point, m_resourceManager->recentColorsTotal()); if (pos >= 0 && pos < m_resourceManager->recentColorsTotal()) { emit sigUpdateRecentColor(pos); } } } } int KisPopupPalette::calculateIndex(QPointF point, int n) { calculatePresetIndex(point, n); //translate to (0,0) point.setX(point.x() - m_popupPaletteSize / 2); point.setY(point.y() - m_popupPaletteSize / 2); //rotate float smallerAngle = M_PI / 2 + M_PI / n - atan2(point.y(), point.x()); float radius = sqrt((float)point.x() * point.x() + point.y() * point.y()); point.setX(radius * cos(smallerAngle)); point.setY(radius * sin(smallerAngle)); //calculate brush index int pos = floor(acos(point.x() / radius) * n / (2 * M_PI)); if (point.y() < 0) pos = n - pos - 1; return pos; } bool KisPopupPalette::isPointInPixmap(QPointF &point, int pos) { if (createPathFromPresetIndex(pos).contains(point + QPointF(-m_popupPaletteSize / 2, -m_popupPaletteSize / 2))) { return true; } return false; } KisPopupPalette::~KisPopupPalette() { } QPainterPath KisPopupPalette::createPathFromPresetIndex(int index) { qreal angleSlice = 360.0 / numSlots() ; // how many degrees each slice will get // the starting angle of the slice we need to draw. the negative sign makes us go clockwise. // adding 90 degrees makes us start at the top. otherwise we would start at the right qreal startingAngle = -(index * angleSlice) + 90; // the radius will get smaller as the amount of presets shown increases. 10 slots == 41 qreal radians = qDegreesToRadians((360.0/10)/2); qreal maxRadius = (m_colorHistoryOuterRadius * qSin(radians) / (1-qSin(radians)))-2; radians = qDegreesToRadians(angleSlice/2); qreal presetRadius = m_colorHistoryOuterRadius * qSin(radians) / (1-qSin(radians)); //If we assume that circles will mesh like a hexagonal grid, then 3.5r is the size of two hexagons interlocking. qreal length = m_colorHistoryOuterRadius + presetRadius; // can we can fit in a second row? We don't want the preset icons to get too tiny. if (maxRadius > presetRadius) { //redo all calculations assuming a second row. if (numSlots() % 2) { angleSlice = 360.0/(numSlots()+1); startingAngle = -(index * angleSlice) + 90; } if (numSlots() != m_cachedNumSlots){ qreal tempRadius = presetRadius; qreal distance = 0; do{ tempRadius+=0.1; // Calculate the XY of two adjectant circles using this tempRadius. qreal length1 = m_colorHistoryOuterRadius + tempRadius; qreal length2 = m_colorHistoryOuterRadius + ((maxRadius*2)-tempRadius); qreal pathX1 = length1 * qCos(qDegreesToRadians(startingAngle)) - tempRadius; qreal pathY1 = -(length1) * qSin(qDegreesToRadians(startingAngle)) - tempRadius; qreal startingAngle2 = -(index+1 * angleSlice) + 90; qreal pathX2 = length2 * qCos(qDegreesToRadians(startingAngle2)) - tempRadius; qreal pathY2 = -(length2) * qSin(qDegreesToRadians(startingAngle2)) - tempRadius; // Use Pythagorean Theorem to calculate the distance between these two values. qreal m1 = pathX2-pathX1; qreal m2 = pathY2-pathY1; distance = sqrt((m1*m1)+(m2*m2)); } //As long at there's more distance than the radius of the two presets, continue increasing the radius. while((tempRadius+1)*2 < distance); m_cachedRadius = tempRadius; } m_cachedNumSlots = numSlots(); presetRadius = m_cachedRadius; length = m_colorHistoryOuterRadius + presetRadius; if (index % 2) { length = m_colorHistoryOuterRadius + ((maxRadius*2)-presetRadius); } } QPainterPath path; qreal pathX = length * qCos(qDegreesToRadians(startingAngle)) - presetRadius; qreal pathY = -(length) * qSin(qDegreesToRadians(startingAngle)) - presetRadius; qreal pathDiameter = 2 * presetRadius; // distance is used to calculate the X/Y in addition to the preset circle size path.addEllipse(pathX, pathY, pathDiameter, pathDiameter); return path; } int KisPopupPalette::calculatePresetIndex(QPointF point, int /*n*/) { for(int i = 0; i < numSlots(); i++) { QPointF adujustedPoint = point - QPointF(m_popupPaletteSize/2, m_popupPaletteSize/2); if(createPathFromPresetIndex(i).contains(adujustedPoint)) { return i; } } return -1; } int KisPopupPalette::numSlots() { KisConfig config(true); return qMax(config.favoritePresets(), 10); } diff --git a/libs/widgets/KisVisualColorSelector.cpp b/libs/widgets/KisVisualColorSelector.cpp index ee3b4720c1..9464092ffd 100644 --- a/libs/widgets/KisVisualColorSelector.cpp +++ b/libs/widgets/KisVisualColorSelector.cpp @@ -1,657 +1,652 @@ /* * Copyright (C) Wolthera van Hovell tot Westerflier , (C) 2016 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisVisualColorSelector.h" -#include -#include -#include -#include #include #include #include #include #include #include -#include #include #include #include #include "KoColorConversions.h" #include "KoColorDisplayRendererInterface.h" #include "KoColorProfile.h" #include "KoChannelInfo.h" #include #include #include "kis_signal_compressor.h" #include "kis_debug.h" #include "KisVisualColorSelectorShape.h" #include "KisVisualRectangleSelectorShape.h" #include "KisVisualTriangleSelectorShape.h" #include "KisVisualEllipticalSelectorShape.h" struct KisVisualColorSelector::Private { KoColor currentcolor; const KoColorSpace *currentCS {0}; QList widgetlist; bool acceptTabletEvents {false}; bool circular {false}; bool exposureSupported {false}; bool isRGBA {false}; bool isLinear {false}; bool applyGamma {false}; int displayPosition[4]; // map channel index to storage index for display int colorChannelCount {0}; qreal gamma {2.2}; qreal lumaRGB[3] {0.2126, 0.7152, 0.0722}; QVector4D channelValues; QVector4D channelMaxValues; ColorModel model {ColorModel::None}; const KoColorDisplayRendererInterface *displayRenderer {0}; KisColorSelectorConfiguration acs_config; KisSignalCompressor *updateTimer {0}; }; KisVisualColorSelector::KisVisualColorSelector(QWidget *parent) : KisColorSelectorInterface(parent) , m_d(new Private) { this->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); m_d->acs_config = KisColorSelectorConfiguration::fromString(cfg.readEntry("colorSelectorConfiguration", KisColorSelectorConfiguration().toString())); m_d->updateTimer = new KisSignalCompressor(100 /* ms */, KisSignalCompressor::POSTPONE); connect(m_d->updateTimer, SIGNAL(timeout()), SLOT(slotRebuildSelectors()), Qt::UniqueConnection); } KisVisualColorSelector::~KisVisualColorSelector() { delete m_d->updateTimer; } void KisVisualColorSelector::slotSetColor(const KoColor &c) { if (!m_d->currentCS) { m_d->currentcolor = c; slotSetColorSpace(c.colorSpace()); } else { m_d->currentcolor = c.convertedTo(m_d->currentCS); m_d->channelValues = convertKoColorToShapeCoordinates(m_d->currentcolor); Q_FOREACH (KisVisualColorSelectorShape *shape, m_d->widgetlist) { shape->setChannelValues(m_d->channelValues, true); } } if (isHSXModel()) { emit sigHSXChanged(QVector3D(m_d->channelValues)); } } void KisVisualColorSelector::slotSetColorSpace(const KoColorSpace *cs) { if (!m_d->currentCS || *m_d->currentCS != *cs) { const KoColorSpace *csNew = cs; // PQ color space is not very suitable for color picking, substitute with linear one if (cs->colorModelId() == RGBAColorModelID && cs->profile()->uniqueId() == KoColorSpaceRegistry::instance()->p2020PQProfile()->uniqueId()) { csNew = KoColorSpaceRegistry::instance()-> colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), KoColorSpaceRegistry::instance()->p2020G10Profile()); } m_d->currentCS = csNew; m_d->currentcolor = KoColor(csNew); slotRebuildSelectors(); } } void KisVisualColorSelector::slotSetHSX(const QVector3D &hsx) { if (isHSXModel()) { m_d->channelValues = QVector4D(hsx, 0.f); Q_FOREACH (KisVisualColorSelectorShape *shape, m_d->widgetlist) { shape->setChannelValues(m_d->channelValues, true); } KoColor newColor = convertShapeCoordsToKoColor(QVector4D(hsx)); if (newColor != m_d->currentcolor) { m_d->currentcolor = newColor; emit sigNewColor(m_d->currentcolor); } } } void KisVisualColorSelector::setConfig(bool forceCircular, bool forceSelfUpdate) { Q_UNUSED(forceSelfUpdate) m_d->circular = forceCircular; } void KisVisualColorSelector::setAcceptTabletEvents(bool on) { m_d->acceptTabletEvents = on; Q_FOREACH (KisVisualColorSelectorShape *shape, m_d->widgetlist) { shape->setAcceptTabletEvents(on); } } KoColor KisVisualColorSelector::getCurrentColor() const { return m_d->currentcolor; } QVector4D KisVisualColorSelector::getChannelValues() const { return m_d->channelValues; } KisVisualColorSelector::ColorModel KisVisualColorSelector::getColorModel() const { return m_d->model; } bool KisVisualColorSelector::isHSXModel() const { return (m_d->model >= ColorModel::HSV && m_d->model <= ColorModel::HSY); } KoColor KisVisualColorSelector::convertShapeCoordsToKoColor(const QVector4D &coordinates) const { KoColor c(m_d->currentCS); QVector4D baseValues(coordinates); QVector channelValues(c.colorSpace()->channelCount()); channelValues.fill(1.0); if (m_d->model != ColorModel::Channel && m_d->isRGBA == true) { if (m_d->model == ColorModel::HSV) { HSVToRGB(coordinates.x()*360, coordinates.y(), coordinates.z(), &baseValues[0], &baseValues[1], &baseValues[2]); } else if (m_d->model == ColorModel::HSL) { HSLToRGB(coordinates.x()*360, coordinates.y(), coordinates.z(), &baseValues[0], &baseValues[1], &baseValues[2]); } else if (m_d->model == ColorModel::HSI) { // why suddenly qreal? qreal temp[3]; HSIToRGB(coordinates.x(), coordinates.y(), coordinates.z(), &temp[0], &temp[1], &temp[2]); baseValues.setX(temp[0]); baseValues.setY(temp[1]); baseValues.setZ(temp[2]); } else /*if (m_d->model == ColorModel::HSY)*/ { qreal temp[3]; qreal Y = pow(coordinates.z(), m_d->gamma); HSYToRGB(coordinates.x(), coordinates.y(), Y, &temp[0], &temp[1], &temp[2], m_d->lumaRGB[0], m_d->lumaRGB[1], m_d->lumaRGB[2]); baseValues.setX(temp[0]); baseValues.setY(temp[1]); baseValues.setZ(temp[2]); if (!m_d->isLinear) { // Note: not all profiles define a TRC necessary for (de-)linearization, // substituting with a linear profiles would be better QVector temp({baseValues[0], baseValues[1], baseValues[2]}); if (m_d->exposureSupported) { m_d->currentCS->profile()->delinearizeFloatValue(temp); } else { m_d->currentCS->profile()->delinearizeFloatValueFast(temp); } baseValues = QVector4D(temp[0], temp[1], temp[2], 0); } } if (m_d->applyGamma) { for (int i=0; i<3; i++) { baseValues[i] = pow(baseValues[i], 2.2); } } } if (m_d->exposureSupported) { baseValues *= m_d->channelMaxValues; } for (int i=0; icolorChannelCount; i++) { channelValues[m_d->displayPosition[i]] = baseValues[i]; } c.colorSpace()->fromNormalisedChannelsValue(c.data(), channelValues); return c; } QVector4D KisVisualColorSelector::convertKoColorToShapeCoordinates(KoColor c) const { if (c.colorSpace() != m_d->currentCS) { c.convertTo(m_d->currentCS); } QVector channelValues (c.colorSpace()->channelCount()); channelValues.fill(1.0); m_d->currentCS->normalisedChannelsValue(c.data(), channelValues); QVector4D channelValuesDisplay(0, 0, 0, 0), coordinates(0, 0, 0, 0); for (int i =0; icolorChannelCount; i++) { channelValuesDisplay[i] = channelValues[m_d->displayPosition[i]]; } if (m_d->exposureSupported) { channelValuesDisplay /= m_d->channelMaxValues; } if (m_d->model != ColorModel::Channel && m_d->isRGBA == true) { if (m_d->isRGBA == true) { if (m_d->applyGamma) { for (int i=0; i<3; i++) { channelValuesDisplay[i] = pow(channelValuesDisplay[i], 1/2.2); } } if (m_d->model == ColorModel::HSV) { QVector3D hsv; RGBToHSV(channelValuesDisplay[0], channelValuesDisplay[1], channelValuesDisplay[2], &hsv[0], &hsv[1], &hsv[2]); hsv[0] /= 360; coordinates = QVector4D(hsv, 0.f); } else if (m_d->model == ColorModel::HSL) { QVector3D hsl; RGBToHSL(channelValuesDisplay[0], channelValuesDisplay[1], channelValuesDisplay[2], &hsl[0], &hsl[1], &hsl[2]); hsl[0] /= 360; coordinates = QVector4D(hsl, 0.f); } else if (m_d->model == ColorModel::HSI) { qreal hsi[3]; RGBToHSI(channelValuesDisplay[0], channelValuesDisplay[1], channelValuesDisplay[2], &hsi[0], &hsi[1], &hsi[2]); coordinates = QVector4D(hsi[0], hsi[1], hsi[2], 0.f); } else if (m_d->model == ColorModel::HSY) { if (!m_d->isLinear) { // Note: not all profiles define a TRC necessary for (de-)linearization, // substituting with a linear profiles would be better QVector temp({channelValuesDisplay[0], channelValuesDisplay[1], channelValuesDisplay[2]}); m_d->currentCS->profile()->linearizeFloatValue(temp); channelValuesDisplay = QVector4D(temp[0], temp[1], temp[2], 0); } qreal hsy[3]; RGBToHSY(channelValuesDisplay[0], channelValuesDisplay[1], channelValuesDisplay[2], &hsy[0], &hsy[1], &hsy[2], m_d->lumaRGB[0], m_d->lumaRGB[1], m_d->lumaRGB[2]); hsy[2] = pow(hsy[2], 1/m_d->gamma); coordinates = QVector4D(hsy[0], hsy[1], hsy[2], 0.f); } // if we couldn't determine a hue, keep last value if (coordinates[0] < 0) { coordinates[0] = m_d->channelValues[0]; } for (int i=0; i<3; i++) { coordinates[i] = qBound(0.f, coordinates[i], 1.f); } } } else { for (int i=0; i<4; i++) { coordinates[i] = qBound(0.f, channelValuesDisplay[i], 1.f); } } return coordinates; } void KisVisualColorSelector::configurationChanged() { if (m_d->updateTimer) { m_d->updateTimer->start(); } } void KisVisualColorSelector::slotDisplayConfigurationChanged() { Q_ASSERT(m_d->displayRenderer); if (m_d->currentCS) { m_d->channelMaxValues = QVector4D(1, 1, 1, 1); QList channels = m_d->currentCS->channels(); for (int i=0; icolorChannelCount; ++i) { m_d->channelMaxValues[i] = m_d->displayRenderer->maxVisibleFloatValue(channels[m_d->displayPosition[i]]); } // need to re-scale our normalized channel values on exposure changes: m_d->channelValues = convertKoColorToShapeCoordinates(m_d->currentcolor); Q_FOREACH (KisVisualColorSelectorShape *shape, m_d->widgetlist) { shape->setChannelValues(m_d->channelValues, true); } if (isHSXModel()) { emit sigHSXChanged(QVector3D(m_d->channelValues)); } } } void KisVisualColorSelector::slotRebuildSelectors() { KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); m_d->acs_config = KisColorSelectorConfiguration::fromString(cfg.readEntry("colorSelectorConfiguration", KisColorSelectorConfiguration().toString())); ColorModel oldModel = m_d->model; QList channelList = m_d->currentCS->channels(); int cCount = 0; Q_FOREACH(const KoChannelInfo *channel, channelList) { if (channel->channelType() != KoChannelInfo::ALPHA) { m_d->displayPosition[cCount] = channel->displayPosition(); ++cCount; } } Q_ASSERT_X(cCount < 5, "", "unsupported channel count!"); m_d->colorChannelCount = cCount; // TODO: The following is done because the IDs are actually strings. Ideally, in the future, we // refactor everything so that the IDs are actually proper enums or something faster. if (m_d->displayRenderer && (m_d->currentCS->colorDepthId() == Float16BitsColorDepthID || m_d->currentCS->colorDepthId() == Float32BitsColorDepthID || m_d->currentCS->colorDepthId() == Float64BitsColorDepthID) && m_d->currentCS->colorModelId() != LABAColorModelID && m_d->currentCS->colorModelId() != CMYKAColorModelID) { m_d->exposureSupported = true; } else { m_d->exposureSupported = false; } m_d->isRGBA = (m_d->currentCS->colorModelId() == RGBAColorModelID); const KoColorProfile *profile = m_d->currentCS->profile(); m_d->isLinear = (profile && profile->isLinear()); qDeleteAll(children()); m_d->widgetlist.clear(); // TODO: Layout only used for monochrome selector currently, but always present QLayout *layout = new QHBoxLayout; //recreate all the widgets. m_d->model = KisVisualColorSelector::Channel; if (m_d->currentCS->colorChannelCount() == 1) { KisVisualColorSelectorShape *bar; if (m_d->circular==false) { bar = new KisVisualRectangleSelectorShape(this, KisVisualColorSelectorShape::onedimensional, m_d->currentCS, 0, 0,m_d->displayRenderer, 20); bar->setMaximumWidth(width()*0.1); bar->setMaximumHeight(height()); } else { bar = new KisVisualEllipticalSelectorShape(this, KisVisualColorSelectorShape::onedimensional, m_d->currentCS, 0, 0,m_d->displayRenderer, 20, KisVisualEllipticalSelectorShape::borderMirrored); layout->setMargin(0); } connect(bar, SIGNAL(sigCursorMoved(QPointF)), SLOT(slotCursorMoved(QPointF))); layout->addWidget(bar); m_d->widgetlist.append(bar); } else if (m_d->currentCS->colorChannelCount() == 3) { KisVisualColorSelector::ColorModel modelS = KisVisualColorSelector::HSV; int channel1 = 0; int channel2 = 1; int channel3 = 2; switch(m_d->acs_config.subTypeParameter) { case KisColorSelectorConfiguration::H: channel1 = 0; break; case KisColorSelectorConfiguration::hsyS: case KisColorSelectorConfiguration::hsiS: case KisColorSelectorConfiguration::hslS: case KisColorSelectorConfiguration::hsvS: channel1 = 1; break; case KisColorSelectorConfiguration::V: case KisColorSelectorConfiguration::L: case KisColorSelectorConfiguration::I: case KisColorSelectorConfiguration::Y: channel1 = 2; break; default: Q_ASSERT_X(false, "", "Invalid acs_config.subTypeParameter"); } switch(m_d->acs_config.mainTypeParameter) { case KisColorSelectorConfiguration::hsySH: modelS = KisVisualColorSelector::HSY; channel2 = 0; channel3 = 1; break; case KisColorSelectorConfiguration::hsiSH: modelS = KisVisualColorSelector::HSI; channel2 = 0; channel3 = 1; break; case KisColorSelectorConfiguration::hslSH: modelS = KisVisualColorSelector::HSL; channel2 = 0; channel3 = 1; break; case KisColorSelectorConfiguration::hsvSH: modelS = KisVisualColorSelector::HSV; channel2 = 0; channel3 = 1; break; case KisColorSelectorConfiguration::YH: modelS = KisVisualColorSelector::HSY; channel2 = 0; channel3 = 2; break; case KisColorSelectorConfiguration::LH: modelS = KisVisualColorSelector::HSL; channel2 = 0; channel3 = 2; break; case KisColorSelectorConfiguration::IH: modelS = KisVisualColorSelector::HSL; channel2 = 0; channel3 = 2; break; case KisColorSelectorConfiguration::VH: modelS = KisVisualColorSelector::HSV; channel2 = 0; channel3 = 2; break; case KisColorSelectorConfiguration::SY: modelS = KisVisualColorSelector::HSY; channel2 = 1; channel3 = 2; break; case KisColorSelectorConfiguration::SI: modelS = KisVisualColorSelector::HSI; channel2 = 1; channel3 = 2; break; case KisColorSelectorConfiguration::SL: modelS = KisVisualColorSelector::HSL; channel2 = 1; channel3 = 2; break; case KisColorSelectorConfiguration::SV: case KisColorSelectorConfiguration::SV2: modelS = KisVisualColorSelector::HSV; channel2 = 1; channel3 = 2; break; default: Q_ASSERT_X(false, "", "Invalid acs_config.mainTypeParameter"); } if (m_d->acs_config.mainType == KisColorSelectorConfiguration::Triangle) { modelS = KisVisualColorSelector::HSV; //Triangle only really works in HSV mode. } // L*a*b* mimics the HSX selector types, but model is still Channel (until someone implements LCH) if (m_d->isRGBA) { m_d->model = modelS; m_d->gamma = cfg.readEntry("gamma", 2.2); m_d->applyGamma = (m_d->isLinear && modelS != ColorModel::HSY); // Note: only profiles that define colorants will give precise luma coefficients. // Maybe using the explicitly set values of the Advanced Color Selector is better? QVector luma = m_d->currentCS->lumaCoefficients(); memcpy(m_d->lumaRGB, luma.constData(), 3*sizeof(qreal)); } KisVisualColorSelectorShape *bar; if (m_d->acs_config.subType == KisColorSelectorConfiguration::Ring) { bar = new KisVisualEllipticalSelectorShape(this, KisVisualColorSelectorShape::onedimensional, m_d->currentCS, channel1, channel1, m_d->displayRenderer, 20,KisVisualEllipticalSelectorShape::border); } else if (m_d->acs_config.subType == KisColorSelectorConfiguration::Slider && m_d->circular == false) { bar = new KisVisualRectangleSelectorShape(this, KisVisualColorSelectorShape::onedimensional, m_d->currentCS, channel1, channel1, m_d->displayRenderer, 20); } else if (m_d->acs_config.subType == KisColorSelectorConfiguration::Slider && m_d->circular == true) { bar = new KisVisualEllipticalSelectorShape(this, KisVisualColorSelectorShape::onedimensional, m_d->currentCS, channel1, channel1, m_d->displayRenderer, 20, KisVisualEllipticalSelectorShape::borderMirrored); } else { // Accessing bar below would crash since it's not initialized. // Hopefully this can never happen. warnUI << "Invalid subType, cannot initialize KisVisualColorSelectorShape"; Q_ASSERT_X(false, "", "Invalid subType, cannot initialize KisVisualColorSelectorShape"); return; } m_d->widgetlist.append(bar); KisVisualColorSelectorShape *block; if (m_d->acs_config.mainType == KisColorSelectorConfiguration::Triangle) { block = new KisVisualTriangleSelectorShape(this, KisVisualColorSelectorShape::twodimensional, m_d->currentCS, channel2, channel3, m_d->displayRenderer); } else if (m_d->acs_config.mainType == KisColorSelectorConfiguration::Square) { block = new KisVisualRectangleSelectorShape(this, KisVisualColorSelectorShape::twodimensional, m_d->currentCS, channel2, channel3, m_d->displayRenderer); } else { block = new KisVisualEllipticalSelectorShape(this, KisVisualColorSelectorShape::twodimensional, m_d->currentCS, channel2, channel3, m_d->displayRenderer); } connect(bar, SIGNAL(sigCursorMoved(QPointF)), SLOT(slotCursorMoved(QPointF))); connect(block, SIGNAL(sigCursorMoved(QPointF)), SLOT(slotCursorMoved(QPointF))); m_d->widgetlist.append(block); } else if (m_d->currentCS->colorChannelCount() == 4) { KisVisualRectangleSelectorShape *block = new KisVisualRectangleSelectorShape(this, KisVisualRectangleSelectorShape::twodimensional, m_d->currentCS, 0, 1); KisVisualRectangleSelectorShape *block2 = new KisVisualRectangleSelectorShape(this, KisVisualRectangleSelectorShape::twodimensional, m_d->currentCS, 2, 3); connect(block, SIGNAL(sigCursorMoved(QPointF)), SLOT(slotCursorMoved(QPointF))); connect(block2, SIGNAL(sigCursorMoved(QPointF)), SLOT(slotCursorMoved(QPointF))); m_d->widgetlist.append(block); m_d->widgetlist.append(block2); } this->setLayout(layout); // make sure we call "our" resize function KisVisualColorSelector::resizeEvent(0); // finally recalculate channel values and update widgets if (m_d->displayRenderer) { slotDisplayConfigurationChanged(); } m_d->channelValues = convertKoColorToShapeCoordinates(m_d->currentcolor); Q_FOREACH (KisVisualColorSelectorShape *shape, m_d->widgetlist) { shape->setChannelValues(m_d->channelValues, true); shape->setAcceptTabletEvents(m_d->acceptTabletEvents); // if this widget is currently visible, new children are hidden by default shape->show(); } if (m_d->model != oldModel) { emit sigColorModelChanged(); } } void KisVisualColorSelector::setDisplayRenderer (const KoColorDisplayRendererInterface *displayRenderer) { m_d->displayRenderer = displayRenderer; if (m_d->widgetlist.size()>0) { Q_FOREACH (KisVisualColorSelectorShape *shape, m_d->widgetlist) { shape->setDisplayRenderer(displayRenderer); } } connect(m_d->displayRenderer, SIGNAL(displayConfigurationChanged()), SLOT(slotDisplayConfigurationChanged()), Qt::UniqueConnection); slotDisplayConfigurationChanged(); } void KisVisualColorSelector::slotCursorMoved(QPointF pos) { const KisVisualColorSelectorShape *shape = qobject_cast(sender()); Q_ASSERT(shape); QVector channels = shape->getChannels(); m_d->channelValues[channels.at(0)] = pos.x(); if (shape->getDimensions() == KisVisualColorSelectorShape::twodimensional) { m_d->channelValues[channels.at(1)] = pos.y(); } KoColor newColor = convertShapeCoordsToKoColor(m_d->channelValues); if (newColor != m_d->currentcolor) { m_d->currentcolor = newColor; emit sigNewColor(m_d->currentcolor); } if (isHSXModel()) { emit sigHSXChanged(QVector3D(m_d->channelValues)); } Q_FOREACH (KisVisualColorSelectorShape *widget, m_d->widgetlist) { if (widget != shape){ widget->setChannelValues(m_d->channelValues, false); } } } void KisVisualColorSelector::resizeEvent(QResizeEvent *) { int sizeValue = qMin(width(), height()); int borderWidth = qMax(sizeValue*0.1, 20.0); QRect newrect(0,0, this->geometry().width(), this->geometry().height()); if (!m_d->currentCS) { slotSetColorSpace(m_d->currentcolor.colorSpace()); } if (m_d->currentCS->colorChannelCount()==3) { // set border width first, else the resized painting may have happened already, and we'd have to re-render m_d->widgetlist.at(0)->setBorderWidth(borderWidth); if (m_d->acs_config.subType == KisColorSelectorConfiguration::Ring) { m_d->widgetlist.at(0)->resize(sizeValue,sizeValue); } else if (m_d->acs_config.subType == KisColorSelectorConfiguration::Slider && m_d->circular==false) { m_d->widgetlist.at(0)->resize(borderWidth, sizeValue); } else if (m_d->acs_config.subType == KisColorSelectorConfiguration::Slider && m_d->circular==true) { m_d->widgetlist.at(0)->resize(sizeValue,sizeValue); } if (m_d->acs_config.mainType == KisColorSelectorConfiguration::Triangle) { m_d->widgetlist.at(1)->setGeometry(m_d->widgetlist.at(0)->getSpaceForTriangle(newrect)); } else if (m_d->acs_config.mainType == KisColorSelectorConfiguration::Square) { m_d->widgetlist.at(1)->setGeometry(m_d->widgetlist.at(0)->getSpaceForSquare(newrect)); } else if (m_d->acs_config.mainType == KisColorSelectorConfiguration::Wheel) { m_d->widgetlist.at(1)->setGeometry(m_d->widgetlist.at(0)->getSpaceForCircle(newrect)); } } else if (m_d->currentCS->colorChannelCount() == 4) { int sizeBlock = qMin(width()/2 - 8, height()); m_d->widgetlist.at(0)->setGeometry(0, 0, sizeBlock, sizeBlock); m_d->widgetlist.at(1)->setGeometry(sizeBlock + 8, 0, sizeBlock, sizeBlock); } } diff --git a/libs/widgets/KisVisualColorSelector.h b/libs/widgets/KisVisualColorSelector.h index ceae1aa13f..a488e4ef5f 100644 --- a/libs/widgets/KisVisualColorSelector.h +++ b/libs/widgets/KisVisualColorSelector.h @@ -1,103 +1,101 @@ /* * Copyright (C) Wolthera van Hovell tot Westerflier , (C) 2016 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_VISUAL_COLOR_SELECTOR_H #define KIS_VISUAL_COLOR_SELECTOR_H #include #include -#include -#include -#include #include -#include -#include "KoColorDisplayRendererInterface.h" #include "KisColorSelectorConfiguration.h" #include "KisColorSelectorInterface.h" #include "kritawidgets_export.h" +class KoColorSpace; +class KoColorDisplayRendererInterface; + /** * @brief The KisVisualColorSelector class * * This gives a color selector box that draws gradients and everything. * * Unlike other color selectors, this one draws the full gamut of the given * colorspace. */ class KRITAWIDGETS_EXPORT KisVisualColorSelector : public KisColorSelectorInterface { Q_OBJECT public: enum ColorModel { None, Channel, HSV, HSL, HSI, HSY, YUV }; explicit KisVisualColorSelector(QWidget *parent = 0); ~KisVisualColorSelector() override; /** * @brief setConfig * @param forceCircular * Force circular is for space where you only have room for a circular selector. * @param forceSelfUpdate * ignored, can possibly be removed from parent class now */ void setConfig(bool forceCircular, bool forceSelfUpdate) override; void setAcceptTabletEvents(bool on); KoColor getCurrentColor() const override; QVector4D getChannelValues() const; ColorModel getColorModel() const; bool isHSXModel() const; KoColor convertShapeCoordsToKoColor(const QVector4D &coordinates) const; QVector4D convertKoColorToShapeCoordinates(KoColor c) const; public Q_SLOTS: void slotSetColor(const KoColor &c) override; void slotSetColorSpace(const KoColorSpace *cs) override; void slotSetHSX(const QVector3D &hsx); void configurationChanged(); void setDisplayRenderer (const KoColorDisplayRendererInterface *displayRenderer) override; private Q_SLOTS: void slotCursorMoved(QPointF pos); void slotDisplayConfigurationChanged(); void slotRebuildSelectors(); Q_SIGNALS: /** * @brief sigColorModelChanged is emitted whenever the selector's color model changes. * * This is mostly relevant for configuration changes where the same RGB model * gets represented in a different way like HSV, HSL etc. so the values of * sigHSXChanged() change meaning. * * @see getColorModel() */ void sigColorModelChanged(); void sigHSXChanged(const QVector3D &hsx); protected: void resizeEvent(QResizeEvent *) override; private: struct Private; const QScopedPointer m_d; }; #endif diff --git a/libs/widgets/KisVisualColorSelectorShape.cpp b/libs/widgets/KisVisualColorSelectorShape.cpp index 6417fb1fc7..24123f5c2b 100644 --- a/libs/widgets/KisVisualColorSelectorShape.cpp +++ b/libs/widgets/KisVisualColorSelectorShape.cpp @@ -1,388 +1,385 @@ /* * Copyright (C) Wolthera van Hovell tot Westerflier , (C) 2016 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisVisualColorSelectorShape.h" #include -#include +#include #include -#include #include #include #include #include #include -#include #include #include #include #include "KoColorConversions.h" #include "KoColorDisplayRendererInterface.h" #include "KoChannelInfo.h" #include #include -#include "kis_signal_compressor.h" #include "kis_debug.h" struct KisVisualColorSelectorShape::Private { QImage gradient; QImage alphaMask; QImage fullSelector; bool imagesNeedUpdate { true }; bool alphaNeedsUpdate { true }; bool acceptTabletEvents { false }; QPointF currentCoordinates; // somewhat redundant? QPointF dragStart; QVector4D currentChannelValues; Dimensions dimension; const KoColorSpace *colorSpace; int channel1; int channel2; const KoColorDisplayRendererInterface *displayRenderer = 0; }; KisVisualColorSelectorShape::KisVisualColorSelectorShape(QWidget *parent, KisVisualColorSelectorShape::Dimensions dimension, const KoColorSpace *cs, int channel1, int channel2, const KoColorDisplayRendererInterface *displayRenderer): QWidget(parent), m_d(new Private) { m_d->dimension = dimension; m_d->colorSpace = cs; int maxchannel = m_d->colorSpace->colorChannelCount()-1; m_d->channel1 = qBound(0, channel1, maxchannel); m_d->channel2 = qBound(0, channel2, maxchannel); this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); setDisplayRenderer(displayRenderer); } KisVisualColorSelectorShape::~KisVisualColorSelectorShape() { } QPointF KisVisualColorSelectorShape::getCursorPosition() { return m_d->currentCoordinates; } void KisVisualColorSelectorShape::setCursorPosition(QPointF position, bool signal) { QPointF newPos(qBound(0.0, position.x(), 1.0), qBound(0.0, position.y(), 1.0)); if (newPos != m_d->currentCoordinates) { m_d->currentCoordinates = newPos; // for internal consistency, because we have a bit of redundancy here m_d->currentChannelValues[m_d->channel1] = newPos.x(); if (m_d->dimension == Dimensions::twodimensional){ m_d->currentChannelValues[m_d->channel2] = newPos.y(); } update(); if (signal){ emit sigCursorMoved(newPos); } } } void KisVisualColorSelectorShape::setChannelValues(QVector4D channelValues, bool setCursor) { //qDebug() << this << "setChannelValues"; m_d->currentChannelValues = channelValues; if (setCursor) { m_d->currentCoordinates = QPointF(qBound(0.f, channelValues[m_d->channel1], 1.f), qBound(0.f, channelValues[m_d->channel2], 1.f)); } else { // for internal consistency, because we have a bit of redundancy here m_d->currentChannelValues[m_d->channel1] = m_d->currentCoordinates.x(); if (m_d->dimension == Dimensions::twodimensional){ m_d->currentChannelValues[m_d->channel2] = m_d->currentCoordinates.y(); } } m_d->imagesNeedUpdate = true; update(); } void KisVisualColorSelectorShape::setAcceptTabletEvents(bool on) { m_d->acceptTabletEvents = on; } void KisVisualColorSelectorShape::setDisplayRenderer (const KoColorDisplayRendererInterface *displayRenderer) { if (displayRenderer) { if (m_d->displayRenderer) { m_d->displayRenderer->disconnect(this); } m_d->displayRenderer = displayRenderer; } else { m_d->displayRenderer = KoDumbColorDisplayRenderer::instance(); } } void KisVisualColorSelectorShape::forceImageUpdate() { //qDebug() << this << "forceImageUpdate"; m_d->alphaNeedsUpdate = true; m_d->imagesNeedUpdate = true; } QColor KisVisualColorSelectorShape::getColorFromConverter(KoColor c){ QColor col; KoColor color = c; if (m_d->displayRenderer) { color.convertTo(m_d->displayRenderer->getPaintingColorSpace()); col = m_d->displayRenderer->toQColor(c); } else { col = c.toQColor(); } return col; } // currently unused? void KisVisualColorSelectorShape::slotSetActiveChannels(int channel1, int channel2) { //qDebug() << this << "slotSetActiveChannels"; int maxchannel = m_d->colorSpace->colorChannelCount()-1; m_d->channel1 = qBound(0, channel1, maxchannel); m_d->channel2 = qBound(0, channel2, maxchannel); m_d->imagesNeedUpdate = true; update(); } bool KisVisualColorSelectorShape::imagesNeedUpdate() const { return m_d->imagesNeedUpdate; } QImage KisVisualColorSelectorShape::getImageMap() { //qDebug() << this << ">>>>>>>>> getImageMap()" << m_d->imagesNeedUpdate; if (m_d->imagesNeedUpdate) { // Fill a buffer with the right kocolors m_d->gradient = renderBackground(m_d->currentChannelValues, m_d->colorSpace->pixelSize()); m_d->imagesNeedUpdate = false; } return m_d->gradient; } const QImage KisVisualColorSelectorShape::getAlphaMask() const { if (m_d->alphaNeedsUpdate) { m_d->alphaMask = renderAlphaMask(); m_d->alphaNeedsUpdate = false; } return m_d->alphaMask; } QImage KisVisualColorSelectorShape::convertImageMap(const quint8 *rawColor, quint32 bufferSize, QSize imgSize) const { Q_ASSERT(bufferSize == imgSize.width() * imgSize.height() * m_d->colorSpace->pixelSize()); QImage image; // Convert the buffer to a qimage if (m_d->displayRenderer) { image = m_d->displayRenderer->convertToQImage(m_d->colorSpace, rawColor, imgSize.width(), imgSize.height()); } else { image = m_d->colorSpace->convertToQImage(rawColor, imgSize.width(), imgSize.height(), 0, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); } // safeguard: if (image.isNull()) { image = QImage(width(), height(), QImage::Format_ARGB32); image.fill(Qt::black); } return image; } QImage KisVisualColorSelectorShape::renderBackground(const QVector4D &channelValues, quint32 pixelSize) const { const KisVisualColorSelector *selector = qobject_cast(parent()); Q_ASSERT(selector); // Hi-DPI aware rendering requires that we determine the device pixel dimension; // actual widget size in device pixels is not accessible unfortunately, it might be 1px smaller... const qreal deviceDivider = 1.0 / devicePixelRatioF(); const int deviceWidth = qCeil(width() * devicePixelRatioF()); const int deviceHeight = qCeil(height() * devicePixelRatioF()); quint32 imageSize = deviceWidth * deviceHeight * m_d->colorSpace->pixelSize(); QScopedArrayPointer raw(new quint8[imageSize] {}); quint8 *dataPtr = raw.data(); QVector4D coordinates = channelValues; QImage alpha = getAlphaMask(); bool checkAlpha = !alpha.isNull() && alpha.valid(deviceWidth - 1, deviceHeight - 1); KIS_SAFE_ASSERT_RECOVER(!checkAlpha || alpha.format() == QImage::Format_Alpha8) { checkAlpha = false; } KoColor filler(Qt::white, m_d->colorSpace); for (int y = 0; y < deviceHeight; y++) { const uchar *alphaLine = checkAlpha ? alpha.scanLine(y) : 0; for (int x=0; x < deviceWidth; x++) { if (!checkAlpha || alphaLine[x]) { QPointF newcoordinate = convertWidgetCoordinateToShapeCoordinate(QPointF(x, y) * deviceDivider); coordinates[m_d->channel1] = newcoordinate.x(); if (m_d->dimension == Dimensions::twodimensional) { coordinates[m_d->channel2] = newcoordinate.y(); } KoColor c = selector->convertShapeCoordsToKoColor(coordinates); memcpy(dataPtr, c.data(), pixelSize); } else { // need to write a color with non-zero alpha, otherwise the display converter // will for some arcane reason crop the final QImage and screw rendering memcpy(dataPtr, filler.data(), pixelSize); } dataPtr += pixelSize; } } QImage image = convertImageMap(raw.data(), imageSize, QSize(deviceWidth, deviceHeight)); image.setDevicePixelRatio(devicePixelRatioF()); if (!alpha.isNull()) { QPainter painter(&image); // transfer alphaMask to Alpha channel painter.setCompositionMode(QPainter::CompositionMode_DestinationIn); painter.drawImage(0, 0, alpha); } return image; } QImage KisVisualColorSelectorShape::renderAlphaMask() const { return QImage(); } QPointF KisVisualColorSelectorShape::mousePositionToShapeCoordinate(const QPointF &pos, const QPointF &dragStart) const { Q_UNUSED(dragStart) return convertWidgetCoordinateToShapeCoordinate(pos); } void KisVisualColorSelectorShape::mousePressEvent(QMouseEvent *e) { if (e->button() == Qt::LeftButton) { m_d->dragStart = e->localPos(); QPointF coordinates = mousePositionToShapeCoordinate(e->localPos(), m_d->dragStart); setCursorPosition(coordinates, true); } else { e->ignore(); } } void KisVisualColorSelectorShape::mouseMoveEvent(QMouseEvent *e) { if (e->buttons() & Qt::LeftButton) { QPointF coordinates = mousePositionToShapeCoordinate(e->localPos(), m_d->dragStart); setCursorPosition(coordinates, true); } else { e->ignore(); } } void KisVisualColorSelectorShape::mouseReleaseEvent(QMouseEvent *e) { if (e->button() != Qt::LeftButton) { e->ignore(); } } void KisVisualColorSelectorShape::tabletEvent(QTabletEvent* event) { // only accept tablet events that are associated to "left" button // NOTE: QTabletEvent does not have a windowPos() equivalent, but we don't need it if (m_d->acceptTabletEvents && (event->button() == Qt::LeftButton || (event->buttons() & Qt::LeftButton))) { event->accept(); switch (event->type()) { case QEvent::TabletPress: { QMouseEvent mouseEvent(QEvent::MouseButtonPress, event->posF(), event->posF(), event->globalPosF(), event->button(), event->buttons(), event->modifiers(), Qt::MouseEventSynthesizedByApplication); mousePressEvent(&mouseEvent); break; } case QEvent::TabletMove: { QMouseEvent mouseEvent(QEvent::MouseMove, event->posF(), event->posF(), event->globalPosF(), event->button(), event->buttons(), event->modifiers(), Qt::MouseEventSynthesizedByApplication); mouseMoveEvent(&mouseEvent); break; } case QEvent::TabletRelease: { QMouseEvent mouseEvent(QEvent::MouseButtonRelease, event->posF(), event->posF(), event->globalPosF(), event->button(), event->buttons(), event->modifiers(), Qt::MouseEventSynthesizedByApplication); mouseReleaseEvent(&mouseEvent); break; } default: event->ignore(); } } } void KisVisualColorSelectorShape::paintEvent(QPaintEvent*) { QPainter painter(this); drawCursor(); painter.drawImage(0,0,m_d->fullSelector); } void KisVisualColorSelectorShape::resizeEvent(QResizeEvent *) { forceImageUpdate(); setMask(getMaskMap()); } KisVisualColorSelectorShape::Dimensions KisVisualColorSelectorShape::getDimensions() const { return m_d->dimension; } void KisVisualColorSelectorShape::setFullImage(QImage full) { m_d->fullSelector = full; } KoColor KisVisualColorSelectorShape::getCurrentColor() { const KisVisualColorSelector *selector = qobject_cast(parent()); if (selector) { return selector->convertShapeCoordsToKoColor(m_d->currentChannelValues); } return KoColor(m_d->colorSpace); } QVector KisVisualColorSelectorShape::getChannels() const { QVector channels(2); channels[0] = m_d->channel1; channels[1] = m_d->channel2; return channels; } diff --git a/libs/widgets/KisVisualEllipticalSelectorShape.cpp b/libs/widgets/KisVisualEllipticalSelectorShape.cpp index abbfa8275b..d4d10163f3 100644 --- a/libs/widgets/KisVisualEllipticalSelectorShape.cpp +++ b/libs/widgets/KisVisualEllipticalSelectorShape.cpp @@ -1,257 +1,242 @@ /* * Copyright (C) Wolthera van Hovell tot Westerflier , (C) 2016 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisVisualEllipticalSelectorShape.h" #include -#include #include -#include #include -#include -#include -#include -#include -#include +#include #include -#include -#include - -#include "KoColorConversions.h" -#include "KoColorDisplayRendererInterface.h" -#include "KoChannelInfo.h" -#include -#include -#include "kis_signal_compressor.h" #include "kis_debug.h" #include "kis_global.h" KisVisualEllipticalSelectorShape::KisVisualEllipticalSelectorShape(QWidget *parent, Dimensions dimension, const KoColorSpace *cs, int channel1, int channel2, const KoColorDisplayRendererInterface *displayRenderer, int barWidth, singelDTypes d) : KisVisualColorSelectorShape(parent, dimension, cs, channel1, channel2, displayRenderer) { //qDebug() << "creating KisVisualEllipticalSelectorShape" << this; m_type = d; m_barWidth = barWidth; } KisVisualEllipticalSelectorShape::~KisVisualEllipticalSelectorShape() { //qDebug() << "deleting KisVisualEllipticalSelectorShape" << this; } QSize KisVisualEllipticalSelectorShape::sizeHint() const { return QSize(180,180); } void KisVisualEllipticalSelectorShape::setBorderWidth(int width) { m_barWidth = width; forceImageUpdate(); update(); } QRect KisVisualEllipticalSelectorShape::getSpaceForSquare(QRect geom) { int sizeValue = qMin(width(),height()); QRect b(geom.left(), geom.top(), sizeValue, sizeValue); QLineF radius(b.center(), QPointF(b.left()+m_barWidth, b.center().y()) ); radius.setAngle(135); QPointF tl = radius.p2(); radius.setAngle(315); QPointF br = radius.p2(); QRect r(tl.toPoint(), br.toPoint()); return r; } QRect KisVisualEllipticalSelectorShape::getSpaceForCircle(QRect geom) { int sizeValue = qMin(width(),height()); QRect b(geom.left(), geom.top(), sizeValue, sizeValue); QPointF tl = QPointF (b.topLeft().x()+m_barWidth, b.topLeft().y()+m_barWidth); QPointF br = QPointF (b.bottomRight().x()-m_barWidth, b.bottomRight().y()-m_barWidth); QRect r(tl.toPoint(), br.toPoint()); return r; } QRect KisVisualEllipticalSelectorShape::getSpaceForTriangle(QRect geom) { int sizeValue = qMin(width(),height()); QPointF center(0.5 * width(), 0.5 * height()); qreal radius = 0.5 * sizeValue - (m_barWidth + 4); QLineF rLine(center, QPointF(center.x() + radius, center.y())); rLine.setAngle(330); QPoint br(rLine.p2().toPoint()); //QPoint br(qCeil(rLine.p2().x()), qCeil(rLine.p2().y())); QPoint tl(width() - br.x(), m_barWidth + 4); QRect bound(tl, br); // adjust with triangle default margin for cursor rendering // it's not +5 because above calculation is for pixel center and ignores // the fact that dimensions are then effectively 1px smaller... bound.adjust(-5, -5, 4, 4); return bound.intersected(geom); } QPointF KisVisualEllipticalSelectorShape::convertShapeCoordinateToWidgetCoordinate(QPointF coordinate) const { qreal offset = 7.0; qreal a = (qreal)width()*0.5; QPointF center(a, a); QLineF line(center, QPoint((m_barWidth*0.5),a)); qreal angle = coordinate.x()*360.0; angle = 360.0 - fmod(angle+180.0, 360.0); if (m_type==KisVisualEllipticalSelectorShape::borderMirrored) { angle = (coordinate.x()/2)*360.0; angle = fmod((angle+270.0), 360.0); } line.setAngle(angle); if (getDimensions()!=KisVisualColorSelectorShape::onedimensional) { line.setLength(qMin(coordinate.y()*(a-offset), a-offset)); } return line.p2(); } QPointF KisVisualEllipticalSelectorShape::convertWidgetCoordinateToShapeCoordinate(QPointF coordinate) const { //default implementation: qreal x = 0.5; qreal y = 1.0; qreal offset = 7.0; QPointF center = QRectF(QPointF(0.0, 0.0), this->size()).center(); qreal a = (qreal(this->width()) / qreal(2)); qreal xRel = center.x()-coordinate.x(); qreal yRel = center.y()-coordinate.y(); qreal radius = sqrt(xRel*xRel+yRel*yRel); if (m_type!=KisVisualEllipticalSelectorShape::borderMirrored){ qreal angle = atan2(yRel, xRel); angle = kisRadiansToDegrees(angle); angle = fmod(angle+360, 360.0); x = angle/360.0; if (getDimensions()==KisVisualColorSelectorShape::twodimensional) { y = qBound(0.0,radius/(a-offset), 1.0); } } else { qreal angle = atan2(xRel, yRel); angle = kisRadiansToDegrees(angle); angle = fmod(angle+180, 360.0); if (angle>180.0) { angle = 360.0-angle; } x = (angle/360.0)*2; if (getDimensions()==KisVisualColorSelectorShape::twodimensional) { y = qBound(0.0,(radius+offset)/a, 1.0); } } return QPointF(x, y); } QPointF KisVisualEllipticalSelectorShape::mousePositionToShapeCoordinate(const QPointF &pos, const QPointF &dragStart) const { QPointF pos2(pos); if (m_type == KisVisualEllipticalSelectorShape::borderMirrored) { qreal h_center = width()/2.0; bool start_left = dragStart.x() < h_center; bool cursor_left = pos.x() < h_center; if (start_left != cursor_left) { pos2.setX(h_center); } } return convertWidgetCoordinateToShapeCoordinate(pos2); } QRegion KisVisualEllipticalSelectorShape::getMaskMap() { QRegion mask = QRegion(0,0,width(),height(), QRegion::Ellipse); if (getDimensions()==KisVisualColorSelectorShape::onedimensional) { mask = mask.subtracted(QRegion(m_barWidth, m_barWidth, width()-(m_barWidth*2), height()-(m_barWidth*2), QRegion::Ellipse)); } return mask; } QImage KisVisualEllipticalSelectorShape::renderAlphaMask() const { // Hi-DPI aware rendering requires that we determine the device pixel dimension; // actual widget size in device pixels is not accessible unfortunately, it might be 1px smaller... const int deviceWidth = qCeil(width() * devicePixelRatioF()); const int deviceHeight = qCeil(height() * devicePixelRatioF()); QImage alphaMask(deviceWidth, deviceHeight, QImage::Format_Alpha8); alphaMask.fill(0); alphaMask.setDevicePixelRatio(devicePixelRatioF()); QPainter painter(&alphaMask); painter.setRenderHint(QPainter::Antialiasing); painter.setBrush(Qt::white); painter.setPen(Qt::NoPen); painter.drawEllipse(2, 2, width() - 4, height() - 4); //painter.setBrush(Qt::black); if (getDimensions() == KisVisualColorSelectorShape::onedimensional) { painter.setCompositionMode(QPainter::CompositionMode_Clear); painter.drawEllipse(m_barWidth - 2, m_barWidth - 2, width() - 2*(m_barWidth-2), height() - 2*(m_barWidth-2)); } return alphaMask; } void KisVisualEllipticalSelectorShape::drawCursor() { //qDebug() << this << "KisVisualEllipticalSelectorShape::drawCursor: image needs update" << imagesNeedUpdate(); QPointF cursorPoint = convertShapeCoordinateToWidgetCoordinate(getCursorPosition()); QImage fullSelector = getImageMap(); QColor col = getColorFromConverter(getCurrentColor()); QPainter painter; painter.begin(&fullSelector); painter.setRenderHint(QPainter::Antialiasing); QBrush fill; fill.setStyle(Qt::SolidPattern); int cursorwidth = 5; if (m_type==KisVisualEllipticalSelectorShape::borderMirrored) { painter.setPen(Qt::white); fill.setColor(Qt::white); painter.setBrush(fill); painter.drawEllipse(cursorPoint, cursorwidth, cursorwidth); QPointF mirror(width() - cursorPoint.x(), cursorPoint.y()); painter.drawEllipse(mirror, cursorwidth, cursorwidth); fill.setColor(col); painter.setPen(Qt::black); painter.setBrush(fill); painter.drawEllipse(cursorPoint, cursorwidth-1, cursorwidth-1); painter.drawEllipse(mirror, cursorwidth-1, cursorwidth-1); } else { painter.setPen(Qt::white); fill.setColor(Qt::white); painter.setBrush(fill); painter.drawEllipse(cursorPoint, cursorwidth, cursorwidth); fill.setColor(col); painter.setPen(Qt::black); painter.setBrush(fill); painter.drawEllipse(cursorPoint, cursorwidth-1.0, cursorwidth-1.0); } painter.end(); setFullImage(fullSelector); } diff --git a/libs/widgets/KisVisualEllipticalSelectorShape.h b/libs/widgets/KisVisualEllipticalSelectorShape.h index a484468096..8e9008fe5b 100644 --- a/libs/widgets/KisVisualEllipticalSelectorShape.h +++ b/libs/widgets/KisVisualEllipticalSelectorShape.h @@ -1,73 +1,62 @@ /* * Copyright (C) Wolthera van Hovell tot Westerflier , (C) 2016 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KISVISUALCOLORSELECTOR_H #define KISVISUALCOLORSELECTOR_H -#include -#include -#include -#include -#include - -#include -#include -#include "KoColorDisplayRendererInterface.h" - -#include "KisColorSelectorConfiguration.h" #include "KisVisualColorSelectorShape.h" class KisVisualEllipticalSelectorShape : public KisVisualColorSelectorShape { Q_OBJECT public: enum singelDTypes{border, borderMirrored}; explicit KisVisualEllipticalSelectorShape(QWidget *parent, Dimensions dimension, const KoColorSpace *cs, int channel1, int channel2, const KoColorDisplayRendererInterface *displayRenderer = KoDumbColorDisplayRenderer::instance(), int barWidth=20, KisVisualEllipticalSelectorShape::singelDTypes d = KisVisualEllipticalSelectorShape::border ); ~KisVisualEllipticalSelectorShape() override; void setBorderWidth(int width) override; /** * @brief getSpaceForSquare * @param geom the full widget rectangle * @return rectangle with enough space for second widget */ QRect getSpaceForSquare(QRect geom) override; QRect getSpaceForCircle(QRect geom) override; QRect getSpaceForTriangle(QRect geom) override; protected: QImage renderAlphaMask() const override; QPointF mousePositionToShapeCoordinate(const QPointF &pos, const QPointF &dragStart) const override; private: QPointF convertShapeCoordinateToWidgetCoordinate(QPointF coordinate) const override; QPointF convertWidgetCoordinateToShapeCoordinate(QPointF coordinate) const override; singelDTypes m_type; int m_barWidth; QRegion getMaskMap() override; void drawCursor() override; QSize sizeHint() const override; }; #endif // KISVISUALCOLORSELECTOR_H diff --git a/libs/widgets/KisVisualRectangleSelectorShape.cpp b/libs/widgets/KisVisualRectangleSelectorShape.cpp index 58e7e7a4be..477ad6f0ae 100644 --- a/libs/widgets/KisVisualRectangleSelectorShape.cpp +++ b/libs/widgets/KisVisualRectangleSelectorShape.cpp @@ -1,376 +1,363 @@ /* * Copyright (C) Wolthera van Hovell tot Westerflier , (C) 2016 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisVisualRectangleSelectorShape.h" #include -#include #include -#include #include -#include -#include #include -#include +#include #include -#include -#include - -#include "KoColorConversions.h" -#include "KoColorDisplayRendererInterface.h" -#include "KoChannelInfo.h" -#include -#include -#include "kis_signal_compressor.h" #include "kis_debug.h" KisVisualRectangleSelectorShape::KisVisualRectangleSelectorShape(QWidget *parent, Dimensions dimension, const KoColorSpace *cs, int channel1, int channel2, const KoColorDisplayRendererInterface *displayRenderer, int width, singelDTypes d) : KisVisualColorSelectorShape(parent, dimension, cs, channel1, channel2, displayRenderer) { //qDebug() << "creating KisVisualRectangleSelectorShape" << this; m_type = d; m_barWidth = width; } KisVisualRectangleSelectorShape::~KisVisualRectangleSelectorShape() { //qDebug() << "deleting KisVisualRectangleSelectorShape" << this; } void KisVisualRectangleSelectorShape::setBorderWidth(int width) { m_barWidth = width; } QRect KisVisualRectangleSelectorShape::getSpaceForSquare(QRect geom) { QPointF tl; QPointF br; if (m_type==KisVisualRectangleSelectorShape::vertical) { br = geom.bottomRight(); tl = QPoint(geom.topLeft().x()+m_barWidth, geom.topLeft().y()); } else if (m_type==KisVisualRectangleSelectorShape::horizontal) { br = geom.bottomRight(); tl = QPoint(geom.topLeft().x(), geom.topLeft().y()+m_barWidth); } else { tl = QPointF (geom.topLeft().x()+m_barWidth, geom.topLeft().y()+m_barWidth); br = QPointF (geom.bottomRight().x()-m_barWidth, geom.bottomRight().y()-m_barWidth); } QRect a(tl.toPoint(), br.toPoint()); QRect r(a.left(), a.top(), qMin(a.height(), a.width()), qMin(a.height(), a.width())); return r; } QRect KisVisualRectangleSelectorShape::getSpaceForCircle(QRect geom) { return getSpaceForSquare(geom); } QRect KisVisualRectangleSelectorShape::getSpaceForTriangle(QRect geom) { return getSpaceForSquare(geom); } QPointF KisVisualRectangleSelectorShape::convertShapeCoordinateToWidgetCoordinate(QPointF coordinate) const { // Reminder: in Qt widget space, origin is top-left, but we want zero y to be at the bottom qreal x = 0.5 * m_barWidth; qreal y = 0.5 * m_barWidth; qreal offset = 5.0; KisVisualColorSelectorShape::Dimensions dimension = getDimensions(); if (dimension == KisVisualColorSelectorShape::onedimensional) { if ( m_type == KisVisualRectangleSelectorShape::vertical) { y = qMin((1.0 - coordinate.x())*(height()-offset*2)+offset, (qreal)height()); } else if (m_type == KisVisualRectangleSelectorShape::horizontal) { x = qMin(coordinate.x()*(width()-offset*2)+offset, (qreal)width()); } else if (m_type == KisVisualRectangleSelectorShape::border) { QRectF innerRect(m_barWidth/2, m_barWidth/2, width()-m_barWidth, height()-m_barWidth); QPointF left (innerRect.left(),innerRect.center().y()); QList polygonLines; polygonLines.append(QLineF(left, innerRect.topLeft())); polygonLines.append(QLineF(innerRect.topLeft(), innerRect.topRight())); polygonLines.append(QLineF(innerRect.topRight(), innerRect.bottomRight())); polygonLines.append(QLineF(innerRect.bottomRight(), innerRect.bottomLeft())); polygonLines.append(QLineF(innerRect.bottomLeft(), left)); qreal totalLength =0.0; Q_FOREACH(QLineF line, polygonLines) { totalLength += line.length(); } qreal length = coordinate.x()*totalLength; QPointF intersect(x,y); Q_FOREACH(QLineF line, polygonLines) { if (line.length()>length && length>0){ intersect = line.pointAt(length/line.length()); } length-=line.length(); } x = qRound(intersect.x()); y = qRound(intersect.y()); } else /*if (m_type == KisVisualRectangleSelectorShape::borderMirrored)*/ { QRectF innerRect(m_barWidth/2, m_barWidth/2, width()-m_barWidth, height()-m_barWidth); QPointF bottom (innerRect.center().x(), innerRect.bottom()); QList polygonLines; polygonLines.append(QLineF(bottom, innerRect.bottomLeft())); polygonLines.append(QLineF(innerRect.bottomLeft(), innerRect.topLeft())); polygonLines.append(QLineF(innerRect.topLeft(), innerRect.topRight())); polygonLines.append(QLineF(innerRect.topRight(), innerRect.bottomRight())); polygonLines.append(QLineF(innerRect.bottomRight(), bottom)); qreal totalLength =0.0; Q_FOREACH(QLineF line, polygonLines) { totalLength += line.length(); } qreal length = coordinate.x()*(totalLength/2); QPointF intersect(x,y); if (coordinate.y()==1) { for (int i = polygonLines.size()-1; i==0; i--) { QLineF line = polygonLines.at(i); if (line.length()>length && length>0){ intersect = line.pointAt(length/line.length()); } length-=line.length(); } } else { Q_FOREACH(QLineF line, polygonLines) { if (line.length()>length && length>0){ intersect = line.pointAt(length/line.length()); } length-=line.length(); } } x = qRound(intersect.x()); y = qRound(intersect.y()); } } else { x = qMin(coordinate.x()*(width()-offset*2)+offset, (qreal)width()); y = qMin((1.0 - coordinate.y())*(height()-offset*2)+offset, (qreal)height()); } return QPointF(x,y); } QPointF KisVisualRectangleSelectorShape::convertWidgetCoordinateToShapeCoordinate(QPointF coordinate) const { // Reminder: in Qt widget space, origin is top-left, but we want zero y to be at the bottom //default values: qreal x = 0.5; qreal y = 0.5; qreal offset = 5.0; KisVisualColorSelectorShape::Dimensions dimension = getDimensions(); if (dimension == KisVisualColorSelectorShape::onedimensional ) { if (m_type == KisVisualRectangleSelectorShape::vertical) { x = 1.0 - (coordinate.y()-offset)/(height()-offset*2); } else if (m_type == KisVisualRectangleSelectorShape::horizontal) { x = (coordinate.x()-offset)/(width()-offset*2); } else if (m_type == KisVisualRectangleSelectorShape::border) { //border QRectF innerRect(m_barWidth, m_barWidth, width()-(m_barWidth*2), height()-(m_barWidth*2)); QPointF left (innerRect.left(),innerRect.center().y()); QList polygonLines; polygonLines.append(QLineF(left, innerRect.topLeft())); polygonLines.append(QLineF(innerRect.topLeft(), innerRect.topRight())); polygonLines.append(QLineF(innerRect.topRight(), innerRect.bottomRight())); polygonLines.append(QLineF(innerRect.bottomRight(), innerRect.bottomLeft())); polygonLines.append(QLineF(innerRect.bottomLeft(), left)); QLineF radius(coordinate, this->geometry().center()); QPointF intersect(0.5,0.5); qreal length = 0.0; qreal totalLength = 0.0; bool foundIntersect = false; Q_FOREACH(QLineF line, polygonLines) { if (line.intersect(radius,&intersect)==QLineF::BoundedIntersection && foundIntersect==false) { foundIntersect = true; length+=QLineF(line.p1(), intersect).length(); } if (foundIntersect==false) { length+=line.length(); } totalLength+=line.length(); } x = length/totalLength; } else /*if (m_type == KisVisualRectangleSelectorShape::borderMirrored)*/ { //border QRectF innerRect(m_barWidth, m_barWidth, width()-(m_barWidth*2), height()-(m_barWidth*2)); QPointF bottom (innerRect.center().x(), innerRect.bottom()); QList polygonLines; polygonLines.append(QLineF(bottom, innerRect.bottomLeft())); polygonLines.append(QLineF(innerRect.bottomLeft(), innerRect.topLeft())); polygonLines.append(QLineF(innerRect.topLeft(), innerRect.topRight())); polygonLines.append(QLineF(innerRect.topRight(), innerRect.bottomRight())); polygonLines.append(QLineF(innerRect.bottomRight(), bottom)); QLineF radius(coordinate, this->geometry().center()); QPointF intersect(0.5,0.5); qreal length = 0.0; qreal totalLength = 0.0; bool foundIntersect = false; Q_FOREACH(QLineF line, polygonLines) { if (line.intersect(radius,&intersect)==QLineF::BoundedIntersection && foundIntersect==false) { foundIntersect = true; length+=QLineF(line.p1(), intersect).length(); } if (foundIntersect==false) { length+=line.length(); } totalLength+=line.length(); } int halflength = totalLength/2; if (length>halflength) { x = (halflength - (length-halflength))/halflength; y = 1.0; } else { x = length/halflength; y = 0.0; } } } else { x = (coordinate.x()-offset)/(width()-offset*2); y = 1.0 - (coordinate.y()-offset)/(height()-offset*2); } x = qBound((qreal)0.0, x, (qreal)1.0); y = qBound((qreal)0.0, y, (qreal)1.0); return QPointF(x, y); } QRegion KisVisualRectangleSelectorShape::getMaskMap() { QRegion mask = QRegion(0,0,width(),height()); if (m_type==KisVisualRectangleSelectorShape::border || m_type==KisVisualRectangleSelectorShape::borderMirrored) { mask = mask.subtracted(QRegion(m_barWidth, m_barWidth, width()-(m_barWidth*2), height()-(m_barWidth*2))); } return mask; } QImage KisVisualRectangleSelectorShape::renderAlphaMask() const { // Hi-DPI aware rendering requires that we determine the device pixel dimension; // actual widget size in device pixels is not accessible unfortunately, it might be 1px smaller... const int deviceWidth = qCeil(width() * devicePixelRatioF()); const int deviceHeight = qCeil(height() * devicePixelRatioF()); QImage alphaMask(deviceWidth, deviceHeight, QImage::Format_Alpha8); alphaMask.fill(0); alphaMask.setDevicePixelRatio(devicePixelRatioF()); QPainter painter(&alphaMask); painter.setRenderHint(QPainter::Antialiasing); painter.setBrush(Qt::white); painter.setPen(Qt::NoPen); painter.drawRect(3, 3, width() - 6, height() - 6); if (m_type == border || m_type == borderMirrored) { painter.setBrush(Qt::black); painter.drawRect(m_barWidth, m_barWidth, width() - 2 * m_barWidth, height() - 2 * m_barWidth); } return alphaMask; } void KisVisualRectangleSelectorShape::drawCursor() { //qDebug() << this << "KisVisualRectangleSelectorShape::drawCursor: image needs update" << imagesNeedUpdate(); QPointF cursorPoint = convertShapeCoordinateToWidgetCoordinate(getCursorPosition()); QImage fullSelector = getImageMap(); QColor col = getColorFromConverter(getCurrentColor()); QPainter painter; painter.begin(&fullSelector); painter.setRenderHint(QPainter::Antialiasing); //QPainterPath path; QBrush fill; fill.setStyle(Qt::SolidPattern); int cursorwidth = 5; QRect rect(cursorPoint.toPoint().x()-cursorwidth,cursorPoint.toPoint().y()-cursorwidth, cursorwidth*2,cursorwidth*2); if (m_type==KisVisualRectangleSelectorShape::vertical){ int x = ( cursorPoint.x()-(width()/2)+1 ); int y = ( cursorPoint.y()-cursorwidth ); rect.setCoords(x, y, x+width()-2, y+(cursorwidth*2)); } else { int x = cursorPoint.x()-cursorwidth; int y = cursorPoint.y()-(height()/2)+1; rect.setCoords(x, y, x+(cursorwidth*2), y+cursorwidth-2); } QRectF innerRect(m_barWidth, m_barWidth, width()-(m_barWidth*2), height()-(m_barWidth*2)); if (getDimensions() == KisVisualColorSelectorShape::onedimensional && m_type!=KisVisualRectangleSelectorShape::border && m_type!=KisVisualRectangleSelectorShape::borderMirrored) { painter.setPen(Qt::white); fill.setColor(Qt::white); painter.setBrush(fill); painter.drawRect(rect); //set filter conversion! fill.setColor(col); painter.setPen(Qt::black); painter.setBrush(fill); rect.setCoords(rect.topLeft().x()+1, rect.topLeft().y()+1, rect.topLeft().x()+rect.width()-2, rect.topLeft().y()+rect.height()-2); painter.drawRect(rect); }else if(m_type==KisVisualRectangleSelectorShape::borderMirrored){ painter.setPen(Qt::white); fill.setColor(Qt::white); painter.setBrush(fill); painter.drawEllipse(cursorPoint, cursorwidth, cursorwidth); QPoint mirror(innerRect.center().x()+(innerRect.center().x()-cursorPoint.x()),cursorPoint.y()); painter.drawEllipse(mirror, cursorwidth, cursorwidth); fill.setColor(col); painter.setPen(Qt::black); painter.setBrush(fill); painter.drawEllipse(cursorPoint, cursorwidth-1, cursorwidth-1); painter.drawEllipse(mirror, cursorwidth-1, cursorwidth-1); } else { painter.setPen(Qt::white); fill.setColor(Qt::white); painter.setBrush(fill); painter.drawEllipse(cursorPoint, cursorwidth, cursorwidth); fill.setColor(col); painter.setPen(Qt::black); painter.setBrush(fill); painter.drawEllipse(cursorPoint, cursorwidth-1.0, cursorwidth-1.0); } painter.end(); setFullImage(fullSelector); } diff --git a/libs/widgets/KisVisualRectangleSelectorShape.h b/libs/widgets/KisVisualRectangleSelectorShape.h index a0560fe810..dbf4ceff99 100644 --- a/libs/widgets/KisVisualRectangleSelectorShape.h +++ b/libs/widgets/KisVisualRectangleSelectorShape.h @@ -1,71 +1,60 @@ /* * Copyright (C) Wolthera van Hovell tot Westerflier , (C) 2016 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_VISUAL_RECTANGLE_SELECTOR_SHAPE_H #define KIS_VISUAL_RECTANGLE_SELECTOR_SHAPE_H -#include -#include -#include -#include -#include - -#include -#include -#include "KoColorDisplayRendererInterface.h" - -#include "KisColorSelectorConfiguration.h" #include "KisVisualColorSelectorShape.h" class KisVisualRectangleSelectorShape : public KisVisualColorSelectorShape { Q_OBJECT public: enum singelDTypes{vertical, horizontal, border, borderMirrored}; explicit KisVisualRectangleSelectorShape(QWidget *parent, Dimensions dimension, const KoColorSpace *cs, int channel1, int channel2, const KoColorDisplayRendererInterface *displayRenderer = KoDumbColorDisplayRenderer::instance(), int width=20, KisVisualRectangleSelectorShape::singelDTypes d = KisVisualRectangleSelectorShape::vertical ); ~KisVisualRectangleSelectorShape() override; void setBorderWidth(int width) override; /** * @brief getSpaceForSquare * @param geom the full widget rectangle * @return rectangle with enough space for second widget */ QRect getSpaceForSquare(QRect geom) override; QRect getSpaceForCircle(QRect geom) override; QRect getSpaceForTriangle(QRect geom) override; protected: QImage renderAlphaMask() const override; private: QPointF convertShapeCoordinateToWidgetCoordinate(QPointF coordinate) const override; QPointF convertWidgetCoordinateToShapeCoordinate(QPointF coordinate) const override; singelDTypes m_type; int m_barWidth; QRegion getMaskMap() override; void drawCursor() override; }; #endif diff --git a/libs/widgets/KisVisualTriangleSelectorShape.cpp b/libs/widgets/KisVisualTriangleSelectorShape.cpp index efd2e2ac0f..bb7a3f54c3 100644 --- a/libs/widgets/KisVisualTriangleSelectorShape.cpp +++ b/libs/widgets/KisVisualTriangleSelectorShape.cpp @@ -1,177 +1,162 @@ /* * Copyright (C) Wolthera van Hovell tot Westerflier , (C) 2016 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisVisualTriangleSelectorShape.h" #include -#include #include -#include #include -#include -#include -#include -#include #include -#include -#include - -#include "KoColorConversions.h" -#include "KoColorDisplayRendererInterface.h" -#include "KoChannelInfo.h" -#include -#include -#include "kis_signal_compressor.h" #include "kis_debug.h" #include "kis_global.h" KisVisualTriangleSelectorShape::KisVisualTriangleSelectorShape(QWidget *parent, Dimensions dimension, const KoColorSpace *cs, int channel1, int channel2, const KoColorDisplayRendererInterface *displayRenderer, int margin) : KisVisualColorSelectorShape(parent, dimension, cs, channel1, channel2, displayRenderer), m_margin(margin) { //qDebug() << "creating KisVisualTriangleSelectorShape" << this; } KisVisualTriangleSelectorShape::~KisVisualTriangleSelectorShape() { //qDebug() << "deleting KisVisualTriangleSelectorShape" << this; } void KisVisualTriangleSelectorShape::setBorderWidth(int /*width*/) { // triangle doesn't have a 1-dimensional mode } QRect KisVisualTriangleSelectorShape::getSpaceForSquare(QRect geom) { return geom; } QRect KisVisualTriangleSelectorShape::getSpaceForCircle(QRect geom) { return geom; } QRect KisVisualTriangleSelectorShape::getSpaceForTriangle(QRect geom) { return geom; } QPointF KisVisualTriangleSelectorShape::convertShapeCoordinateToWidgetCoordinate(QPointF coordinate) const { // margin serves to render the cursor, and triangle is rendered 1px larger than its active area qreal offset = m_margin + 1.0; qreal y = (coordinate.y() * (height() - 1 - 2 * offset)) + offset; qreal triWidth = width() - 1 - 2 * offset; qreal horizontalLineLength = coordinate.y() * triWidth; qreal horizontalLineStart = offset + 0.5 * (triWidth - horizontalLineLength); qreal x = coordinate.x() * horizontalLineLength + horizontalLineStart; return QPointF(x, y); } QPointF KisVisualTriangleSelectorShape::convertWidgetCoordinateToShapeCoordinate(QPointF coordinate) const { // margin serves to render the cursor, and triangle is rendered 1px larger than its active area qreal offset = m_margin + 1.0; qreal x = 0.5; qreal y = qBound(0.0, (coordinate.y() - offset)/(height() - 1 - 2 * offset), 1.0); if (y > 0) { qreal triWidth = width() - 1 - 2 * offset; qreal horizontalLineLength = y * triWidth; qreal horizontalLineStart = offset + 0.5 * (triWidth - horizontalLineLength); x = qBound(0.0, (coordinate.x() - horizontalLineStart) / horizontalLineLength, 1.0); } return QPointF(x, y); } QRegion KisVisualTriangleSelectorShape::getMaskMap() { const int cursorWidth = qMax(2 * m_margin, 2); QPolygon maskPoly; maskPoly << QPoint(qFloor(0.5 * (width() - cursorWidth)), 0) << QPoint(qCeil(0.5 * (width() + cursorWidth)), 0) << QPoint(width(), height() - cursorWidth) << QPoint(width(), height()) << QPoint(0, height()) << QPoint(0, height() - cursorWidth); return QRegion(maskPoly); } QImage KisVisualTriangleSelectorShape::renderAlphaMask() const { // Hi-DPI aware rendering requires that we determine the device pixel dimension; // actual widget size in device pixels is not accessible unfortunately, it might be 1px smaller... const int deviceWidth = qCeil(width() * devicePixelRatioF()); const int deviceHeight = qCeil(height() * devicePixelRatioF()); QImage alphaMask(deviceWidth, deviceHeight, QImage::Format_Alpha8); alphaMask.fill(0); alphaMask.setDevicePixelRatio(devicePixelRatioF()); QPainter painter(&alphaMask); painter.setRenderHint(QPainter::Antialiasing); painter.setBrush(Qt::white); painter.setPen(Qt::NoPen); QPointF triangle[3] = { QPointF(0.5 * width(), m_margin), QPointF(m_margin, height() - m_margin), QPointF(width() - m_margin, height() - m_margin), }; painter.drawConvexPolygon(triangle, 3); return alphaMask; } void KisVisualTriangleSelectorShape::drawCursor() { //qDebug() << this << "KisVisualTriangleSelectorShape::drawCursor: image needs update" << imagesNeedUpdate(); QPointF cursorPoint = convertShapeCoordinateToWidgetCoordinate(getCursorPosition()); QImage fullSelector = getImageMap(); QColor col = getColorFromConverter(getCurrentColor()); QPainter painter(&fullSelector); painter.setRenderHint(QPainter::Antialiasing); QBrush fill(Qt::SolidPattern); int cursorwidth = 5; painter.setPen(Qt::white); fill.setColor(Qt::white); painter.setBrush(fill); painter.drawEllipse(cursorPoint, cursorwidth, cursorwidth); fill.setColor(col); painter.setPen(Qt::black); painter.setBrush(fill); painter.drawEllipse(cursorPoint, cursorwidth-1.0, cursorwidth-1.0); painter.end(); setFullImage(fullSelector); } diff --git a/libs/widgets/KisVisualTriangleSelectorShape.h b/libs/widgets/KisVisualTriangleSelectorShape.h index b36951491a..26d751c908 100644 --- a/libs/widgets/KisVisualTriangleSelectorShape.h +++ b/libs/widgets/KisVisualTriangleSelectorShape.h @@ -1,71 +1,60 @@ /* * Copyright (C) Wolthera van Hovell tot Westerflier , (C) 2016 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_VISUAL_TRIANGLE_SELECTOR_SHAPE_H #define KIS_VISUAL_TRIANGLE_SELECTOR_SHAPE_H -#include -#include -#include -#include -#include - -#include -#include -#include "KoColorDisplayRendererInterface.h" - -#include "KisColorSelectorConfiguration.h" #include "KisVisualColorSelectorShape.h" class KisVisualTriangleSelectorShape : public KisVisualColorSelectorShape { Q_OBJECT public: explicit KisVisualTriangleSelectorShape(QWidget *parent, Dimensions dimension, const KoColorSpace *cs, int channel1, int channel2, const KoColorDisplayRendererInterface *displayRenderer = KoDumbColorDisplayRenderer::instance(), int margin = 5 ); ~KisVisualTriangleSelectorShape() override; void setBorderWidth(int /*width*/) override; /** * @brief getSpaceForSquare * @param geom the full widget rectangle * @return rectangle with enough space for second widget */ QRect getSpaceForSquare(QRect geom) override; QRect getSpaceForCircle(QRect geom) override; QRect getSpaceForTriangle(QRect geom) override; protected: QImage renderAlphaMask() const override; private: QPointF convertShapeCoordinateToWidgetCoordinate(QPointF coordinate) const override; QPointF convertWidgetCoordinateToShapeCoordinate(QPointF coordinate) const override; QRegion getMaskMap() override; void drawCursor() override; int m_margin { 5 }; }; #endif