AlbumShaper  1.0a3
histogramEditor.cpp
Go to the documentation of this file.
00001 //==============================================
00002 //  copyright            : (C) 2003-2005 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 
00011 //Systemwide includes
00012 #include <qlayout.h>
00013 #include <qlabel.h>
00014 #include <qcombobox.h>
00015 #include <qpushbutton.h>
00016 #include <qframe.h>
00017 #include <qslider.h>
00018 #include <qtooltip.h>
00019 #include <qsizegrip.h>
00020 
00021 //Projectwide includes
00022 #include "histogramEditor.h"
00023 #include "scaledPreviewInterface.h"
00024 #include "histogramInterface.h"
00025 #include "../clickableLabel.h"
00026 #include "../dynamicSlider.h"
00027 #include "../../config.h"
00028 #include "../../backend/tools/imageTools.h"
00029 
00030 #define SLIDER_RADIUS 40
00031 
00032 //==============================================
00033 HistogramEditor::HistogramEditor( QString fileName, QWidget *parent, const char* name ) : QDialog(parent,name,true)
00034 {
00035   //set pointer to null to make sure no 
00036   //random data is ever accessed by the preview interface
00037   histogramInterface = NULL;
00038 
00039   //record filename
00040   this->fileName = fileName;
00041   
00042   //-----
00043   //find mean color values
00044   meanR = 0;
00045   meanG = 0;
00046   meanB = 0;
00047   int x, y;
00048   QRgb* rgb;
00049   uchar* scanLine;
00050   QImage image = QImage( fileName );
00051   for( y=0; y<image.height(); y++)
00052   {   
00053     scanLine = image.scanLine(y);
00054     for( x=0; x<image.width(); x++)
00055     {
00056       rgb = ((QRgb*)scanLine+x);
00057       double r = ((double)qRed(*rgb)   )/255.0;
00058       double g = ((double)qGreen(*rgb) )/255.0;
00059       double b = ((double)qBlue(*rgb)  )/255.0;
00060       
00061       meanR+=r;
00062       meanG+=g;
00063       meanB+=b;
00064     } //x
00065   } //y
00066   meanR = meanR / ( image.width() * image.height() );
00067   meanG = meanG / ( image.width() * image.height() );
00068   meanB = meanB / ( image.width() * image.height() );
00069   
00070   QFrame* visibleFrame = new QFrame( this, "visible widgets" );
00071   //--------------  
00072   //Preview frame: 
00073   previewInterface = new ScaledPreviewInterface( fileName, visibleFrame, 
00074                                                  "previewInterface" );
00075   connect( previewInterface, SIGNAL(resized()), 
00076            this, SLOT(generateAdjustedPreviewImage()) );  
00077 
00078   previewSelection = new QComboBox( visibleFrame, "previewSelction" );
00079   previewSelection->insertItem( tr("Split View") );
00080   previewSelection->insertItem( tr("Original Image") );
00081   previewSelection->insertItem( tr("Adjusted Image") );
00082   connect( previewSelection, SIGNAL(activated(int)), this, SLOT(selectPreviewImageType(int)) );  
00083   //--------------
00084   //Adjust frame:   
00085   histogramInterface = new HistogramInterface( fileName, visibleFrame, 
00086                                                "histogramInterface" );  
00087 
00088   //connect adjustments in histogram to generateAdjustedPreviewImage
00089   connect( histogramInterface, SIGNAL( selectedRangeChanged() ),
00090            SLOT( generateAdjustedPreviewImage() ) );
00091   
00092   QToolTip::add( histogramInterface, tr("Click and drag to select tonal range") );  
00093 
00094   histogramType = new QComboBox( visibleFrame, "histogramType" );
00095   histogramType->insertItem( tr("Luminosity") );
00096   histogramType->insertItem( tr("Red") );
00097   histogramType->insertItem( tr("Green") );
00098   histogramType->insertItem( tr("Blue") );  
00099   connect( histogramType, SIGNAL(activated(int)), this, SLOT(selectHistogramType(int)) );  
00100   QToolTip::add( histogramType, tr("Histogram channel displayed") );  
00101   //--------------
00102   //Slider frame:
00103   QString noChange = QString( tr("No change") );
00104   
00105   brightness = new DynamicSlider( Qt::Vertical, visibleFrame );
00106   brightness->setZeroString( noChange );
00107   brightness->setPrefixes("", "+");
00108   brightness->setMinValue( -SLIDER_RADIUS );
00109   brightness->setMaxValue( SLIDER_RADIUS );
00110   connect( brightness, SIGNAL(valueChanged(int)),
00111            this, SLOT(generateAdjustedPreviewImage()) );;
00112   QToolTip::add( brightness, tr("Drag to adjust image brightness") );  
00113            
00114   brightnessIcon = new ClickableLabel( visibleFrame, "brightnessIcon" );
00115   brightnessIcon->setPixmap( QPixmap(QString(IMAGE_PATH)+"miscImages/brightness.png") );
00116   connect( brightnessIcon, SIGNAL(clicked()), SLOT(resetBrightness()) );    
00117   QToolTip::add( brightnessIcon, tr("Reset brightness") );
00118 
00119   contrast = new DynamicSlider( Qt::Vertical, visibleFrame );
00120   contrast->setZeroString( noChange );
00121   contrast->setPrefixes("", "+");
00122   contrast->setMinValue( -SLIDER_RADIUS );
00123   contrast->setMaxValue( SLIDER_RADIUS );
00124   connect( contrast, SIGNAL(valueChanged(int)),
00125            this, SLOT(generateAdjustedPreviewImage()) );
00126   QToolTip::add( contrast, tr("Drag to adjust image contrast") );  
00127 
00128   contrastIcon = new ClickableLabel( visibleFrame, "contrastIcon" );
00129   contrastIcon->setPixmap( QPixmap(QString(IMAGE_PATH)+"miscImages/contrast.png") );
00130   connect( contrastIcon, SIGNAL(clicked()), SLOT(resetContrast()) );    
00131   QToolTip::add( contrastIcon, tr("Reset contrast") );
00132   //--------------
00133   //Dialog buttons:  
00134   buttonsFrame =   new QFrame( visibleFrame, "dialogButtons" );
00135 
00136   QPushButton* applyButton = new QPushButton( tr("Apply"), buttonsFrame );
00137   applyButton->setDefault(true);
00138   applyButton->setFocus();
00139   connect( applyButton, SIGNAL(clicked()), SLOT(applyAction()) );
00140                                 
00141   QPushButton* cancelButton = new QPushButton( tr("Cancel"), buttonsFrame );
00142   connect( cancelButton, SIGNAL(clicked()), SLOT(reject()) );
00143 
00144   QPushButton* resetButton = new QPushButton( tr("Reset"), buttonsFrame );
00145   connect( resetButton, SIGNAL(clicked()), SLOT(resetAction()) );
00146 
00147   QGridLayout* buttonsGrid = new QGridLayout( buttonsFrame, 1, 5, 0 );
00148   buttonsGrid->setColStretch( 0, 1 );
00149   buttonsGrid->addWidget( applyButton,  0, 1 );
00150   buttonsGrid->addWidget( cancelButton, 0, 2 );
00151   buttonsGrid->addWidget( resetButton, 0, 3 );
00152   buttonsGrid->setColStretch( 4, 1 );  
00153   buttonsGrid->setSpacing( WIDGET_SPACING );
00154   //--------------
00155   QGridLayout* mainGrid = new QGridLayout( visibleFrame, 5, 3, 0 );
00156   
00157   mainGrid->addMultiCellWidget( previewInterface,     0,0, 0,2 );
00158   mainGrid->addMultiCellWidget( previewSelection, 1,1, 0,2, Qt::AlignHCenter );  
00159    
00160   mainGrid->addWidget( histogramInterface,    2, 0 ); 
00161   mainGrid->addWidget( brightness,            2, 1 );
00162   mainGrid->addWidget( contrast,              2, 2 );
00163   
00164   //make sure sliders have enough space so all slider units are settable
00165   mainGrid->setRowSpacing( 2, 2*SLIDER_RADIUS + 5) ;
00166     
00167   mainGrid->addWidget( histogramType,    3, 0, Qt::AlignHCenter );
00168   mainGrid->addWidget( brightnessIcon,   3, 1 );  
00169   mainGrid->addWidget( contrastIcon,     3, 2 );  
00170   
00171   mainGrid->addMultiCellWidget( buttonsFrame, 4,4, 0,2 );
00172 
00173   mainGrid->setRowStretch( 0, 1 );
00174   mainGrid->setColStretch( 0, 1 );
00175   
00176   mainGrid->setSpacing( WIDGET_SPACING );
00177   mainGrid->setMargin( WIDGET_SPACING );
00178   
00179   QGridLayout* invisibleGrid = new QGridLayout( this, 2, 1, 0 );
00180   invisibleGrid->addWidget( visibleFrame, 0, 0 );
00181   invisibleGrid->setRowStretch( 0, 1 );
00182   
00183   //PLATFORM_SPECIFIC_CODE
00184   //windows users expect a grip, but qt doesn't put one in by default. we'll add
00185   //it for them too. :-)
00186 #if defined(Q_OS_WIN)
00187   QSizeGrip* sizeGrip = new QSizeGrip( this );
00188   invisibleGrid->addWidget( sizeGrip, 1, 0, Qt::AlignRight | Qt::AlignBottom );
00189 #endif
00190   
00191   
00192   
00193   
00194   //Window caption
00195   setCaption( tr("Histogram Editor") );
00196   //-------------------------------
00197 }
00198 //==============================================
00199 HistogramEditor::~HistogramEditor() { }
00200 //==============================================
00201 void HistogramEditor::applyAction()
00202 {
00203   //check if user has adjusted brightness, contrast, or histogram ranges.
00204   //if any changes have taken place call "accept", else "reject" so image is not
00205   //updated and appear modified
00206   int lumLeft, lumRight, redLeft, redRight, greenLeft, greenRight, blueLeft, blueRight;
00207   histogramInterface->getHistBoundaries( lumLeft, lumRight,
00208                                          redLeft, redRight,
00209                                          greenLeft, greenRight,
00210                                          blueLeft, blueRight );  
00211   if( brightness->value() != 0 || contrast->value()   != 0 ||
00212       lumLeft != 0 || lumRight != 255 ||
00213       redLeft !=0 || redRight != 255 ||
00214       greenLeft != 0 || greenRight != 255 ||
00215       blueLeft != 0 || blueRight != 255 )
00216   { accept(); }
00217   else
00218   { reject(); }  
00219 }
00220 //==============================================
00221 void HistogramEditor::resetAction()
00222 {
00223   histogramInterface->resetBoundaries();
00224   resetBrightness();
00225   resetContrast();
00226 }
00227 //==============================================
00228 QImage* HistogramEditor::getModifiedImage()
00229 { 
00230   QImage* adjustedImage = new QImage(fileName);
00231   
00232   //convert to 32-bit depth if necessary
00233   if( adjustedImage->depth() < 32 )
00234   {
00235     QImage* tmp = adjustedImage;
00236     adjustedImage = new QImage( tmp->convertDepth( 32, Qt::AutoColor ) );
00237     delete tmp; tmp=NULL;
00238   }
00239   
00240   adjustImage( *adjustedImage );
00241   return adjustedImage;  
00242 }
00243 //==============================================
00244 void HistogramEditor::selectPreviewImageType( int selection )
00245 {
00246   previewInterface->setPreviewMode( (PREVIEW_MODE)selection );
00247 }
00248 //==============================================
00249 void HistogramEditor::selectHistogramType( int selection )
00250 {
00251   histogramInterface->setDisplayChannel( (DISPLAYED_CHANNEL) selection );
00252 }
00253 //==============================================
00254 void HistogramEditor::resetBrightness()
00255 { brightness->setValue( 0 ); }
00256 //==============================================
00257 void HistogramEditor::resetContrast()
00258 { contrast->setValue( 0 ); }
00259 //==============================================
00260 void HistogramEditor::getHistBoundaries(int &lumLeft, int &lumRight,
00261                                         int &redLeft, int &redRight,
00262                                         int &greenLeft, int &greenRight,
00263                                         int &blueLeft, int &blueRight)
00264 {
00265   //sanity check
00266   if( histogramInterface )
00267   {
00268     histogramInterface->getHistBoundaries( lumLeft, lumRight,
00269                                            redLeft, redRight,
00270                                            greenLeft, greenRight,
00271                                            blueLeft, blueRight );
00272   }
00273   else
00274   {
00275     lumLeft = 0; lumRight = 255;
00276     redLeft = 0; redRight = 255;
00277     greenLeft = 0; greenRight = 255;
00278     blueLeft = 0; blueRight = 255;
00279   }
00280 }
00281 //==============================================
00282 void HistogramEditor::generateAdjustedPreviewImage()
00283 {
00284   //get original image
00285   QImage origImage = previewInterface->getOrigImage();
00286 
00287   //construct adjusted image
00288   QImage adjustedImage = origImage.copy();
00289   adjustImage( adjustedImage );
00290   
00291   //set adjusted image
00292   previewInterface->setAdjustedImage( adjustedImage );
00293 }
00294 //==============================================
00295 void HistogramEditor::adjustImage( QImage &image )
00296 {
00297   //obtain histogram left and right boundaries
00298   //sanity check
00299   int lumLeft, lumRight, redLeft, redRight, greenLeft, greenRight, blueLeft, blueRight;
00300   if( histogramInterface )
00301   {
00302     histogramInterface->getHistBoundaries( lumLeft, lumRight,
00303                                            redLeft, redRight,
00304                                            greenLeft, greenRight,
00305                                            blueLeft, blueRight );
00306   }
00307   else
00308   {
00309     lumLeft = 0; lumRight = 255;
00310     redLeft = 0; redRight = 255;
00311     greenLeft = 0; greenRight = 255;
00312     blueLeft = 0; blueRight = 255;
00313   }
00314   
00315   //modify image
00316   double displayToOneScalar = 1.0/255.0;
00317   double scaledMeanR = displayToOneScalar*scaleColor( 255.0*meanR, redLeft, redRight );
00318   double scaledMeanG = displayToOneScalar*scaleColor( 255.0*meanG, greenLeft, greenRight );
00319   double scaledMeanB = displayToOneScalar*scaleColor( 255.0*meanB, blueLeft, blueRight );
00320   
00321   double brightnessScalar, addedBrightnessColor;
00322   if(brightness->value() < 0)
00323   {
00324     brightnessScalar = ((double)(SLIDER_RADIUS + brightness->value()))/SLIDER_RADIUS;
00325     addedBrightnessColor = 1.0 - brightnessScalar;
00326   }
00327   else
00328   {
00329     brightnessScalar = ((double)(SLIDER_RADIUS - brightness->value()))/SLIDER_RADIUS;
00330     addedBrightnessColor = 0.0;
00331   }
00332   
00333   int x, y;  
00334   QRgb* rgb;
00335   double r,g,b;
00336   double h,s,v;
00337   int rPrime, gPrime, bPrime;
00338   uchar* scanLine;
00339   
00340   for( y=0; y<image.height(); y++)
00341   {   
00342     scanLine = image.scanLine(y);
00343     for( x=0; x<image.width(); x++)
00344     {
00345       //get rgb value
00346       rgb = ((QRgb*)scanLine+x);
00347       r = qRed(*rgb);
00348       g = qGreen(*rgb);
00349       b = qBlue(*rgb);
00350       
00351       //apply histogram boundaries
00352       RGBtoHSV(r,g,b,&h,&s,&v);
00353       v = scaleColor( v, lumLeft, lumRight );
00354       HSVtoRGB( &r,&g,&b, h,s,v);         
00355       
00356       r = scaleColor( r, redLeft, redRight );
00357       g = scaleColor( g, greenLeft, greenRight );
00358       b = scaleColor( b, blueLeft, blueRight );
00359       
00360       //convert to 0-1 scale
00361       r = r*displayToOneScalar;
00362       g = g*displayToOneScalar;
00363       b = b*displayToOneScalar;
00364       
00365       //adjust contrast
00366       r = ( (r-scaledMeanR) * (SLIDER_RADIUS-contrast->value()) )/SLIDER_RADIUS + scaledMeanR;
00367       g = ( (g-scaledMeanG) * (SLIDER_RADIUS-contrast->value()) )/SLIDER_RADIUS + scaledMeanG;
00368       b = ( (b-scaledMeanB) * (SLIDER_RADIUS-contrast->value()) )/SLIDER_RADIUS + scaledMeanB;
00369       
00370       //apply brightness adjustment
00371       //http://www.sgi.com/misc/grafica/interp/
00372       r = brightnessScalar*r + addedBrightnessColor;
00373       g = brightnessScalar*g + addedBrightnessColor;
00374       b = brightnessScalar*b + addedBrightnessColor;
00375       
00376       //scale and clamp to 0-255 range
00377       rPrime = (int) QMIN( QMAX((r*255), 0), 255 );
00378       gPrime = (int) QMIN( QMAX((g*255), 0), 255 );
00379       bPrime = (int) QMIN( QMAX((b*255), 0), 255 );
00380       
00381       //set adjusted color value
00382       *rgb = qRgb(rPrime, gPrime, bPrime);          
00383     } //x
00384   } //y  
00385 }
00386 //==============================================
00387 double HistogramEditor::scaleColor( double color, int left, int right )
00388 {
00389   return QMAX( QMIN( (255.0*(color-left)) / (right-left), 255), 0 );  
00390 }
00391 //==============================================
00392 void HistogramEditor::keyPressEvent(QKeyEvent *e)
00393 {
00394   if(e->key() == Qt::Key_Control )
00395   {
00396     PREVIEW_MODE curMode = (PREVIEW_MODE) previewSelection->currentItem();
00397     if(curMode == ORIGINAL_IMAGE)
00398       previewInterface->setPreviewMode( ADJUSTED_IMAGE, true );
00399     else if(curMode == ADJUSTED_IMAGE)
00400       previewInterface->setPreviewMode( ORIGINAL_IMAGE, true );
00401     else
00402       previewInterface->setPreviewMode( INV_SPLIT_VIEW );
00403   }
00404   else { QDialog::keyPressEvent(e); }
00405 }
00406 //==============================================
00407 void HistogramEditor::keyReleaseEvent(QKeyEvent *e)
00408 {
00409   if(e->key() == Qt::Key_Control )
00410   {
00411     previewInterface->setPreviewMode( (PREVIEW_MODE) previewSelection->currentItem(), 
00412                                       false );
00413   }
00414   else { QDialog::keyReleaseEvent(e); }
00415 }
00416 //==============================================
00417 
00418