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
51}
52
53bool DigitizeStateColorPicker::canPaste (const Transformation & /* transformation */,
54 const QSize & /* viewSize */) const
55{
56 return false;
57}
58
59bool DigitizeStateColorPicker::computeFilterFromPixel (CmdMediator *cmdMediator,
60 const QPointF &posScreen,
61 const QString &curveName,
62 DocumentModelColorFilter &modelColorFilterAfter)
63{
64 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::computeFilterFromPixel";
65
66 bool rtn = false;
67
68 // Filter for background color now, and then later, once filter mode is set, processing of image
69 ColorFilter filter;
70 QImage image = cmdMediator->document().pixmap().toImage();
71 QRgb rgbBackground = filter.marginColor(&image);
72
73 // Adjust screen position so truncation gives round-up behavior
74 QPointF posScreenPlusHalf = posScreen - QPointF (0.5, 0.5);
75
76 QColor pixel;
77 rtn = findNearestNonBackgroundPixel (cmdMediator,
78 image,
79 posScreenPlusHalf,
80 rgbBackground,
81 pixel);
82 if (rtn) {
83
84 // The choice of which filter mode to use is determined, currently, by the selected pixel. This
85 // could be maybe made smarter by looking at other pixels, or even the entire image
86 int r = qRed (pixel.rgb());
87 int g = qGreen (pixel.rgb());
88 int b = qBlue (pixel.rgb());
89 if (r == g && g == b) {
90
91 // Pixel is gray scale, so we use intensity
92 modelColorFilterAfter.setColorFilterMode (curveName,
94
95 } else {
96
97 // Pixel is not gray scale, so we use hue
98 modelColorFilterAfter.setColorFilterMode (curveName,
100
101 }
102
103 // Generate histogram
104 double *histogramBins = new double [unsigned (ColorFilterHistogram::HISTOGRAM_BINS ())];
105
106 ColorFilterHistogram filterHistogram;
107 int maxBinCount;
108 filterHistogram.generate (filter,
109 histogramBins,
110 modelColorFilterAfter.colorFilterMode (curveName),
111 image,
112 maxBinCount);
113
114 // Bin for pixel
115 int pixelBin = filterHistogram.binFromPixel(filter,
116 modelColorFilterAfter.colorFilterMode (curveName),
117 pixel,
118 rgbBackground);
119
120 // Identify the entire width of the peak that the selected pixel belongs to. Go in both directions until the count
121 // hits zero or goes up
122 int lowerBin = pixelBin, upperBin = pixelBin;
123 while ((lowerBin > 0) &&
124 (histogramBins [lowerBin - 1] <= histogramBins [lowerBin]) &&
125 (histogramBins [lowerBin] > 0)) {
126 --lowerBin;
127 }
128 while ((upperBin < ColorFilterHistogram::HISTOGRAM_BINS () - 1) &&
129 (histogramBins [upperBin + 1] <= histogramBins [upperBin]) &&
130 (histogramBins [upperBin] > 0)) {
131 ++upperBin;
132 }
133
134 // Compute and save values from bin numbers
135 int lowerValue = filterHistogram.valueFromBin(filter,
136 modelColorFilterAfter.colorFilterMode (curveName),
137 lowerBin);
138 int upperValue = filterHistogram.valueFromBin(filter,
139 modelColorFilterAfter.colorFilterMode (curveName),
140 upperBin);
141
142 saveLowerValueUpperValue (modelColorFilterAfter,
143 curveName,
144 lowerValue,
145 upperValue);
146
147 delete [] histogramBins;
148
149 } else {
150
151 QMessageBox::warning (nullptr,
152 QObject::tr ("Color Picker"),
153 QObject::tr ("Sorry, but the color picker point must be near a non-background pixel. Please try again."));
154
155 }
156
157 return rtn;
158}
159
160QCursor DigitizeStateColorPicker::cursor(CmdMediator * /* cmdMediator */) const
161{
162 // Hot point is at the point of the eye dropper
163 const int HOT_X_IN_BITMAP = 8;
164 const int HOT_Y_IN_BITMAP = 24;
165 LOG4CPP_DEBUG_S ((*mainCat)) << "DigitizeStateColorPicker::cursor";
166
167 QBitmap bitmap (":/engauge/img/cursor_eyedropper.xpm");
168 QBitmap bitmapMask (":/engauge/img/cursor_eyedropper_mask.xpm");
169 return QCursor (bitmap,
170 bitmapMask,
171 HOT_X_IN_BITMAP,
172 HOT_Y_IN_BITMAP);
173}
174
176{
177 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::end";
178
179 // Restore original background. The state transition was triggered earlier by either the user selecting
180 // a valid point, or by user clicking on another digitize state button
181 context().mainWindow().selectOriginal(m_previousBackground);
182}
183
184bool DigitizeStateColorPicker::findNearestNonBackgroundPixel (CmdMediator *cmdMediator,
185 const QImage &image,
186 const QPointF &posScreenPlusHalf,
187 const QRgb &rgbBackground,
188 QColor &pixel)
189{
190 QPoint pos = posScreenPlusHalf.toPoint ();
191
192 int maxRadiusForSearch = cmdMediator->document().modelGeneral().cursorSize();
193
194 // Starting at pos, search in ever-widening squares for a non-background pixel
195 for (int radius = 0; radius < maxRadiusForSearch; radius++) {
196
197 for (int xOffset = -radius; xOffset <= radius; xOffset++) {
198 for (int yOffset = -radius; yOffset <= radius; yOffset++) {
199
200 // Top side
201 pixel = image.pixel (pos.x () + xOffset, pos.y () - radius);
202 if (pixel != rgbBackground) {
203 return true;
204 }
205
206 // Bottom side
207 pixel = image.pixel (pos.x () + xOffset, pos.y () + radius);
208 if (pixel != rgbBackground) {
209 return true;
210 }
211
212 // Left side
213 pixel = image.pixel (pos.x () - radius, pos.y () - yOffset);
214 if (pixel != rgbBackground) {
215 return true;
216 }
217
218 // Right side
219 pixel = image.pixel (pos.x () + radius, pos.y () + yOffset);
220 if (pixel != rgbBackground) {
221 return true;
222 }
223 }
224 }
225 }
226
227 return false;
228}
229
231 const QString &pointIdentifier)
232{
233 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::handleContextMenuEventAxis "
234 << " point=" << pointIdentifier.toLatin1 ().data ();
235}
236
238 const QStringList &pointIdentifiers)
239{
240 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker ::handleContextMenuEventGraph "
241 << "points=" << pointIdentifiers.join(",").toLatin1 ().data ();
242}
243
245{
246 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::handleCurveChange";
247}
248
250 Qt::Key key,
251 bool /* atLeastOneSelectedItem */)
252{
253 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::handleKeyPress"
254 << " key=" << QKeySequence (key).toString ().toLatin1 ().data ();
255}
256
258 QPointF /* posScreen */)
259{
260// LOG4CPP_DEBUG_S ((*mainCat)) << "DigitizeStateColorPicker::handleMouseMove";
261}
262
264 QPointF /* posScreen */)
265{
266 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::handleMousePress";
267}
268
270 QPointF posScreen)
271{
272 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::handleMouseRelease";
273
274 DocumentModelColorFilter modelColorFilterBefore = cmdMediator->document().modelColorFilter();
275 DocumentModelColorFilter modelColorFilterAfter = cmdMediator->document().modelColorFilter();
276 if (computeFilterFromPixel (cmdMediator,
277 posScreen,
278 context().mainWindow().selectedGraphCurve(),
279 modelColorFilterAfter)) {
280
281 // Trigger a state transition. The background restoration will be handled by the end method
282 context().requestDelayedStateTransition(m_previousDigitizeState);
283
284 // Create command to change segment filter
285 QUndoCommand *cmd = new CmdSettingsColorFilter (context ().mainWindow(),
286 cmdMediator->document (),
287 modelColorFilterBefore,
288 modelColorFilterAfter);
289 context().appendNewCmd(cmdMediator,
290 cmd);
291 }
292}
293
294void DigitizeStateColorPicker::saveLowerValueUpperValue (DocumentModelColorFilter &modelColorFilterAfter,
295 const QString &curveName,
296 double lowerValueIn,
297 double upperValueIn)
298{
299 int lowerValue = qFloor (lowerValueIn);
300 int upperValue = qFloor (upperValueIn);
301
302 switch (modelColorFilterAfter.colorFilterMode (curveName)) {
304 modelColorFilterAfter.setForegroundLow(curveName,
305 lowerValue);
306 modelColorFilterAfter.setForegroundHigh(curveName,
307 upperValue);
308 break;
309
311 modelColorFilterAfter.setHueLow(curveName,
312 lowerValue);
313 modelColorFilterAfter.setHueHigh(curveName,
314 upperValue);
315 break;
316
318 modelColorFilterAfter.setIntensityLow(curveName,
319 lowerValue);
320 modelColorFilterAfter.setIntensityHigh(curveName,
321 upperValue);
322 break;
323
325 modelColorFilterAfter.setSaturationLow(curveName,
326 lowerValue);
327 modelColorFilterAfter.setSaturationHigh(curveName,
328 upperValue);
329 break;
330
332 modelColorFilterAfter.setValueLow(curveName,
333 lowerValue);
334 modelColorFilterAfter.setValueHigh(curveName,
335 upperValue);
336 break;
337
338 default:
339 ENGAUGE_ASSERT (false);
340 }
341}
342
344{
345 return "DigitizeStateColorPicker";
346}
347
349{
350 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::updateAfterPointAddition";
351}
352
354 const DocumentModelDigitizeCurve & /*modelDigitizeCurve */)
355{
356 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::updateModelDigitizeCurve";
357}
358
360{
361 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateColorPicker::updateModelSegments";
362}
@ 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 if defined(QT_NO_DEBUG) && !defined(QT_FORCE_ASSERTS) define ENGAUGE...
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.
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: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...
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 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 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:817
DocumentModelGeneral modelGeneral() const
Get method for DocumentModelGeneral.
Definition Document.cpp:723
DocumentModelColorFilter modelColorFilter() const
Get method for DocumentModelColorFilter.
Definition Document.cpp:688
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.
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