Add gaussian blur support in PixelBuffer
[platform/core/uifw/dali-adaptor.git] / platform-abstractions / portable / gaussian-blur.cpp
1 /*
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 // EXTERNAL INCLUDES
18 #include <memory.h>
19
20 // INTERNAL INCLUDES
21 #include "gaussian-blur.h"
22 #include "pixel-buffer-impl.h"
23
24 namespace Dali
25 {
26
27 namespace Internal
28 {
29
30 namespace Adaptor
31 {
32
33
34
35 void ConvoluteAndTranspose( unsigned char* inBuffer,
36                             unsigned char* outBuffer,
37                             const unsigned int bufferWidth,
38                             const unsigned int bufferHeight,
39                             const float blurRadius )
40 {
41   // Calculate the weights for gaussian blur
42   int radius = static_cast<int>( std::ceil( blurRadius ) );
43   int rows = radius * 2 + 1;
44
45   float sigma = ( blurRadius < Math::MACHINE_EPSILON_1 ) ? 0.0f : blurRadius * 0.4f + 0.6f; // The same equation used by Android
46   float sigma22 = 2.0f * sigma * sigma;
47   float sqrtSigmaPi2 = std::sqrt( 2.0f * Math::PI ) * sigma;
48   float radius2 = radius * radius;
49   float normalizeFactor = 0.0f;
50
51   float* weightMatrix = new float[rows];
52   int index = 0;
53
54   for ( int row = -radius; row <= radius; row++ )
55   {
56     float distance = row * row;
57     if ( distance > radius2 )
58     {
59       weightMatrix[index] = 0.0f;
60     }
61     else
62     {
63       weightMatrix[index] = static_cast<float>( std::exp( -( distance ) / sigma22 ) / sqrtSigmaPi2 );
64     }
65     normalizeFactor += weightMatrix[index];
66     index++;
67   }
68
69   for ( int i = 0; i < rows; i++ )
70   {
71     weightMatrix[i] /= normalizeFactor;
72   }
73
74   // Perform the convolution and transposition using the weights
75   int columns = rows;
76   int columns2 = columns / 2;
77   for ( unsigned int y = 0; y < bufferHeight; y++ )
78   {
79     unsigned int targetPixelIndex = y;
80     unsigned int ioffset = y * bufferWidth;
81     for ( unsigned int x = 0; x < bufferWidth; x++ )
82     {
83       float r = 0.0f, g = 0.0f, b = 0.0f, a = 0.0f;
84       int weightColumnOffset = columns2;
85       for ( int column = -columns2; column <= columns2; column++ )
86       {
87         float weight = weightMatrix[weightColumnOffset + column];
88         if ( fabsf( weight ) > Math::MACHINE_EPSILON_1 )
89         {
90           int ix = x + column;
91           ix = std::max( 0, std::min( ix, static_cast<int>( bufferWidth - 1 ) ) );
92           unsigned int sourcePixelIndex = ioffset + ix;
93           r += weight * inBuffer[sourcePixelIndex*4];
94           g += weight * inBuffer[sourcePixelIndex*4+1];
95           b += weight * inBuffer[sourcePixelIndex*4+2];
96           a += weight * inBuffer[sourcePixelIndex*4+3];
97         }
98       }
99
100       outBuffer[targetPixelIndex*4] = std::max( 0, std::min( static_cast<int>( r + 0.5f ), 255 ) );
101       outBuffer[targetPixelIndex*4+1] = std::max( 0, std::min( static_cast<int>( g + 0.5f ), 255 ) );
102       outBuffer[targetPixelIndex*4+2] = std::max( 0, std::min( static_cast<int>( b + 0.5f ), 255 ) );
103       outBuffer[targetPixelIndex*4+3] = std::max( 0, std::min( static_cast<int>( a + 0.5f ), 255 ) );
104
105       targetPixelIndex += bufferHeight;
106     }
107   }
108
109   delete [] weightMatrix;
110 }
111
112 void PerformGaussianBlurRGBA( PixelBuffer& buffer, const float blurRadius )
113 {
114   unsigned int bufferWidth = buffer.GetWidth();
115   unsigned int bufferHeight = buffer.GetHeight();
116
117   // Create a temporary buffer for the two-pass blur
118   PixelBufferPtr softShadowImageBuffer = PixelBuffer::New( bufferWidth, bufferHeight, Pixel::RGBA8888 );
119   memcpy( softShadowImageBuffer->GetBuffer(), buffer.GetBuffer(), 4u * bufferWidth * bufferHeight );
120
121   // We perform the blur first but write its output image buffer transposed, so that we
122   // can just do it in two passes. The first pass blurs horizontally and transposes, the
123   // second pass does the same, but as the image is now transposed, it's really doing a
124   // vertical blur. The second transposition makes the image the right way up again. This
125   // is much faster than doing a 2D convolution.
126   ConvoluteAndTranspose( buffer.GetBuffer(), softShadowImageBuffer->GetBuffer(), bufferWidth, bufferHeight, blurRadius );
127   ConvoluteAndTranspose( softShadowImageBuffer->GetBuffer(), buffer.GetBuffer(), bufferHeight, bufferWidth, blurRadius );
128
129   // On leaving scope, softShadowImageBuffer will get destroyed.
130 }
131
132 } //namespace Adaptor
133
134 }// namespace Internal
135
136 }// namespace Dali