svn merge ^/trunk/blender -r50173:50179
[blender.git] / source / blender / compositor / operations / COM_GaussianAlphaYBlurOperation.cpp
1 /*
2  * Copyright 2011, Blender Foundation.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Contributor: 
19  *              Jeroen Bakker 
20  *              Monique Dewanchand
21  *              Campbell Barton
22  */
23
24 #include "COM_GaussianAlphaYBlurOperation.h"
25 #include "BLI_math.h"
26 #include "MEM_guardedalloc.h"
27
28 extern "C" {
29         #include "RE_pipeline.h"
30 }
31
32 GaussianAlphaYBlurOperation::GaussianAlphaYBlurOperation() : BlurBaseOperation(COM_DT_VALUE)
33 {
34         this->m_gausstab = NULL;
35         this->m_rad = 0;
36         this->m_falloff = -1;  /* intentionally invalid, so we can detect uninitialized values */
37 }
38
39 void *GaussianAlphaYBlurOperation::initializeTileData(rcti *rect)
40 {
41         lockMutex();
42         if (!this->m_sizeavailable) {
43                 updateGauss();
44         }
45         void *buffer = getInputOperation(0)->initializeTileData(NULL);
46         unlockMutex();
47         return buffer;
48 }
49
50 void GaussianAlphaYBlurOperation::initExecution()
51 {
52         /* BlurBaseOperation::initExecution(); */ /* until we suppoer size input - comment this */
53
54         initMutex();
55
56         if (this->m_sizeavailable) {
57                 float rad = this->m_size * this->m_data->sizey;
58                 if (rad < 1)
59                         rad = 1;
60
61                 this->m_rad = rad;
62                 this->m_gausstab = BlurBaseOperation::make_gausstab(rad);
63                 this->m_distbuf_inv = BlurBaseOperation::make_dist_fac_inverse(rad, this->m_falloff);
64         }
65 }
66
67 void GaussianAlphaYBlurOperation::updateGauss()
68 {
69         if (this->m_gausstab == NULL) {
70                 updateSize();
71                 float rad = this->m_size * this->m_data->sizey;
72                 if (rad < 1)
73                         rad = 1;
74
75                 this->m_rad = rad;
76                 this->m_gausstab = BlurBaseOperation::make_gausstab(rad);
77         }
78
79         if (this->m_distbuf_inv == NULL) {
80                 updateSize();
81                 float rad = this->m_size * this->m_data->sizex;
82                 if (rad < 1)
83                         rad = 1;
84
85                 this->m_rad = rad;
86                 this->m_distbuf_inv = BlurBaseOperation::make_dist_fac_inverse(rad, this->m_falloff);
87         }
88 }
89
90 BLI_INLINE float finv_test(const float f, const bool test)
91 {
92         return (LIKELY(test == false)) ? f : 1.0f - f;
93 }
94
95 void GaussianAlphaYBlurOperation::executePixel(float output[4], int x, int y, void *data)
96 {
97         const bool do_invert = this->m_do_subtract;
98         MemoryBuffer *inputBuffer = (MemoryBuffer *)data;
99         float *buffer = inputBuffer->getBuffer();
100         int bufferwidth = inputBuffer->getWidth();
101         int bufferstartx = inputBuffer->getRect()->xmin;
102         int bufferstarty = inputBuffer->getRect()->ymin;
103
104         int miny = y - this->m_rad;
105         int maxy = y + this->m_rad;
106         int minx = x;
107         int maxx = x;
108         miny = max(miny, inputBuffer->getRect()->ymin);
109         minx = max(minx, inputBuffer->getRect()->xmin);
110         maxy = min(maxy, inputBuffer->getRect()->ymax);
111         maxx = min(maxx, inputBuffer->getRect()->xmax);
112
113         /* *** this is the main part which is different to 'GaussianYBlurOperation'  *** */
114         int step = getStep();
115
116         /* gauss */
117         float alpha_accum = 0.0f;
118         float multiplier_accum = 0.0f;
119
120         /* dilate */
121         float value_max = finv_test(buffer[(x * 4) + (y * 4 * bufferwidth)], do_invert); /* init with the current color to avoid unneeded lookups */
122         float distfacinv_max = 1.0f; /* 0 to 1 */
123
124         for (int ny = miny; ny < maxy; ny += step) {
125                 int bufferindex = ((minx - bufferstartx) * 4) + ((ny - bufferstarty) * 4 * bufferwidth);
126
127                 const int index = (ny - y) + this->m_rad;
128                 float value = finv_test(buffer[bufferindex], do_invert);
129                 float multiplier;
130
131                 /* gauss */
132                 {
133                         multiplier = this->m_gausstab[index];
134                         alpha_accum += value * multiplier;
135                         multiplier_accum += multiplier;
136                 }
137
138                 /* dilate - find most extreme color */
139                 if (value > value_max) {
140                         multiplier = this->m_distbuf_inv[index];
141                         value *= multiplier;
142                         if (value > value_max) {
143                                 value_max = value;
144                                 distfacinv_max = multiplier;
145                         }
146                 }
147
148         }
149
150         /* blend between the max value and gauss blue - gives nice feather */
151         const float value_blur  = alpha_accum / multiplier_accum;
152         const float value_final = (value_max * distfacinv_max) + (value_blur * (1.0f - distfacinv_max));
153         output[0] = finv_test(value_final, do_invert);
154 }
155
156 void GaussianAlphaYBlurOperation::deinitExecution()
157 {
158         BlurBaseOperation::deinitExecution();
159         MEM_freeN(this->m_gausstab);
160         this->m_gausstab = NULL;
161         MEM_freeN(this->m_distbuf_inv);
162         this->m_distbuf_inv = NULL;
163
164         deinitMutex();
165 }
166
167 bool GaussianAlphaYBlurOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
168 {
169         rcti newInput;
170 #if 0 /* until we add size input */
171         rcti sizeInput;
172         sizeInput.xmin = 0;
173         sizeInput.ymin = 0;
174         sizeInput.xmax = 5;
175         sizeInput.ymax = 5;
176
177         NodeOperation *operation = this->getInputOperation(1);
178         if (operation->determineDependingAreaOfInterest(&sizeInput, readOperation, output)) {
179                 return true;
180         }
181         else
182 #endif
183         {
184                 if (this->m_sizeavailable && this->m_gausstab != NULL) {
185                         newInput.xmax = input->xmax;
186                         newInput.xmin = input->xmin;
187                         newInput.ymax = input->ymax + this->m_rad;
188                         newInput.ymin = input->ymin - this->m_rad;
189                 }
190                 else {
191                         newInput.xmax = this->getWidth();
192                         newInput.xmin = 0;
193                         newInput.ymax = this->getHeight();
194                         newInput.ymin = 0;
195                 }
196                 return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
197         }
198 }