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 "project.h"
20 #include "message_box.h"
21 #include "image_io.h"
22 #include <cassert>
23 #include <iostream>
24 #include "source_image_layer.h"
25 #include "curves_layer.h"
26 #include "conversion_layer.h"
27 #include "str.h"
28 #include <sstream>
29 #include "layer_factory.h"
30 #include <iostream>
31 #include "layer_processor.h"
32 #include "external_editor.h"
33 #include "channel_manager.h"
34 #include "layer_stack.h"
35 #include "layer_frame_manager.h"
36 #include "static_image.h"
37 #include "zoom_manager.h"
38 #include "color_space_utils.h"
39 #include "operation_processor.h"
40 #include "main_window.h"
41 #include "conversion_processor.h"
42 #include "test_image.h"
43 #include "main_frame_events.h"
44 
45 #include "raw_module.h"
46 #include "dcraw_support.h"
47 #include "tmp.h"
48 
deProject(deLayerProcessor & _processor,deChannelManager & _channelManager,deLayerStack & _layerStack,deLayerFrameManager & _layerFrameManager,deStaticImage & _sourceImage,deRawModule & _rawModule,deZoomManager & _zoomManager,deMainWindow & _mainWindow,deGUI & _gui)49 deProject::deProject(deLayerProcessor& _processor, deChannelManager& _channelManager, deLayerStack& _layerStack, deLayerFrameManager& _layerFrameManager, deStaticImage& _sourceImage, deRawModule& _rawModule, deZoomManager& _zoomManager, deMainWindow& _mainWindow, deGUI& _gui)
50 :layerProcessor(_processor),
51  channelManager(_channelManager),
52  viewManager(*this, _processor, _zoomManager),
53  sourceImage(_sourceImage),
54  layerStack(_layerStack),
55  layerFrameManager(_layerFrameManager),
56  rawModule(_rawModule),
57  zoomManager(_zoomManager),
58  mainWindow(_mainWindow),
59  gui(_gui)
60 {
61     imageFileName = "";
62     sourceImageFileName = "";
63 
64     logInfo("project constructor");
65 
66     layerProcessor.setViewManager(&viewManager);
67 
68     resetLayerStack(deColorSpaceRGB);
69 }
70 
~deProject()71 deProject::~deProject()
72 {
73     logInfo("project destructor");
74     layerProcessor.lockLayerProcessor();
75     layerProcessor.unlockLayerProcessor();
76     layerStack.clear();
77 }
78 
setHistogramChannel(int channel)79 void deProject::setHistogramChannel(int channel)
80 {
81     logInfo("project set histogram channel " + str(channel));
82     gui.updateHistogramMode(channel);
83     layerProcessor.generateHistogram();
84 }
85 
onKey(int key)86 void deProject::onKey(int key)
87 {
88     if (key == '`')
89     {
90         viewManager.setNormal();
91     }
92     if (key == '1')
93     {
94         viewManager.setSingleChannel(0);
95     }
96     if (key == '2')
97     {
98         viewManager.setSingleChannel(1);
99     }
100     if (key == '3')
101     {
102         viewManager.setSingleChannel(2);
103     }
104     if (key == '4')
105     {
106         viewManager.setSingleChannel(3);
107     }
108     /*
109     if (key == WXK_F1)
110     {
111         setHistogramChannel(0);
112     }
113     if (key == WXK_F2)
114     {
115         setHistogramChannel(1);
116     }
117     if (key == WXK_F3)
118     {
119         setHistogramChannel(2);
120     }
121     if (key == WXK_F4)
122     {
123         setHistogramChannel(3);
124     }
125     */
126 
127     layerFrameManager.onKey(key);
128 }
129 
init(const std::string & fileName)130 void deProject::init(const std::string& fileName)
131 {
132     bool ok = false;
133     if (openImage(fileName, true, false, false))
134     {
135         ok = true;
136     } else if (openImage(fileName, false, false, false))
137     {
138         ok = true;
139     }
140     if (!ok)
141     {
142         std::string s = "unable to open image: " + fileName;
143         logError(s);
144         showMessageBox(s);
145         return;
146     }
147 }
148 
freeImage()149 void deProject::freeImage()
150 {
151 }
152 
setTestImage(int s)153 void deProject::setTestImage(int s)
154 {
155     if (rawModule.isActive())
156     {
157         showMessageBox("RAW module is already active");
158         return;
159     }
160 
161     generateTestImage(sourceImage, s);
162     gui.updateImageAreaSize();
163     resetLayerStack(sourceImage.getColorSpace());
164     imageFileName = "test";
165 
166 }
167 
resetLayerStack(deColorSpace colorSpace)168 void deProject::resetLayerStack(deColorSpace colorSpace)
169 {
170     logInfo("reset layer stack");
171 
172     layerProcessor.removeAllLayers();
173 
174     deBaseLayer* layer = createLayer("original", -1, colorSpace, layerStack, channelManager, viewManager, sourceImage);
175 
176     if (layer)
177     {
178         layer->allocateChannels();
179         layerProcessor.addLayerInLayerProcessor(layer);
180     }
181 
182     layerProcessor.updateAllImages(true);
183 
184     updateLayerGrid();
185 
186     viewManager.setLastView();
187 
188 }
189 
getPreviewChannelManager()190 deChannelManager& deProject::getPreviewChannelManager()
191 {
192     return channelManager;
193 }
194 
getSourceImageSize()195 deSize deProject::getSourceImageSize()
196 {
197     deSize size = sourceImage.getStaticImageSize();
198 
199     deValue x1;
200     deValue y1;
201     deValue x2;
202     deValue y2;
203 
204     zoomManager.getZoom(x1, y1, x2, y2);
205 
206     deValue w = size.getW() * (x2 - x1);
207     deValue h = size.getH() * (y2 - y1);
208 
209     return deSize(w, h);
210 }
211 
getSourceAspect() const212 deValue deProject::getSourceAspect() const
213 {
214     const deBaseLayer* layer = layerStack.startReadLayer(0);
215     deValue aspect = -1;
216 
217     const deSourceImageLayer* source = dynamic_cast<const deSourceImageLayer*>(layer);
218     if (source)
219     {
220         deValue x1;
221         deValue y1;
222         deValue x2;
223         deValue y2;
224 
225         zoomManager.getZoom(x1, y1, x2, y2);
226 
227         deValue dx = x2 - x1;
228         deValue dy = y2 - y1;
229         if ((dx > 0) && (dy > 0))
230         {
231             deValue a = dy / dx;
232 
233             aspect = source->getAspect() / a;
234         }
235     }
236 
237     layerStack.finishReadLayer(0);
238     return aspect;
239 }
240 
getLayerStack()241 deLayerStack& deProject::getLayerStack()
242 {
243     return layerStack;
244 }
245 
getLayerProcessor()246 deLayerProcessor& deProject::getLayerProcessor()
247 {
248     return layerProcessor;
249 }
250 
onChangeView(int a)251 void deProject::onChangeView(int a)
252 {
253     logInfo("change view from " + str(a) + " START");
254     layerProcessor.onChangeView(a);
255 
256     gui.updateViewModePanelNames();
257     gui.updateHistogramNames();
258     mainWindow.rebuild();
259     logInfo("change view from " + str(a) + " DONE");
260 }
261 
getViewManager() const262 const deViewManager& deProject::getViewManager() const
263 {
264     return viewManager;
265 }
266 
getViewManager()267 deViewManager& deProject::getViewManager()
268 {
269     return viewManager;
270 }
271 
exportFinalImage(const std::string & app,const std::string & type,const std::string & name,deProgressDialog & progressDialog,bool saveAll,const std::string & dir)272 bool deProject::exportFinalImage(const std::string& app, const std::string& type, const std::string& name, deProgressDialog& progressDialog, bool saveAll, const std::string& dir)
273 {
274     // FIXME without dynamic channel allocation all channels are allocated from start - so it will crash with out of memory
275 
276     logInfo("exportFinalImage...");
277 
278     zoomManager.fullZoomOut();
279 
280     // name is taken from file dialog, it can be empty when we are exporting to external editor
281     // but in this case we need correct imageFileName
282     if ((name == "") && (imageFileName == ""))
283     {
284         showMessageBox( "exporting final image failed - no file name set");
285         return false;
286     }
287 
288     std::string fileName;
289 
290     if (name == "")
291     {
292         // path is a directory for temporary save, used only when exporting to external editor
293         std::string path = getTmp();
294 
295         if (dir.size() > 0)
296         {
297             // now used also when exporting all images into one dir
298             path = dir;
299         }
300 
301         // we save file in the temporary directory
302         fileName = path + "/" + imageFileName + "." + type;
303     }
304     else
305     {
306         // wa save file in the location taken from file dialog
307         fileName = name;
308     }
309 
310     bool result = processFullSizeImage(fileName, type, saveAll, progressDialog);
311 
312     if (!result)
313     {
314         showMessageBox( "exporting final image failed - error during update images\n(probably out of memory)");
315     }
316 
317     if (result)
318     {
319         // execute external editor
320         if (app.size() > 0)
321         {
322             executeExternalEditor(fileName, app);
323         }
324     }
325 
326     // calculate image in preview size to continue editing
327     layerProcessor.updateAllImages(true);
328 
329     gui.updateImageAreaSize();
330 
331     return result;
332 }
333 
processFullSizeImage(const std::string & fileName,const std::string & type,bool saveAll,deProgressDialog & progressDialog)334 bool deProject::processFullSizeImage(const std::string& fileName, const std::string& type, bool saveAll, deProgressDialog& progressDialog)
335 {
336     logInfo("processFullSizeImage...");
337 
338     bool result = layerProcessor.updateImagesSmart(progressDialog, fileName, type, saveAll, sourceImage.getStaticImageSize(), gui);
339 
340     return result;
341 }
342 
343 
onChangeViewMode()344 void deProject::onChangeViewMode()
345 {
346     gui.updateViewModePanelMode();
347 }
348 
newProject()349 void deProject::newProject()
350 {
351     resetLayerStack(deColorSpaceRGB);
352     viewManager.setLastView();
353     updateLayerGrid();
354 }
355 
openImageRAW(const std::string & fileName,bool srgb,bool brighten)356 bool deProject::openImageRAW(const std::string& fileName, bool srgb, bool brighten)
357 {
358 
359     bool valid = isRawValid(fileName);
360     if (!valid)
361     {
362         logError("not a valid RAW " + fileName);
363         return false;
364     }
365 
366 /*
367     std::string info;
368     info = getRawInfo(fileName);
369 
370     if (info.size() == 0)
371     {
372         logError("empty RAW info in " + fileName);
373         return false;
374     }
375     */
376 
377     layerProcessor.sendInfoEvent(DE_DCRAW_START);
378     if (rawModule.loadRAW(fileName, sourceImage, true, srgb, brighten))
379     {
380         logInfo("found RAW " + fileName);
381         bool failure = false;
382         while (!rawModule.updateRawLoading(failure))
383         {
384             sleep(200);
385             if (failure)
386             {
387                 logError("failed RAW load " + fileName);
388                 layerProcessor.sendInfoEvent(DE_DCRAW_END);
389                 return false;
390             }
391         }
392         bool result = rawModule.loadRAW(fileName, sourceImage, false, srgb, brighten);
393         if (!result)
394         {
395             return false;
396         }
397 
398         mainWindow.startRawTimer();
399 
400     }
401     else
402     {
403         logError("failed RAW " + fileName);
404         layerProcessor.sendInfoEvent(DE_DCRAW_END);
405         return false;
406     }
407 
408     return true;
409 }
410 
openImage(const std::string & fileName,bool raw,bool srgb,bool brighten)411 bool deProject::openImage(const std::string& fileName, bool raw, bool srgb, bool brighten)
412 {
413     freeImage();
414 
415     logInfo("open image " + fileName);
416 
417     deColorSpace oldColorSpace = sourceImage.getColorSpace();
418 
419     if (raw)
420     {
421         if (!openImageRAW(fileName, srgb, brighten))
422         {
423             return false;
424         }
425     }
426     else
427     {
428         logInfo("trying JPEG/TIFF image " + fileName);
429         if (!loadImage(fileName, sourceImage))
430         {
431             logError("load JPEG/TIFF image failed: " + fileName);
432             return false;
433         }
434     }
435 
436 
437     imageFileName = removePathAndExtension(fileName);
438     onImageNameUpdate();
439     sourceImageFileName = fileName;
440 
441     gui.updateImageAreaSize();
442     layerProcessor.updateAllImages(true);
443 
444     deColorSpace newColorSpace = sourceImage.getColorSpace();
445 
446     if (oldColorSpace != newColorSpace)
447     {
448         resetLayerStack(newColorSpace);
449         onChangeView(0);
450     }
451 
452     return true;
453 }
454 
isSourceValid() const455 bool deProject::isSourceValid() const
456 {
457     return (!channelManager.isImageEmpty());
458 }
459 
createNewLayer(const std::string & type)460 deBaseLayer* deProject::createNewLayer(const std::string& type)
461 {
462     logInfo("createNewLayer " + type);
463 
464     int s = viewManager.getView();
465 
466     deBaseLayer* layer = NULL;
467 
468     const deBaseLayer* vLayer = layerStack.startReadLayer(s);
469     if (vLayer)
470     {
471         deColorSpace colorSpace = vLayer->getColorSpace();
472 
473         layer = createLayer(type, s, colorSpace, layerStack, channelManager, viewManager, sourceImage);
474     }
475     layerStack.finishReadLayer(s);
476 
477     if (!layer)
478     {
479         deColorSpace colorSpace = colorSpaceFromString(type);
480 
481         if (colorSpace != deColorSpaceInvalid)
482         {
483             layer = createLayer("conversion", s, colorSpace, layerStack, channelManager, viewManager, sourceImage);
484         }
485     }
486 
487     if (layer)
488     {
489         logInfo("allocate channels in new layer " + type);
490         layer->allocateChannels();
491     }
492 
493     return layer;
494 }
495 
onImageNameUpdate()496 void deProject::onImageNameUpdate()
497 {
498     mainWindow.setImageName(imageFileName, sourceImage.getStaticImageSize());
499 }
500 
onTimerUpdate()501 void deProject::onTimerUpdate()
502 {
503     bool failure = false;
504 
505     bool result = rawModule.updateRawLoading(failure);
506 
507     if ((result) || (failure))
508     {
509         layerProcessor.sendInfoEvent(DE_DCRAW_END);
510         mainWindow.stopRawTimer();
511     }
512 
513     if (result)
514     {
515         mainWindow.postEvent(DE_IMAGE_LOAD_EVENT, 0);
516     }
517 
518 }
519 
updateLayerGrid()520 void deProject::updateLayerGrid()
521 {
522     gui.updateLayerGrid();
523 }
524 
onAddNewLayer()525 void deProject::onAddNewLayer()
526 {
527     viewManager.setLastView();
528     updateLayerGrid();
529 }
530 
onRemoveTopLayer()531 void deProject::onRemoveTopLayer()
532 {
533     viewManager.setLastView();
534     updateLayerGrid();
535 }
536 
onImageAreaChangeSize(const deSize & ps,bool canSkip)537 deSize deProject::onImageAreaChangeSize(const deSize& ps, bool canSkip)
538 {
539     deValue aspect = getSourceAspect();
540 
541     if (aspect <= 0)
542     {
543         logInfo("image area panel update size skipped, aspect is 0");
544         return deSize(0,0);
545     }
546 
547     deSize fit = fitInside(ps, aspect);
548 
549     layerProcessor.setPreviewSize(fit, canSkip);
550 
551     return fit;
552 }
553 
554 
openLayerFrame(int index)555 void deProject::openLayerFrame(int index)
556 {
557     deBaseLayer* layer = layerStack.getLayer(index);
558     if (layer)
559     {
560         gui.openLayerFrame(*layer, layerProcessor, layerFrameManager, index);
561     }
562 
563 }
564