/*
- * Copyright (c) 2018 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>
*
* @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[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.
*/
-void Rotate90( const uint8_t* const pixelsIn,
+bool Rotate90( const uint8_t* const pixelsIn,
unsigned int widthIn,
unsigned int heightIn,
unsigned int pixelSize,
// 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 )
}
}
}
+
+ return true;
}
/**
*
* @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.
*/
-void Rotate180( const uint8_t* const pixelsIn,
+bool Rotate180( const uint8_t* const pixelsIn,
unsigned int widthIn,
unsigned int heightIn,
unsigned int pixelSize,
{
// 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 )
}
}
}
+
+ return true;
}
/**
*
* @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[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.
*/
-void Rotate270( const uint8_t* const pixelsIn,
+bool Rotate270( const uint8_t* const pixelsIn,
unsigned int widthIn,
unsigned int heightIn,
unsigned int pixelSize,
// 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 )
}
}
}
+
+ return true;
}
/**
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] ) );
}
}
///@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;
/** @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;
}
// Rotate image by 90 degrees into temporary image,
// so it requires only an extra rotation angle
// of -45.0 .. +45.0 to complete rotation.
- Rotate90( pixelsIn,
- widthIn,
- heightIn,
- pixelSize,
- pixelsOut,
- widthOut,
- heightOut );
+ 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;
- fastRotationPerformed = true;
}
else if( ( radians > RAD_135 ) && ( radians <= RAD_225 ) )
{
// so it requires only an extra rotation angle
// of -45.0 .. +45.0 to complete rotation.
- Rotate180( pixelsIn,
- widthIn,
- heightIn,
- pixelSize,
- pixelsOut );
+ 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;
- fastRotationPerformed = true;
}
else if( ( radians > RAD_225 ) && ( radians <= RAD_315 ) )
{
// so it requires only an extra rotation angle
// of -45.0 .. +45.0 to complete rotation.
- Rotate270( pixelsIn,
- widthIn,
- heightIn,
- pixelSize,
- pixelsOut,
- widthOut,
- heightOut );
+ 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;
- fastRotationPerformed = true;
}
if( fabs( radians ) < Dali::Math::MACHINE_EPSILON_10 )
return;
}
- const uint8_t* const firstHorizontalSkwePixelsIn = fastRotationPerformed ? pixelsOut : pixelsIn;
- uint8_t* tmpFirstHorizontalSkwePixelsIn = fastRotationPerformed ? pixelsOut : nullptr; // keep the pointer to free the memory.
+ 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;
// 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( firstHorizontalSkwePixelsIn, widthIn, pixelSize, pixelsOut, widthOut, y, intShear, shear - static_cast<float>( intShear ) );
+ HorizontalSkew( firstHorizontalSkewPixelsIn, widthIn, pixelSize, pixelsOut, widthOut, y, intShear, shear - static_cast<float>( intShear ) );
}
- // Free the memory allocated by the 'Fast Rotations'.
- free( tmpFirstHorizontalSkwePixelsIn );
-
- uint8_t* tmpPixelsIn = pixelsOut;
+ // 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;
// 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 ) ) );
for( column = 0u; column < widthOut; ++column, offset -= angleSinus )
{
const int shear = static_cast<int>( floor( offset ) );
- VerticalSkew( tmpPixelsIn, tmpWidthIn, tmpHeightIn, pixelSize, pixelsOut, widthOut, heightOut, column, shear, offset - static_cast<float>( shear ) );
+ VerticalSkew( tmpPixelsInPtr.get(), tmpWidthIn, tmpHeightIn, pixelSize, pixelsOut, widthOut, heightOut, column, shear, offset - static_cast<float>( shear ) );
}
-
- // Free the memory allocated by the 'First Horizontal Skew'.
- free( tmpPixelsIn );
-
+ // 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
- tmpPixelsIn = pixelsOut;
+ tmpPixelsInPtr.reset( pixelsOut );
tmpWidthIn = widthOut;
tmpHeightIn = heightOut;
pixelsOut = nullptr;
// 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( tmpPixelsIn, tmpWidthIn, pixelSize, pixelsOut, widthOut, y, shear, offset - static_cast<float>( shear ) );
+ HorizontalSkew( tmpPixelsInPtr.get(), tmpWidthIn, pixelSize, pixelsOut, widthOut, y, shear, offset - static_cast<float>( shear ) );
}
- // Free the memory allocated by the 'First Horizontal Skew'.
- free( tmpPixelsIn );
+ // 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;
- // @note Allocated memory by the last 'Horizontal Skew' has to be freed by the called to this function.
+ 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 */