29#define GDAL_MINMAXELT_NS qgis_gdal 
   30#include "gdal_minmax_element.hpp" 
   33const QRgb QgsRasterBlock::NO_DATA_COLOR = qRgba( 0, 0, 0, 0 );
 
   36  : mNoDataValue( std::numeric_limits<double>::quiet_NaN() )
 
 
   41  : mDataType( dataType )
 
   44  , mNoDataValue( std::numeric_limits<double>::quiet_NaN() )
 
   46  ( void )
reset( mDataType, mWidth, mHeight );
 
 
   51  QgsDebugMsgLevel( QStringLiteral( 
"mData = %1" ).arg( 
reinterpret_cast< quint64 
>( mData ) ), 4 );
 
 
   66  mNoDataBitmap = 
nullptr;
 
   71  mHasNoDataValue = 
false;
 
   72  mNoDataValue = std::numeric_limits<double>::quiet_NaN();
 
   90    const QImage::Format format = imageFormat( 
dataType );
 
  104  QgsDebugMsgLevel( QStringLiteral( 
"mWidth= %1 mHeight = %2 mDataType = %3 mData = %4 mImage = %5" ).arg( mWidth ).arg( mHeight ).arg( 
static_cast< int>( mDataType ) )
 
  105                    .arg( 
reinterpret_cast< quint64 
>( mData ) ).arg( 
reinterpret_cast< quint64 
>( mImage ) ), 4 );
 
 
  109QImage::Format QgsRasterBlock::imageFormat( 
Qgis::DataType dataType )
 
  113    return QImage::Format_ARGB32;
 
  117    return QImage::Format_ARGB32_Premultiplied;
 
  119  return QImage::Format_Invalid;
 
  124  if ( format == QImage::Format_ARGB32 )
 
  128  else if ( format == QImage::Format_ARGB32_Premultiplied )
 
  137  QgsDebugMsgLevel( QStringLiteral( 
"mWidth= %1 mHeight = %2 mDataType = %3 mData = %4 mImage = %5" ).arg( mWidth ).arg( mHeight ).arg( 
qgsEnumValueToKey( mDataType ) )
 
  138                    .arg( 
reinterpret_cast< quint64 
>( mData ) ).arg( 
reinterpret_cast< quint64 
>( mImage ) ), 4 );
 
  139  return mWidth == 0 || mHeight == 0 ||
 
 
  245      *
noDataValue = std::numeric_limits<double>::max() * -1.0;
 
  258  QgsDebugMsgLevel( QStringLiteral( 
"newDataType = %1 noDataValue = %2" ).arg( qgsEnumValueToKey< Qgis::DataType >( newDataType ) ).arg( *
noDataValue ), 4 );
 
 
  264  mHasNoDataValue = 
true;
 
 
  270  mHasNoDataValue = 
false;
 
  271  mNoDataValue = std::numeric_limits<double>::quiet_NaN();
 
 
  279    if ( mHasNoDataValue )
 
  281      return fill( mNoDataValue );
 
  286      if ( !mNoDataBitmap )
 
  288        if ( !createNoDataBitmap() )
 
  294      memset( mNoDataBitmap, 0xff, mNoDataBitmapSize );
 
  312    mImage->fill( NO_DATA_COLOR );
 
 
  319  int top = exceptRect.top();
 
  320  int bottom = exceptRect.bottom();
 
  321  int left = exceptRect.left();
 
  322  int right = exceptRect.right();
 
  323  top = std::min( std::max( top, 0 ), mHeight - 1 );
 
  324  left = std::min( std::max( left, 0 ), mWidth - 1 );
 
  325  bottom = std::max( 0, std::min( bottom, mHeight - 1 ) );
 
  326  right = std::max( 0, std::min( right, mWidth - 1 ) );
 
  332    if ( mHasNoDataValue )
 
  336        QgsDebugError( QStringLiteral( 
"Data block not allocated" ) );
 
  341      QByteArray noDataByteArray = 
valueBytes( mDataType, mNoDataValue );
 
  343      char *nodata = noDataByteArray.data();
 
  345      for ( 
int c = 0; 
c < mWidth; 
c++ )
 
  351      for ( 
int r = 0; r < mHeight; r++ )
 
  353        if ( r >= top && r <= bottom ) 
continue; 
 
  358      for ( 
int r = top; r <= bottom; r++ )
 
  365        const int w = mWidth - right - 1;
 
  373      if ( !mNoDataBitmap )
 
  375        if ( !createNoDataBitmap() )
 
  387      char *nodataRow = 
new char[mNoDataBitmapWidth]; 
 
  389      memset( nodataRow, 0, mNoDataBitmapWidth );
 
  390      for ( 
int c = 0; 
c < mWidth; 
c ++ )
 
  392        const int byte = 
c / 8;
 
  393        const int bit = 
c % 8;
 
  394        const char nodata = 0x80 >> bit;
 
  395        memset( nodataRow + 
byte, nodataRow[
byte] | nodata, 1 );
 
  399      for ( 
int r = 0; r < mHeight; r++ )
 
  401        if ( r >= top && r <= bottom ) 
continue; 
 
  402        const qgssize i = 
static_cast< qgssize >( r ) * mNoDataBitmapWidth;
 
  403        memcpy( mNoDataBitmap + i, nodataRow, mNoDataBitmapWidth );
 
  406      memset( nodataRow, 0, mNoDataBitmapWidth );
 
  407      for ( 
int c = 0; 
c < mWidth; 
c ++ )
 
  409        if ( 
c >= left && 
c <= right ) 
continue; 
 
  410        const int byte = 
c / 8;
 
  411        const int bit = 
c % 8;
 
  412        const char nodata = 0x80 >> bit;
 
  413        memset( nodataRow + 
byte, nodataRow[
byte] | nodata, 1 );
 
  415      for ( 
int r = top; r <= bottom; r++ )
 
  417        const qgssize i = 
static_cast< qgssize >( r ) * mNoDataBitmapWidth;
 
  418        memcpy( mNoDataBitmap + i, nodataRow, mNoDataBitmapWidth );
 
  433    if ( mImage->width() != mWidth ||  mImage->height() != mHeight )
 
  435      QgsDebugError( QStringLiteral( 
"Image and block size differ" ) );
 
  439    QgsDebugMsgLevel( QStringLiteral( 
"Fill image depth = %1" ).arg( mImage->depth() ), 4 );
 
  442    if ( mImage->depth() != 32 )
 
  444      QgsDebugError( QStringLiteral( 
"Unsupported image depth" ) );
 
  448    const QRgb nodataRgba = NO_DATA_COLOR;
 
  449    QRgb *nodataRow = 
new QRgb[mWidth]; 
 
  450    const int rgbSize = 
sizeof( QRgb );
 
  451    for ( 
int c = 0; 
c < mWidth; 
c ++ )
 
  453      nodataRow[
c] = nodataRgba;
 
  457    for ( 
int r = 0; r < mHeight; r++ )
 
  459      if ( r >= top && r <= bottom ) 
continue; 
 
  461      memcpy( 
reinterpret_cast< void * 
>( mImage->bits() + rgbSize * i ), nodataRow, rgbSize * 
static_cast< qgssize >( mWidth ) );
 
  464    for ( 
int r = top; r <= bottom; r++ )
 
  470        memcpy( 
reinterpret_cast< void * 
>( mImage->bits() + rgbSize * i ), nodataRow, rgbSize * 
static_cast< qgssize >( left - 1 ) );
 
  474      const int w = mWidth - right - 1;
 
  475      memcpy( 
reinterpret_cast< void * 
>( mImage->bits() + rgbSize * i ), nodataRow, rgbSize * 
static_cast< qgssize >( w ) );
 
 
  485  std::fill_n( 
static_cast<T *
>( data ), count, 
static_cast<T
>( value ) );
 
 
  493    QgsDebugError( QStringLiteral( 
"Cannot fill image block" ) );
 
  499    QgsDebugError( QStringLiteral( 
"Data block not allocated" ) );
 
  504  const std::size_t valueCount = 
static_cast<size_t>( mWidth ) * mHeight;
 
  505  const std::size_t totalSize = valueCount * 
dataTypeSize;
 
  512    memset( mData, 0, totalSize );
 
  519      fillTypedData<quint8>( 
value, mData, valueCount );
 
  522      fillTypedData<qint8>( 
value, mData, valueCount );
 
  525      fillTypedData<quint16>( 
value, mData, valueCount );
 
  528      fillTypedData<qint16>( 
value, mData, valueCount );
 
  531      fillTypedData<quint32>( 
value, mData, valueCount );
 
  534      fillTypedData<qint32>( 
value, mData, valueCount );
 
  537      fillTypedData<float>( 
value, mData, valueCount );
 
  540      fillTypedData<double>( 
value, mData, valueCount );
 
 
  560    return QByteArray::fromRawData( 
static_cast<const char *
>( mData ), 
typeSize( mDataType ) * mWidth * mHeight );
 
  561  else if ( mImage && mImage->constBits() )
 
  562    return QByteArray::fromRawData( 
reinterpret_cast<const char *
>( mImage->constBits() ), mImage->sizeInBytes() );
 
 
  574    const int len = std::min( 
static_cast<int>( 
data.size() ), 
typeSize( mDataType ) * mWidth * mHeight - offset );
 
  575    ::memcpy( 
static_cast<char *
>( mData ) + offset, 
data.constData(), len );
 
  577  else if ( mImage && mImage->constBits() )
 
  579    const qsizetype len = std::min( 
static_cast< qsizetype 
>( 
data.size() ), mImage->sizeInBytes() - offset );
 
  580    ::memcpy( mImage->bits() + offset, 
data.constData(), len );
 
 
  587  if ( index >= 
static_cast< qgssize >( mWidth )*mHeight )
 
  589    QgsDebugMsgLevel( QStringLiteral( 
"Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ), 4 );
 
  594    return reinterpret_cast< char * 
>( mData ) + index * mTypeSize;
 
  598    if ( uchar *
data = mImage->bits() )
 
  600      return reinterpret_cast< char * 
>( 
data + index * 4 );
 
 
  610  if ( index >= 
static_cast< qgssize >( mWidth )*mHeight )
 
  612    QgsDebugMsgLevel( QStringLiteral( 
"Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ), 4 );
 
  617    return reinterpret_cast< const char * 
>( mData ) + index * mTypeSize;
 
  621    if ( 
const uchar *
data = mImage->constBits() )
 
  623      return reinterpret_cast< const char * 
>( 
data + index * 4 );
 
 
  632  return bits( 
static_cast< qgssize >( row ) * mWidth + column );
 
 
  639    return reinterpret_cast< char * 
>( mData );
 
  643    if ( uchar *
data = mImage->bits() )
 
  645      return reinterpret_cast< char * 
>( 
data );
 
 
  656    return reinterpret_cast< const char * 
>( mData );
 
  660    if ( 
const uchar *
data = mImage->constBits() )
 
  662      return reinterpret_cast< const char * 
>( 
data );
 
 
  672  if ( destDataType == mDataType ) 
return true;
 
  676    void *
data = 
convert( mData, mDataType, destDataType, 
static_cast< qgssize >( mWidth ) * 
static_cast< qgssize >( mHeight ) );
 
  680      QgsDebugError( QStringLiteral( 
"Cannot convert raster block" ) );
 
  685    mDataType = destDataType;
 
  690    const QImage::Format format = imageFormat( destDataType );
 
  691    const QImage 
image = mImage->convertToFormat( format );
 
  693    mDataType = destDataType;
 
 
  708  if ( scale == 1.0 && offset == 0.0 ) 
return;
 
  711  for ( 
qgssize i = 0; i < size; ++i )
 
 
  719  if ( rangeList.isEmpty() )
 
  725  for ( 
qgssize i = 0; i < size; ++i )
 
  727    const double val = 
value( i );
 
 
  740    return QImage( *mImage );
 
 
  751  mImage = 
new QImage( *
image );
 
  752  mWidth = mImage->width();
 
  753  mHeight = mImage->height();
 
  754  mDataType = 
dataType( mImage->format() );
 
  756  mNoDataValue = std::numeric_limits<double>::quiet_NaN();
 
 
  786  for ( 
int i = 15; i <= 17; i++ )
 
  788    s.setNum( 
value, 
'g', i );
 
  789    const double doubleValue { s.toDouble( ) };
 
  794        return QLocale().toString( doubleValue, 
'g', i );
 
  800  QgsDebugError( QStringLiteral( 
"Cannot correctly parse printed value" ) );
 
 
  812  for ( 
int i = 6; i <= 9; i++ )
 
  814    s.setNum( 
value, 
'g', i );
 
  815    const float floatValue { s.toFloat() };
 
  820        return QLocale().toString( floatValue, 
'g', i );
 
  826  QgsDebugError( QStringLiteral( 
"Cannot correctly parse printed value" ) );
 
 
  832  const int destDataTypeSize = 
typeSize( destDataType );
 
  833  void *destData = 
qgsMalloc( destDataTypeSize * size );
 
  834  for ( 
qgssize i = 0; i < size; i++ )
 
  848  ba.resize( 
static_cast< int >( size ) );
 
  849  char *
data = ba.data();
 
  860      uc = 
static_cast< quint8 
>( 
value );
 
  861      memcpy( 
data, &uc, size );
 
  865      const qint8 myint8 = 
static_cast< qint8 
>( 
value );
 
  866      memcpy( 
data, &myint8, size );
 
  870      us = 
static_cast< quint16 
>( 
value );
 
  871      memcpy( 
data, &us, size );
 
  874      s = 
static_cast< qint16 
>( 
value );
 
  875      memcpy( 
data, &s, size );
 
  878      ui = 
static_cast< quint32 
>( 
value );
 
  879      memcpy( 
data, &ui, size );
 
  882      i = 
static_cast< qint32 
>( 
value );
 
  883      memcpy( 
data, &i, size );
 
  886      f = 
static_cast< float >( 
value );
 
  887      memcpy( 
data, &f, size );
 
  890      d = 
static_cast< double >( 
value );
 
  891      memcpy( 
data, &d, size );
 
  900      QgsDebugError( QStringLiteral( 
"Data type is not supported" ) );
 
 
  905bool QgsRasterBlock::createNoDataBitmap()
 
  907  mNoDataBitmapWidth = mWidth / 8 + 1;
 
  908  mNoDataBitmapSize = 
static_cast< qgssize >( mNoDataBitmapWidth ) * mHeight;
 
  909  QgsDebugMsgLevel( QStringLiteral( 
"allocate %1 bytes" ).arg( mNoDataBitmapSize ), 4 );
 
  910  mNoDataBitmap = 
reinterpret_cast< char * 
>( 
qgsMalloc( mNoDataBitmapSize ) );
 
  911  if ( !mNoDataBitmap )
 
  913    QgsDebugError( QStringLiteral( 
"Couldn't allocate no data memory of %1 bytes" ).arg( mNoDataBitmapSize ) );
 
  916  memset( mNoDataBitmap, 0, mNoDataBitmapSize );
 
  922  return QStringLiteral( 
"dataType = %1 width = %2 height = %3" )
 
  923         .arg( qgsEnumValueToKey< Qgis::DataType >( mDataType ) ).arg( mWidth ).arg( mHeight );
 
 
  933  QgsDebugMsgLevel( QStringLiteral( 
"theWidth = %1 height = %2 xRes = %3 yRes = %4" ).arg( 
width ).arg( 
height ).arg( xRes ).arg( yRes ), 4 );
 
  938  int right = 
width - 1;
 
  946    bottom = std::round( ( extent.
yMaximum() - subExtent.
yMinimum() ) / yRes ) - 1;
 
  955    right = std::round( ( subExtent.
xMaximum() - extent.
xMinimum() ) / xRes ) - 1;
 
  957  QRect 
subRect = QRect( left, top, right - left + 1, bottom - top + 1 );
 
 
  966    minimum = std::numeric_limits<double>::quiet_NaN();
 
  970  const std::size_t offset = qgis_gdal::min_element( mData, 
static_cast<std::size_t
>( mWidth ) * 
static_cast< std::size_t
>( mHeight ),
 
  973  row = 
static_cast< int >( offset / mWidth );
 
  974  column = 
static_cast< int >( offset % mWidth );
 
 
  984    maximum = std::numeric_limits<double>::quiet_NaN();
 
  987  const std::size_t offset = qgis_gdal::max_element( mData, 
static_cast<std::size_t
>( mWidth ) * 
static_cast< std::size_t
>( mHeight ),
 
  990  row = 
static_cast< int >( offset / mWidth );
 
  991  column = 
static_cast< int >( offset % mWidth );
 
 
 1001    minimum = std::numeric_limits<double>::quiet_NaN();
 
 1002    maximum = std::numeric_limits<double>::quiet_NaN();
 
 1006  const auto [minOffset, maxOffset] = qgis_gdal::minmax_element( mData, 
static_cast<std::size_t
>( mWidth ) * 
static_cast< std::size_t
>( mHeight ),
 
 1009  minimumRow = 
static_cast< int >( minOffset / mWidth );
 
 1010  minimumColumn = 
static_cast< int >( minOffset % mWidth );
 
 1013  maximumRow = 
static_cast< int >( maxOffset / mWidth );
 
 1014  maximumColumn = 
static_cast< int >( maxOffset % mWidth );
 
 
DataType
Raster data types.
 
@ Float32
Thirty two bit floating point (float)
 
@ CFloat64
Complex Float64.
 
@ Int16
Sixteen bit signed integer (qint16)
 
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
 
@ Int8
Eight bit signed integer (qint8) (added in QGIS 3.30)
 
@ UInt16
Sixteen bit unsigned integer (quint16)
 
@ Byte
Eight bit unsigned integer (quint8)
 
@ UnknownDataType
Unknown or unspecified type.
 
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
 
@ Int32
Thirty two bit signed integer (qint32)
 
@ Float64
Sixty four bit floating point (double)
 
@ CFloat32
Complex Float32.
 
@ UInt32
Thirty two bit unsigned integer (quint32)
 
static GDALDataType gdalDataTypeFromQgisDataType(Qgis::DataType dataType)
Returns the GDAL data type corresponding to the QGIS data type dataType.
 
bool isEmpty() const
Returns true if block is empty, i.e.
 
double value(int row, int column) const
Read a single value if type of block is numeric.
 
static bool typeIsNumeric(Qgis::DataType type)
Returns true if a data type is numeric.
 
bool minimumMaximum(double &minimum, int &minimumRow, int &minimumColumn, double &maximum, int &maximumRow, int &maximumColumn) const
Returns the minimum and maximum value present in the raster block.
 
int height() const
Returns the height (number of rows) of the raster block.
 
bool convert(Qgis::DataType destDataType)
Convert data to different type.
 
bool setIsNoData()
Set the whole block to no data.
 
bool maximum(double &maximum, int &row, int &column) const
Returns the maximum value present in the raster block.
 
void resetNoDataValue()
Reset no data value: if there was a no data value previously set, it will be discarded.
 
int dataTypeSize() const
Data type size in bytes.
 
static int typeSize(Qgis::DataType dataType)
Returns the size in bytes for the specified dataType.
 
bool setIsNoDataExcept(QRect exceptRect)
Set the whole block to no data except specified rectangle.
 
bool setImage(const QImage *image)
Sets the block data via an image.
 
QByteArray data() const
Gets access to raw data.
 
double noDataValue() const
Returns no data value.
 
const char * constBits() const
Returns a const pointer to block data.
 
void setData(const QByteArray &data, int offset=0)
Rewrite raw pixel data.
 
void applyNoDataValues(const QgsRasterRangeList &rangeList)
 
bool setValue(int row, int column, double value)
Set value on position.
 
bool isNoData(int row, int column) const
Checks if value at position is no data.
 
Qgis::DataType dataType() const
Returns data type.
 
char * bits()
Returns a pointer to block data.
 
static Qgis::DataType typeWithNoDataValue(Qgis::DataType dataType, double *noDataValue)
For given data type returns wider type and sets no data value.
 
QImage image() const
Returns an image containing the block data, if the block's data type is color.
 
void setNoDataValue(double noDataValue)
Sets cell value that will be considered as "no data".
 
static bool typeIsComplex(Qgis::DataType type)
Returns true if a data type is a complex number type.
 
bool fill(double value)
Fills the whole block with a constant value.
 
virtual ~QgsRasterBlock()
 
static void writeValue(void *data, Qgis::DataType type, qgssize index, double value)
 
int width() const
Returns the width (number of columns) of the raster block.
 
void applyScaleOffset(double scale, double offset)
Apply band scale and offset to raster block values.
 
static QString printValue(double value, bool localized=false)
Print double value with all necessary significant digits.
 
static QRect subRect(const QgsRectangle &extent, int width, int height, const QgsRectangle &subExtent)
For extent and width, height find rectangle covered by subextent.
 
static bool typeIsColor(Qgis::DataType type)
Returns true if a data type is a color type.
 
static double readValue(void *data, Qgis::DataType type, qgssize index)
 
static QByteArray valueBytes(Qgis::DataType dataType, double value)
Gets byte array representing a value.
 
bool reset(Qgis::DataType dataType, int width, int height)
Reset block.
 
bool minimum(double &minimum, int &row, int &column) const
Returns the minimum value present in the raster block.
 
bool contains(double value) const
Returns true if this range contains the specified value.
 
A rectangle specified with double values.
 
Q_INVOKABLE QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
 
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
 
void * qgsMalloc(size_t size)
Allocates size bytes and returns a pointer to the allocated memory.
 
void qgsFree(void *ptr)
Frees the memory space pointed to by ptr.
 
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 qgsFloatNear(float a, float b, float epsilon=4 *FLT_EPSILON)
Compare two floats (but allow some difference)
 
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
 
#define QgsDebugMsgLevel(str, level)
 
#define QgsDebugError(str)
 
void fillTypedData(double value, void *data, std::size_t count)
 
QList< QgsRasterRange > QgsRasterRangeList