TonicTones
Src/TonicTones.cpp
00001 //      TonicTones.cpp
00002 //      
00003 //      Copyright 2010 Jérémy Laumon <jeremy.laumon@gmail.com>
00004 //      
00005 //      This program is free software; you can redistribute it and/or modify
00006 //      it under the terms of the GNU General Public License as published by
00007 //      the Free Software Foundation; either version 2 of the License, or
00008 //      (at your option) any later version.
00009 //      
00010 //      This program is distributed in the hope that it will be useful,
00011 //      but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 //      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 //      GNU General Public License for more details.
00014 //      
00015 //      You should have received a copy of the GNU General Public License
00016 //      along with this program; if not, write to the Free Software
00017 //      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
00018 //      MA 02110-1301, USA.
00019 
00020 
00021 #include <QtGui>
00022 #include <TonicTones.h>
00023 #include <Exceptions.h>
00024 #include <iostream>
00025 
00092 TonicTones::TonicTones(QWidget *parent) : 
00093     QMainWindow(parent),
00094     inputImage(NULL),
00095     outputImage(NULL),
00096     gamma(1.0),
00097     gammaEnabled(true),
00098     operatorEnabled(true),
00099     loaderManager(*ImageLoaderManager::instance()),
00100     operatorManager(*ToneMappingOperatorManager::instance())
00101 {
00102     xyzToRgbMatrix[0][0]= 2.5651; xyzToRgbMatrix[0][1]=-1.1665; xyzToRgbMatrix[0][2]=-0.3986;
00103     xyzToRgbMatrix[1][0]=-1.0217; xyzToRgbMatrix[1][1]= 1.9777; xyzToRgbMatrix[1][2]= 0.0439;
00104     xyzToRgbMatrix[2][0]= 0.0753; xyzToRgbMatrix[2][1]=-0.2543; xyzToRgbMatrix[2][2]= 1.1892;
00105     
00106     setupUi(this);
00107     
00108     scrollArea = new ImageScrollArea;
00109     displayAreaLayout->addWidget(scrollArea);
00110     
00111     zoomLabel = new QLabel;
00112     statusbar->addPermanentWidget(zoomLabel, 1);
00113     
00114     operatorLabel = new QLabel;
00115     statusbar->addPermanentWidget(operatorLabel, 2);
00116     
00117     conversionTimeLabel = new QLabel;
00118     statusbar->addPermanentWidget(conversionTimeLabel, 1);
00119     
00120     displayTimeLabel = new QLabel;
00121     statusbar->addPermanentWidget(displayTimeLabel, 1);
00122 
00123     
00124     connect(operatorComboBox, SIGNAL(currentIndexChanged(const QString&)), 
00125             this, SLOT(updateOperator(const QString&)));
00126     connect(scrollArea, SIGNAL(scaleChanged(double)), this, SLOT(updateZoom(double)));
00127     connect(actionOpen, SIGNAL(triggered()), this, SLOT(open()));
00128     connect(actionScreenColors, SIGNAL(triggered()), this, SLOT(openScreenColorsDialog()));
00129     connect(operatorGroupBox, SIGNAL(toggled(bool)), this, SLOT(enableOperator(bool)));
00130     connect(gammaGroupBox, SIGNAL(toggled(bool)), this, SLOT(enableGamma(bool)));
00131     connect(gammaSlider, SIGNAL(sliderReleased()), this, SLOT(displayImage()));
00132     connect(gammaSlider, SIGNAL(valueChanged(int)), this, SLOT(updateGamma(int)));
00133 
00134     loaderManager.registerLoaders("Loaders");
00135     
00136     QStringList operatorList = operatorManager.registerOperators("Operators");
00137     operatorComboBox->addItems(operatorList);
00138     
00139     
00140     if (loaderManager.empty())
00141     {
00142         qFatal("No image loader found! TonicTones will not be able to open images! Exiting now.");
00143     }
00144     if (operatorManager.empty())
00145     {
00146         qWarning("No tone mapping operator found. Satisfaction not guaranteed.");
00147         operatorGroupBox->hide();
00148         operatorOptionsGroupBox->hide();
00149     }
00150 }
00151 
00155 void TonicTones::open()
00156 {
00157     QString fileName = QFileDialog::getOpenFileName(this);
00158     
00159     try
00160     {
00161         if (!fileName.isEmpty())
00162         {
00163             HdrImage* newImage = new HdrImage(fileName); // may throw an exception
00164             delete inputImage;
00165             inputImage = newImage;
00166             
00167             if(operatorManager.getActiveOperator())
00168             {
00169                 operatorManager.getActiveOperator()->setImage(inputImage);
00170             }
00171             else
00172             {
00173                 operatorEnabled = false;
00174                 updateImage();
00175             }
00176             
00177             setWindowTitle(QString("TonicTones - %1").arg(QDir(fileName).dirName()));
00178                 
00179             scrollArea->scaleImage(1.0, false); // set zoom 100%
00180         }
00181     }
00182     catch(const Exception& e)
00183     {
00184         qWarning() << e.what();
00185     }
00186 }
00187 
00193 void TonicTones::updateImage()
00194 {
00195     const HdrImage* image;
00196     if(operatorEnabled)
00197         image = operatorManager.getActiveOperator()->getToneMappedImage();
00198     else
00199         image = inputImage;
00200     if (image)
00201     {
00202         QTime t;
00203         t.start();
00204         
00205         delete outputImage;
00206         try
00207         {    
00208             outputImage = image->toRgb(xyzToRgbMatrix);
00209         }
00210         catch(const Exception& e)
00211         {
00212             qWarning() << e.what();
00213         }
00214         conversionTimeLabel->setText(tr("Transformation to RGB: %1 ms").arg(t.elapsed()));
00215         displayImage();
00216     }
00217 }
00218 
00222 void TonicTones::displayImage() const
00223 {
00224     if (outputImage)
00225     {
00226         QTime t;
00227         t.start();
00228         
00229         QSize size = outputImage->size();
00230         int width = size.width();
00231         int height = size.height();
00232         int pixels[width*height];
00233         
00234         if(gammaEnabled and gamma!=1.0)
00235         {
00236             double gamma_corr = 1.0/gamma;
00237             for(int i=0; i<height; ++i)
00238                 for(int j=0; j<width; ++j)
00239                 {
00240                     Color p = (*outputImage)[i][j].clamp();
00241                     pixels[i*width+j] = qRgb(pow(p[0],gamma_corr)*255.0,pow(p[1],gamma_corr)*255.0,pow(p[2],gamma_corr)*255.0);
00242                 }
00243         }
00244         else
00245         {
00246             for(int i=0; i<height; ++i)
00247                 for(int j=0; j<width; ++j)
00248                 {
00249                     Color p = (*outputImage)[i][j].clamp();
00250                     pixels[i*width+j] = qRgb(p[0]*255.0,p[1]*255.0,p[2]*255.0);
00251                 }
00252         }
00253         QImage ldrImage((uchar*)pixels, width, height, QImage::Format_RGB32);
00254 
00255         scrollArea->image()->setPixmap(QPixmap::fromImage(ldrImage));
00256         displayTimeLabel->setText(tr("Display: %1 ms").arg(t.elapsed()));
00257     }
00258 }
00259 
00263 void TonicTones::updateZoom(double scaleFactor)
00264 {
00265     zoomLabel->setText(tr("Zoom: %1 %").arg(scaleFactor*100, 0, 'f', 0));
00266 }
00267 
00271 void TonicTones::updateOperator(const QString& operatorName)
00272 {
00273     qDebug("%s selected.", operatorName.toStdString().c_str());
00274 
00275     // set new operator
00276     operatorManager.setActiveOperator(operatorName);
00277     
00278     // remove previous operator ui
00279     QLayoutItem* item = operatorOptionsGroupBox->layout()->itemAt(0);
00280     operatorOptionsGroupBox->layout()->removeItem(item);
00281     if(item)
00282         item->widget()->close();
00283     
00284     // add new operator ui
00285     QWidget *wrapper = new QWidget;
00286     wrapper->setAttribute(Qt::WA_DeleteOnClose);
00287     operatorOptionsGroupBox->layout()->addWidget(wrapper);
00288     operatorManager.getActiveOperator()->setupUi(wrapper);
00289 
00290     connect(operatorManager.getActiveOperator().data(),SIGNAL(imageUpdated()),
00291             this, SLOT(updateImage()));
00292     connect(operatorManager.getActiveOperator().data(),SIGNAL(message(const QString&)),
00293             operatorLabel, SLOT(setText(const QString&)));
00294     // if an image is already loaded, send it to the new operator
00295     if (inputImage)
00296     {
00297         operatorManager.getActiveOperator()->setImage(inputImage);
00298     }
00299 }
00300 
00304 void TonicTones::updateGamma(int value)
00305 {
00306     gamma = float(value)/100.0;
00307     gammaValue->setText(QString("%1").arg(gamma, 0, 'f', 2));
00308 }
00309 
00316 void TonicTones::enableOperator(bool enabled)
00317 {
00318     operatorEnabled = enabled;
00319     operatorOptionsGroupBox->setEnabled(enabled);
00320     updateImage();
00321 }
00322 
00329 void TonicTones::enableGamma(bool enabled)
00330 {
00331     gammaEnabled = enabled;
00332     displayImage();
00333 }
00334 
00335 
00339 void TonicTones::openScreenColorsDialog()
00340 {
00341     QDialog dialog;
00342     uiScreenColors.setupUi(&dialog);
00343     
00344     // invert XYZ to RGB matrix to get RGB to XYZ
00345     QMatrix4x4 m;
00346     for (int i=0; i<3; ++i)
00347         for(int j=0; j<3; ++j)
00348             m(i,j) = xyzToRgbMatrix[i][j];
00349     m = m.inverted();
00350     // set ui spin boxes values
00351     uiScreenColors.m00->setValue(m(0,0)); uiScreenColors.m01->setValue(m(0,1)); uiScreenColors.m02->setValue(m(0,2));
00352     uiScreenColors.m10->setValue(m(1,0)); uiScreenColors.m11->setValue(m(1,1)); uiScreenColors.m12->setValue(m(1,2)); 
00353     uiScreenColors.m20->setValue(m(2,0)); uiScreenColors.m21->setValue(m(2,1)); uiScreenColors.m22->setValue(m(2,2)); 
00354     
00355     connect(&dialog, SIGNAL(accepted()), this, SLOT(updateScreenColors()));
00356     dialog.exec();
00357 }
00358 
00364 void TonicTones::updateScreenColors()
00365 {
00366     // get ui spin boxes values and
00367     // invert RGB to XYZ matrix to get XYZ to RGB
00368     QMatrix4x4 m = QMatrix4x4(uiScreenColors.m00->value(), uiScreenColors.m01->value(), uiScreenColors.m02->value(), 0.0,
00369                               uiScreenColors.m10->value(), uiScreenColors.m11->value(), uiScreenColors.m12->value(), 0.0,
00370                               uiScreenColors.m20->value(), uiScreenColors.m21->value(), uiScreenColors.m22->value(), 0.0,
00371                               0.0,             0.0,             0.0            , 1.0).inverted();
00372     for (int i=0; i<3; ++i)
00373         for(int j=0; j<3; ++j)
00374             xyzToRgbMatrix[i][j] = m(i,j);
00375             
00376     updateImage();
00377 }
00378 
 All Classes Functions Variables