Engauge Digitizer 2
Loading...
Searching...
No Matches
DigitizeStateColorPicker.cpp
1/******************************************************************************************************
2 * (C) 2014 markummitchell@github.com. This file is part of Engauge Digitizer, which is released *
3 * under GNU General Public License version 2 (GPLv2) or (at your option) any later version. See file *
4 * LICENSE or go to gnu.org/licenses for details. Distribution requires prior written permission. *
5 ******************************************************************************************************/
6
7#include "CmdMediator.h"
8#include "CmdSettingsColorFilter.h"
9#include "ColorFilter.h"
10#include "ColorFilterHistogram.h"
11#include "DigitizeStateContext.h"
12#include "DigitizeStateColorPicker.h"
13#include "DocumentModelColorFilter.h"
14#include "EngaugeAssert.h"
15#include "Logger.h"
16#include "MainWindow.h"
17#include <QBitmap>
18#include <QGraphicsPixmapItem>
19#include <QGraphicsScene>
20#include <QImage>
21#include <QMessageBox>
22
27
28DigitizeStateColorPicker::~DigitizeStateColorPicker ()
29{
30}
31
36
38 DigitizeState previousState)
39{
40 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::begin";
41
42 setCursor(cmdMediator);
43 context().setDragMode(QGraphicsView::NoDrag);
44
45 // Save current state stuff so it can be restored afterwards
46 m_previousDigitizeState = previousState;
47 m_previousBackground = context().mainWindow().selectOriginal(BACKGROUND_IMAGE_ORIGINAL); // Only makes sense to have original image with all its colors
48
50}
51
52bool DigitizeStateColorPicker::computeFilterFromPixel (CmdMediator *cmdMediator,
53 const QPointF &posScreen,
54 const QString &curveName,
55 DocumentModelColorFilter &modelColorFilterAfter)
56{
57 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::computeFilterFromPixel";
58
59 bool rtn = false;
60
61 // Filter for background color now, and then later, once filter mode is set, processing of image
62 ColorFilter filter;
63 QImage image = cmdMediator->document().pixmap().toImage();
64 QRgb rgbBackground = filter.marginColor(&image);
65
66 // Adjust screen position so truncation gives round-up behavior
67 QPointF posScreenPlusHalf = posScreen - QPointF (0.5, 0.5);
68
69 QColor pixel;
70 rtn = findNearestNonBackgroundPixel (cmdMediator,
71 image,
72 posScreenPlusHalf,
73 rgbBackground,
74 pixel);
75 if (rtn) {
76
77 // The choice of which filter mode to use is determined, currently, by the selected pixel. This
78 // could be maybe made smarter by looking at other pixels, or even the entire image
79 int r = qRed (pixel.rgb());
80 int g = qGreen (pixel.rgb());
81 int b = qBlue (pixel.rgb());
82 if (r == g && g == b) {
83
84 // Pixel is gray scale, so we use intensity
85 modelColorFilterAfter.setColorFilterMode (curveName,
86 COLOR_FILTER_MODE_INTENSITY);
87
88 } else {
89
90 // Pixel is not gray scale, so we use hue
91 modelColorFilterAfter.setColorFilterMode (curveName,
92 COLOR_FILTER_MODE_HUE);
93
94 }
95
96 // Generate histogram
97 double *histogramBins = new double [ColorFilterHistogram::HISTOGRAM_BINS ()];
98
99 ColorFilterHistogram filterHistogram;
100 int maxBinCount;
101 filterHistogram.generate (filter,
102 histogramBins,
103 modelColorFilterAfter.colorFilterMode (curveName),
104 image,
105 maxBinCount);
106
107 // Bin for pixel
108 int pixelBin = filterHistogram.binFromPixel(filter,
109 modelColorFilterAfter.colorFilterMode (curveName),
110 pixel,
111 rgbBackground);
112
113 // Identify the entire width of the peak that the selected pixel belongs to. Go in both directions until the count
114 // hits zero or goes up
115 int lowerBin = pixelBin, upperBin = pixelBin;
116 while ((lowerBin > 0) &&
117 (histogramBins [lowerBin - 1] <= histogramBins [lowerBin]) &&
118 (histogramBins [lowerBin] > 0)) {
119 --lowerBin;
120 }
121 while ((upperBin < ColorFilterHistogram::HISTOGRAM_BINS () - 1) &&
122 (histogramBins [upperBin + 1] <= histogramBins [upperBin]) &&
123 (histogramBins [upperBin] > 0)) {
124 ++upperBin;
125 }
126
127 // Compute and save values from bin numbers
128 int lowerValue = filterHistogram.valueFromBin(filter,
129 modelColorFilterAfter.colorFilterMode (curveName),
130 lowerBin);
131 int upperValue = filterHistogram.valueFromBin(filter,
132 modelColorFilterAfter.colorFilterMode (curveName),
133 upperBin);
134
135 saveLowerValueUpperValue (modelColorFilterAfter,
136 curveName,
137 lowerValue,
138 upperValue);
139
140 delete histogramBins;
141
142 } else {
143
144 QMessageBox::warning (0,
145 QObject::tr ("Color Picker"),
146 QObject::tr ("Sorry, but the color picker point must be near a non-background pixel. Please try again."));
147
148 }
149
150 return rtn;
151}
152
153QCursor DigitizeStateColorPicker::cursor(CmdMediator * /* cmdMediator */) const
154{
155 // Hot point is at the point of the eye dropper
156 const int HOT_X_IN_BITMAP = 8;
157 const int HOT_Y_IN_BITMAP = 24;
158 LOG4CPP_DEBUG_S ((*mainCat)) << "DigitizeStateColorPicker::cursor";
159
160 QBitmap bitmap (":/engauge/img/cursor_eyedropper.xpm");
161 QBitmap bitmapMask (":/engauge/img/cursor_eyedropper_mask.xpm");
162 return QCursor (bitmap,
163 bitmapMask,
164 HOT_X_IN_BITMAP,
165 HOT_Y_IN_BITMAP);
166}
167
169{
170 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::end";
171
172 // Restore original background. The state transition was triggered earlier by either the user selecting
173 // a valid point, or by user clicking on another digitize state button
174 context().mainWindow().selectOriginal(m_previousBackground);
175}
176
177bool DigitizeStateColorPicker::findNearestNonBackgroundPixel (CmdMediator *cmdMediator,
178 const QImage &image,
179 const QPointF &posScreenPlusHalf,
180 const QRgb &rgbBackground,
181 QColor &pixel)
182{
183 QPoint pos = posScreenPlusHalf.toPoint ();
184
185 int maxRadiusForSearch = cmdMediator->document().modelGeneral().cursorSize();
186
187 // Starting at pos, search in ever-widening squares for a non-background pixel
188 for (int radius = 0; radius < maxRadiusForSearch; radius++) {
189
190 for (int xOffset = -radius; xOffset <= radius; xOffset++) {
191 for (int yOffset = -radius; yOffset <= radius; yOffset++) {
192
193 // Top side
194 pixel = image.pixel (pos.x () + xOffset, pos.y () - radius);
195 if (pixel != rgbBackground) {
196 return true;
197 }
198
199 // Bottom side
200 pixel = image.pixel (pos.x () + xOffset, pos.y () + radius);
201 if (pixel != rgbBackground) {
202 return true;
203 }
204
205 // Left side
206 pixel = image.pixel (pos.x () - radius, pos.y () - yOffset);
207 if (pixel != rgbBackground) {
208 return true;
209 }
210
211 // Right side
212 pixel = image.pixel (pos.x () + radius, pos.y () + yOffset);
213 if (pixel != rgbBackground) {
214 return true;
215 }
216 }
217 }
218 }
219
220 return false;
221}
222
224{
225 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::handleCurveChange";
226}
227
229 Qt::Key key,
230 bool /* atLeastOneSelectedItem */)
231{
232 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::handleKeyPress"
233 << " key=" << QKeySequence (key).toString ().toLatin1 ().data ();
234}
235
237 QPointF /* posScreen */)
238{
239// LOG4CPP_DEBUG_S ((*mainCat)) << "DigitizeStateColorPicker::handleMouseMove";
240}
241
243 QPointF /* posScreen */)
244{
245 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::handleMousePress";
246}
247
249 QPointF posScreen)
250{
251 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::handleMouseRelease";
252
253 DocumentModelColorFilter modelColorFilterBefore = cmdMediator->document().modelColorFilter();
254 DocumentModelColorFilter modelColorFilterAfter = cmdMediator->document().modelColorFilter();
255 if (computeFilterFromPixel (cmdMediator,
256 posScreen,
257 context().mainWindow().selectedGraphCurve(),
258 modelColorFilterAfter)) {
259
260 // Trigger a state transition. The background restoration will be handled by the end method
261 context().requestDelayedStateTransition(m_previousDigitizeState);
262
263 // Create command to change segment filter
264 QUndoCommand *cmd = new CmdSettingsColorFilter (context ().mainWindow(),
265 cmdMediator->document (),
266 modelColorFilterBefore,
267 modelColorFilterAfter);
268 context().appendNewCmd(cmdMediator,
269 cmd);
270 }
271}
272
273void DigitizeStateColorPicker::saveLowerValueUpperValue (DocumentModelColorFilter &modelColorFilterAfter,
274 const QString &curveName,
275 double lowerValue,
276 double upperValue)
277{
278 switch (modelColorFilterAfter.colorFilterMode (curveName)) {
279 case COLOR_FILTER_MODE_FOREGROUND:
280 modelColorFilterAfter.setForegroundLow(curveName,
281 lowerValue);
282 modelColorFilterAfter.setForegroundHigh(curveName,
283 upperValue);
284 break;
285
286 case COLOR_FILTER_MODE_HUE:
287 modelColorFilterAfter.setHueLow(curveName,
288 lowerValue);
289 modelColorFilterAfter.setHueHigh(curveName,
290 upperValue);
291 break;
292
293 case COLOR_FILTER_MODE_INTENSITY:
294 modelColorFilterAfter.setIntensityLow(curveName,
295 lowerValue);
296 modelColorFilterAfter.setIntensityHigh(curveName,
297 upperValue);
298 break;
299
300 case COLOR_FILTER_MODE_SATURATION:
301 modelColorFilterAfter.setSaturationLow(curveName,
302 lowerValue);
303 modelColorFilterAfter.setSaturationHigh(curveName,
304 upperValue);
305 break;
306
307 case COLOR_FILTER_MODE_VALUE:
308 modelColorFilterAfter.setValueLow(curveName,
309 lowerValue);
310 modelColorFilterAfter.setValueHigh(curveName,
311 upperValue);
312 break;
313
314 default:
315 ENGAUGE_ASSERT (false);
316 }
317}
318
320{
321 return "DigitizeStateColorPicker";
322}
323
325 const DocumentModelDigitizeCurve & /*modelDigitizeCurve */)
326{
327 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::updateModelDigitizeCurve";
328}
329
331{
332 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::updateModelSegments";
333}
Command queue stack.
Definition CmdMediator.h:24
Document & document()
Provide the Document to commands, primarily for undo/redo processing.
Command for DlgSettingsColorFilter.
Class that generates a histogram according to the current filter.
void generate(const ColorFilter &filter, double histogramBins[], ColorFilterMode colorFilterMode, const QImage &image, int &maxBinCount) const
Generate the histogram.
int valueFromBin(const ColorFilter &filter, ColorFilterMode colorFilterMode, int bin)
Inverse of binFromPixel.
static int HISTOGRAM_BINS()
Number of histogram bins.
int binFromPixel(const ColorFilter &filter, ColorFilterMode colorFilterMode, const QColor &pixel, const QRgb &rgbBackground) const
Compute histogram bin number from pixel according to filter.
Class for filtering image to remove unimportant information.
Definition ColorFilter.h:19
QRgb marginColor(const QImage *image) const
Identify the margin color of the image, which is defined as the most common color in the four margins...
Base class for all digitizing states. This serves as an interface to DigitizeStateContext.
DigitizeStateContext & context()
Reference to the DigitizeStateContext that contains all the DigitizeStateAbstractBase subclasses,...
void setCursor(CmdMediator *cmdMediator)
Update the cursor according to the current state.
virtual QCursor cursor(CmdMediator *cmdMediator) const
Returns the state-specific cursor shape.
virtual void handleMousePress(CmdMediator *cmdMediator, QPointF posScreen)
Handle a mouse press that was intercepted earlier.
virtual void handleKeyPress(CmdMediator *cmdMediator, Qt::Key key, bool atLeastOneSelectedItem)
Handle a key press that was intercepted earlier.
virtual void updateModelDigitizeCurve(CmdMediator *cmdMediator, const DocumentModelDigitizeCurve &modelDigitizeCurve)
Update the digitize curve settings.
virtual void end()
Method that is called at the exact moment a state is exited. Typically called just before begin for t...
virtual QString activeCurve() const
Name of the active Curve. This can include AXIS_CURVE_NAME.
virtual void updateModelSegments(const DocumentModelSegments &modelSegments)
Update the segments given the new settings.
virtual void begin(CmdMediator *cmdMediator, DigitizeState previousState)
Method that is called at the exact moment a state is entered.
virtual void handleMouseRelease(CmdMediator *cmdMediator, QPointF posScreen)
Handle a mouse release that was intercepted earlier.
virtual QString state() const
State name for debugging.
virtual void handleMouseMove(CmdMediator *cmdMediator, QPointF posScreen)
Handle a mouse move. This is part of an experiment to see if augmenting the cursor in Point Match mod...
DigitizeStateColorPicker(DigitizeStateContext &context)
Single constructor.
virtual void handleCurveChange(CmdMediator *cmdMediator)
Handle the selection of a new curve. At a minimum, DigitizeStateSegment will generate a new set of Se...
Container for all DigitizeStateAbstractBase subclasses. This functions as the context class in a stan...
void setDragMode(QGraphicsView::DragMode dragMode)
Set QGraphicsView drag mode (in m_view). Called from DigitizeStateAbstractBase subclasses.
void appendNewCmd(CmdMediator *cmdMediator, QUndoCommand *cmd)
Append just-created QUndoCommand to command stack. This is called from DigitizeStateAbstractBase subc...
void requestDelayedStateTransition(DigitizeState digitizeState)
Initiate state transition to be performed later, when DigitizeState is off the stack.
MainWindow & mainWindow()
Reference to the MainWindow, without const.
Model for DlgSettingsColorFilter and CmdSettingsColorFilter.
void setValueHigh(const QString &curveName, int valueHigh)
Set method for value high.
ColorFilterMode colorFilterMode(const QString &curveName) const
Get method for filter mode.
void setIntensityLow(const QString &curveName, int intensityLow)
Set method for intensity lower bound.
void setHueHigh(const QString &curveName, int hueHigh)
Set method for hue higher bound.
void setForegroundHigh(const QString &curveName, int foregroundHigh)
Set method for foreground higher bound.
void setSaturationHigh(const QString &curveName, int saturationHigh)
Set method for saturation high.
void setHueLow(const QString &curveName, int hueLow)
Set method for hue lower bound.
void setValueLow(const QString &curveName, int valueLow)
Set method for value low.
void setSaturationLow(const QString &curveName, int saturationLow)
Set method for saturation low.
void setColorFilterMode(const QString &curveName, ColorFilterMode colorFilterMode)
Set method for filter mode.
void setIntensityHigh(const QString &curveName, int intensityHigh)
Set method for intensity higher bound.
void setForegroundLow(const QString &curveName, int foregroundLow)
Set method for foreground lower bound.
Model for DlgSettingsDigitizeCurve and CmdSettingsDigitizeCurve.
int cursorSize() const
Get method for effective cursor size.
Model for DlgSettingsSegments and CmdSettingsSegments.
QPixmap pixmap() const
Return the image that is being digitized.
Definition Document.cpp:681
DocumentModelGeneral modelGeneral() const
Get method for DocumentModelGeneral.
Definition Document.cpp:639
DocumentModelColorFilter modelColorFilter() const
Get method for DocumentModelColorFilter.
Definition Document.cpp:604
BackgroundImage selectOriginal(BackgroundImage backgroundImage)
Make original background visible, for DigitizeStateColorPicker.
void updateViewsOfSettings(const QString &activeCurve)
Update curve-specific view of settings. Private version gets active curve name from DigitizeStateCont...
QString selectedGraphCurve() const
Curve name that is currently selected in m_cmbCurve.