From 6774f439cf1f339c40364110cb2eebd185bff000 Mon Sep 17 00:00:00 2001 From: Victor Cebollada Date: Fri, 31 Aug 2018 09:06:55 +0100 Subject: [PATCH] [4.0] Rotate() method added to the PixelBuffer. * Need to rotate pixel buffers i.e rotate an emoji for circular text. * Implemented a rotate by shear algorithm. Change-Id: I746db8ac3004900fd60655e12695bc817e176882 Signed-off-by: Victor Cebollada --- adaptors/common/pixel-buffer-impl.cpp | 72 +++ adaptors/common/pixel-buffer-impl.h | 6 +- .../devel-api/adaptor-framework/pixel-buffer.cpp | 7 +- .../devel-api/adaptor-framework/pixel-buffer.h | 15 +- .../portable/image-operations.cpp | 600 ++++++++++++++++++++- platform-abstractions/portable/image-operations.h | 23 +- 6 files changed, 717 insertions(+), 6 deletions(-) diff --git a/adaptors/common/pixel-buffer-impl.cpp b/adaptors/common/pixel-buffer-impl.cpp index b5b5ca4..fbf72a1 100644 --- a/adaptors/common/pixel-buffer-impl.cpp +++ b/adaptors/common/pixel-buffer-impl.cpp @@ -38,6 +38,11 @@ namespace Internal namespace Adaptor { +namespace +{ +const float TWO_PI = 2.f * Math::PI; ///< 360 degrees in radians +} // namespace + PixelBuffer::PixelBuffer( unsigned char* buffer, unsigned int bufferSize, unsigned int width, @@ -211,6 +216,73 @@ void PixelBuffer::AllocateFixedSize( uint32_t size ) mBufferSize = size; } +void PixelBuffer::Rotate( Degree angle ) +{ + // Check first if Rotate() can perform the operation in the current pixel buffer. + + bool validPixelFormat = false; + switch( mPixelFormat ) + { + case Pixel::A8: + case Pixel::L8: + case Pixel::LA88: + case Pixel::RGB888: + case Pixel::RGB8888: + case Pixel::BGR8888: + case Pixel::RGBA8888: + case Pixel::BGRA8888: // FALL THROUGH + { + validPixelFormat = true; + break; + } + default: + { + // This pixel format is not supported for this operation. + validPixelFormat = false; + break; + } + } + + if( !validPixelFormat ) + { + // Can't rotate the pixel buffer with the current pixel format. + DALI_LOG_ERROR( "Can't rotate the pixel buffer with the current pixel format\n" ); + return; + } + + float radians = Radian( angle ).radian; + + // Transform the input angle into the range [0..2PI] + radians = fmod( radians, TWO_PI ); + radians += ( radians < 0.f ) ? TWO_PI : 0.f; + + if( radians < Dali::Math::MACHINE_EPSILON_10 ) + { + // Nothing to do if the angle is zero. + return; + } + + const unsigned int pixelSize = Pixel::GetBytesPerPixel( mPixelFormat ); + + uint8_t* pixelsOut = nullptr; + Platform::RotateByShear( mBuffer, + mWidth, + mHeight, + pixelSize, + radians, + pixelsOut, + mWidth, + mHeight ); + + // Release the memory of the current pixel buffer. + ReleaseBuffer(); + + // Set the new pixel buffer. + mBuffer = pixelsOut; + pixelsOut = nullptr; + mBufferSize = mWidth * mHeight * pixelSize; +} + void PixelBuffer::ScaleAndCrop( float scaleFactor, ImageDimensions cropDimensions ) { ImageDimensions outDimensions( float(mWidth) * scaleFactor, diff --git a/adaptors/common/pixel-buffer-impl.h b/adaptors/common/pixel-buffer-impl.h index 35cb5a4..b8ad251 100644 --- a/adaptors/common/pixel-buffer-impl.h +++ b/adaptors/common/pixel-buffer-impl.h @@ -212,6 +212,11 @@ public: */ void AllocateFixedSize( uint32_t size ); + /** + * @copydoc Devel::PixelBuffer::Rotate() + */ + void Rotate( Degree angle ); + private: /* * Undefined copy constructor. @@ -265,7 +270,6 @@ private: */ static PixelBufferPtr NewResize( const PixelBuffer& inBuffer, ImageDimensions outDimensions ); - private: std::unique_ptr mMetadata; ///< Metadata fields diff --git a/adaptors/devel-api/adaptor-framework/pixel-buffer.cpp b/adaptors/devel-api/adaptor-framework/pixel-buffer.cpp index 5fe2a28..3e01857 100644 --- a/adaptors/devel-api/adaptor-framework/pixel-buffer.cpp +++ b/adaptors/devel-api/adaptor-framework/pixel-buffer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * Copyright (c) 2018 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. @@ -126,6 +126,11 @@ bool PixelBuffer::GetMetadata( Property::Map& metadata ) const return GetImplementation(*this).GetMetadata(metadata); } +void PixelBuffer::Rotate( Degree angle ) +{ + GetImplementation(*this).Rotate( angle ); +} + } // namespace Devel } // namespace Dali diff --git a/adaptors/devel-api/adaptor-framework/pixel-buffer.h b/adaptors/devel-api/adaptor-framework/pixel-buffer.h index 2290d54..b59214a 100644 --- a/adaptors/devel-api/adaptor-framework/pixel-buffer.h +++ b/adaptors/devel-api/adaptor-framework/pixel-buffer.h @@ -202,19 +202,30 @@ public: void Resize( uint16_t width, uint16_t height ); /** - * Multiplies the image's color values by the alpha value. This provides better + * @brief Multiplies the image's color values by the alpha value. This provides better * blending capability. */ void MultiplyColorByAlpha(); /** - * Returns Exif metadata as a property map + * @brief Returns Exif metadata as a property map * * @param[out] metadata Property map object to write into * @return True on success */ bool GetMetadata( Property::Map& metadata ) const; + /** + * @brief Rotates the pixel buffer by the given angle. + * + * @note Operation valid for pixel formats: A8, L8, LA88, RGB888, RGB8888, BGR8888, RGBA8888 and BGRA8888. Does nothing otherwise. + * @note The operation does nothing for angles equivalent to 0 degrees: -360, 360, 720, etc. + * @note If the pixel buffer does rotate, all the pointers to the internal pixel buffer retrieved by the method GetPixelBuffer() become invalid. + * + * @param[in] angle The angle in degrees. + */ + void Rotate( Degree angle ); + public: /** diff --git a/platform-abstractions/portable/image-operations.cpp b/platform-abstractions/portable/image-operations.cpp index a1408ce..fdf20c1 100644 --- a/platform-abstractions/portable/image-operations.cpp +++ b/platform-abstractions/portable/image-operations.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * Copyright (c) 2018 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. @@ -51,6 +51,11 @@ const unsigned int MAXIMUM_TARGET_BITMAP_SIZE( ( 1u << 16 ) - 1 ); 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; @@ -492,6 +497,376 @@ ImageDimensions CalculateDesiredDimensions( unsigned int bitmapWidth, unsigned i 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( 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( 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( 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( static_cast( 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( static_cast( 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 ) @@ -1768,6 +2143,229 @@ void LinearSample( const unsigned char * __restrict__ inPixels, } } +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 ) + { + // 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 ) + { + // 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 ) + { + // 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 firstHorizontalSkwePixelsIn = fastRotationPerformed ? pixelsOut : pixelsIn; + uint8_t* tmpFirstHorizontalSkwePixelsIn = fastRotationPerformed ? pixelsOut : nullptr; // keep the pointer to free the memory. + + // 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( fabs( angleTangent ) * static_cast( heightIn ) ); + heightOut = heightIn; + + // Allocate the buffer for the 1st shear + pixelsOut = static_cast( malloc( widthOut * heightOut * pixelSize ) ); + + if( nullptr == pixelsOut ) + { + // Free the memory allocated by the 'Fast Rotations'. + free( tmpFirstHorizontalSkwePixelsIn ); + + widthOut = 0u; + heightOut = 0u; + + // 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( y ) ) : ( 0.5f + static_cast( y ) - static_cast( heightOut ) ) ); + + const int intShear = static_cast( floor( shear ) ); + HorizontalSkew( firstHorizontalSkwePixelsIn, widthIn, pixelSize, pixelsOut, widthOut, y, intShear, shear - static_cast( intShear ) ); + } + + // Free the memory allocated by the 'Fast Rotations'. + free( tmpFirstHorizontalSkwePixelsIn ); + + uint8_t* tmpPixelsIn = 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( static_cast( widthIn ) * fabs( angleSinus ) + static_cast( heightIn ) * angleCosinus ); + + // Allocate the buffer for the 2nd shear + pixelsOut = static_cast( malloc( widthOut * heightOut * pixelSize ) ); + + if( nullptr == pixelsOut ) + { + // Free the memory allocated by the 'First Horizontal Skew'. + free( tmpPixelsIn ); + + widthOut = 0u; + heightOut = 0u; + + // Nothing else to do if the memory allocation fails. + return; + } + + // Variable skew offset + float offset = angleSinus * ( ( angleSinus > 0.f ) ? static_cast( widthIn - 1u ) : -( static_cast( widthIn ) - static_cast( widthOut ) ) ); + + unsigned int column = 0u; + for( column = 0u; column < widthOut; ++column, offset -= angleSinus ) + { + const int shear = static_cast( floor( offset ) ); + VerticalSkew( tmpPixelsIn, tmpWidthIn, tmpHeightIn, pixelSize, pixelsOut, widthOut, heightOut, column, shear, offset - static_cast( shear ) ); + } + + // Free the memory allocated by the 'First Horizontal Skew'. + free( tmpPixelsIn ); + + // Reset the input/output + tmpPixelsIn = pixelsOut; + tmpWidthIn = widthOut; + tmpHeightIn = heightOut; + pixelsOut = nullptr; + + /////////////////////////////////////// + // Perform 3rd shear (horizontal) + /////////////////////////////////////// + + // Calc 3rd shear (horizontal) destination image dimensions + widthOut = static_cast( static_cast( heightIn ) * fabs( angleSinus ) + static_cast( widthIn ) * angleCosinus ) + 1u; + + // Allocate the buffer for the 3rd shear + pixelsOut = static_cast( malloc( widthOut * heightOut * pixelSize ) ); + + if( nullptr == pixelsOut ) + { + // Free the memory allocated by the 'Vertical Skew'. + free( tmpPixelsIn ); + + widthOut = 0u; + heightOut = 0u; + + // Nothing else to do if the memory allocation fails. + return; + } + + offset = ( angleSinus >= 0.f ) ? -angleSinus * angleTangent * static_cast( widthIn - 1u ) : angleTangent * ( static_cast( widthIn - 1u ) * -angleSinus + ( 1.f - static_cast( heightOut ) ) ); + + for( unsigned int y = 0u; y < heightOut; ++y, offset += angleTangent ) + { + const int shear = static_cast( floor( offset ) ); + HorizontalSkew( tmpPixelsIn, tmpWidthIn, pixelSize, pixelsOut, widthOut, y, shear, offset - static_cast( shear ) ); + } + + // Free the memory allocated by the 'Vertical Skew'. + free( tmpPixelsIn ); + + // @note Allocated memory by the last 'Horizontal Skew' has to be freed by the caller to this function. +} + } /* namespace Platform */ } /* namespace Internal */ } /* namespace Dali */ diff --git a/platform-abstractions/portable/image-operations.h b/platform-abstractions/portable/image-operations.h index b790da5..b83cca1 100644 --- a/platform-abstractions/portable/image-operations.h +++ b/platform-abstractions/portable/image-operations.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * Copyright (c) 2018 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. @@ -387,6 +387,27 @@ void Resample( const unsigned char * __restrict__ inPixels, int numChannels, bool hasAlpha ); +/** + * @brief Rotates the input image with an implementation of the 'Rotate by Shear' algorithm. + * + * @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[in] radians The rotation angle in radians. + * @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. + */ +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 ); + /**@}*/ /** -- 2.7.4