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 
#define MIN(x, y)
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 selectPreviewImageType(int selection)
updates preview image
void applyAction()
check for changes to settings, if so
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
QComboBox * histogramType
void resetContrast()
reset contrast
HistogramInterface * histogramInterface
Histogram view.
double meanR
Mean color values.
long b
Definition: jpegInternal.h:125
void setPixmap(const QPixmap &p)
QImage * getModifiedImage()
get modified image that resulted from adjustments
void resetAction()
resets all adjustments
DynamicSlider * brightness
Brightness slider.
void setDisplayChannel(DISPLAYED_CHANNEL channel)
Sets currently displayed channel.
void adjustImage(QImage &image)
double scaleColor(double color, int left, int right)
#define SLIDER_RADIUS
QString IMAGE_PATH
Definition: config.cpp:18
ClickableLabel * contrastIcon
ClickableLabel * brightnessIcon
void keyReleaseEvent(QKeyEvent *e)
A clickable label.
HistogramEditor(QString filename, QWidget *parent=0, const char *name=0)
Constructs layout.
QImage & getOrigImage()
returns orig image object
DISPLAYED_CHANNEL
chanel histogram displays
#define WIDGET_SPACING
Definition: config.h:31
void generateAdjustedPreviewImage()
updates adjusted preview image
Q3Frame * buttonsFrame
#define MAX(x, y)
DynamicSlider * contrast
Contrast slider.
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 selectHistogramType(int selection)
updates historgram interface
A more dynamic slider that provides moving tooltips that show the slider value.
Definition: dynamicSlider.h:23
void setPreviewMode(PREVIEW_MODE mode, bool forceDrawLabel=false)
Sets preview mode.
PREVIEW_MODE
current preview mode
ScaledPreviewInterface * previewInterface
Preview image.
void setZeroString(QString val)
when set, a zero string is shown instead of the current value/prefix/suffix when the slider value is ...
void getHistBoundaries(int &lumLeft, int &lumRight, int &redLeft, int &redRight, int &greenLeft, int &greenRight, int &blueLeft, int &blueRight)
returns histogram boundaries
void resetBoundaries()
resets all boundaries
void setAdjustedImage(QImage adjustedImage)
sets adjusted image and repaints
void keyPressEvent(QKeyEvent *e)
void getHistBoundaries(int &lumLeft, int &lumRight, int &redLeft, int &redRight, int &greenLeft, int &greenRight, int &blueLeft, int &blueRight)
returns histogram boundaries
QComboBox * previewSelection
void resetBrightness()
reset brightness