22 #include <QtConcurrentMap>
29 #define BLOCK_THREADS 16
33 template <
typename PixelOperation>
34 void QgsImageOperation::runPixelOperation( QImage &image, PixelOperation& operation )
36 if ( image.height() * image.width() < 100000 )
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 )
71 if ( image.height() * image.width() < 100000 )
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 )
102 if ( image.height() * image.width() < 100000 )
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();
123 int bpl = image.bytesPerLine();
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 )
149 QList< ImageBlock > blocks;
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 = ℑ
172 QtConcurrent::blockingMap( blocks, operation );
184 GrayscalePixelOperation operation( mode );
185 runPixelOperation( image, operation );
188 void QgsImageOperation::GrayscalePixelOperation::operator()( QRgb &rgb,
const int x,
const int y )
194 case GrayscaleLuminosity:
195 grayscaleLuminosityOp( rgb );
197 case GrayscaleAverage:
198 grayscaleAverageOp( rgb );
200 case GrayscaleLightness:
202 grayscaleLightnessOp( rgb );
207 void QgsImageOperation::grayscaleLightnessOp( QRgb &rgb )
209 int red = qRed( rgb );
210 int green = qGreen( rgb );
211 int blue = qBlue( rgb );
213 int min = qMin( qMin( red, green ), blue );
214 int max = qMax( qMax( red, green ), blue );
216 int lightness = qMin(( min + max ) / 2, 255 );
217 rgb = qRgba( lightness, lightness, lightness, qAlpha( rgb ) );
220 void QgsImageOperation::grayscaleLuminosityOp( QRgb &rgb )
222 int luminosity = 0.21 * qRed( rgb ) + 0.72 * qGreen( rgb ) + 0.07 * qBlue( rgb );
223 rgb = qRgba( luminosity, luminosity, luminosity, qAlpha( rgb ) );
226 void QgsImageOperation::grayscaleAverageOp( QRgb &rgb )
228 int average = ( qRed( rgb ) + qGreen( rgb ) + qBlue( rgb ) ) / 3;
229 rgb = qRgba( average, average, average, qAlpha( rgb ) );
237 BrightnessContrastPixelOperation operation( brightness, contrast );
238 runPixelOperation( image, operation );
241 void QgsImageOperation::BrightnessContrastPixelOperation::operator()( QRgb &rgb,
const int x,
const int 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 ) );
251 int QgsImageOperation::adjustColorComponent(
int colorComponent,
int brightness,
double contrastFactor )
253 return qBound( 0, (
int )(((((( colorComponent / 255.0 ) - 0.5 ) * contrastFactor ) + 0.5 ) * 255 ) + brightness ), 255 );
260 HueSaturationPixelOperation operation( saturation, colorizeColor.isValid() && colorizeStrength > 0.0,
261 colorizeColor.hue(), colorizeColor.saturation(), colorizeStrength );
262 runPixelOperation( image, operation );
265 void QgsImageOperation::HueSaturationPixelOperation::operator()( QRgb &rgb,
const int x,
const int y )
269 QColor tmpColor( rgb );
271 tmpColor.getHsl( &h, &s, &l );
273 if ( mSaturation < 1.0 )
276 s = qMin((
int )( s * mSaturation ), 255 );
278 else if ( mSaturation > 1.0 )
282 s = qMin((
int )( 255. * ( 1 - qPow( 1 - ( s / 255. ), qPow( mSaturation, 2 ) ) ) ), 255 );
288 s = mColorizeSaturation;
289 if ( mColorizeStrength < 1.0 )
292 QColor colorizedColor = QColor::fromHsl( h, s, l );
293 int colorizedR, colorizedG, colorizedB;
294 colorizedColor.getRgb( &colorizedR, &colorizedG, &colorizedB );
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();
301 rgb = qRgba( r, g, b, qAlpha( rgb ) );
306 tmpColor.setHsl( h, s, l, qAlpha( rgb ) );
307 rgb = tmpColor.rgba();
319 else if ( factor < 1.0 )
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 );
332 MultiplyOpacityPixelOperation operation( factor );
333 runPixelOperation( image, operation );
337 void QgsImageOperation::MultiplyOpacityPixelOperation::operator()( QRgb &rgb,
const int x,
const int y )
341 rgb = qRgba( qRed( rgb ), qGreen( rgb ), qBlue( rgb ), qBound( 0, qRound( mFactor * qAlpha( rgb ) ), 255 ) );
348 QColor opaqueColor = color;
349 opaqueColor.setAlpha( 255 );
353 QPainter painter( &image );
354 painter.setCompositionMode( QPainter::CompositionMode_SourceIn );
355 painter.fillRect( 0, 0, image.width(), image.height(), opaqueColor );
363 if ( ! properties.
ramp )
365 QgsDebugMsg( QString(
"no color ramp specified for distance transform" ) );
370 double * array =
new double[ image.width() * image.height()];
371 ConvertToArrayPixelOperation convertToArray( image.width(), array, properties.
shadeExterior );
372 runPixelOperation( image, convertToArray );
375 distanceTransform2d( array, image.width(), image.height() );
380 spread = sqrt( maxValueInDistanceTransformArray( array, image.width() * image.height() ) );
384 spread = properties.
spread;
388 ShadeFromArrayOperation shadeFromArray( image.width(), array, spread, properties );
389 runPixelOperation( image, shadeFromArray );
393 void QgsImageOperation::ConvertToArrayPixelOperation::operator()( QRgb &rgb,
const int x,
const int y )
398 int idx = y * mWidth + x;
399 if (( mExterior && qAlpha( rgb ) >= mAlphaThreshold ) || ( !mExterior && qAlpha( rgb ) < mAlphaThreshold ) )
414 void QgsImageOperation::distanceTransform1d(
double *f,
int n,
int *v,
double *z,
double *d )
420 for (
int q = 1; q <= n - 1; q++ )
422 double s = (( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
426 s = (( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
435 for (
int q = 0; q <= n - 1; q++ )
439 d[q] = ( q - v[k] ) * ( q - v[k] ) + f[v[k]];
443 double QgsImageOperation::maxValueInDistanceTransformArray(
const double *array,
const unsigned int size )
445 double dtMaxValue = array[0];
446 for (
unsigned int i = 1; i <
size; ++i )
448 if ( array[i] > dtMaxValue )
450 dtMaxValue = array[i];
457 void QgsImageOperation::distanceTransform2d(
double * im,
int width,
int height )
459 int maxDimension = qMax( width, height );
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 ];
467 for (
int x = 0; x < width; x++ )
469 for (
int y = 0; y < height; y++ )
471 f[y] = im[ x + y * width ];
473 distanceTransform1d( f, height, v, z, d );
474 for (
int y = 0; y < height; y++ )
476 im[ x + y * width ] = d[y];
481 for (
int y = 0; y < height; y++ )
483 for (
int x = 0; x < width; x++ )
485 f[x] = im[ x + y*width ];
487 distanceTransform1d( f, width, v, z, d );
488 for (
int x = 0; x < width; x++ )
490 im[ x + y*width ] = d[x];
500 void QgsImageOperation::ShadeFromArrayOperation::operator()( QRgb &rgb,
const int x,
const int y )
502 if ( ! mProperties.ramp )
507 rgb = mProperties.ramp->color( 1.0 ).rgba();
511 int idx = y * mWidth + x;
514 double squaredVal = mArray[ idx ];
515 if ( squaredVal > mSpreadSquared )
517 rgb = Qt::transparent;
520 double val = squaredVal > 0 ? qMin(( sqrt( squaredVal ) / mSpread ), 1.0 ) : 0;
522 rgb = mProperties.ramp->color( val ).rgba();
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];
537 QImage::Format originalFormat = image.format();
538 QImage* pImage = ℑ
539 if ( !alphaOnly && originalFormat != QImage::Format_ARGB32_Premultiplied )
541 pImage =
new QImage( image.convertToFormat( QImage::Format_ARGB32_Premultiplied ) );
543 else if ( alphaOnly && originalFormat != QImage::Format_ARGB32 )
545 pImage =
new QImage( image.convertToFormat( QImage::Format_ARGB32 ) );
549 i1 = i2 = ( QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3 );
551 StackBlurLineOperation topToBottomBlur( alpha, QgsImageOperation::ByColumn,
true, i1, i2 );
552 runLineOperation( *pImage, topToBottomBlur );
554 StackBlurLineOperation leftToRightBlur( alpha, QgsImageOperation::ByRow,
true, i1, i2 );
555 runLineOperation( *pImage, leftToRightBlur );
557 StackBlurLineOperation bottomToTopBlur( alpha, QgsImageOperation::ByColumn,
false, i1, i2 );
558 runLineOperation( *pImage, bottomToTopBlur );
560 StackBlurLineOperation rightToLeftBlur( alpha, QgsImageOperation::ByRow,
false, i1, i2 );
561 runLineOperation( *pImage, rightToLeftBlur );
563 if ( pImage->format() != originalFormat )
565 image = pImage->convertToFormat( originalFormat );
570 void QgsImageOperation::StackBlurLineOperation::operator()( QRgb* startRef,
const int lineLength,
const int bytesPerLine )
572 unsigned char* p = (
unsigned char* )startRef;
574 int increment = ( mDirection == QgsImageOperation::ByRow ) ? 4 : bytesPerLine;
575 if ( !mForwardDirection )
577 p += ( lineLength - 1 ) * increment;
578 increment = -increment;
581 for (
int i = mi1; i <= mi2; ++i )
587 for (
int j = 1; j < lineLength; ++j, p += increment )
589 for (
int i = mi1; i <= mi2; ++i )
591 p[i] = ( rgba[i] += (( p[i] << 4 ) - rgba[i] ) * mAlpha / 16 ) >> 4;
600 int width = image.width();
601 int height = image.height();
606 QImage* copy =
new QImage( image.copy() );
610 double* kernel = createGaussianKernel( radius );
613 QImage::Format originalFormat = image.format();
614 QImage* pImage = ℑ
615 if ( originalFormat != QImage::Format_ARGB32_Premultiplied )
617 pImage =
new QImage( image.convertToFormat( QImage::Format_ARGB32_Premultiplied ) );
621 QImage xBlurImage = QImage( width, height, QImage::Format_ARGB32_Premultiplied );
622 GaussianBlurOperation rowBlur( radius, QgsImageOperation::ByRow, &xBlurImage, kernel );
623 runRectOperation( *pImage, rowBlur );
626 QImage* yBlurImage =
new QImage( width, height, QImage::Format_ARGB32_Premultiplied );
627 GaussianBlurOperation colBlur( radius, QgsImageOperation::ByColumn, yBlurImage, kernel );
628 runRectOperation( xBlurImage, colBlur );
632 if ( originalFormat != QImage::Format_ARGB32_Premultiplied )
634 QImage* convertedImage =
new QImage( yBlurImage->convertToFormat( originalFormat ) );
637 return convertedImage;
643 void QgsImageOperation::GaussianBlurOperation::operator()( QgsImageOperation::ImageBlock &block )
645 int width = block.image->width();
646 int height = block.image->height();
647 int sourceBpl = block.image->bytesPerLine();
649 unsigned char* outputLineRef = mDestImage->scanLine( block.beginLine );
651 if ( mDirection == ByRow )
653 unsigned char* sourceFirstLine = block.image->scanLine( 0 );
654 unsigned char* sourceRef;
657 for (
unsigned int y = block.beginLine; y < block.endLine; ++y, outputLineRef += mDestImageBpl )
659 sourceRef = sourceFirstLine;
660 destRef = ( QRgb* )outputLineRef;
661 for (
int x = 0; x < width; ++x, ++destRef, sourceRef += 4 )
663 *destRef = gaussianBlurVertical( y, sourceRef, sourceBpl, height );
669 unsigned char* sourceRef = block.image->scanLine( block.beginLine );
670 for (
unsigned int y = block.beginLine; y < block.endLine; ++y, outputLineRef += mDestImageBpl, sourceRef += sourceBpl )
672 destRef = ( QRgb* )outputLineRef;
673 for (
int x = 0; x < width; ++x, ++destRef )
675 *destRef = gaussianBlurHorizontal( x, sourceRef, width );
681 inline QRgb QgsImageOperation::GaussianBlurOperation::gaussianBlurVertical(
const int posy,
unsigned char *sourceFirstLine,
const int sourceBpl,
const int height )
690 for (
int i = 0; i <= mRadius*2; ++i )
692 y = qBound( 0, posy + ( i - mRadius ), height - 1 );
693 ref = sourceFirstLine + sourceBpl * y;
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 );
702 return qRgba( r, g, b, a );
705 inline QRgb QgsImageOperation::GaussianBlurOperation::gaussianBlurHorizontal(
const int posx,
unsigned char *sourceFirstLine,
const int width )
714 for (
int i = 0; i <= mRadius*2; ++i )
716 x = qBound( 0, posx + ( i - mRadius ), width - 1 );
717 ref = sourceFirstLine + x * 4;
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 );
726 return qRgba( r, g, b, a );
730 double* QgsImageOperation::createGaussianKernel(
const int radius )
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;
740 for (
int i = 0; i <= radius; ++i )
742 result = coefficient * exp( i * i * expCoefficient );
743 kernel[ radius - i ] = result;
747 kernel[radius + i] = result;
752 for (
int i = 0; i <= radius * 2; ++i )
765 runLineOperation( image, flipOperation );
768 void QgsImageOperation::FlipLineOperation::operator()( QRgb *startRef,
const int lineLength,
const int bytesPerLine )
770 int increment = ( mDirection == QgsImageOperation::ByRow ) ? 4 : bytesPerLine;
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 )
777 tempLine[i++] = *( p++ );
778 tempLine[i++] = *( p++ );
779 tempLine[i++] = *( p++ );
780 tempLine[i] = *( p );
785 p = (
unsigned char* )startRef;
786 for (
int i = ( lineLength - 1 ) * 4; i >= 0; i -= 7, p += increment )
788 *( p++ ) = tempLine[i++];
789 *( p++ ) = tempLine[i++];
790 *( p++ ) = tempLine[i++];
791 *( p ) = tempLine[i];