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