Color management: use partial buffer update when painting image
[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 }
37
38 void *GaussianAlphaYBlurOperation::initializeTileData(rcti *rect)
39 {
40         lockMutex();
41         if (!this->m_sizeavailable) {
42                 updateGauss();
43         }
44         void *buffer = getInputOperation(0)->initializeTileData(NULL);
45         unlockMutex();
46         return buffer;
47 }
48
49 void GaussianAlphaYBlurOperation::initExecution()
50 {
51         /* BlurBaseOperation::initExecution(); */ /* until we suppoer size input - comment this */
52
53         initMutex();
54
55         if (this->m_sizeavailable) {
56                 float rad = this->m_size * this->m_data->sizey;
57                 if (rad < 1)
58                         rad = 1;
59
60                 this->m_rad = rad;
61                 this->m_gausstab = BlurBaseOperation::make_gausstab(rad);
62                 this->m_distbuf_inv = BlurBaseOperation::make_dist_fac_inverse(rad, this->m_falloff);
63         }
64 }
65
66 void GaussianAlphaYBlurOperation::updateGauss()
67 {
68         if (this->m_gausstab == NULL) {
69                 updateSize();
70                 float rad = this->m_size * this->m_data->sizey;
71                 if (rad < 1)
72                         rad = 1;
73
74                 this->m_rad = rad;
75                 this->m_gausstab = BlurBaseOperation::make_gausstab(rad);
76         }
77
78         if (this->m_distbuf_inv == NULL) {
79                 updateSize();
80                 float rad = this->m_size * this->m_data->sizex;
81                 if (rad < 1)
82                         rad = 1;
83
84                 this->m_rad = rad;
85                 this->m_distbuf_inv = BlurBaseOperation::make_dist_fac_inverse(rad, this->m_falloff);
86         }
87 }
88
89 BLI_INLINE float finv_test(const float f, const bool test)
90 {
91         return (LIKELY(test == false)) ? f : 1.0f - f;
92 }
93
94 void GaussianAlphaYBlurOperation::executePixel(float output[4], int x, int y, void *data)
95 {
96         const bool do_invert = this->m_do_subtract;
97         MemoryBuffer *inputBuffer = (MemoryBuffer *)data;
98         float *buffer = inputBuffer->getBuffer();
99         int bufferwidth = inputBuffer->getWidth();
100         int bufferstartx = inputBuffer->getRect()->xmin;
101         int bufferstarty = inputBuffer->getRect()->ymin;
102
103         int miny = y - this->m_rad;
104         int maxy = y + this->m_rad;
105         int minx = x;
106         int maxx = x;
107         miny = max(miny, inputBuffer->getRect()->ymin);
108         minx = max(minx, inputBuffer->getRect()->xmin);
109         maxy = min(maxy, inputBuffer->getRect()->ymax);
110         maxx = min(maxx, inputBuffer->getRect()->xmax);
111
112         /* *** this is the main part which is different to 'GaussianYBlurOperation'  *** */
113         int step = getStep();
114
115         /* gauss */
116         float alpha_accum = 0.0f;
117         float multiplier_accum = 0.0f;
118
119         /* dilate */
120         float value_max = finv_test(buffer[(x * 4) + (y * 4 * bufferwidth)], do_invert); /* init with the current color to avoid unneeded lookups */
121         float distfacinv_max = 1.0f; /* 0 to 1 */
122
123         for (int ny = miny; ny < maxy; ny += step) {
124                 int bufferindex = ((minx - bufferstartx) * 4) + ((ny - bufferstarty) * 4 * bufferwidth);
125
126                 const int index = (ny - y) + this->m_rad;
127                 float value = finv_test(buffer[bufferindex], do_invert);
128                 float multiplier;
129
130                 /* gauss */
131                 {
132                         multiplier = this->m_gausstab[index];
133                         alpha_accum += value * multiplier;
134                         multiplier_accum += multiplier;
135                 }
136
137                 /* dilate - find most extreme color */
138                 if (value > value_max) {
139                         multiplier = this->m_distbuf_inv[index];
140                         value *= multiplier;
141                         if (value > value_max) {
142                                 value_max = value;
143                                 distfacinv_max = multiplier;
144                         }
145                 }
146
147         }
148
149         /* blend between the max value and gauss blue - gives nice feather */
150         const float value_blur  = alpha_accum / multiplier_accum;
151         const float value_final = (value_max * distfacinv_max) + (value_blur * (1.0f - distfacinv_max));
152         output[0] = finv_test(value_final, do_invert);
153 }
154
155 void GaussianAlphaYBlurOperation::deinitExecution()
156 {
157         BlurBaseOperation::deinitExecution();
158         MEM_freeN(this->m_gausstab);
159         this->m_gausstab = NULL;
160         MEM_freeN(this->m_distbuf_inv);
161         this->m_distbuf_inv = NULL;
162
163         deinitMutex();
164 }
165
166 bool GaussianAlphaYBlurOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
167 {
168         rcti newInput;
169 #if 0 /* until we add size input */
170         rcti sizeInput;
171         sizeInput.xmin = 0;
172         sizeInput.ymin = 0;
173         sizeInput.xmax = 5;
174         sizeInput.ymax = 5;
175
176         NodeOperation *operation = this->getInputOperation(1);
177         if (operation->determineDependingAreaOfInterest(&sizeInput, readOperation, output)) {
178                 return true;
179         }
180         else
181 #endif
182         {
183                 if (this->m_sizeavailable && this->m_gausstab != NULL) {
184                         newInput.xmax = input->xmax;
185                         newInput.xmin = input->xmin;
186                         newInput.ymax = input->ymax + this->m_rad;
187                         newInput.ymin = input->ymin - this->m_rad;
188                 }
189                 else {
190                         newInput.xmax = this->getWidth();
191                         newInput.xmin = 0;
192                         newInput.ymax = this->getHeight();
193                         newInput.ymin = 0;
194                 }
195                 return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
196         }
197 }