QGIS API Documentation  2.8.2-Wien
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgsimageoperation.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsimageoperation.cpp
3  ----------------------
4  begin : January 2015
5  copyright : (C) 2015 by Nyall Dawson
6  email : nyall.dawson@gmail.com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgsimageoperation.h"
19 #include "qgis.h"
20 #include "qgsvectorcolorrampv2.h"
21 #include "qgslogger.h"
22 #include <QtConcurrentMap>
23 #include <QColor>
24 #include <QPainter>
25 #include <qmath.h>
26 
27 //determined via trial-and-error. Could possibly be optimised, or varied
28 //depending on the image size.
29 #define BLOCK_THREADS 16
30 
31 #define INF 1E20
32 
33 template <typename PixelOperation>
34 void QgsImageOperation::runPixelOperation( QImage &image, PixelOperation& operation )
35 {
36  if ( image.height() * image.width() < 100000 )
37  {
38  //small image, don't multithread
39  //this threshold was determined via testing various images
40  runPixelOperationOnWholeImage( image, operation );
41  }
42  else
43  {
44  //large image, multithread operation
45  QgsImageOperation::ProcessBlockUsingPixelOperation<PixelOperation> blockOp( operation ) ;
46  runBlockOperationInThreads( image, blockOp, QgsImageOperation::ByRow );
47  }
48 }
49 
50 template <typename PixelOperation>
51 void QgsImageOperation::runPixelOperationOnWholeImage( QImage &image, PixelOperation& operation )
52 {
53  int height = image.height();
54  int width = image.width();
55  for ( int y = 0; y < height; ++y )
56  {
57  QRgb* ref = ( QRgb* )image.scanLine( y );
58  for ( int x = 0; x < width; ++x )
59  {
60  operation( ref[x], x, y );
61  }
62  }
63 }
64 
65 //rect operations
66 
67 template <typename RectOperation>
68 void QgsImageOperation::runRectOperation( QImage &image, RectOperation& operation )
69 {
70  //possibly could be tweaked for rect operations
71  if ( image.height() * image.width() < 100000 )
72  {
73  //small image, don't multithread
74  //this threshold was determined via testing various images
75  runRectOperationOnWholeImage( image, operation );
76  }
77  else
78  {
79  //large image, multithread operation
80  runBlockOperationInThreads( image, operation, ByRow );
81  }
82 }
83 
84 template <class RectOperation>
85 void QgsImageOperation::runRectOperationOnWholeImage( QImage &image, RectOperation& operation )
86 {
87  ImageBlock fullImage;
88  fullImage.beginLine = 0;
89  fullImage.endLine = image.height();
90  fullImage.lineLength = image.width();
91  fullImage.image = &image;
92 
93  operation( fullImage );
94 }
95 
96 //linear operations
97 
98 template <typename LineOperation>
99 void QgsImageOperation::runLineOperation( QImage &image, LineOperation& operation )
100 {
101  //possibly could be tweaked for rect operations
102  if ( image.height() * image.width() < 100000 )
103  {
104  //small image, don't multithread
105  //this threshold was determined via testing various images
106  runLineOperationOnWholeImage( image, operation );
107  }
108  else
109  {
110  //large image, multithread operation
111  QgsImageOperation::ProcessBlockUsingLineOperation<LineOperation> blockOp( operation ) ;
112  runBlockOperationInThreads( image, blockOp, operation.direction() );
113  }
114 }
115 
116 template <class LineOperation>
117 void QgsImageOperation::runLineOperationOnWholeImage( QImage &image, LineOperation& operation )
118 {
119  int height = image.height();
120  int width = image.width();
121 
122  //do something with whole lines
123  int bpl = image.bytesPerLine();
124  if ( operation.direction() == ByRow )
125  {
126  for ( int y = 0; y < height; ++y )
127  {
128  QRgb* ref = ( QRgb* )image.scanLine( y );
129  operation( ref, width, bpl );
130  }
131  }
132  else
133  {
134  //by column
135  unsigned char* ref = image.scanLine( 0 );
136  for ( int x = 0; x < width; ++x, ref += 4 )
137  {
138  operation(( QRgb* )ref, height, bpl );
139  }
140  }
141 }
142 
143 
144 //multithreaded block processing
145 
146 template <typename BlockOperation>
147 void QgsImageOperation::runBlockOperationInThreads( QImage &image, BlockOperation &operation, LineOperationDirection direction )
148 {
149  QList< ImageBlock > blocks;
150  unsigned int height = image.height();
151  unsigned int width = image.width();
152 
153  unsigned int blockDimension1 = ( direction == QgsImageOperation::ByRow ) ? height : width;
154  unsigned int blockDimension2 = ( direction == QgsImageOperation::ByRow ) ? width : height;
155 
156  //chunk image up into vertical blocks
157  blocks.reserve( BLOCK_THREADS );
158  unsigned int begin = 0;
159  unsigned int blockLen = blockDimension1 / BLOCK_THREADS;
160  for ( unsigned int block = 0; block < BLOCK_THREADS; ++block, begin += blockLen )
161  {
162  ImageBlock newBlock;
163  newBlock.beginLine = begin;
164  //make sure last block goes to end of image
165  newBlock.endLine = block < ( BLOCK_THREADS - 1 ) ? begin + blockLen : blockDimension1;
166  newBlock.lineLength = blockDimension2;
167  newBlock.image = &image;
168  blocks << newBlock;
169  }
170 
171  //process blocks
172  QtConcurrent::blockingMap( blocks, operation );
173 }
174 
175 
176 //
177 //operation specific code
178 //
179 
180 //grayscale
181 
182 void QgsImageOperation::convertToGrayscale( QImage &image, const GrayscaleMode mode )
183 {
184  GrayscalePixelOperation operation( mode );
185  runPixelOperation( image, operation );
186 }
187 
188 void QgsImageOperation::GrayscalePixelOperation::operator()( QRgb &rgb, const int x, const int y )
189 {
190  Q_UNUSED( x );
191  Q_UNUSED( y );
192  switch ( mMode )
193  {
194  case GrayscaleLuminosity:
195  grayscaleLuminosityOp( rgb );
196  return;
197  case GrayscaleAverage:
198  grayscaleAverageOp( rgb );
199  return;
200  case GrayscaleLightness:
201  default:
202  grayscaleLightnessOp( rgb );
203  return;
204  }
205 }
206 
207 void QgsImageOperation::grayscaleLightnessOp( QRgb &rgb )
208 {
209  int red = qRed( rgb );
210  int green = qGreen( rgb );
211  int blue = qBlue( rgb );
212 
213  int min = qMin( qMin( red, green ), blue );
214  int max = qMax( qMax( red, green ), blue );
215 
216  int lightness = qMin(( min + max ) / 2, 255 );
217  rgb = qRgba( lightness, lightness, lightness, qAlpha( rgb ) );
218 }
219 
220 void QgsImageOperation::grayscaleLuminosityOp( QRgb &rgb )
221 {
222  int luminosity = 0.21 * qRed( rgb ) + 0.72 * qGreen( rgb ) + 0.07 * qBlue( rgb );
223  rgb = qRgba( luminosity, luminosity, luminosity, qAlpha( rgb ) );
224 }
225 
226 void QgsImageOperation::grayscaleAverageOp( QRgb &rgb )
227 {
228  int average = ( qRed( rgb ) + qGreen( rgb ) + qBlue( rgb ) ) / 3;
229  rgb = qRgba( average, average, average, qAlpha( rgb ) );
230 }
231 
232 
233 //brightness/contrast
234 
235 void QgsImageOperation::adjustBrightnessContrast( QImage &image, const int brightness, const double contrast )
236 {
237  BrightnessContrastPixelOperation operation( brightness, contrast );
238  runPixelOperation( image, operation );
239 }
240 
241 void QgsImageOperation::BrightnessContrastPixelOperation::operator()( QRgb &rgb, const int x, const int y )
242 {
243  Q_UNUSED( x );
244  Q_UNUSED( y );
245  int red = adjustColorComponent( qRed( rgb ), mBrightness, mContrast );
246  int blue = adjustColorComponent( qBlue( rgb ), mBrightness, mContrast );
247  int green = adjustColorComponent( qGreen( rgb ), mBrightness, mContrast );
248  rgb = qRgba( red, green, blue, qAlpha( rgb ) );
249 }
250 
251 int QgsImageOperation::adjustColorComponent( int colorComponent, int brightness, double contrastFactor )
252 {
253  return qBound( 0, ( int )(((((( colorComponent / 255.0 ) - 0.5 ) * contrastFactor ) + 0.5 ) * 255 ) + brightness ), 255 );
254 }
255 
256 //hue/saturation
257 
258 void QgsImageOperation::adjustHueSaturation( QImage &image, const double saturation, const QColor &colorizeColor, const double colorizeStrength )
259 {
260  HueSaturationPixelOperation operation( saturation, colorizeColor.isValid() && colorizeStrength > 0.0,
261  colorizeColor.hue(), colorizeColor.saturation(), colorizeStrength );
262  runPixelOperation( image, operation );
263 }
264 
265 void QgsImageOperation::HueSaturationPixelOperation::operator()( QRgb &rgb, const int x, const int y )
266 {
267  Q_UNUSED( x );
268  Q_UNUSED( y );
269  QColor tmpColor( rgb );
270  int h, s, l;
271  tmpColor.getHsl( &h, &s, &l );
272 
273  if ( mSaturation < 1.0 )
274  {
275  // Lowering the saturation. Use a simple linear relationship
276  s = qMin(( int )( s * mSaturation ), 255 );
277  }
278  else if ( mSaturation > 1.0 )
279  {
280  // Raising the saturation. Use a saturation curve to prevent
281  // clipping at maximum saturation with ugly results.
282  s = qMin(( int )( 255. * ( 1 - qPow( 1 - ( s / 255. ), qPow( mSaturation, 2 ) ) ) ), 255 );
283  }
284 
285  if ( mColorize )
286  {
287  h = mColorizeHue;
288  s = mColorizeSaturation;
289  if ( mColorizeStrength < 1.0 )
290  {
291  //get rgb for colorized color
292  QColor colorizedColor = QColor::fromHsl( h, s, l );
293  int colorizedR, colorizedG, colorizedB;
294  colorizedColor.getRgb( &colorizedR, &colorizedG, &colorizedB );
295 
296  // Now, linearly scale by colorize strength
297  int r = mColorizeStrength * colorizedR + ( 1 - mColorizeStrength ) * tmpColor.red();
298  int g = mColorizeStrength * colorizedG + ( 1 - mColorizeStrength ) * tmpColor.green();
299  int b = mColorizeStrength * colorizedB + ( 1 - mColorizeStrength ) * tmpColor.blue();
300 
301  rgb = qRgba( r, g, b, qAlpha( rgb ) );
302  return;
303  }
304  }
305 
306  tmpColor.setHsl( h, s, l, qAlpha( rgb ) );
307  rgb = tmpColor.rgba();
308 }
309 
310 //multiply opacity
311 
312 void QgsImageOperation::multiplyOpacity( QImage &image, const double factor )
313 {
314  if ( qgsDoubleNear( factor, 1.0 ) )
315  {
316  //no change
317  return;
318  }
319  else if ( factor < 1.0 )
320  {
321  //decreasing opacity - we can use the faster DestinationIn composition mode
322  //to reduce the alpha channel
323  QColor transparentFillColor = QColor( 0, 0, 0, 255 * factor );
324  QPainter painter( &image );
325  painter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
326  painter.fillRect( 0, 0, image.width(), image.height(), transparentFillColor );
327  painter.end();
328  }
329  else
330  {
331  //increasing opacity - run this as a pixel operation for multithreading
332  MultiplyOpacityPixelOperation operation( factor );
333  runPixelOperation( image, operation );
334  }
335 }
336 
337 void QgsImageOperation::MultiplyOpacityPixelOperation::operator()( QRgb &rgb, const int x, const int y )
338 {
339  Q_UNUSED( x );
340  Q_UNUSED( y );
341  rgb = qRgba( qRed( rgb ), qGreen( rgb ), qBlue( rgb ), qBound( 0, qRound( mFactor * qAlpha( rgb ) ), 255 ) );
342 }
343 
344 // overlay color
345 
346 void QgsImageOperation::overlayColor( QImage &image, const QColor &color )
347 {
348  QColor opaqueColor = color;
349  opaqueColor.setAlpha( 255 );
350 
351  //use QPainter SourceIn composition mode to overlay color (fast)
352  //this retains image's alpha channel but replaces color
353  QPainter painter( &image );
354  painter.setCompositionMode( QPainter::CompositionMode_SourceIn );
355  painter.fillRect( 0, 0, image.width(), image.height(), opaqueColor );
356  painter.end();
357 }
358 
359 // distance transform
360 
362 {
363  if ( ! properties.ramp )
364  {
365  QgsDebugMsg( QString( "no color ramp specified for distance transform" ) );
366  return;
367  }
368 
369  //first convert to 1 bit alpha mask array
370  double * array = new double[ image.width() * image.height()];
371  ConvertToArrayPixelOperation convertToArray( image.width(), array, properties.shadeExterior );
372  runPixelOperation( image, convertToArray );
373 
374  //calculate distance transform (single threaded only)
375  distanceTransform2d( array, image.width(), image.height() );
376 
377  double spread;
378  if ( properties.useMaxDistance )
379  {
380  spread = sqrt( maxValueInDistanceTransformArray( array, image.width() * image.height() ) );
381  }
382  else
383  {
384  spread = properties.spread;
385  }
386 
387  //shade distance transform
388  ShadeFromArrayOperation shadeFromArray( image.width(), array, spread, properties );
389  runPixelOperation( image, shadeFromArray );
390  delete [] array;
391 }
392 
393 void QgsImageOperation::ConvertToArrayPixelOperation::operator()( QRgb &rgb, const int x, const int y )
394 {
395 
396  // TODO - try initial distance = 1-alphaF, INF for alphaF = 0 only
397 
398  int idx = y * mWidth + x;
399  if (( mExterior && qAlpha( rgb ) >= mAlphaThreshold ) || ( !mExterior && qAlpha( rgb ) < mAlphaThreshold ) )
400  {
401  //opaque pixel, so zero distance
402  mArray[ idx ] = 0;
403  }
404  else
405  {
406  //transparent pixel, so initially set distance as infinite
407  mArray[ idx ] = INF;
408  }
409 }
410 
411 //fast distance transform code, adapted from http://cs.brown.edu/~pff/dt/
412 
413 /* distance transform of a 1d function using squared distance */
414 void QgsImageOperation::distanceTransform1d( double *f, int n, int *v, double *z, double *d )
415 {
416  int k = 0;
417  v[0] = 0;
418  z[0] = -INF;
419  z[1] = + INF;
420  for ( int q = 1; q <= n - 1; q++ )
421  {
422  double s = (( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
423  while ( s <= z[k] )
424  {
425  k--;
426  s = (( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
427  }
428  k++;
429  v[k] = q;
430  z[k] = s;
431  z[k+1] = + INF;
432  }
433 
434  k = 0;
435  for ( int q = 0; q <= n - 1; q++ )
436  {
437  while ( z[k+1] < q )
438  k++;
439  d[q] = ( q - v[k] ) * ( q - v[k] ) + f[v[k]];
440  }
441 }
442 
443 double QgsImageOperation::maxValueInDistanceTransformArray( const double *array, const unsigned int size )
444 {
445  double dtMaxValue = array[0];
446  for ( unsigned int i = 1; i < size; ++i )
447  {
448  if ( array[i] > dtMaxValue )
449  {
450  dtMaxValue = array[i];
451  }
452  }
453  return dtMaxValue;
454 }
455 
456 /* distance transform of 2d function using squared distance */
457 void QgsImageOperation::distanceTransform2d( double * im, int width, int height )
458 {
459  int maxDimension = qMax( width, height );
460 
461  double *f = new double[ maxDimension ];
462  int *v = new int[ maxDimension ];
463  double *z = new double[ maxDimension + 1 ];
464  double *d = new double[ maxDimension ];
465 
466  // transform along columns
467  for ( int x = 0; x < width; x++ )
468  {
469  for ( int y = 0; y < height; y++ )
470  {
471  f[y] = im[ x + y * width ];
472  }
473  distanceTransform1d( f, height, v, z, d );
474  for ( int y = 0; y < height; y++ )
475  {
476  im[ x + y * width ] = d[y];
477  }
478  }
479 
480  // transform along rows
481  for ( int y = 0; y < height; y++ )
482  {
483  for ( int x = 0; x < width; x++ )
484  {
485  f[x] = im[ x + y*width ];
486  }
487  distanceTransform1d( f, width, v, z, d );
488  for ( int x = 0; x < width; x++ )
489  {
490  im[ x + y*width ] = d[x];
491  }
492  }
493 
494  delete [] d;
495  delete [] f;
496  delete [] v;
497  delete [] z;
498 }
499 
500 void QgsImageOperation::ShadeFromArrayOperation::operator()( QRgb &rgb, const int x, const int y )
501 {
502  if ( ! mProperties.ramp )
503  return;
504 
505  if ( mSpread == 0 )
506  {
507  rgb = mProperties.ramp->color( 1.0 ).rgba();
508  return;
509  }
510 
511  int idx = y * mWidth + x;
512 
513  //values are distance squared
514  double squaredVal = mArray[ idx ];
515  if ( squaredVal > mSpreadSquared )
516  {
517  rgb = Qt::transparent;
518  return;
519  }
520  double val = squaredVal > 0 ? qMin(( sqrt( squaredVal ) / mSpread ), 1.0 ) : 0;
521 
522  rgb = mProperties.ramp->color( val ).rgba();
523 }
524 
525 //stack blur
526 
527 void QgsImageOperation::stackBlur( QImage &image, const int radius, const bool alphaOnly )
528 {
529  // culled from Qt's qpixmapfilter.cpp, see: http://www.qtcentre.org/archive/index.php/t-26534.html
530  int tab[] = { 14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 };
531  int alpha = ( radius < 1 ) ? 16 : ( radius > 17 ) ? 1 : tab[radius-1];
532 
533  int i1 = 0;
534  int i2 = 3;
535 
536  //ensure correct source format.
537  QImage::Format originalFormat = image.format();
538  QImage* pImage = &image;
539  if ( !alphaOnly && originalFormat != QImage::Format_ARGB32_Premultiplied )
540  {
541  pImage = new QImage( image.convertToFormat( QImage::Format_ARGB32_Premultiplied ) );
542  }
543  else if ( alphaOnly && originalFormat != QImage::Format_ARGB32 )
544  {
545  pImage = new QImage( image.convertToFormat( QImage::Format_ARGB32 ) );
546  }
547 
548  if ( alphaOnly )
549  i1 = i2 = ( QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3 );
550 
551  StackBlurLineOperation topToBottomBlur( alpha, QgsImageOperation::ByColumn, true, i1, i2 );
552  runLineOperation( *pImage, topToBottomBlur );
553 
554  StackBlurLineOperation leftToRightBlur( alpha, QgsImageOperation::ByRow, true, i1, i2 );
555  runLineOperation( *pImage, leftToRightBlur );
556 
557  StackBlurLineOperation bottomToTopBlur( alpha, QgsImageOperation::ByColumn, false, i1, i2 );
558  runLineOperation( *pImage, bottomToTopBlur );
559 
560  StackBlurLineOperation rightToLeftBlur( alpha, QgsImageOperation::ByRow, false, i1, i2 );
561  runLineOperation( *pImage, rightToLeftBlur );
562 
563  if ( pImage->format() != originalFormat )
564  {
565  image = pImage->convertToFormat( originalFormat );
566  delete pImage;
567  }
568 }
569 
570 void QgsImageOperation::StackBlurLineOperation::operator()( QRgb* startRef, const int lineLength, const int bytesPerLine )
571 {
572  unsigned char* p = ( unsigned char* )startRef;
573  int rgba[4];
574  int increment = ( mDirection == QgsImageOperation::ByRow ) ? 4 : bytesPerLine;
575  if ( !mForwardDirection )
576  {
577  p += ( lineLength - 1 ) * increment;
578  increment = -increment;
579  }
580 
581  for ( int i = mi1; i <= mi2; ++i )
582  {
583  rgba[i] = p[i] << 4;
584  }
585 
586  p += increment;
587  for ( int j = 1; j < lineLength; ++j, p += increment )
588  {
589  for ( int i = mi1; i <= mi2; ++i )
590  {
591  p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * mAlpha / 16 ) >> 4;
592  }
593  }
594 }
595 
596 //gaussian blur
597 
598 QImage *QgsImageOperation::gaussianBlur( QImage &image, const int radius )
599 {
600  int width = image.width();
601  int height = image.height();
602 
603  if ( radius <= 0 )
604  {
605  //just make an unchanged copy
606  QImage* copy = new QImage( image.copy() );
607  return copy;
608  }
609 
610  double* kernel = createGaussianKernel( radius );
611 
612  //ensure correct source format.
613  QImage::Format originalFormat = image.format();
614  QImage* pImage = &image;
615  if ( originalFormat != QImage::Format_ARGB32_Premultiplied )
616  {
617  pImage = new QImage( image.convertToFormat( QImage::Format_ARGB32_Premultiplied ) );
618  }
619 
620  //blur along rows
621  QImage xBlurImage = QImage( width, height, QImage::Format_ARGB32_Premultiplied );
622  GaussianBlurOperation rowBlur( radius, QgsImageOperation::ByRow, &xBlurImage, kernel );
623  runRectOperation( *pImage, rowBlur );
624 
625  //blur along columns
626  QImage* yBlurImage = new QImage( width, height, QImage::Format_ARGB32_Premultiplied );
627  GaussianBlurOperation colBlur( radius, QgsImageOperation::ByColumn, yBlurImage, kernel );
628  runRectOperation( xBlurImage, colBlur );
629 
630  delete[] kernel;
631 
632  if ( originalFormat != QImage::Format_ARGB32_Premultiplied )
633  {
634  QImage* convertedImage = new QImage( yBlurImage->convertToFormat( originalFormat ) );
635  delete yBlurImage;
636  delete pImage;
637  return convertedImage;
638  }
639 
640  return yBlurImage;
641 }
642 
643 void QgsImageOperation::GaussianBlurOperation::operator()( QgsImageOperation::ImageBlock &block )
644 {
645  int width = block.image->width();
646  int height = block.image->height();
647  int sourceBpl = block.image->bytesPerLine();
648 
649  unsigned char* outputLineRef = mDestImage->scanLine( block.beginLine );
650  QRgb* destRef = 0;
651  if ( mDirection == ByRow )
652  {
653  unsigned char* sourceFirstLine = block.image->scanLine( 0 );
654  unsigned char* sourceRef;
655 
656  //blur along rows
657  for ( unsigned int y = block.beginLine; y < block.endLine; ++y, outputLineRef += mDestImageBpl )
658  {
659  sourceRef = sourceFirstLine;
660  destRef = ( QRgb* )outputLineRef;
661  for ( int x = 0; x < width; ++x, ++destRef, sourceRef += 4 )
662  {
663  *destRef = gaussianBlurVertical( y, sourceRef, sourceBpl, height );
664  }
665  }
666  }
667  else
668  {
669  unsigned char* sourceRef = block.image->scanLine( block.beginLine );
670  for ( unsigned int y = block.beginLine; y < block.endLine; ++y, outputLineRef += mDestImageBpl, sourceRef += sourceBpl )
671  {
672  destRef = ( QRgb* )outputLineRef;
673  for ( int x = 0; x < width; ++x, ++destRef )
674  {
675  *destRef = gaussianBlurHorizontal( x, sourceRef, width );
676  }
677  }
678  }
679 }
680 
681 inline QRgb QgsImageOperation::GaussianBlurOperation::gaussianBlurVertical( const int posy, unsigned char *sourceFirstLine, const int sourceBpl, const int height )
682 {
683  double r = 0;
684  double b = 0;
685  double g = 0;
686  double a = 0;
687  int y;
688  unsigned char *ref;
689 
690  for ( int i = 0; i <= mRadius*2; ++i )
691  {
692  y = qBound( 0, posy + ( i - mRadius ), height - 1 );
693  ref = sourceFirstLine + sourceBpl * y;
694 
695  QRgb* refRgb = ( QRgb* )ref;
696  r += mKernel[i] * qRed( *refRgb );
697  g += mKernel[i] * qGreen( *refRgb );
698  b += mKernel[i] * qBlue( *refRgb );
699  a += mKernel[i] * qAlpha( *refRgb );
700  }
701 
702  return qRgba( r, g, b, a );
703 }
704 
705 inline QRgb QgsImageOperation::GaussianBlurOperation::gaussianBlurHorizontal( const int posx, unsigned char *sourceFirstLine, const int width )
706 {
707  double r = 0;
708  double b = 0;
709  double g = 0;
710  double a = 0;
711  int x;
712  unsigned char *ref;
713 
714  for ( int i = 0; i <= mRadius*2; ++i )
715  {
716  x = qBound( 0, posx + ( i - mRadius ), width - 1 );
717  ref = sourceFirstLine + x * 4;
718 
719  QRgb* refRgb = ( QRgb* )ref;
720  r += mKernel[i] * qRed( *refRgb );
721  g += mKernel[i] * qGreen( *refRgb );
722  b += mKernel[i] * qBlue( *refRgb );
723  a += mKernel[i] * qAlpha( *refRgb );
724  }
725 
726  return qRgba( r, g, b, a );
727 }
728 
729 
730 double* QgsImageOperation::createGaussianKernel( const int radius )
731 {
732  double* kernel = new double[ radius*2+1 ];
733  double sigma = radius / 3.0;
734  double twoSigmaSquared = 2 * sigma * sigma;
735  double coefficient = 1.0 / sqrt( M_PI * twoSigmaSquared );
736  double expCoefficient = -1.0 / twoSigmaSquared;
737 
738  double sum = 0;
739  double result;
740  for ( int i = 0; i <= radius; ++i )
741  {
742  result = coefficient * exp( i * i * expCoefficient );
743  kernel[ radius - i ] = result;
744  sum += result;
745  if ( i > 0 )
746  {
747  kernel[radius + i] = result;
748  sum += result;
749  }
750  }
751  //normalize
752  for ( int i = 0; i <= radius * 2; ++i )
753  {
754  kernel[i] /= sum;
755  }
756  return kernel;
757 }
758 
759 
760 // flip
761 
763 {
764  FlipLineOperation flipOperation( type == QgsImageOperation::FlipHorizontal ? QgsImageOperation::ByRow : QgsImageOperation::ByColumn );
765  runLineOperation( image, flipOperation );
766 }
767 
768 void QgsImageOperation::FlipLineOperation::operator()( QRgb *startRef, const int lineLength, const int bytesPerLine )
769 {
770  int increment = ( mDirection == QgsImageOperation::ByRow ) ? 4 : bytesPerLine;
771 
772  //store temporary line
773  unsigned char* p = ( unsigned char* )startRef;
774  unsigned char* tempLine = new unsigned char[ lineLength * 4 ];
775  for ( int i = 0; i < lineLength * 4; ++i, p += increment )
776  {
777  tempLine[i++] = *( p++ );
778  tempLine[i++] = *( p++ );
779  tempLine[i++] = *( p++ );
780  tempLine[i] = *( p );
781  p -= 3;
782  }
783 
784  //write values back in reverse order
785  p = ( unsigned char* )startRef;
786  for ( int i = ( lineLength - 1 ) * 4; i >= 0; i -= 7, p += increment )
787  {
788  *( p++ ) = tempLine[i++];
789  *( p++ ) = tempLine[i++];
790  *( p++ ) = tempLine[i++];
791  *( p ) = tempLine[i];
792  p -= 3;
793  }
794 
795  delete[] tempLine;
796 }
797 
798 
799 
800