1 /*
2 delaboratory - color correction utility
3 Copyright (C) 2011 Jacek Poplawski
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "property_levels_ui.h"
20 #include "property_levels.h"
21 #include "layer_processor.h"
22 #include "panel_wx.h"
23 #include "window_wx.h"
24 #include "histogram.h"
25 #include <wx/dcbuffer.h>
26 #include "canvas_wx.h"
27 #include "gradient_panel.h"
28 #include "channel_selector.h"
29 #include "base_layer_with_source.h"
30
31 class deLevelsChannelSelector:public deChannelSelector
32 {
33 private:
34 dePropertyLevelsUIImpl& ui;
35 public:
deLevelsChannelSelector(deWindow & window,deColorSpace colorSpace,dePropertyLevelsUIImpl & _ui)36 deLevelsChannelSelector(deWindow& window, deColorSpace colorSpace, dePropertyLevelsUIImpl& _ui)
37 :deChannelSelector(window, colorSpace), ui(_ui)
38 {
39 }
~deLevelsChannelSelector()40 virtual ~deLevelsChannelSelector()
41 {
42 }
43 virtual void onValueChange(int channel);
44 };
45
46
47 class deLevelsControl:public dePanelWX
48 {
49 private:
50 wxBitmap bitmap;
51 dePropertyLevels& property;
52 int width;
53 int height;
54 int margin;
55
56 deLayerProcessor& layerProcessor;
57 int layerIndex;
58
59 int channel;
60
61 enum
62 {
63 deModeNothing,
64 deModeMin,
65 deModeMiddle,
66 deModeMax
67 } mode;
68
click(wxMouseEvent & event)69 void click(wxMouseEvent &event)
70 {
71 deValue v = getValue(event.GetX());
72
73 const deLevels& levels = property.getLevels(channel);
74
75 deValue min = levels.getMin();
76 deValue middle = levels.getMiddle();
77 deValue max = levels.getMax();
78
79 deValue dmin = fabs(v - min);
80 deValue dmiddle = fabs(v - middle);
81 deValue dmax = fabs(v - max);
82
83 deValue d = 0.01;
84
85 if (dmin < d)
86 {
87 mode = deModeMin;
88 } else
89 if (dmax < d)
90 {
91 mode = deModeMax;
92 } else
93 if (dmiddle < d)
94 {
95 mode = deModeMiddle;
96 }
97 }
98
release(wxMouseEvent & event)99 void release(wxMouseEvent &event)
100 {
101 mode = deModeNothing;
102 }
103
move(wxMouseEvent & event)104 void move(wxMouseEvent &event)
105 {
106 deValue v = getValue(event.GetX());
107
108 if ((v < 0) || (v > 1.0))
109 {
110 return;
111 }
112
113 deLevels& levels = property.getLevels(channel);
114
115 switch (mode)
116 {
117 case deModeMin:
118 {
119 levels.setMin(v);
120 break;
121 }
122 case deModeMiddle:
123 {
124 levels.setMiddle(v);
125 break;
126 }
127 case deModeMax:
128 {
129 levels.setMax(v);
130 break;
131 }
132 default:
133 {
134 return;
135 break;
136 }
137 }
138
139 paint();
140
141 layerProcessor.markUpdateSingleChannel(layerIndex, channel);
142 }
143
drawArrow(deCanvas & canvas,deValue v)144 void drawArrow(deCanvas& canvas, deValue v)
145 {
146 int p = margin + v * (width - 2 * margin);
147 int m = 3;
148 int p1 = p - m;
149 if (p1 < 0)
150 {
151 p1 = 0;
152 }
153 int p2 = p + m;
154 if (p2 >= width)
155 {
156 p2 = width - 1;
157 }
158
159 int g = 50;
160 canvas.setBrush(g, g, g);
161 canvas.drawTriangle(p, 0, p1, height, p2, height);
162 }
163
getValue(int p)164 deValue getValue(int p)
165 {
166 deValue a = p - margin;
167 deValue b = width - 2 * margin;
168 return a / b;
169 }
170
171
172 public:
deLevelsControl(deWindow & _parentWindow,dePropertyLevels & _property,int _width,int _height,int _margin,deLayerProcessor & _layerProcessor,int _layerIndex)173 deLevelsControl(deWindow& _parentWindow, dePropertyLevels& _property, int _width, int _height, int _margin, deLayerProcessor& _layerProcessor, int _layerIndex)
174 :dePanelWX(_parentWindow, _width, _height), property(_property), width(_width), height(_height), margin(_margin), layerProcessor(_layerProcessor), layerIndex(_layerIndex)
175 {
176 SetMinSize(wxSize(width, height));
177
178 channel = -1;
179
180 mode = deModeNothing;
181
182 bitmap.Create(_width, _height);
183
184 Connect(wxEVT_PAINT, wxPaintEventHandler(deLevelsControl::paintEvent));
185 Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(deLevelsControl::click));
186 Connect(wxEVT_LEFT_UP, wxMouseEventHandler(deLevelsControl::release));
187 Connect(wxEVT_MOTION, wxMouseEventHandler(deLevelsControl::move));
188
189 }
190
~deLevelsControl()191 ~deLevelsControl()
192 {
193 }
194
setChannel(int _channel)195 void setChannel(int _channel)
196 {
197 channel = _channel;
198 paint();
199 }
200
paint()201 void paint()
202 {
203 wxClientDC dc(this);
204
205 render(dc);
206 }
207
paintEvent(wxPaintEvent & evt)208 void paintEvent(wxPaintEvent & evt)
209 {
210 wxPaintDC dc(this);
211
212 render(dc);
213 }
214
render(wxDC & dc_orig)215 void render(wxDC& dc_orig)
216 {
217 wxBufferedDC dc(&dc_orig, bitmap, wxBUFFER_CLIENT_AREA);
218
219 deCanvasWX canvas(dc);
220
221 int h = height / 5;
222
223 canvas.clear();
224 canvas.drawLine(0, h, width, h);
225
226 const deLevels& levels = property.getLevels(channel);
227 deValue min = levels.getMin();
228 deValue middle = levels.getMiddle();
229 deValue max = levels.getMax();
230
231 unsigned char g = 50;
232 canvas.setPen(g, g, g);
233 drawArrow(canvas, min);
234 drawArrow(canvas, middle);
235 drawArrow(canvas, max);
236 }
237 };
238
239 class deChannelHistogramPanel:public dePanelWX
240 {
241 private:
242 wxBitmap* backgroundBitmap;
243 int width;
244 int height;
245 int margin;
246
247 public:
deChannelHistogramPanel(deWindow & _parentWindow,const deValue * c,int n,int _width,int _height,int _margin)248 deChannelHistogramPanel(deWindow& _parentWindow, const deValue* c, int n, int _width, int _height, int _margin)
249 :dePanelWX(_parentWindow, _width, _height), width(_width), height(_height), margin(_margin)
250 {
251 backgroundBitmap = NULL;
252
253 generate(c, n);
254
255 Connect(wxEVT_PAINT, wxPaintEventHandler(deChannelHistogramPanel::paintEvent));
256 }
257
~deChannelHistogramPanel()258 virtual ~deChannelHistogramPanel()
259 {
260 }
261
paintEvent(wxPaintEvent & evt)262 void paintEvent(wxPaintEvent & evt)
263 {
264 wxPaintDC dc(this);
265 dc.DrawBitmap(*backgroundBitmap, 0, 0, false);
266 }
267
paint()268 void paint()
269 {
270 wxClientDC dc(this);
271 dc.DrawBitmap(*backgroundBitmap, 0, 0, false);
272 }
273
generate(const deValue * c,int n)274 void generate(const deValue* c, int n)
275 {
276 if (backgroundBitmap)
277 {
278 delete backgroundBitmap;
279 backgroundBitmap = NULL;
280 }
281
282 deHistogram histogram(width - 2 * margin);
283
284 histogram.clear();
285 histogram.calc(c, n);
286
287 wxImage* image = new wxImage(width, height);
288 unsigned char* data = image->GetData();
289
290 unsigned char g1 = 255;
291 unsigned char g2 = 200;
292
293 histogram.render(data, width, height, g1, g2, margin);
294
295 backgroundBitmap = new wxBitmap(*image);
296 delete image;
297
298 paint();
299 }
300 };
301
302
303 class dePropertyLevelsUIImpl:public dePanelWX
304 {
305 private:
306 deBaseLayerWithSource& layer;
307 dePropertyLevelsUI& parent;
308 dePropertyLevels& property;
309
310 deLevelsChannelSelector *channelSelector;
311 deChannelHistogramPanel* histogramPanel;
312 deLevelsControl* levelsControl;
313 deGradientPanel0* gradient;
314
315 int channel;
316
317 public:
dePropertyLevelsUIImpl(dePropertyLevelsUI & _parent,deWindow & _parentWindow,dePropertyLevels & _property,deBaseLayerWithSource & _layer,deLayerProcessor & layerProcessor,int layerIndex,int width)318 dePropertyLevelsUIImpl(dePropertyLevelsUI& _parent, deWindow& _parentWindow, dePropertyLevels& _property, deBaseLayerWithSource& _layer, deLayerProcessor& layerProcessor, int layerIndex, int width)
319 :dePanelWX(_parentWindow), layer(_layer), parent(_parent), property(_property)
320 {
321 int margin = 10;
322
323 wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
324 SetSizer(sizer);
325
326 channel = -1;
327
328 deColorSpace colorSpace = layer.getColorSpace();
329
330 {
331 channelSelector = new deLevelsChannelSelector(getWindow(), colorSpace, *this);
332 deWindowWX& w = dynamic_cast<deWindowWX&>(channelSelector->getWindow());
333 sizer->Add(w.getWindow());
334 }
335
336 int h = 80;
337 histogramPanel = new deChannelHistogramPanel(getWindow(), NULL, 0, width, h, margin);
338 sizer->Add(histogramPanel);
339
340 gradient = new deGradientPanel0(this, wxSize(width, 8), layer.getColorSpace(), 0, margin);
341 sizer->Add(gradient);
342
343 levelsControl = new deLevelsControl(getWindow(), property, width, 10, margin, layerProcessor, layerIndex);
344 sizer->Add(levelsControl);
345
346 setChannel(0);
347
348 Fit();
349 }
350
~dePropertyLevelsUIImpl()351 virtual ~dePropertyLevelsUIImpl()
352 {
353 }
354
generate()355 void generate()
356 {
357 const deImage& sourceImage = layer.getSourceImage();
358
359 const deValue* c = sourceImage.startRead(channel);
360 int n = sourceImage.getChannelSize().getN();
361
362 histogramPanel->generate(c, n);
363
364 sourceImage.finishRead(channel);
365 }
366
setFromProperty()367 void setFromProperty()
368 {
369 levelsControl->paint();
370 }
371
setChannel(int _channel)372 void setChannel(int _channel)
373 {
374 channel = _channel;
375 levelsControl->setChannel(channel);
376 gradient->changeChannel(channel);
377 generate();
378 }
379
380
381 };
382
dePropertyLevelsUI(deWindow & window,dePropertyLevels & _property,deLayerProcessor & _layerProcessor,int _layerIndex,deBaseLayerWithSource & _layer,int width)383 dePropertyLevelsUI::dePropertyLevelsUI(deWindow& window, dePropertyLevels& _property, deLayerProcessor& _layerProcessor, int _layerIndex, deBaseLayerWithSource& _layer, int width)
384 :property(_property), layerProcessor(_layerProcessor), layerIndex(_layerIndex), layer(_layer)
385 {
386 deWindowWX* w = dynamic_cast<deWindowWX*>(&window);
387 if (w)
388 {
389 impl = new dePropertyLevelsUIImpl(*this, window, property, layer, layerProcessor, layerIndex, width);
390 }
391 else
392 {
393 impl = NULL;
394 }
395 }
396
~dePropertyLevelsUI()397 dePropertyLevelsUI::~dePropertyLevelsUI()
398 {
399 if (impl)
400 {
401 delete impl;
402 }
403 }
404
getWindow()405 deWindow& dePropertyLevelsUI::getWindow()
406 {
407 return impl->getWindow();
408 }
409
setFromProperty()410 void dePropertyLevelsUI::setFromProperty()
411 {
412 if (impl)
413 {
414 impl->setFromProperty();
415 }
416 }
417
onValueChange(int channel)418 void deLevelsChannelSelector::onValueChange(int channel)
419 {
420 ui.setChannel(channel);
421 }
422