Main Page   Compound List   File List   Compound Members   File Members  

photoEditWidget.cpp

Go to the documentation of this file.
00001 //==============================================
00002 //  copyright            : (C) 2003 by Will Stokes
00003 //==============================================
00004 //  This program is free software; you can redistribute it 
00005 //  and/or modify it under the terms of the GNU General 
00006 //  Public License as published by the Free Software 
00007 //  Foundation; either version 2 of the License, or  
00008 //  (at your option) any later version.         
00009 //
00010 //  As a special exception, Will Stokes gives permission to 
00011 //  link this program with Qt non-commercial edition, and 
00012 //  distribute the resulting executable, without including the 
00013 //  source code for the Qt non-commercial edition in the 
00014 //  source distribution. 
00015 //==============================================
00016 
00017 //Systemwide includes
00018 #include <qlayout.h>
00019 #include <qlabel.h>
00020 #include <qfont.h>
00021 #include <qframe.h>
00022 #include <qpushbutton.h>
00023 #include <qpixmap.h>
00024 #include <qimage.h>
00025 #include <qlineedit.h>
00026 #include <qcursor.h>
00027 #include <qapplication.h>
00028 #include <math.h>
00029 
00030 //Projectwide includes
00031 #include "photoEditWidget.h"
00032 #include "photoViewWidget.h"
00033 #include "questionDialog.h"
00034 #include "../backend/photo.h"
00035 #include "layoutWidget.h"
00036 #include "../config.h"
00037 #include "../backend/imageTools.h"
00038 
00039 #include <iostream.h>
00040 //==============================================
00041 PhotoEditWidget::PhotoEditWidget(QWidget *parent, 
00042                                                 const char* name ) : 
00043                                                 QWidget(parent,name)
00044 {
00045   //set photo pointer to null by default
00046   photo = NULL;
00047   
00048   //backup image null by default
00049   originalImage = NULL;
00050   
00051   //store layout pointer
00052   layout = (LayoutWidget*)parent;
00053   
00054   //create photo description labels
00055   photoDescription = new QLabel( this );
00056   photoDescription->setText( "Photo Description:" );
00057   photoDescription->setFont( QFont( "Times", 12, QFont::Bold ) );
00058   photoDescriptionVal = new QLineEdit( this );
00059   photoDescriptionVal->setFont( QFont( "Times", 12, QFont::Bold ) );
00060   connect( photoDescriptionVal, SIGNAL(textChanged(const QString&)),
00061            SLOT(updateDescription(const QString&)) );
00062 
00063   //create photo label
00064   photoView = new PhotoViewWidget( this, "photo view" );
00065   
00066   //create buttons  
00067   buttons = new QFrame(this);
00068     
00069   adjExposureImage = new QPixmap( QString(IMAGE_PATH)+"adjExposure.png" );
00070   adjExposureButton = new QPushButton( buttons );
00071   adjExposureButton->setPixmap( *adjExposureImage );
00072   adjExposureButton->setEnabled(false);
00073   connect( adjExposureButton, SIGNAL(clicked()), SLOT(adjustExposure()) );
00074   
00075   cropImage = new QPixmap( QString(IMAGE_PATH)+"crop.png" );
00076   cropButton = new QPushButton( buttons );
00077   cropButton->setPixmap( *cropImage );
00078   connect( cropButton, SIGNAL(clicked()), SLOT(cropToRegion()) );
00079 
00080   invertImage = new QPixmap( QString(IMAGE_PATH)+"invert.png" );
00081   invertButton = new QPushButton( buttons );
00082   invertButton->setPixmap( *invertImage );
00083   connect( invertButton, SIGNAL(clicked()), SLOT(invertSelection()) );
00084   
00085   redEyeImage = new QPixmap( QString(IMAGE_PATH)+"removeRedEye.png" );
00086   redEyeButton = new QPushButton( buttons );
00087   redEyeButton->setPixmap( *redEyeImage );
00088   redEyeButton->setEnabled(false);
00089   connect( redEyeButton, SIGNAL(clicked()), SLOT(reduceRedeye()) );
00090   
00091   resetImage = new QPixmap( QString(IMAGE_PATH)+"reset.png" );
00092   resetButton = new QPushButton( buttons );
00093   resetButton->setPixmap( *resetImage );
00094   connect( resetButton, SIGNAL(clicked()), SLOT(resetImageAction()) );
00095     
00096   returnImage = new QPixmap( QString(IMAGE_PATH)+"return.png" );
00097   returnButton = new QPushButton( buttons );
00098   returnButton->setPixmap( *returnImage );
00099   connect( returnButton, SIGNAL(clicked()), SLOT(returnFromEdit()) );
00100   
00101   //place all items in grid layout
00102   grid = new QGridLayout( this, 3, 3, 0 );
00103   grid->addWidget( photoDescription, 0, 0, Qt::AlignLeft );
00104   grid->addWidget( photoDescriptionVal, 0, 1 );
00105   grid->addMultiCellWidget( photoView, 1, 1, 0, 2, Qt::AlignCenter );
00106 
00107   grid2 = new QGridLayout( buttons, 1, 8, 0 );
00108   grid2->addWidget( adjExposureButton, 0, 1, Qt::AlignLeft );
00109   grid2->addWidget( cropButton, 0, 2, Qt::AlignLeft );
00110   grid2->addWidget( invertButton, 0, 3, Qt::AlignLeft );
00111   grid2->addWidget( redEyeButton, 0, 4, Qt::AlignLeft );
00112   grid2->addWidget( resetButton, 0, 5, Qt::AlignLeft );
00113   grid2->addWidget( returnButton, 0, 6, Qt::AlignLeft );
00114   
00115   //set first and last column of button grid to take up remaining space
00116   grid2->setColStretch( 0, 1 );
00117   grid2->setColStretch( 7, 1 );
00118   grid->addMultiCellWidget( buttons, 2, 2, 0, 2 );
00119   
00120   //set the last column to stretch to fill any blank space
00121   grid->setColStretch( 2, 1 );
00122 
00123   //set photo row to stretch to fill any blank space
00124   grid->setRowStretch( 1, 1 );
00125    
00126   //Set the second column, the actual photo description
00127   //to have a minimum width
00128   grid->addColSpacing(1, 300 );
00129   
00130   //set the background of the widget to be white
00131   setPaletteBackgroundColor( QColor(255, 255, 255) );
00132 }
00133 //==============================================
00134 PhotoEditWidget::~PhotoEditWidget()
00135 {
00136   delete originalImage;
00137   originalImage = NULL;
00138 }
00139 //==============================================
00140 void PhotoEditWidget::setPhoto(Photo* photo)
00141 {
00142   //store photo object handle
00143   this->photo = photo;
00144   
00145   //delete old original photo
00146   delete originalImage;
00147   originalImage = NULL;
00148   
00149   //set description
00150   photoDescriptionVal->setText( photo->getDescription() );
00151 
00152   //update view of photo
00153   photoView->setPhoto(photo);
00154 
00155   //check to see if slideshow dimensions are known, 
00156   //if not load image now and compute size, set
00157   //dimensions in photo object for future use
00158   if(photo->actualSlideshowWidth() == -1)
00159   {
00160     QImage *backendImage = new QImage( photo->getImageFilename() );
00161     
00162     int w, h;   
00163     resizeImage( backendImage->width(), backendImage->height(),
00164                       SLIDESHOW_WIDTH, SLIDESHOW_HEIGHT,
00165                       w, h);
00166     photo->setActualSlideshowDimensions( w, h);    
00167     delete backendImage;
00168     backendImage = NULL;
00169   }
00170 }
00171 //==============================================
00172 void PhotoEditWidget::updateDescription( const QString& val )
00173 {
00174   if(photo != NULL)
00175     photo->setDescription(val);
00176 }
00177 //==============================================
00178 void PhotoEditWidget::adjustExposure()
00179 {
00180   //---------------------------------
00181   //load the full image
00182   QImage *backendImage = new QImage( photo->getImageFilename() );
00183   //---------------------------------------------
00184   //find mean and max log of luminance
00185   double delta = 0.00001;
00186   double max = 0.0;
00187   double sum = 0.0;
00188   double tmp, tmp2;
00189   int x;
00190   for(x=0; x<backendImage->width(); x++)
00191   {
00192     int y;
00193     for(y=0; y<backendImage->height(); y++)
00194     {
00195       QRgb val = backendImage->pixel( x, y );
00196       tmp = qGray(val);
00197       tmp = tmp / 255.0;
00198       tmp2 = log( tmp + delta );
00199       
00200       sum+=tmp2;
00201       if(tmp > max)
00202         max = tmp;
00203     }
00204   }
00205   double avg = exp(sum/(backendImage->width() * backendImage->height()));
00206   cout << "max: " << max << endl;
00207   cout << "avg: " << avg << endl;
00208   double whiteSquared = max*max;
00209   //---------------------------------------------
00210   //create new image and fill in adjusted values
00211   QImage* equalizedImage = new QImage(backendImage->width(),
00212                                                             backendImage->height(),
00213                                                             backendImage->depth());
00214   double r, g, b;
00215   double alpha = 0.18;
00216   for(x=0; x<backendImage->width(); x++)
00217   {
00218     int y;
00219     for(y=0; y<backendImage->height(); y++)
00220     {        
00221         QRgb val = backendImage->pixel( x, y );
00222         r = qRed(val); g = qGreen(val); b = qBlue(val);
00223         r = r/255.0; g = g/255.0; b = b/255.0;
00224         
00225         r = (alpha/avg) * r;
00226         g = (alpha/avg) * g;
00227         b = (alpha/avg) * b;
00228 
00229         r = (r * (1 + (r / whiteSquared))) / (1 + r);
00230         g = (g * (1 + (g / whiteSquared))) / (1 + g);
00231         b = (b * (1 + (b / whiteSquared))) / (1 + b);
00232                 
00233         r = r*255; g = g*255; b = b*255;
00234         val = qRgb( (int)r, (int)g, (int)b );
00235         
00236         equalizedImage->setPixel( x, y, val );
00237     }
00238   }
00239   //---------------------------------
00242   if(originalImage == NULL)
00243   {
00244     originalImage = backendImage;
00245   }
00246   else
00247   {
00248     delete backendImage;
00249     backendImage = NULL;
00250   }
00251   //---------------------------------  
00253   photo->setImage(equalizedImage);
00254   
00255   //set image function deletes object once done so remove pointer to memory
00256   //we don't own any more
00257   equalizedImage = NULL;
00258   //---------------------------------
00260   photoView->setPhoto( photo );
00261   //---------------------------------
00262 }
00263 //==============================================
00264 void PhotoEditWidget::cropToRegion()
00265 {
00266   //---------------------------------
00267   //first get selected coordinates
00268   QPoint topLeft, bottomRight;
00269   photoView->getSelection(topLeft, bottomRight);
00270   
00271   //if selection has no width or height then bail immediately
00272   if(bottomRight.x() - topLeft.x() == 0 ||
00273      bottomRight.y() - topLeft.y() == 0)
00274    {
00275      //scold user for not selection part of the image
00276      
00277      return;
00278    }
00279   //---------------------------------
00280   //load the full image
00281   QImage *backendImage = new QImage( photo->getImageFilename() );
00282   //---------------------------------  
00283   //scale the selected coordinates up into actual full size coordinates
00284   float widthRatio = (1.0f * backendImage->width()) / photo->actualSlideshowWidth();
00285   float heightRatio = (1.0f * backendImage->height()) / photo->actualSlideshowHeight();
00286    
00287   //if user selected to end of photo fix selection to end rather
00288   //then scale and get numerical error which prevents selection
00289   //from running to edge ot photo
00290   if(bottomRight.x() == (photo->actualSlideshowWidth() - 1))
00291     bottomRight.setX( backendImage->width() - 1 );
00292   else
00293     bottomRight.setX( bottomRight.x() * widthRatio );
00294   
00295   if(bottomRight.y() == (photo->actualSlideshowHeight() - 1))
00296     bottomRight.setY( backendImage->height() - 1 );
00297   else
00298     bottomRight.setY( bottomRight.y() * heightRatio );
00299   //---------------------------------        
00300   //create new fullsize image
00301   QImage* croppedImage = new QImage(bottomRight.x() - topLeft.x() + 1,
00302                                                         bottomRight.y() - topLeft.y() + 1,
00303                                                         backendImage->depth());
00304   int newX, newY;
00305   newX = 0;
00306   
00307   int x;
00308   for(x=topLeft.x(); x<= bottomRight.x(); x++)
00309   {
00310     newY = 0;
00311     int y;
00312     for(y=topLeft.y(); y<= bottomRight.y(); y++)
00313     {
00314       croppedImage->setPixel( newX, newY, backendImage->pixel(x, y) );
00315       newY++;
00316     }
00317     newX++;
00318   }
00319   //---------------------------------
00322   if(originalImage == NULL)
00323   {
00324     originalImage = backendImage;
00325   }
00326   else
00327   {
00328     delete backendImage;
00329     backendImage = NULL;
00330   }
00331   //---------------------------------  
00333   photo->setImage(croppedImage);
00334   
00335   //set image function deletes object once done so remove pointer to memory
00336   //we don't own any more
00337   croppedImage = NULL;
00338   //---------------------------------
00340   photoView->setPhoto( photo );
00341   //---------------------------------
00342 }
00343 //==============================================
00344 void PhotoEditWidget::invertSelection()
00345 {
00346   //---------------------------------
00347   //first get selected coordinates
00348   QPoint topLeft, bottomRight;
00349   photoView->getSelection(topLeft, bottomRight);
00350   //---------------------------------
00351   //load the full image
00352   QImage *backendImage = new QImage( photo->getImageFilename() );
00353   //---------------------------------  
00354   //if selected region is empty then invert entire image
00355   if(bottomRight.x() - topLeft.x() == 0 ||
00356      bottomRight.y() - topLeft.y() == 0)
00357   {
00358     topLeft.setX(0);
00359     topLeft.setY(0);
00360     bottomRight.setX(backendImage->width() - 1);
00361     bottomRight.setY(backendImage->height() - 1);
00362   }
00363   //else scale the selected coordinates up into actual full size coordinates
00364   else
00365   {
00366     float widthRatio = (1.0f * backendImage->width()) / photo->actualSlideshowWidth();
00367     float heightRatio = (1.0f * backendImage->height()) / photo->actualSlideshowHeight();
00368    
00369     topLeft.setX( topLeft.x() * widthRatio );
00370     topLeft.setY( topLeft.y() * heightRatio );
00371     
00372     //if user selected to end of photo fix selection to end rather
00373     //then scale and get numerical error which prevents selection
00374     //from running to edge ot photo
00375     if(bottomRight.x() == (photo->actualSlideshowWidth() - 1))
00376       bottomRight.setX( backendImage->width() - 1 );
00377     else
00378       bottomRight.setX( bottomRight.x() * widthRatio );
00379     
00380     if(bottomRight.y() == (photo->actualSlideshowHeight() - 1))
00381       bottomRight.setY( backendImage->height() - 1 );
00382     else
00383       bottomRight.setY( bottomRight.y() * heightRatio );
00384   }
00385   //---------------------------------        
00386   //invert image
00387   QImage* invertedImage = new QImage(backendImage->width(),
00388                                      backendImage->height(),
00389                                      backendImage->depth());
00390   int x;
00391   for(x=0; x<backendImage->width(); x++)
00392   {
00393     int y;
00394     for(y=0; y<backendImage->height(); y++)
00395     {
00396       //if within selected region invert color
00397       if( x >= topLeft.x() && x <= bottomRight.x() &&
00398           y >= topLeft.y() && y <= bottomRight.y())
00399       {
00400         QRgb val = backendImage->pixel( x, y );
00401         val = qRgb( 255 - qRed(val), 255 - qGreen(val), 255 - qBlue(val) );
00402         invertedImage->setPixel( x, y, val );
00403       }
00404       //otherwise copy color
00405       else
00406       {
00407         invertedImage->setPixel( x, y, backendImage->pixel(x, y) );
00408       }
00409     }
00410   }
00411   //---------------------------------
00414   if(originalImage == NULL)
00415   {
00416     originalImage = backendImage;
00417   }
00418   else
00419   {
00420     delete backendImage;
00421     backendImage = NULL;
00422   }
00423   //---------------------------------  
00425   photo->setImage(invertedImage);
00426   
00427   //set image function deletes object once done so remove pointer to memory
00428   //we don't own any more
00429   invertedImage = NULL;
00430   //---------------------------------
00432   photoView->setPhoto( photo );
00433   //---------------------------------
00434 }
00435 //==============================================
00436 void PhotoEditWidget::reduceRedeye()
00437 {
00438 
00439 }
00440 //==============================================
00441 void PhotoEditWidget::resetImageAction()
00442 {
00443   //if previous state saved revert all changes
00444   if(originalImage != NULL)
00445   {
00447     photo->setImage(originalImage);
00448   
00449     //set image function deletes object once done so remove pointer to memory
00450     //we don't own any more
00451     originalImage = NULL;
00452   
00454     photoView->setPhoto( photo );
00455   } 
00456 }
00457 //==============================================
00458 void PhotoEditWidget::returnFromEdit()
00459 {
00460   //no changes? then just exit
00461   if(originalImage == NULL)
00462   {
00463     layout->stopEdit(true);
00464     return;
00465   }
00466   
00467   //otherwise ask user if they are sure they want to permantly scare their photo :)
00468   QuestionDialog sure( "Keep changes?",
00469                        "Once applied alterations cannot be undone.",
00470                        "warning.png",
00471                        this );
00472   //apply changes and exit
00473   if(sure.exec())  
00474   {
00475     layout->stopEdit(true);
00476   }
00477   //revert before exiting
00478   else
00479   {
00480     resetImageAction();
00481     layout->stopEdit(true);
00482   }
00483 }
00484 //==============================================

Generated on Tue Jun 10 23:41:21 2003 for AlbumShaper by doxygen 1.3.1