Adding scale and crop feature to alpha mask 29/139629/3
authorDavid Steele <david.steele@samsung.com>
Tue, 18 Jul 2017 15:26:37 +0000 (16:26 +0100)
committerDavid Steele <david.steele@samsung.com>
Thu, 20 Jul 2017 12:11:43 +0000 (13:11 +0100)
When alpha masking an image, there is a requirement to first
scale the content image by some factor, then crop it to match the
mask size, then apply the mask to it.

Uses Lanczos resampling for reducing an image, or Mitchell resampling for
upscaling an image ( Mitchell has fewer artifacts on my test images ).

Change-Id: I0334baa855a3e1547947f0a81b42f5e6271232ce
Signed-off-by: David Steele <david.steele@samsung.com>
adaptors/common/pixel-buffer-impl.cpp
adaptors/common/pixel-buffer-impl.h
adaptors/devel-api/adaptor-framework/pixel-buffer.cpp
adaptors/devel-api/adaptor-framework/pixel-buffer.h
automated-tests/src/dali-adaptor-internal/CMakeLists.txt
automated-tests/src/dali-adaptor/utc-Dali-PixelBuffer.cpp
automated-tests/src/dali-platform-abstraction/CMakeLists.txt
platform-abstractions/portable/alpha-mask.cpp
platform-abstractions/portable/alpha-mask.h
platform-abstractions/portable/image-operations.cpp
platform-abstractions/portable/image-operations.h

index a2b1610..1de5382 100644 (file)
@@ -25,6 +25,7 @@
 // INTERNAL INCLUDES
 #include "pixel-manipulation.h"
 #include "alpha-mask.h"
+#include <platform-abstractions/portable/image-operations.h>
 
 namespace Dali
 {
@@ -133,13 +134,40 @@ Dali::PixelData PixelBuffer::CreatePixelData() const
   return pixelData;
 }
 
-void PixelBuffer::ApplyMask( const PixelBuffer& mask )
+void PixelBuffer::ApplyMask( const PixelBuffer& inMask, float contentScale, bool cropToMask )
+{
+  if( cropToMask )
+  {
+    // First scale this buffer by the contentScale, and crop to the mask size
+    // If it's too small, then scale the mask to match the image size
+    // Then apply the mask
+    ScaleAndCrop( contentScale, ImageDimensions( inMask.GetWidth(), inMask.GetHeight() ) );
+
+    if( inMask.mWidth > mWidth || inMask.mHeight > mHeight )
+    {
+      PixelBufferPtr mask = NewResize( inMask, ImageDimensions( mWidth, mHeight ) );
+      ApplyMaskInternal( *mask );
+    }
+    else
+    {
+      ApplyMaskInternal( inMask );
+    }
+  }
+  else
+  {
+    // First, scale the mask to match the image size,
+    // then apply the mask.
+    PixelBufferPtr mask = NewResize( inMask, ImageDimensions( mWidth, mHeight ) );
+    ApplyMaskInternal( *mask );
+  }
+}
+
+void PixelBuffer::ApplyMaskInternal( const PixelBuffer& mask )
 {
   int byteOffset=0;
   int bitMask=0;
 
   Dali::Pixel::GetAlphaOffsetAndMask(mPixelFormat, byteOffset, bitMask);
-
   if( Dali::Pixel::HasAlpha( mPixelFormat ) && bitMask == 255 )
   {
     ApplyMaskToAlphaChannel( *this, mask );
@@ -147,18 +175,24 @@ void PixelBuffer::ApplyMask( const PixelBuffer& mask )
   else
   {
     PixelBufferPtr newPixelBuffer = CreateNewMaskedBuffer( *this, mask );
-    ReleaseBuffer();
-
-    // Take ownership of new buffer
-    mBuffer = newPixelBuffer->mBuffer;
-    newPixelBuffer->mBuffer = NULL;
-    mPixelFormat = newPixelBuffer->mPixelFormat;
-    mBufferSize = newPixelBuffer->mBufferSize;
-
+    TakeOwnershipOfBuffer( *newPixelBuffer );
     // On leaving scope, newPixelBuffer will get destroyed.
   }
 }
 
+void PixelBuffer::TakeOwnershipOfBuffer( PixelBuffer& pixelBuffer )
+{
+  ReleaseBuffer();
+
+  // Take ownership of new buffer
+  mBuffer = pixelBuffer.mBuffer;
+  pixelBuffer.mBuffer = NULL;
+  mBufferSize = pixelBuffer.mBufferSize;
+  mWidth = pixelBuffer.mWidth;
+  mHeight = pixelBuffer.mHeight;
+  mPixelFormat = pixelBuffer.mPixelFormat;
+}
+
 void PixelBuffer::ReleaseBuffer()
 {
   if( mBuffer )
@@ -167,6 +201,113 @@ void PixelBuffer::ReleaseBuffer()
   }
 }
 
+void PixelBuffer::ScaleAndCrop( float scaleFactor, ImageDimensions cropDimensions )
+{
+  ImageDimensions outDimensions( float(mWidth) * scaleFactor,
+                                 float(mHeight) * scaleFactor );
+
+  if( outDimensions.GetWidth() != mWidth || outDimensions.GetHeight() != mHeight )
+  {
+    Resize( outDimensions );
+  }
+
+  ImageDimensions postCropDimensions(
+    std::min(cropDimensions.GetWidth(), outDimensions.GetWidth()),
+    std::min(cropDimensions.GetHeight(), outDimensions.GetHeight()));
+
+  if( postCropDimensions.GetWidth() < outDimensions.GetWidth() ||
+      postCropDimensions.GetHeight() < outDimensions.GetHeight() )
+  {
+    uint16_t x = ( outDimensions.GetWidth()  - postCropDimensions.GetWidth() ) / 2;
+    uint16_t y = ( outDimensions.GetHeight() - postCropDimensions.GetHeight() ) / 2;
+    Crop( x, y, postCropDimensions );
+  }
+}
+
+void PixelBuffer::Crop( uint16_t x, uint16_t y, ImageDimensions cropDimensions )
+{
+  PixelBufferPtr outBuffer = NewCrop( *this, x, y, cropDimensions );
+  TakeOwnershipOfBuffer( *outBuffer );
+}
+
+PixelBufferPtr PixelBuffer::NewCrop( const PixelBuffer& inBuffer, uint16_t x, uint16_t y, ImageDimensions cropDimensions )
+{
+  PixelBufferPtr outBuffer = PixelBuffer::New( cropDimensions.GetWidth(), cropDimensions.GetHeight(), inBuffer.GetPixelFormat() );
+  int bytesPerPixel = Pixel::GetBytesPerPixel( inBuffer.mPixelFormat );
+  int srcStride = inBuffer.mWidth * bytesPerPixel;
+  int destStride = cropDimensions.GetWidth() * bytesPerPixel;
+
+  // Clamp crop to right edge
+  if( x + cropDimensions.GetWidth() > inBuffer.mWidth )
+  {
+    destStride = ( inBuffer.mWidth - x ) * bytesPerPixel;
+  }
+
+  int srcOffset = x * bytesPerPixel + y * srcStride;
+  int destOffset = 0;
+  unsigned char* destBuffer = outBuffer->mBuffer;
+
+  // Clamp crop to last row
+  unsigned int endRow = y + cropDimensions.GetHeight();
+  if( endRow > inBuffer.mHeight )
+  {
+    endRow = inBuffer.mHeight - 1 ;
+  }
+  for( uint16_t row = y; row < endRow; ++row )
+  {
+    memcpy(destBuffer + destOffset, inBuffer.mBuffer + srcOffset, destStride );
+    srcOffset += srcStride;
+    destOffset += destStride;
+  }
+  return outBuffer;
+
+}
+
+void PixelBuffer::Resize( ImageDimensions outDimensions )
+{
+  if( mWidth != outDimensions.GetWidth() || mHeight != outDimensions.GetHeight() )
+  {
+    PixelBufferPtr outBuffer = NewResize( *this, outDimensions );
+    TakeOwnershipOfBuffer( *outBuffer );
+  }
+}
+
+PixelBufferPtr PixelBuffer::NewResize( const PixelBuffer& inBuffer, ImageDimensions outDimensions )
+{
+  PixelBufferPtr outBuffer = PixelBuffer::New( outDimensions.GetWidth(), outDimensions.GetHeight(), inBuffer.GetPixelFormat() );
+  ImageDimensions inDimensions( inBuffer.mWidth, inBuffer.mHeight );
+
+  bool hasAlpha = Pixel::HasAlpha( inBuffer.mPixelFormat );
+  int bytesPerPixel = Pixel::GetBytesPerPixel( inBuffer.mPixelFormat );
+
+  Resampler::Filter filterType = Resampler::LANCZOS4;
+  if( inDimensions.GetWidth() < outDimensions.GetWidth() && inDimensions.GetHeight() < outDimensions.GetHeight() )
+  {
+    filterType = Resampler::MITCHELL;
+  }
+
+  // This method only really works for 8 bit wide channels.
+  // (But could be expanded to work)
+  if( inBuffer.mPixelFormat == Pixel::A8 ||
+      inBuffer.mPixelFormat == Pixel::L8 ||
+      inBuffer.mPixelFormat == Pixel::LA88 ||
+      inBuffer.mPixelFormat == Pixel::RGB888 ||
+      inBuffer.mPixelFormat == Pixel::RGB8888 ||
+      inBuffer.mPixelFormat == Pixel::BGR8888 ||
+      inBuffer.mPixelFormat == Pixel::RGBA8888 ||
+      inBuffer.mPixelFormat == Pixel::BGRA8888 )
+  {
+    Dali::Internal::Platform::Resample( inBuffer.mBuffer, inDimensions,
+                                        outBuffer->GetBuffer(), outDimensions,
+                                        filterType, bytesPerPixel, hasAlpha );
+  }
+  else
+  {
+    DALI_LOG_ERROR( "Trying to resize an image with too narrow a channel width" );
+  }
+
+  return outBuffer;
+}
 
 }// namespace Adaptor
 }// namespace Internal
index 4853001..ec7ee2b 100644 (file)
@@ -20,6 +20,7 @@
 
 // INTERNAL INCLUDES
 #include <pixel-buffer.h>
+#include <dali/public-api/images/image-operations.h> // For ImageDimensions
 #include <dali/public-api/images/pixel-data.h>
 #include <dali/public-api/object/base-object.h>
 
@@ -143,8 +144,11 @@ public:
    * internal object - e.g. the new buffer may have a different pixel
    * format - as an alpha channel may be added.
    * @param[in] mask The mask to apply to this pixel buffer
+   * @param[in] contentScale The scaling factor to apply to the content
+   * @param[in] cropToMask Whether to crop the output to the mask size (true) or scale the
+   * mask to the content size (false)
    */
-  void ApplyMask( const PixelBuffer& mask );
+  void ApplyMask( const PixelBuffer& mask, float contentScale, bool cropToMask );
 
 private:
   /*
@@ -158,10 +162,63 @@ private:
   PixelBuffer& operator= (const PixelBuffer& other);
 
   /**
+   * Internal method to apply the mask to this buffer. Expects that they are the same size.
+   */
+  void ApplyMaskInternal( const PixelBuffer& mask );
+
+  /**
+   * Takes ownership of the other object's pixel buffer.
+   */
+  void TakeOwnershipOfBuffer( PixelBuffer& pixelBuffer );
+
+  /**
    * Release the buffer
    */
   void ReleaseBuffer();
 
+  /**
+   * Scales this buffer buffer by the given factor, and crops at the center to the
+   * given dimensions.
+   */
+  void ScaleAndCrop( float scaleFactor, ImageDimensions cropDimensions );
+
+  /**
+   * Creates a new buffer which is a crop of the passed in buffer,
+   * using the given crop rectangle. Assumes the crop rectangle is
+   * within the bounds of this size.
+   * @param[in] inBuffer The source buffer
+   * @param[in] x The top left corner's X
+   * @param[in] y The top left corner's y
+   * @param[in] cropDimensions The dimensions of the crop
+   * @return the new pixel buffer
+   */
+  static PixelBufferPtr NewCrop( const PixelBuffer& inBuffer, uint16_t x, uint16_t y, ImageDimensions cropDimensions );
+
+  /**
+   * Creates a new buffer which is a resized version of the passed in buffer.
+   * Uses either Lanczos4 for downscaling, or Mitchell for upscaling.
+   * @param[in] inBuffer The source buffer
+   * @param[in] outDimensions The new dimensions
+   * @return a new buffer of the given size.
+   */
+  static PixelBufferPtr NewResize( const PixelBuffer& inBuffer, ImageDimensions outDimensions );
+
+  /**
+   * Crops this buffer to the given crop rectangle. Assumes the crop rectangle
+   * is within the bounds of this size.
+   * @param[in] x The top left corner's X
+   * @param[in] y The top left corner's y
+   * @param[in] cropDimensions The dimensions of the crop
+   */
+  void Crop( uint16_t x, uint16_t y, ImageDimensions cropDimensions );
+
+  /**
+   * Resizes the buffer to the given dimensions. Uses either Lanczos4 for downscaling
+   * or Mitchell for upscaling
+   * @param[in] outDimensions The new dimensions
+   */
+  void Resize( ImageDimensions outDimensions );
+
 private:
 
   unsigned char* mBuffer;           ///< The raw pixel data
index 52d8b8f..0b366d0 100644 (file)
@@ -96,9 +96,9 @@ unsigned char* PixelBuffer::GetBuffer()
   return GetImplementation(*this).GetBuffer();
 }
 
-void PixelBuffer::ApplyMask( PixelBuffer mask )
+void PixelBuffer::ApplyMask( PixelBuffer mask, float contentScale, bool cropToMask )
 {
-  GetImplementation(*this).ApplyMask( GetImplementation( mask ) );
+  GetImplementation(*this).ApplyMask( GetImplementation( mask ), contentScale, cropToMask );
 }
 
 } // namespace Devel
index deb7823..91c7161 100644 (file)
@@ -153,14 +153,22 @@ public:
   Pixel::Format GetPixelFormat() const;
 
   /**
-   * Apply the mask to this pixel data, and return a new pixel data
-   * containing the masked image. If this PixelBuffer doesn't have an alpha channel,
-   * then the resultant PixelBuffer will be converted to a format that
-   * supports at least the width of the color channels and the alpha channel
-   * from the mask.
+   * Apply the mask to this pixel data, and return a new pixel data containing
+   * the masked image. If this PixelBuffer doesn't have an alpha channel, then
+   * the resultant PixelBuffer will be converted to a format that supports at
+   * least the width of the color channels and the alpha channel from the mask.
+   *
+   * If cropToMask is set to true, then the contentScale is applied first to
+   * this buffer, and the target buffer is cropped to the size of the mask. If
+   * it's set to false, then the mask is scaled to match this buffer's size
+   * before the mask is applied.
+   *
    * @param[in] mask The mask to apply.
+   * @param[in] contentScale The scaling factor to apply to the content
+   * @param[in] cropToMask Whether to crop the output to the mask size (true)
+   * or scale the mask to the content size (false)
    */
-  void ApplyMask( PixelBuffer mask );
+  void ApplyMask( PixelBuffer mask, float contentScale=1.0f, bool cropToMask=false );
 
 public:
 
index ee37c4e..2292b64 100644 (file)
@@ -65,6 +65,7 @@ INCLUDE_DIRECTORIES(
     ../../../adaptors/tizen
     ../../../adaptors/ubuntu
     ../../../text
+    ../../../third-party/image-resampler
     ${${CAPI_LIB}_INCLUDE_DIRS}
     ../dali-adaptor/dali-test-suite-utils
 )
index 98173db..24a17d0 100644 (file)
@@ -72,6 +72,61 @@ void Mask1stQuadrant( Devel::PixelBuffer maskData )
   }
 }
 
+void MaskCenterSquare( Devel::PixelBuffer maskData )
+{
+  int width = maskData.GetWidth();
+  int height = maskData.GetHeight();
+  Pixel::Format pixelFormat = maskData.GetPixelFormat();
+  int bpp = Pixel::GetBytesPerPixel(pixelFormat);
+
+  unsigned char* maskBuffer = maskData.GetBuffer();
+  memset( maskBuffer, 0, width*height*bpp );
+  int offset=0;
+  for( int y=0; y<height; ++y)
+  {
+    for( int x=0; x<width; ++x)
+    {
+      if(x>=width/4 && x<3*width/4 &&
+         y>=height/4 && y<3*height/4 )
+      {
+        for(int b=0;b<bpp;++b)
+        {
+          maskBuffer[offset+b] = 0xff;
+        }
+      }
+      offset+=bpp;
+    }
+  }
+}
+
+void AlternateQuadrants( Devel::PixelBuffer buffer )
+{
+  int width = buffer.GetWidth();
+  int height = buffer.GetHeight();
+  Pixel::Format pixelFormat = buffer.GetPixelFormat();
+  int bpp = Pixel::GetBytesPerPixel(pixelFormat);
+  int stride=width*bpp;
+
+  unsigned char* pixels = buffer.GetBuffer();
+  memset( pixels, 0, width*height*bpp );
+
+  for( int x=0; x<width; ++x)
+  {
+    for( int y=0; y<height; ++y)
+    {
+      if( ( x < width/2 && y >= height/2 ) ||
+          ( x >= width/2 && y < height/2 ) )
+      {
+        for(int b=0;b<bpp;++b)
+        {
+          pixels[y*stride+x*bpp+b] = 0xff;
+        }
+      }
+    }
+  }
+}
+
+
 void FillCheckerboard( Devel::PixelBuffer imageData )
 {
   int width = imageData.GetWidth();
@@ -123,6 +178,17 @@ void FillCheckerboard( Devel::PixelBuffer imageData )
   }
 }
 
+int GetAlphaAt( Devel::PixelBuffer buffer, int x, int y )
+{
+  unsigned char* pixels = buffer.GetBuffer();
+  int bpp = Pixel::GetBytesPerPixel(buffer.GetPixelFormat());
+  int stride = buffer.GetWidth() * bpp;
+  int byteOffset;
+  int bitMask;
+  GetAlphaOffsetAndMask( buffer.GetPixelFormat(), byteOffset, bitMask );
+  return int(pixels[stride * y + x*bpp + byteOffset])  & bitMask;
+}
+
 int UtcDaliPixelBufferNew01P(void)
 {
   TestApplication application;
@@ -243,7 +309,7 @@ int UtcDaliPixelBufferMask01(void)
   Devel::PixelBuffer imageData = Devel::PixelBuffer::New( width, height, pixelFormat );
   FillCheckerboard(imageData);
 
-  imageData.ApplyMask( maskData );
+  imageData.ApplyMask( maskData, 1.0f, false );
 
   // Test that the pixel format has been promoted to RGBA8888
   DALI_TEST_EQUALS( imageData.GetPixelFormat(), Pixel::RGBA8888, TEST_LOCATION );
@@ -281,7 +347,7 @@ int UtcDaliPixelBufferMask02(void)
   Devel::PixelBuffer imageData = Devel::PixelBuffer::New( width, height, pixelFormat );
   FillCheckerboard(imageData);
 
-  imageData.ApplyMask( maskData );
+  imageData.ApplyMask( maskData, 1.0f, false );
 
   // Test that the pixel format has been promoted to RGBA8888
   DALI_TEST_EQUALS( imageData.GetPixelFormat(), Pixel::RGBA8888, TEST_LOCATION );
@@ -316,7 +382,7 @@ int UtcDaliPixelBufferMask03(void)
   Devel::PixelBuffer imageData = Devel::PixelBuffer::New( width, height, format );
   FillCheckerboard(imageData);
 
-  imageData.ApplyMask( maskData );
+  imageData.ApplyMask( maskData, 1.0f, false );
 
   // Test that the pixel format has been promoted to RGBA8888
   DALI_TEST_EQUALS( imageData.GetPixelFormat(), Pixel::RGBA8888, TEST_LOCATION );
@@ -351,7 +417,7 @@ int UtcDaliPixelBufferMask04(void)
   Devel::PixelBuffer imageData = Devel::PixelBuffer::New( width, height, Pixel::RGBA8888 );
   FillCheckerboard(imageData);
 
-  imageData.ApplyMask( maskData );
+  imageData.ApplyMask( maskData, 1.0f, false );
 
   // Test that the pixel format has been promoted to RGBA8888
   DALI_TEST_EQUALS( imageData.GetPixelFormat(), Pixel::RGBA8888, TEST_LOCATION );
@@ -385,7 +451,7 @@ int UtcDaliPixelBufferMask05(void)
   Devel::PixelBuffer imageData = Devel::PixelBuffer::New( width, height, Pixel::RGBA8888 );
   FillCheckerboard(imageData);
 
-  imageData.ApplyMask( maskData );
+  imageData.ApplyMask( maskData, 1.0f, false );
 
   // Test that the pixel format has been promoted to RGBA8888
   DALI_TEST_EQUALS( imageData.GetPixelFormat(), Pixel::RGBA8888, TEST_LOCATION );
@@ -419,7 +485,7 @@ int UtcDaliPixelBufferMask06(void)
   Devel::PixelBuffer imageData = Devel::PixelBuffer::New( width, height, Pixel::RGBA8888 );
   FillCheckerboard(imageData);
 
-  imageData.ApplyMask( maskData );
+  imageData.ApplyMask( maskData, 1.0f, false );
 
   // Test that the pixel format has been promoted to RGBA8888
   DALI_TEST_EQUALS( imageData.GetPixelFormat(), Pixel::RGBA8888, TEST_LOCATION );
@@ -437,3 +503,217 @@ int UtcDaliPixelBufferMask06(void)
 
   END_TEST;
 }
+
+
+int UtcDaliPixelBufferMask07(void)
+{
+  TestApplication application;
+  tet_infoline("Test scaling of source image to match alpha mask" );
+
+  unsigned int width = 20u;
+  unsigned int height = 20u;
+  Devel::PixelBuffer maskData = Devel::PixelBuffer::New( width, height, Pixel::RGBA8888 );
+  MaskCenterSquare(maskData);
+
+  // +----------+
+  // |  XXXXXX  |
+  // |  XXXXXX  |
+  // |  XXXXXX  |
+  // |  XXXXXX  |
+  // *----------+
+
+  width = 10u;
+  height = 10u;
+  Devel::PixelBuffer imageData = Devel::PixelBuffer::New( width, height, Pixel::RGBA8888 );
+  AlternateQuadrants( imageData );
+
+  // +-----XXXXX+
+  // |     XXXXX|
+  // |     XXXXX|
+  // |XXXXX     |
+  // |XXXXX     |
+  // *XXXXX-----+
+
+  imageData.ApplyMask( maskData, 2.0f, true );
+
+  // +----------+
+  // |     XXX  |
+  // |     XXX  |
+  // |  XXX     |
+  // |  XXX     |
+  // *----------+
+
+  tet_infoline("Test that the image has been scaled to match the alpha mask" );
+  DALI_TEST_EQUALS( imageData.GetWidth(), 20, TEST_LOCATION );
+  DALI_TEST_EQUALS( imageData.GetHeight(), 20, TEST_LOCATION );
+
+  tet_infoline( "Test that pixels in the outer eighths have no alpha\n" );
+
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 0, 0), 0x00u, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 9, 4), 0x00u, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 15, 4), 0x00u, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 19, 4), 0x00u, TEST_LOCATION );
+
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 0, 19), 0x00u, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 8, 18), 0x00u, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 15,17), 0x00u, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 19,16), 0x00u, TEST_LOCATION );
+
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 0, 1), 0x00u, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 1, 7), 0x00u, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 2, 10), 0x00u, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 3, 19), 0x00u, TEST_LOCATION );
+
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 19, 1), 0x00u, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 18, 7), 0x00u, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 17, 10), 0x00u, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 16, 19), 0x00u, TEST_LOCATION );
+
+  tet_infoline( "Test that pixels in the center have full alpha\n" );
+
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 12, 8), 0xffu, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData,  8, 12), 0xffu, TEST_LOCATION );
+
+  END_TEST;
+}
+
+int UtcDaliPixelBufferMask08(void)
+{
+  TestApplication application;
+  tet_infoline("Test scaling of source image to larger than the alpha mask" );
+
+  unsigned int width = 32u;
+  unsigned int height = 20u;
+  Devel::PixelBuffer maskData = Devel::PixelBuffer::New( width, height, Pixel::RGBA8888 );
+  AlternateQuadrants( maskData );
+
+  // +-----XXXXX+
+  // |     XXXXX|
+  // |     XXXXX|
+  // |XXXXX     |
+  // |XXXXX     |
+  // *XXXXX-----+
+
+  width = 20u;
+  height = 16u;
+  Devel::PixelBuffer imageData = Devel::PixelBuffer::New( width, height, Pixel::RGBA8888 );
+  MaskCenterSquare(imageData);
+
+  // +----------+
+  // |  XXXXXX  |
+  // |  XXXXXX  |
+  // |  XXXXXX  |
+  // |  XXXXXX  |
+  // *----------+
+
+  imageData.ApplyMask( maskData, 4.0f, true );
+
+  // +-----XXXXX+   quadrant
+  // |     XXXXX|    1    2
+  // |     XXXXX|
+  // |XXXXX     |    4    3
+  // |XXXXX     |
+  // *XXXXX-----+
+
+  tet_infoline("Test that the image has been scaled and cropped to match the alpha mask" );
+  DALI_TEST_EQUALS( imageData.GetWidth(), 32, TEST_LOCATION );
+  DALI_TEST_EQUALS( imageData.GetHeight(), 20, TEST_LOCATION );
+
+  tet_infoline( "Test that the image has been resized (the center square should now fill the image)\n");
+  tet_infoline( "Test that the first quadrant has no alpha");
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 0, 0), 0x00u, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 5, 4), 0x00u, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 5, 8), 0x00u, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 14, 8), 0x00u, TEST_LOCATION );
+
+  tet_infoline( "Test that the second quadrant has alpha and data");
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 18, 0), 0xffu, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 30, 1), 0xffu, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 30, 8), 0xffu, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 19, 8), 0xffu, TEST_LOCATION );
+
+  tet_infoline( "Test that the third quadrant has no alpha");
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 18, 12), 0x00u, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 31, 12), 0x00u, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 31, 19), 0x00u, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 18, 19), 0x00u, TEST_LOCATION );
+
+  tet_infoline( "Test that the fourth quadrant has alpha and data");
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 1, 12), 0xffu, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 7, 12), 0xffu, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 7, 19), 0xffu, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 1, 19), 0xffu, TEST_LOCATION );
+
+  END_TEST;
+}
+
+
+int UtcDaliPixelBufferMask09(void)
+{
+  TestApplication application;
+  tet_infoline("Test scaling of large source image to larger than the alpha mask" );
+
+  unsigned int width = 32u;
+  unsigned int height = 20u;
+  Devel::PixelBuffer maskData = Devel::PixelBuffer::New( width, height, Pixel::RGBA8888 );
+  AlternateQuadrants( maskData );
+
+  // +-----XXXXX+
+  // |     XXXXX|
+  // |     XXXXX|
+  // |XXXXX     |
+  // |XXXXX     |
+  // *XXXXX-----+
+
+  width = 40u;
+  height = 50u;
+  Devel::PixelBuffer imageData = Devel::PixelBuffer::New( width, height, Pixel::RGBA8888 );
+  MaskCenterSquare(imageData);
+
+  // +----------+
+  // |  XXXXXX  |
+  // |  XXXXXX  |
+  // |  XXXXXX  |
+  // |  XXXXXX  |
+  // *----------+
+
+  imageData.ApplyMask( maskData, 1.6f, true );
+
+  // +-----XXXXX+   quadrant
+  // |     XXXXX|    1    2
+  // |     XXXXX|
+  // |XXXXX     |    4    3
+  // |XXXXX     |
+  // *XXXXX-----+
+
+  tet_infoline("Test that the image has been scaled and cropped to match the alpha mask" );
+  DALI_TEST_EQUALS( imageData.GetWidth(), 32, TEST_LOCATION );
+  DALI_TEST_EQUALS( imageData.GetHeight(), 20, TEST_LOCATION );
+
+  tet_infoline( "Test that the image has been resized (the center square should now fill the image)\n");
+  tet_infoline( "Test that the first quadrant has no alpha");
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 0, 0), 0x00u, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 5, 4), 0x00u, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 5, 8), 0x00u, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 14, 8), 0x00u, TEST_LOCATION );
+
+  tet_infoline( "Test that the second quadrant has alpha and data");
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 18, 0), 0xffu, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 30, 1), 0xffu, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 30, 8), 0xffu, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 19, 8), 0xffu, TEST_LOCATION );
+
+  tet_infoline( "Test that the third quadrant has no alpha");
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 18, 12), 0x00u, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 31, 12), 0x00u, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 31, 19), 0x00u, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 18, 19), 0x00u, TEST_LOCATION );
+
+  tet_infoline( "Test that the fourth quadrant has alpha and data");
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 1, 12), 0xffu, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 7, 12), 0xffu, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 7, 19), 0xffu, TEST_LOCATION );
+  DALI_TEST_EQUALS( GetAlphaAt(imageData, 1, 19), 0xffu, TEST_LOCATION );
+
+  END_TEST;
+}
index 5d04249..3705a12 100644 (file)
@@ -47,6 +47,7 @@ INCLUDE_DIRECTORIES(
     ../../../
     ../../../adaptors/tizen
     ../../../platform-abstractions/tizen
+    ../../../third-party/image-resampler
     ${${CAPI_LIB}_INCLUDE_DIRS}
     ../dali-adaptor/dali-test-suite-utils
     /usr/include/freetype2
index 3fdb96d..9a1a1f7 100644 (file)
@@ -27,40 +27,11 @@ namespace Internal
 namespace Adaptor
 {
 
-PixelBufferPtr ResizeMask( const PixelBuffer& inMask, ImageDimensions outDimensions )
-{
-  PixelBufferPtr mask;
-
-  if( inMask.GetWidth() != outDimensions.GetWidth() || inMask.GetHeight() != outDimensions.GetHeight() )
-  {
-    mask = PixelBuffer::New( outDimensions.GetWidth(), outDimensions.GetHeight(), inMask.GetPixelFormat() );
-    ImageDimensions inDimensions( inMask.GetWidth(), inMask.GetHeight() );
-
-    if( Pixel::GetBytesPerPixel( inMask.GetPixelFormat() ) == 4 )
-    {
-      Dali::Internal::Platform::LanczosSample4BPP( inMask.GetBuffer(), inDimensions,
-                                                   mask->GetBuffer(), outDimensions );
-    }
-    else if( inMask.GetPixelFormat() == Pixel::L8 )
-    {
-      Dali::Internal::Platform::LanczosSample1BPP( inMask.GetBuffer(), inDimensions,
-                                                   mask->GetBuffer(), outDimensions );
-    }
-  }
-  else
-  {
-    mask = const_cast<PixelBuffer*>(&inMask);
-  }
-  return mask;
-}
-
-void ApplyMaskToAlphaChannel( PixelBuffer& buffer, const PixelBuffer& inMask )
+void ApplyMaskToAlphaChannel( PixelBuffer& buffer, const PixelBuffer& mask )
 {
   int srcAlphaByteOffset=0;
   int srcAlphaMask=0;
-  Dali::Pixel::Format srcPixelFormat = inMask.GetPixelFormat();
-
-  PixelBufferPtr mask = ResizeMask( inMask, ImageDimensions( buffer.GetWidth(), buffer.GetHeight() ) );
+  Dali::Pixel::Format srcPixelFormat = mask.GetPixelFormat();
 
   if( Pixel::HasAlpha(srcPixelFormat) )
   {
@@ -76,7 +47,7 @@ void ApplyMaskToAlphaChannel( PixelBuffer& buffer, const PixelBuffer& inMask )
   Dali::Pixel::GetAlphaOffsetAndMask( buffer.GetPixelFormat(), destAlphaByteOffset, destAlphaMask );
 
   unsigned int srcBytesPerPixel = Dali::Pixel::GetBytesPerPixel( srcPixelFormat );
-  unsigned char* srcBuffer = mask->GetBuffer();
+  unsigned char* srcBuffer = mask.GetBuffer();
   unsigned char* destBuffer = buffer.GetBuffer();
 
   unsigned int destBytesPerPixel = Dali::Pixel::GetBytesPerPixel( buffer.GetPixelFormat() );
@@ -105,14 +76,13 @@ void ApplyMaskToAlphaChannel( PixelBuffer& buffer, const PixelBuffer& inMask )
   }
 }
 
-PixelBufferPtr CreateNewMaskedBuffer( const PixelBuffer& buffer, const PixelBuffer& inMask )
+PixelBufferPtr CreateNewMaskedBuffer( const PixelBuffer& buffer, const PixelBuffer& mask )
 {
   // Set up source alpha offsets
   int srcAlphaByteOffset=0;
   int srcAlphaMask=0;
-  Dali::Pixel::Format srcPixelFormat = inMask.GetPixelFormat();
+  Dali::Pixel::Format srcPixelFormat = mask.GetPixelFormat();
 
-  PixelBufferPtr mask = ResizeMask( inMask, ImageDimensions( buffer.GetWidth(), buffer.GetHeight() ) );
   if( Pixel::HasAlpha(srcPixelFormat) )
   {
     Dali::Pixel::GetAlphaOffsetAndMask( srcPixelFormat, srcAlphaByteOffset, srcAlphaMask );
@@ -123,7 +93,7 @@ PixelBufferPtr CreateNewMaskedBuffer( const PixelBuffer& buffer, const PixelBuff
   }
 
   unsigned int srcBytesPerPixel = Dali::Pixel::GetBytesPerPixel( srcPixelFormat );
-  unsigned char* srcBuffer = mask->GetBuffer();
+  unsigned char* srcBuffer = mask.GetBuffer();
 
   // Set up source color offsets
   Dali::Pixel::Format srcColorPixelFormat = buffer.GetPixelFormat();
index 192b9f5..382129b 100644 (file)
@@ -25,7 +25,6 @@ namespace Internal
 namespace Adaptor
 {
 
-
 /**
  * Apply the mask to a buffer's alpha channel
  * @param[in] buffer The buffer to apply the mask to
@@ -45,20 +44,6 @@ void ApplyMaskToAlphaChannel( PixelBuffer& buffer, const PixelBuffer& mask );
  */
 PixelBufferPtr CreateNewMaskedBuffer( const PixelBuffer& buffer, const PixelBuffer& mask );
 
-/**
- * Read a weighted sample from the given channel for a given coordinate
- * @param[in] buffer The buffer to read from
- * @param[in] pixelFormat The pixel format of the buffer
- * @param[in] stride The stride across the buffer
- * @param[in] x The x coordinate to sample from
- * @param[in] y The y coordinate to sample from
- * @param[in] width The width of the buffer in pixels
- * @param[in] height  The height of the buffer in pixels
- * @param[in] channel The channel to read from
- * @return An averaged value from the 4 pixels around the given coordinate
- */
-float ReadWeightedSample( unsigned char* buffer, Pixel::Format pixelFormat, int stride, float x, float y, int width, int height, Channel channel );
-
 } //namespace Adaptor
 } //namespace Internal
 } //namespace Dali
index f759d39..e8c47d6 100644 (file)
@@ -50,7 +50,6 @@ 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 Resampler::Filter FILTER_TYPE = Resampler::LANCZOS4; ///< Default filter used in the Resampler() function. Possible Lanczos filters are: lanczos3, lanczos4, lanczos6, lanczos12
 
 using Integration::Bitmap;
 using Integration::BitmapPtr;
@@ -1523,11 +1522,13 @@ void LinearSample4BPP( const unsigned char * __restrict__ inPixels,
   LinearSampleGeneric<Pixel4Bytes, BilinearFilter4Bytes, true>( inPixels, inputDimensions, outPixels, desiredDimensions );
 }
 
-void LanczosSample( const unsigned char * __restrict__ inPixels,
-                    ImageDimensions inputDimensions,
-                    unsigned char * __restrict__ outPixels,
-                    ImageDimensions desiredDimensions,
-                    int numChannels, bool hasAlpha )
+
+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;
@@ -1583,7 +1584,7 @@ void LanczosSample( const unsigned char * __restrict__ inPixels,
                                  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.
-                                 FILTER_TYPE,    // The type of filter. Currently Lanczos.
+                                 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,
@@ -1598,7 +1599,7 @@ void LanczosSample( const unsigned char * __restrict__ inPixels,
                                    Resampler::BOUNDARY_CLAMP,
                                    0.0f,
                                    1.0f,
-                                   FILTER_TYPE,
+                                   filterType,
                                    resamplers[0]->get_clist_x(),
                                    resamplers[0]->get_clist_y(),
                                    FILTER_SCALE,
@@ -1705,7 +1706,7 @@ void LanczosSample4BPP( const unsigned char * __restrict__ inPixels,
                         unsigned char * __restrict__ outPixels,
                         ImageDimensions desiredDimensions )
 {
-  LanczosSample( inPixels, inputDimensions, outPixels, desiredDimensions, 4, true );
+  Resample( inPixels, inputDimensions, outPixels, desiredDimensions, Resampler::LANCZOS4, 4, true );
 }
 
 void LanczosSample1BPP( const unsigned char * __restrict__ inPixels,
@@ -1714,7 +1715,7 @@ void LanczosSample1BPP( const unsigned char * __restrict__ inPixels,
                         ImageDimensions desiredDimensions )
 {
   // For L8 images
-  LanczosSample( inPixels, inputDimensions, outPixels, desiredDimensions, 1, false );
+  Resample( inPixels, inputDimensions, outPixels, desiredDimensions, Resampler::LANCZOS4, 1, false );
 }
 
 // Dispatch to a format-appropriate linear sampling function:
index 3b2ac9d..0c1e253 100644 (file)
@@ -24,6 +24,7 @@
 // INTERNAL INCLUDES
 #include <dali/integration-api/bitmap.h>
 #include <dali/public-api/images/image-operations.h>
+#include <resampler.h>
 
 namespace Dali
 {
@@ -357,6 +358,25 @@ void LanczosSample1BPP( const unsigned char * __restrict__ inPixels,
                         unsigned char * __restrict__ outPixels,
                         ImageDimensions desiredDimensions );
 
+/**
+ * @brief Resamples the input image with the Lanczos algorithm.
+ *
+ * @pre @p inPixels must not alias @p outPixels. The input image should be a totally
+ * separate buffer from the output buffer.
+ *
+ * @param[in] inPixels Pointer to the input image buffer.
+ * @param[in] inputDimensions The input dimensions of the image.
+ * @param[out] outPixels Pointer to the output image buffer.
+ * @param[in] desiredDimensions The output dimensions of the image.
+ */
+void Resample( const unsigned char * __restrict__ inPixels,
+               ImageDimensions inputDimensions,
+               unsigned char * __restrict__ outPixels,
+               ImageDimensions desiredDimensions,
+               Resampler::Filter filterType,
+               int numChannels, bool hasAlpha );
+
+
 /**@}*/
 
 /**