END_TEST;
}
+
+int UtcDaliPixelBufferGaussianBlur(void)
+{
+ TestApplication application;
+
+ Devel::PixelBuffer imageData = Devel::PixelBuffer::New( 10, 10, Pixel::RGBA8888 );
+ FillCheckerboard(imageData);
+
+ DALI_TEST_EQUALS( imageData.GetWidth(), 10, TEST_LOCATION ) ;
+ DALI_TEST_EQUALS( imageData.GetHeight(), 10, TEST_LOCATION ) ;
+
+ unsigned char* buffer = imageData.GetBuffer();
+
+ // Test that an even pixel in the odd row has full alpha value
+ DALI_TEST_EQUALS( buffer[43], 0xffu, TEST_LOCATION );
+
+ // Test that an even pixel in the even row has no alpha value
+ DALI_TEST_EQUALS( buffer[55], 0x00u, TEST_LOCATION );
+
+ imageData.ApplyGaussianBlur( 0.0f );
+
+ // Test that the pixels' alpha values are not changed because there is no blur
+ DALI_TEST_EQUALS( buffer[43], 0xffu, TEST_LOCATION );
+ DALI_TEST_EQUALS( buffer[55], 0x00u, TEST_LOCATION );
+
+ imageData.ApplyGaussianBlur( 1.0f );
+
+ // Test that the pixels' alpha values are changed after applying gaussian blur
+ DALI_TEST_EQUALS( buffer[43], 0x7Au, TEST_LOCATION );
+ DALI_TEST_EQUALS( buffer[55], 0x7Eu, TEST_LOCATION );
+
+ END_TEST;
+}
--- /dev/null
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// EXTERNAL INCLUDES
+#include <memory.h>
+
+// INTERNAL INCLUDES
+#include "gaussian-blur.h"
+#include "pixel-buffer-impl.h"
+
+namespace Dali
+{
+
+namespace Internal
+{
+
+namespace Adaptor
+{
+
+
+
+void ConvoluteAndTranspose( unsigned char* inBuffer,
+ unsigned char* outBuffer,
+ const unsigned int bufferWidth,
+ const unsigned int bufferHeight,
+ const float blurRadius )
+{
+ // Calculate the weights for gaussian blur
+ int radius = static_cast<int>( std::ceil( blurRadius ) );
+ int rows = radius * 2 + 1;
+
+ float sigma = ( blurRadius < Math::MACHINE_EPSILON_1 ) ? 0.0f : blurRadius * 0.4f + 0.6f; // The same equation used by Android
+ float sigma22 = 2.0f * sigma * sigma;
+ float sqrtSigmaPi2 = std::sqrt( 2.0f * Math::PI ) * sigma;
+ float radius2 = radius * radius;
+ float normalizeFactor = 0.0f;
+
+ float* weightMatrix = new float[rows];
+ int index = 0;
+
+ for ( int row = -radius; row <= radius; row++ )
+ {
+ float distance = row * row;
+ if ( distance > radius2 )
+ {
+ weightMatrix[index] = 0.0f;
+ }
+ else
+ {
+ weightMatrix[index] = static_cast<float>( std::exp( -( distance ) / sigma22 ) / sqrtSigmaPi2 );
+ }
+ normalizeFactor += weightMatrix[index];
+ index++;
+ }
+
+ for ( int i = 0; i < rows; i++ )
+ {
+ weightMatrix[i] /= normalizeFactor;
+ }
+
+ // Perform the convolution and transposition using the weights
+ int columns = rows;
+ int columns2 = columns / 2;
+ for ( unsigned int y = 0; y < bufferHeight; y++ )
+ {
+ unsigned int targetPixelIndex = y;
+ unsigned int ioffset = y * bufferWidth;
+ for ( unsigned int x = 0; x < bufferWidth; x++ )
+ {
+ float r = 0.0f, g = 0.0f, b = 0.0f, a = 0.0f;
+ int weightColumnOffset = columns2;
+ for ( int column = -columns2; column <= columns2; column++ )
+ {
+ float weight = weightMatrix[weightColumnOffset + column];
+ if ( fabsf( weight ) > Math::MACHINE_EPSILON_1 )
+ {
+ int ix = x + column;
+ ix = std::max( 0, std::min( ix, static_cast<int>( bufferWidth - 1 ) ) );
+ unsigned int sourcePixelIndex = ioffset + ix;
+ r += weight * inBuffer[sourcePixelIndex*4];
+ g += weight * inBuffer[sourcePixelIndex*4+1];
+ b += weight * inBuffer[sourcePixelIndex*4+2];
+ a += weight * inBuffer[sourcePixelIndex*4+3];
+ }
+ }
+
+ outBuffer[targetPixelIndex*4] = std::max( 0, std::min( static_cast<int>( r + 0.5f ), 255 ) );
+ outBuffer[targetPixelIndex*4+1] = std::max( 0, std::min( static_cast<int>( g + 0.5f ), 255 ) );
+ outBuffer[targetPixelIndex*4+2] = std::max( 0, std::min( static_cast<int>( b + 0.5f ), 255 ) );
+ outBuffer[targetPixelIndex*4+3] = std::max( 0, std::min( static_cast<int>( a + 0.5f ), 255 ) );
+
+ targetPixelIndex += bufferHeight;
+ }
+ }
+
+ delete [] weightMatrix;
+}
+
+void PerformGaussianBlurRGBA( PixelBuffer& buffer, const float blurRadius )
+{
+ unsigned int bufferWidth = buffer.GetWidth();
+ unsigned int bufferHeight = buffer.GetHeight();
+
+ // Create a temporary buffer for the two-pass blur
+ PixelBufferPtr softShadowImageBuffer = PixelBuffer::New( bufferWidth, bufferHeight, Pixel::RGBA8888 );
+ memcpy( softShadowImageBuffer->GetBuffer(), buffer.GetBuffer(), 4u * bufferWidth * bufferHeight );
+
+ // We perform the blur first but write its output image buffer transposed, so that we
+ // can just do it in two passes. The first pass blurs horizontally and transposes, the
+ // second pass does the same, but as the image is now transposed, it's really doing a
+ // vertical blur. The second transposition makes the image the right way up again. This
+ // is much faster than doing a 2D convolution.
+ ConvoluteAndTranspose( buffer.GetBuffer(), softShadowImageBuffer->GetBuffer(), bufferWidth, bufferHeight, blurRadius );
+ ConvoluteAndTranspose( softShadowImageBuffer->GetBuffer(), buffer.GetBuffer(), bufferHeight, bufferWidth, blurRadius );
+
+ // On leaving scope, softShadowImageBuffer will get destroyed.
+}
+
+} //namespace Adaptor
+
+}// namespace Internal
+
+}// namespace Dali
--- /dev/null
+#ifndef DALI_INTERNAL_ADAPTOR_GAUSSIAN_BLUR_H
+#define DALI_INTERNAL_ADAPTOR_GAUSSIAN_BLUR_H
+
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "pixel-buffer-impl.h"
+
+namespace Dali
+{
+
+namespace Internal
+{
+
+namespace Adaptor
+{
+
+/**
+ * Perform a one dimension Gaussian blur convolution and write its output buffer transposed.
+ *
+ * @param[in] inBuffer The input buffer with the source image
+ * @param[in] outBuffer The output buffer with the Gaussian blur applied and transposed
+ * @param[in] bufferWidth The width of the buffer
+ * @param[in] bufferHeight The height of the buffer
+ * @param[in] blurRadius The radius for Gaussian blur
+ */
+void ConvoluteAndTranspose( unsigned char* inBuffer, unsigned char* outBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight, const float blurRadius );
+
+/**
+ * Perform Gaussian blur on a buffer.
+ *
+ * A Gaussian blur is generated by replacing each pixel’s color values with the average of the surrounding pixels’
+ * colors. This region is a circle with the given radius. Thus, a bigger radius yields a blurrier image.
+ *
+ * @note The pixel format of the buffer must be RGBA8888
+ *
+ * @param[in] buffer The buffer to apply the Gaussian blur to
+ * @param[in] blurRadius The radius for Gaussian blur
+ */
+void PerformGaussianBlurRGBA( PixelBuffer& buffer, const float blurRadius );
+
+} //namespace Adaptor
+
+} //namespace Internal
+
+} //namespace Dali
+
+#endif // DALI_INTERNAL_ADAPTOR_GAUSSIAN_BLUR_H