1 /**
2  * @brief
3  *
4  * This file is a part of PFSTOOLS package.
5  * ----------------------------------------------------------------------
6  * Copyright (C) 2003,2004 Rafal Mantiuk and Grzegorz Krawczyk
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  * ----------------------------------------------------------------------
22  *
23  * @author Rafal Mantiuk, <mantiuk@mpi-sb.mpg.de>
24  *
25  * $Id: main.cpp,v 1.19 2014/10/13 09:27:38 rafm Exp $
26  */
27 
28 #include <config.h>
29 
30 #include "main.h"
31 
32 #include <stdio.h>
33 #include <math.h>
34 #include <stdlib.h>
35 
36 #include <qcombobox.h>
37 #include <qlabel.h>
38 #include <qtoolbutton.h>
39 #include <qstatusbar.h>
40 #include <qtooltip.h>
41 #include <qcursor.h>
42 #include <qclipboard.h>
43 #include <QApplication>
44 #include <QFileDialog>
45 #include <QImageWriter>
46 #include <QToolBar>
47 #include <pfs.h>
48 #include <QShortcut>
49 
50 
51 #include "pfsview_widget.h"
52 #include "luminancerange_widget.h"
53 
54 #define PROGNAME "pfsview"
55 
56 //QApplication *qApp;
57 
PFSViewMainWin(float window_min,float window_max)58 PFSViewMainWin::PFSViewMainWin(  float window_min, float window_max ):
59   QMainWindow( 0 )
60 {
61   currentFrame = frameList.end();
62 
63   QScrollArea *pfsViewArea = new PFSViewWidgetArea( this );
64 
65   pfsView = (PFSViewWidget*)pfsViewArea->widget();
66 
67   setCentralWidget( pfsViewArea );
68 
69   setWindowIcon( QIcon( ":icons/appicon.png" ) );
70 
71   QAction *nextFrameAct = new QAction( tr( "&Next frame" ), this );
72   nextFrameAct->setStatusTip( tr( "Load next frame" ) );
73   nextFrameAct->setShortcut( Qt::Key_PageDown );
74   connect( nextFrameAct, SIGNAL(triggered()), this, SLOT(gotoNextFrame()) );
75 
76   QAction *previousFrameAct = new QAction( tr( "&Previous frame" ), this );
77   previousFrameAct->setStatusTip( tr( "Load previous frame" ) );
78   previousFrameAct->setShortcut( Qt::Key_PageUp );
79   connect( previousFrameAct, SIGNAL(triggered()), this, SLOT(gotoPreviousFrame()) );
80 
81   QToolBar *toolBar = addToolBar( tr( "Navigation" ) );
82 //  toolBar->setHorizontalStretchable( true );
83 
84   QToolButton *previousFrameBt = new QToolButton( toolBar );
85   previousFrameBt->setArrowType( Qt::LeftArrow );
86   previousFrameBt->setMinimumWidth( 15 );
87   connect( previousFrameBt, SIGNAL(clicked()), this, SLOT(gotoPreviousFrame()) );
88   previousFrameBt->setToolTip( "Goto previous frame" );
89   toolBar->addWidget( previousFrameBt );
90 
91   QToolButton *nextFrameBt = new QToolButton( toolBar );
92   nextFrameBt->setArrowType( Qt::RightArrow );
93   nextFrameBt->setMinimumWidth( 15 );
94   connect( nextFrameBt, SIGNAL(clicked()), this, SLOT(gotoNextFrame()) );
95   nextFrameBt->setToolTip( "Goto next frame" );
96   toolBar->addWidget( nextFrameBt );
97 
98   QLabel *channelSelLabel = new QLabel( "&Channel", toolBar );
99   channelSelection = new QComboBox( toolBar );
100   channelSelLabel->setBuddy( channelSelection );
101   connect( channelSelection, SIGNAL( activated( int ) ),
102     this, SLOT( setChannelSelection(int) ) );
103   toolBar->addWidget( channelSelLabel );
104   toolBar->addWidget( channelSelection );
105 
106   toolBar->addSeparator();
107 
108   QLabel *mappingMethodLabel = new QLabel( "&Mapping", toolBar );
109   mappingMethodLabel->setAlignment(  Qt::AlignRight | Qt::AlignVCenter ); // |
110 //				    Qt::TextExpandTabs | Qt::TextShowMnemonic );
111   mappingMethodCB = new QComboBox( toolBar );
112   mappingMethodLabel->setBuddy( mappingMethodCB );
113   mappingMethodCB->addItem( "Linear" );
114   mappingMethodCB->addItem( "Gamma 1.4" );
115   mappingMethodCB->addItem( "Gamma 1.8" );
116   mappingMethodCB->addItem( "Gamma 2.2" );
117   mappingMethodCB->addItem( "Gamma 2.6" );
118   mappingMethodCB->addItem( "Logarithmic" );
119   mappingMethodCB->setCurrentIndex( 3 );
120   connect( mappingMethodCB, SIGNAL( activated( int ) ),
121     this, SLOT( setLumMappingMethod(int) ) );
122   toolBar->addWidget( mappingMethodLabel );
123   toolBar->addWidget( mappingMethodCB );
124 
125 //  addToolBar( Qt::BottomToolBarArea, toolBar );
126 
127   QToolBar *toolBarLR = addToolBar( tr( "Histogram" ) );
128   lumRange = new LuminanceRangeWidget( toolBarLR );
129   connect( lumRange, SIGNAL( updateRangeWindow() ), this,
130     SLOT( updateRangeWindow() ) );
131   toolBarLR->addWidget( lumRange );
132 //  addToolBar( toolBar );
133 
134 
135   pointerPosAndVal = new QLabel( statusBar() );
136   statusBar()->addWidget( pointerPosAndVal );
137 //  QFont fixedFont = QFont::defaultFont();
138 //  fixedFont.setFixedPitch( true );
139 //  pointerPosAndVal->setFont( fixedFont );
140   zoomValue = new QLabel( statusBar() );
141   statusBar()->addWidget( zoomValue );
142   exposureValue = new QLabel( statusBar() );
143   statusBar()->addWidget( exposureValue );
144 
145   connect( pfsView, SIGNAL(updatePointerValue()),
146              this, SLOT(updatePointerValue()) );
147 
148 
149 
150   QMenu *frameMenu = menuBar()->addMenu( tr( "&Frame" ) );
151   frameMenu->addAction( nextFrameAct );
152   frameMenu->addAction( previousFrameAct );
153   frameMenu->addSeparator();
154   frameMenu->addAction( "&Save image...", this, SLOT(saveImage()), QKeySequence::Save );
155   frameMenu->addAction( "&Copy image to clipboard", this, SLOT(copyImage()), QKeySequence::Copy );
156   frameMenu->addSeparator();
157   frameMenu->addAction( "&Quit", qApp, SLOT(quit()), Qt::Key_Q ); //QKeySequence::Quit
158   QShortcut *shortcut = new QShortcut( QKeySequence::Close, this );
159   connect( shortcut, SIGNAL(activated()), qApp, SLOT(quit()) );
160 
161 
162   QAction *act;
163   QMenu *viewMenu = menuBar()->addMenu( tr( "&View" ) );
164 
165   act = viewMenu->addAction( "&Zoom in", pfsView, SLOT(zoomIn()), Qt::Key_Period ); // QKeySequence::ZoomIn -- not doing it -- silly binding under Linux
166   connect( act, SIGNAL(triggered()), this, SLOT(updateZoomValue()) );
167   act = viewMenu->addAction( "Zoom &out", pfsView, SLOT(zoomOut()), Qt::Key_Comma );
168   connect( act, SIGNAL(triggered()), this, SLOT(updateZoomValue()) );
169   act = viewMenu->addAction( "Zoom &1:1", pfsView, SLOT(zoomOriginal()), Qt::Key_N );
170   connect( act, SIGNAL(triggered()), this, SLOT(updateZoomValue()) );
171   viewMenu->addAction( "&Fit window to content", this, SLOT(updateViewSize()), Qt::Key_C );
172 
173   viewMenu->addSeparator();
174 
175   QMenu *infnanMenu = viewMenu->addMenu( "NaN and &Inf values" );
176   QActionGroup *infnanActGrp = new QActionGroup( this );
177   infnanActGrp->setExclusive( true );
178   QAction *infnanHideAct = new QAction( tr( "&Hide" ), this );
179   infnanHideAct->setCheckable(true);
180   infnanHideAct->setData(0);
181   infnanActGrp->addAction( infnanHideAct );
182   infnanMenu->addAction( infnanHideAct );
183   QAction *infnanMarkAct = new QAction( tr( "Mark with &red color" ), this );
184   infnanMarkAct->setCheckable(true);
185   infnanMarkAct->setData(1);
186   infnanActGrp->addAction( infnanMarkAct );
187   infnanMenu->addAction( infnanMarkAct );
188   infnanMarkAct->setChecked( true );
189   connect( infnanActGrp, SIGNAL(triggered(QAction*)), pfsView, SLOT(setInfNaNTreatment(QAction*)) );
190 
191   QMenu *colorClipMenu = viewMenu->addMenu( "&Color clipping" );
192   QActionGroup *colorClipActGrp = new QActionGroup( this );
193   colorClipActGrp->setExclusive( true );
194   QAction *colorClipSimpleAct = new QAction( tr( "&Simple clipping" ), this );
195   colorClipSimpleAct->setCheckable(true);
196   colorClipSimpleAct->setData(CLIP_SIMPLE);
197   colorClipSimpleAct->setShortcut( Qt::CTRL + Qt::Key_H );
198   colorClipActGrp->addAction( colorClipSimpleAct );
199   colorClipMenu->addAction( colorClipSimpleAct );
200   QAction *colorClipCodedAct = new QAction( tr( "&Color-coded clipping" ), this );
201   colorClipCodedAct->setCheckable(true);
202   colorClipCodedAct->setShortcut( Qt::CTRL + Qt::Key_J );
203   colorClipCodedAct->setData(CLIP_COLORCODED);
204   colorClipActGrp->addAction( colorClipCodedAct );
205   colorClipMenu->addAction( colorClipCodedAct );
206   QAction *colorClipBriHueAct = new QAction( tr( "&Keep brightness and hue" ), this );
207   colorClipBriHueAct->setCheckable(true);
208   colorClipBriHueAct->setShortcut( Qt::CTRL + Qt::Key_K );
209   colorClipBriHueAct->setData(CLIP_KEEP_BRI_HUE);
210   colorClipActGrp->addAction( colorClipBriHueAct );
211   colorClipMenu->addAction( colorClipBriHueAct );
212   colorClipSimpleAct->setChecked( true );
213   connect( colorClipActGrp, SIGNAL(triggered(QAction*)), pfsView, SLOT(setRGBClippingMethod(QAction*)) );
214 
215   QMenu *negativeMenu = viewMenu->addMenu( "&Negative values" );
216   QActionGroup *negativeActGrp = new QActionGroup( this );
217   negativeActGrp->setExclusive( true );
218   act = new QAction( tr( "&Black" ), this );
219   act->setCheckable(true);
220   act->setData(NEGATIVE_BLACK);
221   act->setShortcut( Qt::ALT + Qt::Key_B );
222   negativeActGrp->addAction( act );
223   negativeMenu->addAction( act );
224   act->setChecked( true );
225   act = new QAction( tr( "Mark with &red color" ), this );
226   act->setCheckable(true);
227   act->setData(NEGATIVE_MARK_AS_RED);
228   act->setShortcut( Qt::ALT + Qt::Key_R );
229   negativeActGrp->addAction( act );
230   negativeMenu->addAction( act );
231   act = new QAction( tr( "Use &green color scale" ), this );
232   act->setCheckable(true);
233   act->setData(NEGATIVE_GREEN_SCALE);
234   act->setShortcut( Qt::ALT + Qt::Key_G );
235   negativeActGrp->addAction( act );
236   negativeMenu->addAction( act );
237   act = new QAction( tr( "Use &absolute values" ), this );
238   act->setCheckable(true);
239   act->setData(NEGATIVE_ABSOLUTE);
240   act->setShortcut( Qt::ALT + Qt::Key_A );
241   negativeActGrp->addAction( act );
242   negativeMenu->addAction( act );
243   connect( negativeActGrp, SIGNAL(triggered(QAction*)), pfsView, SLOT(setNegativeTreatment(QAction*)) );
244 
245   viewMenu->addSeparator();
246 
247   QMenu *colorCoordMenu = viewMenu->addMenu( "Color coo&rdinates" );
248   QActionGroup *colorCoordActGrp = new QActionGroup( this );
249   colorCoordActGrp->setExclusive( true );
250   act = new QAction( tr( "&RGB" ), this );
251   act->setCheckable(true);
252   act->setData(CC_RGB);
253   act->setShortcut( Qt::SHIFT + Qt::ALT + Qt::Key_R );
254   colorCoordActGrp->addAction( act );
255   colorCoordMenu->addAction( act );
256   act->setChecked( true );
257   act = new QAction( tr( "&XYZ" ), this );
258   act->setCheckable(true);
259   act->setData(CC_XYZ);
260   act->setShortcut( Qt::SHIFT + Qt::ALT + Qt::Key_X );
261   colorCoordActGrp->addAction( act );
262   colorCoordMenu->addAction( act );
263   act = new QAction( tr( "Y&u'v'" ), this );
264   act->setCheckable(true);
265   act->setData(CC_Yupvp);
266   act->setShortcut( Qt::SHIFT + Qt::ALT + Qt::Key_U );
267   colorCoordActGrp->addAction( act );
268   colorCoordMenu->addAction( act );
269   act = new QAction( tr( "Yx&y" ), this );
270   act->setCheckable(true);
271   act->setData(CC_Yxy);
272   act->setShortcut( Qt::SHIFT + Qt::ALT + Qt::Key_Y );
273   colorCoordActGrp->addAction( act );
274   colorCoordMenu->addAction( act );
275   connect( colorCoordActGrp, SIGNAL(triggered(QAction*)), this, SLOT(setColorCoord(QAction*)) );
276 
277 
278   QMenu *mappingMenu = menuBar()->addMenu( tr( "&Tone mapping" ) );
279 
280 
281   mappingMenu->addAction( "Increase exposure", lumRange,
282 			SLOT(increaseExposure()), Qt::Key_Minus );
283   mappingMenu->addAction( "Decrease exposure", lumRange,
284 			SLOT(decreaseExposure()), Qt::Key_Equal );
285   mappingMenu->addAction( "Extend dynamic range", lumRange,
286 			SLOT(extendRange()), Qt::Key_BracketRight );
287   mappingMenu->addAction( "Shrink dynamic range", lumRange,
288 			SLOT(shrinkRange()), Qt::Key_BracketLeft );
289   mappingMenu->addAction( "Fit to dynamic range", lumRange,
290 			SLOT(fitToDynamicRange()), Qt::Key_Backslash );
291   mappingMenu->addAction( "Low dynamic range", lumRange,
292 			SLOT(lowDynamicRange()), Qt::ALT + Qt::Key_L );
293 
294 
295   QMenu *mapfuncMenu = mappingMenu->addMenu( "&Mapping function" );
296   QActionGroup *mapfuncActGrp = new QActionGroup( this );
297   mapfuncActGrp->setExclusive( true );
298   mappingAct[0] = act = new QAction( tr( "&Linear" ), this );
299   act->setCheckable(true);
300   act->setData(0);
301   act->setShortcut( Qt::Key_L );
302   mapfuncActGrp->addAction( act );
303   mapfuncMenu->addAction( act );
304   mappingAct[1] = act = new QAction( tr( "Gamma 1.&4" ), this );
305   act->setCheckable(true);
306   act->setData(1);
307   act->setShortcut( Qt::Key_1 );
308   mapfuncActGrp->addAction( act );
309   mapfuncMenu->addAction( act );
310   mappingAct[2] = act = new QAction( tr( "Gamma 1.&8" ), this );
311   act->setCheckable(true);
312   act->setData(2);
313   act->setShortcut( Qt::Key_2 );
314   mapfuncActGrp->addAction( act );
315   mapfuncMenu->addAction( act );
316   mappingAct[3] = act = new QAction( tr( "Gamma 2.&2" ), this );
317   act->setCheckable(true);
318   act->setData(3);
319   act->setChecked( true );
320   act->setShortcut( Qt::Key_3 );
321   mapfuncActGrp->addAction( act );
322   mapfuncMenu->addAction( act );
323   mappingAct[4] = act = new QAction( tr( "Gamma 2.&6" ), this );
324   act->setCheckable(true);
325   act->setData(4);
326   act->setShortcut( Qt::Key_4 );
327   mapfuncActGrp->addAction( act );
328   mapfuncMenu->addAction( act );
329   mappingAct[5] = act = new QAction( tr( "L&ogarithmic" ), this );
330   act->setCheckable(true);
331   act->setData(5);
332   act->setShortcut( Qt::Key_O );
333   mapfuncActGrp->addAction( act );
334   mapfuncMenu->addAction( act );
335   connect( mapfuncActGrp, SIGNAL(triggered(QAction*)), this, SLOT(setLumMappingMethod(QAction*)) );
336 
337 
338   QMenu *helpMenu = menuBar()->addMenu( tr( "&Help" ) );
339   helpMenu->addAction( "&About", this, SLOT(showAboutdialog()) );
340 
341   colorCoord = CC_RGB;
342 
343   //Window should not be larger than desktop
344   // TODO: how to find desktop size - gnome taksbars
345 //  setMaximumSize( QApplication::desktop()->width(), QApplication::desktop()->height() );
346 
347   try {
348 
349     if( !readNextFrame() )
350       throw PFSViewException();
351 
352     if( window_min < window_max )
353       lumRange->setRangeWindowMinMax( window_min, window_max );
354 
355   } catch( pfs::Exception ex ) {
356     QMessageBox::critical( this, "pfsview error", ex.getMessage() );
357     throw PFSViewException();
358   }
359 
360 }
361 
362 
363 
364 /*
365 void PFSViewMainWin::changeClippingMethod( int clippingMethod )
366 {
367   for( int i = 0; i < CLIP_COUNT; i++ )
368     viewMenu->setItemChecked( clippingMethodMI[i], clippingMethod == i );
369 
370   statusBar()->message( viewMenu->text(clippingMethodMI[clippingMethod]), 1000 );
371 }
372 
373 void PFSViewMainWin::setInfNaNTreatment( int method )
374 {
375   infnanMenu->setItemChecked( infNaNMI[0], method == 0 );
376   infnanMenu->setItemChecked( infNaNMI[1], method == 1 );
377 }
378 
379 void PFSViewMainWin::setNegativeTreatment( int method )
380 {
381   for( int i = 0; i < NEGATIVE_COUNT; i++ )
382     negativeMenu->setItemChecked( negativeMI[i], method == i );
383 }
384 */
385 
setLumMappingMethod(int method)386 void PFSViewMainWin::setLumMappingMethod( int method )
387 {
388   mappingMethodCB->setCurrentIndex( method );
389   pfsView->setLumMappingMethod( method );
390   mappingAct[method]->setChecked( true );
391 }
392 
setLumMappingMethod(QAction * action)393 void PFSViewMainWin::setLumMappingMethod( QAction *action )
394 {
395   int method = action->data().toUInt();
396   mappingMethodCB->setCurrentIndex( method );
397   pfsView->setLumMappingMethod( method );
398 }
399 
400 struct ColorSpaceLabel
401 {
402   const char *comboBoxLabel;
403   const char *c1, *c2, *c3;
404 };
405 
406 static const ColorSpaceLabel scLabels[] = {
407   { "Color XYZ", "X", "Y", "Z" }
408 };
409 
410 int scLabelsCount = sizeof( scLabels ) / sizeof( ColorSpaceLabel );
411 
updatePointerValue()412 void PFSViewMainWin::updatePointerValue()
413 {
414   const PointerValue &pv = pfsView->getPointerValue();
415 
416   if( pv.valid ) {
417     char pointerValueStr[256];
418     const PointerValue &pv = pfsView->getPointerValue();
419     sprintf( pointerValueStr, "(x,y)=(%4d,%4d) ", pv.x, pv.y );
420     QString label( pointerValueStr );
421 
422     int channelSel = channelSelection->currentIndex();
423     if( pv.valuesUsed == 3 ) { // Color
424       assert( channelSel < scLabelsCount );
425 
426       pfs::Array2DImpl X(1,1), Y(1,1), Z(1,1);
427       X(0) = pv.value[0];
428       Y(0) = pv.value[1];
429       Z(0) = pv.value[2];
430 
431       const char *l1, *l2, *l3;
432       switch( colorCoord ) {
433       case CC_RGB:
434         l1 = "R"; l2 = "G"; l3 = "B";
435         pfs::transformColorSpace( pfs::CS_XYZ, &X, &Y, &Z, pfs::CS_RGB, &X, &Y, &Z );
436         break;
437       case CC_XYZ:
438         l1 = "X"; l2 = "Y"; l3 = "Z";
439         break;
440       case CC_Yupvp:
441         pfs::transformColorSpace( pfs::CS_XYZ, &X, &Y, &Z, pfs::CS_YUV, &X, &Y, &Z );
442         l1 = "Y"; l2 = "u'"; l3 = "v'";
443         break;
444       case CC_Yxy:
445         pfs::transformColorSpace( pfs::CS_XYZ, &X, &Y, &Z, pfs::CS_Yxy, &X, &Y, &Z );
446         l1 = "Y"; l2 = "x"; l3 = "y";
447         break;
448       case CC_COUNT:
449 	assert( 0 );
450 	break;
451       };
452 
453       sprintf( pointerValueStr, "%s=%07.4g %s=%07.4g %s=%07.4g",
454         l1, X(0),
455         l2, Y(0),
456         l3, Z(0)
457         );
458       label += pointerValueStr;
459       lumRange->showValuePointer( log10(pv.value[1]) );
460 
461     } else {                    // Single channel
462       const QString &name = channelSelection->itemText( channelSel );
463       sprintf( pointerValueStr, "%s=%07.4g", (const char*)name.toLatin1(), pv.value[0] );
464       label += pointerValueStr;
465       lumRange->showValuePointer( log10(pv.value[0]) );
466     }
467 
468     pointerPosAndVal->setText( label );
469   } else {
470     pointerPosAndVal->setText( "(?,?)" );
471     lumRange->hideValuePointer();
472   }
473 
474 
475 }
476 
477 
setColorCoord(QAction * action)478 void PFSViewMainWin::setColorCoord( QAction *action )
479 {
480   colorCoord = (ColorCoord)action->data().toUInt();
481   updatePointerValue();
482 }
483 
484 
485 
updateZoomValue()486 void PFSViewMainWin::updateZoomValue()
487 {
488   char strBuf[20];
489   sprintf( strBuf, "%d%%", (int)(pfsView->getZoom()*100) );
490   zoomValue->setText( strBuf );
491 }
492 
updateViewSize()493 void PFSViewMainWin::updateViewSize()
494 {
495 //  QSize sz = sizeHint();
496 //  printf( "main window: %d %d\n", sz.width(), sz.height() );
497 
498   centralWidget()->updateGeometry();
499   resize( sizeHint() );
500 }
501 
updateRangeWindow()502 void PFSViewMainWin::updateRangeWindow()
503 {
504   pfsView->setRangeWindow( pow( 10, lumRange->getRangeWindowMin() ), pow( 10, lumRange->getRangeWindowMax() ) );
505 
506   char ev_str[100];
507   sprintf( ev_str, "EV = %+.2g f-stops   %+.2g D   %.4g",  -lumRange->getRangeWindowMax()*log(2)/log(10),
508     -lumRange->getRangeWindowMax(), 1/pow( 10, lumRange->getRangeWindowMax() ) );
509   exposureValue->setText( ev_str );
510 }
511 
updateChannelSelection()512 void PFSViewMainWin::updateChannelSelection()
513 {
514   channelSelection->clear();
515 
516   const char *visibleChannel = pfsView->getVisibleChannel();
517 
518   if( pfsView->hasColorChannels() ) {
519     channelSelection->addItem( "Color XYZ" );
520     if( !strcmp( visibleChannel, "Color" ) )
521       channelSelection->setCurrentIndex( 0 );
522   }
523 
524   /*  channelSelection->insertItem( "Color RGB" );
525   channelSelection->insertItem( "Color XYZ" );
526   channelSelection->insertItem( "Color L*ab" );
527   channelSelection->insertItem( "Luminance Y" );*/
528 
529   QList<const char*> channelNames = pfsView->getChannels();
530   for( int c = 0; c < channelNames.size(); c++ ) {
531     channelSelection->addItem( channelNames[c] );
532     if( !strcmp( channelNames[c], visibleChannel ) )
533       channelSelection->setCurrentIndex( channelSelection->count()-1 );
534   }
535 
536 
537 }
538 
setChannelSelection(int selectedChannel)539 void PFSViewMainWin::setChannelSelection( int selectedChannel )
540 {
541   // If channel combo box has not been initialized yet
542   if( selectedChannel >= channelSelection->count() )
543     return;
544 
545   const QString &name = channelSelection->itemText( selectedChannel );
546 
547   if( pfsView->hasColorChannels() && selectedChannel < scLabelsCount ) {
548     pfsView->setVisibleChannel( NULL ); // Color
549   } else {
550     pfsView->setVisibleChannel( (const char*)name.toLatin1() );
551   }
552 
553   updateMenuEnable( );
554   lumRange->setHistogramImage(pfsView->getPrimaryChannel());
555 
556   updatePointerValue();
557 
558 }
559 
updateMenuEnable()560 void PFSViewMainWin::updateMenuEnable( )
561 {
562   bool color = pfsView->getVisibleChannel() == COLOR_CHANNELS;
563 
564 //  negativeMenu->setItemEnabled( negativeMI[NEGATIVE_GREEN_SCALE], !color );
565 //  negativeMenu->setItemEnabled( negativeMI[NEGATIVE_ABSOLUTE], !color );
566 }
567 
568 
showAboutdialog()569 void PFSViewMainWin::showAboutdialog()
570 {
571    QMessageBox::information( this, "pfsview",
572      "pfsview " PACKAGE_VERSION "\n"
573      "Copyright (C) 2005-2011 Rafal Mantiuk\n\n"
574      "See the manual page (man pfsview) and\n"
575      "the web page (http://pfstools.sourceforge.net/)\n"
576      "for more information"
577      );
578 
579 }
580 
581 // ======================= Loading next frame ==========================
582 
583 /**
584  * GUI action -> goto next frame
585  * handle error and eof of frame
586  */
gotoNextFrame()587 void PFSViewMainWin::gotoNextFrame()
588 {
589   try {
590     if( !readNextFrame() ) {
591       statusBar()->showMessage( "No more frames", 1000 );
592     }
593   }
594   catch( pfs::Exception ex )
595     {
596       // Display message and keep the old frame
597       QMessageBox::critical( this, "pfsview error", ex.getMessage() );
598       qApp->quit();
599     }
600 }
601 
gotoPreviousFrame()602 void PFSViewMainWin::gotoPreviousFrame()
603 {
604   currentFrame++;
605   if( currentFrame == frameList.end() ) {
606     currentFrame--;
607     statusBar()->showMessage( "No more frames in buffer (buffer holds max 5 frames)", 1000 );
608     return;
609   }
610 
611   setFrame( *currentFrame );
612 }
613 
setFrame(pfs::Frame * frame)614 void PFSViewMainWin::setFrame( pfs::Frame *frame )
615 {
616   QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
617 
618 
619   pfsView->setFrame( frame );
620 
621   lumRange->setHistogramImage(pfsView->getPrimaryChannel());
622 
623   updateChannelSelection();
624   updateZoomValue();
625 
626   const char *luminanceTag = frame->getTags()->getString( "LUMINANCE" );
627   if( luminanceTag != NULL && !strcmp( luminanceTag, "DISPLAY" ) ) {
628     setLumMappingMethod(0);
629     lumRange->lowDynamicRange();
630   }
631 
632   updateRangeWindow();
633 
634   // Set the window caption to the name of the frame, if it is given
635   const char *fileName = frame->getTags()->getString( "FILE_NAME" );
636   QString qLuminance( "" );
637   if( luminanceTag!=NULL )
638     qLuminance=QString(" (") + QString(luminanceTag) + QString(")");
639   if( fileName == NULL )
640     setWindowTitle( QString( "pfsview" ) );
641   else {
642     QString qFileName( fileName );
643     if( qFileName.length() > 30 )
644       setWindowTitle( QString( "pfsview: ... " ) + QString( fileName ).right( 30 ) + qLuminance);
645     else
646       setWindowTitle( QString( "pfsview: " ) + QString( fileName ) + qLuminance);
647   }
648   updateMenuEnable( );
649 
650 
651   QApplication::restoreOverrideCursor();
652 }
653 
654 /**
655  * Load next frame from the stream.
656  * @return false if there are no more frames
657  */
readNextFrame()658 bool PFSViewMainWin::readNextFrame()
659 {
660   if( currentFrame != frameList.begin() )
661   {
662     currentFrame--;
663     setFrame( *currentFrame );
664     return true;
665   }
666 
667   pfs::DOMIO pfsCtx;
668   pfs::Frame *newFrame;
669   newFrame = pfsCtx.readFrame( stdin );
670   if( newFrame == NULL ) return false; // No more frames
671 
672   if( frameList.size() == MAX_FRAMES_IN_MEMORY ) {
673     // Remove one frame from the back
674     pfsCtx.freeFrame( frameList.back() );
675     frameList.pop_back();
676   }
677   frameList.push_front( newFrame );
678   currentFrame = frameList.begin();
679 
680   setFrame( newFrame );
681   return true;
682 }
683 
684 // ======================= Saving image ================================
685 
saveImage()686 void PFSViewMainWin::saveImage()
687 {
688   QString fileName = QFileDialog::getSaveFileName( this, "Save current view as...", "./", "*.png; *.jpg" );
689   QByteArray jpeg_format( "jpg" );
690   QByteArray png_format( "png" );
691 
692   if ( !fileName.isNull() ) {                 // got a file name
693 
694     QList<QByteArray> file_formats = QImageWriter::supportedImageFormats();
695     QList<QByteArray>::iterator it;
696     QByteArray *format = NULL;
697     for( it = file_formats.begin(); it != file_formats.end(); it++ ) {
698       if( fileName.endsWith( (const char*)*it ) ) {
699         format = &(*it);
700         break;
701       }
702     }
703     if( format == NULL ) {
704       int sel = QMessageBox::question( this, "Select image format", "The file name is missing extension or the extension is not recongnized as an image format. Choose the image format.", "Cancel", "JPEG image", "PNG image" );
705       if( sel == 1 ) {
706         format = &jpeg_format;
707         fileName = fileName + ".jpg";
708       } else if( sel == 2 ) {
709         format = &png_format;
710         fileName = fileName + ".png";
711       }
712     }
713 
714     if( format != NULL ) {
715 
716       QImage *image = pfsView->getDisplayedImage();
717 
718       if( !image->save( fileName, format->data(), -1 ) )
719       {
720         QMessageBox::warning( this, "Saving image problem", "Cannot save image " + fileName );
721       }
722       else
723         statusBar()->showMessage( fileName + QString( "' saved as " ) + QString( format->data() ) + QString( " image" ), 4000 );
724     }
725 
726   }
727 }
728 
copyImage()729 void PFSViewMainWin::copyImage()
730 {
731   QClipboard *cb = QApplication::clipboard();
732 
733   const QImage *image = pfsView->getDisplayedImage();
734   cb->setImage( *image, QClipboard::Clipboard );
735   statusBar()->showMessage( "Image copied to clipboard", 2000 );
736 }
737 
738 
739 // ======================= main and command line =======================
740 
printUsage()741 void printUsage()
742 {
743     fprintf( stderr,
744       "Usage: " PROGNAME " [options]\n"
745       "\n"
746       "Displays high-dynamic range image in pfs format. The image is read from the "
747       "standard output.\n"
748       "\n"
749       "options:\n"
750       "\t--help                        : prints this message\n"
751       "\t--window_min log_lum          : lower bound of hdr display window, given as "
752       "a log10 of luminance.\n"
753       "\t--window_min log_lum          : the same as above, but the upper bound\n"
754       );
755 }
756 
errorCheck(bool condition,const char * string)757 static void errorCheck( bool condition, const char *string )
758 {
759     if( !condition ) {
760 	fprintf( stderr, PROGNAME " error: %s\n", string );
761 	abort();
762     }
763 }
764 
main(int argc,char ** argv)765 int main(int argc, char** argv)
766 {
767     QApplication app(argc, argv);
768     //    qApp = &app;
769 
770     float window_min = 0, window_max = 0;
771 
772     for( int i=1 ; i<argc ; i++ )
773     {
774       if( !strcmp( argv[i], "--help") ) {
775         printUsage();
776         return 0;
777       }
778       else if( !strcmp( argv[i], "--window_min") ) {
779         i++;
780         errorCheck( i < argc, "expected parameter after --window_min" );
781         char *checkPtr;
782         window_min = (float)strtod( argv[i], &checkPtr );
783         errorCheck( checkPtr != NULL && checkPtr[0] == 0 &&
784           window_min < 100 && window_min > -100,
785           "--window_min expects floating point value between -100 and 100" );
786       }
787       else if( !strcmp( argv[i], "--window_max") ) {
788         i++;
789         errorCheck( i < argc, "expected parameter after --window_max" );
790         char *checkPtr;
791         window_max = (float)strtod( argv[i], &checkPtr );
792         errorCheck( checkPtr != NULL && checkPtr[0] == 0 &&
793           window_max < 100 && window_max > -100,
794           "--window_max expects floating point value between -100 and 100" );
795       } else {
796         fprintf( stderr, PROGNAME " error: not recognized parameter '%s'\n", argv[i] );
797         printUsage();
798         return -1;
799       }
800     }
801 
802     errorCheck( window_min <= window_max, "window_min must be less than window_max" );
803 
804 
805     try {
806 
807       PFSViewMainWin pfsViewMW( window_min, window_max );
808 //      app.setMainWidget(&pfsViewMW);
809       pfsViewMW.show();
810       pfsViewMW.updateViewSize();
811 
812       return app.exec();
813 
814     }
815     catch( PFSViewException ex )
816     {
817       QMessageBox::critical( NULL, "pfsview error", "Can not open the first PFS frame. Quitting." );
818       return -1;
819     }
820 
821 
822 }
823 
824