TonicTones
|
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