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