[4.0] PixelBuffer::Rotate() amendments.
[platform/core/uifw/dali-adaptor.git] / platform-abstractions / portable / image-operations.cpp
index 6a5ee4b..a54eae0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 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.
 #include <cstring>
 #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>
+#include <resampler.h>
+#include <image-loading.h>
 
 // INTERNAL INCLUDES
 
@@ -43,6 +48,15 @@ const uint8_t BORDER_FILL_VALUE( 0x00 );
 // A maximum size limit for newly created bitmaps. ( 1u << 16 ) - 1 is chosen as we are using 16bit words for dimensions.
 const unsigned int MAXIMUM_TARGET_BITMAP_SIZE( ( 1u << 16 ) - 1 );
 
+// Constants used by the ImageResampler.
+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;
@@ -152,7 +166,7 @@ inline void DebugAssertDualScanlineParameters( const uint8_t * const scanline1,
   DALI_ASSERT_DEBUG( scanline2 && "Null pointer." );
   DALI_ASSERT_DEBUG( outputScanline && "Null pointer." );
   DALI_ASSERT_DEBUG( ((scanline1 >= scanline2 + widthInComponents) || (scanline2 >= scanline1 + widthInComponents )) && "Scanlines alias." );
-  DALI_ASSERT_DEBUG( ((((void*)outputScanline) >= (void*)(scanline2 + widthInComponents)) || (((void*)scanline2) >= (void*)(scanline1 + widthInComponents))) && "Scanline 2 aliases output." );
+  DALI_ASSERT_DEBUG( ((outputScanline >= (scanline2 + widthInComponents)) || (scanline2 >= (scanline1 + widthInComponents))) && "Scanline 2 aliases output." );
 }
 
 /**
@@ -403,30 +417,17 @@ void CalculateBordersFromFittingMode(  ImageDimensions sourceSize, FittingMode::
 }
 
 /**
- * @brief Construct a bitmap with format and dimensions requested.
- */
-BitmapPtr MakeEmptyBitmap( Pixel::Format pixelFormat, unsigned int width, unsigned int height )
-{
-  DALI_ASSERT_DEBUG( Pixel::GetBytesPerPixel(pixelFormat) && "Compressed formats not supported." );
-
-  // Allocate a pixel buffer to hold the image passed in:
-  Integration::BitmapPtr newBitmap = Integration::Bitmap::New( Integration::Bitmap::BITMAP_2D_PACKED_PIXELS, ResourcePolicy::OWNED_DISCARD );
-  newBitmap->GetPackedPixelsProfile()->ReserveBuffer( pixelFormat, width, height, width, height );
-  return newBitmap;
-}
-
-/**
- * @brief Construct a bitmap object from a copy of the pixel array passed in.
+ * @brief Construct a pixel buffer object from a copy of the pixel array passed in.
  */
-BitmapPtr MakeBitmap( const uint8_t * const pixels, Pixel::Format pixelFormat, unsigned int width, unsigned int height )
+Dali::Devel::PixelBuffer MakePixelBuffer( const uint8_t * const pixels, Pixel::Format pixelFormat, unsigned int width, unsigned int height )
 {
   DALI_ASSERT_DEBUG( pixels && "Null bitmap buffer to copy." );
 
   // Allocate a pixel buffer to hold the image passed in:
-  Integration::BitmapPtr newBitmap = MakeEmptyBitmap( pixelFormat, width, height );
+  auto newBitmap = Dali::Devel::PixelBuffer::New( width, height, pixelFormat );
 
   // Copy over the pixels from the downscaled image that was generated in-place in the pixel buffer of the input bitmap:
-  memcpy( newBitmap->GetBuffer(), pixels, width * height * Pixel::GetBytesPerPixel( pixelFormat ) );
+  memcpy( newBitmap.GetBuffer(), pixels, width * height * Pixel::GetBytesPerPixel( pixelFormat ) );
   return newBitmap;
 }
 
@@ -441,27 +442,432 @@ BitmapPtr MakeBitmap( const uint8_t * const pixels, Pixel::Format pixelFormat, u
  */
 ImageDimensions CalculateDesiredDimensions( unsigned int bitmapWidth, unsigned int bitmapHeight, unsigned int requestedWidth, unsigned int requestedHeight )
 {
+  unsigned int maxSize = Dali::GetMaxTextureSize();
+
   // If no dimensions have been requested, default to the source ones:
   if( requestedWidth == 0 && requestedHeight == 0 )
   {
-    return ImageDimensions( bitmapWidth, bitmapHeight );
+    if( bitmapWidth <= maxSize && bitmapHeight <= maxSize )
+    {
+      return ImageDimensions( bitmapWidth, bitmapHeight );
+    }
+    else
+    {
+      // Calculate the size from the max texture size and the source image aspect ratio
+      if( bitmapWidth > bitmapHeight )
+      {
+        return ImageDimensions( maxSize, bitmapHeight * maxSize / static_cast< float >( bitmapWidth ) + 0.5f );
+      }
+      else
+      {
+        return ImageDimensions( bitmapWidth * maxSize / static_cast< float >( bitmapHeight ) + 0.5f, maxSize );
+      }
+    }
   }
 
   // If both dimensions have values requested, use them both:
   if( requestedWidth != 0 && requestedHeight != 0 )
   {
-    return ImageDimensions( requestedWidth, requestedHeight );
+    if( requestedWidth <= maxSize && requestedHeight <= maxSize )
+    {
+      return ImageDimensions( requestedWidth, requestedHeight );
+    }
+    else
+    {
+      // Calculate the size from the max texture size and the source image aspect ratio
+      if( requestedWidth > requestedHeight )
+      {
+        return ImageDimensions( maxSize, requestedHeight * maxSize / static_cast< float >( requestedWidth ) + 0.5f );
+      }
+      else
+      {
+        return ImageDimensions( requestedWidth * maxSize / static_cast< float >( requestedHeight ) + 0.5f, maxSize );
+      }
+    }
   }
 
   // Only one of the dimensions has been requested. Calculate the other from
   // the requested one and the source image aspect ratio:
   if( requestedWidth != 0 )
   {
+    requestedWidth = std::min( requestedWidth, maxSize );
     return ImageDimensions( requestedWidth, bitmapHeight / float(bitmapWidth) * requestedWidth + 0.5f );
   }
+
+  requestedHeight = std::min( requestedHeight, maxSize );
   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 )
@@ -482,14 +888,14 @@ ImageDimensions CalculateDesiredDimensions( ImageDimensions rawDimensions, Image
  *   bitmaps dimensions to only be as large as necessary, as a memory saving optimization. This will cause
  *   GPU scaling to be performed at render time giving the same result with less texture traversal.
  *
- * @param[in] bitmap            The source bitmap to perform modifications on.
+ * @param[in] bitmap            The source pixel buffer to perform modifications on.
  * @param[in] desiredDimensions The target dimensions to aim to fill based on the fitting mode.
  * @param[in] fittingMode       The fitting mode to use.
  *
  * @return                      A new bitmap with the padding and cropping required for fitting mode applied.
  *                              If no modification is needed or possible, the passed in bitmap is returned.
  */
-Integration::BitmapPtr CropAndPadForFittingMode( Integration::BitmapPtr bitmap, ImageDimensions desiredDimensions, FittingMode::Type fittingMode );
+Dali::Devel::PixelBuffer CropAndPadForFittingMode( Dali::Devel::PixelBuffer& bitmap, ImageDimensions desiredDimensions, FittingMode::Type fittingMode );
 
 /**
  * @brief Adds horizontal or vertical borders to the source image.
@@ -501,41 +907,34 @@ Integration::BitmapPtr CropAndPadForFittingMode( Integration::BitmapPtr bitmap,
  */
 void AddBorders( PixelBuffer *targetPixels, const unsigned int bytesPerPixel, const ImageDimensions targetDimensions, const ImageDimensions padDimensions );
 
-BitmapPtr ApplyAttributesToBitmap( BitmapPtr bitmap, ImageDimensions dimensions, FittingMode::Type fittingMode, SamplingMode::Type samplingMode )
+Dali::Devel::PixelBuffer ApplyAttributesToBitmap( Dali::Devel::PixelBuffer bitmap, ImageDimensions dimensions, FittingMode::Type fittingMode, SamplingMode::Type samplingMode )
 {
   if( bitmap )
   {
     // Calculate the desired box, accounting for a possible zero component:
-    const ImageDimensions desiredDimensions  = CalculateDesiredDimensions( bitmap->GetImageWidth(), bitmap->GetImageHeight(), dimensions.GetWidth(), dimensions.GetHeight() );
+    const ImageDimensions desiredDimensions  = CalculateDesiredDimensions( bitmap.GetWidth(), bitmap.GetHeight(), dimensions.GetWidth(), dimensions.GetHeight() );
 
     // If a different size than the raw one has been requested, resize the image
     // maximally using a repeated box filter without making it smaller than the
     // requested size in either dimension:
-    bitmap = DownscaleBitmap( *bitmap, desiredDimensions, fittingMode, samplingMode );
+    bitmap = DownscaleBitmap( bitmap, desiredDimensions, fittingMode, samplingMode );
 
     // Cut the bitmap according to the desired width and height so that the
     // resulting bitmap has the same aspect ratio as the desired dimensions.
     // Add crop and add borders if necessary depending on fitting mode.
-    if( bitmap && bitmap->GetPackedPixelsProfile() )
+    if( bitmap )
     {
       bitmap = CropAndPadForFittingMode( bitmap, desiredDimensions, fittingMode );
     }
-
-    // Examine the image pixels remaining after cropping and scaling to see if all
-    // are opaque, allowing faster rendering, or some have non-1.0 alpha:
-    if( bitmap && bitmap->GetPackedPixelsProfile() && Pixel::HasAlpha( bitmap->GetPixelFormat() ) )
-    {
-      bitmap->GetPackedPixelsProfile()->TestForTransparency();
-    }
   }
 
   return bitmap;
 }
 
-BitmapPtr CropAndPadForFittingMode( BitmapPtr bitmap, ImageDimensions desiredDimensions, FittingMode::Type fittingMode )
+Dali::Devel::PixelBuffer CropAndPadForFittingMode( Dali::Devel::PixelBuffer& bitmap, ImageDimensions desiredDimensions, FittingMode::Type fittingMode )
 {
-  const unsigned int inputWidth = bitmap->GetImageWidth();
-  const unsigned int inputHeight = bitmap->GetImageHeight();
+  const unsigned int inputWidth = bitmap.GetWidth();
+  const unsigned int inputHeight = bitmap.GetHeight();
 
   if( desiredDimensions.GetWidth() < 1u || desiredDimensions.GetHeight() < 1u )
   {
@@ -580,18 +979,16 @@ BitmapPtr CropAndPadForFittingMode( BitmapPtr bitmap, ImageDimensions desiredDim
         return bitmap;
       }
 
-      // Create a new bitmap with the desired size.
-      BitmapPtr croppedBitmap = Integration::Bitmap::New( Integration::Bitmap::BITMAP_2D_PACKED_PIXELS, ResourcePolicy::OWNED_DISCARD );
-      Integration::Bitmap::PackedPixelsProfile *packedView = croppedBitmap->GetPackedPixelsProfile();
-      DALI_ASSERT_DEBUG( packedView );
-      const Pixel::Format pixelFormat = bitmap->GetPixelFormat();
-      packedView->ReserveBuffer( pixelFormat, desiredWidth, desiredHeight, desiredWidth, desiredHeight );
+      // Create new PixelBuffer with the desired size.
+      const auto pixelFormat = bitmap.GetPixelFormat();
+
+      auto croppedBitmap = Devel::PixelBuffer::New( desiredWidth, desiredHeight, pixelFormat );
 
       // Add some pre-calculated offsets to the bitmap pointers so this is not done within a loop.
       // The cropping is added to the source pointer, and the padding is added to the destination.
-      const unsigned int bytesPerPixel = Pixel::GetBytesPerPixel( pixelFormat );
-      const PixelBuffer * const sourcePixels = bitmap->GetBuffer() + ( ( ( ( scanlinesToCrop / 2 ) * inputWidth ) + ( columnsToCrop / 2 ) ) * bytesPerPixel );
-      PixelBuffer * const targetPixels = croppedBitmap->GetBuffer();
+      const auto bytesPerPixel = Pixel::GetBytesPerPixel( pixelFormat );
+      const PixelBuffer * const sourcePixels = bitmap.GetBuffer() + ( ( ( ( scanlinesToCrop / 2 ) * inputWidth ) + ( columnsToCrop / 2 ) ) * bytesPerPixel );
+      PixelBuffer * const targetPixels = croppedBitmap.GetBuffer();
       PixelBuffer * const targetPixelsActive = targetPixels + ( ( ( ( scanlinesToPad / 2 ) * desiredWidth ) + ( columnsToPad / 2 ) ) * bytesPerPixel );
       DALI_ASSERT_DEBUG( sourcePixels && targetPixels );
 
@@ -619,7 +1016,7 @@ BitmapPtr CropAndPadForFittingMode( BitmapPtr bitmap, ImageDimensions desiredDim
       // Add vertical or horizontal borders to the final image (if required).
       desiredDimensions.SetWidth( desiredWidth );
       desiredDimensions.SetHeight( desiredHeight );
-      AddBorders( croppedBitmap->GetBuffer(), bytesPerPixel, desiredDimensions, ImageDimensions( columnsToPad, scanlinesToPad ) );
+      AddBorders( croppedBitmap.GetBuffer(), bytesPerPixel, desiredDimensions, ImageDimensions( columnsToPad, scanlinesToPad ) );
       // Overwrite the loaded bitmap with the cropped version
       bitmap = croppedBitmap;
     }
@@ -676,26 +1073,26 @@ void AddBorders( PixelBuffer *targetPixels, const unsigned int bytesPerPixel, co
   }
 }
 
-Integration::BitmapPtr DownscaleBitmap( Integration::Bitmap& bitmap,
+Dali::Devel::PixelBuffer DownscaleBitmap( Dali::Devel::PixelBuffer bitmap,
                                         ImageDimensions desired,
                                         FittingMode::Type fittingMode,
                                         SamplingMode::Type samplingMode )
 {
   // Source dimensions as loaded from resources (e.g. filesystem):
-  const unsigned int bitmapWidth  = bitmap.GetImageWidth();
-  const unsigned int bitmapHeight = bitmap.GetImageHeight();
+  auto bitmapWidth  = bitmap.GetWidth();
+  auto bitmapHeight = bitmap.GetHeight();
   // Desired dimensions (the rectangle to fit the source image to):
-  const unsigned int desiredWidth = desired.GetWidth();
-  const unsigned int desiredHeight = desired.GetHeight();
+  auto desiredWidth = desired.GetWidth();
+  auto desiredHeight = desired.GetHeight();
 
-  BitmapPtr outputBitmap( &bitmap );
+  Dali::Devel::PixelBuffer outputBitmap { bitmap };
 
   // If a different size than the raw one has been requested, resize the image:
-  if( bitmap.GetPackedPixelsProfile() &&
+  if(
       (desiredWidth > 0.0f) && (desiredHeight > 0.0f) &&
       ((desiredWidth < bitmapWidth) || (desiredHeight < bitmapHeight)) )
   {
-    const Pixel::Format pixelFormat = bitmap.GetPixelFormat();
+    auto pixelFormat = bitmap.GetPixelFormat();
 
     // Do the fast power of 2 iterated box filter to get to roughly the right side if the filter mode requests that:
     unsigned int shrunkWidth = -1, shrunkHeight = -1;
@@ -713,16 +1110,17 @@ Integration::BitmapPtr DownscaleBitmap( Integration::Bitmap& bitmap,
       if( samplingMode == SamplingMode::LINEAR || samplingMode == SamplingMode::BOX_THEN_LINEAR ||
           samplingMode == SamplingMode::NEAREST || samplingMode == SamplingMode::BOX_THEN_NEAREST )
       {
-        outputBitmap = MakeEmptyBitmap( pixelFormat, filteredWidth, filteredHeight );
+        outputBitmap = Dali::Devel::PixelBuffer::New( filteredWidth, filteredHeight, pixelFormat );
+
         if( outputBitmap )
         {
           if( samplingMode == SamplingMode::LINEAR || samplingMode == SamplingMode::BOX_THEN_LINEAR )
           {
-            LinearSample( bitmap.GetBuffer(), ImageDimensions(shrunkWidth, shrunkHeight), pixelFormat, outputBitmap->GetBuffer(), filteredDimensions );
+            LinearSample( bitmap.GetBuffer(), ImageDimensions(shrunkWidth, shrunkHeight), pixelFormat, outputBitmap.GetBuffer(), filteredDimensions );
           }
           else
           {
-            PointSample( bitmap.GetBuffer(), shrunkWidth, shrunkHeight, pixelFormat, outputBitmap->GetBuffer(), filteredWidth, filteredHeight );
+            PointSample( bitmap.GetBuffer(), shrunkWidth, shrunkHeight, pixelFormat, outputBitmap.GetBuffer(), filteredWidth, filteredHeight );
           }
           filtered = true;
         }
@@ -731,7 +1129,7 @@ Integration::BitmapPtr DownscaleBitmap( Integration::Bitmap& bitmap,
     // Copy out the 2^x downscaled, box-filtered pixels if no secondary filter (point or linear) was applied:
     if( filtered == false && ( shrunkWidth < bitmapWidth || shrunkHeight < bitmapHeight ) )
     {
-      outputBitmap = MakeBitmap( bitmap.GetBuffer(), pixelFormat, shrunkWidth, shrunkHeight );
+      outputBitmap = MakePixelBuffer( bitmap.GetBuffer(), pixelFormat, shrunkWidth, shrunkHeight );
     }
   }
 
@@ -1169,8 +1567,8 @@ inline void PointSampleAddressablePixels( const uint8_t * inPixels,
   DALI_ASSERT_DEBUG( ((desiredWidth <= inputWidth && desiredHeight <= inputHeight) ||
       outPixels >= inPixels + inputWidth * inputHeight * sizeof(PIXEL) || outPixels <= inPixels - desiredWidth * desiredHeight * sizeof(PIXEL)) &&
       "The input and output buffers must not overlap for an upscaling.");
-  DALI_ASSERT_DEBUG( ((uint64_t) inPixels)  % sizeof(PIXEL) == 0 && "Pixel pointers need to be aligned to the size of the pixels (E.g., 4 bytes for RGBA, 2 bytes for RGB565, ...)." );
-  DALI_ASSERT_DEBUG( ((uint64_t) outPixels) % sizeof(PIXEL) == 0 && "Pixel pointers need to be aligned to the size of the pixels (E.g., 4 bytes for RGBA, 2 bytes for RGB565, ...)." );
+  DALI_ASSERT_DEBUG( reinterpret_cast< uint64_t >( inPixels )  % sizeof(PIXEL) == 0 && "Pixel pointers need to be aligned to the size of the pixels (E.g., 4 bytes for RGBA, 2 bytes for RGB565, ...)." );
+  DALI_ASSERT_DEBUG( reinterpret_cast< uint64_t >( outPixels ) % sizeof(PIXEL) == 0 && "Pixel pointers need to be aligned to the size of the pixels (E.g., 4 bytes for RGBA, 2 bytes for RGB565, ...)." );
 
   if( inputWidth < 1u || inputHeight < 1u || desiredWidth < 1u || desiredHeight < 1u )
   {
@@ -1412,8 +1810,8 @@ inline void LinearSampleGeneric( const unsigned char * __restrict__ inPixels,
                      "Input and output buffers cannot overlap.");
   if( DEBUG_ASSERT_ALIGNMENT )
   {
-    DALI_ASSERT_DEBUG( ((uint64_t) inPixels)  % sizeof(PIXEL) == 0 && "Pixel pointers need to be aligned to the size of the pixels (E.g., 4 bytes for RGBA, 2 bytes for RGB565, ...)." );
-    DALI_ASSERT_DEBUG( ((uint64_t) outPixels) % sizeof(PIXEL) == 0 && "Pixel pointers need to be aligned to the size of the pixels (E.g., 4 bytes for RGBA, 2 bytes for RGB565, ...)." );
+    DALI_ASSERT_DEBUG( reinterpret_cast< uint64_t >( inPixels )  % sizeof(PIXEL) == 0 && "Pixel pointers need to be aligned to the size of the pixels (E.g., 4 bytes for RGBA, 2 bytes for RGB565, ...)." );
+    DALI_ASSERT_DEBUG( reinterpret_cast< uint64_t >( outPixels) % sizeof(PIXEL) == 0 && "Pixel pointers need to be aligned to the size of the pixels (E.g., 4 bytes for RGBA, 2 bytes for RGB565, ...)." );
   }
 
   if( inputWidth < 1u || inputHeight < 1u || desiredWidth < 1u || desiredHeight < 1u )
@@ -1509,6 +1907,202 @@ void LinearSample4BPP( const unsigned char * __restrict__ inPixels,
   LinearSampleGeneric<Pixel4Bytes, BilinearFilter4Bytes, true>( inPixels, inputDimensions, outPixels, desiredDimensions );
 }
 
+
+void Resample( const unsigned char * __restrict__ inPixels,
+               ImageDimensions inputDimensions,
+               unsigned char * __restrict__ outPixels,
+               ImageDimensions desiredDimensions,
+               Resampler::Filter filterType,
+               int numChannels, bool hasAlpha )
+{
+  // Got from the test.cpp of the ImageResampler lib.
+  const float ONE_DIV_255 = 1.0f / 255.0f;
+  const int MAX_UNSIGNED_CHAR = std::numeric_limits<uint8_t>::max();
+  const int LINEAR_TO_SRGB_TABLE_SIZE = 4096;
+  const int ALPHA_CHANNEL = hasAlpha ? (numChannels-1) : 0;
+
+  static bool loadColorSpaces = true;
+  static float srgbToLinear[MAX_UNSIGNED_CHAR + 1];
+  static unsigned char linearToSrgb[LINEAR_TO_SRGB_TABLE_SIZE];
+
+  if( loadColorSpaces ) // Only create the color space conversions on the first execution
+  {
+    loadColorSpaces = false;
+
+    for( int i = 0; i <= MAX_UNSIGNED_CHAR; ++i )
+    {
+      srgbToLinear[i] = pow( static_cast<float>( i ) * ONE_DIV_255, DEFAULT_SOURCE_GAMMA );
+    }
+
+    const float invLinearToSrgbTableSize = 1.0f / static_cast<float>( LINEAR_TO_SRGB_TABLE_SIZE );
+    const float invSourceGamma = 1.0f / DEFAULT_SOURCE_GAMMA;
+
+    for( int i = 0; i < LINEAR_TO_SRGB_TABLE_SIZE; ++i )
+    {
+      int k = static_cast<int>( 255.0f * pow( static_cast<float>( i ) * invLinearToSrgbTableSize, invSourceGamma ) + 0.5f );
+      if( k < 0 )
+      {
+        k = 0;
+      }
+      else if( k > MAX_UNSIGNED_CHAR )
+      {
+        k = MAX_UNSIGNED_CHAR;
+      }
+      linearToSrgb[i] = static_cast<unsigned char>( k );
+    }
+  }
+
+  Resampler* resamplers[numChannels];
+  Vector<float> samples[numChannels];
+
+  const int srcWidth = inputDimensions.GetWidth();
+  const int srcHeight = inputDimensions.GetHeight();
+  const int dstWidth = desiredDimensions.GetWidth();
+  const int dstHeight = desiredDimensions.GetHeight();
+
+  // Now create a Resampler instance for each component to process. The first instance will create new contributor tables, which are shared by the resamplers
+  // used for the other components (a memory and slight cache efficiency optimization).
+  resamplers[0] = new Resampler( srcWidth,
+                                 srcHeight,
+                                 dstWidth,
+                                 dstHeight,
+                                 Resampler::BOUNDARY_CLAMP,
+                                 0.0f,           // sample_low,
+                                 1.0f,           // sample_high. Clamp output samples to specified range, or disable clamping if sample_low >= sample_high.
+                                 filterType,    // The type of filter.
+                                 NULL,           // Pclist_x,
+                                 NULL,           // Pclist_y. Optional pointers to contributor lists from another instance of a Resampler.
+                                 FILTER_SCALE,   // src_x_ofs,
+                                 FILTER_SCALE ); // src_y_ofs. Offset input image by specified amount (fractional values okay).
+  samples[0].Resize( srcWidth );
+  for( int i = 1; i < numChannels; ++i )
+  {
+    resamplers[i] = new Resampler( srcWidth,
+                                   srcHeight,
+                                   dstWidth,
+                                   dstHeight,
+                                   Resampler::BOUNDARY_CLAMP,
+                                   0.0f,
+                                   1.0f,
+                                   filterType,
+                                   resamplers[0]->get_clist_x(),
+                                   resamplers[0]->get_clist_y(),
+                                   FILTER_SCALE,
+                                   FILTER_SCALE );
+    samples[i].Resize( srcWidth );
+  }
+
+  const int srcPitch = srcWidth * numChannels;
+  const int dstPitch = dstWidth * numChannels;
+  int dstY = 0;
+
+  for( int srcY = 0; srcY < srcHeight; ++srcY )
+  {
+    const unsigned char* pSrc = &inPixels[srcY * srcPitch];
+
+    for( int x = 0; x < srcWidth; ++x )
+    {
+      for( int c = 0; c < numChannels; ++c )
+      {
+        if( c == ALPHA_CHANNEL && hasAlpha )
+        {
+          samples[c][x] = *pSrc++ * ONE_DIV_255;
+        }
+        else
+        {
+          samples[c][x] = srgbToLinear[*pSrc++];
+        }
+      }
+    }
+
+    for( int c = 0; c < numChannels; ++c )
+    {
+      if( !resamplers[c]->put_line( &samples[c][0] ) )
+      {
+        DALI_ASSERT_DEBUG( !"Out of memory" );
+      }
+    }
+
+    for(;;)
+    {
+      int compIndex;
+      for( compIndex = 0; compIndex < numChannels; ++compIndex )
+      {
+        const float* pOutputSamples = resamplers[compIndex]->get_line();
+        if( !pOutputSamples )
+        {
+          break;
+        }
+
+        const bool isAlphaChannel = ( compIndex == ALPHA_CHANNEL && hasAlpha );
+        DALI_ASSERT_DEBUG( dstY < dstHeight );
+        unsigned char* pDst = &outPixels[dstY * dstPitch + compIndex];
+
+        for( int x = 0; x < dstWidth; ++x )
+        {
+          if( isAlphaChannel )
+          {
+            int c = static_cast<int>( 255.0f * pOutputSamples[x] + 0.5f );
+            if( c < 0 )
+            {
+              c = 0;
+            }
+            else if( c > MAX_UNSIGNED_CHAR )
+            {
+              c = MAX_UNSIGNED_CHAR;
+            }
+            *pDst = static_cast<unsigned char>( c );
+          }
+          else
+          {
+            int j = static_cast<int>( LINEAR_TO_SRGB_TABLE_SIZE * pOutputSamples[x] + 0.5f );
+            if( j < 0 )
+            {
+              j = 0;
+            }
+            else if( j >= LINEAR_TO_SRGB_TABLE_SIZE )
+            {
+              j = LINEAR_TO_SRGB_TABLE_SIZE - 1;
+            }
+            *pDst = linearToSrgb[j];
+          }
+
+          pDst += numChannels;
+        }
+      }
+      if( compIndex < numChannels )
+      {
+        break;
+      }
+
+      ++dstY;
+    }
+  }
+
+  // Delete the resamplers.
+  for( int i = 0; i < numChannels; ++i )
+  {
+    delete resamplers[i];
+  }
+}
+
+void LanczosSample4BPP( const unsigned char * __restrict__ inPixels,
+                        ImageDimensions inputDimensions,
+                        unsigned char * __restrict__ outPixels,
+                        ImageDimensions desiredDimensions )
+{
+  Resample( inPixels, inputDimensions, outPixels, desiredDimensions, Resampler::LANCZOS4, 4, true );
+}
+
+void LanczosSample1BPP( const unsigned char * __restrict__ inPixels,
+                        ImageDimensions inputDimensions,
+                        unsigned char * __restrict__ outPixels,
+                        ImageDimensions desiredDimensions )
+{
+  // For L8 images
+  Resample( inPixels, inputDimensions, outPixels, desiredDimensions, Resampler::LANCZOS4, 1, false );
+}
+
 // Dispatch to a format-appropriate linear sampling function:
 void LinearSample( const unsigned char * __restrict__ inPixels,
                    ImageDimensions inDimensions,
@@ -1550,6 +2144,216 @@ 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 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;
+
+    // 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;
+
+    // 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;
+
+    // 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.
+}
+
 } /* namespace Platform */
 } /* namespace Internal */
 } /* namespace Dali */