106 auto outputBlock = std::make_unique<QgsRasterBlock>();
110 return outputBlock.release();
113 std::shared_ptr< QgsRasterBlock > inputBlock(
mInput->block( mBand,
extent, width, height, feedback ) );
115 if ( !inputBlock || inputBlock->isEmpty() )
118 return outputBlock.release();
121 std::shared_ptr< QgsRasterBlock > alphaBlock;
126 if ( !alphaBlock || alphaBlock->isEmpty() )
129 return outputBlock.release();
134 alphaBlock = inputBlock;
139 return outputBlock.release();
142 if ( width == 0 || height == 0 )
143 return outputBlock.release();
148 float cellXSize =
static_cast<float>(
extent.width() ) / width;
149 float cellYSize =
static_cast<float>(
extent.height() ) / height;
150 float zenithRad =
static_cast<float>( std::max( 0.0, 90 - mLightAngle ) * M_PI / 180.0 );
151 float azimuthRad =
static_cast<float>( -1 * mLightAzimuth * M_PI / 180.0 );
152 float cosZenithRad = std::cos( zenithRad );
153 float sinZenithRad = std::sin( zenithRad );
156 float cos_alt_mul_z = cosZenithRad *
static_cast<float>( mZFactor );
157 float cos_az_mul_cos_alt_mul_z = std::cos( azimuthRad ) * cos_alt_mul_z;
158 float sin_az_mul_cos_alt_mul_z = std::sin( azimuthRad ) * cos_alt_mul_z;
159 float cos_az_mul_cos_alt_mul_z_mul_254 = 254.0f * cos_az_mul_cos_alt_mul_z;
160 float sin_az_mul_cos_alt_mul_z_mul_254 = 254.0f * sin_az_mul_cos_alt_mul_z;
161 float square_z =
static_cast<float>( mZFactor * mZFactor );
162 float sin_altRadians_mul_254 = 254.0f * sinZenithRad;
165 float sin_altRadians_mul_127 = 127.0f * sinZenithRad;
167 float cos225_az_mul_cos_alt_mul_z_mul_127 = -32.87001872802012f * cos_alt_mul_z;
168 float cos_alt_mul_z_mul_127 = 127.0f * cos_alt_mul_z;
181 if ( source.isEmpty() )
189 std::chrono::time_point<std::chrono::system_clock> startTime( std::chrono::system_clock::now() );
196 std::size_t inputDataTypeSize = inputBlock->dataTypeSize();
197 std::size_t outputDataTypeSize = outputBlock->dataTypeSize();
200 switch ( inputBlock->dataType() )
203 typeName = u
"unsigned char"_s;
206 typeName = u
"unsigned int"_s;
209 typeName = u
"short"_s;
212 typeName = u
"unsigned int"_s;
218 typeName = u
"float"_s;
221 throw QgsException( u
"Unsupported data type for OpenCL processing."_s );
226 source.replace(
"__global float *scanLine"_L1, u
"__global %1 *scanLine"_s.arg( typeName ) );
230 std::size_t scanLineWidth( inputBlock->width() + 2 );
231 std::size_t inputSize( inputDataTypeSize * inputBlock->width() );
234 std::size_t bufferWidth( width + 2 );
235 std::size_t bufferSize( inputDataTypeSize * bufferWidth );
240 auto scanLine = std::make_unique<QgsRasterBlock>( inputBlock->dataType(), scanLineWidth, 1 );
247 std::vector<float> rasterParams;
248 rasterParams.push_back( inputBlock->noDataValue() );
249 rasterParams.push_back( outputBlock->noDataValue() );
250 rasterParams.push_back( mZFactor );
251 rasterParams.push_back( cellXSize );
252 rasterParams.push_back( cellYSize );
253 rasterParams.push_back(
static_cast<float>(
mOpacity ) );
256 rasterParams.push_back( cos_az_mul_cos_alt_mul_z_mul_254 );
257 rasterParams.push_back( sin_az_mul_cos_alt_mul_z_mul_254 );
258 rasterParams.push_back( square_z );
259 rasterParams.push_back( sin_altRadians_mul_254 );
262 rasterParams.push_back( sin_altRadians_mul_127 );
263 rasterParams.push_back( cos225_az_mul_cos_alt_mul_z_mul_127 );
264 rasterParams.push_back( cos_alt_mul_z_mul_127 );
267 rasterParams.push_back(
static_cast<float>( qBlue( defaultNodataColor ) ) );
268 rasterParams.push_back(
static_cast<float>( qGreen( defaultNodataColor ) ) );
269 rasterParams.push_back(
static_cast<float>( qRed( defaultNodataColor ) ) );
270 rasterParams.push_back(
static_cast<float>( qAlpha( defaultNodataColor ) ) / 255.0f );
273 rasterParams.push_back(
static_cast<float>( mMultiDirectional ) );
275 cl::Buffer rasterParamsBuffer( queue, rasterParams.begin(), rasterParams.end(),
true,
false,
nullptr );
276 cl::Buffer scanLine1Buffer( ctx, CL_MEM_READ_ONLY, bufferSize,
nullptr,
nullptr );
277 cl::Buffer scanLine2Buffer( ctx, CL_MEM_READ_ONLY, bufferSize,
nullptr,
nullptr );
278 cl::Buffer scanLine3Buffer( ctx, CL_MEM_READ_ONLY, bufferSize,
nullptr,
nullptr );
279 cl::Buffer *scanLineBuffer[3] = { &scanLine1Buffer, &scanLine2Buffer, &scanLine3Buffer };
281 cl::Buffer resultLineBuffer( ctx, CL_MEM_WRITE_ONLY, outputDataTypeSize * width,
nullptr,
nullptr );
283 static std::map<Qgis::DataType, cl::Program> programCache;
284 cl::Program program = programCache[inputBlock->dataType()];
285 if ( !program.get() )
289 program = programCache[inputBlock->dataType()];
296 auto kernel = cl::KernelFunctor< cl::Buffer &, cl::Buffer &, cl::Buffer &, cl::Buffer &, cl::Buffer & >( program,
"processNineCellWindow" );
300 std::vector<int> rowIndex = { 0, 1, 2 };
302 for (
int i = 0; i < height; i++ )
311 feedback->
setProgress( 100.0 *
static_cast< double >( i ) / height );
317 scanLine->resetNoDataValue();
318 queue.enqueueWriteBuffer( scanLine1Buffer, CL_TRUE, 0, bufferSize, scanLine->bits() );
320 memcpy( scanLine->bits( 0, 1 ), inputBlock->bits( i, 0 ), inputSize );
321 queue.enqueueWriteBuffer( scanLine2Buffer, CL_TRUE, 0, bufferSize, scanLine->bits() );
323 memcpy( scanLine->bits( 0, 1 ), inputBlock->bits( i + 1, 0 ), inputSize );
324 queue.enqueueWriteBuffer( scanLine3Buffer, CL_TRUE, 0, bufferSize, scanLine->bits() );
330 if ( i == inputBlock->height() - 1 )
332 scanLine->resetNoDataValue();
333 queue.enqueueWriteBuffer( *scanLineBuffer[rowIndex[2]], CL_TRUE, 0, bufferSize, scanLine->bits() );
337 queue.enqueueWriteBuffer( *scanLineBuffer[rowIndex[2]], CL_TRUE, inputDataTypeSize * 1 , inputSize, inputBlock->bits( i + 1, 0 ) );
341 kernel( cl::EnqueueArgs( queue, cl::NDRange( width ) ), *scanLineBuffer[rowIndex[0]], *scanLineBuffer[rowIndex[1]], *scanLineBuffer[rowIndex[2]], resultLineBuffer, rasterParamsBuffer );
343 queue.enqueueReadBuffer( resultLineBuffer, CL_TRUE, 0, outputDataTypeSize * outputBlock->width(), outputBlock->bits( i, 0 ) );
344 std::rotate( rowIndex.begin(), rowIndex.begin() + 1, rowIndex.end() );
347 catch ( cl::Error &e )
359 double pixelValues[9] { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
360 bool isNoData[9] {
false,
false,
false,
false,
false,
false,
false,
false,
false };
362 for (
int row = 0; row < height; row++ )
364 for (
int col = 0; col < width; col++ )
372 else if ( row == height - 1 )
380 pixelValues[0] = inputBlock->valueAndNoData( iUp, 0, isNoData[0] );
381 pixelValues[1] = pixelValues[0];
382 isNoData[1] = isNoData[0];
383 pixelValues[2] = pixelValues[0];
384 isNoData[2] = isNoData[0];
386 pixelValues[3] = inputBlock->valueAndNoData( row, 0, isNoData[3] );
387 pixelValues[4] = pixelValues[3];
388 isNoData[4] = isNoData[3];
389 pixelValues[5] = pixelValues[3];
390 isNoData[5] = isNoData[3];
392 pixelValues[6] = inputBlock->valueAndNoData( iDown, 0, isNoData[6] );
393 pixelValues[7] = pixelValues[6];
394 isNoData[7] = isNoData[6];
395 pixelValues[8] = pixelValues[6];
396 isNoData[8] = isNoData[6];
401 pixelValues[0] = pixelValues[1];
402 pixelValues[1] = pixelValues[2];
403 pixelValues[3] = pixelValues[4];
404 pixelValues[4] = pixelValues[5];
405 pixelValues[6] = pixelValues[7];
406 pixelValues[7] = pixelValues[8];
407 isNoData[0] = isNoData[1];
408 isNoData[1] = isNoData[2];
409 isNoData[3] = isNoData[4];
410 isNoData[4] = isNoData[5];
411 isNoData[6] = isNoData[7];
412 isNoData[7] = isNoData[8];
416 if ( col < width - 1 )
418 pixelValues[2] = inputBlock->valueAndNoData( iUp, col + 1, isNoData[2] );
419 pixelValues[5] = inputBlock->valueAndNoData( row, col + 1, isNoData[5] );
420 pixelValues[8] = inputBlock->valueAndNoData( iDown, col + 1, isNoData[8] );
425 outputBlock->setColor( row, col, defaultNodataColor );
430 const double x22 = pixelValues[4];
432 const double x11 = isNoData[0] ? x22 : pixelValues[0];
433 const double x21 = isNoData[3] ? x22 : pixelValues[3];
434 const double x31 = isNoData[6] ? x22 : pixelValues[6];
435 const double x12 = isNoData[1] ? x22 : pixelValues[1];
437 const double x32 = isNoData[7] ? x22 : pixelValues[7];
438 const double x13 = isNoData[2] ? x22 : pixelValues[2];
439 const double x23 = isNoData[5] ? x22 : pixelValues[5];
440 const double x33 = isNoData[8] ? x22 : pixelValues[8];
443 const double derX = ( ( x13 + x23 + x23 + x33 ) - ( x11 + x21 + x21 + x31 ) ) / ( 8 * cellXSize );
444 const double derY = ( ( x31 + x32 + x32 + x33 ) - ( x11 + x12 + x12 + x13 ) ) / ( 8 * -cellYSize );
449 if ( !mMultiDirectional )
453 = std::clamp( ( sin_altRadians_mul_254 - ( derY * cos_az_mul_cos_alt_mul_z_mul_254 - derX * sin_az_mul_cos_alt_mul_z_mul_254 ) ) / std::sqrt( 1 + square_z * ( derX * derX + derY * derY ) ), 0.0, 255.0 );
459 const float xx = derX * derX;
460 const float yy = derY * derY;
461 const float xx_plus_yy = xx + yy;
463 if ( xx_plus_yy == 0.0 )
465 grayValue = std::clamp(
static_cast<float>( 1.0 + sin_altRadians_mul_254 ), 0.0f, 255.0f );
470 float val225_mul_127 = sin_altRadians_mul_127 + ( derX - derY ) * cos225_az_mul_cos_alt_mul_z_mul_127;
471 val225_mul_127 = ( val225_mul_127 <= 0.0 ) ? 0.0 : val225_mul_127;
472 float val270_mul_127 = sin_altRadians_mul_127 - derX * cos_alt_mul_z_mul_127;
473 val270_mul_127 = ( val270_mul_127 <= 0.0 ) ? 0.0 : val270_mul_127;
474 float val315_mul_127 = sin_altRadians_mul_127 + ( derX + derY ) * cos225_az_mul_cos_alt_mul_z_mul_127;
475 val315_mul_127 = ( val315_mul_127 <= 0.0 ) ? 0.0 : val315_mul_127;
476 float val360_mul_127 = sin_altRadians_mul_127 - derY * cos_alt_mul_z_mul_127;
477 val360_mul_127 = ( val360_mul_127 <= 0.0 ) ? 0.0 : val360_mul_127;
480 const float weight_225 = 0.5 * xx_plus_yy - derX * derY;
481 const float weight_270 = xx;
482 const float weight_315 = xx_plus_yy - weight_225;
483 const float weight_360 = yy;
484 const float cang_mul_127 = ( ( weight_225 * val225_mul_127 + weight_270 * val270_mul_127 + weight_315 * val315_mul_127 + weight_360 * val360_mul_127 ) / xx_plus_yy )
485 / ( 1 + square_z * xx_plus_yy );
487 grayValue = std::clamp( 1.0f + cang_mul_127, 0.0f, 255.0f );
498 currentAlpha *= alphaBlock->value(
static_cast<qgssize>( row ) * width + col ) / 255.0;
503 outputBlock->setColor( row, col, qRgba( grayValue, grayValue, grayValue, 255 ) );
507 outputBlock->setColor( row, col, qRgba( currentAlpha * grayValue, currentAlpha * grayValue, currentAlpha * grayValue, currentAlpha * 255 ) );
516 if (
QgsSettings().value( u
"Map/logCanvasRefreshEvent"_s,
false ).toBool() )
519 u
"%1 processing time for hillshade (%2 x %3 ): %4 ms"_s.arg( useOpenCL ? u
"OpenCL"_s : u
"CPU"_s )
522 .arg( std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::system_clock::now() - startTime ).count() ),
529 return outputBlock.release();
580 QDomNodeList elements = element.elementsByTagName( u
"sld:RasterSymbolizer"_s );
581 if ( elements.size() == 0 )
585 QDomElement rasterSymbolizerElem = elements.at( 0 ).toElement();
592 QDomElement channelSelectionElem = doc.createElement( u
"sld:ChannelSelection"_s );
593 elements = rasterSymbolizerElem.elementsByTagName( u
"sld:Opacity"_s );
594 if ( elements.size() != 0 )
596 rasterSymbolizerElem.insertAfter( channelSelectionElem, elements.at( 0 ) );
600 elements = rasterSymbolizerElem.elementsByTagName( u
"sld:Geometry"_s );
601 if ( elements.size() != 0 )
603 rasterSymbolizerElem.insertAfter( channelSelectionElem, elements.at( 0 ) );
607 rasterSymbolizerElem.insertBefore( channelSelectionElem, rasterSymbolizerElem.firstChild() );
612 QDomElement channelElem = doc.createElement( u
"sld:GrayChannel"_s );
613 channelSelectionElem.appendChild( channelElem );
616 QDomElement sourceChannelNameElem = doc.createElement( u
"sld:SourceChannelName"_s );
617 sourceChannelNameElem.appendChild( doc.createTextNode( QString::number( mBand ) ) );
618 channelElem.appendChild( sourceChannelNameElem );
622 QDomElement shadedReliefElem = doc.createElement( u
"sld:ShadedRelief"_s );
623 rasterSymbolizerElem.appendChild( shadedReliefElem );
626 QDomElement brightnessOnlyElem = doc.createElement( u
"sld:BrightnessOnly"_s );
627 brightnessOnlyElem.appendChild( doc.createTextNode( u
"true"_s ) );
628 shadedReliefElem.appendChild( brightnessOnlyElem );
631 QDomElement reliefFactorElem = doc.createElement( u
"sld:ReliefFactor"_s );
632 reliefFactorElem.appendChild( doc.createTextNode( QString::number(
zFactor() ) ) );
633 shadedReliefElem.appendChild( reliefFactorElem );
636 QDomElement altitudeVendorOptionElem = doc.createElement( u
"sld:VendorOption"_s );
637 altitudeVendorOptionElem.setAttribute( u
"name"_s, u
"altitude"_s );
638 altitudeVendorOptionElem.appendChild( doc.createTextNode( QString::number(
altitude() ) ) );
639 shadedReliefElem.appendChild( altitudeVendorOptionElem );
642 QDomElement azimutVendorOptionElem = doc.createElement( u
"sld:VendorOption"_s );
643 azimutVendorOptionElem.setAttribute( u
"name"_s, u
"azimuth"_s );
644 azimutVendorOptionElem.appendChild( doc.createTextNode( QString::number(
azimuth() ) ) );
645 shadedReliefElem.appendChild( azimutVendorOptionElem );
648 QDomElement multidirectionalVendorOptionElem = doc.createElement( u
"sld:VendorOption"_s );
649 multidirectionalVendorOptionElem.setAttribute( u
"name"_s, u
"multidirectional"_s );
650 multidirectionalVendorOptionElem.appendChild( doc.createTextNode( QString::number(
multiDirectional() ) ) );
651 shadedReliefElem.appendChild( multidirectionalVendorOptionElem );