Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Class Members | File Members

histogramInterface.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 <qapplication.h>
00013 #include <qpainter.h>
00014 #include <qpixmap.h>
00015 #include <qimage.h>
00016 #include <qaccel.h>
00017 #include <qcursor.h>
00018 
00019 //Projectwide includes
00020 #include "histogramInterface.h"
00021 #include "histogramEditor.h"
00022 #include "../../backend/tools/imageTools.h"
00023 #include "../cursors.h"
00024 
00025 //a mouse press within DRAG_THRESHOLD will move boundaries of selection
00026 //if mouse press is not within DRAG_THRESHOLD a new selection will be started
00027 //and both click and drag points will be reset
00028 #define DRAG_THRESHOLD 5
00029 
00030 #define COLOR_BAR_MARGIN 2
00031 #define COLOR_BAR_BORDER 2
00032 #define COLOR_BAR_HEIGHT 6
00033 #define HISTOGRAM_HEIGHT ( height() - COLOR_BAR_BORDER - 2*COLOR_BAR_MARGIN - COLOR_BAR_HEIGHT )
00034 
00035 //==============================================
00036 HistogramInterface::HistogramInterface( QString imageFilename, 
00037                                         QWidget *parent, const char* name ) : 
00038                                         QWidget (parent, name, WNoAutoErase)
00039 {                     
00040   //set default mode to adjusted image
00041   displayedChannel = LUMINOSITY;
00042   
00043   //record original image width and height
00044   getImageSize( imageFilename, origImageSize );
00045   
00046   //construct histogram for color and luminosity channels
00047   //resize image to current screen size for faster
00048   //scaling during resize events
00049   QRect screenSize = qApp->desktop()->availableGeometry();
00050   QImage image;
00051   scaleImage( imageFilename, image, screenSize.width()/4, screenSize.height()/4 );
00052 
00053   int i;
00054   for(i=0; i<256; i++)
00055   {    
00056     redVals[i] = 0;
00057     greenVals[i] = 0;
00058     blueVals[i] = 0;
00059     grayVals[i] = 0;
00060   }
00061   int x, y;
00062   QRgb* rgb;
00063   uchar* scanLine;
00064   for( y=0; y<image.height(); y++)
00065   {   
00066     scanLine = image.scanLine(y);
00067     for( x=0; x<image.width(); x++)
00068     {
00069       rgb = ((QRgb*)scanLine+x);
00070       redVals[ qRed(*rgb) ]++;
00071       greenVals[ qGreen(*rgb) ]++; 
00072       blueVals[ qBlue(*rgb) ]++;
00073       grayVals[ qGray(*rgb) ]++;
00074     } //x
00075   } //y
00076   
00077   //find max r,g,b, and gray counts
00078   maxRcount = 0;
00079   maxGcount = 0;
00080   maxBcount = 0;
00081   maxGRAYcount = 0;
00082   for(i=0; i<256; i++)
00083   {
00084     if(redVals[i]   > maxRcount)    maxRcount    = redVals[i]; 
00085     if(greenVals[i] > maxGcount)    maxGcount    = greenVals[i]; 
00086     if(blueVals[i]  > maxBcount)    maxBcount    = blueVals[i]; 
00087     if(grayVals[i]  > maxGRAYcount) maxGRAYcount = grayVals[i]; 
00088   }
00089   //----          
00090   //by default mouse drags have no effect
00091   dragMode = NO_EFFECT;
00092   currentMouseShape = NO_EFFECT;
00093 
00094   //watch mouse movements in order to drag selection
00095   setMouseTracking(true);
00096   
00097   //accept focus when clicked on
00098   setFocusPolicy( QWidget::ClickFocus );  
00099 
00101   QAccel *keyAccel = new QAccel( this );
00102   keyAccel->connectItem( keyAccel->insertItem( CTRL + Key_A),
00103                          this, SLOT(selectAll()) );  
00104   
00105   //default cursor is cross hair indication regions can be selected
00106   setCursor( getCursor(CROSS_CURSOR) );
00107   
00108   //by default entire range is selected for all channels
00109   resetBoundaries();
00110 }
00111 //==============================================
00112 HistogramInterface::~HistogramInterface() { }
00113 //==============================================
00114 void HistogramInterface::resizeEvent( QResizeEvent * )
00115 {  
00116   repaint(false);
00117 }
00118 //==============================================
00119 void HistogramInterface::getSelectedRange( int &left, int &right )
00120 {
00121   if(displayedChannel == LUMINOSITY) 
00122   {
00123     left = QMIN( lumClick, lumDrag );
00124     right = QMAX( lumClick, lumDrag );
00125   }
00126   else if(displayedChannel == RED)   
00127   { 
00128     left = QMIN( redClick, redDrag );
00129     right = QMAX( redClick, redDrag );
00130   }
00131   else if(displayedChannel == GREEN) 
00132   { 
00133     left = QMIN( greenClick, greenDrag );
00134     right = QMAX( greenClick, greenDrag );
00135   }
00136   else if(displayedChannel == BLUE)  
00137   { 
00138     left = QMIN( blueClick, blueDrag );
00139     right = QMAX( blueClick, blueDrag );
00140   }   
00141   else
00142   { left = 0; right = 0; }
00143 }
00144 //==============================================
00145 double HistogramInterface::displayToIndex( int coordinate )
00146 {
00147   return (255.0*coordinate) / ( width()-1 );
00148 }
00149 //==============================================
00150 int HistogramInterface::indexToDisplay( int index )
00151 {
00152   return (index* (width()-1) ) / 255;
00153 }
00154 //==============================================
00155 void HistogramInterface::paintEvent(QPaintEvent *e)
00156 { 
00157   //create buffer to draw in
00158   QPixmap buffer( size() );
00159   buffer.fill( white );
00160   
00161   //create a painter pointing to the buffer
00162   QPainter bufferPainter( &buffer );
00163   
00164   //turn off clipping to make painting operations faster
00165   bufferPainter.setClipping(false);
00166 
00167   //initialize buffer with background brush
00168   bufferPainter.fillRect( buffer.rect(), backgroundBrush() );
00169 
00170   //get handle on histogram data, get max count, set default draw color, and find
00171   //left and right boundaries of current selection  
00172   QColor color = black;
00173   int* data = grayVals;
00174   int maxCount = maxGRAYcount;
00175 
00176   if(displayedChannel == RED)        { data = redVals;   color = red;   maxCount = maxRcount; }
00177   else if(displayedChannel == GREEN) { data = greenVals; color = green; maxCount = maxGcount; }
00178   else if(displayedChannel == BLUE)  { data = blueVals;  color = blue;  maxCount = maxBcount; }
00179   
00180   int indexLeft, indexRight;
00181   getSelectedRange(indexLeft,indexRight);
00182   int displayLeft = indexToDisplay ( indexLeft );
00183   int displayRight = indexToDisplay ( indexRight );  
00184 
00185   int histogramHeight = HISTOGRAM_HEIGHT;
00186   
00187   //iterate over each pixel column
00188   int x;
00189   for(x=0; x<width(); x++)
00190   {    
00191     double index = displayToIndex( x );
00192     int indexL = (int)index;
00193     double scaleR = index - indexL;
00194     
00195     int h = 0;
00196     if(indexL < 255)
00197     { 
00198       h = (int) ((1-scaleR)*data[indexL] + scaleR*data[indexL+1]); 
00199     }
00200     else 
00201     { 
00202       h = data[255]; 
00203     }
00204 
00205     //scale count so that the maxCount maps to the maximum height
00206     double scaledH = (histogramHeight*h)/maxCount;
00207     h = (int) scaledH;    
00208     //round up values between 0 and 1 so show data is there
00209     if( h == 0 && scaledH > h) h++;
00210     
00211     if(h > 0)
00212     {
00213       //use a gray color outside selected range
00214       QColor usedColor = color;
00215       if(x < displayLeft || x > displayRight) { usedColor = gray; }
00216         
00217       bufferPainter.fillRect( QRect(x, histogramHeight - h, 
00218                                     1, h),
00219                               QBrush(usedColor) );    
00220     }
00221     
00222     //if this is left or right boundary of selection and entire range not selected then
00223     //draw a vertical black line to make it stand out more
00224     if( (x == displayLeft || x == displayLeft+1 ||
00225          x == displayRight || x == displayRight-1) )
00226     {
00227       bufferPainter.drawLine( x, 0, x, histogramHeight-1 );
00228     }
00229   }
00230   //----
00231   //paint color bar key below
00232   
00233   //first a black border
00234   bufferPainter.fillRect( QRect(0, histogramHeight + COLOR_BAR_MARGIN, 
00235                                 width(), COLOR_BAR_HEIGHT+2*COLOR_BAR_BORDER),
00236                           QBrush(black) );    
00237 
00238   //next the color gradient
00239   QColor scaledColor;
00240   for(x=COLOR_BAR_BORDER; x < width()-COLOR_BAR_BORDER; x++)
00241   {    
00242     int index;
00243     if(x <= displayLeft )
00244       index = 0;
00245     else if(x >= displayRight)
00246       index = 255;
00247     else
00248       index = (int) (255.0*(x-displayLeft))/(displayRight - displayLeft);
00249   
00250     int r = color.red();
00251     int g = color.green();
00252     int b = color.blue();
00253     
00254     if( r != 0) r = index;
00255     if( g != 0) g = index;
00256     if( b != 0) b = index;
00257     
00258     //black color was used when adjusting luminance, scale to white instead (since black is 0)
00259     if( color == black )
00260     { r = g = b = index; }
00261 
00262     scaledColor.setRgb( r,g,b );
00263     bufferPainter.fillRect( QRect(x, histogramHeight + COLOR_BAR_MARGIN + COLOR_BAR_BORDER, 
00264                                   1, COLOR_BAR_HEIGHT),
00265                             QBrush(scaledColor) );    
00266   }    
00267     
00268   //end painter  
00269   bufferPainter.end();
00270   
00271   //blit buffer to screen
00272   bitBlt( this,
00273           e->rect().x(), e->rect().y(),
00274           &buffer, 
00275           e->rect().x(), e->rect().y(),
00276           e->rect().width(), e->rect().height() );
00277 }
00278 //==============================================
00279 void HistogramInterface::setDisplayChannel( DISPLAYED_CHANNEL channel )
00280 {
00281   //set mode and repaint
00282   displayedChannel = channel;
00283   repaint(false);
00284 }
00285 //==============================================
00286 QSize HistogramInterface::minimumSizeHint() const
00287 {
00288   return QSize( 256,100 + COLOR_BAR_MARGIN + 2*COLOR_BAR_BORDER + COLOR_BAR_HEIGHT );
00289 }
00290 //==============================================
00291 bool HistogramInterface::nearBoundary( QPoint p )
00292 {
00293   //compute index from mouse position
00294   int index = (int) displayToIndex( p.x() );
00295   
00296   //get left and right to check for clicks near current boundaries
00297   int left, right;
00298   getSelectedRange( left, right );
00299 
00300   //check if within threshold of left or right boundaries
00301   return ( (index < left + 1 + DRAG_THRESHOLD &&
00302             index > left -     DRAG_THRESHOLD) ||
00303            (index < right + DRAG_THRESHOLD &&
00304             index > right - 1 - DRAG_THRESHOLD) );
00305 }
00306 //==============================================
00307 void HistogramInterface::mousePressEvent( QMouseEvent *e)
00308 {
00309   //begin drag mode!
00310   dragMode = DRAG;
00311 
00312   //compute index from mouse position
00313   int index = (int) displayToIndex( e->pos().x() );
00314       
00315   //get left and right to check for clicks near current boundaries
00316   int left, right;
00317   getSelectedRange( left, right );
00318 
00319   //get click and drag handles
00320   int *click, *drag;
00321   if(displayedChannel == LUMINOSITY) 
00322   { 
00323     click = &lumClick; drag = &lumDrag; 
00324   }
00325   else if(displayedChannel == RED)   
00326   { 
00327     click = &redClick; drag = &redDrag; 
00328   }
00329   else if(displayedChannel == GREEN) 
00330   { 
00331     click = &greenClick; drag = &greenDrag; 
00332   }
00333   else
00334   { 
00335     click = &blueClick; drag = &blueDrag; 
00336   }
00337     
00338   //if within threshold of left then start dragging that side
00339   if( index < left + DRAG_THRESHOLD &&
00340       index > left - DRAG_THRESHOLD )
00341   {
00342     *click = right;
00343     *drag = left;
00344     return;
00345   }
00346   //if within threshold of left then start dragging that side
00347   if( index < right + DRAG_THRESHOLD &&
00348       index > right - DRAG_THRESHOLD )
00349   {
00350     *click = left;
00351     *drag = right;
00352     return;
00353   }
00354   //else begin new drag
00355   else
00356   {
00357     *click = index;
00358     *drag = index;
00359     repaint(false);
00360 
00361     //emit selectection changed signal
00362     int left, right;
00363     getSelectedRange( left, right );
00364     emit selectedRangeChanged();    
00365   }
00366 }
00367 //==============================================
00368 void HistogramInterface::mouseMoveEvent( QMouseEvent *e)
00369 {
00370   //if not dragging a selection then update mouse cursor as appropriate
00371   if(dragMode == NO_EFFECT)
00372   {
00373     if( nearBoundary(e->pos()) && currentMouseShape == NO_EFFECT )
00374     {
00375       currentMouseShape = DRAG;
00376       setCursor( getCursor(MOVE_HOR_CURSOR) );
00377     }
00378     else if( !nearBoundary(e->pos()) && currentMouseShape == DRAG )
00379     { 
00380       currentMouseShape = NO_EFFECT;
00381       setCursor( getCursor(CROSS_CURSOR) );
00382     }
00383 
00384     return;
00385   }
00386   
00387   //compute index in 0-255 range from mouse coordinates
00388   int x = QMAX( QMIN( e->pos().x(), width()-1 ), 0 );
00389   int index = (int) displayToIndex( x );
00390 
00391   //reset boundary
00392   if(displayedChannel == LUMINOSITY) { lumDrag = index; }
00393   else if(displayedChannel == RED)   { redDrag = index; }
00394   else if(displayedChannel == GREEN) { greenDrag = index; }
00395   else if(displayedChannel == BLUE)  { blueDrag = index; }          
00396   
00397   //repaint
00398   repaint(false);  
00399   
00400   //emit selectection changed signal
00401   int left, right;
00402   getSelectedRange( left, right );
00403   emit selectedRangeChanged();    
00404 }
00405 //==============================================
00406 void HistogramInterface::mouseReleaseEvent( QMouseEvent *e)
00407 {
00408   //set mouse drags to no longer have any effect on boundary
00409   dragMode = NO_EFFECT;
00410 
00411   //update mouse cursor if necessary
00412   if( !nearBoundary(e->pos()) && currentMouseShape == DRAG )
00413   { 
00414     currentMouseShape = NO_EFFECT;
00415     setCursor( getCursor(CROSS_CURSOR) );
00416   }    
00417 }
00418 //==============================================
00419 void HistogramInterface::selectAll()
00420 {
00421   //reset boundary
00422   if(displayedChannel == LUMINOSITY) { lumClick = 0,   lumDrag = 255;   }
00423   else if(displayedChannel == RED)   { redClick = 0;   redDrag = 255;   }
00424   else if(displayedChannel == GREEN) { greenClick = 0; greenDrag = 255; }
00425   else if(displayedChannel == BLUE)  { blueClick = 0;  blueDrag = 255;  } 
00426   repaint(false);  
00427 
00428   //emit selectection changed signal
00429   int left, right;
00430   getSelectedRange( left, right );
00431   emit selectedRangeChanged();    
00432 }
00433 //==============================================
00434 void HistogramInterface::getHistBoundaries(int &lumLeft, int &lumRight,
00435                                            int &redLeft, int &redRight,
00436                                            int &greenLeft, int &greenRight,
00437                                            int &blueLeft, int &blueRight)
00438 {
00439   lumLeft = QMIN( lumClick, lumDrag );
00440   lumRight = QMAX( lumClick, lumDrag );
00441 
00442   redLeft = QMIN( redClick, redDrag );
00443   redRight = QMAX( redClick, redDrag );
00444 
00445   greenLeft = QMIN( greenClick, greenDrag );
00446   greenRight = QMAX( greenClick, greenDrag );
00447 
00448   blueLeft = QMIN( blueClick, blueDrag );
00449   blueRight = QMAX( blueClick, blueDrag );
00450 }
00451 //==============================================
00452 void HistogramInterface::resetBoundaries()
00453 {
00454   lumClick = redClick = greenClick = blueClick = 0; 
00455   lumDrag = redDrag = greenDrag = blueDrag = 255;  
00456   repaint(false);
00457   emit selectedRangeChanged();    
00458 }
00459 //==============================================
00460 
00461 
00462 
00463 

Generated on Mon Apr 11 18:27:46 2005 for AlbumShaper by  doxygen 1.3.9.1