28#include <QtConcurrentMap>
30using namespace Qt::StringLiterals;
34#define BLOCK_THREADS 16
40template<
typename PixelOperation>
void QgsImageOperation::runPixelOperation( QImage &image, PixelOperation &operation,
QgsFeedback *feedback )
42 if (
static_cast< qgssize >( image.height() ) * image.width() < 100000 )
46 runPixelOperationOnWholeImage( image, operation, feedback );
51 QgsImageOperation::ProcessBlockUsingPixelOperation<PixelOperation> blockOp( operation, feedback );
52 runBlockOperationInThreads( image, blockOp, QgsImageOperation::ByRow );
56template<
typename PixelOperation>
void QgsImageOperation::runPixelOperationOnWholeImage( QImage &image, PixelOperation &operation,
QgsFeedback *feedback )
58 int height = image.height();
59 int width = image.width();
60 for (
int y = 0; y < height; ++y )
65 QRgb *ref =
reinterpret_cast< QRgb *
>( image.scanLine( y ) );
66 for (
int x = 0; x < width; ++x )
68 operation( ref[x], x, y );
75template<
typename RectOperation>
void QgsImageOperation::runRectOperation( QImage &image, RectOperation &operation )
78 if (
static_cast< qgssize >( image.height() ) * image.width() < 100000 )
82 runRectOperationOnWholeImage( image, operation );
87 runBlockOperationInThreads( image, operation, ByRow );
91template<
class RectOperation>
void QgsImageOperation::runRectOperationOnWholeImage( QImage &image, RectOperation &operation )
94 fullImage.beginLine = 0;
95 fullImage.endLine = image.height();
96 fullImage.lineLength = image.width();
97 fullImage.image = ℑ
99 operation( fullImage );
104template<
typename LineOperation>
void QgsImageOperation::runLineOperation( QImage &image, LineOperation &operation,
QgsFeedback *feedback )
107 if (
static_cast< qgssize >( image.height() ) * image.width() < 100000 )
111 runLineOperationOnWholeImage( image, operation, feedback );
116 QgsImageOperation::ProcessBlockUsingLineOperation<LineOperation> blockOp( operation );
117 runBlockOperationInThreads( image, blockOp, operation.direction() );
121template<
class LineOperation>
void QgsImageOperation::runLineOperationOnWholeImage( QImage &image, LineOperation &operation,
QgsFeedback *feedback )
123 int height = image.height();
124 int width = image.width();
127 int bpl = image.bytesPerLine();
128 if ( operation.direction() == ByRow )
130 for (
int y = 0; y < height; ++y )
135 QRgb *ref =
reinterpret_cast< QRgb *
>( image.scanLine( y ) );
136 operation( ref, width, bpl );
142 unsigned char *ref = image.scanLine( 0 );
143 for (
int x = 0; x < width; ++x, ref += 4 )
148 operation(
reinterpret_cast< QRgb *
>( ref ), height, bpl );
156template<
typename BlockOperation>
void QgsImageOperation::runBlockOperationInThreads( QImage &image, BlockOperation &operation, LineOperationDirection direction )
158 QList< ImageBlock > blocks;
159 unsigned int height = image.height();
160 unsigned int width = image.width();
162 unsigned int blockDimension1 = ( direction == QgsImageOperation::ByRow ) ? height : width;
163 unsigned int blockDimension2 = ( direction == QgsImageOperation::ByRow ) ? width : height;
167 unsigned int begin = 0;
169 for (
unsigned int block = 0; block <
BLOCK_THREADS; ++block, begin += blockLen )
172 newBlock.beginLine = begin;
174 newBlock.endLine = block < (
BLOCK_THREADS - 1 ) ? begin + blockLen : blockDimension1;
175 newBlock.lineLength = blockDimension2;
176 newBlock.image = ℑ
181 QtConcurrent::blockingMap( blocks, operation );
201 GrayscalePixelOperation operation( mode );
202 runPixelOperation( image, operation, feedback );
205void QgsImageOperation::GrayscalePixelOperation::operator()( QRgb &rgb,
const int x,
const int y )
const
214 grayscaleLuminosityOp( rgb );
217 grayscaleAverageOp( rgb );
221 grayscaleLightnessOp( rgb );
226void QgsImageOperation::grayscaleLightnessOp( QRgb &rgb )
228 int red = qRed( rgb );
229 int green = qGreen( rgb );
230 int blue = qBlue( rgb );
232 int min = std::min( std::min( red, green ), blue );
233 int max = std::max( std::max( red, green ), blue );
235 int lightness = std::min( ( min + max ) / 2, 255 );
236 rgb = qRgba( lightness, lightness, lightness, qAlpha( rgb ) );
239void QgsImageOperation::grayscaleLuminosityOp( QRgb &rgb )
241 int luminosity = 0.21 * qRed( rgb ) + 0.72 * qGreen( rgb ) + 0.07 * qBlue( rgb );
242 rgb = qRgba( luminosity, luminosity, luminosity, qAlpha( rgb ) );
245void QgsImageOperation::grayscaleAverageOp( QRgb &rgb )
247 int average = ( qRed( rgb ) + qGreen( rgb ) + qBlue( rgb ) ) / 3;
248 rgb = qRgba( average, average, average, qAlpha( rgb ) );
257 BrightnessContrastPixelOperation operation( brightness, contrast );
258 runPixelOperation( image, operation, feedback );
261void QgsImageOperation::BrightnessContrastPixelOperation::operator()( QRgb &rgb,
const int x,
const int y )
const
265 int red = adjustColorComponent( qRed( rgb ), mBrightness, mContrast );
266 int blue = adjustColorComponent( qBlue( rgb ), mBrightness, mContrast );
267 int green = adjustColorComponent( qGreen( rgb ), mBrightness, mContrast );
268 rgb = qRgba( red, green, blue, qAlpha( rgb ) );
271int QgsImageOperation::adjustColorComponent(
int colorComponent,
int brightness,
double contrastFactor )
273 return std::clamp(
static_cast< int >( ( ( ( ( ( colorComponent / 255.0 ) - 0.5 ) * contrastFactor ) + 0.5 ) * 255 ) + brightness ), 0, 255 );
281 HueSaturationPixelOperation operation( saturation, colorizeColor.isValid() && colorizeStrength > 0.0, colorizeColor.hue(), colorizeColor.saturation(), colorizeStrength );
282 runPixelOperation( image, operation, feedback );
285void QgsImageOperation::HueSaturationPixelOperation::operator()( QRgb &rgb,
const int x,
const int y )
const
289 QColor tmpColor( rgb );
291 tmpColor.getHsl( &h, &s, &l );
293 if ( mSaturation < 1.0 )
296 s = std::min(
static_cast< int >( s * mSaturation ), 255 );
298 else if ( mSaturation > 1.0 )
302 s = std::min(
static_cast< int >( 255. * ( 1 - std::pow( 1 - ( s / 255. ), std::pow( mSaturation, 2 ) ) ) ), 255 );
308 s = mColorizeSaturation;
309 if ( mColorizeStrength < 1.0 )
312 QColor colorizedColor = QColor::fromHsl( h, s, l );
313 int colorizedR, colorizedG, colorizedB;
314 colorizedColor.getRgb( &colorizedR, &colorizedG, &colorizedB );
317 int r = mColorizeStrength * colorizedR + ( 1 - mColorizeStrength ) * tmpColor.red();
318 int g = mColorizeStrength * colorizedG + ( 1 - mColorizeStrength ) * tmpColor.green();
319 int b = mColorizeStrength * colorizedB + ( 1 - mColorizeStrength ) * tmpColor.blue();
321 rgb = qRgba( r, g, b, qAlpha( rgb ) );
326 tmpColor.setHsl( h, s, l, qAlpha( rgb ) );
327 rgb = tmpColor.rgba();
339 else if ( factor < 1.0 )
343 QColor transparentFillColor = QColor( 0, 0, 0, 255 * factor );
344 if ( image.format() == QImage::Format_Indexed8 )
345 image = image.convertToFormat( QImage::Format_ARGB32 );
349 QPainter painter( &image );
350 painter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
351 painter.fillRect( 0, 0, image.width(), image.height(), transparentFillColor );
358 MultiplyOpacityPixelOperation operation( factor );
359 runPixelOperation( image, operation, feedback );
363void QgsImageOperation::MultiplyOpacityPixelOperation::operator()( QRgb &rgb,
const int x,
const int y )
const
367 rgb = qRgba( qRed( rgb ), qGreen( rgb ), qBlue( rgb ), std::clamp( std::round( mFactor * qAlpha( rgb ) ), 0.0, 255.0 ) );
374 QColor opaqueColor = color;
375 opaqueColor.setAlpha( 255 );
380 QPainter painter( &image );
381 painter.setCompositionMode( QPainter::CompositionMode_SourceIn );
382 painter.fillRect( 0, 0, image.width(), image.height(), opaqueColor );
390 if ( !properties.
ramp )
392 QgsDebugError( u
"no color ramp specified for distance transform"_s );
397 std::unique_ptr<double[]> array(
new double[
static_cast< qgssize >( image.width() ) * image.height()] );
402 ConvertToArrayPixelOperation convertToArray( image.width(), array.get(), properties.
shadeExterior );
403 runPixelOperation( image, convertToArray, feedback );
408 distanceTransform2d( array.get(), image.width(), image.height(), feedback );
415 spread = std::sqrt( maxValueInDistanceTransformArray( array.get(), image.width() * image.height() ) );
419 spread = properties.
spread;
426 ShadeFromArrayOperation shadeFromArray( image.width(), array.get(), spread, properties );
427 runPixelOperation( image, shadeFromArray, feedback );
430void QgsImageOperation::ConvertToArrayPixelOperation::operator()( QRgb &rgb,
const int x,
const int y )
435 if ( qAlpha( rgb ) > 0 )
438 mArray[idx] = 1 - qAlpha( rgb ) / 255.0;
449 if ( qAlpha( rgb ) == 255 )
463void QgsImageOperation::distanceTransform1d(
double *f,
int n,
int *v,
double *z,
double *d )
469 for (
int q = 1; q <= n - 1; q++ )
471 double s = ( ( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
475 s = ( ( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
484 for (
int q = 0; q <= n - 1; q++ )
486 while ( z[k + 1] < q )
488 d[q] = ( q - v[k] ) * ( q - v[k] ) + f[v[k]];
492double QgsImageOperation::maxValueInDistanceTransformArray(
const double *array,
const unsigned int size )
494 double dtMaxValue = array[0];
495 for (
unsigned int i = 1; i < size; ++i )
497 if ( array[i] > dtMaxValue )
499 dtMaxValue = array[i];
506void QgsImageOperation::distanceTransform2d(
double *im,
int width,
int height, QgsFeedback *feedback )
508 int maxDimension = std::max( width, height );
510 std::unique_ptr<double[]> f(
new double[maxDimension] );
511 std::unique_ptr<int[]> v(
new int[maxDimension] );
512 std::unique_ptr<double[]> z(
new double[maxDimension + 1] );
513 std::unique_ptr<double[]> d(
new double[maxDimension] );
516 for (
int x = 0; x < width; x++ )
521 for (
int y = 0; y < height; y++ )
523 f[y] = im[x + y * width];
525 distanceTransform1d( f.get(), height, v.get(), z.get(), d.get() );
526 for (
int y = 0; y < height; y++ )
528 im[x + y * width] = d[y];
533 for (
int y = 0; y < height; y++ )
538 for (
int x = 0; x < width; x++ )
540 f[x] = im[x + y * width];
542 distanceTransform1d( f.get(), width, v.get(), z.get(), d.get() );
543 for (
int x = 0; x < width; x++ )
545 im[x + y * width] = d[x];
550void QgsImageOperation::ShadeFromArrayOperation::operator()( QRgb &rgb,
const int x,
const int y )
552 if ( !mProperties.ramp )
557 rgb = mProperties.ramp->color( 1.0 ).rgba();
561 int idx = y * mWidth + x;
564 double squaredVal = mArray[idx];
565 if ( squaredVal > mSpreadSquared )
567 rgb = Qt::transparent;
571 double distance = std::sqrt( squaredVal );
572 double val = distance / mSpread;
573 QColor rampColor = mProperties.ramp->color( val );
575 if ( ( mProperties.shadeExterior && distance > mSpread - 1 ) )
578 double alphaMultiplyFactor = mSpread - distance;
579 rampColor.setAlpha( rampColor.alpha() * alphaMultiplyFactor );
581 rgb = rampColor.rgba();
589 int tab[] = { 14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 };
590 int alpha = ( radius < 1 ) ? 16 : ( radius > 17 ) ? 1 : tab[radius - 1];
596 QImage::Format originalFormat = image.format();
597 QImage *pImage = ℑ
598 std::unique_ptr< QImage> convertedImage;
599 if ( !alphaOnly && originalFormat != QImage::Format_ARGB32_Premultiplied )
601 convertedImage = std::make_unique< QImage >( image.convertToFormat( QImage::Format_ARGB32_Premultiplied ) );
602 pImage = convertedImage.get();
604 else if ( alphaOnly && originalFormat != QImage::Format_ARGB32 )
606 convertedImage = std::make_unique< QImage >( image.convertToFormat( QImage::Format_ARGB32 ) );
607 pImage = convertedImage.get();
618 i1 = i2 = ( QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3 );
620 StackBlurLineOperation topToBottomBlur( alpha, QgsImageOperation::ByColumn,
true, i1, i2, feedback );
621 runLineOperation( *pImage, topToBottomBlur, feedback );
626 StackBlurLineOperation leftToRightBlur( alpha, QgsImageOperation::ByRow,
true, i1, i2, feedback );
627 runLineOperation( *pImage, leftToRightBlur, feedback );
632 StackBlurLineOperation bottomToTopBlur( alpha, QgsImageOperation::ByColumn,
false, i1, i2, feedback );
633 runLineOperation( *pImage, bottomToTopBlur, feedback );
638 StackBlurLineOperation rightToLeftBlur( alpha, QgsImageOperation::ByRow,
false, i1, i2, feedback );
639 runLineOperation( *pImage, rightToLeftBlur, feedback );
644 if ( pImage->format() != originalFormat )
646 image = pImage->convertToFormat( originalFormat );
654 int width = image.width();
655 int height = image.height();
660 QImage *copy =
new QImage( image.copy() );
664 std::unique_ptr<double[]> kernel( createGaussianKernel( radius ) );
669 QImage::Format originalFormat = image.format();
670 QImage *pImage = ℑ
671 std::unique_ptr< QImage> convertedImage;
672 if ( originalFormat != QImage::Format_ARGB32_Premultiplied )
674 convertedImage = std::make_unique< QImage >( image.convertToFormat( QImage::Format_ARGB32_Premultiplied ) );
675 pImage = convertedImage.get();
685 QImage xBlurImage = QImage( width, height, QImage::Format_ARGB32_Premultiplied );
686 GaussianBlurOperation rowBlur( radius, QgsImageOperation::ByRow, &xBlurImage, kernel.get(), feedback );
687 runRectOperation( *pImage, rowBlur );
693 auto yBlurImage = std::make_unique< QImage >( width, height, QImage::Format_ARGB32_Premultiplied );
694 GaussianBlurOperation colBlur( radius, QgsImageOperation::ByColumn, yBlurImage.get(), kernel.get(), feedback );
695 runRectOperation( xBlurImage, colBlur );
702 if ( originalFormat != QImage::Format_ARGB32_Premultiplied )
704 return new QImage( yBlurImage->convertToFormat( originalFormat ) );
707 return yBlurImage.release();
710void QgsImageOperation::GaussianBlurOperation::operator()( QgsImageOperation::ImageBlock &block )
712 if ( mFeedback && mFeedback->isCanceled() )
715 int width = block.image->width();
716 int height = block.image->height();
717 int sourceBpl = block.image->bytesPerLine();
719 unsigned char *outputLineRef = mDestImage->scanLine( block.beginLine );
720 QRgb *destRef =
nullptr;
721 if ( mDirection == ByRow )
723 unsigned char *sourceFirstLine = block.image->scanLine( 0 );
724 unsigned char *sourceRef;
727 for (
unsigned int y = block.beginLine; y < block.endLine; ++y, outputLineRef += mDestImageBpl )
729 if ( mFeedback && mFeedback->isCanceled() )
732 sourceRef = sourceFirstLine;
733 destRef =
reinterpret_cast< QRgb *
>( outputLineRef );
734 for (
int x = 0; x < width; ++x, ++destRef, sourceRef += 4 )
736 if ( mFeedback && mFeedback->isCanceled() )
739 *destRef = gaussianBlurVertical( y, sourceRef, sourceBpl, height );
745 unsigned char *sourceRef = block.image->scanLine( block.beginLine );
746 for (
unsigned int y = block.beginLine; y < block.endLine; ++y, outputLineRef += mDestImageBpl, sourceRef += sourceBpl )
748 if ( mFeedback && mFeedback->isCanceled() )
751 destRef =
reinterpret_cast< QRgb *
>( outputLineRef );
752 for (
int x = 0; x < width; ++x, ++destRef )
754 if ( mFeedback && mFeedback->isCanceled() )
757 *destRef = gaussianBlurHorizontal( x, sourceRef, width );
763inline QRgb QgsImageOperation::GaussianBlurOperation::gaussianBlurVertical(
const int posy,
unsigned char *sourceFirstLine,
const int sourceBpl,
const int height )
const
772 for (
int i = 0; i <= mRadius * 2; ++i )
774 y = std::clamp( posy + ( i - mRadius ), 0, height - 1 );
775 ref = sourceFirstLine +
static_cast< std::size_t
>( sourceBpl ) * y;
777 QRgb *refRgb =
reinterpret_cast< QRgb *
>( ref );
778 r += mKernel[i] * qRed( *refRgb );
779 g += mKernel[i] * qGreen( *refRgb );
780 b += mKernel[i] * qBlue( *refRgb );
781 a += mKernel[i] * qAlpha( *refRgb );
784 return qRgba( r, g, b, a );
787inline QRgb QgsImageOperation::GaussianBlurOperation::gaussianBlurHorizontal(
const int posx,
unsigned char *sourceFirstLine,
const int width )
const
796 for (
int i = 0; i <= mRadius * 2; ++i )
798 x = std::clamp( posx + ( i - mRadius ), 0, width - 1 );
799 ref = sourceFirstLine + x * 4;
801 QRgb *refRgb =
reinterpret_cast< QRgb *
>( ref );
802 r += mKernel[i] * qRed( *refRgb );
803 g += mKernel[i] * qGreen( *refRgb );
804 b += mKernel[i] * qBlue( *refRgb );
805 a += mKernel[i] * qAlpha( *refRgb );
808 return qRgba( r, g, b, a );
812double *QgsImageOperation::createGaussianKernel(
const int radius )
814 double *kernel =
new double[radius * 2 + 1];
815 double sigma = radius / 3.0;
816 double twoSigmaSquared = 2 * sigma * sigma;
817 double coefficient = 1.0 / std::sqrt( M_PI * twoSigmaSquared );
818 double expCoefficient = -1.0 / twoSigmaSquared;
822 for (
int i = 0; i <= radius; ++i )
824 result = coefficient * std::exp( i * i * expCoefficient );
825 kernel[radius - i] = result;
829 kernel[radius + i] = result;
834 for (
int i = 0; i <= radius * 2; ++i )
848 runLineOperation( image, flipOperation );
853 int width = image.width();
854 int height = image.height();
861 for (
int y = 0; y < height; ++y )
864 const QRgb *imgScanline =
reinterpret_cast< const QRgb *
>( image.constScanLine( y ) );
865 for (
int x = 0; x < width; ++x )
867 if ( qAlpha( imgScanline[x] ) )
882 for (
int y = height - 1; y >= ymin; --y )
885 const QRgb *imgScanline =
reinterpret_cast< const QRgb *
>( image.constScanLine( y ) );
886 for (
int x = 0; x < width; ++x )
888 if ( qAlpha( imgScanline[x] ) )
891 xmin = std::min( xmin, x );
892 xmax = std::max( xmax, x );
902 for (
int y = ymin; y <= ymax; ++y )
904 const QRgb *imgScanline =
reinterpret_cast< const QRgb *
>( image.constScanLine( y ) );
905 for (
int x = 0; x < xmin; ++x )
907 if ( qAlpha( imgScanline[x] ) )
916 for (
int y = ymin; y <= ymax; ++y )
918 const QRgb *imgScanline =
reinterpret_cast< const QRgb *
>( image.constScanLine( y ) );
919 for (
int x = width - 1; x > xmax; --x )
921 if ( qAlpha( imgScanline[x] ) )
929 if ( minSize.isValid() )
931 if ( xmax - xmin < minSize.width() )
933 xmin = std::max( ( xmax + xmin ) / 2 - minSize.width() / 2, 0 );
934 xmax = xmin + minSize.width();
936 if ( ymax - ymin < minSize.height() )
938 ymin = std::max( ( ymax + ymin ) / 2 - minSize.height() / 2, 0 );
939 ymax = ymin + minSize.height();
945 const int dx = std::max( std::abs( xmax - width / 2 ), std::abs( xmin - width / 2 ) );
946 const int dy = std::max( std::abs( ymax - height / 2 ), std::abs( ymin - height / 2 ) );
947 xmin = std::max( 0, width / 2 - dx );
948 xmax = std::min( width, width / 2 + dx );
949 ymin = std::max( 0, height / 2 - dy );
950 ymax = std::min( height, height / 2 + dy );
953 return QRect( xmin, ymin, xmax - xmin, ymax - ymin );
961void QgsImageOperation::FlipLineOperation::operator()( QRgb *startRef,
const int lineLength,
const int bytesPerLine )
const
963 int increment = ( mDirection == QgsImageOperation::ByRow ) ? 4 : bytesPerLine;
966 unsigned char *p =
reinterpret_cast< unsigned char *
>( startRef );
967 unsigned char *tempLine =
new unsigned char[lineLength * 4];
968 for (
int i = 0; i < lineLength * 4; ++i, p += increment )
970 tempLine[i++] = *( p++ );
971 tempLine[i++] = *( p++ );
972 tempLine[i++] = *( p++ );
973 tempLine[i] = *( p );
978 p =
reinterpret_cast< unsigned char *
>( startRef );
979 for (
int i = ( lineLength - 1 ) * 4; i >= 0; i -= 7, p += increment )
981 *( p++ ) = tempLine[i++];
982 *( p++ ) = tempLine[i++];
983 *( p++ ) = tempLine[i++];
984 *( p ) = tempLine[i];
Base class for feedback objects to be used for cancellation of something running in a worker thread.
bool isCanceled() const
Tells whether the operation has been canceled already.
static void adjustHueSaturation(QImage &image, double saturation, const QColor &colorizeColor=QColor(), double colorizeStrength=1.0, QgsFeedback *feedback=nullptr)
Alter the hue or saturation of a QImage.
static void multiplyOpacity(QImage &image, double factor, QgsFeedback *feedback=nullptr)
Multiplies opacity of image pixel values by a factor.
static void distanceTransform(QImage &image, const QgsImageOperation::DistanceTransformProperties &properties, QgsFeedback *feedback=nullptr)
Performs a distance transform on the source image and shades the result using a color ramp.
FlipType
Flip operation types.
@ FlipHorizontal
Flip the image horizontally.
static void overlayColor(QImage &image, const QColor &color)
Overlays a color onto an image.
static void flipImage(QImage &image, FlipType type)
Flips an image horizontally or vertically.
static void adjustBrightnessContrast(QImage &image, int brightness, double contrast, QgsFeedback *feedback=nullptr)
Alter the brightness or contrast of a QImage.
static QImage * gaussianBlur(QImage &image, int radius, QgsFeedback *feedback=nullptr)
Performs a gaussian blur on an image.
static QRect nonTransparentImageRect(const QImage &image, QSize minSize=QSize(), bool center=false)
Calculates the non-transparent region of an image.
static void stackBlur(QImage &image, int radius, bool alphaOnly=false, QgsFeedback *feedback=nullptr)
Performs a stack 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 convertToGrayscale(QImage &image, GrayscaleMode mode=GrayscaleLuminosity, QgsFeedback *feedback=nullptr)
Convert a QImage to a grayscale image.
GrayscaleMode
Modes for converting a QImage to grayscale.
@ GrayscaleLightness
Keep the lightness of the color, drops the saturation.
@ GrayscaleLuminosity
Grayscale by perceptual luminosity (weighted sum of color RGB components).
@ GrayscaleAverage
Grayscale by taking average of color RGB components.
unsigned long long qgssize
Qgssize is used instead of size_t, because size_t is stdlib type, unknown by SIP, and it would be har...
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
#define QgsDebugError(str)