1 /***************************************************************************
2     qgsrasterblock.cpp - Class representing a block of raster data
3      --------------------------------------
4     Date                 : Oct 9, 2012
5     Copyright            : (C) 2012 by Radim Blazek
6     email                : radim dot blazek at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include <limits>
19 
20 #include <QByteArray>
21 #include <QColor>
22 
23 #include "qgslogger.h"
24 #include "qgsrasterblock.h"
25 #include "qgsrectangle.h"
26 
27 // See #9101 before any change of NODATA_COLOR!
28 const QRgb QgsRasterBlock::NO_DATA_COLOR = qRgba( 0, 0, 0, 0 );
29 
QgsRasterBlock()30 QgsRasterBlock::QgsRasterBlock()
31   : mNoDataValue( std::numeric_limits<double>::quiet_NaN() )
32 {
33 }
34 
QgsRasterBlock(Qgis::DataType dataType,int width,int height)35 QgsRasterBlock::QgsRasterBlock( Qgis::DataType dataType, int width, int height )
36   : mDataType( dataType )
37   , mWidth( width )
38   , mHeight( height )
39   , mNoDataValue( std::numeric_limits<double>::quiet_NaN() )
40 {
41   ( void )reset( mDataType, mWidth, mHeight );
42 }
43 
~QgsRasterBlock()44 QgsRasterBlock::~QgsRasterBlock()
45 {
46   QgsDebugMsgLevel( QStringLiteral( "mData = %1" ).arg( reinterpret_cast< quint64 >( mData ) ), 4 );
47   qgsFree( mData );
48   delete mImage;
49   qgsFree( mNoDataBitmap );
50 }
51 
reset(Qgis::DataType dataType,int width,int height)52 bool QgsRasterBlock::reset( Qgis::DataType dataType, int width, int height )
53 {
54   QgsDebugMsgLevel( QStringLiteral( "theWidth= %1 height = %2 dataType = %3" ).arg( width ).arg( height ).arg( dataType ), 4 );
55 
56   qgsFree( mData );
57   mData = nullptr;
58   delete mImage;
59   mImage = nullptr;
60   qgsFree( mNoDataBitmap );
61   mNoDataBitmap = nullptr;
62   mDataType = Qgis::UnknownDataType;
63   mTypeSize = 0;
64   mWidth = 0;
65   mHeight = 0;
66   mHasNoDataValue = false;
67   mNoDataValue = std::numeric_limits<double>::quiet_NaN();
68   mValid = false;
69 
70   if ( typeIsNumeric( dataType ) )
71   {
72     QgsDebugMsgLevel( QStringLiteral( "Numeric type" ), 4 );
73     qgssize tSize = typeSize( dataType );
74     QgsDebugMsgLevel( QStringLiteral( "allocate %1 bytes" ).arg( tSize * width * height ), 4 );
75     mData = qgsMalloc( tSize * width * height );
76     if ( !mData )
77     {
78       QgsDebugMsg( QStringLiteral( "Couldn't allocate data memory of %1 bytes" ).arg( tSize * width * height ) );
79       return false;
80     }
81   }
82   else if ( typeIsColor( dataType ) )
83   {
84     QgsDebugMsgLevel( QStringLiteral( "Color type" ), 4 );
85     QImage::Format format = imageFormat( dataType );
86     mImage = new QImage( width, height, format );
87   }
88   else
89   {
90     QgsDebugMsg( QStringLiteral( "Wrong data type" ) );
91     return false;
92   }
93 
94   mValid = true;
95   mDataType = dataType;
96   mTypeSize = QgsRasterBlock::typeSize( mDataType );
97   mWidth = width;
98   mHeight = height;
99   QgsDebugMsgLevel( QStringLiteral( "mWidth= %1 mHeight = %2 mDataType = %3 mData = %4 mImage = %5" ).arg( mWidth ).arg( mHeight ).arg( mDataType )
100                     .arg( reinterpret_cast< quint64 >( mData ) ).arg( reinterpret_cast< quint64 >( mImage ) ), 4 );
101   return true;
102 }
103 
imageFormat(Qgis::DataType dataType)104 QImage::Format QgsRasterBlock::imageFormat( Qgis::DataType dataType )
105 {
106   if ( dataType == Qgis::ARGB32 )
107   {
108     return QImage::Format_ARGB32;
109   }
110   else if ( dataType == Qgis::ARGB32_Premultiplied )
111   {
112     return QImage::Format_ARGB32_Premultiplied;
113   }
114   return QImage::Format_Invalid;
115 }
116 
dataType(QImage::Format format)117 Qgis::DataType QgsRasterBlock::dataType( QImage::Format format )
118 {
119   if ( format == QImage::Format_ARGB32 )
120   {
121     return Qgis::ARGB32;
122   }
123   else if ( format == QImage::Format_ARGB32_Premultiplied )
124   {
125     return Qgis::ARGB32_Premultiplied;
126   }
127   return Qgis::UnknownDataType;
128 }
129 
isEmpty() const130 bool QgsRasterBlock::isEmpty() const
131 {
132   QgsDebugMsgLevel( QStringLiteral( "mWidth= %1 mHeight = %2 mDataType = %3 mData = %4 mImage = %5" ).arg( mWidth ).arg( mHeight ).arg( mDataType )
133                     .arg( reinterpret_cast< quint64 >( mData ) ).arg( reinterpret_cast< quint64 >( mImage ) ), 4 );
134   return mWidth == 0 || mHeight == 0 ||
135          ( typeIsNumeric( mDataType ) && !mData ) ||
136          ( typeIsColor( mDataType ) && !mImage );
137 }
138 
typeIsNumeric(Qgis::DataType dataType)139 bool QgsRasterBlock::typeIsNumeric( Qgis::DataType dataType )
140 {
141   switch ( dataType )
142   {
143     case Qgis::Byte:
144     case Qgis::UInt16:
145     case Qgis::Int16:
146     case Qgis::UInt32:
147     case Qgis::Int32:
148     case Qgis::Float32:
149     case Qgis::CInt16:
150     case Qgis::Float64:
151     case Qgis::CInt32:
152     case Qgis::CFloat32:
153     case Qgis::CFloat64:
154       return true;
155 
156     case Qgis::UnknownDataType:
157     case Qgis::ARGB32:
158     case Qgis::ARGB32_Premultiplied:
159       return false;
160   }
161   return false;
162 }
163 
typeIsColor(Qgis::DataType dataType)164 bool QgsRasterBlock::typeIsColor( Qgis::DataType dataType )
165 {
166   switch ( dataType )
167   {
168     case Qgis::ARGB32:
169     case Qgis::ARGB32_Premultiplied:
170       return true;
171 
172     case Qgis::UnknownDataType:
173     case Qgis::Byte:
174     case Qgis::UInt16:
175     case Qgis::Int16:
176     case Qgis::UInt32:
177     case Qgis::Int32:
178     case Qgis::Float32:
179     case Qgis::CInt16:
180     case Qgis::Float64:
181     case Qgis::CInt32:
182     case Qgis::CFloat32:
183     case Qgis::CFloat64:
184       return false;
185   }
186   return false;
187 }
188 
typeWithNoDataValue(Qgis::DataType dataType,double * noDataValue)189 Qgis::DataType QgsRasterBlock::typeWithNoDataValue( Qgis::DataType dataType, double *noDataValue )
190 {
191   Qgis::DataType newDataType;
192 
193   switch ( dataType )
194   {
195     case Qgis::Byte:
196       *noDataValue = -32768.0;
197       newDataType = Qgis::Int16;
198       break;
199     case Qgis::Int16:
200       *noDataValue = -2147483648.0;
201       newDataType = Qgis::Int32;
202       break;
203     case Qgis::UInt16:
204       *noDataValue = -2147483648.0;
205       newDataType = Qgis::Int32;
206       break;
207     case Qgis::UInt32:
208     case Qgis::Int32:
209     case Qgis::Float32:
210     case Qgis::Float64:
211       *noDataValue = std::numeric_limits<double>::max() * -1.0;
212       newDataType = Qgis::Float64;
213       break;
214     default:
215       QgsDebugMsg( QStringLiteral( "Unknown data type %1" ).arg( dataType ) );
216       return Qgis::UnknownDataType;
217   }
218   QgsDebugMsgLevel( QStringLiteral( "newDataType = %1 noDataValue = %2" ).arg( newDataType ).arg( *noDataValue ), 4 );
219   return newDataType;
220 }
221 
setNoDataValue(double noDataValue)222 void QgsRasterBlock::setNoDataValue( double noDataValue )
223 {
224   mHasNoDataValue = true;
225   mNoDataValue = noDataValue;
226 }
227 
resetNoDataValue()228 void QgsRasterBlock::resetNoDataValue()
229 {
230   mHasNoDataValue = false;
231   mNoDataValue = std::numeric_limits<double>::quiet_NaN();
232 }
233 
setIsNoData()234 bool QgsRasterBlock::setIsNoData()
235 {
236   QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
237   if ( typeIsNumeric( mDataType ) )
238   {
239     const size_t dataTypeSize = typeSize( mDataType );
240     if ( mHasNoDataValue )
241     {
242       if ( !mData )
243       {
244         QgsDebugMsg( QStringLiteral( "Data block not allocated" ) );
245         return false;
246       }
247 
248       QgsDebugMsgLevel( QStringLiteral( "set mData to mNoDataValue" ), 4 );
249       QByteArray noDataByteArray = valueBytes( mDataType, mNoDataValue );
250       if ( mNoDataValue == 0 )
251       {
252         memset( mData, 0, dataTypeSize * mWidth * mHeight );
253       }
254       else
255       {
256         const char *nodata = noDataByteArray.data();
257         for ( qgssize i = 0; i < static_cast< qgssize >( mWidth )*mHeight; i++ )
258         {
259           memcpy( reinterpret_cast< char * >( mData ) + i * dataTypeSize, nodata, dataTypeSize );
260         }
261       }
262     }
263     else
264     {
265       // use bitmap
266       if ( !mNoDataBitmap )
267       {
268         if ( !createNoDataBitmap() )
269         {
270           return false;
271         }
272       }
273       QgsDebugMsgLevel( QStringLiteral( "set mNoDataBitmap to 1" ), 4 );
274       memset( mNoDataBitmap, 0xff, mNoDataBitmapSize );
275       if ( mData )
276       {
277         memset( mData, 0, dataTypeSize * mWidth * mHeight );
278       }
279     }
280     return true;
281   }
282   else
283   {
284     // image
285     if ( !mImage )
286     {
287       QgsDebugMsg( QStringLiteral( "Image not allocated" ) );
288       return false;
289     }
290     QgsDebugMsgLevel( QStringLiteral( "Fill image" ), 4 );
291     mImage->fill( NO_DATA_COLOR );
292     return true;
293   }
294 }
295 
setIsNoDataExcept(QRect exceptRect)296 bool QgsRasterBlock::setIsNoDataExcept( QRect exceptRect )
297 {
298   int top = exceptRect.top();
299   int bottom = exceptRect.bottom();
300   int left = exceptRect.left();
301   int right = exceptRect.right();
302   top = std::min( std::max( top, 0 ), mHeight - 1 );
303   left = std::min( std::max( left, 0 ), mWidth - 1 );
304   bottom = std::max( 0, std::min( bottom, mHeight - 1 ) );
305   right = std::max( 0, std::min( right, mWidth - 1 ) );
306 
307   QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
308   if ( typeIsNumeric( mDataType ) )
309   {
310     const size_t dataTypeSize = typeSize( mDataType );
311     if ( mHasNoDataValue )
312     {
313       if ( !mData )
314       {
315         QgsDebugMsg( QStringLiteral( "Data block not allocated" ) );
316         return false;
317       }
318 
319       QgsDebugMsgLevel( QStringLiteral( "set mData to mNoDataValue" ), 4 );
320       QByteArray noDataByteArray = valueBytes( mDataType, mNoDataValue );
321 
322       char *nodata = noDataByteArray.data();
323       char *nodataRow = new char[mWidth * dataTypeSize]; // full row of no data
324       for ( int c = 0; c < mWidth; c++ )
325       {
326         memcpy( nodataRow + c * dataTypeSize, nodata, dataTypeSize );
327       }
328 
329       // top and bottom
330       for ( int r = 0; r < mHeight; r++ )
331       {
332         if ( r >= top && r <= bottom ) continue; // middle
333         qgssize i = static_cast< qgssize >( r ) * mWidth;
334         memcpy( reinterpret_cast< char * >( mData ) + i * dataTypeSize, nodataRow, dataTypeSize * static_cast< qgssize >( mWidth ) );
335       }
336       // middle
337       for ( int r = top; r <= bottom; r++ )
338       {
339         qgssize i = static_cast< qgssize >( r ) * mWidth;
340         // middle left
341         memcpy( reinterpret_cast< char * >( mData ) + i * dataTypeSize, nodataRow, dataTypeSize * static_cast< qgssize >( left ) );
342         // middle right
343         i += right + 1;
344         int w = mWidth - right - 1;
345         memcpy( reinterpret_cast< char * >( mData ) + i * dataTypeSize, nodataRow, dataTypeSize * static_cast< qgssize >( w ) );
346       }
347       delete [] nodataRow;
348     }
349     else
350     {
351       // use bitmap
352       if ( !mNoDataBitmap )
353       {
354         if ( !createNoDataBitmap() )
355         {
356           return false;
357         }
358       }
359       QgsDebugMsgLevel( QStringLiteral( "set mNoDataBitmap to 1" ), 4 );
360 
361       if ( mData )
362       {
363         memset( mData, 0, dataTypeSize * mWidth * mHeight );
364       }
365 
366       char *nodataRow = new char[mNoDataBitmapWidth]; // full row of no data
367       // TODO: we can simply set all bytes to 11111111 (~0) I think
368       memset( nodataRow, 0, mNoDataBitmapWidth );
369       for ( int c = 0; c < mWidth; c ++ )
370       {
371         int byte = c / 8;
372         int bit = c % 8;
373         char nodata = 0x80 >> bit;
374         memset( nodataRow + byte, nodataRow[byte] | nodata, 1 );
375       }
376 
377       // top and bottom
378       for ( int r = 0; r < mHeight; r++ )
379       {
380         if ( r >= top && r <= bottom ) continue; // middle
381         qgssize i = static_cast< qgssize >( r ) * mNoDataBitmapWidth;
382         memcpy( mNoDataBitmap + i, nodataRow, mNoDataBitmapWidth );
383       }
384       // middle
385       memset( nodataRow, 0, mNoDataBitmapWidth );
386       for ( int c = 0; c < mWidth; c ++ )
387       {
388         if ( c >= left && c <= right ) continue; // middle
389         int byte = c / 8;
390         int bit = c % 8;
391         char nodata = 0x80 >> bit;
392         memset( nodataRow + byte, nodataRow[byte] | nodata, 1 );
393       }
394       for ( int r = top; r <= bottom; r++ )
395       {
396         qgssize i = static_cast< qgssize >( r ) * mNoDataBitmapWidth;
397         memcpy( mNoDataBitmap + i, nodataRow, mNoDataBitmapWidth );
398       }
399       delete [] nodataRow;
400     }
401     return true;
402   }
403   else
404   {
405     // image
406     if ( !mImage )
407     {
408       QgsDebugMsg( QStringLiteral( "Image not allocated" ) );
409       return false;
410     }
411 
412     if ( mImage->width() != mWidth ||  mImage->height() != mHeight )
413     {
414       QgsDebugMsg( QStringLiteral( "Image and block size differ" ) );
415       return false;
416     }
417 
418     QgsDebugMsgLevel( QStringLiteral( "Fill image depth = %1" ).arg( mImage->depth() ), 4 );
419 
420     // TODO: support different depths
421     if ( mImage->depth() != 32 )
422     {
423       QgsDebugMsg( QStringLiteral( "Unsupported image depth" ) );
424       return false;
425     }
426 
427     QRgb nodataRgba = NO_DATA_COLOR;
428     QRgb *nodataRow = new QRgb[mWidth]; // full row of no data
429     int rgbSize = sizeof( QRgb );
430     for ( int c = 0; c < mWidth; c ++ )
431     {
432       nodataRow[c] = nodataRgba;
433     }
434 
435     // top and bottom
436     for ( int r = 0; r < mHeight; r++ )
437     {
438       if ( r >= top && r <= bottom ) continue; // middle
439       qgssize i = static_cast< qgssize >( r ) * mWidth;
440       memcpy( reinterpret_cast< void * >( mImage->bits() + rgbSize * i ), nodataRow, rgbSize * static_cast< qgssize >( mWidth ) );
441     }
442     // middle
443     for ( int r = top; r <= bottom; r++ )
444     {
445       qgssize i = static_cast< qgssize >( r ) * mWidth;
446       // middle left
447       if ( left > 0 )
448       {
449         memcpy( reinterpret_cast< void * >( mImage->bits() + rgbSize * i ), nodataRow, rgbSize * static_cast< qgssize >( left - 1 ) );
450       }
451       // middle right
452       i += right + 1;
453       int w = mWidth - right - 1;
454       memcpy( reinterpret_cast< void * >( mImage->bits() + rgbSize * i ), nodataRow, rgbSize * static_cast< qgssize >( w ) );
455     }
456     delete [] nodataRow;
457     return true;
458   }
459 }
460 
data() const461 QByteArray QgsRasterBlock::data() const
462 {
463   if ( mData )
464     return QByteArray::fromRawData( static_cast<const char *>( mData ), typeSize( mDataType ) * mWidth * mHeight );
465   else if ( mImage && mImage->constBits() )
466 #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
467     return QByteArray::fromRawData( reinterpret_cast<const char *>( mImage->constBits() ), mImage->byteCount() );
468 #else
469     return QByteArray::fromRawData( reinterpret_cast<const char *>( mImage->constBits() ), mImage->sizeInBytes() );
470 #endif
471   else
472     return QByteArray();
473 }
474 
setData(const QByteArray & data,int offset)475 void QgsRasterBlock::setData( const QByteArray &data, int offset )
476 {
477   if ( offset < 0 )
478     return;  // negative offsets not allowed
479 
480   if ( mData )
481   {
482     int len = std::min( data.size(), typeSize( mDataType ) * mWidth * mHeight - offset );
483     ::memcpy( static_cast<char *>( mData ) + offset, data.constData(), len );
484   }
485   else if ( mImage && mImage->constBits() )
486   {
487 #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
488     int len = std::min( data.size(), mImage->byteCount() - offset );
489 #else
490     qsizetype len = std::min( static_cast< qsizetype >( data.size() ), mImage->sizeInBytes() - offset );
491 #endif
492     ::memcpy( mImage->bits() + offset, data.constData(), len );
493   }
494 }
495 
bits(qgssize index)496 char *QgsRasterBlock::bits( qgssize index )
497 {
498   // Not testing type to avoid too much overhead because this method is called per pixel
499   if ( index >= static_cast< qgssize >( mWidth )*mHeight )
500   {
501     QgsDebugMsgLevel( QStringLiteral( "Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ), 4 );
502     return nullptr;
503   }
504   if ( mData )
505   {
506     return reinterpret_cast< char * >( mData ) + index * mTypeSize;
507   }
508   if ( mImage && mImage->bits() )
509   {
510     return reinterpret_cast< char * >( mImage->bits() + index * 4 );
511   }
512 
513   return nullptr;
514 }
515 
bits(int row,int column)516 char *QgsRasterBlock::bits( int row, int column )
517 {
518   return bits( static_cast< qgssize >( row ) * mWidth + column );
519 }
520 
bits()521 char *QgsRasterBlock::bits()
522 {
523   if ( mData )
524   {
525     return reinterpret_cast< char * >( mData );
526   }
527   if ( mImage && mImage->bits() )
528   {
529     return reinterpret_cast< char * >( mImage->bits() );
530   }
531 
532   return nullptr;
533 }
534 
convert(Qgis::DataType destDataType)535 bool QgsRasterBlock::convert( Qgis::DataType destDataType )
536 {
537   if ( isEmpty() ) return false;
538   if ( destDataType == mDataType ) return true;
539 
540   if ( typeIsNumeric( mDataType ) && typeIsNumeric( destDataType ) )
541   {
542     void *data = convert( mData, mDataType, destDataType, static_cast< qgssize >( mWidth ) * static_cast< qgssize >( mHeight ) );
543 
544     if ( !data )
545     {
546       QgsDebugMsg( QStringLiteral( "Cannot convert raster block" ) );
547       return false;
548     }
549     qgsFree( mData );
550     mData = data;
551     mDataType = destDataType;
552     mTypeSize = typeSize( mDataType );
553   }
554   else if ( typeIsColor( mDataType ) && typeIsColor( destDataType ) )
555   {
556     QImage::Format format = imageFormat( destDataType );
557     QImage image = mImage->convertToFormat( format );
558     *mImage = image;
559     mDataType = destDataType;
560     mTypeSize = typeSize( mDataType );
561   }
562   else
563   {
564     return false;
565   }
566 
567   return true;
568 }
569 
applyScaleOffset(double scale,double offset)570 void QgsRasterBlock::applyScaleOffset( double scale, double offset )
571 {
572   if ( isEmpty() ) return;
573   if ( !typeIsNumeric( mDataType ) ) return;
574   if ( scale == 1.0 && offset == 0.0 ) return;
575 
576   qgssize size = static_cast< qgssize >( mWidth ) * mHeight;
577   for ( qgssize i = 0; i < size; ++i )
578   {
579     if ( !isNoData( i ) ) setValue( i, value( i ) * scale + offset );
580   }
581 }
582 
applyNoDataValues(const QgsRasterRangeList & rangeList)583 void QgsRasterBlock::applyNoDataValues( const QgsRasterRangeList &rangeList )
584 {
585   if ( rangeList.isEmpty() )
586   {
587     return;
588   }
589 
590   qgssize size = static_cast< qgssize >( mWidth ) * static_cast< qgssize >( mHeight );
591   for ( qgssize i = 0; i < size; ++i )
592   {
593     double val = value( i );
594     if ( QgsRasterRange::contains( val, rangeList ) )
595     {
596       //setValue( i, mNoDataValue );
597       setIsNoData( i );
598     }
599   }
600 }
601 
image() const602 QImage QgsRasterBlock::image() const
603 {
604   if ( mImage )
605   {
606     return QImage( *mImage );
607   }
608   return QImage();
609 }
610 
setImage(const QImage * image)611 bool QgsRasterBlock::setImage( const QImage *image )
612 {
613   qgsFree( mData );
614   mData = nullptr;
615   delete mImage;
616   mImage = nullptr;
617   mImage = new QImage( *image );
618   mWidth = mImage->width();
619   mHeight = mImage->height();
620   mDataType = dataType( mImage->format() );
621   mTypeSize = QgsRasterBlock::typeSize( mDataType );
622   mNoDataValue = std::numeric_limits<double>::quiet_NaN();
623   return true;
624 }
625 
printValue(double value)626 QString QgsRasterBlock::printValue( double value )
627 {
628   /*
629    *  IEEE 754 double has 15-17 significant digits. It specifies:
630    *
631    * "If a decimal string with at most 15 significant decimal is converted to
632    *  IEEE 754 double precision and then converted back to the same number of
633    *  significant decimal, then the final string should match the original;
634    *  and if an IEEE 754 double precision is converted to a decimal string with at
635    *  least 17 significant decimal and then converted back to double, then the final
636    *  number must match the original."
637    *
638    * If printing only 15 digits, some precision could be lost. Printing 17 digits may
639    * add some confusing digits.
640    *
641    * Default 'g' precision on linux is 6 digits, not all significant digits like
642    * some sprintf manuals say.
643    *
644    * We need to ensure that the number printed and used in QLineEdit or XML will
645    * give the same number when parsed.
646    *
647    * Is there a better solution?
648    */
649 
650   QString s;
651 
652   for ( int i = 15; i <= 17; i++ )
653   {
654     s.setNum( value, 'g', i );
655     if ( qgsDoubleNear( s.toDouble(), value ) )
656     {
657       return s;
658     }
659   }
660   // Should not happen
661   QgsDebugMsg( QStringLiteral( "Cannot correctly parse printed value" ) );
662   return s;
663 }
664 
printValue(float value)665 QString QgsRasterBlock::printValue( float value )
666 {
667   /*
668    *  IEEE 754 double has 6-9 significant digits. See printValue(double)
669    */
670 
671   QString s;
672 
673   for ( int i = 6; i <= 9; i++ )
674   {
675     s.setNum( value, 'g', i );
676     if ( qgsFloatNear( s.toFloat(), value ) )
677     {
678       return s;
679     }
680   }
681   // Should not happen
682   QgsDebugMsg( QStringLiteral( "Cannot correctly parse printed value" ) );
683   return s;
684 }
685 
convert(void * srcData,Qgis::DataType srcDataType,Qgis::DataType destDataType,qgssize size)686 void *QgsRasterBlock::convert( void *srcData, Qgis::DataType srcDataType, Qgis::DataType destDataType, qgssize size )
687 {
688   int destDataTypeSize = typeSize( destDataType );
689   void *destData = qgsMalloc( destDataTypeSize * size );
690   for ( qgssize i = 0; i < size; i++ )
691   {
692     double value = readValue( srcData, srcDataType, i );
693     writeValue( destData, destDataType, i, value );
694     //double newValue = readValue( destData, destDataType, i );
695     //QgsDebugMsg( QStringLiteral("convert %1 type %2 to %3: %4 -> %5").arg(i).arg(srcDataType).arg(destDataType).arg( value ).arg( newValue ) );
696   }
697   return destData;
698 }
699 
valueBytes(Qgis::DataType dataType,double value)700 QByteArray QgsRasterBlock::valueBytes( Qgis::DataType dataType, double value )
701 {
702   qgssize size = QgsRasterBlock::typeSize( dataType );
703   QByteArray ba;
704   ba.resize( static_cast< int >( size ) );
705   char *data = ba.data();
706   quint8 uc;
707   quint16 us;
708   qint16 s;
709   quint32 ui;
710   qint32 i;
711   float f;
712   double d;
713   switch ( dataType )
714   {
715     case Qgis::Byte:
716       uc = static_cast< quint8 >( value );
717       memcpy( data, &uc, size );
718       break;
719     case Qgis::UInt16:
720       us = static_cast< quint16 >( value );
721       memcpy( data, &us, size );
722       break;
723     case Qgis::Int16:
724       s = static_cast< qint16 >( value );
725       memcpy( data, &s, size );
726       break;
727     case Qgis::UInt32:
728       ui = static_cast< quint32 >( value );
729       memcpy( data, &ui, size );
730       break;
731     case Qgis::Int32:
732       i = static_cast< qint32 >( value );
733       memcpy( data, &i, size );
734       break;
735     case Qgis::Float32:
736       f = static_cast< float >( value );
737       memcpy( data, &f, size );
738       break;
739     case Qgis::Float64:
740       d = static_cast< double >( value );
741       memcpy( data, &d, size );
742       break;
743     default:
744       QgsDebugMsg( QStringLiteral( "Data type is not supported" ) );
745   }
746   return ba;
747 }
748 
createNoDataBitmap()749 bool QgsRasterBlock::createNoDataBitmap()
750 {
751   mNoDataBitmapWidth = mWidth / 8 + 1;
752   mNoDataBitmapSize = static_cast< qgssize >( mNoDataBitmapWidth ) * mHeight;
753   QgsDebugMsgLevel( QStringLiteral( "allocate %1 bytes" ).arg( mNoDataBitmapSize ), 4 );
754   mNoDataBitmap = reinterpret_cast< char * >( qgsMalloc( mNoDataBitmapSize ) );
755   if ( !mNoDataBitmap )
756   {
757     QgsDebugMsg( QStringLiteral( "Couldn't allocate no data memory of %1 bytes" ).arg( mNoDataBitmapSize ) );
758     return false;
759   }
760   memset( mNoDataBitmap, 0, mNoDataBitmapSize );
761   return true;
762 }
763 
toString() const764 QString  QgsRasterBlock::toString() const
765 {
766   return QStringLiteral( "dataType = %1 width = %2 height = %3" )
767          .arg( mDataType ).arg( mWidth ).arg( mHeight );
768 }
769 
subRect(const QgsRectangle & extent,int width,int height,const QgsRectangle & subExtent)770 QRect QgsRasterBlock::subRect( const QgsRectangle &extent, int width, int height, const QgsRectangle   &subExtent )
771 {
772   QgsDebugMsgLevel( "theExtent = " + extent.toString(), 4 );
773   QgsDebugMsgLevel( "theSubExtent = " + subExtent.toString(), 4 );
774   double xRes = extent.width() / width;
775   double yRes = extent.height() / height;
776 
777   QgsDebugMsgLevel( QStringLiteral( "theWidth = %1 height = %2 xRes = %3 yRes = %4" ).arg( width ).arg( height ).arg( xRes ).arg( yRes ), 4 );
778 
779   int top = 0;
780   int bottom = height - 1;
781   int left = 0;
782   int right = width - 1;
783 
784   if ( subExtent.yMaximum() < extent.yMaximum() )
785   {
786     top = std::round( ( extent.yMaximum() - subExtent.yMaximum() ) / yRes );
787   }
788   if ( subExtent.yMinimum() > extent.yMinimum() )
789   {
790     bottom = std::round( ( extent.yMaximum() - subExtent.yMinimum() ) / yRes ) - 1;
791   }
792 
793   if ( subExtent.xMinimum() > extent.xMinimum() )
794   {
795     left = std::round( ( subExtent.xMinimum() - extent.xMinimum() ) / xRes );
796   }
797   if ( subExtent.xMaximum() < extent.xMaximum() )
798   {
799     right = std::round( ( subExtent.xMaximum() - extent.xMinimum() ) / xRes ) - 1;
800   }
801   QRect subRect = QRect( left, top, right - left + 1, bottom - top + 1 );
802   QgsDebugMsgLevel( QStringLiteral( "subRect: %1 %2 %3 %4" ).arg( subRect.x() ).arg( subRect.y() ).arg( subRect.width() ).arg( subRect.height() ), 4 );
803   return subRect;
804 }
805