[dali_1.9.9] Merge branch 'devel/master'
[platform/core/uifw/dali-adaptor.git] / dali / internal / imaging / common / 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 #include <cmath>
20
21 // INTERNAL INCLUDES
22 #include <dali/internal/imaging/common/gaussian-blur.h>
23 #include <dali/internal/imaging/common/pixel-buffer-impl.h>
24
25 namespace Dali
26 {
27
28 namespace Internal
29 {
30
31 namespace Adaptor
32 {
33
34
35
36 void ConvoluteAndTranspose( unsigned char* inBuffer,
37                             unsigned char* outBuffer,
38                             const unsigned int bufferWidth,
39                             const unsigned int bufferHeight,
40                             const float blurRadius )
41 {
42   // Calculate the weights for gaussian blur
43   int radius = static_cast<int>( std::ceil( blurRadius ) );
44   int rows = radius * 2 + 1;
45
46   float sigma = ( blurRadius < Math::MACHINE_EPSILON_1 ) ? 0.0f : blurRadius * 0.4f + 0.6f; // The same equation used by Android
47   float sigma22 = 2.0f * sigma * sigma;
48   float sqrtSigmaPi2 = std::sqrt( 2.0f * Math::PI ) * sigma;
49   float radius2 = radius * radius;
50   float normalizeFactor = 0.0f;
51
52   float* weightMatrix = new float[rows];
53   int index = 0;
54
55   for ( int row = -radius; row <= radius; row++ )
56   {
57     float distance = row * row;
58     if ( distance > radius2 )
59     {
60       weightMatrix[index] = 0.0f;
61     }
62     else
63     {
64       weightMatrix[index] = static_cast<float>( std::exp( -( distance ) / sigma22 ) / sqrtSigmaPi2 );
65     }
66     normalizeFactor += weightMatrix[index];
67     index++;
68   }
69
70   for ( int i = 0; i < rows; i++ )
71   {
72     weightMatrix[i] /= normalizeFactor;
73   }
74
75   // Perform the convolution and transposition using the weights
76   int columns = rows;
77   int columns2 = columns / 2;
78   for ( unsigned int y = 0; y < bufferHeight; y++ )
79   {
80     unsigned int targetPixelIndex = y;
81     unsigned int ioffset = y * bufferWidth;
82     for ( unsigned int x = 0; x < bufferWidth; x++ )
83     {
84       float r = 0.0f, g = 0.0f, b = 0.0f, a = 0.0f;
85       int weightColumnOffset = columns2;
86       for ( int column = -columns2; column <= columns2; column++ )
87       {
88         float weight = weightMatrix[weightColumnOffset + column];
89         if ( fabsf( weight ) > Math::MACHINE_EPSILON_1 )
90         {
91           int ix = x + column;
92           ix = std::max( 0, std::min( ix, static_cast<int>( bufferWidth - 1 ) ) );
93           unsigned int sourcePixelIndex = ioffset + ix;
94           r += weight * inBuffer[sourcePixelIndex*4];
95           g += weight * inBuffer[sourcePixelIndex*4+1];
96           b += weight * inBuffer[sourcePixelIndex*4+2];
97           a += weight * inBuffer[sourcePixelIndex*4+3];
98         }
99       }
100
101       outBuffer[targetPixelIndex*4] = std::max( 0, std::min( static_cast<int>( r + 0.5f ), 255 ) );
102       outBuffer[targetPixelIndex*4+1] = std::max( 0, std::min( static_cast<int>( g + 0.5f ), 255 ) );
103       outBuffer[targetPixelIndex*4+2] = std::max( 0, std::min( static_cast<int>( b + 0.5f ), 255 ) );
104       outBuffer[targetPixelIndex*4+3] = std::max( 0, std::min( static_cast<int>( a + 0.5f ), 255 ) );
105
106       targetPixelIndex += bufferHeight;
107     }
108   }
109
110   delete [] weightMatrix;
111 }
112
113 void PerformGaussianBlurRGBA( PixelBuffer& buffer, const float blurRadius )
114 {
115   unsigned int bufferWidth = buffer.GetWidth();
116   unsigned int bufferHeight = buffer.GetHeight();
117
118   // Create a temporary buffer for the two-pass blur
119   PixelBufferPtr softShadowImageBuffer = PixelBuffer::New( bufferWidth, bufferHeight, Pixel::RGBA8888 );
120   memcpy( softShadowImageBuffer->GetBuffer(), buffer.GetBuffer(), 4u * bufferWidth * bufferHeight );
121
122   // We perform the blur first but write its output image buffer transposed, so that we
123   // can just do it in two passes. The first pass blurs horizontally and transposes, the
124   // second pass does the same, but as the image is now transposed, it's really doing a
125   // vertical blur. The second transposition makes the image the right way up again. This
126   // is much faster than doing a 2D convolution.
127   ConvoluteAndTranspose( buffer.GetBuffer(), softShadowImageBuffer->GetBuffer(), bufferWidth, bufferHeight, blurRadius );
128   ConvoluteAndTranspose( softShadowImageBuffer->GetBuffer(), buffer.GetBuffer(), bufferHeight, bufferWidth, blurRadius );
129
130   // On leaving scope, softShadowImageBuffer will get destroyed.
131 }
132
133 } //namespace Adaptor
134
135 }// namespace Internal
136
137 }// namespace Dali