22 #include <QtConcurrentMap> 29 #define BLOCK_THREADS 16 35 template <
typename PixelOperation>
36 void QgsImageOperation::runPixelOperation(
QImage &image, PixelOperation& operation )
42 runPixelOperationOnWholeImage( image, operation );
47 QgsImageOperation::ProcessBlockUsingPixelOperation<PixelOperation> blockOp( operation ) ;
48 runBlockOperationInThreads( image, blockOp, QgsImageOperation::ByRow );
52 template <
typename PixelOperation>
53 void QgsImageOperation::runPixelOperationOnWholeImage(
QImage &image, PixelOperation& operation )
55 int height = image.
height();
56 int width = image.
width();
57 for (
int y = 0; y < height; ++y )
59 QRgb* ref =
reinterpret_cast< QRgb*
>( image.
scanLine( y ) );
60 for (
int x = 0; x < width; ++x )
62 operation( ref[x], x, y );
69 template <
typename RectOperation>
70 void QgsImageOperation::runRectOperation(
QImage &image, RectOperation& operation )
77 runRectOperationOnWholeImage( image, operation );
82 runBlockOperationInThreads( image, operation, ByRow );
86 template <
class RectOperation>
87 void QgsImageOperation::runRectOperationOnWholeImage(
QImage &image, RectOperation& operation )
90 fullImage.beginLine = 0;
91 fullImage.endLine = image.
height();
92 fullImage.lineLength = image.
width();
93 fullImage.image = ℑ
95 operation( fullImage );
100 template <
typename LineOperation>
101 void QgsImageOperation::runLineOperation(
QImage &image, LineOperation& operation )
108 runLineOperationOnWholeImage( image, operation );
113 QgsImageOperation::ProcessBlockUsingLineOperation<LineOperation> blockOp( operation ) ;
114 runBlockOperationInThreads( image, blockOp, operation.direction() );
118 template <
class LineOperation>
119 void QgsImageOperation::runLineOperationOnWholeImage(
QImage &image, LineOperation& operation )
121 int height = image.
height();
122 int width = image.
width();
126 if ( operation.direction() == ByRow )
128 for (
int y = 0; y < height; ++y )
130 QRgb* ref =
reinterpret_cast< QRgb*
>( image.
scanLine( y ) );
131 operation( ref, width, bpl );
137 unsigned char* ref = image.
scanLine( 0 );
138 for (
int x = 0; x < width; ++x, ref += 4 )
140 operation( reinterpret_cast< QRgb* >( ref ), height, bpl );
148 template <
typename BlockOperation>
149 void QgsImageOperation::runBlockOperationInThreads(
QImage &image, BlockOperation &operation, LineOperationDirection direction )
152 unsigned int height = image.
height();
153 unsigned int width = image.
width();
155 unsigned int blockDimension1 = ( direction == QgsImageOperation::ByRow ) ? height : width;
156 unsigned int blockDimension2 = ( direction == QgsImageOperation::ByRow ) ? width : height;
160 unsigned int begin = 0;
162 for (
unsigned int block = 0; block <
BLOCK_THREADS; ++block, begin += blockLen )
165 newBlock.beginLine = begin;
167 newBlock.endLine = block < ( BLOCK_THREADS - 1 ) ? begin + blockLen : blockDimension1;
168 newBlock.lineLength = blockDimension2;
169 newBlock.image = ℑ
193 GrayscalePixelOperation operation( mode );
194 runPixelOperation( image, operation );
197 void QgsImageOperation::GrayscalePixelOperation::operator()( QRgb &rgb,
const int x,
const int y )
206 grayscaleLuminosityOp( rgb );
209 grayscaleAverageOp( rgb );
213 grayscaleLightnessOp( rgb );
218 void QgsImageOperation::grayscaleLightnessOp( QRgb &rgb )
220 int red = qRed( rgb );
221 int green = qGreen( rgb );
222 int blue = qBlue( rgb );
224 int min = qMin( qMin( red, green ), blue );
225 int max = qMax( qMax( red, green ), blue );
227 int lightness = qMin(( min + max ) / 2, 255 );
228 rgb = qRgba( lightness, lightness, lightness, qAlpha( rgb ) );
231 void QgsImageOperation::grayscaleLuminosityOp( QRgb &rgb )
233 int luminosity = 0.21 * qRed( rgb ) + 0.72 * qGreen( rgb ) + 0.07 * qBlue( rgb );
234 rgb = qRgba( luminosity, luminosity, luminosity, qAlpha( rgb ) );
237 void QgsImageOperation::grayscaleAverageOp( QRgb &rgb )
239 int average = ( qRed( rgb ) + qGreen( rgb ) + qBlue( rgb ) ) / 3;
240 rgb = qRgba( average, average, average, qAlpha( rgb ) );
248 BrightnessContrastPixelOperation operation( brightness, contrast );
249 runPixelOperation( image, operation );
252 void QgsImageOperation::BrightnessContrastPixelOperation::operator()( QRgb &rgb,
const int x,
const int y )
256 int red = adjustColorComponent( qRed( rgb ), mBrightness, mContrast );
257 int blue = adjustColorComponent( qBlue( rgb ), mBrightness, mContrast );
258 int green = adjustColorComponent( qGreen( rgb ), mBrightness, mContrast );
259 rgb = qRgba( red, green, blue, qAlpha( rgb ) );
262 int QgsImageOperation::adjustColorComponent(
int colorComponent,
int brightness,
double contrastFactor )
264 return qBound( 0, static_cast< int >(((((( colorComponent / 255.0 ) - 0.5 ) * contrastFactor ) + 0.5 ) * 255 ) + brightness ), 255 );
271 HueSaturationPixelOperation operation( saturation, colorizeColor.
isValid() && colorizeStrength > 0.0,
272 colorizeColor.
hue(), colorizeColor.
saturation(), colorizeStrength );
273 runPixelOperation( image, operation );
276 void QgsImageOperation::HueSaturationPixelOperation::operator()( QRgb &rgb,
const int x,
const int y )
282 tmpColor.
getHsl( &h, &s, &l );
284 if ( mSaturation < 1.0 )
287 s = qMin( static_cast< int >( s * mSaturation ), 255 );
289 else if ( mSaturation > 1.0 )
293 s = qMin( static_cast< int >( 255. * ( 1 - qPow( 1 - ( s / 255. ), qPow( mSaturation, 2 ) ) ) ), 255 );
299 s = mColorizeSaturation;
300 if ( mColorizeStrength < 1.0 )
304 int colorizedR, colorizedG, colorizedB;
305 colorizedColor.
getRgb( &colorizedR, &colorizedG, &colorizedB );
308 int r = mColorizeStrength * colorizedR + ( 1 - mColorizeStrength ) * tmpColor.
red();
309 int g = mColorizeStrength * colorizedG + ( 1 - mColorizeStrength ) * tmpColor.
green();
310 int b = mColorizeStrength * colorizedB + ( 1 - mColorizeStrength ) * tmpColor.
blue();
312 rgb = qRgba( r, g, b, qAlpha( rgb ) );
317 tmpColor.
setHsl( h, s, l, qAlpha( rgb ) );
318 rgb = tmpColor.
rgba();
330 else if ( factor < 1.0 )
334 QColor transparentFillColor =
QColor( 0, 0, 0, 255 * factor );
343 MultiplyOpacityPixelOperation operation( factor );
344 runPixelOperation( image, operation );
348 void QgsImageOperation::MultiplyOpacityPixelOperation::operator()( QRgb &rgb,
const int x,
const int y )
352 rgb = qRgba( qRed( rgb ), qGreen( rgb ), qBlue( rgb ), qBound( 0, qRound( mFactor * qAlpha( rgb ) ), 255 ) );
359 QColor opaqueColor = color;
374 if ( ! properties.
ramp )
381 double * array =
new double[ image.
width() * image.
height()];
382 ConvertToArrayPixelOperation convertToArray( image.
width(), array, properties.
shadeExterior );
383 runPixelOperation( image, convertToArray );
386 distanceTransform2d( array, image.
width(), image.
height() );
391 spread = sqrt( maxValueInDistanceTransformArray( array, image.
width() * image.
height() ) );
395 spread = properties.
spread;
399 ShadeFromArrayOperation shadeFromArray( image.
width(), array, spread, properties );
400 runPixelOperation( image, shadeFromArray );
404 void QgsImageOperation::ConvertToArrayPixelOperation::operator()( QRgb &rgb,
const int x,
const int y )
406 int idx = y * mWidth + x;
409 if ( qAlpha( rgb ) > 0 )
412 mArray[ idx ] = 1 - qAlpha( rgb ) / 255.0;
423 if ( qAlpha( rgb ) == 255 )
437 void QgsImageOperation::distanceTransform1d(
double *f,
int n,
int *v,
double *z,
double *d )
443 for (
int q = 1; q <= n - 1; q++ )
445 double s = (( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
449 s = (( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
458 for (
int q = 0; q <= n - 1; q++ )
462 d[q] = ( q - v[k] ) * ( q - v[k] ) + f[v[k]];
466 double QgsImageOperation::maxValueInDistanceTransformArray(
const double *array,
const unsigned int size )
468 double dtMaxValue = array[0];
469 for (
unsigned int i = 1; i < size; ++i )
471 if ( array[i] > dtMaxValue )
473 dtMaxValue = array[i];
480 void QgsImageOperation::distanceTransform2d(
double * im,
int width,
int height )
482 int maxDimension = qMax( width, height );
484 double *f =
new double[ maxDimension ];
485 int *v =
new int[ maxDimension ];
486 double *z =
new double[ maxDimension + 1 ];
487 double *d =
new double[ maxDimension ];
490 for (
int x = 0; x < width; x++ )
492 for (
int y = 0; y < height; y++ )
494 f[y] = im[ x + y * width ];
496 distanceTransform1d( f, height, v, z, d );
497 for (
int y = 0; y < height; y++ )
499 im[ x + y * width ] = d[y];
504 for (
int y = 0; y < height; y++ )
506 for (
int x = 0; x < width; x++ )
508 f[x] = im[ x + y*width ];
510 distanceTransform1d( f, width, v, z, d );
511 for (
int x = 0; x < width; x++ )
513 im[ x + y*width ] = d[x];
523 void QgsImageOperation::ShadeFromArrayOperation::operator()( QRgb &rgb,
const int x,
const int y )
525 if ( ! mProperties.ramp )
530 rgb = mProperties.ramp->color( 1.0 ).rgba();
534 int idx = y * mWidth + x;
537 double squaredVal = mArray[ idx ];
538 if ( squaredVal > mSpreadSquared )
540 rgb = Qt::transparent;
544 double distance = sqrt( squaredVal );
545 double val = distance / mSpread;
546 QColor rampColor = mProperties.ramp->color( val );
548 if (( mProperties.shadeExterior && distance > mSpread - 1 ) )
551 double alphaMultiplyFactor = mSpread - distance;
552 rampColor.
setAlpha( rampColor.
alpha() * alphaMultiplyFactor );
554 rgb = rampColor.
rgba();
562 int tab[] = { 14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 };
563 int alpha = ( radius < 1 ) ? 16 : ( radius > 17 ) ? 1 : tab[radius-1];
569 QImage::Format originalFormat = image.
format();
571 if ( !alphaOnly && originalFormat != QImage::Format_ARGB32_Premultiplied )
575 else if ( alphaOnly && originalFormat != QImage::Format_ARGB32 )
581 i1 = i2 = ( QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3 );
583 StackBlurLineOperation topToBottomBlur( alpha, QgsImageOperation::ByColumn,
true, i1, i2 );
584 runLineOperation( *pImage, topToBottomBlur );
586 StackBlurLineOperation leftToRightBlur( alpha, QgsImageOperation::ByRow,
true, i1, i2 );
587 runLineOperation( *pImage, leftToRightBlur );
589 StackBlurLineOperation bottomToTopBlur( alpha, QgsImageOperation::ByColumn,
false, i1, i2 );
590 runLineOperation( *pImage, bottomToTopBlur );
592 StackBlurLineOperation rightToLeftBlur( alpha, QgsImageOperation::ByRow,
false, i1, i2 );
593 runLineOperation( *pImage, rightToLeftBlur );
595 if ( pImage->
format() != originalFormat )
602 void QgsImageOperation::StackBlurLineOperation::operator()( QRgb* startRef,
const int lineLength,
const int bytesPerLine )
604 unsigned char* p =
reinterpret_cast< unsigned char*
>( startRef );
606 int increment = ( mDirection == QgsImageOperation::ByRow ) ? 4 : bytesPerLine;
607 if ( !mForwardDirection )
609 p += ( lineLength - 1 ) * increment;
610 increment = -increment;
613 for (
int i = mi1; i <= mi2; ++i )
619 for (
int j = 1; j < lineLength; ++j, p += increment )
621 for (
int i = mi1; i <= mi2; ++i )
623 p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * mAlpha / 16 ) >> 4;
632 int width = image.
width();
633 int height = image.
height();
642 double* kernel = createGaussianKernel( radius );
645 QImage::Format originalFormat = image.
format();
647 if ( originalFormat != QImage::Format_ARGB32_Premultiplied )
653 QImage xBlurImage =
QImage( width, height, QImage::Format_ARGB32_Premultiplied );
654 GaussianBlurOperation rowBlur( radius, QgsImageOperation::ByRow, &xBlurImage, kernel );
655 runRectOperation( *pImage, rowBlur );
658 QImage* yBlurImage =
new QImage( width, height, QImage::Format_ARGB32_Premultiplied );
659 GaussianBlurOperation colBlur( radius, QgsImageOperation::ByColumn, yBlurImage, kernel );
660 runRectOperation( xBlurImage, colBlur );
664 if ( originalFormat != QImage::Format_ARGB32_Premultiplied )
669 return convertedImage;
675 void QgsImageOperation::GaussianBlurOperation::operator()( QgsImageOperation::ImageBlock &block )
677 int width = block.image->
width();
678 int height = block.image->height();
679 int sourceBpl = block.image->bytesPerLine();
681 unsigned char* outputLineRef = mDestImage->scanLine( block.beginLine );
682 QRgb* destRef =
nullptr;
683 if ( mDirection == ByRow )
685 unsigned char* sourceFirstLine = block.image->scanLine( 0 );
686 unsigned char* sourceRef;
689 for (
unsigned int y = block.beginLine; y < block.endLine; ++y, outputLineRef += mDestImageBpl )
691 sourceRef = sourceFirstLine;
692 destRef =
reinterpret_cast< QRgb*
>( outputLineRef );
693 for (
int x = 0; x < width; ++x, ++destRef, sourceRef += 4 )
695 *destRef = gaussianBlurVertical( y, sourceRef, sourceBpl, height );
701 unsigned char* sourceRef = block.image->scanLine( block.beginLine );
702 for (
unsigned int y = block.beginLine; y < block.endLine; ++y, outputLineRef += mDestImageBpl, sourceRef += sourceBpl )
704 destRef =
reinterpret_cast< QRgb*
>( outputLineRef );
705 for (
int x = 0; x < width; ++x, ++destRef )
707 *destRef = gaussianBlurHorizontal( x, sourceRef, width );
713 inline QRgb QgsImageOperation::GaussianBlurOperation::gaussianBlurVertical(
const int posy,
unsigned char *sourceFirstLine,
const int sourceBpl,
const int height )
722 for (
int i = 0; i <= mRadius*2; ++i )
724 y = qBound( 0, posy + ( i - mRadius ), height - 1 );
725 ref = sourceFirstLine + sourceBpl * y;
727 QRgb* refRgb =
reinterpret_cast< QRgb*
>( ref );
728 r += mKernel[i] * qRed( *refRgb );
729 g += mKernel[i] * qGreen( *refRgb );
730 b += mKernel[i] * qBlue( *refRgb );
731 a += mKernel[i] * qAlpha( *refRgb );
734 return qRgba( r, g, b, a );
737 inline QRgb QgsImageOperation::GaussianBlurOperation::gaussianBlurHorizontal(
const int posx,
unsigned char *sourceFirstLine,
const int width )
746 for (
int i = 0; i <= mRadius*2; ++i )
748 x = qBound( 0, posx + ( i - mRadius ), width - 1 );
749 ref = sourceFirstLine + x * 4;
751 QRgb* refRgb =
reinterpret_cast< QRgb*
>( ref );
752 r += mKernel[i] * qRed( *refRgb );
753 g += mKernel[i] * qGreen( *refRgb );
754 b += mKernel[i] * qBlue( *refRgb );
755 a += mKernel[i] * qAlpha( *refRgb );
758 return qRgba( r, g, b, a );
762 double* QgsImageOperation::createGaussianKernel(
const int radius )
764 double* kernel =
new double[ radius*2+1 ];
765 double sigma = radius / 3.0;
766 double twoSigmaSquared = 2 * sigma * sigma;
767 double coefficient = 1.0 / sqrt(
M_PI * twoSigmaSquared );
768 double expCoefficient = -1.0 / twoSigmaSquared;
772 for (
int i = 0; i <= radius; ++i )
774 result = coefficient * exp( i * i * expCoefficient );
775 kernel[ radius - i ] = result;
779 kernel[radius + i] = result;
784 for (
int i = 0; i <= radius * 2; ++i )
797 runLineOperation( image, flipOperation );
802 int width = image.
width();
803 int height = image.
height();
809 for (
int x = 0; x < width; ++x )
811 for (
int y = 0; y < height; ++y )
813 if ( qAlpha( image.
pixel( x, y ) ) )
815 xmin = qMin( x, xmin );
816 xmax = qMax( x, xmax );
817 ymin = qMin( y, ymin );
818 ymax = qMax( y, ymax );
824 if ( xmax - xmin < minSize.
width() )
826 xmin = qMax(( xmax + xmin ) / 2 - minSize.
width() / 2, 0 );
827 xmax = xmin + minSize.
width();
829 if ( ymax - ymin < minSize.
height() )
831 ymin = qMax(( ymax + ymin ) / 2 - minSize.
height() / 2, 0 );
832 ymax = ymin + minSize.
height();
838 const int dx = qMax( qAbs( xmax - width / 2 ), qAbs( xmin - width / 2 ) );
839 const int dy = qMax( qAbs( ymax - height / 2 ), qAbs( ymin - height / 2 ) );
840 xmin = qMax( 0, width / 2 - dx );
841 xmax = qMin( width, width / 2 + dx );
842 ymin = qMax( 0, height / 2 - dy );
843 ymax = qMin( height, height / 2 + dy );
846 return QRect( xmin, ymin, xmax - xmin, ymax - ymin );
854 void QgsImageOperation::FlipLineOperation::operator()( QRgb *startRef,
const int lineLength,
const int bytesPerLine )
856 int increment = ( mDirection == QgsImageOperation::ByRow ) ? 4 : bytesPerLine;
859 unsigned char* p =
reinterpret_cast< unsigned char*
>( startRef );
860 unsigned char* tempLine =
new unsigned char[ lineLength * 4 ];
861 for (
int i = 0; i < lineLength * 4; ++i, p += increment )
863 tempLine[i++] = *( p++ );
864 tempLine[i++] = *( p++ );
865 tempLine[i++] = *( p++ );
866 tempLine[i] = *( p );
871 p =
reinterpret_cast< unsigned char*
>( startRef );
872 for (
int i = ( lineLength - 1 ) * 4; i >= 0; i -= 7, p += increment )
874 *( p++ ) = tempLine[i++];
875 *( p++ ) = tempLine[i++];
876 *( p++ ) = tempLine[i++];
877 *( p ) = tempLine[i];
static void overlayColor(QImage &image, const QColor &color)
Overlays a color onto an image.
void fillRect(const QRectF &rectangle, const QBrush &brush)
void setCompositionMode(CompositionMode mode)
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.
static void distanceTransform(QImage &image, const DistanceTransformProperties &properties)
Performs a distance transform on the source image and shades the result using a color ramp...
QImage copy(const QRect &rectangle) const
void getHsl(int *h, int *s, int *l, int *a) const
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
FlipType
Flip operation types.
QColor fromHsl(int h, int s, int l, int a)
static void adjustBrightnessContrast(QImage &image, const int brightness, const double contrast)
Alter the brightness or contrast of a QImage.
double ANALYSIS_EXPORT max(double x, double y)
Returns the maximum of two doubles or the first argument if both are equal.
QRgb pixel(int x, int y) const
static QImage * gaussianBlur(QImage &image, const int radius)
Performs a gaussian blur on an image.
static QImage cropTransparent(const QImage &image, QSize minSize=QSize(), bool center=false)
Crop any transparent border from around an image.
static void flipImage(QImage &image, FlipType type)
Flips an image horizontally or vertically.
void blockingMap(Sequence &sequence, MapFunction function)
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.
void getRgb(int *r, int *g, int *b, int *a) const
static QRect nonTransparentImageRect(const QImage &image, QSize minSize=QSize(), bool center=false)
Calculates the non-transparent region of an image.
void setHsl(int h, int s, int l, int a)
double ANALYSIS_EXPORT min(double x, double y)
Returns the minimum of two doubles or the first argument if both are equal.
GrayscaleMode
Modes for converting a QImage to grayscale.