Increases the quality significantly.
Changed the outer sampling method to handle both 4 channel and 1 channel images
(in case the alpha mask is L8 only).
With some slight blurring of the alpha mask during scaling, have had to choose
different pixels in the test cases.
Change-Id: I348300cb4a28b1c0814495cc10ff589196b339e8
Signed-off-by: David Steele <david.steele@samsung.com>
DALI_TEST_EQUALS( buffer[3], 0x00u, TEST_LOCATION );
DALI_TEST_EQUALS( buffer[7], 0x00u, TEST_LOCATION );
- // Test that an odd pixel in the second quadrant has full alpha value
- DALI_TEST_EQUALS( buffer[23], 0xffu, TEST_LOCATION );
+ // Test that an odd pixel in the fourth quadrant has full alpha value
+ DALI_TEST_EQUALS( buffer[(6*10+7)*4+3], 0xffu, TEST_LOCATION );
- // Test that an even pixel in the second quadrant has full alpha value
- DALI_TEST_EQUALS( buffer[27], 0xffu, TEST_LOCATION );
+ // Test that an even pixel in the fourth quadrant has full alpha value
+ DALI_TEST_EQUALS( buffer[(6*10+8)*4+3], 0xffu, TEST_LOCATION );
END_TEST;
}
unsigned int width = 20u;
unsigned int height = 20u;
- Devel::PixelBuffer maskData = Devel::PixelBuffer::New( width, height, Pixel::L8 );
+ Devel::PixelBuffer maskData = Devel::PixelBuffer::New( width, height, Pixel::RGBA8888 );
+ Mask1stQuadrant(maskData);
+
+ width = 10u;
+ height = 10u;
+ Devel::PixelBuffer imageData = Devel::PixelBuffer::New( width, height, Pixel::RGBA8888 );
+ FillCheckerboard(imageData);
+
+ imageData.ApplyMask( maskData );
+
+ // Test that the pixel format has been promoted to RGBA8888
+ DALI_TEST_EQUALS( imageData.GetPixelFormat(), Pixel::RGBA8888, TEST_LOCATION );
+
+ // Test that a pixel in the first quadrant has no alpha value
+ unsigned char* buffer = imageData.GetBuffer();
+ DALI_TEST_EQUALS( buffer[3], 0x00u, TEST_LOCATION );
+ DALI_TEST_EQUALS( buffer[7], 0x00u, TEST_LOCATION );
+
+ // Test that an odd pixel in the second quadrant has full alpha value
+ DALI_TEST_EQUALS( buffer[39], 0xffu, TEST_LOCATION );
+
+ // Test that an even pixel in the second quadrant has no alpha value
+ DALI_TEST_EQUALS( buffer[27], 0x00u, TEST_LOCATION );
+
+ END_TEST;
+}
+
+int UtcDaliPixelBufferMask06(void)
+{
+ TestApplication application;
+ tet_infoline("Test application of alpha mask to same size RGBA8888 image");
+
+ unsigned int width = 10u;
+ unsigned int height = 10u;
+ Devel::PixelBuffer maskData = Devel::PixelBuffer::New( width, height, Pixel::RGBA8888 );
Mask1stQuadrant(maskData);
width = 10u;
DALI_TEST_EQUALS( buffer[7], 0x00u, TEST_LOCATION );
// Test that an odd pixel in the second quadrant has full alpha value
- DALI_TEST_EQUALS( buffer[23], 0xffu, TEST_LOCATION );
+ DALI_TEST_EQUALS( buffer[39], 0xffu, TEST_LOCATION );
// Test that an even pixel in the second quadrant has no alpha value
DALI_TEST_EQUALS( buffer[27], 0x00u, TEST_LOCATION );
#include "pixel-manipulation.h"
#include "alpha-mask.h"
#include "pixel-buffer-impl.h"
+#include <dali/public-api/images/image-operations.h> // For ImageDimensions
+#include <platform-abstractions/portable/image-operations.h>
namespace Dali
{
namespace Adaptor
{
-
-void ApplyMaskToAlphaChannel( PixelBuffer& buffer, const PixelBuffer& mask )
+PixelBufferPtr ResizeMask( const PixelBuffer& inMask, ImageDimensions outDimensions )
{
- const float rowFactor = float(mask.GetHeight()) / (1.0f * buffer.GetHeight());
- const float colFactor = float(mask.GetWidth()) / (1.0f * buffer.GetWidth()) ;
+ 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() );
- int numSamples = 1;
- if( mask.GetHeight() > buffer.GetHeight() || mask.GetWidth() > buffer.GetWidth() )
+ 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
{
- numSamples = 4;
+ mask = const_cast<PixelBuffer*>(&inMask);
}
+ return mask;
+}
+void ApplyMaskToAlphaChannel( PixelBuffer& buffer, const PixelBuffer& inMask )
+{
int srcAlphaByteOffset=0;
int srcAlphaMask=0;
- Dali::Pixel::Format srcPixelFormat = mask.GetPixelFormat();
+ Dali::Pixel::Format srcPixelFormat = inMask.GetPixelFormat();
+
+ PixelBufferPtr mask = ResizeMask( inMask, ImageDimensions( buffer.GetWidth(), buffer.GetHeight() ) );
- Channel alphaChannel = ALPHA;
if( Pixel::HasAlpha(srcPixelFormat) )
{
Dali::Pixel::GetAlphaOffsetAndMask( srcPixelFormat, srcAlphaByteOffset, srcAlphaMask );
else if( srcPixelFormat == Pixel::L8 )
{
srcAlphaMask=0xFF;
- alphaChannel = LUMINANCE;
}
int destAlphaByteOffset=0;
Dali::Pixel::GetAlphaOffsetAndMask( buffer.GetPixelFormat(), destAlphaByteOffset, destAlphaMask );
unsigned int srcBytesPerPixel = Dali::Pixel::GetBytesPerPixel( srcPixelFormat );
- int srcStride = mask.GetWidth() * srcBytesPerPixel;
- unsigned char* srcBuffer = mask.GetBuffer();
+ unsigned char* srcBuffer = mask->GetBuffer();
unsigned char* destBuffer = buffer.GetBuffer();
unsigned int destBytesPerPixel = Dali::Pixel::GetBytesPerPixel( buffer.GetPixelFormat() );
{
for( unsigned int col = 0; col < buffer.GetWidth(); ++col )
{
- if( numSamples == 1 )
- {
- srcOffset = floorf(row * rowFactor) * srcStride + floorf(col * colFactor) * srcBytesPerPixel;
- unsigned char alpha = srcBuffer[srcOffset + srcAlphaByteOffset] & srcAlphaMask;
- srcAlphaValue = float(alpha)/255.0f;
- }
- else
- {
- srcAlphaValue = ReadWeightedSample( srcBuffer, srcPixelFormat, srcStride, col*colFactor, row*rowFactor, mask.GetWidth(), mask.GetHeight(), alphaChannel );
- }
+ unsigned char alpha = srcBuffer[srcOffset + srcAlphaByteOffset] & srcAlphaMask;
+ srcAlphaValue = float(alpha)/255.0f;
unsigned char destAlpha = destBuffer[destOffset + destAlphaByteOffset] & destAlphaMask;
float destAlphaValue = Clamp(float(destAlpha) * srcAlphaValue, 0.0f, 255.0f);
destBuffer[destOffset + destAlphaByteOffset] &= ~destAlphaMask;
destBuffer[destOffset + destAlphaByteOffset] |= ( destAlpha & destAlphaMask );
+ srcOffset += srcBytesPerPixel;
destOffset += destBytesPerPixel;
}
}
}
-PixelBufferPtr CreateNewMaskedBuffer( const PixelBuffer& buffer, const PixelBuffer& mask )
+PixelBufferPtr CreateNewMaskedBuffer( const PixelBuffer& buffer, const PixelBuffer& inMask )
{
- const float rowFactor = float(mask.GetHeight()) / (1.0f * buffer.GetHeight());
- const float colFactor = float(mask.GetWidth()) / (1.0f * buffer.GetWidth()) ;
-
- int numSamples = 1;
- if( mask.GetHeight() > buffer.GetHeight() || mask.GetWidth() > buffer.GetWidth() )
- {
- numSamples = 4;
- }
-
// Set up source alpha offsets
int srcAlphaByteOffset=0;
int srcAlphaMask=0;
- Dali::Pixel::Format srcPixelFormat = mask.GetPixelFormat();
- Channel alphaChannel = ALPHA;
+ Dali::Pixel::Format srcPixelFormat = inMask.GetPixelFormat();
+
+ PixelBufferPtr mask = ResizeMask( inMask, ImageDimensions( buffer.GetWidth(), buffer.GetHeight() ) );
if( Pixel::HasAlpha(srcPixelFormat) )
{
Dali::Pixel::GetAlphaOffsetAndMask( srcPixelFormat, srcAlphaByteOffset, srcAlphaMask );
else if( srcPixelFormat == Pixel::L8 )
{
srcAlphaMask=0xFF;
- alphaChannel = LUMINANCE;
}
unsigned int srcBytesPerPixel = Dali::Pixel::GetBytesPerPixel( srcPixelFormat );
- int srcStride = mask.GetWidth() * srcBytesPerPixel;
- unsigned char* srcBuffer = mask.GetBuffer();
+ unsigned char* srcBuffer = mask->GetBuffer();
// Set up source color offsets
Dali::Pixel::Format srcColorPixelFormat = buffer.GetPixelFormat();
PixelBufferPtr newPixelBuffer = PixelBuffer::New( buffer.GetWidth(), buffer.GetHeight(),
destPixelFormat );
unsigned char* destBuffer = newPixelBuffer->GetBuffer();
-
unsigned char* oldBuffer = buffer.GetBuffer();
int srcAlphaOffset=0;
{
for( unsigned int col = 0; col < buffer.GetWidth(); ++col )
{
- if( numSamples == 1 )
- {
- srcAlphaOffset = floorf(row * rowFactor) * srcStride + floorf(col * colFactor) * srcBytesPerPixel;
- unsigned char alpha = srcBuffer[srcAlphaOffset + srcAlphaByteOffset] & srcAlphaMask;
- srcAlphaValue = float(alpha)/255.0f;
- }
- else
- {
- srcAlphaValue = ReadWeightedSample( srcBuffer, srcPixelFormat, srcStride, col*colFactor, row*rowFactor, mask.GetWidth(), mask.GetHeight(), alphaChannel );
- }
+ unsigned char alpha = srcBuffer[srcAlphaOffset + srcAlphaByteOffset] & srcAlphaMask;
+ srcAlphaValue = float(alpha)/255.0f;
ConvertColorChannelsToRGBA8888(oldBuffer, srcColorOffset, srcColorPixelFormat, destBuffer, destOffset );
destBuffer[destOffset + destAlphaByteOffset] |= ( destAlpha & destAlphaMask );
srcColorOffset += srcColorBytesPerPixel;
+ srcAlphaOffset += srcBytesPerPixel;
destOffset += destBytesPerPixel;
}
}
return newPixelBuffer;
}
-
-float ReadWeightedSample( unsigned char* buffer, Pixel::Format pixelFormat, int stride, float x, float y, int width, int height, Channel alphaChannel )
-{
- int srcRow = floorf( y );
- int srcCol = floorf( x );
-
- int bytesPerPixel = Dali::Pixel::GetBytesPerPixel( pixelFormat );
- int srcOffset = srcRow * stride + srcCol * bytesPerPixel;
- float samples[4];
-
- samples[0] = ReadChannel( buffer + srcOffset, pixelFormat, alphaChannel );
-
- if( srcCol < width-1 )
- {
- samples[1] = ReadChannel( buffer + srcOffset+bytesPerPixel, pixelFormat, alphaChannel );
- }
- else
- {
- samples[1] = samples[0];
- }
-
- if( srcRow < height-1 )
- {
- samples[2] = ReadChannel( buffer + stride + srcOffset, pixelFormat, alphaChannel );
- }
- else
- {
- samples[2] = samples[0];
- }
-
- if( srcRow < height-1 && srcCol < width-1 )
- {
- samples[3] = ReadChannel( buffer + stride + srcOffset + bytesPerPixel, pixelFormat, alphaChannel );
- }
- else
- {
- samples[3] = samples[2];
- }
-
- // Bilinear interpolation:
- float weight[4];
- weight[0] = float(srcRow+1.0f) - y;
- weight[1] = y - float(srcRow);
- weight[2] = float(srcCol+1.0f) - x;
- weight[3] = x - float(srcCol);
-
- return ( weight[2] * (samples[0] * weight[0] + samples[1] * weight[1]) +
- weight[3] * (samples[2] * weight[0] + samples[3] * weight[1]) ) / 255.0f;
-}
-
} //namespace Adaptor
}// namespace Internal
LinearSampleGeneric<Pixel4Bytes, BilinearFilter4Bytes, true>( inPixels, inputDimensions, outPixels, desiredDimensions );
}
-void LanczosSample4BPP( const unsigned char * __restrict__ inPixels,
- ImageDimensions inputDimensions,
- unsigned char * __restrict__ outPixels,
- ImageDimensions desiredDimensions )
+void LanczosSample( const unsigned char * __restrict__ inPixels,
+ ImageDimensions inputDimensions,
+ unsigned char * __restrict__ outPixels,
+ ImageDimensions desiredDimensions,
+ 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 = 3;
- const int NUMBER_OF_CHANNELS = 4;
-
- float srgbToLinear[MAX_UNSIGNED_CHAR + 1];
- for( int i = 0; i <= MAX_UNSIGNED_CHAR; ++i )
- {
- srgbToLinear[i] = pow( static_cast<float>( i ) * ONE_DIV_255, DEFAULT_SOURCE_GAMMA );
- }
+ const int ALPHA_CHANNEL = hasAlpha ? (numChannels-1) : 0;
- unsigned char linearToSrgb[LINEAR_TO_SRGB_TABLE_SIZE];
+ static bool loadColorSpaces = true;
+ static float srgbToLinear[MAX_UNSIGNED_CHAR + 1];
+ static unsigned char linearToSrgb[LINEAR_TO_SRGB_TABLE_SIZE];
- 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 )
+ if( loadColorSpaces ) // Only create the color space conversions on the first execution
{
- int k = static_cast<int>( 255.0f * pow( static_cast<float>( i ) * invLinearToSrgbTableSize, invSourceGamma ) + 0.5f );
- if( k < 0 )
+ loadColorSpaces = false;
+
+ for( int i = 0; i <= MAX_UNSIGNED_CHAR; ++i )
{
- k = 0;
+ srgbToLinear[i] = pow( static_cast<float>( i ) * ONE_DIV_255, DEFAULT_SOURCE_GAMMA );
}
- else if( k > MAX_UNSIGNED_CHAR )
+
+ 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 )
{
- k = MAX_UNSIGNED_CHAR;
+ 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 );
}
- linearToSrgb[i] = static_cast<unsigned char>( k );
}
- Resampler* resamplers[NUMBER_OF_CHANNELS] = { 0 };
- Vector<float> samples[NUMBER_OF_CHANNELS];
+ Resampler* resamplers[numChannels];
+ Vector<float> samples[numChannels];
const int srcWidth = inputDimensions.GetWidth();
const int srcHeight = inputDimensions.GetHeight();
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 < NUMBER_OF_CHANNELS; ++i )
+ for( int i = 1; i < numChannels; ++i )
{
resamplers[i] = new Resampler( srcWidth,
srcHeight,
samples[i].Resize( srcWidth );
}
- const int srcPitch = srcWidth * NUMBER_OF_CHANNELS;
- const int dstPitch = dstWidth * NUMBER_OF_CHANNELS;
+ const int srcPitch = srcWidth * numChannels;
+ const int dstPitch = dstWidth * numChannels;
int dstY = 0;
for( int srcY = 0; srcY < srcHeight; ++srcY )
for( int x = 0; x < srcWidth; ++x )
{
- for( int c = 0; c < NUMBER_OF_CHANNELS; ++c )
+ for( int c = 0; c < numChannels; ++c )
{
- if( c == ALPHA_CHANNEL )
+ if( c == ALPHA_CHANNEL && hasAlpha )
{
samples[c][x] = *pSrc++ * ONE_DIV_255;
}
}
}
- for( int c = 0; c < NUMBER_OF_CHANNELS; ++c )
+ for( int c = 0; c < numChannels; ++c )
{
if( !resamplers[c]->put_line( &samples[c][0] ) )
{
for(;;)
{
int compIndex;
- for( compIndex = 0; compIndex < NUMBER_OF_CHANNELS; ++compIndex )
+ for( compIndex = 0; compIndex < numChannels; ++compIndex )
{
const float* pOutputSamples = resamplers[compIndex]->get_line();
if( !pOutputSamples )
break;
}
- const bool isAlphaChannel = ( compIndex == ALPHA_CHANNEL );
+ const bool isAlphaChannel = ( compIndex == ALPHA_CHANNEL && hasAlpha );
DALI_ASSERT_DEBUG( dstY < dstHeight );
unsigned char* pDst = &outPixels[dstY * dstPitch + compIndex];
*pDst = linearToSrgb[j];
}
- pDst += NUMBER_OF_CHANNELS;
+ pDst += numChannels;
}
}
- if( compIndex < NUMBER_OF_CHANNELS )
+ if( compIndex < numChannels )
{
break;
}
}
// Delete the resamplers.
- for( int i = 0; i < NUMBER_OF_CHANNELS; ++i )
+ 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 )
+{
+ LanczosSample( inPixels, inputDimensions, outPixels, desiredDimensions, 4, true );
+}
+
+void LanczosSample1BPP( const unsigned char * __restrict__ inPixels,
+ ImageDimensions inputDimensions,
+ unsigned char * __restrict__ outPixels,
+ ImageDimensions desiredDimensions )
+{
+ // For L8 images
+ LanczosSample( inPixels, inputDimensions, outPixels, desiredDimensions, 1, false );
+}
+
// Dispatch to a format-appropriate linear sampling function:
void LinearSample( const unsigned char * __restrict__ inPixels,
ImageDimensions inDimensions,
/*
- * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2017 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.
* @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 input one.
+ * separate buffer from the output buffer.
*
* @param[in] inPixels Pointer to the input image buffer.
* @param[in] inputDimensions The input dimensions of the image.
ImageDimensions inputDimensions,
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 LanczosSample1BPP( const unsigned char * __restrict__ inPixels,
+ ImageDimensions inputDimensions,
+ unsigned char * __restrict__ outPixels,
+ ImageDimensions desiredDimensions );
+
/**@}*/
/**