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