From: Richard Huang Date: Tue, 24 Oct 2017 15:11:05 +0000 (+0100) Subject: [4.0] Add gaussian blur support in PixelBuffer X-Git-Tag: accepted/tizen/4.0/unified/20171103.162651~2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=2bb66930795a09950fab348b14697720a76aec62;p=platform%2Fcore%2Fuifw%2Fdali-adaptor.git [4.0] Add gaussian blur support in PixelBuffer Change-Id: Ibc05b718340bf3f6c6e51746f9ee708a9d6ae137 Signed-off-by: Adeel Kazmi --- diff --git a/adaptors/common/pixel-buffer-impl.cpp b/adaptors/common/pixel-buffer-impl.cpp index 1de5382..24ea4bc 100644 --- a/adaptors/common/pixel-buffer-impl.cpp +++ b/adaptors/common/pixel-buffer-impl.cpp @@ -25,6 +25,7 @@ // INTERNAL INCLUDES #include "pixel-manipulation.h" #include "alpha-mask.h" +#include "gaussian-blur.h" #include namespace Dali @@ -309,6 +310,22 @@ PixelBufferPtr PixelBuffer::NewResize( const PixelBuffer& inBuffer, ImageDimensi return outBuffer; } +void PixelBuffer::ApplyGaussianBlur( const float blurRadius ) +{ + // This method only works for pixel buffer in RGBA format. + if( mWidth > 0 && mHeight > 0 && mPixelFormat == Pixel::RGBA8888 ) + { + if ( blurRadius > Math::MACHINE_EPSILON_1 ) + { + PerformGaussianBlurRGBA( *this, blurRadius ); + } + } + else + { + DALI_LOG_ERROR( "Trying to apply gaussian blur to an empty pixel buffer or a pixel buffer not in RGBA format" ); + } +} + }// namespace Adaptor }// namespace Internal }// namespace Dali diff --git a/adaptors/common/pixel-buffer-impl.h b/adaptors/common/pixel-buffer-impl.h index ec7ee2b..3484416 100644 --- a/adaptors/common/pixel-buffer-impl.h +++ b/adaptors/common/pixel-buffer-impl.h @@ -150,6 +150,13 @@ public: */ void ApplyMask( const PixelBuffer& mask, float contentScale, bool cropToMask ); + /** + * Apply a Gaussian blur to the current buffer with the given radius. + * + * @param[in] blurRadius The radius for Gaussian blur + */ + void ApplyGaussianBlur( const float blurRadius ); + private: /* * Undefined copy constructor. diff --git a/adaptors/devel-api/adaptor-framework/pixel-buffer.cpp b/adaptors/devel-api/adaptor-framework/pixel-buffer.cpp index 0b366d0..b767d49 100644 --- a/adaptors/devel-api/adaptor-framework/pixel-buffer.cpp +++ b/adaptors/devel-api/adaptor-framework/pixel-buffer.cpp @@ -101,6 +101,11 @@ void PixelBuffer::ApplyMask( PixelBuffer mask, float contentScale, bool cropToMa GetImplementation(*this).ApplyMask( GetImplementation( mask ), contentScale, cropToMask ); } +void PixelBuffer::ApplyGaussianBlur( const float blurRadius ) +{ + GetImplementation(*this).ApplyGaussianBlur( blurRadius ); +} + } // namespace Devel } // namespace Dali diff --git a/adaptors/devel-api/adaptor-framework/pixel-buffer.h b/adaptors/devel-api/adaptor-framework/pixel-buffer.h index 91c7161..dfe228b 100644 --- a/adaptors/devel-api/adaptor-framework/pixel-buffer.h +++ b/adaptors/devel-api/adaptor-framework/pixel-buffer.h @@ -170,6 +170,15 @@ public: */ void ApplyMask( PixelBuffer mask, float contentScale=1.0f, bool cropToMask=false ); + /** + * Apply a Gaussian blur to this pixel data with the given radius. + * + * @note A bigger radius will yield a blurrier image. Only works for pixel data in RGBA format. + * + * @param[in] blurRadius The radius for Gaussian blur. A value of 0 or negative value indicates no blur. + */ + void ApplyGaussianBlur( const float blurRadius ); + public: /** diff --git a/automated-tests/src/dali-adaptor/utc-Dali-PixelBuffer.cpp b/automated-tests/src/dali-adaptor/utc-Dali-PixelBuffer.cpp index 24a17d0..709b2c9 100644 --- a/automated-tests/src/dali-adaptor/utc-Dali-PixelBuffer.cpp +++ b/automated-tests/src/dali-adaptor/utc-Dali-PixelBuffer.cpp @@ -717,3 +717,36 @@ int UtcDaliPixelBufferMask09(void) 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; +} diff --git a/platform-abstractions/portable/gaussian-blur.cpp b/platform-abstractions/portable/gaussian-blur.cpp new file mode 100644 index 0000000..443d175 --- /dev/null +++ b/platform-abstractions/portable/gaussian-blur.cpp @@ -0,0 +1,136 @@ +/* + * 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 + +// 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( 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( 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( 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( r + 0.5f ), 255 ) ); + outBuffer[targetPixelIndex*4+1] = std::max( 0, std::min( static_cast( g + 0.5f ), 255 ) ); + outBuffer[targetPixelIndex*4+2] = std::max( 0, std::min( static_cast( b + 0.5f ), 255 ) ); + outBuffer[targetPixelIndex*4+3] = std::max( 0, std::min( static_cast( 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 diff --git a/platform-abstractions/portable/gaussian-blur.h b/platform-abstractions/portable/gaussian-blur.h new file mode 100644 index 0000000..d55308d --- /dev/null +++ b/platform-abstractions/portable/gaussian-blur.h @@ -0,0 +1,61 @@ +#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 diff --git a/platform-abstractions/tizen/file.list b/platform-abstractions/tizen/file.list index bb4e908..5233f6e 100755 --- a/platform-abstractions/tizen/file.list +++ b/platform-abstractions/tizen/file.list @@ -19,7 +19,8 @@ tizen_platform_abstraction_src_files = \ \ $(portable_platform_abstraction_src_dir)/image-operations.cpp \ $(portable_platform_abstraction_src_dir)/pixel-manipulation.cpp \ - $(portable_platform_abstraction_src_dir)/alpha-mask.cpp + $(portable_platform_abstraction_src_dir)/alpha-mask.cpp \ + $(portable_platform_abstraction_src_dir)/gaussian-blur.cpp # Add public headers here: