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