X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dali%2Finternal%2Fimaging%2Fcommon%2Fimage-operations.cpp;h=b44a0242034257b1c6b33c5b455db272cd928870;hb=15cb030e9396d29dab2de0bd298c1daf810bcf5f;hp=e825c7d2df1c33bf78a7b02e234a315b874be42d;hpb=454e03e2413be60a42ae4bdd2976b0e1d6a20945;p=platform%2Fcore%2Fuifw%2Fdali-adaptor.git diff --git a/dali/internal/imaging/common/image-operations.cpp b/dali/internal/imaging/common/image-operations.cpp old mode 100644 new mode 100755 index e825c7d..b44a024 --- a/dali/internal/imaging/common/image-operations.cpp +++ b/dali/internal/imaging/common/image-operations.cpp @@ -1,5 +1,5 @@ /* - * 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. @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +52,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 +498,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 ) @@ -892,9 +1268,9 @@ void HalveScanlineInPlaceRGB888( unsigned char * const pixels, const unsigned in 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( AverageComponent( c11, c21 ) ); + pixels[outPixel * 3 + 1] = static_cast( AverageComponent( c12, c22 ) ); + pixels[outPixel * 3 + 2] = static_cast( AverageComponent( c13, c23 ) ); } } @@ -945,8 +1321,8 @@ void HalveScanlineInPlace2Bytes( unsigned char * const pixels, const unsigned in 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( AverageComponent( c11, c21 ) ); + pixels[outPixel * 2 + 1] = static_cast( AverageComponent( c12, c22 ) ); } } @@ -963,7 +1339,7 @@ void HalveScanlineInPlace1Byte( unsigned char * const pixels, const unsigned int const unsigned int c2 = pixels[pixel + 1]; // Save the averaged byte pixel component: - pixels[outPixel] = AverageComponent( c1, c2 ); + pixels[outPixel] = static_cast( AverageComponent( c1, c2 ) ); } } @@ -980,7 +1356,7 @@ void AverageScanlines1( const unsigned char * const scanline1, for( unsigned int component = 0; component < width; ++component ) { - outputScanline[component] = AverageComponent( scanline1[component], scanline2[component] ); + outputScanline[component] = static_cast( AverageComponent( scanline1[component], scanline2[component] ) ); } } @@ -993,7 +1369,7 @@ void AverageScanlines2( const unsigned char * const scanline1, for( unsigned int component = 0; component < width * 2; ++component ) { - outputScanline[component] = AverageComponent( scanline1[component], scanline2[component] ); + outputScanline[component] = static_cast( AverageComponent( scanline1[component], scanline2[component] ) ); } } @@ -1006,7 +1382,7 @@ void AverageScanlines3( const unsigned char * const scanline1, for( unsigned int component = 0; component < width * 3; ++component ) { - outputScanline[component] = AverageComponent( scanline1[component], scanline2[component] ); + outputScanline[component] = static_cast( AverageComponent( scanline1[component], scanline2[component] ) ); } } @@ -1094,7 +1470,7 @@ void DownscaleInPlacePow2( unsigned char * const pixels, } 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." ); } } } @@ -1308,9 +1684,9 @@ void PointSample3BPP( const uint8_t * inPixels, ///@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( c0 ); + outScanline[outX + 1] = static_cast( c1 ); + outScanline[outX + 2] = static_cast( c2 ); // Increment the fixed-point input coordinate: inX += deltaX; @@ -1350,7 +1726,7 @@ void PointSample( const unsigned char * inPixels, } else { - DALI_ASSERT_DEBUG( false == "Inner branch conditions don't match outer branch." ); + DALI_ASSERT_DEBUG( 0 == "Inner branch conditions don't match outer branch." ); } } else @@ -1367,15 +1743,15 @@ namespace /** @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( 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( BilinearFilter1Component( tl.l, tr.l, bl.l, br.l, fractBlendHorizontal, fractBlendVertical ) ); + pixel.a = static_cast( BilinearFilter1Component( tl.a, tr.a, bl.a, br.a, fractBlendHorizontal, fractBlendVertical ) ); return pixel; } @@ -1383,18 +1759,18 @@ inline Pixel2Bytes BilinearFilter2Bytes( Pixel2Bytes tl, Pixel2Bytes tr, Pixel2B 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( BilinearFilter1Component( tl.r, tr.r, bl.r, br.r, fractBlendHorizontal, fractBlendVertical ) ); + pixel.g = static_cast( BilinearFilter1Component( tl.g, tr.g, bl.g, br.g, fractBlendHorizontal, fractBlendVertical ) ); + pixel.b = static_cast( 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( (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; } @@ -1402,10 +1778,10 @@ inline PixelRGB565 BilinearFilterRGB565( PixelRGB565 tl, PixelRGB565 tr, PixelRG 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( BilinearFilter1Component( tl.r, tr.r, bl.r, br.r, fractBlendHorizontal, fractBlendVertical ) ); + pixel.g = static_cast( BilinearFilter1Component( tl.g, tr.g, bl.g, br.g, fractBlendHorizontal, fractBlendVertical ) ); + pixel.b = static_cast( BilinearFilter1Component( tl.b, tr.b, bl.b, br.b, fractBlendHorizontal, fractBlendVertical ) ); + pixel.a = static_cast( BilinearFilter1Component( tl.a, tr.a, bl.a, br.a, fractBlendHorizontal, fractBlendVertical ) ); return pixel; } @@ -1576,8 +1952,8 @@ void Resample( const unsigned char * __restrict__ inPixels, } } - Resampler* resamplers[numChannels]; - Vector samples[numChannels]; + std::vector resamplers( numChannels ); + std::vector> samples(numChannels); const int srcWidth = inputDimensions.GetWidth(); const int srcHeight = inputDimensions.GetHeight(); @@ -1759,7 +2135,7 @@ void LinearSample( const unsigned char * __restrict__ inPixels, } else { - DALI_ASSERT_DEBUG( false == "Inner branch conditions don't match outer branch." ); + DALI_ASSERT_DEBUG( 0 == "Inner branch conditions don't match outer branch." ); } } else @@ -1768,6 +2144,270 @@ 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 ) + { + 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 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( fabs( angleTangent ) * static_cast( heightIn ) ); + heightOut = heightIn; + + // Allocate the buffer for the 1st shear + pixelsOut = static_cast( 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( y ) ) : ( 0.5f + static_cast( y ) - static_cast( heightOut ) ) ); + + const int intShear = static_cast( floor( shear ) ); + HorizontalSkew( firstHorizontalSkewPixelsIn, widthIn, pixelSize, pixelsOut, widthOut, y, intShear, shear - static_cast( 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( 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 ) + { + 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( 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( tmpPixelsInPtr.get(), tmpWidthIn, tmpHeightIn, pixelSize, pixelsOut, widthOut, heightOut, column, shear, offset - static_cast( 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( 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 ) + { + 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( 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( tmpPixelsInPtr.get(), tmpWidthIn, pixelSize, pixelsOut, widthOut, y, shear, offset - static_cast( 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( absRadians * static_cast( heightIn ) ); + heightOut = heightIn; + + // Allocate the buffer for the shear. + pixelsOut = static_cast( 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( y ) ) : ( 0.5f + static_cast( y ) - static_cast( heightOut ) ) ); + + const int intShear = static_cast( floor( shear ) ); + HorizontalSkew( pixelsIn, widthIn, pixelSize, pixelsOut, widthOut, y, intShear, shear - static_cast( intShear ) ); + } +} + } /* namespace Platform */ } /* namespace Internal */ } /* namespace Dali */