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.