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"
35#include "../../backend/tools/imageTools.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//==============================================
229{
233}
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//==============================================
252{
254}
255//==============================================
257{
259}
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.
Definition: dynamicSlider.h:24
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)
Q3Frame * buttonsFrame
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.
QString IMAGE_PATH
Definition: config.cpp:18
#define WIDGET_SPACING
Definition: config.h:31
#define SLIDER_RADIUS
#define MIN(x, y)
#define MAX(x, y)
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.
Definition: imageTools.cpp:231
void HSVtoRGB(double *r, double *g, double *b, double h, double s, double v)
Convert a HSV color triplet to RGB.
Definition: imageTools.cpp:264
long b
Definition: jpegInternal.h:125
PREVIEW_MODE
current preview mode
@ ORIGINAL_IMAGE
@ ADJUSTED_IMAGE
@ INV_SPLIT_VIEW