AlbumShaper
1.0a3
|
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