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];