41 , mLightAngle( lightAngle )
42 , mLightAzimuth( lightAzimuth )
43 , mMultiDirectional( false )
65 int band = elem.attribute( QStringLiteral(
"band" ), QStringLiteral(
"0" ) ).toInt();
66 double azimuth = elem.attribute( QStringLiteral(
"azimuth" ), QStringLiteral(
"315" ) ).toDouble();
67 double angle = elem.attribute( QStringLiteral(
"angle" ), QStringLiteral(
"45" ) ).toDouble();
68 double zFactor = elem.attribute( QStringLiteral(
"zfactor" ), QStringLiteral(
"1" ) ).toDouble();
69 bool multiDirectional = elem.attribute( QStringLiteral(
"multidirection" ), QStringLiteral(
"0" ) ).toInt();
80 if ( parentElem.isNull() )
85 QDomElement rasterRendererElem = doc.createElement( QStringLiteral(
"rasterrenderer" ) );
88 rasterRendererElem.setAttribute( QStringLiteral(
"band" ), mBand );
89 rasterRendererElem.setAttribute( QStringLiteral(
"azimuth" ), QString::number( mLightAzimuth ) );
90 rasterRendererElem.setAttribute( QStringLiteral(
"angle" ), QString::number( mLightAngle ) );
91 rasterRendererElem.setAttribute( QStringLiteral(
"zfactor" ), QString::number( mZFactor ) );
92 rasterRendererElem.setAttribute( QStringLiteral(
"multidirection" ), QString::number( mMultiDirectional ) );
93 parentElem.appendChild( rasterRendererElem );
99 std::unique_ptr< QgsRasterBlock > outputBlock(
new QgsRasterBlock() );
102 QgsDebugMsg( QStringLiteral(
"No input raster!" ) );
103 return outputBlock.release();
106 std::shared_ptr< QgsRasterBlock > inputBlock(
mInput->
block( mBand, extent, width, height, feedback ) );
108 if ( !inputBlock || inputBlock->isEmpty() )
110 QgsDebugMsg( QStringLiteral(
"No raster data!" ) );
111 return outputBlock.release();
114 std::shared_ptr< QgsRasterBlock > alphaBlock;
119 if ( !alphaBlock || alphaBlock->isEmpty() )
122 return outputBlock.release();
127 alphaBlock = inputBlock;
132 return outputBlock.release();
138 float cellXSize =
static_cast<float>( extent.
width() ) / width;
139 float cellYSize =
static_cast<float>( extent.
height() ) / height;
140 float zenithRad =
static_cast<float>( std::max( 0.0, 90 - mLightAngle ) * M_PI / 180.0 );
141 float azimuthRad =
static_cast<float>( -1 * mLightAzimuth * M_PI / 180.0 );
142 float cosZenithRad = std::cos( zenithRad );
143 float sinZenithRad = std::sin( zenithRad );
146 float cos_alt_mul_z = cosZenithRad *
static_cast<float>( mZFactor );
147 float cos_az_mul_cos_alt_mul_z = std::cos( azimuthRad ) * cos_alt_mul_z;
148 float sin_az_mul_cos_alt_mul_z = std::sin( azimuthRad ) * cos_alt_mul_z;
149 float cos_az_mul_cos_alt_mul_z_mul_254 = 254.0f * cos_az_mul_cos_alt_mul_z;
150 float sin_az_mul_cos_alt_mul_z_mul_254 = 254.0f * sin_az_mul_cos_alt_mul_z;
151 float square_z =
static_cast<float>( mZFactor * mZFactor );
152 float sin_altRadians_mul_254 = 254.0f * sinZenithRad;
155 float sin_altRadians_mul_127 = 127.0f * sinZenithRad;
157 float cos225_az_mul_cos_alt_mul_z_mul_127 = -32.87001872802012f * cos_alt_mul_z;
158 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() )
198 case Qgis::DataType::Byte:
199 typeName = QStringLiteral(
"unsigned char" );
201 case Qgis::DataType::UInt16:
202 typeName = QStringLiteral(
"unsigned int" );
204 case Qgis::DataType::Int16:
205 typeName = QStringLiteral(
"short" );
207 case Qgis::DataType::UInt32:
208 typeName = QStringLiteral(
"unsigned int" );
210 case Qgis::DataType::Int32:
211 typeName = QStringLiteral(
"int" );
213 case Qgis::DataType::Float32:
214 typeName = QStringLiteral(
"float" );
217 throw QgsException( QStringLiteral(
"Unsupported data type for OpenCL processing." ) );
220 if ( inputBlock->dataType() != Qgis::DataType::Float32 )
222 source.replace( QStringLiteral(
"__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 = qgis::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 for (
qgssize i = 0; i < static_cast<qgssize>( height ); i++ )
376 for (
qgssize j = 0; j < static_cast<qgssize>( width ); j++ )
379 if ( inputBlock->isNoData( i, j ) )
381 outputBlock->setColor( static_cast<int>( i ), static_cast<int>( j ), defaultNodataColor );
385 qgssize iUp, iDown, jLeft, jRight;
391 else if ( i < static_cast<qgssize>( height ) - 1 )
407 else if ( j < static_cast<qgssize>( width ) - 1 )
429 x22 = inputBlock->value( i, j );
431 x11 = inputBlock->isNoData( iUp, jLeft ) ? x22 : inputBlock->value( iUp, jLeft );
432 x21 = inputBlock->isNoData( i, jLeft ) ? x22 : inputBlock->value( i, jLeft );
433 x31 = inputBlock->isNoData( iDown, jLeft ) ? x22 : inputBlock->value( iDown, jLeft );
435 x12 = inputBlock->isNoData( iUp, j ) ? x22 : inputBlock->value( iUp, j );
437 x32 = inputBlock->isNoData( iDown, j ) ? x22 : inputBlock->value( iDown, j );
439 x13 = inputBlock->isNoData( iUp, jRight ) ? x22 : inputBlock->value( iUp, jRight );
440 x23 = inputBlock->isNoData( i, jRight ) ? x22 : inputBlock->value( i, jRight );
441 x33 = inputBlock->isNoData( iDown, jRight ) ? x22 : inputBlock->value( iDown, jRight );
443 double derX = calcFirstDerX( x11, x21, x31, x12, x22, x32, x13, x23, x33, cellXSize );
444 double derY = calcFirstDerY( x11, x21, x31, x12, x22, x32, x13, x23, x33, cellYSize );
449 if ( !mMultiDirectional )
452 grayValue = qBound( 0.0, ( sin_altRadians_mul_254 -
453 ( derY * cos_az_mul_cos_alt_mul_z_mul_254 -
454 derX * sin_az_mul_cos_alt_mul_z_mul_254 ) ) /
455 std::sqrt( 1 + square_z * ( derX * derX + derY * derY ) )
462 const float xx = derX * derX;
463 const float yy = derY * derY;
464 const float xx_plus_yy = xx + yy;
466 if ( xx_plus_yy == 0.0 )
468 grayValue = qBound( 0.0f, static_cast<float>( 1.0 + sin_altRadians_mul_254 ), 255.0f );
473 float val225_mul_127 = sin_altRadians_mul_127 +
474 ( derX - derY ) * cos225_az_mul_cos_alt_mul_z_mul_127;
475 val225_mul_127 = ( val225_mul_127 <= 0.0 ) ? 0.0 : val225_mul_127;
476 float val270_mul_127 = sin_altRadians_mul_127 -
477 derX * cos_alt_mul_z_mul_127;
478 val270_mul_127 = ( val270_mul_127 <= 0.0 ) ? 0.0 : val270_mul_127;
479 float val315_mul_127 = sin_altRadians_mul_127 +
480 ( derX + derY ) * cos225_az_mul_cos_alt_mul_z_mul_127;
481 val315_mul_127 = ( val315_mul_127 <= 0.0 ) ? 0.0 : val315_mul_127;
482 float val360_mul_127 = sin_altRadians_mul_127 -
483 derY * cos_alt_mul_z_mul_127;
484 val360_mul_127 = ( val360_mul_127 <= 0.0 ) ? 0.0 : val360_mul_127;
487 const float weight_225 = 0.5 * xx_plus_yy - derX * derY;
488 const float weight_270 = xx;
489 const float weight_315 = xx_plus_yy - weight_225;
490 const float weight_360 = yy;
491 const float cang_mul_127 = (
492 ( weight_225 * val225_mul_127 +
493 weight_270 * val270_mul_127 +
494 weight_315 * val315_mul_127 +
495 weight_360 * val360_mul_127 ) / xx_plus_yy ) /
496 ( 1 + square_z * xx_plus_yy );
498 grayValue = qBound( 0.0f, 1.0f + cang_mul_127, 255.0f );
509 currentAlpha *= alphaBlock->value( i ) / 255.0;
514 outputBlock->setColor( i, j, qRgba( grayValue, grayValue, grayValue, 255 ) );
518 outputBlock->setColor( i, j, qRgba( currentAlpha * grayValue, currentAlpha * grayValue, currentAlpha * grayValue, currentAlpha * 255 ) );
527 if (
QgsSettings().value( QStringLiteral(
"Map/logCanvasRefreshEvent" ),
false ).toBool() )
530 .arg( useOpenCL ? QStringLiteral(
"OpenCL" ) : QStringLiteral(
"CPU" ) )
533 .arg( std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::system_clock::now() - startTime ).count() ),
539 return outputBlock.release();
562 double QgsHillshadeRenderer::calcFirstDerX(
double x11,
double x21,
double x31,
double x12,
double x22,
double x32,
double x13,
double x23,
double x33,
double cellsize )
567 return ( ( x13 + x23 + x23 + x33 ) - ( x11 + x21 + x21 + x31 ) ) / ( 8 * cellsize );
570 double QgsHillshadeRenderer::calcFirstDerY(
double x11,
double x21,
double x31,
double x12,
double x22,
double x32,
double x13,
double x23,
double x33,
double cellsize )
575 return ( ( x31 + x32 + x32 + x33 ) - ( x11 + x12 + x12 + x13 ) ) / ( 8 * -cellsize );
586 QDomNodeList elements = element.elementsByTagName( QStringLiteral(
"sld:RasterSymbolizer" ) );
587 if ( elements.size() == 0 )
591 QDomElement rasterSymbolizerElem = elements.at( 0 ).toElement();
598 QDomElement channelSelectionElem = doc.createElement( QStringLiteral(
"sld:ChannelSelection" ) );
599 elements = rasterSymbolizerElem.elementsByTagName( QStringLiteral(
"sld:Opacity" ) );
600 if ( elements.size() != 0 )
602 rasterSymbolizerElem.insertAfter( channelSelectionElem, elements.at( 0 ) );
606 elements = rasterSymbolizerElem.elementsByTagName( QStringLiteral(
"sld:Geometry" ) );
607 if ( elements.size() != 0 )
609 rasterSymbolizerElem.insertAfter( channelSelectionElem, elements.at( 0 ) );
613 rasterSymbolizerElem.insertBefore( channelSelectionElem, rasterSymbolizerElem.firstChild() );
618 QDomElement channelElem = doc.createElement( QStringLiteral(
"sld:GrayChannel" ) );
619 channelSelectionElem.appendChild( channelElem );
622 QDomElement sourceChannelNameElem = doc.createElement( QStringLiteral(
"sld:SourceChannelName" ) );
623 sourceChannelNameElem.appendChild( doc.createTextNode( QString::number(
band() ) ) );
624 channelElem.appendChild( sourceChannelNameElem );
628 QDomElement shadedReliefElem = doc.createElement( QStringLiteral(
"sld:ShadedRelief" ) );
629 rasterSymbolizerElem.appendChild( shadedReliefElem );
632 QDomElement brightnessOnlyElem = doc.createElement( QStringLiteral(
"sld:BrightnessOnly" ) );
633 brightnessOnlyElem.appendChild( doc.createTextNode( QStringLiteral(
"true" ) ) );
634 shadedReliefElem.appendChild( brightnessOnlyElem );
637 QDomElement reliefFactorElem = doc.createElement( QStringLiteral(
"sld:ReliefFactor" ) );
638 reliefFactorElem.appendChild( doc.createTextNode( QString::number(
zFactor() ) ) );
639 shadedReliefElem.appendChild( reliefFactorElem );
642 QDomElement altitudeVendorOptionElem = doc.createElement( QStringLiteral(
"sld:VendorOption" ) );
643 altitudeVendorOptionElem.setAttribute( QStringLiteral(
"name" ), QStringLiteral(
"altitude" ) );
644 altitudeVendorOptionElem.appendChild( doc.createTextNode( QString::number(
altitude() ) ) );
645 shadedReliefElem.appendChild( altitudeVendorOptionElem );
648 QDomElement azimutVendorOptionElem = doc.createElement( QStringLiteral(
"sld:VendorOption" ) );
649 azimutVendorOptionElem.setAttribute( QStringLiteral(
"name" ), QStringLiteral(
"azimuth" ) );
650 azimutVendorOptionElem.appendChild( doc.createTextNode( QString::number(
azimuth() ) ) );
651 shadedReliefElem.appendChild( azimutVendorOptionElem );
654 QDomElement multidirectionalVendorOptionElem = doc.createElement( QStringLiteral(
"sld:VendorOption" ) );
655 multidirectionalVendorOptionElem.setAttribute( QStringLiteral(
"name" ), QStringLiteral(
"multidirectional" ) );
656 multidirectionalVendorOptionElem.appendChild( doc.createTextNode( QString::number(
multiDirectional() ) ) );
657 shadedReliefElem.appendChild( multidirectionalVendorOptionElem );
virtual int bandCount() const =0
Gets number of bands.
bool isCanceled() const
Tells whether the operation has been canceled already.
static QString sourcePath()
Returns the base path to OpenCL program directory.
int band() const
Returns the band used by the renderer.
int alphaValue(double value, int globalTransparency=255) const
Returns the transparency value for a single value pixel.
A rectangle specified with double values.
static void setEnabled(bool enabled)
Set the OpenCL user setting to enabled.
static QString sourceFromBaseName(const QString &baseName)
Returns the full path to a an OpenCL source file from the baseName ('.cl' extension is automatically ...
void setMultiDirectional(bool isMultiDirectional)
Sets whether to render using a multi-directional hillshade algorithm.
This class is a composition of two QSettings instances:
QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback=nullptr) override
Read block of data using given extent and size.
double altitude() const
Returns the angle of the light source over the raster.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
void setProgress(double progress)
Sets the current progress for the feedback object.
virtual QgsRasterInterface * input() const
Current input.
static bool available()
Checks whether a suitable OpenCL platform and device is available on this system and initialize the Q...
QMap< QString, QString > QgsStringMap
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
static cl::Context context()
Context factory.
void setBand(int bandNo)
Sets the band used by the renderer.
QgsRasterTransparency * mRasterTransparency
Raster transparency per color or value. Overwrites global alpha value.
void setZFactor(double zfactor)
Set the Z scaling factor of the result image.
static const QRgb NODATA_COLOR
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
virtual void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props=QgsStringMap()) const
Used from subclasses to create SLD Rule elements following SLD v1.0 specs.
QList< int > usesBands() const override
Returns a list of band numbers used by the renderer.
virtual QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback=nullptr)=0
Read block of data using given extent and size.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
void copyCommonProperties(const QgsRasterRenderer *other, bool copyMinMaxOrigin=true)
Copies common properties like opacity / transparency data from other renderer.
static bool enabled()
Returns true if OpenCL is enabled in the user settings.
bool isEmpty() const
True if there are no entries in the pixel lists except the nodata value.
A renderer for generating live hillshade models.
QgsHillshadeRenderer(QgsRasterInterface *input, int band, double lightAzimuth, double lightAltitude)
A renderer for generating live hillshade models.
int mAlphaBand
Read alpha value from band.
void readXml(const QDomElement &rendererElem) override
Sets base class members from xml. Usually called from create() methods of subclasses.
Base class for processing filters like renderers, reprojector, resampler etc.
static QString errorText(const int errorCode)
Returns a string representation from an OpenCL errorCode.
unsigned long long qgssize
Qgssize is used instead of size_t, because size_t is stdlib type, unknown by SIP, and it would be har...
QgsHillshadeRenderer * clone() const override
Clone itself, create deep copy.
static Q_DECL_DEPRECATED cl::Program buildProgram(const cl::Context &context, const QString &source, ExceptionBehavior exceptionBehavior=Catch)
Build the program from source in the given context and depending on exceptionBehavior can throw or ca...
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props=QgsStringMap()) const override
Used from subclasses to create SLD Rule elements following SLD v1.0 specs.
double azimuth() const
Returns the direction of the light over the raster between 0-360.
static QLatin1String LOGMESSAGE_TAG
OpenCL string for message logs.
virtual QgsRectangle extent() const
Gets the extent of the interface.
void writeXml(QDomDocument &doc, QDomElement &parentElem) const override
Write base class members to xml.
double mOpacity
Global alpha value (0-1)
bool multiDirectional() const
Returns true if the renderer is using multi-directional hillshading.
double zFactor() const
Returns the Z scaling factor.
static cl::CommandQueue commandQueue()
Create an OpenCL command queue from the default context.
double width() const
Returns the width of the rectangle.
static QgsRasterRenderer * create(const QDomElement &elem, QgsRasterInterface *input)
Factory method to create a new renderer.
QgsRasterInterface * mInput
void _writeXml(QDomDocument &doc, QDomElement &rasterRendererElem) const
Write upper class info into rasterrenderer element (called by writeXml method of subclasses) ...
Feedback object tailored for raster block reading.
Defines a QGIS exception class.
Raster renderer pipe that applies colors to a raster.
double height() const
Returns the height of the rectangle.