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