File indexing completed on 2024-06-16 04:16:27

0001 /*
0002  *  dlg_colorrange.cc - part of KimageShop^WKrayon^WKrita
0003  *
0004  *  SPDX-FileCopyrightText: 2004 Boudewijn Rempt <boud@valdyas.org>
0005  *
0006  *  SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include "dlg_colorrange.h"
0010 #include <QApplication>
0011 #include <QPushButton>
0012 #include <QCheckBox>
0013 #include <QSlider>
0014 #include <QComboBox>
0015 #include <QImage>
0016 #include <QLabel>
0017 #include <QColor>
0018 #include <QRadioButton>
0019 
0020 #include <klocalizedstring.h>
0021 #include <kis_debug.h>
0022 
0023 #include <KoColorConversions.h>
0024 #include <KoColorProfile.h>
0025 #include <KoColorSpace.h>
0026 #include <KoColor.h>
0027 #include <KoColorSpaceRegistry.h>
0028 #include <KoColorModelStandardIds.h>
0029 #include <KoColorSpaceTraits.h>
0030 
0031 #include <kis_layer.h>
0032 #include <kis_paint_device.h>
0033 #include <kis_selection.h>
0034 #include <kis_selection_manager.h>
0035 #include <kis_default_bounds.h>
0036 #include <KisImageResolutionProxy.h>
0037 #include <kis_types.h>
0038 #include <kis_undo_adapter.h>
0039 #include <KisViewManager.h>
0040 #include <kis_transaction.h>
0041 #include <kis_cursor.h>
0042 #include "kis_iterator_ng.h"
0043 #include "kis_selection_tool_helper.h"
0044 #include <kis_slider_spin_box.h>
0045 #include <KisCursorOverrideLock.h>
0046 
0047 DlgColorRange::DlgColorRange(KisViewManager *viewManager, QWidget *parent)
0048     : KoDialog(parent)
0049     , m_selectionCommandsAdded(0)
0050     , m_viewManager(viewManager)
0051 {
0052     setCaption(i18n("Color Range"));
0053     setButtons(Ok | Cancel);
0054     setDefaultButton(Ok);
0055 
0056     m_page = new WdgColorRange(this);
0057     Q_CHECK_PTR(m_page);
0058     m_page->setObjectName("color_range");
0059 
0060     setCaption(i18n("Color Range"));
0061     setMainWidget(m_page);
0062     resize(m_page->sizeHint());
0063 
0064     m_page->intFuzziness->setObjectName("fuzziness");
0065     m_page->intFuzziness->setRange(0, 200);
0066     m_page->intFuzziness->setSingleStep(10);
0067     m_page->intFuzziness->setValue(100);
0068 
0069     m_invert = false;
0070     m_mode = SELECTION_ADD;
0071     m_currentAction = REDS;
0072 
0073     connect(this, SIGNAL(okClicked()),
0074             this, SLOT(okClicked()));
0075 
0076     connect(this, SIGNAL(cancelClicked()),
0077             this, SLOT(cancelClicked()));
0078 
0079     connect(m_page->chkInvert, SIGNAL(clicked()),
0080             this, SLOT(slotInvertClicked()));
0081 
0082     connect(m_page->cmbSelect, SIGNAL(activated(int)),
0083             this, SLOT(slotSelectionTypeChanged(int)));
0084 
0085     connect(m_page->radioAdd, SIGNAL(toggled(bool)),
0086             this, SLOT(slotAdd(bool)));
0087 
0088     connect(m_page->radioSubtract, SIGNAL(toggled(bool)),
0089             this, SLOT(slotSubtract(bool)));
0090 
0091     connect(m_page->bnSelect, SIGNAL(clicked()),
0092             this, SLOT(slotSelectClicked()));
0093 
0094     connect(m_page->bnDeselect, SIGNAL(clicked()),
0095             this, SLOT(slotDeselectClicked()));
0096 
0097     m_page->bnDeselect->setEnabled(false);
0098 
0099 }
0100 
0101 DlgColorRange::~DlgColorRange()
0102 {
0103     delete m_page;
0104 }
0105 
0106 void DlgColorRange::okClicked()
0107 {
0108     accept();
0109 }
0110 
0111 void DlgColorRange::cancelClicked()
0112 {
0113     if (!m_viewManager) return;
0114     if (!m_viewManager->image()) return;
0115 
0116     for (int i = 0; i < m_selectionCommandsAdded; i++) {
0117         m_viewManager->undoAdapter()->undoLastCommand();
0118     }
0119     m_viewManager->canvas()->update();
0120     reject();
0121 }
0122 
0123 void DlgColorRange::slotInvertClicked()
0124 {
0125     m_invert = m_page->chkInvert->isChecked();
0126 }
0127 
0128 void DlgColorRange::slotSelectionTypeChanged(int index)
0129 {
0130     m_currentAction = (enumAction)index;
0131 }
0132 
0133 void DlgColorRange::slotSubtract(bool on)
0134 {
0135     if (on)
0136         m_mode = SELECTION_SUBTRACT;
0137 }
0138 
0139 void DlgColorRange::slotAdd(bool on)
0140 {
0141     if (on)
0142         m_mode = SELECTION_ADD;
0143 }
0144 
0145 void DlgColorRange::slotSelectClicked()
0146 {
0147     KisPaintDeviceSP device = m_viewManager->activeDevice();
0148     KIS_ASSERT_RECOVER_RETURN(device);
0149 
0150     QRect rc = m_viewManager->image()->bounds();
0151 
0152     if (rc.isEmpty()) return;
0153 
0154     KisCursorOverrideLock cursorLock(KisCursor::waitCursor());
0155 
0156     qint32 x, y, w, h;
0157     rc.getRect(&x, &y, &w, &h);
0158 
0159     const KoColorSpace *cs = m_viewManager->activeDevice()->colorSpace();
0160     const KoColorSpace *lab = KoColorSpaceRegistry::instance()->lab16();
0161 
0162     KoColor match;
0163     switch (m_currentAction) {
0164     case REDS:
0165         match = KoColor(QColor(Qt::red), cs);
0166         break;
0167     case YELLOWS:
0168         match = KoColor(QColor(Qt::yellow), cs);
0169         break;
0170     case GREENS:
0171         match = KoColor(QColor(Qt::green), cs);
0172         break;
0173     case CYANS:
0174         match = KoColor(QColor(Qt::cyan), cs);
0175         break;
0176     case BLUES:
0177         match = KoColor(QColor(Qt::blue), cs);
0178         break;
0179     case MAGENTAS:
0180         match = KoColor(QColor(Qt::magenta), cs);
0181         break;
0182     default:
0183         ;
0184     };
0185 
0186     int fuzziness = m_page->intFuzziness->value();
0187 
0188     KisSelectionSP selection = new KisSelection(new KisSelectionDefaultBounds(m_viewManager->activeDevice()),
0189                                                 toQShared(new KisImageResolutionProxy(m_viewManager->image())));
0190 
0191     KisHLineConstIteratorSP hiter = m_viewManager->activeDevice()->createHLineConstIteratorNG(x, y, w);
0192     KisHLineIteratorSP selIter = selection->pixelSelection()->createHLineIteratorNG(x, y, w);
0193 
0194     for (int row = y; row < h - y; ++row) {
0195         do {
0196             // Don't try to select transparent pixels.
0197             if (cs->opacityU8(hiter->oldRawData()) >  OPACITY_TRANSPARENT_U8) {
0198 
0199                 bool selected = false;
0200 
0201                 KoColor c(hiter->oldRawData(), cs);
0202                 if (m_currentAction > MAGENTAS) {
0203                     c.convertTo(lab);
0204                     quint8 L = lab->scaleToU8(c.data(), 0);
0205 
0206                     switch (m_currentAction) {
0207                     case HIGHLIGHTS:
0208                         selected = (L > MAX_SELECTED - fuzziness);
0209                         break;
0210                     case MIDTONES:
0211                         selected = (L > MAX_SELECTED / 2 - fuzziness && L < MAX_SELECTED / 2 + fuzziness);
0212                         break;
0213                     case SHADOWS:
0214                         selected = (L < MIN_SELECTED + fuzziness);
0215                         break;
0216                     default:
0217                         ;
0218                     }
0219                 }
0220                 else {
0221                     quint8 difference = cs->difference(match.data(), c.data());
0222                     selected = (difference <= fuzziness);
0223                 }
0224 
0225                 if (selected) {
0226                     if (!m_invert) {
0227                         if (m_mode == SELECTION_ADD) {
0228                             *(selIter->rawData()) =  MAX_SELECTED;
0229                         } else if (m_mode == SELECTION_SUBTRACT) {
0230                             *(selIter->rawData()) = MIN_SELECTED;
0231                         }
0232                     } else {
0233                         if (m_mode == SELECTION_ADD) {
0234                             *(selIter->rawData()) = MIN_SELECTED;
0235                         } else if (m_mode == SELECTION_SUBTRACT) {
0236                             *(selIter->rawData()) =  MAX_SELECTED;
0237                         }
0238                     }
0239                 }
0240             }
0241         } while (hiter->nextPixel() && selIter->nextPixel());
0242         hiter->nextRow();
0243         selIter->nextRow();
0244     }
0245 
0246     selection->pixelSelection()->invalidateOutlineCache();
0247     KisSelectionToolHelper helper(m_viewManager->canvasBase(), kundo2_i18n("Color Range Selection"));
0248     helper.selectPixelSelection(selection->pixelSelection(), m_mode);
0249 
0250     m_page->bnDeselect->setEnabled(true);
0251     m_selectionCommandsAdded++;
0252 }
0253 
0254 void DlgColorRange::slotDeselectClicked()
0255 {
0256     if (!m_viewManager) return;
0257 
0258 
0259     m_viewManager->undoAdapter()->undoLastCommand();
0260     m_selectionCommandsAdded--;
0261     if (!m_selectionCommandsAdded) {
0262         m_page->bnDeselect->setEnabled(false);
0263     }
0264 }
0265 
0266