30#include <QtConcurrentMap>
32using namespace Qt::StringLiterals;
36#define BLOCK_THREADS 16
42template<
typename PixelOperation>
void QgsImageOperation::runPixelOperation( QImage &image, PixelOperation &operation,
QgsFeedback *feedback )
44 if (
static_cast< qgssize >( image.height() ) * image.width() < 100000 )
48 runPixelOperationOnWholeImage( image, operation, feedback );
53 QgsImageOperation::ProcessBlockUsingPixelOperation<PixelOperation> blockOp( operation, feedback );
54 runBlockOperationInThreads( image, blockOp, QgsImageOperation::ByRow );
58template<
typename PixelOperation>
void QgsImageOperation::runPixelOperationOnWholeImage( QImage &image, PixelOperation &operation,
QgsFeedback *feedback )
60 int height = image.height();
61 int width = image.width();
62 for (
int y = 0; y < height; ++y )
67 QRgb *ref =
reinterpret_cast< QRgb *
>( image.scanLine( y ) );
68 for (
int x = 0; x < width; ++x )
70 operation( ref[x], x, y );
77template<
typename RectOperation>
void QgsImageOperation::runRectOperation( QImage &image, RectOperation &operation )
80 if (
static_cast< qgssize >( image.height() ) * image.width() < 100000 )
84 runRectOperationOnWholeImage( image, operation );
89 runBlockOperationInThreads( image, operation, ByRow );
93template<
class RectOperation>
void QgsImageOperation::runRectOperationOnWholeImage( QImage &image, RectOperation &operation )
96 fullImage.beginLine = 0;
97 fullImage.endLine = image.height();
98 fullImage.lineLength = image.width();
99 fullImage.image = ℑ
101 operation( fullImage );
106template<
typename LineOperation>
void QgsImageOperation::runLineOperation( QImage &image, LineOperation &operation,
QgsFeedback *feedback )
109 if (
static_cast< qgssize >( image.height() ) * image.width() < 100000 )
113 runLineOperationOnWholeImage( image, operation, feedback );
118 QgsImageOperation::ProcessBlockUsingLineOperation<LineOperation> blockOp( operation );
119 runBlockOperationInThreads( image, blockOp, operation.direction() );
123template<
class LineOperation>
void QgsImageOperation::runLineOperationOnWholeImage( QImage &image, LineOperation &operation,
QgsFeedback *feedback )
125 int height = image.height();
126 int width = image.width();
129 int bpl = image.bytesPerLine();
130 if ( operation.direction() == ByRow )
132 for (
int y = 0; y < height; ++y )
137 QRgb *ref =
reinterpret_cast< QRgb *
>( image.scanLine( y ) );
138 operation( ref, width, bpl );
144 unsigned char *ref = image.scanLine( 0 );
145 for (
int x = 0; x < width; ++x, ref += 4 )
150 operation(
reinterpret_cast< QRgb *
>( ref ), height, bpl );
158template<
typename BlockOperation>
void QgsImageOperation::runBlockOperationInThreads( QImage &image, BlockOperation &operation, LineOperationDirection direction )
160 QList< ImageBlock > blocks;
161 unsigned int height = image.height();
162 unsigned int width = image.width();
164 unsigned int blockDimension1 = ( direction == QgsImageOperation::ByRow ) ? height : width;
165 unsigned int blockDimension2 = ( direction == QgsImageOperation::ByRow ) ? width : height;
169 unsigned int begin = 0;
171 for (
unsigned int block = 0; block <
BLOCK_THREADS; ++block, begin += blockLen )
174 newBlock.beginLine = begin;
176 newBlock.endLine = block < (
BLOCK_THREADS - 1 ) ? begin + blockLen : blockDimension1;
177 newBlock.lineLength = blockDimension2;
178 newBlock.image = ℑ
183 QtConcurrent::blockingMap( blocks, operation );
203 GrayscalePixelOperation operation( mode );
204 runPixelOperation( image, operation, feedback );
207void QgsImageOperation::GrayscalePixelOperation::operator()( QRgb &rgb,
const int x,
const int y )
const
216 grayscaleLuminosityOp( rgb );
219 grayscaleAverageOp( rgb );
223 grayscaleLightnessOp( rgb );
228void QgsImageOperation::grayscaleLightnessOp( QRgb &rgb )
230 int red = qRed( rgb );
231 int green = qGreen( rgb );
232 int blue = qBlue( rgb );
234 int min = std::min( std::min( red, green ), blue );
235 int max = std::max( std::max( red, green ), blue );
237 int lightness = std::min( ( min + max ) / 2, 255 );
238 rgb = qRgba( lightness, lightness, lightness, qAlpha( rgb ) );
241void QgsImageOperation::grayscaleLuminosityOp( QRgb &rgb )
243 int luminosity = 0.21 * qRed( rgb ) + 0.72 * qGreen( rgb ) + 0.07 * qBlue( rgb );
244 rgb = qRgba( luminosity, luminosity, luminosity, qAlpha( rgb ) );
247void QgsImageOperation::grayscaleAverageOp( QRgb &rgb )
249 int average = ( qRed( rgb ) + qGreen( rgb ) + qBlue( rgb ) ) / 3;
250 rgb = qRgba( average, average, average, qAlpha( rgb ) );
259 BrightnessContrastPixelOperation operation( brightness, contrast );
260 runPixelOperation( image, operation, feedback );
263void QgsImageOperation::BrightnessContrastPixelOperation::operator()( QRgb &rgb,
const int x,
const int y )
const
267 int red = adjustColorComponent( qRed( rgb ), mBrightness, mContrast );
268 int blue = adjustColorComponent( qBlue( rgb ), mBrightness, mContrast );
269 int green = adjustColorComponent( qGreen( rgb ), mBrightness, mContrast );
270 rgb = qRgba( red, green, blue, qAlpha( rgb ) );
273int QgsImageOperation::adjustColorComponent(
int colorComponent,
int brightness,
double contrastFactor )
275 return std::clamp(
static_cast< int >( ( ( ( ( ( colorComponent / 255.0 ) - 0.5 ) * contrastFactor ) + 0.5 ) * 255 ) + brightness ), 0, 255 );
283 HueSaturationPixelOperation operation( saturation, colorizeColor.isValid() && colorizeStrength > 0.0, colorizeColor.hue(), colorizeColor.saturation(), colorizeStrength );
284 runPixelOperation( image, operation, feedback );
287void QgsImageOperation::HueSaturationPixelOperation::operator()( QRgb &rgb,
const int x,
const int y )
const
291 QColor tmpColor( rgb );
293 tmpColor.getHsl( &h, &s, &l );
295 if ( mSaturation < 1.0 )
298 s = std::min(
static_cast< int >( s * mSaturation ), 255 );
300 else if ( mSaturation > 1.0 )
304 s = std::min(
static_cast< int >( 255. * ( 1 - std::pow( 1 - ( s / 255. ), std::pow( mSaturation, 2 ) ) ) ), 255 );
310 s = mColorizeSaturation;
311 if ( mColorizeStrength < 1.0 )
314 QColor colorizedColor = QColor::fromHsl( h, s, l );
315 int colorizedR, colorizedG, colorizedB;
316 colorizedColor.getRgb( &colorizedR, &colorizedG, &colorizedB );
319 int r = mColorizeStrength * colorizedR + ( 1 - mColorizeStrength ) * tmpColor.red();
320 int g = mColorizeStrength * colorizedG + ( 1 - mColorizeStrength ) * tmpColor.green();
321 int b = mColorizeStrength * colorizedB + ( 1 - mColorizeStrength ) * tmpColor.blue();
323 rgb = qRgba( r, g, b, qAlpha( rgb ) );
328 tmpColor.setHsl( h, s, l, qAlpha( rgb ) );
329 rgb = tmpColor.rgba();
341 else if ( factor < 1.0 )
345 QColor transparentFillColor = QColor( 0, 0, 0, 255 * factor );
346 if ( image.format() == QImage::Format_Indexed8 )
347 image = image.convertToFormat( QImage::Format_ARGB32 );
351 QPainter painter( &image );
352 painter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
353 painter.fillRect( 0, 0, image.width(), image.height(), transparentFillColor );
360 MultiplyOpacityPixelOperation operation( factor );
361 runPixelOperation( image, operation, feedback );
365void QgsImageOperation::MultiplyOpacityPixelOperation::operator()( QRgb &rgb,
const int x,
const int y )
const
369 rgb = qRgba( qRed( rgb ), qGreen( rgb ), qBlue( rgb ), std::clamp( std::round( mFactor * qAlpha( rgb ) ), 0.0, 255.0 ) );
376 QColor opaqueColor = color;
377 opaqueColor.setAlpha( 255 );
382 QPainter painter( &image );
383 painter.setCompositionMode( QPainter::CompositionMode_SourceIn );
384 painter.fillRect( 0, 0, image.width(), image.height(), opaqueColor );
392 if ( !properties.
ramp )
394 QgsDebugError( u
"no color ramp specified for distance transform"_s );
399 std::unique_ptr<double[]> array(
new double[
static_cast< qgssize >( image.width() ) * image.height()] );
404 ConvertToArrayPixelOperation convertToArray( image.width(), array.get(), properties.
shadeExterior );
405 runPixelOperation( image, convertToArray, feedback );
410 distanceTransform2d( array.get(), image.width(), image.height(), feedback );
417 spread = std::sqrt( maxValueInDistanceTransformArray( array.get(), image.width() * image.height() ) );
421 spread = properties.
spread;
428 ShadeFromArrayOperation shadeFromArray( image.width(), array.get(), spread, properties );
429 runPixelOperation( image, shadeFromArray, feedback );
432void QgsImageOperation::ConvertToArrayPixelOperation::operator()( QRgb &rgb,
const int x,
const int y )
437 if ( qAlpha( rgb ) > 0 )
440 mArray[idx] = 1 - qAlpha( rgb ) / 255.0;
451 if ( qAlpha( rgb ) == 255 )
465void QgsImageOperation::distanceTransform1d(
double *f,
int n,
int *v,
double *z,
double *d )
471 for (
int q = 1; q <= n - 1; q++ )
473 double s = ( ( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
477 s = ( ( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
486 for (
int q = 0; q <= n - 1; q++ )
488 while ( z[k + 1] < q )
490 d[q] = ( q - v[k] ) * ( q - v[k] ) + f[v[k]];
494double QgsImageOperation::maxValueInDistanceTransformArray(
const double *array,
const unsigned int size )
496 double dtMaxValue = array[0];
497 for (
unsigned int i = 1; i < size; ++i )
499 if ( array[i] > dtMaxValue )
501 dtMaxValue = array[i];
508void QgsImageOperation::distanceTransform2d(
double *im,
int width,
int height, QgsFeedback *feedback )
510 int maxDimension = std::max( width, height );
512 std::unique_ptr<double[]> f(
new double[maxDimension] );
513 std::unique_ptr<int[]> v(
new int[maxDimension] );
514 std::unique_ptr<double[]> z(
new double[maxDimension + 1] );
515 std::unique_ptr<double[]> d(
new double[maxDimension] );
518 for (
int x = 0; x < width; x++ )
523 for (
int y = 0; y < height; y++ )
525 f[y] = im[x + y * width];
527 distanceTransform1d( f.get(), height, v.get(), z.get(), d.get() );
528 for (
int y = 0; y < height; y++ )
530 im[x + y * width] = d[y];
535 for (
int y = 0; y < height; y++ )
540 for (
int x = 0; x < width; x++ )
542 f[x] = im[x + y * width];
544 distanceTransform1d( f.get(), width, v.get(), z.get(), d.get() );
545 for (
int x = 0; x < width; x++ )
547 im[x + y * width] = d[x];
552void QgsImageOperation::ShadeFromArrayOperation::operator()( QRgb &rgb,
const int x,
const int y )
554 if ( !mProperties.ramp )
559 rgb = mProperties.ramp->color( 1.0 ).rgba();
563 int idx = y * mWidth + x;
566 double squaredVal = mArray[idx];
567 if ( squaredVal > mSpreadSquared )
569 rgb = Qt::transparent;
573 double distance = std::sqrt( squaredVal );
574 double val = distance / mSpread;
575 QColor rampColor = mProperties.ramp->color( val );
577 if ( ( mProperties.shadeExterior && distance > mSpread - 1 ) )
580 double alphaMultiplyFactor = mSpread - distance;
581 rampColor.setAlpha( rampColor.alpha() * alphaMultiplyFactor );
583 rgb = rampColor.rgba();
591 int tab[] = { 14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 };
592 int alpha = ( radius < 1 ) ? 16 : ( radius > 17 ) ? 1 : tab[radius - 1];
598 QImage::Format originalFormat = image.format();
599 QImage *pImage = ℑ
600 std::unique_ptr< QImage> convertedImage;
601 if ( !alphaOnly && originalFormat != QImage::Format_ARGB32_Premultiplied )
603 convertedImage = std::make_unique< QImage >( image.convertToFormat( QImage::Format_ARGB32_Premultiplied ) );
604 pImage = convertedImage.get();
606 else if ( alphaOnly && originalFormat != QImage::Format_ARGB32 )
608 convertedImage = std::make_unique< QImage >( image.convertToFormat( QImage::Format_ARGB32 ) );
609 pImage = convertedImage.get();
620 i1 = i2 = ( QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3 );
622 StackBlurLineOperation topToBottomBlur( alpha, QgsImageOperation::ByColumn,
true, i1, i2, feedback );
623 runLineOperation( *pImage, topToBottomBlur, feedback );
628 StackBlurLineOperation leftToRightBlur( alpha, QgsImageOperation::ByRow,
true, i1, i2, feedback );
629 runLineOperation( *pImage, leftToRightBlur, feedback );
634 StackBlurLineOperation bottomToTopBlur( alpha, QgsImageOperation::ByColumn,
false, i1, i2, feedback );
635 runLineOperation( *pImage, bottomToTopBlur, feedback );
640 StackBlurLineOperation rightToLeftBlur( alpha, QgsImageOperation::ByRow,
false, i1, i2, feedback );
641 runLineOperation( *pImage, rightToLeftBlur, feedback );
646 if ( pImage->format() != originalFormat )
648 image = pImage->convertToFormat( originalFormat );
656 int width = image.width();
657 int height = image.height();
665 std::unique_ptr<double[]> kernel( createGaussianKernel( radius ) );
670 QImage::Format originalFormat = image.format();
671 QImage *pImage = ℑ
672 std::unique_ptr< QImage> convertedImage;
673 if ( originalFormat != QImage::Format_ARGB32_Premultiplied )
675 convertedImage = std::make_unique< QImage >( image.convertToFormat( QImage::Format_ARGB32_Premultiplied ) );
676 pImage = convertedImage.get();
686 QImage xBlurImage = QImage( width, height, QImage::Format_ARGB32_Premultiplied );
687 GaussianBlurOperation rowBlur( radius, QgsImageOperation::ByRow, &xBlurImage, kernel.get(), feedback );
688 runRectOperation( *pImage, rowBlur );
694 auto yBlurImage = QImage( width, height, QImage::Format_ARGB32_Premultiplied );
695 GaussianBlurOperation colBlur( radius, QgsImageOperation::ByColumn, &yBlurImage, kernel.get(), feedback );
696 runRectOperation( xBlurImage, colBlur );
703 if ( originalFormat != QImage::Format_ARGB32_Premultiplied )
705 return QImage( yBlurImage.convertToFormat( originalFormat ) );
711void QgsImageOperation::GaussianBlurOperation::operator()( QgsImageOperation::ImageBlock &block )
713 if ( mFeedback && mFeedback->isCanceled() )
716 int width = block.image->width();
717 int height = block.image->height();
718 int sourceBpl = block.image->bytesPerLine();
720 unsigned char *outputLineRef = mDestImage->scanLine( block.beginLine );
721 QRgb *destRef =
nullptr;
722 if ( mDirection == ByRow )
724 unsigned char *sourceFirstLine = block.image->scanLine( 0 );
725 unsigned char *sourceRef;
728 for (
unsigned int y = block.beginLine; y < block.endLine; ++y, outputLineRef += mDestImageBpl )
730 if ( mFeedback && mFeedback->isCanceled() )
733 sourceRef = sourceFirstLine;
734 destRef =
reinterpret_cast< QRgb *
>( outputLineRef );
735 for (
int x = 0; x < width; ++x, ++destRef, sourceRef += 4 )
737 if ( mFeedback && mFeedback->isCanceled() )
740 *destRef = gaussianBlurVertical( y, sourceRef, sourceBpl, height );
746 unsigned char *sourceRef = block.image->scanLine( block.beginLine );
747 for (
unsigned int y = block.beginLine; y < block.endLine; ++y, outputLineRef += mDestImageBpl, sourceRef += sourceBpl )
749 if ( mFeedback && mFeedback->isCanceled() )
752 destRef =
reinterpret_cast< QRgb *
>( outputLineRef );
753 for (
int x = 0; x < width; ++x, ++destRef )
755 if ( mFeedback && mFeedback->isCanceled() )
758 *destRef = gaussianBlurHorizontal( x, sourceRef, width );
764inline QRgb QgsImageOperation::GaussianBlurOperation::gaussianBlurVertical(
const int posy,
unsigned char *sourceFirstLine,
const int sourceBpl,
const int height )
const
773 for (
int i = 0; i <= mRadius * 2; ++i )
775 y = std::clamp( posy + ( i - mRadius ), 0, height - 1 );
776 ref = sourceFirstLine +
static_cast< std::size_t
>( sourceBpl ) * y;
778 QRgb *refRgb =
reinterpret_cast< QRgb *
>( ref );
779 r += mKernel[i] * qRed( *refRgb );
780 g += mKernel[i] * qGreen( *refRgb );
781 b += mKernel[i] * qBlue( *refRgb );
782 a += mKernel[i] * qAlpha( *refRgb );
785 return qRgba( r, g, b, a );
788inline QRgb QgsImageOperation::GaussianBlurOperation::gaussianBlurHorizontal(
const int posx,
unsigned char *sourceFirstLine,
const int width )
const
797 for (
int i = 0; i <= mRadius * 2; ++i )
799 x = std::clamp( posx + ( i - mRadius ), 0, width - 1 );
800 ref = sourceFirstLine + x * 4;
802 QRgb *refRgb =
reinterpret_cast< QRgb *
>( ref );
803 r += mKernel[i] * qRed( *refRgb );
804 g += mKernel[i] * qGreen( *refRgb );
805 b += mKernel[i] * qBlue( *refRgb );
806 a += mKernel[i] * qAlpha( *refRgb );
809 return qRgba( r, g, b, a );
813double *QgsImageOperation::createGaussianKernel(
const int radius )
815 double *kernel =
new double[radius * 2 + 1];
816 double sigma = radius / 3.0;
817 double twoSigmaSquared = 2 * sigma * sigma;
818 double coefficient = 1.0 / std::sqrt( M_PI * twoSigmaSquared );
819 double expCoefficient = -1.0 / twoSigmaSquared;
823 for (
int i = 0; i <= radius; ++i )
825 result = coefficient * std::exp( i * i * expCoefficient );
826 kernel[radius - i] = result;
830 kernel[radius + i] = result;
835 for (
int i = 0; i <= radius * 2; ++i )
849 runLineOperation( image, flipOperation );
854 int width = image.width();
855 int height = image.height();
862 for (
int y = 0; y < height; ++y )
865 const QRgb *imgScanline =
reinterpret_cast< const QRgb *
>( image.constScanLine( y ) );
866 for (
int x = 0; x < width; ++x )
868 if ( qAlpha( imgScanline[x] ) )
883 for (
int y = height - 1; y >= ymin; --y )
886 const QRgb *imgScanline =
reinterpret_cast< const QRgb *
>( image.constScanLine( y ) );
887 for (
int x = 0; x < width; ++x )
889 if ( qAlpha( imgScanline[x] ) )
892 xmin = std::min( xmin, x );
893 xmax = std::max( xmax, x );
903 for (
int y = ymin; y <= ymax; ++y )
905 const QRgb *imgScanline =
reinterpret_cast< const QRgb *
>( image.constScanLine( y ) );
906 for (
int x = 0; x < xmin; ++x )
908 if ( qAlpha( imgScanline[x] ) )
917 for (
int y = ymin; y <= ymax; ++y )
919 const QRgb *imgScanline =
reinterpret_cast< const QRgb *
>( image.constScanLine( y ) );
920 for (
int x = width - 1; x > xmax; --x )
922 if ( qAlpha( imgScanline[x] ) )
930 if ( minSize.isValid() )
932 if ( xmax - xmin < minSize.width() )
934 xmin = std::max( ( xmax + xmin ) / 2 - minSize.width() / 2, 0 );
935 xmax = xmin + minSize.width();
937 if ( ymax - ymin < minSize.height() )
939 ymin = std::max( ( ymax + ymin ) / 2 - minSize.height() / 2, 0 );
940 ymax = ymin + minSize.height();
946 const int dx = std::max( std::abs( xmax - width / 2 ), std::abs( xmin - width / 2 ) );
947 const int dy = std::max( std::abs( ymax - height / 2 ), std::abs( ymin - height / 2 ) );
948 xmin = std::max( 0, width / 2 - dx );
949 xmax = std::min( width, width / 2 + dx );
950 ymin = std::max( 0, height / 2 - dy );
951 ymax = std::min( height, height / 2 + dy );
954 return QRect( xmin, ymin, xmax - xmin, ymax - ymin );
964 if ( image.isNull() )
968 switch ( image.format() )
970 case QImage::Format_Invalid:
971 case QImage::Format_Mono:
972 case QImage::Format_MonoLSB:
973 case QImage::Format_Indexed8:
974 case QImage::Format_RGB32:
975 case QImage::Format_RGB16:
976 case QImage::Format_RGB666:
977 case QImage::Format_RGB555:
978 case QImage::Format_RGB888:
979 case QImage::Format_RGB444:
980 case QImage::Format_RGBX8888:
981 case QImage::Format_BGR30:
982 case QImage::Format_RGB30:
983 case QImage::Format_Grayscale8:
984 case QImage::Format_RGBX64:
985 case QImage::Format_Grayscale16:
986 case QImage::Format_BGR888:
987 case QImage::Format_RGBX16FPx4:
988 case QImage::Format_RGBX32FPx4:
989 case QImage::Format_CMYK8888:
990 case QImage::NImageFormats:
993 case QImage::Format_ARGB32:
994 case QImage::Format_ARGB32_Premultiplied:
995 case QImage::Format_ARGB8565_Premultiplied:
996 case QImage::Format_ARGB6666_Premultiplied:
997 case QImage::Format_ARGB8555_Premultiplied:
998 case QImage::Format_ARGB4444_Premultiplied:
999 case QImage::Format_RGBA8888:
1000 case QImage::Format_RGBA8888_Premultiplied:
1001 case QImage::Format_A2BGR30_Premultiplied:
1002 case QImage::Format_A2RGB30_Premultiplied:
1003 case QImage::Format_Alpha8:
1004 case QImage::Format_RGBA64:
1005 case QImage::Format_RGBA64_Premultiplied:
1006 case QImage::Format_RGBA16FPx4:
1007 case QImage::Format_RGBA16FPx4_Premultiplied:
1008 case QImage::Format_RGBA32FPx4:
1009 case QImage::Format_RGBA32FPx4_Premultiplied:
1012 const int width = image.width();
1013 const int height = image.height();
1014 const qsizetype bytesPerLine = image.bytesPerLine();
1015 const qsizetype totalPixels =
static_cast<qsizetype
>( width ) * height;
1017 constexpr uint32_t ALPHA_MASK_32 = 0xFF000000;
1018 constexpr uint64_t ALPHA_MASK_64 = 0xFF000000FF000000ULL;
1021 if ( image.format() == QImage::Format_ARGB32 || image.format() == QImage::Format_ARGB32_Premultiplied )
1023 if ( bytesPerLine ==
static_cast< qsizetype
>( width ) * 4 )
1026 const uint64_t *ptr64 =
reinterpret_cast<const uint64_t *
>( image.constBits() );
1027 const qsizetype count64 = totalPixels / 2;
1028 for ( qsizetype i = 0; i < count64; ++i )
1031 if ( ( ptr64[i] & ALPHA_MASK_64 ) != 0 )
1035 if ( totalPixels % 2 != 0 )
1037 const uint32_t *ptr32 =
reinterpret_cast<const uint32_t *
>( image.constBits() );
1038 if ( ( ptr32[totalPixels - 1] & ALPHA_MASK_32 ) != 0 )
1045 for (
int y = 0; y < height; ++y )
1047 const uint32_t *line =
reinterpret_cast<const uint32_t *
>( image.constScanLine( y ) );
1048 for (
int x = 0; x < width; ++x )
1051 if ( ( line[x] & ALPHA_MASK_32 ) != 0 )
1062 return isBlankImage( image.convertToFormat( QImage::Format_ARGB32 ) );
1067 inline bool check32BitImage(
const QImage &image, uint32_t targetPixel )
1069 const int width = image.width();
1070 const int height = image.height();
1071 const qsizetype bytesPerLine = image.bytesPerLine();
1072 const qsizetype totalPixels =
static_cast<qsizetype
>( width ) * height;
1075 if ( bytesPerLine ==
static_cast<qsizetype
>( width ) * 4 )
1077 const uint64_t *ptr64 =
reinterpret_cast<const uint64_t *
>( image.constBits() );
1078 const qsizetype count64 = totalPixels / 2;
1079 const uint64_t target64 = (
static_cast<uint64_t
>( targetPixel ) << 32 ) | targetPixel;
1080 for ( qsizetype i = 0; i < count64; ++i )
1082 if ( ptr64[i] != target64 )
1086 if ( totalPixels % 2 != 0 )
1088 const uint32_t *ptr32 =
reinterpret_cast<const uint32_t *
>( image.constBits() );
1089 if ( ptr32[totalPixels - 1] != targetPixel )
1095 for (
int y = 0; y < height; ++y )
1097 const uint32_t *line =
reinterpret_cast<const uint32_t *
>( image.constScanLine( y ) );
1098 for (
int x = 0; x < width; ++x )
1100 if ( line[x] != targetPixel )
1111 if ( image.isNull() )
1114 switch ( image.format() )
1116 case QImage::Format_ARGB32:
1117 return check32BitImage( image,
static_cast<uint32_t
>( color.rgba() ) );
1119 case QImage::Format_RGB32:
1120 return check32BitImage( image,
static_cast<uint32_t
>( color.rgb() ) );
1122 case QImage::Format_ARGB32_Premultiplied:
1123 return check32BitImage( image,
static_cast<uint32_t
>( qPremultiply( color.rgba() ) ) );
1132 return isSingleColor( image.convertToFormat( QImage::Format_ARGB32 ), color );
1137 if ( tolerance == 0 )
1141 return std::abs( qRed( c1 ) - qRed( c2 ) ) <= tolerance
1142 && std::abs( qGreen( c1 ) - qGreen( c2 ) ) <= tolerance
1143 && std::abs( qBlue( c1 ) - qBlue( c2 ) ) <= tolerance
1144 && std::abs( qAlpha( c1 ) - qAlpha( c2 ) ) <= tolerance;
1149 if ( image.isNull() || !image.rect().contains( startPoint ) )
1155 if ( image.format() != QImage::Format_ARGB32 && image.format() != QImage::Format_ARGB32_Premultiplied && image.format() != QImage::Format_RGB32 )
1157 resultImage = image.convertToFormat( QImage::Format_ARGB32 );
1161 resultImage = image.copy();
1164 const QRgb targetColorRgb = resultImage.pixel( startPoint );
1165 const QRgb newColorRgb = newColor.rgba();
1171 const int width = resultImage.width();
1172 const int height = resultImage.height();
1174 std::stack<QPoint> stack;
1175 stack.push( startPoint );
1176 while ( !stack.empty() )
1183 const QPoint pt = stack.top();
1185 const int x = pt.x();
1186 const int y = pt.y();
1187 QRgb *scanline =
reinterpret_cast< QRgb *
>( resultImage.scanLine( y ) );
1197 scanline[x2] = newColorRgb;
1202 bool spanAbove =
false;
1203 bool spanBelow =
false;
1205 QRgb *scanlineAbove = ( y > 0 ) ?
reinterpret_cast< QRgb *
>( resultImage.scanLine( y - 1 ) ) :
nullptr;
1206 QRgb *scanlineBelow = ( y < height - 1 ) ?
reinterpret_cast< QRgb *
>( resultImage.scanLine( y + 1 ) ) :
nullptr;
1207 for (
int currX = x1; currX <= x2; currX++ )
1210 if ( scanlineAbove )
1213 if ( !spanAbove && match )
1215 stack.push( QPoint( currX, y - 1 ) );
1218 else if ( spanAbove && !match )
1225 if ( scanlineBelow )
1228 if ( !spanBelow && match )
1230 stack.push( QPoint( currX, y + 1 ) );
1233 else if ( spanBelow && !match )
1244void QgsImageOperation::FlipLineOperation::operator()( QRgb *startRef,
const int lineLength,
const int bytesPerLine )
const
1246 int increment = ( mDirection == QgsImageOperation::ByRow ) ? 4 : bytesPerLine;
1249 unsigned char *p =
reinterpret_cast< unsigned char *
>( startRef );
1250 unsigned char *tempLine =
new unsigned char[lineLength * 4];
1251 for (
int i = 0; i < lineLength * 4; ++i, p += increment )
1253 tempLine[i++] = *( p++ );
1254 tempLine[i++] = *( p++ );
1255 tempLine[i++] = *( p++ );
1256 tempLine[i] = *( p );
1261 p =
reinterpret_cast< unsigned char *
>( startRef );
1262 for (
int i = ( lineLength - 1 ) * 4; i >= 0; i -= 7, p += increment )
1264 *( p++ ) = tempLine[i++];
1265 *( p++ ) = tempLine[i++];
1266 *( p++ ) = tempLine[i++];
1267 *( 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 QImage floodFill(const QImage &image, const QPoint &startPoint, const QColor &newColor, int tolerance=0, QgsFeedback *feedback=nullptr)
Performs a flood fill operation on an image, replacing contiguous areas of the same color.
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 bool isBlankImage(const QImage &image)
Tests whether an image is completely blank, i.e.
static bool isSingleColor(const QImage &image, const QColor &color)
Tests whether an image is consists only of pixels matching the specified color.
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.
static QImage gaussianBlur(QImage &image, int radius, QgsFeedback *feedback=nullptr)
Performs a gaussian blur on an image.
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
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).
bool colorsMatchFloodFill(QRgb c1, QRgb c2, int tolerance)
#define QgsDebugError(str)