/*
- * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <stddef.h>
#include <cmath>
#include <limits>
+#include <memory>
#include <dali/integration-api/debug.h>
#include <dali/public-api/common/dali-vector.h>
#include <dali/public-api/math/vector2.h>
const float DEFAULT_SOURCE_GAMMA = 1.75f; ///< Default source gamma value used in the Resampler() function. Partial gamma correction looks better on mips. Set to 1.0 to disable gamma correction.
const float FILTER_SCALE = 1.f; ///< Default filter scale value used in the Resampler() function. Filter scale - values < 1.0 cause aliasing, but create sharper looking mips.
+const float RAD_135 = Math::PI_2 + Math::PI_4; ///< 135 degrees in radians;
+const float RAD_225 = RAD_135 + Math::PI_2; ///< 225 degrees in radians;
+const float RAD_270 = 3.f * Math::PI_2; ///< 270 degrees in radians;
+const float RAD_315 = RAD_225 + Math::PI_2; ///< 315 degrees in radians;
+
using Integration::Bitmap;
using Integration::BitmapPtr;
typedef unsigned char PixelBuffer;
return ImageDimensions( bitmapWidth / float(bitmapHeight) * requestedHeight + 0.5f, requestedHeight );
}
+/**
+ * @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;
+ }
+ }
+}
+
} // namespace - unnamed
ImageDimensions CalculateDesiredDimensions( ImageDimensions rawDimensions, ImageDimensions requestedDimensions )
const unsigned int c23 = pixels[pixel * 3 + 5];
// Save the averaged byte pixel components:
- pixels[outPixel * 3] = AverageComponent( c11, c21 );
- pixels[outPixel * 3 + 1] = AverageComponent( c12, c22 );
- pixels[outPixel * 3 + 2] = AverageComponent( c13, c23 );
+ pixels[outPixel * 3] = static_cast<unsigned char>( AverageComponent( c11, c21 ) );
+ pixels[outPixel * 3 + 1] = static_cast<unsigned char>( AverageComponent( c12, c22 ) );
+ pixels[outPixel * 3 + 2] = static_cast<unsigned char>( AverageComponent( c13, c23 ) );
}
}
const unsigned int c22 = pixels[pixel * 2 + 3];
// Save the averaged byte pixel components:
- pixels[outPixel * 2] = AverageComponent( c11, c21 );
- pixels[outPixel * 2 + 1] = AverageComponent( c12, c22 );
+ pixels[outPixel * 2] = static_cast<unsigned char>( AverageComponent( c11, c21 ) );
+ pixels[outPixel * 2 + 1] = static_cast<unsigned char>( AverageComponent( c12, c22 ) );
}
}
const unsigned int c2 = pixels[pixel + 1];
// Save the averaged byte pixel component:
- pixels[outPixel] = AverageComponent( c1, c2 );
+ pixels[outPixel] = static_cast<unsigned char>( AverageComponent( c1, c2 ) );
}
}
for( unsigned int component = 0; component < width; ++component )
{
- outputScanline[component] = AverageComponent( scanline1[component], scanline2[component] );
+ outputScanline[component] = static_cast<unsigned char>( AverageComponent( scanline1[component], scanline2[component] ) );
}
}
for( unsigned int component = 0; component < width * 2; ++component )
{
- outputScanline[component] = AverageComponent( scanline1[component], scanline2[component] );
+ outputScanline[component] = static_cast<unsigned char>( AverageComponent( scanline1[component], scanline2[component] ) );
}
}
for( unsigned int component = 0; component < width * 3; ++component )
{
- outputScanline[component] = AverageComponent( scanline1[component], scanline2[component] );
+ outputScanline[component] = static_cast<unsigned char>( AverageComponent( scanline1[component], scanline2[component] ) );
}
}
}
else
{
- DALI_ASSERT_DEBUG( false == "Inner branch conditions don't match outer branch." );
+ DALI_ASSERT_DEBUG( false && "Inner branch conditions don't match outer branch." );
}
}
}
///@ToDo: Optimise - Benchmark one 32bit load that will be unaligned 2/3 of the time + 3 rotate and masks, versus these three aligned byte loads, versus using an RGB packed, aligned(1) struct and letting compiler pick a strategy.
// Output the pixel components:
- outScanline[outX] = c0;
- outScanline[outX + 1] = c1;
- outScanline[outX + 2] = c2;
+ outScanline[outX] = static_cast<uint8_t>( c0 );
+ outScanline[outX + 1] = static_cast<uint8_t>( c1 );
+ outScanline[outX + 2] = static_cast<uint8_t>( c2 );
// Increment the fixed-point input coordinate:
inX += deltaX;
}
else
{
- DALI_ASSERT_DEBUG( false == "Inner branch conditions don't match outer branch." );
+ DALI_ASSERT_DEBUG( false && "Inner branch conditions don't match outer branch." );
}
}
else
/** @brief Blend 4 pixels together using horizontal and vertical weights. */
inline uint8_t BilinearFilter1BPPByte( uint8_t tl, uint8_t tr, uint8_t bl, uint8_t br, unsigned int fractBlendHorizontal, unsigned int fractBlendVertical )
{
- return BilinearFilter1Component( tl, tr, bl, br, fractBlendHorizontal, fractBlendVertical );
+ return static_cast<uint8_t>( BilinearFilter1Component( tl, tr, bl, br, fractBlendHorizontal, fractBlendVertical ) );
}
/** @copydoc BilinearFilter1BPPByte */
inline Pixel2Bytes BilinearFilter2Bytes( Pixel2Bytes tl, Pixel2Bytes tr, Pixel2Bytes bl, Pixel2Bytes br, unsigned int fractBlendHorizontal, unsigned int fractBlendVertical )
{
Pixel2Bytes pixel;
- pixel.l = BilinearFilter1Component( tl.l, tr.l, bl.l, br.l, fractBlendHorizontal, fractBlendVertical );
- pixel.a = BilinearFilter1Component( tl.a, tr.a, bl.a, br.a, fractBlendHorizontal, fractBlendVertical );
+ pixel.l = static_cast<uint8_t>( BilinearFilter1Component( tl.l, tr.l, bl.l, br.l, fractBlendHorizontal, fractBlendVertical ) );
+ pixel.a = static_cast<uint8_t>( BilinearFilter1Component( tl.a, tr.a, bl.a, br.a, fractBlendHorizontal, fractBlendVertical ) );
return pixel;
}
inline Pixel3Bytes BilinearFilterRGB888( Pixel3Bytes tl, Pixel3Bytes tr, Pixel3Bytes bl, Pixel3Bytes br, unsigned int fractBlendHorizontal, unsigned int fractBlendVertical )
{
Pixel3Bytes pixel;
- pixel.r = BilinearFilter1Component( tl.r, tr.r, bl.r, br.r, fractBlendHorizontal, fractBlendVertical );
- pixel.g = BilinearFilter1Component( tl.g, tr.g, bl.g, br.g, fractBlendHorizontal, fractBlendVertical );
- pixel.b = BilinearFilter1Component( tl.b, tr.b, bl.b, br.b, fractBlendHorizontal, fractBlendVertical );
+ pixel.r = static_cast<uint8_t>( BilinearFilter1Component( tl.r, tr.r, bl.r, br.r, fractBlendHorizontal, fractBlendVertical ) );
+ pixel.g = static_cast<uint8_t>( BilinearFilter1Component( tl.g, tr.g, bl.g, br.g, fractBlendHorizontal, fractBlendVertical ) );
+ pixel.b = static_cast<uint8_t>( BilinearFilter1Component( tl.b, tr.b, bl.b, br.b, fractBlendHorizontal, fractBlendVertical ) );
return pixel;
}
/** @copydoc BilinearFilter1BPPByte */
inline PixelRGB565 BilinearFilterRGB565( PixelRGB565 tl, PixelRGB565 tr, PixelRGB565 bl, PixelRGB565 br, unsigned int fractBlendHorizontal, unsigned int fractBlendVertical )
{
- const PixelRGB565 pixel = (BilinearFilter1Component( tl >> 11u, tr >> 11u, bl >> 11u, br >> 11u, fractBlendHorizontal, fractBlendVertical ) << 11u) +
+ const PixelRGB565 pixel = static_cast<PixelRGB565>( (BilinearFilter1Component( tl >> 11u, tr >> 11u, bl >> 11u, br >> 11u, fractBlendHorizontal, fractBlendVertical ) << 11u) +
(BilinearFilter1Component( (tl >> 5u) & 63u, (tr >> 5u) & 63u, (bl >> 5u) & 63u, (br >> 5u) & 63u, fractBlendHorizontal, fractBlendVertical ) << 5u) +
- BilinearFilter1Component( tl & 31u, tr & 31u, bl & 31u, br & 31u, fractBlendHorizontal, fractBlendVertical );
+ BilinearFilter1Component( tl & 31u, tr & 31u, bl & 31u, br & 31u, fractBlendHorizontal, fractBlendVertical ) );
return pixel;
}
inline Pixel4Bytes BilinearFilter4Bytes( Pixel4Bytes tl, Pixel4Bytes tr, Pixel4Bytes bl, Pixel4Bytes br, unsigned int fractBlendHorizontal, unsigned int fractBlendVertical )
{
Pixel4Bytes pixel;
- pixel.r = BilinearFilter1Component( tl.r, tr.r, bl.r, br.r, fractBlendHorizontal, fractBlendVertical );
- pixel.g = BilinearFilter1Component( tl.g, tr.g, bl.g, br.g, fractBlendHorizontal, fractBlendVertical );
- pixel.b = BilinearFilter1Component( tl.b, tr.b, bl.b, br.b, fractBlendHorizontal, fractBlendVertical );
- pixel.a = BilinearFilter1Component( tl.a, tr.a, bl.a, br.a, fractBlendHorizontal, fractBlendVertical );
+ pixel.r = static_cast<uint8_t>( BilinearFilter1Component( tl.r, tr.r, bl.r, br.r, fractBlendHorizontal, fractBlendVertical ) );
+ pixel.g = static_cast<uint8_t>( BilinearFilter1Component( tl.g, tr.g, bl.g, br.g, fractBlendHorizontal, fractBlendVertical ) );
+ pixel.b = static_cast<uint8_t>( BilinearFilter1Component( tl.b, tr.b, bl.b, br.b, fractBlendHorizontal, fractBlendVertical ) );
+ pixel.a = static_cast<uint8_t>( BilinearFilter1Component( tl.a, tr.a, bl.a, br.a, fractBlendHorizontal, fractBlendVertical ) );
return pixel;
}
}
}
- Resampler* resamplers[numChannels];
- Vector<float> samples[numChannels];
+ std::vector<Resampler*> resamplers( numChannels );
+ std::vector<Vector<float>> samples(numChannels);
const int srcWidth = inputDimensions.GetWidth();
const int srcHeight = inputDimensions.GetHeight();
}
else
{
- DALI_ASSERT_DEBUG( false == "Inner branch conditions don't match outer branch." );
+ DALI_ASSERT_DEBUG( false && "Inner branch conditions don't match outer branch." );
}
}
else
}
}
+void RotateByShear( const uint8_t* const pixelsIn,
+ unsigned int widthIn,
+ unsigned int heightIn,
+ unsigned int pixelSize,
+ float radians,
+ uint8_t*& pixelsOut,
+ unsigned int& widthOut,
+ unsigned int& heightOut )
+{
+ // @note Code got from https://www.codeproject.com/Articles/202/High-quality-image-rotation-rotate-by-shear by Eran Yariv.
+
+ // Do first the fast rotations to transform the angle into a (-45..45] range.
+
+ float fastRotationPerformed = false;
+ if( ( radians > Math::PI_4 ) && ( radians <= RAD_135 ) )
+ {
+ // Angle in (45.0 .. 135.0]
+ // Rotate image by 90 degrees into temporary image,
+ // so it requires only an extra rotation angle
+ // of -45.0 .. +45.0 to complete rotation.
+ fastRotationPerformed = Rotate90( pixelsIn,
+ widthIn,
+ heightIn,
+ pixelSize,
+ pixelsOut,
+ widthOut,
+ heightOut );
+
+ if( !fastRotationPerformed )
+ {
+ DALI_LOG_INFO(gImageOpsLogFilter, Dali::Integration::Log::Verbose, "fast rotation failed\n");
+ // The fast rotation failed.
+ return;
+ }
+
+ radians -= Math::PI_2;
+ }
+ else if( ( radians > RAD_135 ) && ( radians <= RAD_225 ) )
+ {
+ // Angle in (135.0 .. 225.0]
+ // Rotate image by 180 degrees into temporary image,
+ // so it requires only an extra rotation angle
+ // of -45.0 .. +45.0 to complete rotation.
+
+ fastRotationPerformed = Rotate180( pixelsIn,
+ widthIn,
+ heightIn,
+ pixelSize,
+ pixelsOut );
+
+ if( !fastRotationPerformed )
+ {
+ DALI_LOG_INFO(gImageOpsLogFilter, Dali::Integration::Log::Verbose, "fast rotation failed\n");
+ // The fast rotation failed.
+ return;
+ }
+
+ radians -= Math::PI;
+ widthOut = widthIn;
+ heightOut = heightIn;
+ }
+ else if( ( radians > RAD_225 ) && ( radians <= RAD_315 ) )
+ {
+ // Angle in (225.0 .. 315.0]
+ // Rotate image by 270 degrees into temporary image,
+ // so it requires only an extra rotation angle
+ // of -45.0 .. +45.0 to complete rotation.
+
+ fastRotationPerformed = Rotate270( pixelsIn,
+ widthIn,
+ heightIn,
+ pixelSize,
+ pixelsOut,
+ widthOut,
+ heightOut );
+
+ if( !fastRotationPerformed )
+ {
+ DALI_LOG_INFO(gImageOpsLogFilter, Dali::Integration::Log::Verbose, "fast rotation failed\n");
+ // The fast rotation failed.
+ return;
+ }
+
+ radians -= RAD_270;
+ }
+
+ if( fabs( radians ) < Dali::Math::MACHINE_EPSILON_10 )
+ {
+ // Nothing else to do if the angle is zero.
+ // The rotation angle was 90, 180 or 270.
+
+ // @note Allocated memory by 'Fast Rotations', if any, has to be freed by the called to this function.
+ return;
+ }
+
+ const uint8_t* const firstHorizontalSkewPixelsIn = fastRotationPerformed ? pixelsOut : pixelsIn;
+ std::unique_ptr<uint8_t, void(*)(void*)> tmpPixelsInPtr( ( fastRotationPerformed ? pixelsOut : nullptr ), free );
+
+ // Reset the input/output
+ widthIn = widthOut;
+ heightIn = heightOut;
+ pixelsOut = nullptr;
+
+ const float angleSinus = sin( radians );
+ const float angleCosinus = cos( radians );
+ const float angleTangent = tan( 0.5f * radians );
+
+ ///////////////////////////////////////
+ // Perform 1st shear (horizontal)
+ ///////////////////////////////////////
+
+ // Calculate first shear (horizontal) destination image dimensions
+
+ widthOut = widthIn + static_cast<unsigned int>( fabs( angleTangent ) * static_cast<float>( heightIn ) );
+ heightOut = heightIn;
+
+ // Allocate the buffer for the 1st shear
+ pixelsOut = static_cast<uint8_t*>( malloc( widthOut * heightOut * pixelSize ) );
+
+ if( nullptr == pixelsOut )
+ {
+ widthOut = 0u;
+ heightOut = 0u;
+
+ DALI_LOG_INFO(gImageOpsLogFilter, Dali::Integration::Log::Verbose, "malloc failed to allocate memory\n");
+
+ // The deleter of the tmpPixelsInPtr unique pointer is called freeing the memory allocated by the 'Fast rotations'.
+ // Nothing else to do if the memory allocation fails.
+ return;
+ }
+
+ for( unsigned int y = 0u; y < heightOut; ++y )
+ {
+ const float shear = angleTangent * ( ( angleTangent >= 0.f ) ? ( 0.5f + static_cast<float>( y ) ) : ( 0.5f + static_cast<float>( y ) - static_cast<float>( heightOut ) ) );
+
+ const int intShear = static_cast<int>( floor( shear ) );
+ HorizontalSkew( firstHorizontalSkewPixelsIn, widthIn, pixelSize, pixelsOut, widthOut, y, intShear, shear - static_cast<float>( intShear ) );
+ }
+
+ // Reset the 'pixel in' pointer with the output of the 'First Horizontal Skew' and free the memory allocated by the 'Fast Rotations'.
+ tmpPixelsInPtr.reset( pixelsOut );
+ unsigned int tmpWidthIn = widthOut;
+ unsigned int tmpHeightIn = heightOut;
+
+ // Reset the input/output
+ pixelsOut = nullptr;
+
+ ///////////////////////////////////////
+ // Perform 2nd shear (vertical)
+ ///////////////////////////////////////
+
+ // Calc 2nd shear (vertical) destination image dimensions
+ heightOut = static_cast<unsigned int>( static_cast<float>( widthIn ) * fabs( angleSinus ) + static_cast<float>( heightIn ) * angleCosinus );
+
+ // Allocate the buffer for the 2nd shear
+ pixelsOut = static_cast<uint8_t*>( malloc( widthOut * heightOut * pixelSize ) );
+
+ if( nullptr == pixelsOut )
+ {
+ widthOut = 0u;
+ heightOut = 0u;
+
+ DALI_LOG_INFO(gImageOpsLogFilter, Dali::Integration::Log::Verbose, "malloc failed to allocate memory\n");
+ // The deleter of the tmpPixelsInPtr unique pointer is called freeing the memory allocated by the 'First Horizontal Skew'.
+ // Nothing else to do if the memory allocation fails.
+ return;
+ }
+
+ // Variable skew offset
+ float offset = angleSinus * ( ( angleSinus > 0.f ) ? static_cast<float>( widthIn - 1u ) : -( static_cast<float>( widthIn ) - static_cast<float>( widthOut ) ) );
+
+ unsigned int column = 0u;
+ for( column = 0u; column < widthOut; ++column, offset -= angleSinus )
+ {
+ const int shear = static_cast<int>( floor( offset ) );
+ VerticalSkew( tmpPixelsInPtr.get(), tmpWidthIn, tmpHeightIn, pixelSize, pixelsOut, widthOut, heightOut, column, shear, offset - static_cast<float>( shear ) );
+ }
+ // Reset the 'pixel in' pointer with the output of the 'Vertical Skew' and free the memory allocated by the 'First Horizontal Skew'.
+ // Reset the input/output
+ tmpPixelsInPtr.reset( pixelsOut );
+ tmpWidthIn = widthOut;
+ tmpHeightIn = heightOut;
+ pixelsOut = nullptr;
+
+ ///////////////////////////////////////
+ // Perform 3rd shear (horizontal)
+ ///////////////////////////////////////
+
+ // Calc 3rd shear (horizontal) destination image dimensions
+ widthOut = static_cast<unsigned int>( static_cast<float>( heightIn ) * fabs( angleSinus ) + static_cast<float>( widthIn ) * angleCosinus ) + 1u;
+
+ // Allocate the buffer for the 3rd shear
+ pixelsOut = static_cast<uint8_t*>( malloc( widthOut * heightOut * pixelSize ) );
+
+ if( nullptr == pixelsOut )
+ {
+ widthOut = 0u;
+ heightOut = 0u;
+
+ DALI_LOG_INFO(gImageOpsLogFilter, Dali::Integration::Log::Verbose, "malloc failed to allocate memory\n");
+ // The deleter of the tmpPixelsInPtr unique pointer is called freeing the memory allocated by the 'Vertical Skew'.
+ // Nothing else to do if the memory allocation fails.
+ return;
+ }
+
+ offset = ( angleSinus >= 0.f ) ? -angleSinus * angleTangent * static_cast<float>( widthIn - 1u ) : angleTangent * ( static_cast<float>( widthIn - 1u ) * -angleSinus + ( 1.f - static_cast<float>( heightOut ) ) );
+
+ for( unsigned int y = 0u; y < heightOut; ++y, offset += angleTangent )
+ {
+ const int shear = static_cast<int>( floor( offset ) );
+ HorizontalSkew( tmpPixelsInPtr.get(), tmpWidthIn, pixelSize, pixelsOut, widthOut, y, shear, offset - static_cast<float>( shear ) );
+ }
+
+ // The deleter of the tmpPixelsInPtr unique pointer is called freeing the memory allocated by the 'Vertical Skew'.
+ // @note Allocated memory by the last 'Horizontal Skew' has to be freed by the caller to this function.
+}
+
+void HorizontalShear( const uint8_t* const pixelsIn,
+ unsigned int widthIn,
+ unsigned int heightIn,
+ unsigned int pixelSize,
+ float radians,
+ uint8_t*& pixelsOut,
+ unsigned int& widthOut,
+ unsigned int& heightOut )
+{
+ // Calculate the destination image dimensions.
+
+ const float absRadians = fabs( radians );
+
+ if( absRadians > Math::PI_4 )
+ {
+ // Can't shear more than 45 degrees.
+ widthOut = 0u;
+ heightOut = 0u;
+
+ DALI_LOG_INFO( gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Can't shear more than 45 degrees (PI/4 radians). radians : %f\n", radians );
+ return;
+ }
+
+ widthOut = widthIn + static_cast<unsigned int>( absRadians * static_cast<float>( heightIn ) );
+ heightOut = heightIn;
+
+ // Allocate the buffer for the shear.
+ pixelsOut = static_cast<uint8_t*>( malloc( widthOut * heightOut * pixelSize ) );
+
+ if( nullptr == pixelsOut )
+ {
+ widthOut = 0u;
+ heightOut = 0u;
+
+ DALI_LOG_INFO( gImageOpsLogFilter, Dali::Integration::Log::Verbose, "malloc failed to allocate memory\n" );
+ return;
+ }
+
+ for( unsigned int y = 0u; y < heightOut; ++y )
+ {
+ const float shear = radians * ( ( radians >= 0.f ) ? ( 0.5f + static_cast<float>( y ) ) : ( 0.5f + static_cast<float>( y ) - static_cast<float>( heightOut ) ) );
+
+ const int intShear = static_cast<int>( floor( shear ) );
+ HorizontalSkew( pixelsIn, widthIn, pixelSize, pixelsOut, widthOut, y, intShear, shear - static_cast<float>( intShear ) );
+ }
+}
+
} /* namespace Platform */
} /* namespace Internal */
} /* namespace Dali */