Branch data Line data Source code
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 : 0 : 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 : 0 : 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 : 0 : bool useMaxDistance = true;
131 : :
132 : : /**
133 : : * Maximum distance (in pixels) for the distance transform shading to
134 : : * spread
135 : : */
136 : 0 : double spread = 10.0;
137 : :
138 : : /**
139 : : * Color ramp to use for shading the distance transform
140 : : */
141 : 0 : 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 : 0 : struct ImageBlock
214 : : {
215 : : unsigned int beginLine;
216 : : unsigned int endLine;
217 : : unsigned int lineLength;
218 : 0 : 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 : : {
231 : 0 : explicit ProcessBlockUsingPixelOperation( PixelOperation &operation )
232 : 0 : : mOperation( operation ) { }
233 : :
234 : : typedef void result_type;
235 : :
236 : 0 : void operator()( ImageBlock &block )
237 : : {
238 : 0 : for ( unsigned int y = block.beginLine; y < block.endLine; ++y )
239 : : {
240 : 0 : QRgb *ref = reinterpret_cast< QRgb * >( block.image->scanLine( y ) );
241 : 0 : for ( unsigned int x = 0; x < block.lineLength; ++x )
242 : : {
243 : 0 : mOperation( ref[x], x, y );
244 : 0 : }
245 : 0 : }
246 : 0 : }
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 : : {
257 : 0 : explicit ProcessBlockUsingLineOperation( LineOperation &operation )
258 : 0 : : mOperation( operation ) { }
259 : :
260 : : typedef void result_type;
261 : :
262 : 0 : void operator()( ImageBlock &block )
263 : : {
264 : : //do something with whole lines
265 : 0 : int bpl = block.image->bytesPerLine();
266 : 0 : if ( mOperation.direction() == ByRow )
267 : : {
268 : 0 : for ( unsigned int y = block.beginLine; y < block.endLine; ++y )
269 : : {
270 : 0 : QRgb *ref = reinterpret_cast< QRgb * >( block.image->scanLine( y ) );
271 : 0 : mOperation( ref, block.lineLength, bpl );
272 : 0 : }
273 : 0 : }
274 : : else
275 : : {
276 : : //by column
277 : 0 : unsigned char *ref = block.image->scanLine( 0 ) + 4 * block.beginLine;
278 : 0 : for ( unsigned int x = block.beginLine; x < block.endLine; ++x, ref += 4 )
279 : : {
280 : 0 : mOperation( reinterpret_cast< QRgb * >( ref ), block.lineLength, bpl );
281 : 0 : }
282 : : }
283 : 0 : }
284 : :
285 : : LineOperation &mOperation;
286 : : };
287 : :
288 : :
289 : : //individual operation implementations
290 : :
291 : : class GrayscalePixelOperation
292 : : {
293 : : public:
294 : 0 : explicit GrayscalePixelOperation( const GrayscaleMode mode )
295 : 0 : : mMode( mode )
296 : 0 : { }
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:
311 : 0 : BrightnessContrastPixelOperation( const int brightness, const double contrast )
312 : 0 : : mBrightness( brightness )
313 : 0 : , mContrast( contrast )
314 : 0 : { }
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:
327 : 0 : HueSaturationPixelOperation( const double saturation, const bool colorize,
328 : : const int colorizeHue, const int colorizeSaturation,
329 : : const double colorizeStrength )
330 : 0 : : mSaturation( saturation )
331 : 0 : , mColorize( colorize )
332 : 0 : , mColorizeHue( colorizeHue )
333 : 0 : , mColorizeSaturation( colorizeSaturation )
334 : 0 : , mColorizeStrength( colorizeStrength )
335 : 0 : { }
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:
352 : 0 : explicit MultiplyOpacityPixelOperation( const double factor )
353 : 0 : : mFactor( factor )
354 : 0 : { }
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 : 0 : ConvertToArrayPixelOperation( const int width, double *array, const bool exterior = true )
366 : 0 : : mWidth( width )
367 : 0 : , mArray( array )
368 : 0 : , mExterior( exterior )
369 : : {
370 : 0 : }
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:
383 : 0 : ShadeFromArrayOperation( const int width, double *array, const double spread,
384 : : const DistanceTransformProperties &properties )
385 : 0 : : mWidth( width )
386 : 0 : , mArray( array )
387 : 0 : , mSpread( spread )
388 : 0 : , mProperties( properties )
389 : : {
390 : 0 : mSpreadSquared = std::pow( mSpread, 2.0 );
391 : 0 : }
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:
410 : 0 : StackBlurLineOperation( int alpha, LineOperationDirection direction, bool forwardDirection, int i1, int i2 )
411 : 0 : : mAlpha( alpha )
412 : 0 : , mDirection( direction )
413 : 0 : , mForwardDirection( forwardDirection )
414 : 0 : , mi1( i1 )
415 : 0 : , mi2( i2 )
416 : 0 : { }
417 : :
418 : : typedef void result_type;
419 : :
420 : 0 : LineOperationDirection direction() { return mDirection; }
421 : :
422 : 0 : void operator()( QRgb *startRef, int lineLength, int bytesPerLine )
423 : : {
424 : 0 : unsigned char *p = reinterpret_cast< unsigned char * >( startRef );
425 : : int rgba[4];
426 : 0 : int increment = ( mDirection == QgsImageOperation::ByRow ) ? 4 : bytesPerLine;
427 : 0 : if ( !mForwardDirection )
428 : : {
429 : 0 : p += ( lineLength - 1 ) * increment;
430 : 0 : increment = -increment;
431 : 0 : }
432 : :
433 : 0 : for ( int i = mi1; i <= mi2; ++i )
434 : : {
435 : 0 : rgba[i] = p[i] << 4;
436 : 0 : }
437 : :
438 : 0 : p += increment;
439 : 0 : for ( int j = 1; j < lineLength; ++j, p += increment )
440 : : {
441 : 0 : for ( int i = mi1; i <= mi2; ++i )
442 : : {
443 : 0 : p[i] = ( rgba[i] += ( ( p[i] << 4 ) - rgba[i] ) * mAlpha / 16 ) >> 4;
444 : 0 : }
445 : 0 : }
446 : 0 : }
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:
461 : 0 : GaussianBlurOperation( int radius, LineOperationDirection direction, QImage *destImage, double *kernel )
462 : 0 : : mRadius( radius )
463 : 0 : , mDirection( direction )
464 : 0 : , mDestImage( destImage )
465 : 0 : , mDestImageBpl( destImage->bytesPerLine() )
466 : 0 : , mKernel( kernel )
467 : 0 : {}
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:
490 : 0 : explicit FlipLineOperation( LineOperationDirection direction )
491 : 0 : : mDirection( direction )
492 : 0 : { }
493 : :
494 : : typedef void result_type;
495 : :
496 : 0 : 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 : :
|