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"
30 #include "scaledPreviewInterface.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 //==============================================
40 HistogramEditor::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:
80  previewInterface = new ScaledPreviewInterface( fileName, visibleFrame,
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:
92  histogramInterface = new HistogramInterface( fileName, visibleFrame,
93  "histogramInterface" );
94 
95  //connect adjustments in histogram to generateAdjustedPreviewImage
96  connect( histogramInterface, SIGNAL( selectedRangeChanged() ),
97  SLOT( generateAdjustedPreviewImage() ) );
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 {
231  resetBrightness();
232  resetContrast();
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 //==============================================
267 void 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
273  if( histogramInterface )
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 //==============================================
302 void 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;
307  if( histogramInterface )
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 //==============================================
394 double 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