Engauge Digitizer 2
Loading...
Searching...
No Matches
DigitizeStateColorPicker.cpp
Go to the documentation of this file.
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"
9#include "ColorFilter.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 <qmath.h>
22#include <QMessageBox>
23
28
32
37
39 DigitizeState previousState)
40{
41 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::begin";
42
43 setCursor(cmdMediator);
44 context().setDragMode(QGraphicsView::NoDrag);
45
46 // Save current state stuff so it can be restored afterwards
47 m_previousDigitizeState = previousState;
48 m_previousBackground = context().mainWindow().selectOriginal(BACKGROUND_IMAGE_ORIGINAL); // Only makes sense to have original image with all its colors
49
52}
53
54bool DigitizeStateColorPicker::canPaste (const Transformation & /* transformation */,
55 const QSize & /* viewSize */) const
56{
57 return false;
58}
59
60bool DigitizeStateColorPicker::computeFilterFromPixel (CmdMediator *cmdMediator,
61 const QPointF &posScreen,
62 const QString &curveName,
63 DocumentModelColorFilter &modelColorFilterAfter)
64{
65 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::computeFilterFromPixel";
66
67 bool rtn = false;
68
69 // Filter for background color now, and then later, once filter mode is set, processing of image
70 ColorFilter filter;
71 QImage image = cmdMediator->document().pixmap().toImage();
72 QRgb rgbBackground = filter.marginColor(&image);
73
74 // Adjust screen position so truncation gives round-up behavior
75 QPointF posScreenPlusHalf = posScreen - QPointF (0.5, 0.5);
76
77 QColor pixel;
78 rtn = findNearestNonBackgroundPixel (cmdMediator,
79 image,
80 posScreenPlusHalf,
81 rgbBackground,
82 pixel);
83 if (rtn) {
84
85 // The choice of which filter mode to use is determined, currently, by the selected pixel. This
86 // could be maybe made smarter by looking at other pixels, or even the entire image
87 int r = qRed (pixel.rgb());
88 int g = qGreen (pixel.rgb());
89 int b = qBlue (pixel.rgb());
90 if (r == g && g == b) {
91
92 // Pixel is gray scale, so we use intensity
93 modelColorFilterAfter.setColorFilterMode (curveName,
95
96 } else {
97
98 // Pixel is not gray scale, so we use hue
99 modelColorFilterAfter.setColorFilterMode (curveName,
101
102 }
103
104 // Generate histogram
105 double *histogramBins = new double [unsigned (ColorFilterHistogram::HISTOGRAM_BINS ())];
106
107 ColorFilterHistogram filterHistogram;
108 int maxBinCount;
109 filterHistogram.generate (filter,
110 histogramBins,
111 modelColorFilterAfter.colorFilterMode (curveName),
112 image,
113 maxBinCount);
114
115 // Bin for pixel
116 int pixelBin = filterHistogram.binFromPixel(filter,
117 modelColorFilterAfter.colorFilterMode (curveName),
118 pixel,
119 rgbBackground);
120
121 // Identify the entire width of the peak that the selected pixel belongs to. Go in both directions until the count
122 // hits zero or goes up
123 int lowerBin = pixelBin, upperBin = pixelBin;
124 while ((lowerBin > 0) &&
125 (histogramBins [lowerBin - 1] <= histogramBins [lowerBin]) &&
126 (histogramBins [lowerBin] > 0)) {
127 --lowerBin;
128 }
129 while ((upperBin < ColorFilterHistogram::HISTOGRAM_BINS () - 1) &&
130 (histogramBins [upperBin + 1] <= histogramBins [upperBin]) &&
131 (histogramBins [upperBin] > 0)) {
132 ++upperBin;
133 }
134
135 // Compute and save values from bin numbers
136 int lowerValue = filterHistogram.valueFromBin(filter,
137 modelColorFilterAfter.colorFilterMode (curveName),
138 lowerBin);
139 int upperValue = filterHistogram.valueFromBin(filter,
140 modelColorFilterAfter.colorFilterMode (curveName),
141 upperBin);
142
143 saveLowerValueUpperValue (modelColorFilterAfter,
144 curveName,
145 lowerValue,
146 upperValue);
147
148 delete [] histogramBins;
149
150 } else {
151
152 QMessageBox::warning (nullptr,
153 QObject::tr ("Color Picker"),
154 QObject::tr ("Sorry, but the color picker point must be near a non-background pixel. Please try again."));
155
156 }
157
158 return rtn;
159}
160
161QCursor DigitizeStateColorPicker::cursor(CmdMediator * /* cmdMediator */) const
162{
163 // Hot point is at the point of the eye dropper
164 const int HOT_X_IN_BITMAP = 8;
165 const int HOT_Y_IN_BITMAP = 24;
166 LOG4CPP_DEBUG_S ((*mainCat)) << "DigitizeStateColorPicker::cursor";
167
168 QBitmap bitmap (":/engauge/img/cursor_eyedropper.xpm");
169 QBitmap bitmapMask (":/engauge/img/cursor_eyedropper_mask.xpm");
170 return QCursor (bitmap,
171 bitmapMask,
172 HOT_X_IN_BITMAP,
173 HOT_Y_IN_BITMAP);
174}
175
177{
178 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::end";
179
180 // Restore original background. The state transition was triggered earlier by either the user selecting
181 // a valid point, or by user clicking on another digitize state button
182 context().mainWindow().selectOriginal(m_previousBackground);
183}
184
185bool DigitizeStateColorPicker::findNearestNonBackgroundPixel (CmdMediator *cmdMediator,
186 const QImage &image,
187 const QPointF &posScreenPlusHalf,
188 const QRgb &rgbBackground,
189 QColor &pixel)
190{
191 QPoint pos = posScreenPlusHalf.toPoint ();
192
193 int maxRadiusForSearch = cmdMediator->document().modelGeneral().cursorSize();
194
195 // Starting at pos, search in ever-widening squares for a non-background pixel
196 for (int radius = 0; radius < maxRadiusForSearch; radius++) {
197
198 for (int xOffset = -radius; xOffset <= radius; xOffset++) {
199 for (int yOffset = -radius; yOffset <= radius; yOffset++) {
200
201 // Top side
202 pixel = image.pixel (pos.x () + xOffset, pos.y () - radius);
203 if (pixel != rgbBackground) {
204 return true;
205 }
206
207 // Bottom side
208 pixel = image.pixel (pos.x () + xOffset, pos.y () + radius);
209 if (pixel != rgbBackground) {
210 return true;
211 }
212
213 // Left side
214 pixel = image.pixel (pos.x () - radius, pos.y () - yOffset);
215 if (pixel != rgbBackground) {
216 return true;
217 }
218
219 // Right side
220 pixel = image.pixel (pos.x () + radius, pos.y () + yOffset);
221 if (pixel != rgbBackground) {
222 return true;
223 }
224 }
225 }
226 }
227
228 return false;
229}
230
232{
233 return false;
234}
235
237 const QString &pointIdentifier)
238{
239 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::handleContextMenuEventAxis "
240 << " point=" << pointIdentifier.toLatin1 ().data ();
241}
242
244 const QStringList &pointIdentifiers)
245{
246 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker ::handleContextMenuEventGraph "
247 << "points=" << pointIdentifiers.join(",").toLatin1 ().data ();
248}
249
251{
252 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::handleCurveChange";
253}
254
256 Qt::Key key,
257 bool /* atLeastOneSelectedItem */)
258{
259 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::handleKeyPress"
260 << " key=" << QKeySequence (key).toString ().toLatin1 ().data ();
261}
262
264 QPointF /* posScreen */)
265{
266// LOG4CPP_DEBUG_S ((*mainCat)) << "DigitizeStateColorPicker::handleMouseMove";
267}
268
270 QPointF /* posScreen */)
271{
272 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::handleMousePress";
273}
274
276 QPointF posScreen)
277{
278 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::handleMouseRelease";
279
280 DocumentModelColorFilter modelColorFilterBefore = cmdMediator->document().modelColorFilter();
281 DocumentModelColorFilter modelColorFilterAfter = cmdMediator->document().modelColorFilter();
282 if (computeFilterFromPixel (cmdMediator,
283 posScreen,
284 context().mainWindow().selectedGraphCurve(),
285 modelColorFilterAfter)) {
286
287 // Trigger a state transition. The background restoration will be handled by the end method
288 context().requestDelayedStateTransition(m_previousDigitizeState);
289
290 // Create command to change segment filter
291 QUndoCommand *cmd = new CmdSettingsColorFilter (context ().mainWindow(),
292 cmdMediator->document (),
293 modelColorFilterBefore,
294 modelColorFilterAfter);
295 context().appendNewCmd(cmdMediator,
296 cmd);
297 }
298}
299
300void DigitizeStateColorPicker::saveLowerValueUpperValue (DocumentModelColorFilter &modelColorFilterAfter,
301 const QString &curveName,
302 double lowerValueIn,
303 double upperValueIn)
304{
305 int lowerValue = qFloor (lowerValueIn);
306 int upperValue = qFloor (upperValueIn);
307
308 switch (modelColorFilterAfter.colorFilterMode (curveName)) {
310 modelColorFilterAfter.setForegroundLow(curveName,
311 lowerValue);
312 modelColorFilterAfter.setForegroundHigh(curveName,
313 upperValue);
314 break;
315
317 modelColorFilterAfter.setHueLow(curveName,
318 lowerValue);
319 modelColorFilterAfter.setHueHigh(curveName,
320 upperValue);
321 break;
322
324 modelColorFilterAfter.setIntensityLow(curveName,
325 lowerValue);
326 modelColorFilterAfter.setIntensityHigh(curveName,
327 upperValue);
328 break;
329
331 modelColorFilterAfter.setSaturationLow(curveName,
332 lowerValue);
333 modelColorFilterAfter.setSaturationHigh(curveName,
334 upperValue);
335 break;
336
338 modelColorFilterAfter.setValueLow(curveName,
339 lowerValue);
340 modelColorFilterAfter.setValueHigh(curveName,
341 upperValue);
342 break;
343
344 default:
345 LOG4CPP_ERROR_S ((*mainCat)) << "DigitizeStateColorPicker::saveLowerValueUpperValue unexpected color filter mode "
346 << modelColorFilterAfter.colorFilterMode (curveName);
347 ENGAUGE_ASSERT (false);
348 }
349}
350
352{
353 return "DigitizeStateColorPicker";
354}
355
357{
358 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::updateAfterPointAddition";
359}
360
362 const DocumentModelDigitizeCurve & /*modelDigitizeCurve */)
363{
364 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::updateModelDigitizeCurve";
365}
366
368{
369 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::updateModelSegments";
370}
@ BACKGROUND_IMAGE_ORIGINAL
@ COLOR_FILTER_MODE_FOREGROUND
@ COLOR_FILTER_MODE_VALUE
@ COLOR_FILTER_MODE_INTENSITY
@ COLOR_FILTER_MODE_SATURATION
@ COLOR_FILTER_MODE_HUE
DigitizeState
Set of possible states of Digitize toolbar.
#define ENGAUGE_ASSERT(cond)
Drop in replacement for Q_ASSERT.
log4cpp::Category * mainCat
Definition Logger.cpp:14
Command queue stack.
Definition CmdMediator.h:24
Document & document()
Provide the Document to commands, primarily for undo/redo processing.
Command for DlgSettingsColorFilter.
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:21
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...
DigitizeStateAbstractBase(DigitizeStateContext &context)
Single constructor.
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 handleContextMenuEventAxis(CmdMediator *cmdMediator, const QString &pointIdentifier)
Handle a right click, on an axis point, that was intercepted earlier.
virtual void handleMousePress(CmdMediator *cmdMediator, QPointF posScreen)
Handle a mouse press that was intercepted earlier.
virtual bool guidelinesAreSelectable() const
Enable/disable guidelines according to state.
virtual void updateAfterPointAddition()
Update graphics attributes after possible new points. This is useful for highlight opacity.
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 void handleContextMenuEventGraph(CmdMediator *cmdMediator, const QStringList &pointIdentifiers)
Handle a right click, on a graph point, 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...
virtual bool canPaste(const Transformation &transformation, const QSize &viewSize) const
Return true if there is good data in the clipboard for pasting, and that is compatible with the curre...
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:843
DocumentModelGeneral modelGeneral() const
Get method for DocumentModelGeneral.
Definition Document.cpp:735
DocumentModelColorFilter modelColorFilter() const
Get method for DocumentModelColorFilter.
Definition Document.cpp:700
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...
void handleGuidelinesActiveChange(bool active)
Handle Guidelines active status toggle.
QString selectedGraphCurve() const
Curve name that is currently selected in m_cmbCurve.
Affine transformation between screen and graph coordinates, based on digitized axis points.
#define LOG4CPP_INFO_S(logger)
Definition convenience.h:18
#define LOG4CPP_DEBUG_S(logger)
Definition convenience.h:20
#define LOG4CPP_ERROR_S(logger)
Definition convenience.h:12