AlbumShaper 1.0a3
histogramEditor.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 <qlayout.h>
13#include <qlabel.h>
14#include <qcombobox.h>
15#include <qpushbutton.h>
16#include <q3frame.h>
17#include <qslider.h>
18#include <qtooltip.h>
19#include <qsizegrip.h>
20//Added by qt3to4:
21#include <Q3GridLayout>
22#include <QPixmap>
23#include <QKeyEvent>
24
25#define MIN(x,y) ((x) < (y) ? (x) : (y))
26#define MAX(x,y) ((x) > (y) ? (x) : (y))
27
28//Projectwide includes
29#include "histogramEditor.h"
31#include "histogramInterface.h"
32#include "../clickableLabel.h"
33#include "../dynamicSlider.h"
34#include "../../config.h"
36
37#define SLIDER_RADIUS 40
38
39//==============================================
40HistogramEditor::HistogramEditor( QString fileName, QWidget *parent, const char* name ) : QDialog(parent,name,true)
41{
42 //set pointer to null to make sure no
43 //random data is ever accessed by the preview interface
44 histogramInterface = NULL;
45
46 //record filename
47 this->fileName = fileName;
48
49 //-----
50 //find mean color values
51 meanR = 0;
52 meanG = 0;
53 meanB = 0;
54 int x, y;
55 QRgb* rgb;
56 uchar* scanLine;
57 QImage image = QImage( fileName );
58 for( y=0; y<image.height(); y++)
59 {
60 scanLine = image.scanLine(y);
61 for( x=0; x<image.width(); x++)
62 {
63 rgb = ((QRgb*)scanLine+x);
64 double r = ((double)qRed(*rgb) )/255.0;
65 double g = ((double)qGreen(*rgb) )/255.0;
66 double b = ((double)qBlue(*rgb) )/255.0;
67
68 meanR+=r;
69 meanG+=g;
70 meanB+=b;
71 } //x
72 } //y
73 meanR = meanR / ( image.width() * image.height() );
74 meanG = meanG / ( image.width() * image.height() );
75 meanB = meanB / ( image.width() * image.height() );
76
77 Q3Frame* visibleFrame = new Q3Frame( this, "visible widgets" );
78 //--------------
79 //Preview frame:
81 "previewInterface" );
82 connect( previewInterface, SIGNAL(resized()),
83 this, SLOT(generateAdjustedPreviewImage()) );
84
85 previewSelection = new QComboBox( visibleFrame, "previewSelction" );
86 previewSelection->insertItem( tr("Split View") );
87 previewSelection->insertItem( tr("Original Image") );
88 previewSelection->insertItem( tr("Adjusted Image") );
89 connect( previewSelection, SIGNAL(activated(int)), this, SLOT(selectPreviewImageType(int)) );
90 //--------------
91 //Adjust frame:
93 "histogramInterface" );
94
95 //connect adjustments in histogram to generateAdjustedPreviewImage
96 connect( histogramInterface, SIGNAL( selectedRangeChanged() ),
98
99 QToolTip::add( histogramInterface, tr("Click and drag to select tonal range") );
100
101 histogramType = new QComboBox( visibleFrame, "histogramType" );
102 histogramType->insertItem( tr("Luminosity") );
103 histogramType->insertItem( tr("Red") );
104 histogramType->insertItem( tr("Green") );
105 histogramType->insertItem( tr("Blue") );
106 connect( histogramType, SIGNAL(activated(int)), this, SLOT(selectHistogramType(int)) );
107 QToolTip::add( histogramType, tr("Histogram channel displayed") );
108 //--------------
109 //Slider frame:
110 QString noChange = QString( tr("No change") );
111
112 brightness = new DynamicSlider( Qt::Vertical, visibleFrame );
113 brightness->setZeroString( noChange );
114 brightness->setPrefixes("", "+");
115 brightness->setMinValue( -SLIDER_RADIUS );
116 brightness->setMaxValue( SLIDER_RADIUS );
117 connect( brightness, SIGNAL(valueChanged(int)),
118 this, SLOT(generateAdjustedPreviewImage()) );;
119 QToolTip::add( brightness, tr("Drag to adjust image brightness") );
120
121 brightnessIcon = new ClickableLabel( visibleFrame, "brightnessIcon" );
122 brightnessIcon->setPixmap( QPixmap(QString(IMAGE_PATH)+"miscImages/brightness.png") );
123 connect( brightnessIcon, SIGNAL(clicked()), SLOT(resetBrightness()) );
124 QToolTip::add( brightnessIcon, tr("Reset brightness") );
125
126 contrast = new DynamicSlider( Qt::Vertical, visibleFrame );
127 contrast->setZeroString( noChange );
128 contrast->setPrefixes("", "+");
129 contrast->setMinValue( -SLIDER_RADIUS );
130 contrast->setMaxValue( SLIDER_RADIUS );
131 connect( contrast, SIGNAL(valueChanged(int)),
132 this, SLOT(generateAdjustedPreviewImage()) );
133 QToolTip::add( contrast, tr("Drag to adjust image contrast") );
134
135 contrastIcon = new ClickableLabel( visibleFrame, "contrastIcon" );
136 contrastIcon->setPixmap( QPixmap(QString(IMAGE_PATH)+"miscImages/contrast.png") );
137 connect( contrastIcon, SIGNAL(clicked()), SLOT(resetContrast()) );
138 QToolTip::add( contrastIcon, tr("Reset contrast") );
139 //--------------
140 //Dialog buttons:
141 buttonsFrame = new Q3Frame( visibleFrame, "dialogButtons" );
142
143 QPushButton* applyButton = new QPushButton( tr("Apply"), buttonsFrame );
144 applyButton->setDefault(true);
145 applyButton->setFocus();
146 connect( applyButton, SIGNAL(clicked()), SLOT(applyAction()) );
147
148 QPushButton* cancelButton = new QPushButton( tr("Cancel"), buttonsFrame );
149 connect( cancelButton, SIGNAL(clicked()), SLOT(reject()) );
150
151 QPushButton* resetButton = new QPushButton( tr("Reset"), buttonsFrame );
152 connect( resetButton, SIGNAL(clicked()), SLOT(resetAction()) );
153
154 Q3GridLayout* buttonsGrid = new Q3GridLayout( buttonsFrame, 1, 5, 0 );
155 buttonsGrid->setColStretch( 0, 1 );
156 buttonsGrid->addWidget( applyButton, 0, 1 );
157 buttonsGrid->addWidget( cancelButton, 0, 2 );
158 buttonsGrid->addWidget( resetButton, 0, 3 );
159 buttonsGrid->setColStretch( 4, 1 );
160 buttonsGrid->setSpacing( WIDGET_SPACING );
161 //--------------
162 Q3GridLayout* mainGrid = new Q3GridLayout( visibleFrame, 5, 3, 0 );
163
164 mainGrid->addMultiCellWidget( previewInterface, 0,0, 0,2 );
165 mainGrid->addMultiCellWidget( previewSelection, 1,1, 0,2, Qt::AlignHCenter );
166
167 mainGrid->addWidget( histogramInterface, 2, 0 );
168 mainGrid->addWidget( brightness, 2, 1 );
169 mainGrid->addWidget( contrast, 2, 2 );
170
171 //make sure sliders have enough space so all slider units are settable
172 mainGrid->setRowSpacing( 2, 2*SLIDER_RADIUS + 5) ;
173
174 mainGrid->addWidget( histogramType, 3, 0, Qt::AlignHCenter );
175 mainGrid->addWidget( brightnessIcon, 3, 1 );
176 mainGrid->addWidget( contrastIcon, 3, 2 );
177
178 mainGrid->addMultiCellWidget( buttonsFrame, 4,4, 0,2 );
179
180 mainGrid->setRowStretch( 0, 1 );
181 mainGrid->setColStretch( 0, 1 );
182
183 mainGrid->setSpacing( WIDGET_SPACING );
184 mainGrid->setMargin( WIDGET_SPACING );
185
186 Q3GridLayout* invisibleGrid = new Q3GridLayout( this, 2, 1, 0 );
187 invisibleGrid->addWidget( visibleFrame, 0, 0 );
188 invisibleGrid->setRowStretch( 0, 1 );
189
190 //PLATFORM_SPECIFIC_CODE
191 //windows users expect a grip, but qt doesn't put one in by default. we'll add
192 //it for them too. :-)
193#if defined(Q_OS_WIN)
194 QSizeGrip* sizeGrip = new QSizeGrip( this );
195 invisibleGrid->addWidget( sizeGrip, 1, 0, Qt::AlignRight | Qt::AlignBottom );
196#endif
197
198
199
200
201 //Window caption
202 setCaption( tr("Histogram Editor") );
203 //-------------------------------
204}
205//==============================================
207//==============================================
209{
210 //check if user has adjusted brightness, contrast, or histogram ranges.
211 //if any changes have taken place call "accept", else "reject" so image is not
212 //updated and appear modified
213 int lumLeft, lumRight, redLeft, redRight, greenLeft, greenRight, blueLeft, blueRight;
214 histogramInterface->getHistBoundaries( lumLeft, lumRight,
215 redLeft, redRight,
216 greenLeft, greenRight,
217 blueLeft, blueRight );
218 if( brightness->value() != 0 || contrast->value() != 0 ||
219 lumLeft != 0 || lumRight != 255 ||
220 redLeft !=0 || redRight != 255 ||
221 greenLeft != 0 || greenRight != 255 ||
222 blueLeft != 0 || blueRight != 255 )
223 { accept(); }
224 else
225 { reject(); }
226}
227//==============================================
234//==============================================
236{
237 QImage* adjustedImage = new QImage(fileName);
238
239 //convert to 32-bit depth if necessary
240 if( adjustedImage->depth() < 32 )
241 {
242 QImage* tmp = adjustedImage;
243 adjustedImage = new QImage( tmp->convertDepth( 32, Qt::AutoColor ) );
244 delete tmp; tmp=NULL;
245 }
246
247 adjustImage( *adjustedImage );
248 return adjustedImage;
249}
250//==============================================
255//==============================================
260//==============================================
262{ brightness->setValue( 0 ); }
263//==============================================
265{ contrast->setValue( 0 ); }
266//==============================================
267void HistogramEditor::getHistBoundaries(int &lumLeft, int &lumRight,
268 int &redLeft, int &redRight,
269 int &greenLeft, int &greenRight,
270 int &blueLeft, int &blueRight)
271{
272 //sanity check
274 {
275 histogramInterface->getHistBoundaries( lumLeft, lumRight,
276 redLeft, redRight,
277 greenLeft, greenRight,
278 blueLeft, blueRight );
279 }
280 else
281 {
282 lumLeft = 0; lumRight = 255;
283 redLeft = 0; redRight = 255;
284 greenLeft = 0; greenRight = 255;
285 blueLeft = 0; blueRight = 255;
286 }
287}
288//==============================================
290{
291 //get original image
292 QImage origImage = previewInterface->getOrigImage();
293
294 //construct adjusted image
295 QImage adjustedImage = origImage.copy();
296 adjustImage( adjustedImage );
297
298 //set adjusted image
299 previewInterface->setAdjustedImage( adjustedImage );
300}
301//==============================================
302void HistogramEditor::adjustImage( QImage &image )
303{
304 //obtain histogram left and right boundaries
305 //sanity check
306 int lumLeft, lumRight, redLeft, redRight, greenLeft, greenRight, blueLeft, blueRight;
308 {
309 histogramInterface->getHistBoundaries( lumLeft, lumRight,
310 redLeft, redRight,
311 greenLeft, greenRight,
312 blueLeft, blueRight );
313 }
314 else
315 {
316 lumLeft = 0; lumRight = 255;
317 redLeft = 0; redRight = 255;
318 greenLeft = 0; greenRight = 255;
319 blueLeft = 0; blueRight = 255;
320 }
321
322 //modify image
323 double displayToOneScalar = 1.0/255.0;
324 double scaledMeanR = displayToOneScalar*scaleColor( 255.0*meanR, redLeft, redRight );
325 double scaledMeanG = displayToOneScalar*scaleColor( 255.0*meanG, greenLeft, greenRight );
326 double scaledMeanB = displayToOneScalar*scaleColor( 255.0*meanB, blueLeft, blueRight );
327
328 double brightnessScalar, addedBrightnessColor;
329 if(brightness->value() < 0)
330 {
331 brightnessScalar = ((double)(SLIDER_RADIUS + brightness->value()))/SLIDER_RADIUS;
332 addedBrightnessColor = 1.0 - brightnessScalar;
333 }
334 else
335 {
336 brightnessScalar = ((double)(SLIDER_RADIUS - brightness->value()))/SLIDER_RADIUS;
337 addedBrightnessColor = 0.0;
338 }
339
340 int x, y;
341 QRgb* rgb;
342 double r,g,b;
343 double h,s,v;
344 int rPrime, gPrime, bPrime;
345 uchar* scanLine;
346
347 for( y=0; y<image.height(); y++)
348 {
349 scanLine = image.scanLine(y);
350 for( x=0; x<image.width(); x++)
351 {
352 //get rgb value
353 rgb = ((QRgb*)scanLine+x);
354 r = qRed(*rgb);
355 g = qGreen(*rgb);
356 b = qBlue(*rgb);
357
358 //apply histogram boundaries
359 RGBtoHSV(r,g,b,&h,&s,&v);
360 v = scaleColor( v, lumLeft, lumRight );
361 HSVtoRGB( &r,&g,&b, h,s,v);
362
363 r = scaleColor( r, redLeft, redRight );
364 g = scaleColor( g, greenLeft, greenRight );
365 b = scaleColor( b, blueLeft, blueRight );
366
367 //convert to 0-1 scale
368 r = r*displayToOneScalar;
369 g = g*displayToOneScalar;
370 b = b*displayToOneScalar;
371
372 //adjust contrast
373 r = ( (r-scaledMeanR) * (SLIDER_RADIUS-contrast->value()) )/SLIDER_RADIUS + scaledMeanR;
374 g = ( (g-scaledMeanG) * (SLIDER_RADIUS-contrast->value()) )/SLIDER_RADIUS + scaledMeanG;
375 b = ( (b-scaledMeanB) * (SLIDER_RADIUS-contrast->value()) )/SLIDER_RADIUS + scaledMeanB;
376
377 //apply brightness adjustment
378 //http://www.sgi.com/misc/grafica/interp/
379 r = brightnessScalar*r + addedBrightnessColor;
380 g = brightnessScalar*g + addedBrightnessColor;
381 b = brightnessScalar*b + addedBrightnessColor;
382
383 //scale and clamp to 0-255 range
384 rPrime = (int) MIN( MAX((r*255), 0), 255 );
385 gPrime = (int) MIN( MAX((g*255), 0), 255 );
386 bPrime = (int) MIN( MAX((b*255), 0), 255 );
387
388 //set adjusted color value
389 *rgb = qRgb(rPrime, gPrime, bPrime);
390 } //x
391 } //y
392}
393//==============================================
394double HistogramEditor::scaleColor( double color, int left, int right )
395{
396 return MAX( MIN( (255.0*(color-left)) / (right-left), 255), 0 );
397}
398//==============================================
400{
401 if(e->key() == Qt::Key_Control )
402 {
403 PREVIEW_MODE curMode = (PREVIEW_MODE) previewSelection->currentItem();
404 if(curMode == ORIGINAL_IMAGE)
406 else if(curMode == ADJUSTED_IMAGE)
408 else
410 }
411 else { QDialog::keyPressEvent(e); }
412}
413//==============================================
415{
416 if(e->key() == Qt::Key_Control )
417 {
419 false );
420 }
421 else { QDialog::keyReleaseEvent(e); }
422}
423//==============================================
424
425
A clickable label.
void setPixmap(const QPixmap &p)
A more dynamic slider that provides moving tooltips that show the slider value.
void setPrefixes(QString prefix1, QString prefix2)
set two prefix values, one for when the value is positive and one for when the value is negative.
void setZeroString(QString val)
when set, a zero string is shown instead of the current value/prefix/suffix when the slider value is ...
void resetContrast()
reset contrast
HistogramEditor(QString filename, QWidget *parent=0, const char *name=0)
Constructs layout.
void applyAction()
check for changes to settings, if so
QComboBox * previewSelection
void keyPressEvent(QKeyEvent *e)
void resetAction()
resets all adjustments
void selectHistogramType(int selection)
updates historgram interface
DynamicSlider * contrast
Contrast slider.
ClickableLabel * brightnessIcon
void selectPreviewImageType(int selection)
updates preview image
QImage * getModifiedImage()
get modified image that resulted from adjustments
void keyReleaseEvent(QKeyEvent *e)
HistogramInterface * histogramInterface
Histogram view.
double meanR
Mean color values.
void getHistBoundaries(int &lumLeft, int &lumRight, int &redLeft, int &redRight, int &greenLeft, int &greenRight, int &blueLeft, int &blueRight)
returns histogram boundaries
void adjustImage(QImage &image)
DynamicSlider * brightness
Brightness slider.
ClickableLabel * contrastIcon
QComboBox * histogramType
void generateAdjustedPreviewImage()
updates adjusted preview image
ScaledPreviewInterface * previewInterface
Preview image.
double scaleColor(double color, int left, int right)
void resetBrightness()
reset brightness
void setDisplayChannel(DISPLAYED_CHANNEL channel)
Sets currently displayed channel.
void resetBoundaries()
resets all boundaries
void getHistBoundaries(int &lumLeft, int &lumRight, int &redLeft, int &redRight, int &greenLeft, int &greenRight, int &blueLeft, int &blueRight)
returns histogram boundaries
QImage & getOrigImage()
returns orig image object
void setAdjustedImage(QImage adjustedImage)
sets adjusted image and repaints
void setPreviewMode(PREVIEW_MODE mode, bool forceDrawLabel=false)
Sets preview mode.
#define MIN(x, y)
Definition color.cpp:20
#define MAX(x, y)
Definition color.cpp:21
QString IMAGE_PATH
Definition config.cpp:18
#define WIDGET_SPACING
Definition config.h:31
#define SLIDER_RADIUS
DISPLAYED_CHANNEL
chanel histogram displays
void RGBtoHSV(double r, double g, double b, double *h, double *s, double *v)
Convert a RGB color triplet to HSV.
void HSVtoRGB(double *r, double *g, double *b, double h, double s, double v)
Convert a HSV color triplet to RGB.
long b
PREVIEW_MODE
current preview mode
@ ORIGINAL_IMAGE
@ ADJUSTED_IMAGE
@ INV_SPLIT_VIEW