Revert "[Tizen](ATSPI) squashed implementation"
[platform/core/uifw/dali-adaptor.git] / dali / internal / imaging / common / image-operations.cpp
index 09c430a..b44a024 100755 (executable)
@@ -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 <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>
@@ -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<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 )
@@ -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<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 ) );
   }
 }
 
@@ -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<unsigned char>( AverageComponent( c11, c21 ) );
+    pixels[outPixel * 2 + 1] = static_cast<unsigned char>( 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<unsigned char>( 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<unsigned char>( 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<unsigned char>( 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<unsigned char>( 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<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;
@@ -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<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;
 }
 
@@ -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<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;
 }
 
@@ -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<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;
 }
 
@@ -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<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 */