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