QGIS API Documentation  2.8.6-Wien
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 
static void overlayColor(QImage &image, const QColor &color)
Overlays a color onto an image.
static void multiplyOpacity(QImage &image, const double factor)
Multiplies opacity of image pixel values by a factor.
static void convertToGrayscale(QImage &image, const GrayscaleMode mode=GrayscaleLuminosity)
Convert a QImage to a grayscale image.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
static void distanceTransform(QImage &image, const DistanceTransformProperties &properties)
Performs a distance transform on the source image and shades the result using a color ramp...
double spread
Maximum distance (in pixels) for the distance transform shading to spread.
bool shadeExterior
Set to true to perform the distance transform on transparent pixels in the source image...
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:330
FlipType
Flip operation types.
static void adjustBrightnessContrast(QImage &image, const int brightness, const double contrast)
Alter the brightness or contrast of a QImage.
static QImage * gaussianBlur(QImage &image, const int radius)
Performs a gaussian blur on an image.
static void flipImage(QImage &image, FlipType type)
Flips an image horizontally or vertically.
#define M_PI
bool useMaxDistance
Set to true to automatically calculate the maximum distance in the transform to use as the spread val...
int min(int a, int b)
Definition: util.h:93
QgsVectorColorRampV2 * ramp
Color ramp to use for shading the distance transform.
static void adjustHueSaturation(QImage &image, const double saturation, const QColor &colorizeColor=QColor(), const double colorizeStrength=1.0)
Alter the hue or saturation of a QImage.
static void stackBlur(QImage &image, const int radius, const bool alphaOnly=false)
Performs a stack blur on an image.
#define INF
#define BLOCK_THREADS
GrayscaleMode
Modes for converting a QImage to grayscale.
int max(int a, int b)
Definition: util.h:87
Struct for storing properties of a distance transform operation.