1 /*************************************************************************** 2 qgsimageoperation.h 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 #ifndef QGSIMAGEOPERATION_H 19 #define QGSIMAGEOPERATION_H 20 21 #include <QImage> 22 #include "qgis_sip.h" 23 #include <QColor> 24 25 #include "qgis_core.h" 26 #include <cmath> 27 28 class QgsColorRamp; 29 30 /** 31 * \ingroup core 32 * \class QgsImageOperation 33 * \brief Contains operations and filters which apply to QImages 34 * 35 * A set of optimised pixel manipulation operations and filters which can be applied 36 * to QImages. All operations only apply to ARGB32 format images, and it is left up 37 * to the calling procedure to ensure that any passed images are of the correct 38 * format. 39 * 40 * Operations are written to either modify an image in place or return a new image, depending 41 * on which is faster for the particular operation. 42 * 43 * \since QGIS 2.7 44 */ 45 class CORE_EXPORT QgsImageOperation 46 { 47 48 public: 49 50 /** 51 * Modes for converting a QImage to grayscale 52 */ 53 enum GrayscaleMode 54 { 55 GrayscaleLightness, //!< Keep the lightness of the color, drops the saturation 56 GrayscaleLuminosity, //!< Grayscale by perceptual luminosity (weighted sum of color RGB components) 57 GrayscaleAverage, //!< Grayscale by taking average of color RGB components 58 GrayscaleOff //!< No change 59 }; 60 61 /** 62 * Flip operation types 63 */ 64 enum FlipType 65 { 66 FlipHorizontal, //!< Flip the image horizontally 67 FlipVertical //!< Flip the image vertically 68 }; 69 70 /** 71 * Convert a QImage to a grayscale image. Alpha channel is preserved. 72 * \param image QImage to convert 73 * \param mode mode to use during grayscale conversion 74 */ 75 static void convertToGrayscale( QImage &image, GrayscaleMode mode = GrayscaleLuminosity ); 76 77 /** 78 * Alter the brightness or contrast of a QImage. 79 * \param image QImage to alter 80 * \param brightness brightness value, in the range -255 to 255. A brightness value of 0 indicates 81 * no change to brightness, a negative value will darken the image, and a positive value will brighten 82 * the image. 83 * \param contrast contrast value. Must be a positive or zero value. A value of 1.0 indicates no change 84 * to the contrast, a value of 0 represents an image with 0 contrast, and a value > 1.0 will increase the 85 * contrast of the image. 86 */ 87 static void adjustBrightnessContrast( QImage &image, int brightness, double contrast ); 88 89 /** 90 * Alter the hue or saturation of a QImage. 91 * \param image QImage to alter 92 * \param saturation double between 0 and 2 inclusive, where 0 = desaturate and 1.0 = no change 93 * \param colorizeColor color to use for colorizing image. Set to an invalid QColor to disable 94 * colorization. 95 * \param colorizeStrength double between 0 and 1, where 0 = no colorization and 1.0 = full colorization 96 */ 97 static void adjustHueSaturation( QImage &image, double saturation, const QColor &colorizeColor = QColor(), 98 double colorizeStrength = 1.0 ); 99 100 /** 101 * Multiplies opacity of image pixel values by a factor. 102 * \param image QImage to alter 103 * \param factor factor to multiple pixel's opacity by 104 */ 105 static void multiplyOpacity( QImage &image, double factor ); 106 107 /** 108 * Overlays a color onto an image. This operation retains the alpha channel of the 109 * original image, but replaces all image pixel colors with the specified color. 110 * \param image QImage to alter 111 * \param color color to overlay (any alpha component of the color is ignored) 112 */ 113 static void overlayColor( QImage &image, const QColor &color ); 114 115 //! Struct for storing properties of a distance transform operation 116 struct DistanceTransformProperties 117 { 118 119 /** 120 * Set to TRUE to perform the distance transform on transparent pixels 121 * in the source image, set to FALSE to perform the distance transform 122 * on opaque pixels 123 */ 124 bool shadeExterior = true; 125 126 /** 127 * Set to TRUE to automatically calculate the maximum distance in the 128 * transform to use as the spread value 129 */ 130 bool useMaxDistance = true; 131 132 /** 133 * Maximum distance (in pixels) for the distance transform shading to 134 * spread 135 */ 136 double spread = 10.0; 137 138 /** 139 * Color ramp to use for shading the distance transform 140 */ 141 QgsColorRamp *ramp = nullptr; 142 }; 143 144 /** 145 * Performs a distance transform on the source image and shades the result 146 * using a color ramp. 147 * \param image QImage to alter 148 * \param properties DistanceTransformProperties object with parameters 149 * for the distance transform operation 150 */ 151 static void distanceTransform( QImage &image, const QgsImageOperation::DistanceTransformProperties &properties ); 152 153 /** 154 * Performs a stack blur on an image. Stack blur represents a good balance between 155 * speed and blur quality. 156 * \param image QImage to blur 157 * \param radius blur radius in pixels, maximum value of 16 158 * \param alphaOnly set to TRUE to blur only the alpha component of the image 159 * \note for fastest operation, ensure the source image is ARGB32_Premultiplied if 160 * alphaOnly is set to FALSE, or ARGB32 if alphaOnly is TRUE 161 */ 162 static void stackBlur( QImage &image, int radius, bool alphaOnly = false ); 163 164 /** 165 * Performs a gaussian blur on an image. Gaussian blur is slower but results in a high 166 * quality blur. 167 * \param image QImage to blur 168 * \param radius blur radius in pixels 169 * \returns blurred image 170 * \note for fastest operation, ensure the source image is ARGB32_Premultiplied 171 */ 172 static QImage *gaussianBlur( QImage &image, int radius ) SIP_FACTORY; 173 174 /** 175 * Flips an image horizontally or vertically 176 * \param image QImage to flip 177 * \param type type of flip to perform (horizontal or vertical) 178 */ 179 static void flipImage( QImage &image, FlipType type ); 180 181 /** 182 * Calculates the non-transparent region of an image. 183 * \param image source image 184 * \param minSize minimum size for returned region, if desired. If the 185 * non-transparent region of the image is smaller than this minimum size, 186 * it will be centered in the returned rectangle. 187 * \param center return rectangle will be centered on the center of the original image if set to TRUE 188 * \see cropTransparent 189 * \since QGIS 2.9 190 */ 191 static QRect nonTransparentImageRect( const QImage &image, QSize minSize = QSize(), bool center = false ); 192 193 /** 194 * Crop any transparent border from around an image. 195 * \param image source image 196 * \param minSize minimum size for cropped image, if desired. If the 197 * cropped image is smaller than the minimum size, it will be centered 198 * in the returned image. 199 * \param center cropped image will be centered on the center of the original image if set to TRUE 200 * \since QGIS 2.9 201 */ 202 static QImage cropTransparent( const QImage &image, QSize minSize = QSize(), bool center = false ); 203 204 private: 205 206 //for blocked operations 207 enum LineOperationDirection 208 { 209 ByRow, 210 ByColumn 211 }; 212 template <class BlockOperation> static void runBlockOperationInThreads( QImage &image, BlockOperation &operation, LineOperationDirection direction ); 213 struct ImageBlock 214 { 215 unsigned int beginLine; 216 unsigned int endLine; 217 unsigned int lineLength; 218 QImage *image = nullptr; 219 }; 220 221 //for rect operations 222 template <typename RectOperation> static void runRectOperation( QImage &image, RectOperation &operation ); 223 template <class RectOperation> static void runRectOperationOnWholeImage( QImage &image, RectOperation &operation ); 224 225 //for per pixel operations 226 template <class PixelOperation> static void runPixelOperation( QImage &image, PixelOperation &operation ); 227 template <class PixelOperation> static void runPixelOperationOnWholeImage( QImage &image, PixelOperation &operation ); 228 template <class PixelOperation> 229 struct ProcessBlockUsingPixelOperation 230 { ProcessBlockUsingPixelOperationProcessBlockUsingPixelOperation231 explicit ProcessBlockUsingPixelOperation( PixelOperation &operation ) 232 : mOperation( operation ) { } 233 234 typedef void result_type; 235 operatorProcessBlockUsingPixelOperation236 void operator()( ImageBlock &block ) 237 { 238 for ( unsigned int y = block.beginLine; y < block.endLine; ++y ) 239 { 240 QRgb *ref = reinterpret_cast< QRgb * >( block.image->scanLine( y ) ); 241 for ( unsigned int x = 0; x < block.lineLength; ++x ) 242 { 243 mOperation( ref[x], x, y ); 244 } 245 } 246 } 247 248 PixelOperation &mOperation; 249 }; 250 251 //for linear operations 252 template <typename LineOperation> static void runLineOperation( QImage &image, LineOperation &operation ); 253 template <class LineOperation> static void runLineOperationOnWholeImage( QImage &image, LineOperation &operation ); 254 template <class LineOperation> 255 struct ProcessBlockUsingLineOperation 256 { ProcessBlockUsingLineOperationProcessBlockUsingLineOperation257 explicit ProcessBlockUsingLineOperation( LineOperation &operation ) 258 : mOperation( operation ) { } 259 260 typedef void result_type; 261 operatorProcessBlockUsingLineOperation262 void operator()( ImageBlock &block ) 263 { 264 //do something with whole lines 265 int bpl = block.image->bytesPerLine(); 266 if ( mOperation.direction() == ByRow ) 267 { 268 for ( unsigned int y = block.beginLine; y < block.endLine; ++y ) 269 { 270 QRgb *ref = reinterpret_cast< QRgb * >( block.image->scanLine( y ) ); 271 mOperation( ref, block.lineLength, bpl ); 272 } 273 } 274 else 275 { 276 //by column 277 unsigned char *ref = block.image->scanLine( 0 ) + 4 * block.beginLine; 278 for ( unsigned int x = block.beginLine; x < block.endLine; ++x, ref += 4 ) 279 { 280 mOperation( reinterpret_cast< QRgb * >( ref ), block.lineLength, bpl ); 281 } 282 } 283 } 284 285 LineOperation &mOperation; 286 }; 287 288 289 //individual operation implementations 290 291 class GrayscalePixelOperation 292 { 293 public: GrayscalePixelOperation(const GrayscaleMode mode)294 explicit GrayscalePixelOperation( const GrayscaleMode mode ) 295 : mMode( mode ) 296 { } 297 298 void operator()( QRgb &rgb, int x, int y ); 299 300 private: 301 GrayscaleMode mMode; 302 }; 303 static void grayscaleLightnessOp( QRgb &rgb ); 304 static void grayscaleLuminosityOp( QRgb &rgb ); 305 static void grayscaleAverageOp( QRgb &rgb ); 306 307 308 class BrightnessContrastPixelOperation 309 { 310 public: BrightnessContrastPixelOperation(const int brightness,const double contrast)311 BrightnessContrastPixelOperation( const int brightness, const double contrast ) 312 : mBrightness( brightness ) 313 , mContrast( contrast ) 314 { } 315 316 void operator()( QRgb &rgb, int x, int y ); 317 318 private: 319 int mBrightness; 320 double mContrast; 321 }; 322 323 324 class HueSaturationPixelOperation 325 { 326 public: HueSaturationPixelOperation(const double saturation,const bool colorize,const int colorizeHue,const int colorizeSaturation,const double colorizeStrength)327 HueSaturationPixelOperation( const double saturation, const bool colorize, 328 const int colorizeHue, const int colorizeSaturation, 329 const double colorizeStrength ) 330 : mSaturation( saturation ) 331 , mColorize( colorize ) 332 , mColorizeHue( colorizeHue ) 333 , mColorizeSaturation( colorizeSaturation ) 334 , mColorizeStrength( colorizeStrength ) 335 { } 336 337 void operator()( QRgb &rgb, int x, int y ); 338 339 private: 340 double mSaturation; // [0, 2], 1 = no change 341 bool mColorize; 342 int mColorizeHue; 343 int mColorizeSaturation; 344 double mColorizeStrength; // [0,1] 345 }; 346 static int adjustColorComponent( int colorComponent, int brightness, double contrastFactor ); 347 348 349 class MultiplyOpacityPixelOperation 350 { 351 public: MultiplyOpacityPixelOperation(const double factor)352 explicit MultiplyOpacityPixelOperation( const double factor ) 353 : mFactor( factor ) 354 { } 355 356 void operator()( QRgb &rgb, int x, int y ); 357 358 private: 359 double mFactor; 360 }; 361 362 class ConvertToArrayPixelOperation 363 { 364 public: 365 ConvertToArrayPixelOperation( const int width, double *array, const bool exterior = true ) mWidth(width)366 : mWidth( width ) 367 , mArray( array ) 368 , mExterior( exterior ) 369 { 370 } 371 372 void operator()( QRgb &rgb, int x, int y ); 373 374 private: 375 int mWidth; 376 double *mArray = nullptr; 377 bool mExterior; 378 }; 379 380 class ShadeFromArrayOperation 381 { 382 public: ShadeFromArrayOperation(const int width,double * array,const double spread,const DistanceTransformProperties & properties)383 ShadeFromArrayOperation( const int width, double *array, const double spread, 384 const DistanceTransformProperties &properties ) 385 : mWidth( width ) 386 , mArray( array ) 387 , mSpread( spread ) 388 , mProperties( properties ) 389 { 390 mSpreadSquared = std::pow( mSpread, 2.0 ); 391 } 392 393 void operator()( QRgb &rgb, int x, int y ); 394 395 private: 396 int mWidth; 397 double *mArray = nullptr; 398 double mSpread; 399 double mSpreadSquared; 400 const DistanceTransformProperties &mProperties; 401 }; 402 static void distanceTransform2d( double *im, int width, int height ); 403 static void distanceTransform1d( double *f, int n, int *v, double *z, double *d ); 404 static double maxValueInDistanceTransformArray( const double *array, unsigned int size ); 405 406 407 class StackBlurLineOperation 408 { 409 public: StackBlurLineOperation(int alpha,LineOperationDirection direction,bool forwardDirection,int i1,int i2)410 StackBlurLineOperation( int alpha, LineOperationDirection direction, bool forwardDirection, int i1, int i2 ) 411 : mAlpha( alpha ) 412 , mDirection( direction ) 413 , mForwardDirection( forwardDirection ) 414 , mi1( i1 ) 415 , mi2( i2 ) 416 { } 417 418 typedef void result_type; 419 direction()420 LineOperationDirection direction() { return mDirection; } 421 operator()422 void operator()( QRgb *startRef, int lineLength, int bytesPerLine ) 423 { 424 unsigned char *p = reinterpret_cast< unsigned char * >( startRef ); 425 int rgba[4]; 426 int increment = ( mDirection == QgsImageOperation::ByRow ) ? 4 : bytesPerLine; 427 if ( !mForwardDirection ) 428 { 429 p += ( lineLength - 1 ) * increment; 430 increment = -increment; 431 } 432 433 for ( int i = mi1; i <= mi2; ++i ) 434 { 435 rgba[i] = p[i] << 4; 436 } 437 438 p += increment; 439 for ( int j = 1; j < lineLength; ++j, p += increment ) 440 { 441 for ( int i = mi1; i <= mi2; ++i ) 442 { 443 p[i] = ( rgba[i] += ( ( p[i] << 4 ) - rgba[i] ) * mAlpha / 16 ) >> 4; 444 } 445 } 446 } 447 448 private: 449 int mAlpha; 450 LineOperationDirection mDirection; 451 bool mForwardDirection; 452 int mi1; 453 int mi2; 454 }; 455 456 static double *createGaussianKernel( int radius ); 457 458 class GaussianBlurOperation 459 { 460 public: GaussianBlurOperation(int radius,LineOperationDirection direction,QImage * destImage,double * kernel)461 GaussianBlurOperation( int radius, LineOperationDirection direction, QImage *destImage, double *kernel ) 462 : mRadius( radius ) 463 , mDirection( direction ) 464 , mDestImage( destImage ) 465 , mDestImageBpl( destImage->bytesPerLine() ) 466 , mKernel( kernel ) 467 {} 468 469 typedef void result_type; 470 471 void operator()( ImageBlock &block ); 472 473 private: 474 int mRadius; 475 LineOperationDirection mDirection; 476 QImage *mDestImage = nullptr; 477 int mDestImageBpl; 478 double *mKernel = nullptr; 479 480 inline QRgb gaussianBlurVertical( int posy, unsigned char *sourceFirstLine, int sourceBpl, int height ); 481 inline QRgb gaussianBlurHorizontal( int posx, unsigned char *sourceFirstLine, int width ); 482 }; 483 484 //flip 485 486 487 class FlipLineOperation 488 { 489 public: FlipLineOperation(LineOperationDirection direction)490 explicit FlipLineOperation( LineOperationDirection direction ) 491 : mDirection( direction ) 492 { } 493 494 typedef void result_type; 495 direction()496 LineOperationDirection direction() { return mDirection; } 497 498 void operator()( QRgb *startRef, int lineLength, int bytesPerLine ); 499 500 private: 501 LineOperationDirection mDirection; 502 }; 503 504 505 }; 506 507 #endif // QGSIMAGEOPERATION_H 508 509