1 /*
2  *  dlg_colorrange.cc - part of KimageShop^WKrayon^WKrita
3  *
4  *  Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  */
20 
21 #include "dlg_colorrange.h"
22 #include <QApplication>
23 #include <QPushButton>
24 #include <QCheckBox>
25 #include <QSlider>
26 #include <QComboBox>
27 #include <QImage>
28 #include <QLabel>
29 #include <QColor>
30 #include <QRadioButton>
31 
32 #include <klocalizedstring.h>
33 #include <kis_debug.h>
34 
35 #include <KoColorConversions.h>
36 #include <KoColorProfile.h>
37 #include <KoColorSpace.h>
38 #include <KoColor.h>
39 #include <KoColorSpaceRegistry.h>
40 #include <KoColorModelStandardIds.h>
41 #include <KoColorSpaceTraits.h>
42 
43 #include <kis_layer.h>
44 #include <kis_paint_device.h>
45 #include <kis_selection.h>
46 #include <kis_selection_manager.h>
47 #include <kis_types.h>
48 #include <kis_undo_adapter.h>
49 #include <KisViewManager.h>
50 #include <kis_transaction.h>
51 #include <kis_cursor.h>
52 #include "kis_iterator_ng.h"
53 #include "kis_selection_tool_helper.h"
54 #include <kis_slider_spin_box.h>
55 
DlgColorRange(KisViewManager * viewManager,QWidget * parent)56 DlgColorRange::DlgColorRange(KisViewManager *viewManager, QWidget *parent)
57     : KoDialog(parent)
58     , m_selectionCommandsAdded(0)
59     , m_viewManager(viewManager)
60 {
61     setCaption(i18n("Color Range"));
62     setButtons(Ok | Cancel);
63     setDefaultButton(Ok);
64 
65     m_page = new WdgColorRange(this);
66     Q_CHECK_PTR(m_page);
67     m_page->setObjectName("color_range");
68 
69     setCaption(i18n("Color Range"));
70     setMainWidget(m_page);
71     resize(m_page->sizeHint());
72 
73     m_page->intFuzziness->setObjectName("fuzziness");
74     m_page->intFuzziness->setRange(0, 200);
75     m_page->intFuzziness->setSingleStep(10);
76     m_page->intFuzziness->setValue(100);
77 
78     m_invert = false;
79     m_mode = SELECTION_ADD;
80     m_currentAction = REDS;
81 
82     connect(this, SIGNAL(okClicked()),
83             this, SLOT(okClicked()));
84 
85     connect(this, SIGNAL(cancelClicked()),
86             this, SLOT(cancelClicked()));
87 
88     connect(m_page->chkInvert, SIGNAL(clicked()),
89             this, SLOT(slotInvertClicked()));
90 
91     connect(m_page->cmbSelect, SIGNAL(activated(int)),
92             this, SLOT(slotSelectionTypeChanged(int)));
93 
94     connect(m_page->radioAdd, SIGNAL(toggled(bool)),
95             this, SLOT(slotAdd(bool)));
96 
97     connect(m_page->radioSubtract, SIGNAL(toggled(bool)),
98             this, SLOT(slotSubtract(bool)));
99 
100     connect(m_page->bnSelect, SIGNAL(clicked()),
101             this, SLOT(slotSelectClicked()));
102 
103     connect(m_page->bnDeselect, SIGNAL(clicked()),
104             this, SLOT(slotDeselectClicked()));
105 
106     m_page->bnDeselect->setEnabled(false);
107 
108 }
109 
~DlgColorRange()110 DlgColorRange::~DlgColorRange()
111 {
112     delete m_page;
113 }
114 
okClicked()115 void DlgColorRange::okClicked()
116 {
117     accept();
118 }
119 
cancelClicked()120 void DlgColorRange::cancelClicked()
121 {
122     if (!m_viewManager) return;
123     if (!m_viewManager->image()) return;
124 
125     for (int i = 0; i < m_selectionCommandsAdded; i++) {
126         m_viewManager->undoAdapter()->undoLastCommand();
127     }
128     m_viewManager->canvas()->update();
129     reject();
130 }
131 
slotInvertClicked()132 void DlgColorRange::slotInvertClicked()
133 {
134     m_invert = m_page->chkInvert->isChecked();
135 }
136 
slotSelectionTypeChanged(int index)137 void DlgColorRange::slotSelectionTypeChanged(int index)
138 {
139     m_currentAction = (enumAction)index;
140 }
141 
slotSubtract(bool on)142 void DlgColorRange::slotSubtract(bool on)
143 {
144     if (on)
145         m_mode = SELECTION_SUBTRACT;
146 }
147 
slotAdd(bool on)148 void DlgColorRange::slotAdd(bool on)
149 {
150     if (on)
151         m_mode = SELECTION_ADD;
152 }
153 
slotSelectClicked()154 void DlgColorRange::slotSelectClicked()
155 {
156     KisPaintDeviceSP device = m_viewManager->activeDevice();
157     KIS_ASSERT_RECOVER_RETURN(device);
158 
159     QRect rc = m_viewManager->image()->bounds();
160 
161     if (rc.isEmpty()) return;
162 
163     QApplication::setOverrideCursor(KisCursor::waitCursor());
164 
165     qint32 x, y, w, h;
166     rc.getRect(&x, &y, &w, &h);
167 
168     const KoColorSpace *cs = m_viewManager->activeDevice()->colorSpace();
169     const KoColorSpace *lab = KoColorSpaceRegistry::instance()->lab16();
170 
171     KoColor match;
172     switch (m_currentAction) {
173     case REDS:
174         match = KoColor(QColor(Qt::red), cs);
175         break;
176     case YELLOWS:
177         match = KoColor(QColor(Qt::yellow), cs);
178         break;
179     case GREENS:
180         match = KoColor(QColor(Qt::green), cs);
181         break;
182     case CYANS:
183         match = KoColor(QColor(Qt::cyan), cs);
184         break;
185     case BLUES:
186         match = KoColor(QColor(Qt::blue), cs);
187         break;
188     case MAGENTAS:
189         match = KoColor(QColor(Qt::magenta), cs);
190         break;
191     default:
192         ;
193     };
194 
195     int fuzziness = m_page->intFuzziness->value();
196 
197     KisSelectionSP selection = new KisSelection(new KisSelectionDefaultBounds(m_viewManager->activeDevice()));
198 
199     KisHLineConstIteratorSP hiter = m_viewManager->activeDevice()->createHLineConstIteratorNG(x, y, w);
200     KisHLineIteratorSP selIter = selection->pixelSelection()->createHLineIteratorNG(x, y, w);
201 
202     for (int row = y; row < h - y; ++row) {
203         do {
204             // Don't try to select transparent pixels.
205             if (cs->opacityU8(hiter->oldRawData()) >  OPACITY_TRANSPARENT_U8) {
206 
207                 bool selected = false;
208 
209                 KoColor c(hiter->oldRawData(), cs);
210                 if (m_currentAction > MAGENTAS) {
211                     c.convertTo(lab);
212                     quint8 L = lab->scaleToU8(c.data(), 0);
213 
214                     switch (m_currentAction) {
215                     case HIGHLIGHTS:
216                         selected = (L > MAX_SELECTED - fuzziness);
217                         break;
218                     case MIDTONES:
219                         selected = (L > MAX_SELECTED / 2 - fuzziness && L < MAX_SELECTED / 2 + fuzziness);
220                         break;
221                     case SHADOWS:
222                         selected = (L < MIN_SELECTED + fuzziness);
223                         break;
224                     default:
225                         ;
226                     }
227                 }
228                 else {
229                     quint8 difference = cs->difference(match.data(), c.data());
230                     selected = (difference <= fuzziness);
231                 }
232 
233                 if (selected) {
234                     if (!m_invert) {
235                         if (m_mode == SELECTION_ADD) {
236                             *(selIter->rawData()) =  MAX_SELECTED;
237                         } else if (m_mode == SELECTION_SUBTRACT) {
238                             *(selIter->rawData()) = MIN_SELECTED;
239                         }
240                     } else {
241                         if (m_mode == SELECTION_ADD) {
242                             *(selIter->rawData()) = MIN_SELECTED;
243                         } else if (m_mode == SELECTION_SUBTRACT) {
244                             *(selIter->rawData()) =  MAX_SELECTED;
245                         }
246                     }
247                 }
248             }
249         } while (hiter->nextPixel() && selIter->nextPixel());
250         hiter->nextRow();
251         selIter->nextRow();
252     }
253 
254     selection->pixelSelection()->invalidateOutlineCache();
255     KisSelectionToolHelper helper(m_viewManager->canvasBase(), kundo2_i18n("Color Range Selection"));
256     helper.selectPixelSelection(selection->pixelSelection(), m_mode);
257 
258     m_page->bnDeselect->setEnabled(true);
259     m_selectionCommandsAdded++;
260     QApplication::restoreOverrideCursor();
261 }
262 
slotDeselectClicked()263 void DlgColorRange::slotDeselectClicked()
264 {
265     if (!m_viewManager) return;
266 
267 
268     m_viewManager->undoAdapter()->undoLastCommand();
269     m_selectionCommandsAdded--;
270     if (!m_selectionCommandsAdded) {
271         m_page->bnDeselect->setEnabled(false);
272     }
273 }
274 
275 
276