22 #include <QtConcurrentMap>
29 #define BLOCK_THREADS 16
33 template <
typename PixelOperation>
34 void QgsImageOperation::runPixelOperation(
QImage &image, PixelOperation& operation )
40 runPixelOperationOnWholeImage( image, operation );
45 QgsImageOperation::ProcessBlockUsingPixelOperation<PixelOperation> blockOp( operation ) ;
46 runBlockOperationInThreads( image, blockOp, QgsImageOperation::ByRow );
50 template <
typename PixelOperation>
51 void QgsImageOperation::runPixelOperationOnWholeImage(
QImage &image, PixelOperation& operation )
53 int height = image.
height();
54 int width = image.
width();
55 for (
int y = 0; y < height; ++y )
57 QRgb* ref = ( QRgb* )image.
scanLine( y );
58 for (
int x = 0; x < width; ++x )
60 operation( ref[x], x, y );
67 template <
typename RectOperation>
68 void QgsImageOperation::runRectOperation(
QImage &image, RectOperation& operation )
75 runRectOperationOnWholeImage( image, operation );
80 runBlockOperationInThreads( image, operation, ByRow );
84 template <
class RectOperation>
85 void QgsImageOperation::runRectOperationOnWholeImage(
QImage &image, RectOperation& operation )
88 fullImage.beginLine = 0;
89 fullImage.endLine = image.
height();
90 fullImage.lineLength = image.
width();
91 fullImage.image = ℑ
93 operation( fullImage );
98 template <
typename LineOperation>
99 void QgsImageOperation::runLineOperation(
QImage &image, LineOperation& operation )
106 runLineOperationOnWholeImage( image, operation );
111 QgsImageOperation::ProcessBlockUsingLineOperation<LineOperation> blockOp( operation ) ;
112 runBlockOperationInThreads( image, blockOp, operation.direction() );
116 template <
class LineOperation>
117 void QgsImageOperation::runLineOperationOnWholeImage(
QImage &image, LineOperation& operation )
119 int height = image.
height();
120 int width = image.
width();
124 if ( operation.direction() == ByRow )
126 for (
int y = 0; y < height; ++y )
128 QRgb* ref = ( QRgb* )image.
scanLine( y );
129 operation( ref, width, bpl );
135 unsigned char* ref = image.
scanLine( 0 );
136 for (
int x = 0; x < width; ++x, ref += 4 )
138 operation(( QRgb* )ref, height, bpl );
146 template <
typename BlockOperation>
147 void QgsImageOperation::runBlockOperationInThreads(
QImage &image, BlockOperation &operation, LineOperationDirection direction )
150 unsigned int height = image.
height();
151 unsigned int width = image.
width();
153 unsigned int blockDimension1 = ( direction == QgsImageOperation::ByRow ) ? height : width;
154 unsigned int blockDimension2 = ( direction == QgsImageOperation::ByRow ) ? width : height;
158 unsigned int begin = 0;
160 for (
unsigned int block = 0; block <
BLOCK_THREADS; ++block, begin += blockLen )
163 newBlock.beginLine = begin;
165 newBlock.endLine = block < ( BLOCK_THREADS - 1 ) ? begin + blockLen : blockDimension1;
166 newBlock.lineLength = blockDimension2;
167 newBlock.image = ℑ
189 GrayscalePixelOperation operation( mode );
190 runPixelOperation( image, operation );
193 void QgsImageOperation::GrayscalePixelOperation::operator()( QRgb &rgb,
const int x,
const int y )
201 case GrayscaleLuminosity:
202 grayscaleLuminosityOp( rgb );
204 case GrayscaleAverage:
205 grayscaleAverageOp( rgb );
207 case GrayscaleLightness:
209 grayscaleLightnessOp( rgb );
214 void QgsImageOperation::grayscaleLightnessOp( QRgb &rgb )
216 int red = qRed( rgb );
217 int green = qGreen( rgb );
218 int blue = qBlue( rgb );
220 int min = qMin( qMin( red, green ), blue );
221 int max = qMax( qMax( red, green ), blue );
223 int lightness = qMin(( min + max ) / 2, 255 );
224 rgb = qRgba( lightness, lightness, lightness, qAlpha( rgb ) );
227 void QgsImageOperation::grayscaleLuminosityOp( QRgb &rgb )
229 int luminosity = 0.21 * qRed( rgb ) + 0.72 * qGreen( rgb ) + 0.07 * qBlue( rgb );
230 rgb = qRgba( luminosity, luminosity, luminosity, qAlpha( rgb ) );
233 void QgsImageOperation::grayscaleAverageOp( QRgb &rgb )
235 int average = ( qRed( rgb ) + qGreen( rgb ) + qBlue( rgb ) ) / 3;
236 rgb = qRgba( average, average, average, qAlpha( rgb ) );
244 BrightnessContrastPixelOperation operation( brightness, contrast );
245 runPixelOperation( image, operation );
248 void QgsImageOperation::BrightnessContrastPixelOperation::operator()( QRgb &rgb,
const int x,
const int y )
252 int red = adjustColorComponent( qRed( rgb ), mBrightness, mContrast );
253 int blue = adjustColorComponent( qBlue( rgb ), mBrightness, mContrast );
254 int green = adjustColorComponent( qGreen( rgb ), mBrightness, mContrast );
255 rgb = qRgba( red, green, blue, qAlpha( rgb ) );
258 int QgsImageOperation::adjustColorComponent(
int colorComponent,
int brightness,
double contrastFactor )
260 return qBound( 0, (
int )(((((( colorComponent / 255.0 ) - 0.5 ) * contrastFactor ) + 0.5 ) * 255 ) + brightness ), 255 );
267 HueSaturationPixelOperation operation( saturation, colorizeColor.
isValid() && colorizeStrength > 0.0,
268 colorizeColor.
hue(), colorizeColor.
saturation(), colorizeStrength );
269 runPixelOperation( image, operation );
272 void QgsImageOperation::HueSaturationPixelOperation::operator()( QRgb &rgb,
const int x,
const int y )
278 tmpColor.getHsl( &h, &s, &l );
280 if ( mSaturation < 1.0 )
283 s = qMin((
int )( s * mSaturation ), 255 );
285 else if ( mSaturation > 1.0 )
289 s = qMin((
int )( 255. * ( 1 - qPow( 1 - ( s / 255. ), qPow( mSaturation, 2 ) ) ) ), 255 );
295 s = mColorizeSaturation;
296 if ( mColorizeStrength < 1.0 )
300 int colorizedR, colorizedG, colorizedB;
301 colorizedColor.
getRgb( &colorizedR, &colorizedG, &colorizedB );
304 int r = mColorizeStrength * colorizedR + ( 1 - mColorizeStrength ) * tmpColor.red();
305 int g = mColorizeStrength * colorizedG + ( 1 - mColorizeStrength ) * tmpColor.green();
306 int b = mColorizeStrength * colorizedB + ( 1 - mColorizeStrength ) * tmpColor.blue();
308 rgb = qRgba( r, g, b, qAlpha( rgb ) );
313 tmpColor.setHsl( h, s, l, qAlpha( rgb ) );
314 rgb = tmpColor.rgba();
326 else if ( factor < 1.0 )
330 QColor transparentFillColor =
QColor( 0, 0, 0, 255 * factor );
339 MultiplyOpacityPixelOperation operation( factor );
340 runPixelOperation( image, operation );
344 void QgsImageOperation::MultiplyOpacityPixelOperation::operator()( QRgb &rgb,
const int x,
const int y )
348 rgb = qRgba( qRed( rgb ), qGreen( rgb ), qBlue( rgb ), qBound( 0, qRound( mFactor * qAlpha( rgb ) ), 255 ) );
355 QColor opaqueColor = color;
370 if ( ! properties.
ramp )
377 double * array =
new double[ image.
width() * image.
height()];
378 ConvertToArrayPixelOperation convertToArray( image.
width(), array, properties.
shadeExterior );
379 runPixelOperation( image, convertToArray );
382 distanceTransform2d( array, image.
width(), image.
height() );
387 spread = sqrt( maxValueInDistanceTransformArray( array, image.
width() * image.
height() ) );
391 spread = properties.
spread;
395 ShadeFromArrayOperation shadeFromArray( image.
width(), array, spread, properties );
396 runPixelOperation( image, shadeFromArray );
400 void QgsImageOperation::ConvertToArrayPixelOperation::operator()( QRgb &rgb,
const int x,
const int y )
402 int idx = y * mWidth + x;
405 if ( qAlpha( rgb ) > 0 )
408 mArray[ idx ] = 1 - qAlpha( rgb ) / 255.0;
419 if ( qAlpha( rgb ) == 255 )
433 void QgsImageOperation::distanceTransform1d(
double *f,
int n,
int *v,
double *z,
double *d )
439 for (
int q = 1; q <= n - 1; q++ )
441 double s = (( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
445 s = (( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
454 for (
int q = 0; q <= n - 1; q++ )
458 d[q] = ( q - v[k] ) * ( q - v[k] ) + f[v[k]];
462 double QgsImageOperation::maxValueInDistanceTransformArray(
const double *array,
const unsigned int size )
464 double dtMaxValue = array[0];
465 for (
unsigned int i = 1; i < size; ++i )
467 if ( array[i] > dtMaxValue )
469 dtMaxValue = array[i];
476 void QgsImageOperation::distanceTransform2d(
double * im,
int width,
int height )
478 int maxDimension = qMax( width, height );
480 double *f =
new double[ maxDimension ];
481 int *v =
new int[ maxDimension ];
482 double *z =
new double[ maxDimension + 1 ];
483 double *d =
new double[ maxDimension ];
486 for (
int x = 0; x < width; x++ )
488 for (
int y = 0; y < height; y++ )
490 f[y] = im[ x + y * width ];
492 distanceTransform1d( f, height, v, z, d );
493 for (
int y = 0; y < height; y++ )
495 im[ x + y * width ] = d[y];
500 for (
int y = 0; y < height; y++ )
502 for (
int x = 0; x < width; x++ )
504 f[x] = im[ x + y*width ];
506 distanceTransform1d( f, width, v, z, d );
507 for (
int x = 0; x < width; x++ )
509 im[ x + y*width ] = d[x];
519 void QgsImageOperation::ShadeFromArrayOperation::operator()( QRgb &rgb,
const int x,
const int y )
521 if ( ! mProperties.ramp )
526 rgb = mProperties.ramp->color( 1.0 ).rgba();
530 int idx = y * mWidth + x;
533 double squaredVal = mArray[ idx ];
534 if ( squaredVal > mSpreadSquared )
536 rgb = Qt::transparent;
540 double distance = sqrt( squaredVal );
541 double val = distance / mSpread;
542 QColor rampColor = mProperties.ramp->color( val );
544 if (( mProperties.shadeExterior && distance > mSpread - 1 ) )
547 double alphaMultiplyFactor = mSpread - distance;
548 rampColor.
setAlpha( rampColor.
alpha() * alphaMultiplyFactor );
550 rgb = rampColor.
rgba();
558 int tab[] = { 14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 };
559 int alpha = ( radius < 1 ) ? 16 : ( radius > 17 ) ? 1 : tab[radius-1];
565 QImage::Format originalFormat = image.
format();
567 if ( !alphaOnly && originalFormat != QImage::Format_ARGB32_Premultiplied )
571 else if ( alphaOnly && originalFormat != QImage::Format_ARGB32 )
577 i1 = i2 = ( QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3 );
579 StackBlurLineOperation topToBottomBlur( alpha, QgsImageOperation::ByColumn,
true, i1, i2 );
580 runLineOperation( *pImage, topToBottomBlur );
582 StackBlurLineOperation leftToRightBlur( alpha, QgsImageOperation::ByRow,
true, i1, i2 );
583 runLineOperation( *pImage, leftToRightBlur );
585 StackBlurLineOperation bottomToTopBlur( alpha, QgsImageOperation::ByColumn,
false, i1, i2 );
586 runLineOperation( *pImage, bottomToTopBlur );
588 StackBlurLineOperation rightToLeftBlur( alpha, QgsImageOperation::ByRow,
false, i1, i2 );
589 runLineOperation( *pImage, rightToLeftBlur );
591 if ( pImage->
format() != originalFormat )
598 void QgsImageOperation::StackBlurLineOperation::operator()( QRgb* startRef,
const int lineLength,
const int bytesPerLine )
600 unsigned char* p = (
unsigned char* )startRef;
602 int increment = ( mDirection == QgsImageOperation::ByRow ) ? 4 : bytesPerLine;
603 if ( !mForwardDirection )
605 p += ( lineLength - 1 ) * increment;
606 increment = -increment;
609 for (
int i = mi1; i <= mi2; ++i )
615 for (
int j = 1; j < lineLength; ++j, p += increment )
617 for (
int i = mi1; i <= mi2; ++i )
619 p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * mAlpha / 16 ) >> 4;
628 int width = image.
width();
629 int height = image.
height();
638 double* kernel = createGaussianKernel( radius );
641 QImage::Format originalFormat = image.
format();
643 if ( originalFormat != QImage::Format_ARGB32_Premultiplied )
649 QImage xBlurImage =
QImage( width, height, QImage::Format_ARGB32_Premultiplied );
650 GaussianBlurOperation rowBlur( radius, QgsImageOperation::ByRow, &xBlurImage, kernel );
651 runRectOperation( *pImage, rowBlur );
654 QImage* yBlurImage =
new QImage( width, height, QImage::Format_ARGB32_Premultiplied );
655 GaussianBlurOperation colBlur( radius, QgsImageOperation::ByColumn, yBlurImage, kernel );
656 runRectOperation( xBlurImage, colBlur );
660 if ( originalFormat != QImage::Format_ARGB32_Premultiplied )
665 return convertedImage;
671 void QgsImageOperation::GaussianBlurOperation::operator()( QgsImageOperation::ImageBlock &block )
673 int width = block.image->
width();
674 int height = block.image->height();
675 int sourceBpl = block.image->bytesPerLine();
677 unsigned char* outputLineRef = mDestImage->scanLine( block.beginLine );
679 if ( mDirection == ByRow )
681 unsigned char* sourceFirstLine = block.image->scanLine( 0 );
682 unsigned char* sourceRef;
685 for (
unsigned int y = block.beginLine; y < block.endLine; ++y, outputLineRef += mDestImageBpl )
687 sourceRef = sourceFirstLine;
688 destRef = ( QRgb* )outputLineRef;
689 for (
int x = 0; x < width; ++x, ++destRef, sourceRef += 4 )
691 *destRef = gaussianBlurVertical( y, sourceRef, sourceBpl, height );
697 unsigned char* sourceRef = block.image->scanLine( block.beginLine );
698 for (
unsigned int y = block.beginLine; y < block.endLine; ++y, outputLineRef += mDestImageBpl, sourceRef += sourceBpl )
700 destRef = ( QRgb* )outputLineRef;
701 for (
int x = 0; x < width; ++x, ++destRef )
703 *destRef = gaussianBlurHorizontal( x, sourceRef, width );
709 inline QRgb QgsImageOperation::GaussianBlurOperation::gaussianBlurVertical(
const int posy,
unsigned char *sourceFirstLine,
const int sourceBpl,
const int height )
718 for (
int i = 0; i <= mRadius*2; ++i )
720 y = qBound( 0, posy + ( i - mRadius ), height - 1 );
721 ref = sourceFirstLine + sourceBpl * y;
723 QRgb* refRgb = ( QRgb* )ref;
724 r += mKernel[i] * qRed( *refRgb );
725 g += mKernel[i] * qGreen( *refRgb );
726 b += mKernel[i] * qBlue( *refRgb );
727 a += mKernel[i] * qAlpha( *refRgb );
730 return qRgba( r, g, b, a );
733 inline QRgb QgsImageOperation::GaussianBlurOperation::gaussianBlurHorizontal(
const int posx,
unsigned char *sourceFirstLine,
const int width )
742 for (
int i = 0; i <= mRadius*2; ++i )
744 x = qBound( 0, posx + ( i - mRadius ), width - 1 );
745 ref = sourceFirstLine + x * 4;
747 QRgb* refRgb = ( QRgb* )ref;
748 r += mKernel[i] * qRed( *refRgb );
749 g += mKernel[i] * qGreen( *refRgb );
750 b += mKernel[i] * qBlue( *refRgb );
751 a += mKernel[i] * qAlpha( *refRgb );
754 return qRgba( r, g, b, a );
758 double* QgsImageOperation::createGaussianKernel(
const int radius )
760 double* kernel =
new double[ radius*2+1 ];
761 double sigma = radius / 3.0;
762 double twoSigmaSquared = 2 * sigma * sigma;
763 double coefficient = 1.0 / sqrt(
M_PI * twoSigmaSquared );
764 double expCoefficient = -1.0 / twoSigmaSquared;
768 for (
int i = 0; i <= radius; ++i )
770 result = coefficient * exp( i * i * expCoefficient );
771 kernel[ radius - i ] = result;
775 kernel[radius + i] = result;
780 for (
int i = 0; i <= radius * 2; ++i )
793 runLineOperation( image, flipOperation );
798 int width = image.
width();
799 int height = image.
height();
805 for (
int x = 0; x < width; ++x )
807 for (
int y = 0; y < height; ++y )
809 if ( qAlpha( image.
pixel( x, y ) ) )
811 xmin = qMin( x, xmin );
812 xmax = qMax( x, xmax );
813 ymin = qMin( y, ymin );
814 ymax = qMax( y, ymax );
820 if ( xmax - xmin < minSize.
width() )
822 xmin = qMax(( xmax + xmin ) / 2 - minSize.
width() / 2, 0 );
823 xmax = xmin + minSize.
width();
825 if ( ymax - ymin < minSize.
height() )
827 ymin = qMax(( ymax + ymin ) / 2 - minSize.
height() / 2, 0 );
828 ymax = ymin + minSize.
height();
834 const int dx = qMax( qAbs( xmax - width / 2 ), qAbs( xmin - width / 2 ) );
835 const int dy = qMax( qAbs( ymax - height / 2 ), qAbs( ymin - height / 2 ) );
836 xmin = qMax( 0, width / 2 - dx );
837 xmax = qMin( width, width / 2 + dx );
838 ymin = qMax( 0, height / 2 - dy );
839 ymax = qMin( height, height / 2 + dy );
842 return QRect( xmin, ymin, xmax - xmin, ymax - ymin );
850 void QgsImageOperation::FlipLineOperation::operator()( QRgb *startRef,
const int lineLength,
const int bytesPerLine )
852 int increment = ( mDirection == QgsImageOperation::ByRow ) ? 4 : bytesPerLine;
855 unsigned char* p = (
unsigned char* )startRef;
856 unsigned char* tempLine =
new unsigned char[ lineLength * 4 ];
857 for (
int i = 0; i < lineLength * 4; ++i, p += increment )
859 tempLine[i++] = *( p++ );
860 tempLine[i++] = *( p++ );
861 tempLine[i++] = *( p++ );
862 tempLine[i] = *( p );
867 p = (
unsigned char* )startRef;
868 for (
int i = ( lineLength - 1 ) * 4; i >= 0; i -= 7, p += increment )
870 *( p++ ) = tempLine[i++];
871 *( p++ ) = tempLine[i++];
872 *( p++ ) = tempLine[i++];
873 *( 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...
static QImage cropTransparent(const QImage &image, const QSize &minSize=QSize(), bool center=false)
Crop any transparent border from around an image.
QImage copy(const QRect &rectangle) const
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
FlipType
Flip operation types.
static QRect nonTransparentImageRect(const QImage &image, const QSize &minSize=QSize(), bool center=false)
Calculates the non-transparent region of an image.
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.
QRgb pixel(int x, int y) const
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.
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
GrayscaleMode
Modes for converting a QImage to grayscale.