+/**
+ * @brief Rotates the given buffer @p pixelsIn 90 degrees counter clockwise.
+ *
+ * @note It allocates memory for the returned @p pixelsOut buffer.
+ * @note Code got from https://www.codeproject.com/Articles/202/High-quality-image-rotation-rotate-by-shear by Eran Yariv.
+ * @note It may fail if malloc() fails to allocate memory.
+ *
+ * @param[in] pixelsIn The input buffer.
+ * @param[in] widthIn The width of the input buffer.
+ * @param[in] heightIn The height of the input buffer.
+ * @param[in] pixelSize The size of the pixel.
+ * @param[out] pixelsOut The rotated output buffer.
+ * @param[out] widthOut The width of the output buffer.
+ * @param[out] heightOut The height of the output buffer.
+ *
+ * @return Whether the rotation succeded.
+ */
+bool Rotate90( const uint8_t* const pixelsIn,
+ unsigned int widthIn,
+ unsigned int heightIn,
+ unsigned int pixelSize,
+ uint8_t*& pixelsOut,
+ unsigned int& widthOut,
+ unsigned int& heightOut )
+{
+ // The new size of the image.
+ widthOut = heightIn;
+ heightOut = widthIn;
+
+ // Allocate memory for the rotated buffer.
+ pixelsOut = static_cast<uint8_t*>( malloc ( widthOut * heightOut * pixelSize ) );
+ if( nullptr == pixelsOut )
+ {
+ widthOut = 0u;
+ heightOut = 0u;
+
+ // Return if the memory allocations fails.
+ return false;
+ }
+
+ // Rotate the buffer.
+ for( unsigned int y = 0u; y < heightIn; ++y )
+ {
+ const unsigned int srcLineIndex = y * widthIn;
+ const unsigned int dstX = y;
+ for( unsigned int x = 0u; x < widthIn; ++x )
+ {
+ const unsigned int dstY = heightOut - x - 1u;
+ const unsigned int dstIndex = pixelSize * ( dstY * widthOut + dstX );
+ const unsigned int srcIndex = pixelSize * ( srcLineIndex + x );
+
+ for( unsigned int channel = 0u; channel < pixelSize; ++channel )
+ {
+ *( pixelsOut + dstIndex + channel ) = *( pixelsIn + srcIndex + channel );
+ }
+ }
+ }
+
+ return true;
+}
+
+/**
+ * @brief Rotates the given buffer @p pixelsIn 180 degrees counter clockwise.
+ *
+ * @note It allocates memory for the returned @p pixelsOut buffer.
+ * @note Code got from https://www.codeproject.com/Articles/202/High-quality-image-rotation-rotate-by-shear by Eran Yariv.
+ * @note It may fail if malloc() fails to allocate memory.
+ *
+ * @param[in] pixelsIn The input buffer.
+ * @param[in] widthIn The width of the input buffer.
+ * @param[in] heightIn The height of the input buffer.
+ * @param[in] pixelSize The size of the pixel.
+ * @param[out] pixelsOut The rotated output buffer.
+ *
+ * @return Whether the rotation succeded.
+ */
+bool Rotate180( const uint8_t* const pixelsIn,
+ unsigned int widthIn,
+ unsigned int heightIn,
+ unsigned int pixelSize,
+ uint8_t*& pixelsOut )
+{
+ // Allocate memory for the rotated buffer.
+ pixelsOut = static_cast<uint8_t*>( malloc ( widthIn * heightIn * pixelSize ) );
+ if( nullptr == pixelsOut )
+ {
+ // Return if the memory allocations fails.
+ return false;
+ }
+
+ // Rotate the buffer.
+ for( unsigned int y = 0u; y < heightIn; ++y )
+ {
+ const unsigned int srcLineIndex = y * widthIn;
+ const unsigned int dstY = heightIn - y - 1u;
+ for( unsigned int x = 0u; x < widthIn; ++x )
+ {
+ const unsigned int dstX = widthIn - x - 1u;
+ const unsigned int dstIndex = pixelSize * ( dstY * widthIn + dstX );
+ const unsigned int srcIndex = pixelSize * ( srcLineIndex + x );
+
+ for( unsigned int channel = 0u; channel < pixelSize; ++channel )
+ {
+ *( pixelsOut + dstIndex + channel ) = *( pixelsIn + srcIndex + channel );
+ }
+ }
+ }
+
+ return true;
+}
+
+/**
+ * @brief Rotates the given buffer @p pixelsIn 270 degrees counter clockwise.
+ *
+ * @note It allocates memory for the returned @p pixelsOut buffer.
+ * @note Code got from https://www.codeproject.com/Articles/202/High-quality-image-rotation-rotate-by-shear by Eran Yariv.
+ * @note It may fail if malloc() fails to allocate memory.
+ *
+ * @param[in] pixelsIn The input buffer.
+ * @param[in] widthIn The width of the input buffer.
+ * @param[in] heightIn The height of the input buffer.
+ * @param[in] pixelSize The size of the pixel.
+ * @param[out] pixelsOut The rotated output buffer.
+ * @param[out] widthOut The width of the output buffer.
+ * @param[out] heightOut The height of the output buffer.
+ *
+ * @return Whether the rotation succeded.
+ */
+bool Rotate270( const uint8_t* const pixelsIn,
+ unsigned int widthIn,
+ unsigned int heightIn,
+ unsigned int pixelSize,
+ uint8_t*& pixelsOut,
+ unsigned int& widthOut,
+ unsigned int& heightOut )
+{
+ // The new size of the image.
+ widthOut = heightIn;
+ heightOut = widthIn;
+
+ // Allocate memory for the rotated buffer.
+ pixelsOut = static_cast<uint8_t*>( malloc ( widthOut * heightOut * pixelSize ) );
+ if( nullptr == pixelsOut )
+ {
+ widthOut = 0u;
+ heightOut = 0u;
+
+ // Return if the memory allocations fails.
+ return false;
+ }
+
+ // Rotate the buffer.
+ for( unsigned int y = 0u; y < heightIn; ++y )
+ {
+ const unsigned int srcLineIndex = y * widthIn;
+ const unsigned int dstX = widthOut - y - 1u;
+ for( unsigned int x = 0u; x < widthIn; ++x )
+ {
+ const unsigned int dstY = x;
+ const unsigned int dstIndex = pixelSize * ( dstY * widthOut + dstX );
+ const unsigned int srcIndex = pixelSize * ( srcLineIndex + x );
+
+ for( unsigned int channel = 0u; channel < pixelSize; ++channel )
+ {
+ *( pixelsOut + dstIndex + channel ) = *( pixelsIn + srcIndex + channel );
+ }
+ }
+ }
+
+ return true;
+}
+
+/**
+ * @brief Skews a row horizontally (with filtered weights)
+ *
+ * @note Limited to 45 degree skewing only.
+ * @note Code got from https://www.codeproject.com/Articles/202/High-quality-image-rotation-rotate-by-shear by Eran Yariv.
+ *
+ * @param[in] srcBufferPtr Pointer to the input pixel buffer.
+ * @param[in] srcWidth The width of the input pixel buffer.
+ * @param[in] pixelSize The size of the pixel.
+ * @param[in,out] dstPixelBuffer Pointer to the output pixel buffer.
+ * @param[in] dstWidth The width of the output pixel buffer.
+ * @param[in] row The row index.
+ * @param[in] offset The skew offset.
+ * @param[in] weight The relative weight of right pixel.
+ */
+void HorizontalSkew( const uint8_t* const srcBufferPtr,
+ int srcWidth,
+ unsigned int pixelSize,
+ uint8_t*& dstBufferPtr,
+ int dstWidth,
+ unsigned int row,
+ int offset,
+ float weight )
+{
+ if( offset > 0 )
+ {
+ // Fill gap left of skew with background.
+ memset( dstBufferPtr + row * pixelSize * dstWidth, 0u, pixelSize * offset );
+ }
+
+ unsigned char oldLeft[4u] = { 0u, 0u, 0u, 0u };
+
+ int i = 0;
+ for( i = 0u; i < srcWidth; ++i )
+ {
+ // Loop through row pixels
+ const unsigned int srcIndex = pixelSize * ( row * srcWidth + i );
+
+ unsigned char src[4u] = { 0u, 0u, 0u, 0u };
+ for( unsigned int channel = 0u; channel < pixelSize; ++channel )
+ {
+ src[channel] = *( srcBufferPtr + srcIndex + channel );
+ }
+
+ // Calculate weights
+ unsigned char left[4u] = { 0u, 0u, 0u, 0u };
+ for( unsigned int channel = 0u; channel < pixelSize; ++channel )
+ {
+ left[channel] = static_cast<unsigned char>( static_cast<float>( src[channel] ) * weight );
+
+ // Update left over on source
+ src[channel] -= ( left[channel] - oldLeft[channel] );
+ }
+
+ // Check boundaries
+ if( ( i + offset >= 0 ) && ( i + offset < dstWidth ) )
+ {
+ const unsigned int dstIndex = pixelSize * ( row * dstWidth + i + offset );
+
+ for( unsigned int channel = 0u; channel < pixelSize; ++channel )
+ {
+ *( dstBufferPtr + dstIndex + channel ) = src[channel];
+ }
+ }
+
+ // Save leftover for next pixel in scan
+ for( unsigned int channel = 0u; channel < pixelSize; ++channel )
+ {
+ oldLeft[channel] = left[channel];
+ }
+ }
+
+ // Go to rightmost point of skew
+ i += offset;
+ if( i < dstWidth )
+ {
+ // If still in image bounds, put leftovers there
+ const unsigned int dstIndex = pixelSize * ( row * dstWidth + i );
+
+ for( unsigned int channel = 0u; channel < pixelSize; ++channel )
+ {
+ *( dstBufferPtr + dstIndex + channel ) = oldLeft[channel];
+ }
+
+ // Clear to the right of the skewed line with background
+ ++i;
+ memset( dstBufferPtr + pixelSize * ( row * dstWidth + i ), 0u, pixelSize * ( dstWidth - i ) );
+ }
+}
+
+/**
+ * @brief Skews a column vertically (with filtered weights)
+ *
+ * @note Limited to 45 degree skewing only.
+ * @note Code got from https://www.codeproject.com/Articles/202/High-quality-image-rotation-rotate-by-shear by Eran Yariv.
+ *
+ * @param[in] srcBufferPtr Pointer to the input pixel buffer.
+ * @param[in] srcWidth The width of the input pixel buffer.
+ * @param[in] srcHeight The height of the input pixel buffer.
+ * @param[in] pixelSize The size of the pixel.
+ * @param[in,out] dstPixelBuffer Pointer to the output pixel buffer.
+ * @param[in] dstWidth The width of the output pixel buffer.
+ * @param[in] dstHeight The height of the output pixel buffer.
+ * @param[in] column The column index.
+ * @param[in] offset The skew offset.
+ * @param[in] weight The relative weight of uppeer pixel.
+ */
+void VerticalSkew( const uint8_t* const srcBufferPtr,
+ int srcWidth,
+ int srcHeight,
+ unsigned int pixelSize,
+ uint8_t*& dstBufferPtr,
+ int dstWidth,
+ int dstHeight,
+ unsigned int column,
+ int offset,
+ float weight )
+{
+ for( int i = 0; i < offset; ++i )
+ {
+ // Fill gap above skew with background
+ const unsigned int dstIndex = pixelSize * ( i * dstWidth + column );
+
+ for( unsigned int channel = 0u; channel < pixelSize; ++channel )
+ {
+ *( dstBufferPtr + dstIndex + channel ) = 0u;
+ }
+ }
+
+ unsigned char oldLeft[4u] = { 0u, 0u, 0u, 0u };
+
+ int yPos = 0;
+ int i = 0;
+ for( i = 0; i < srcHeight; ++i )
+ {
+ // Loop through column pixels
+ const unsigned int srcIndex = pixelSize * ( i * srcWidth + column );
+
+ unsigned char src[4u] = { 0u, 0u, 0u, 0u };
+ for( unsigned int channel = 0u; channel < pixelSize; ++channel )
+ {
+ src[channel] = *( srcBufferPtr + srcIndex + channel );
+ }
+
+ yPos = i + offset;
+
+ // Calculate weights
+ unsigned char left[4u] = { 0u, 0u, 0u, 0u };
+ for( unsigned int channel = 0u; channel < pixelSize; ++channel )
+ {
+ left[channel] = static_cast<unsigned char>( static_cast<float>( src[channel] ) * weight );
+ // Update left over on source
+ src[channel] -= ( left[channel] - oldLeft[channel] );
+ }
+
+ // Check boundaries
+ if( ( yPos >= 0 ) && ( yPos < dstHeight ) )
+ {
+ const unsigned int dstIndex = pixelSize * ( yPos * dstWidth + column );
+
+ for( unsigned int channel = 0u; channel < pixelSize; ++channel )
+ {
+ *( dstBufferPtr + dstIndex + channel ) = src[channel];
+ }
+ }
+
+ // Save leftover for next pixel in scan
+ for( unsigned int channel = 0u; channel < pixelSize; ++channel )
+ {
+ oldLeft[channel] = left[channel];
+ }
+ }
+
+ // Go to bottom point of skew
+ i = yPos;
+ if( i < dstHeight )
+ {
+ // If still in image bounds, put leftovers there
+ const unsigned int dstIndex = pixelSize * ( i * dstWidth + column );
+
+ for( unsigned int channel = 0u; channel < pixelSize; ++channel )
+ {
+ *( dstBufferPtr + dstIndex + channel ) = oldLeft[channel];
+ }
+ }
+
+ while( ++i < dstHeight )
+ {
+ // Clear below skewed line with background
+ const unsigned int dstIndex = pixelSize * ( i * dstWidth + column );
+
+ for( unsigned int channel = 0u; channel < pixelSize; ++channel )
+ {
+ *( dstBufferPtr + dstIndex + channel ) = 0u;
+ }
+ }
+}
+