40 , mLightAngle( lightAngle )
41 , mLightAzimuth( lightAzimuth )
63 int band = elem.attribute( QStringLiteral(
"band" ), QStringLiteral(
"0" ) ).toInt();
64 double azimuth = elem.attribute( QStringLiteral(
"azimuth" ), QStringLiteral(
"315" ) ).toDouble();
65 double angle = elem.attribute( QStringLiteral(
"angle" ), QStringLiteral(
"45" ) ).toDouble();
66 double zFactor = elem.attribute( QStringLiteral(
"zfactor" ), QStringLiteral(
"1" ) ).toDouble();
67 bool multiDirectional = elem.attribute( QStringLiteral(
"multidirection" ), QStringLiteral(
"0" ) ).toInt();
78 if ( parentElem.isNull() )
83 QDomElement rasterRendererElem = doc.createElement( QStringLiteral(
"rasterrenderer" ) );
86 rasterRendererElem.setAttribute( QStringLiteral(
"band" ), mBand );
87 rasterRendererElem.setAttribute( QStringLiteral(
"azimuth" ), QString::number( mLightAzimuth ) );
88 rasterRendererElem.setAttribute( QStringLiteral(
"angle" ), QString::number( mLightAngle ) );
89 rasterRendererElem.setAttribute( QStringLiteral(
"zfactor" ), QString::number( mZFactor ) );
90 rasterRendererElem.setAttribute( QStringLiteral(
"multidirection" ), QString::number( mMultiDirectional ) );
91 parentElem.appendChild( rasterRendererElem );
97 std::unique_ptr< QgsRasterBlock > outputBlock(
new QgsRasterBlock() );
100 QgsDebugMsg( QStringLiteral(
"No input raster!" ) );
101 return outputBlock.release();
104 std::shared_ptr< QgsRasterBlock > inputBlock(
mInput->
block( mBand,
extent, width, height, feedback ) );
106 if ( !inputBlock || inputBlock->isEmpty() )
108 QgsDebugMsg( QStringLiteral(
"No raster data!" ) );
109 return outputBlock.release();
112 std::shared_ptr< QgsRasterBlock > alphaBlock;
117 if ( !alphaBlock || alphaBlock->isEmpty() )
120 return outputBlock.release();
125 alphaBlock = inputBlock;
130 return outputBlock.release();
133 if ( width == 0 || height == 0 )
134 return outputBlock.release();
139 float cellXSize =
static_cast<float>(
extent.
width() ) / width;
140 float cellYSize =
static_cast<float>(
extent.
height() ) / height;
141 float zenithRad =
static_cast<float>( std::max( 0.0, 90 - mLightAngle ) * M_PI / 180.0 );
142 float azimuthRad =
static_cast<float>( -1 * mLightAzimuth * M_PI / 180.0 );
143 float cosZenithRad = std::cos( zenithRad );
144 float sinZenithRad = std::sin( zenithRad );
147 float cos_alt_mul_z = cosZenithRad *
static_cast<float>( mZFactor );
148 float cos_az_mul_cos_alt_mul_z = std::cos( azimuthRad ) * cos_alt_mul_z;
149 float sin_az_mul_cos_alt_mul_z = std::sin( azimuthRad ) * cos_alt_mul_z;
150 float cos_az_mul_cos_alt_mul_z_mul_254 = 254.0f * cos_az_mul_cos_alt_mul_z;
151 float sin_az_mul_cos_alt_mul_z_mul_254 = 254.0f * sin_az_mul_cos_alt_mul_z;
152 float square_z =
static_cast<float>( mZFactor * mZFactor );
153 float sin_altRadians_mul_254 = 254.0f * sinZenithRad;
156 float sin_altRadians_mul_127 = 127.0f * sinZenithRad;
158 float cos225_az_mul_cos_alt_mul_z_mul_127 = -32.87001872802012f * cos_alt_mul_z;
159 float cos_alt_mul_z_mul_127 = 127.0f * cos_alt_mul_z;
170 && inputBlock->dataTypeSize() <= 4 );
176 if ( source.isEmpty() )
184 std::chrono::time_point<std::chrono::system_clock> startTime( std::chrono::system_clock::now() );
192 std::size_t inputDataTypeSize = inputBlock->dataTypeSize();
193 std::size_t outputDataTypeSize = outputBlock->dataTypeSize();
196 switch ( inputBlock->dataType() )
199 typeName = QStringLiteral(
"unsigned char" );
202 typeName = QStringLiteral(
"unsigned int" );
205 typeName = QStringLiteral(
"short" );
208 typeName = QStringLiteral(
"unsigned int" );
214 typeName = QStringLiteral(
"float" );
217 throw QgsException( QStringLiteral(
"Unsupported data type for OpenCL processing." ) );
222 source.replace( QLatin1String(
"__global float *scanLine" ), QStringLiteral(
"__global %1 *scanLine" ).arg(
typeName ) );
226 std::size_t scanLineWidth( inputBlock->width() + 2 );
227 std::size_t inputSize( inputDataTypeSize * inputBlock->width() );
230 std::size_t bufferWidth( width + 2 );
231 std::size_t bufferSize( inputDataTypeSize * bufferWidth );
236 std::unique_ptr<QgsRasterBlock> scanLine = std::make_unique<QgsRasterBlock>( inputBlock->dataType(), scanLineWidth, 1 );
243 std::vector<float> rasterParams;
244 rasterParams.push_back( inputBlock->noDataValue() );
245 rasterParams.push_back( outputBlock->noDataValue() );
246 rasterParams.push_back( mZFactor );
247 rasterParams.push_back( cellXSize );
248 rasterParams.push_back( cellYSize );
249 rasterParams.push_back(
static_cast<float>(
mOpacity ) );
252 rasterParams.push_back( cos_az_mul_cos_alt_mul_z_mul_254 );
253 rasterParams.push_back( sin_az_mul_cos_alt_mul_z_mul_254 );
254 rasterParams.push_back( square_z );
255 rasterParams.push_back( sin_altRadians_mul_254 );
258 rasterParams.push_back( sin_altRadians_mul_127 );
259 rasterParams.push_back( cos225_az_mul_cos_alt_mul_z_mul_127 );
260 rasterParams.push_back( cos_alt_mul_z_mul_127 );
263 rasterParams.push_back(
static_cast<float>( qBlue( defaultNodataColor ) ) );
264 rasterParams.push_back(
static_cast<float>( qGreen( defaultNodataColor ) ) );
265 rasterParams.push_back(
static_cast<float>( qRed( defaultNodataColor ) ) );
266 rasterParams.push_back(
static_cast<float>( qAlpha( defaultNodataColor ) ) / 255.0f );
269 rasterParams.push_back(
static_cast<float>( mMultiDirectional ) );
271 cl::Buffer rasterParamsBuffer( queue, rasterParams.begin(), rasterParams.end(),
true,
false,
nullptr );
272 cl::Buffer scanLine1Buffer( ctx, CL_MEM_READ_ONLY, bufferSize,
nullptr,
nullptr );
273 cl::Buffer scanLine2Buffer( ctx, CL_MEM_READ_ONLY, bufferSize,
nullptr,
nullptr );
274 cl::Buffer scanLine3Buffer( ctx, CL_MEM_READ_ONLY, bufferSize,
nullptr,
nullptr );
275 cl::Buffer *scanLineBuffer[3] = {&scanLine1Buffer, &scanLine2Buffer, &scanLine3Buffer};
277 cl::Buffer resultLineBuffer( ctx, CL_MEM_WRITE_ONLY, outputDataTypeSize * width,
nullptr,
nullptr );
279 static std::map<Qgis::DataType, cl::Program> programCache;
280 cl::Program program = programCache[inputBlock->dataType()];
281 if ( ! program.get() )
285 program = programCache[inputBlock->dataType()];
292 auto kernel = cl::KernelFunctor <
298 > ( program,
"processNineCellWindow" );
302 std::vector<int> rowIndex = {0, 1, 2};
304 for (
int i = 0; i < height; i++ )
313 feedback->
setProgress( 100.0 *
static_cast< double >( i ) / height );
319 scanLine->resetNoDataValue();
320 queue.enqueueWriteBuffer( scanLine1Buffer, CL_TRUE, 0, bufferSize, scanLine->bits( ) );
322 memcpy( scanLine->bits( 0, 1 ), inputBlock->bits( i, 0 ), inputSize );
323 queue.enqueueWriteBuffer( scanLine2Buffer, CL_TRUE, 0, bufferSize, scanLine->bits( ) );
325 memcpy( scanLine->bits( 0, 1 ), inputBlock->bits( i + 1, 0 ), inputSize );
326 queue.enqueueWriteBuffer( scanLine3Buffer, CL_TRUE, 0, bufferSize, scanLine->bits( ) );
332 if ( i == inputBlock->height() - 1 )
334 scanLine->resetNoDataValue();
335 queue.enqueueWriteBuffer( *scanLineBuffer[rowIndex[2]], CL_TRUE, 0, bufferSize, scanLine->bits( ) );
339 queue.enqueueWriteBuffer( *scanLineBuffer[rowIndex[2]], CL_TRUE, inputDataTypeSize * 1 , inputSize, inputBlock->bits( i + 1, 0 ) );
343 kernel( cl::EnqueueArgs(
347 *scanLineBuffer[rowIndex[0]],
348 *scanLineBuffer[rowIndex[1]],
349 *scanLineBuffer[rowIndex[2]],
354 queue.enqueueReadBuffer( resultLineBuffer, CL_TRUE, 0, outputDataTypeSize * outputBlock->width( ), outputBlock->bits( i, 0 ) );
355 std::rotate( rowIndex.begin(), rowIndex.begin() + 1, rowIndex.end() );
358 catch ( cl::Error &e )
373 double pixelValues[9] {0, 0, 0, 0, 0, 0, 0, 0, 0};
374 bool isNoData[9] {
false,
false,
false,
false,
false,
false,
false,
false,
false};
376 for (
int row = 0; row < height; row++ )
378 for (
int col = 0; col < width; col++ )
386 else if ( row == height - 1 )
394 pixelValues[ 0 ] = inputBlock->valueAndNoData( iUp, 0, isNoData[0] );
395 pixelValues[ 1 ] = pixelValues[0];
396 isNoData[1] = isNoData[0];
397 pixelValues[ 2 ] = pixelValues[0];
398 isNoData[2] = isNoData[0];
400 pixelValues[ 3 ] = inputBlock->valueAndNoData( row, 0, isNoData[3] );
401 pixelValues[ 4 ] = pixelValues[3];
402 isNoData[4] = isNoData[3];
403 pixelValues[ 5 ] = pixelValues[3];
404 isNoData[5] = isNoData[3];
406 pixelValues[ 6 ] = inputBlock->valueAndNoData( iDown, 0, isNoData[6] );
407 pixelValues[ 7 ] = pixelValues[6];
408 isNoData[7] = isNoData[6];
409 pixelValues[ 8 ] = pixelValues[6];
410 isNoData[8] = isNoData[6];
415 pixelValues[ 0 ] = pixelValues[1];
416 pixelValues[ 1 ] = pixelValues[2];
417 pixelValues[ 3 ] = pixelValues[4];
418 pixelValues[ 4 ] = pixelValues[5];
419 pixelValues[ 6 ] = pixelValues[7];
420 pixelValues[ 7 ] = pixelValues[8];
421 isNoData[ 0 ] = isNoData[1];
422 isNoData[ 1 ] = isNoData[2];
423 isNoData[ 3 ] = isNoData[4];
424 isNoData[ 4 ] = isNoData[5];
425 isNoData[ 6 ] = isNoData[7];
426 isNoData[ 7 ] = isNoData[8];
430 if ( col < width - 1 )
432 pixelValues[2] = inputBlock->valueAndNoData( iUp, col + 1, isNoData[2] );
433 pixelValues[5] = inputBlock->valueAndNoData( row, col + 1, isNoData[5] );
434 pixelValues[8] = inputBlock->valueAndNoData( iDown, col + 1, isNoData[8] );
439 outputBlock->setColor( row, col, defaultNodataColor );
444 const double x22 = pixelValues[4];
446 const double x11 = isNoData[0] ? x22 : pixelValues[0];
447 const double x21 = isNoData[3] ? x22 : pixelValues[3];
448 const double x31 = isNoData[6] ? x22 : pixelValues[6];
449 const double x12 = isNoData[1] ? x22 : pixelValues[1];
451 const double x32 = isNoData[7] ? x22 : pixelValues[7];
452 const double x13 = isNoData[2] ? x22 : pixelValues[2];
453 const double x23 = isNoData[5] ? x22 : pixelValues[5];
454 const double x33 = isNoData[8] ? x22 : pixelValues[8];
457 const double derX = ( ( x13 + x23 + x23 + x33 ) - ( x11 + x21 + x21 + x31 ) ) / ( 8 * cellXSize );
458 const double derY = ( ( x31 + x32 + x32 + x33 ) - ( x11 + x12 + x12 + x13 ) ) / ( 8 * -cellYSize );
463 if ( !mMultiDirectional )
466 grayValue = std::clamp( ( sin_altRadians_mul_254 -
467 ( derY * cos_az_mul_cos_alt_mul_z_mul_254 -
468 derX * sin_az_mul_cos_alt_mul_z_mul_254 ) ) /
469 std::sqrt( 1 + square_z * ( derX * derX + derY * derY ) ),
476 const float xx = derX * derX;
477 const float yy = derY * derY;
478 const float xx_plus_yy = xx + yy;
480 if ( xx_plus_yy == 0.0 )
482 grayValue = std::clamp(
static_cast<float>( 1.0 + sin_altRadians_mul_254 ), 0.0f, 255.0f );
487 float val225_mul_127 = sin_altRadians_mul_127 +
488 ( derX - derY ) * cos225_az_mul_cos_alt_mul_z_mul_127;
489 val225_mul_127 = ( val225_mul_127 <= 0.0 ) ? 0.0 : val225_mul_127;
490 float val270_mul_127 = sin_altRadians_mul_127 -
491 derX * cos_alt_mul_z_mul_127;
492 val270_mul_127 = ( val270_mul_127 <= 0.0 ) ? 0.0 : val270_mul_127;
493 float val315_mul_127 = sin_altRadians_mul_127 +
494 ( derX + derY ) * cos225_az_mul_cos_alt_mul_z_mul_127;
495 val315_mul_127 = ( val315_mul_127 <= 0.0 ) ? 0.0 : val315_mul_127;
496 float val360_mul_127 = sin_altRadians_mul_127 -
497 derY * cos_alt_mul_z_mul_127;
498 val360_mul_127 = ( val360_mul_127 <= 0.0 ) ? 0.0 : val360_mul_127;
501 const float weight_225 = 0.5 * xx_plus_yy - derX * derY;
502 const float weight_270 = xx;
503 const float weight_315 = xx_plus_yy - weight_225;
504 const float weight_360 = yy;
505 const float cang_mul_127 = (
506 ( weight_225 * val225_mul_127 +
507 weight_270 * val270_mul_127 +
508 weight_315 * val315_mul_127 +
509 weight_360 * val360_mul_127 ) / xx_plus_yy ) /
510 ( 1 + square_z * xx_plus_yy );
512 grayValue = std::clamp( 1.0f + cang_mul_127, 0.0f, 255.0f );
523 currentAlpha *= alphaBlock->value( row ) / 255.0;
528 outputBlock->setColor( row, col, qRgba( grayValue, grayValue, grayValue, 255 ) );
532 outputBlock->setColor( row, col, qRgba( currentAlpha * grayValue, currentAlpha * grayValue, currentAlpha * grayValue, currentAlpha * 255 ) );
541 if (
QgsSettings().value( QStringLiteral(
"Map/logCanvasRefreshEvent" ),
false ).toBool() )
544 .arg( useOpenCL ? QStringLiteral(
"OpenCL" ) : QStringLiteral(
"CPU" ) )
547 .arg( std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::system_clock::now() - startTime ).count() ),
553 return outputBlock.release();
582 QDomNodeList elements = element.elementsByTagName( QStringLiteral(
"sld:RasterSymbolizer" ) );
583 if ( elements.size() == 0 )
587 QDomElement rasterSymbolizerElem = elements.at( 0 ).toElement();
594 QDomElement channelSelectionElem = doc.createElement( QStringLiteral(
"sld:ChannelSelection" ) );
595 elements = rasterSymbolizerElem.elementsByTagName( QStringLiteral(
"sld:Opacity" ) );
596 if ( elements.size() != 0 )
598 rasterSymbolizerElem.insertAfter( channelSelectionElem, elements.at( 0 ) );
602 elements = rasterSymbolizerElem.elementsByTagName( QStringLiteral(
"sld:Geometry" ) );
603 if ( elements.size() != 0 )
605 rasterSymbolizerElem.insertAfter( channelSelectionElem, elements.at( 0 ) );
609 rasterSymbolizerElem.insertBefore( channelSelectionElem, rasterSymbolizerElem.firstChild() );
614 QDomElement channelElem = doc.createElement( QStringLiteral(
"sld:GrayChannel" ) );
615 channelSelectionElem.appendChild( channelElem );
618 QDomElement sourceChannelNameElem = doc.createElement( QStringLiteral(
"sld:SourceChannelName" ) );
619 sourceChannelNameElem.appendChild( doc.createTextNode( QString::number(
band() ) ) );
620 channelElem.appendChild( sourceChannelNameElem );
624 QDomElement shadedReliefElem = doc.createElement( QStringLiteral(
"sld:ShadedRelief" ) );
625 rasterSymbolizerElem.appendChild( shadedReliefElem );
628 QDomElement brightnessOnlyElem = doc.createElement( QStringLiteral(
"sld:BrightnessOnly" ) );
629 brightnessOnlyElem.appendChild( doc.createTextNode( QStringLiteral(
"true" ) ) );
630 shadedReliefElem.appendChild( brightnessOnlyElem );
633 QDomElement reliefFactorElem = doc.createElement( QStringLiteral(
"sld:ReliefFactor" ) );
634 reliefFactorElem.appendChild( doc.createTextNode( QString::number(
zFactor() ) ) );
635 shadedReliefElem.appendChild( reliefFactorElem );
638 QDomElement altitudeVendorOptionElem = doc.createElement( QStringLiteral(
"sld:VendorOption" ) );
639 altitudeVendorOptionElem.setAttribute( QStringLiteral(
"name" ), QStringLiteral(
"altitude" ) );
640 altitudeVendorOptionElem.appendChild( doc.createTextNode( QString::number(
altitude() ) ) );
641 shadedReliefElem.appendChild( altitudeVendorOptionElem );
644 QDomElement azimutVendorOptionElem = doc.createElement( QStringLiteral(
"sld:VendorOption" ) );
645 azimutVendorOptionElem.setAttribute( QStringLiteral(
"name" ), QStringLiteral(
"azimuth" ) );
646 azimutVendorOptionElem.appendChild( doc.createTextNode( QString::number(
azimuth() ) ) );
647 shadedReliefElem.appendChild( azimutVendorOptionElem );
650 QDomElement multidirectionalVendorOptionElem = doc.createElement( QStringLiteral(
"sld:VendorOption" ) );
651 multidirectionalVendorOptionElem.setAttribute( QStringLiteral(
"name" ), QStringLiteral(
"multidirectional" ) );
652 multidirectionalVendorOptionElem.appendChild( doc.createTextNode( QString::number(
multiDirectional() ) ) );
653 shadedReliefElem.appendChild( multidirectionalVendorOptionElem );