AlbumShaper 1.0a3
histogramInterface.cpp
Go to the documentation of this file.
1//==============================================
2// copyright : (C) 2003-2005 by Will Stokes
3//==============================================
4// This program is free software; you can redistribute it
5// and/or modify it under the terms of the GNU General
6// Public License as published by the Free Software
7// Foundation; either version 2 of the License, or
8// (at your option) any later version.
9//==============================================
10
11//Systemwide includes
12#include <qapplication.h>
13#include <qpainter.h>
14#include <qpixmap.h>
15#include <qimage.h>
16#include <q3accel.h>
17#include <qcursor.h>
18//Added by qt3to4:
19#include <QPaintEvent>
20#include <QResizeEvent>
21#include <QMouseEvent>
22#include <QDesktopWidget>
23
24//Projectwide includes
25#include "histogramInterface.h"
26#include "histogramEditor.h"
28#include "../cursors.h"
29
30//a mouse press within DRAG_THRESHOLD will move boundaries of selection
31//if mouse press is not within DRAG_THRESHOLD a new selection will be started
32//and both click and drag points will be reset
33#define DRAG_THRESHOLD 5
34
35#define COLOR_BAR_MARGIN 2
36#define COLOR_BAR_BORDER 2
37#define COLOR_BAR_HEIGHT 6
38#define HISTOGRAM_HEIGHT ( height() - COLOR_BAR_BORDER - 2*COLOR_BAR_MARGIN - COLOR_BAR_HEIGHT )
39
40//==============================================
42 QWidget *parent, const char* name ) :
43 QWidget (parent, name, Qt::WNoAutoErase)
44{
45 //set default mode to adjusted image
47
48 //record original image width and height
49 getImageSize( imageFilename, origImageSize );
50
51 //construct histogram for color and luminosity channels
52 //resize image to current screen size for faster
53 //scaling during resize events
54 QRect screenSize = qApp->desktop()->availableGeometry();
55 QImage image;
56 scaleImage( imageFilename, image, screenSize.width()/4, screenSize.height()/4 );
57
58 int i;
59 for(i=0; i<256; i++)
60 {
61 redVals[i] = 0;
62 greenVals[i] = 0;
63 blueVals[i] = 0;
64 grayVals[i] = 0;
65 }
66 int x, y;
67 QRgb* rgb;
68 uchar* scanLine;
69 for( y=0; y<image.height(); y++)
70 {
71 scanLine = image.scanLine(y);
72 for( x=0; x<image.width(); x++)
73 {
74 rgb = ((QRgb*)scanLine+x);
75 redVals[ qRed(*rgb) ]++;
76 greenVals[ qGreen(*rgb) ]++;
77 blueVals[ qBlue(*rgb) ]++;
78 grayVals[ qGray(*rgb) ]++;
79 } //x
80 } //y
81
82 //find max r,g,b, and gray counts
83 maxRcount = 0;
84 maxGcount = 0;
85 maxBcount = 0;
86 maxGRAYcount = 0;
87 for(i=0; i<256; i++)
88 {
89 if(redVals[i] > maxRcount) maxRcount = redVals[i];
91 if(blueVals[i] > maxBcount) maxBcount = blueVals[i];
93 }
94 //----
95 //by default mouse drags have no effect
98
99 //watch mouse movements in order to drag selection
100 setMouseTracking(true);
101
102 //accept focus when clicked on
103 setFocusPolicy( Qt::ClickFocus );
104
106 Q3Accel *keyAccel = new Q3Accel( this );
107 keyAccel->connectItem( keyAccel->insertItem( Qt::CTRL + Qt::Key_A),
108 this, SLOT(selectAll()) );
109
110 //default cursor is cross hair indication regions can be selected
111 setCursor( getCursor(CROSS_CURSOR) );
112
113 //by default entire range is selected for all channels
115}
116//==============================================
118//==============================================
120{
121 repaint(false);
122}
123//==============================================
124void HistogramInterface::getSelectedRange( int &left, int &right )
125{
127 {
128 left = QMIN( lumClick, lumDrag );
129 right = QMAX( lumClick, lumDrag );
130 }
131 else if(displayedChannel == RED)
132 {
133 left = QMIN( redClick, redDrag );
134 right = QMAX( redClick, redDrag );
135 }
136 else if(displayedChannel == GREEN)
137 {
138 left = QMIN( greenClick, greenDrag );
139 right = QMAX( greenClick, greenDrag );
140 }
141 else if(displayedChannel == BLUE)
142 {
143 left = QMIN( blueClick, blueDrag );
144 right = QMAX( blueClick, blueDrag );
145 }
146 else
147 { left = 0; right = 0; }
148}
149//==============================================
151{
152 return (255.0*coordinate) / ( width()-1 );
153}
154//==============================================
156{
157 return (index* (width()-1) ) / 255;
158}
159//==============================================
161{
162 //create buffer to draw in
163 QPixmap buffer( size() );
164 buffer.fill( Qt::white );
165
166 //create a painter pointing to the buffer
167 QPainter bufferPainter( &buffer );
168
169 //turn off clipping to make painting operations faster
170 bufferPainter.setClipping(false);
171
172 //initialize buffer with background brush
173 bufferPainter.fillRect( buffer.rect(), backgroundBrush() );
174
175 //get handle on histogram data, get max count, set default draw color, and find
176 //left and right boundaries of current selection
177 QColor color = Qt::black;
178 int* data = grayVals;
179 int maxCount = maxGRAYcount;
180
181 if(displayedChannel == RED) { data = redVals; color = Qt::red; maxCount = maxRcount; }
182 else if(displayedChannel == GREEN) { data = greenVals; color = Qt::green; maxCount = maxGcount; }
183 else if(displayedChannel == BLUE) { data = blueVals; color = Qt::blue; maxCount = maxBcount; }
184
185 int indexLeft, indexRight;
186 getSelectedRange(indexLeft,indexRight);
187 int displayLeft = indexToDisplay ( indexLeft );
188 int displayRight = indexToDisplay ( indexRight );
189
190 int histogramHeight = HISTOGRAM_HEIGHT;
191
192 //iterate over each pixel column
193 int x;
194 for(x=0; x<width(); x++)
195 {
196 double index = displayToIndex( x );
197 int indexL = (int)index;
198 double scaleR = index - indexL;
199
200 int h = 0;
201 if(indexL < 255)
202 {
203 h = (int) ((1-scaleR)*data[indexL] + scaleR*data[indexL+1]);
204 }
205 else
206 {
207 h = data[255];
208 }
209
210 //scale count so that the maxCount maps to the maximum height
211 double scaledH = (histogramHeight*h)/maxCount;
212 h = (int) scaledH;
213 //round up values between 0 and 1 so show data is there
214 if( h == 0 && scaledH > h) h++;
215
216 if(h > 0)
217 {
218 //use a gray color outside selected range
219 QColor usedColor = color;
220 if(x < displayLeft || x > displayRight) { usedColor = Qt::gray; }
221
222 bufferPainter.fillRect( QRect(x, histogramHeight - h,
223 1, h),
224 QBrush(usedColor) );
225 }
226
227 //if this is left or right boundary of selection and entire range not selected then
228 //draw a vertical black line to make it stand out more
229 if( (x == displayLeft || x == displayLeft+1 ||
230 x == displayRight || x == displayRight-1) )
231 {
232 bufferPainter.drawLine( x, 0, x, histogramHeight-1 );
233 }
234 }
235 //----
236 //paint color bar key below
237
238 //first a black border
239 bufferPainter.fillRect( QRect(0, histogramHeight + COLOR_BAR_MARGIN,
241 QBrush(Qt::black) );
242
243 //next the color gradient
244 QColor scaledColor;
245 for(x=COLOR_BAR_BORDER; x < width()-COLOR_BAR_BORDER; x++)
246 {
247 int index;
248 if(x <= displayLeft )
249 index = 0;
250 else if(x >= displayRight)
251 index = 255;
252 else
253 index = (int) (255.0*(x-displayLeft))/(displayRight - displayLeft);
254
255 int r = color.red();
256 int g = color.green();
257 int b = color.blue();
258
259 if( r != 0) r = index;
260 if( g != 0) g = index;
261 if( b != 0) b = index;
262
263 //black color was used when adjusting luminance, scale to white instead (since black is 0)
264 if( color == Qt::black )
265 { r = g = b = index; }
266
267 scaledColor.setRgb( r,g,b );
268 bufferPainter.fillRect( QRect(x, histogramHeight + COLOR_BAR_MARGIN + COLOR_BAR_BORDER,
270 QBrush(scaledColor) );
271 }
272
273 //end painter
274 bufferPainter.end();
275
276 //blit buffer to screen
277 bitBlt( this,
278 e->rect().x(), e->rect().y(),
279 &buffer,
280 e->rect().x(), e->rect().y(),
281 e->rect().width(), e->rect().height() );
282}
283//==============================================
285{
286 //set mode and repaint
287 displayedChannel = channel;
288 repaint(false);
289}
290//==============================================
292{
293 return QSize( 256,100 + COLOR_BAR_MARGIN + 2*COLOR_BAR_BORDER + COLOR_BAR_HEIGHT );
294}
295//==============================================
297{
298 //compute index from mouse position
299 int index = (int) displayToIndex( p.x() );
300
301 //get left and right to check for clicks near current boundaries
302 int left, right;
303 getSelectedRange( left, right );
304
305 //check if within threshold of left or right boundaries
306 return ( (index < left + 1 + DRAG_THRESHOLD &&
307 index > left - DRAG_THRESHOLD) ||
308 (index < right + DRAG_THRESHOLD &&
309 index > right - 1 - DRAG_THRESHOLD) );
310}
311//==============================================
313{
314 //begin drag mode!
315 dragMode = DRAG;
316
317 //compute index from mouse position
318 int index = (int) displayToIndex( e->pos().x() );
319
320 //get left and right to check for clicks near current boundaries
321 int left, right;
322 getSelectedRange( left, right );
323
324 //get click and drag handles
325 int *click, *drag;
327 {
328 click = &lumClick; drag = &lumDrag;
329 }
330 else if(displayedChannel == RED)
331 {
332 click = &redClick; drag = &redDrag;
333 }
334 else if(displayedChannel == GREEN)
335 {
336 click = &greenClick; drag = &greenDrag;
337 }
338 else
339 {
340 click = &blueClick; drag = &blueDrag;
341 }
342
343 //if within threshold of left then start dragging that side
344 if( index < left + DRAG_THRESHOLD &&
345 index > left - DRAG_THRESHOLD )
346 {
347 *click = right;
348 *drag = left;
349 return;
350 }
351 //if within threshold of left then start dragging that side
352 if( index < right + DRAG_THRESHOLD &&
353 index > right - DRAG_THRESHOLD )
354 {
355 *click = left;
356 *drag = right;
357 return;
358 }
359 //else begin new drag
360 else
361 {
362 *click = index;
363 *drag = index;
364 repaint(false);
365
366 //emit selectection changed signal
367 int left, right;
368 getSelectedRange( left, right );
369 emit selectedRangeChanged();
370 }
371}
372//==============================================
374{
375 //if not dragging a selection then update mouse cursor as appropriate
376 if(dragMode == NO_EFFECT)
377 {
378 if( nearBoundary(e->pos()) && currentMouseShape == NO_EFFECT )
379 {
381 setCursor( getCursor(MOVE_HOR_CURSOR) );
382 }
383 else if( !nearBoundary(e->pos()) && currentMouseShape == DRAG )
384 {
386 setCursor( getCursor(CROSS_CURSOR) );
387 }
388
389 return;
390 }
391
392 //compute index in 0-255 range from mouse coordinates
393 int x = QMAX( QMIN( e->pos().x(), width()-1 ), 0 );
394 int index = (int) displayToIndex( x );
395
396 //reset boundary
397 if(displayedChannel == LUMINOSITY) { lumDrag = index; }
398 else if(displayedChannel == RED) { redDrag = index; }
399 else if(displayedChannel == GREEN) { greenDrag = index; }
400 else if(displayedChannel == BLUE) { blueDrag = index; }
401
402 //repaint
403 repaint(false);
404
405 //emit selectection changed signal
406 int left, right;
407 getSelectedRange( left, right );
408 emit selectedRangeChanged();
409}
410//==============================================
412{
413 //set mouse drags to no longer have any effect on boundary
415
416 //update mouse cursor if necessary
417 if( !nearBoundary(e->pos()) && currentMouseShape == DRAG )
418 {
420 setCursor( getCursor(CROSS_CURSOR) );
421 }
422}
423//==============================================
425{
426 //reset boundary
427 if(displayedChannel == LUMINOSITY) { lumClick = 0, lumDrag = 255; }
428 else if(displayedChannel == RED) { redClick = 0; redDrag = 255; }
429 else if(displayedChannel == GREEN) { greenClick = 0; greenDrag = 255; }
430 else if(displayedChannel == BLUE) { blueClick = 0; blueDrag = 255; }
431 repaint(false);
432
433 //emit selectection changed signal
434 int left, right;
435 getSelectedRange( left, right );
436 emit selectedRangeChanged();
437}
438//==============================================
439void HistogramInterface::getHistBoundaries(int &lumLeft, int &lumRight,
440 int &redLeft, int &redRight,
441 int &greenLeft, int &greenRight,
442 int &blueLeft, int &blueRight)
443{
444 lumLeft = QMIN( lumClick, lumDrag );
445 lumRight = QMAX( lumClick, lumDrag );
446
447 redLeft = QMIN( redClick, redDrag );
448 redRight = QMAX( redClick, redDrag );
449
450 greenLeft = QMIN( greenClick, greenDrag );
451 greenRight = QMAX( greenClick, greenDrag );
452
453 blueLeft = QMIN( blueClick, blueDrag );
454 blueRight = QMAX( blueClick, blueDrag );
455}
456//==============================================
458{
460 lumDrag = redDrag = greenDrag = blueDrag = 255;
461 repaint(false);
462 emit selectedRangeChanged();
463}
464//==============================================
465
466
467
468
int width
Definition blur.cpp:79
float * buffer
Definition blur.cpp:80
DISPLAYED_CHANNEL displayedChannel
Currently displayed channel.
void mousePressEvent(QMouseEvent *e)
void selectedRangeChanged()
int redVals[256]
color and luminosity histograms
QSize origImageSize
original image dimensions, needed for painting
bool nearBoundary(QPoint p)
determines if mouse is near boundary
HistogramInterface(QString imageFilename, QWidget *parent=0, const char *name=0)
Creates layout.
void resizeEvent(QResizeEvent *)
void mouseMoveEvent(QMouseEvent *e)
DRAG_MODE dragMode
effect of mouse drags
double displayToIndex(int val)
convert screen coordinate to index in 0-255 range
void setDisplayChannel(DISPLAYED_CHANNEL channel)
Sets currently displayed channel.
DRAG_MODE currentMouseShape
current mouse shape.
int indexToDisplay(int val)
converts index in 0-255 ranges to screen coordinate
void resetBoundaries()
resets all boundaries
void mouseReleaseEvent(QMouseEvent *)
void getSelectedRange(int &left, int &right)
this utility function finds currently selected range
int maxRcount
max r,g,b, and gray counts
~HistogramInterface()
Deletes objects.
void getHistBoundaries(int &lumLeft, int &lumRight, int &redLeft, int &redRight, int &greenLeft, int &greenRight, int &blueLeft, int &blueRight)
returns histogram boundaries
virtual QSize minimumSizeHint() const
int lumClick
left and right bounds for each channel
void paintEvent(QPaintEvent *e)
const QCursor & getCursor(CUSTOM_CURSOR_TYPE type)
Definition cursors.cpp:52
@ MOVE_HOR_CURSOR
Definition cursors.h:20
@ CROSS_CURSOR
Definition cursors.h:17
#define COLOR_BAR_MARGIN
#define COLOR_BAR_BORDER
#define HISTOGRAM_HEIGHT
#define COLOR_BAR_HEIGHT
#define DRAG_THRESHOLD
DISPLAYED_CHANNEL
chanel histogram displays
@ LUMINOSITY
@ NO_EFFECT
bool scaleImage(QString fileIn, QString fileOut, int newWidth, int newHeight)
Scale image and save copy to disk.
bool getImageSize(const char *filename, QSize &size)
Get image dimensions.
long b