8e30a99b2a05dd38467a62f534338c902ede704f
[blender.git] / source / blender / compositor / operations / COM_PlaneTrackWarpImageOperation.cpp
1 /*
2  * Copyright 2013, 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  *              Sergey Sharybin
20  */
21
22 #include "COM_PlaneTrackWarpImageOperation.h"
23 #include "COM_ReadBufferOperation.h"
24
25 #include "MEM_guardedalloc.h"
26
27 #include "BLI_listbase.h"
28 #include "BLI_math.h"
29 #include "BLI_math_color.h"
30
31 extern "C" {
32         #include "BLI_jitter.h"
33
34         #include "BKE_movieclip.h"
35         #include "BKE_node.h"
36         #include "BKE_tracking.h"
37 }
38
39 BLI_INLINE bool isPointInsideQuad(const float x, const float y, const float corners[4][2])
40 {
41         float point[2];
42
43         point[0] = x;
44         point[1] = y;
45
46         return isect_point_tri_v2(point, corners[0], corners[1], corners[2]) ||
47                isect_point_tri_v2(point, corners[0], corners[2], corners[3]);
48 }
49
50 BLI_INLINE bool resolveUV(const float x, const float y, const float corners[4][2], float uv[2])
51 {
52         float point[2];
53         bool inside;
54
55         inside = isPointInsideQuad(x, y, corners);
56
57         point[0] = x;
58         point[1] = y;
59
60         /* Use reverse bilinear to get UV coordinates within original frame */
61         resolve_quad_uv(uv, point, corners[0], corners[1], corners[2], corners[3]);
62
63         return inside;
64 }
65
66 BLI_INLINE void resolveUVAndDxDy(const float x, const float y, const float corners[4][2],
67                                  float *u_r, float *v_r, float *dx_r, float *dy_r)
68 {
69         float inputUV[2];
70         float uv_a[2], uv_b[2];
71
72         float dx, dy;
73         float uv_l, uv_r;
74         float uv_u, uv_d;
75
76         bool ok1, ok2;
77
78         resolveUV(x, y, corners, inputUV);
79
80         /* adaptive sampling, red (U) channel */
81         ok1 = resolveUV(x - 1, y, corners, uv_a);
82         ok2 = resolveUV(x + 1, y, corners, uv_b);
83         uv_l = ok1 ? fabsf(inputUV[0] - uv_a[0]) : 0.0f;
84         uv_r = ok2 ? fabsf(inputUV[0] - uv_b[0]) : 0.0f;
85
86         dx = 0.5f * (uv_l + uv_r);
87
88         /* adaptive sampling, green (V) channel */
89         ok1 = resolveUV(x, y - 1, corners, uv_a);
90         ok2 = resolveUV(x, y + 1, corners, uv_b);
91         uv_u = ok1 ? fabsf(inputUV[1] - uv_a[1]) : 0.f;
92         uv_d = ok2 ? fabsf(inputUV[1] - uv_b[1]) : 0.f;
93
94         dy = 0.5f * (uv_u + uv_d);
95
96 #if 0
97         /* more adaptive sampling, red and green (UV) channels */
98         ok1 = resolveUV(x - 1, y - 1, corners, uv_a);
99         ok2 = resolveUV(x - 1, y + 1, corners, uv_b);
100         uv_l = ok1 ? fabsf(inputUV[0] - uv_a[0]) : 0.f;
101         uv_r = ok2 ? fabsf(inputUV[0] - uv_b[0]) : 0.f;
102         uv_u = ok1 ? fabsf(inputUV[1] - uv_a[1]) : 0.f;
103         uv_d = ok2 ? fabsf(inputUV[1] - uv_b[1]) : 0.f;
104
105         dx += 0.25f * (uv_l + uv_r);
106         dy += 0.25f * (uv_u + uv_d);
107
108         ok1 = resolveUV(x + 1, y - 1, corners, uv_a);
109         ok2 = resolveUV(x + 1, y + 1, corners, uv_b);
110         uv_l = ok1 ? fabsf(inputUV[0] - uv_a[0]) : 0.f;
111         uv_r = ok2 ? fabsf(inputUV[0] - uv_b[0]) : 0.f;
112         uv_u = ok1 ? fabsf(inputUV[1] - uv_a[1]) : 0.f;
113         uv_d = ok2 ? fabsf(inputUV[1] - uv_b[1]) : 0.f;
114
115         dx += 0.25f * (uv_l + uv_r);
116         dy += 0.25f * (uv_u + uv_d);
117
118         /* should use mipmap */
119         *dx_r = min(dx, 0.2f);
120         *dy_r = min(dy, 0.2f);
121 #else
122         *dx_r = dx;
123         *dy_r = dy;
124 #endif
125
126         *u_r = inputUV[0];
127         *v_r = inputUV[1];
128 }
129
130 PlaneTrackWarpImageOperation::PlaneTrackWarpImageOperation() : PlaneTrackCommonOperation()
131 {
132         this->addInputSocket(COM_DT_COLOR, COM_SC_NO_RESIZE);
133         this->addOutputSocket(COM_DT_COLOR);
134         this->m_pixelReader = NULL;
135         this->setComplex(true);
136 }
137
138 void PlaneTrackWarpImageOperation::initExecution()
139 {
140         PlaneTrackCommonOperation::initExecution();
141
142         this->m_pixelReader = this->getInputSocketReader(0);
143
144         const int osa = 8;
145         this->m_osa = osa;
146         BLI_jitter_init(this->m_jitter[0], osa);
147 }
148
149 void PlaneTrackWarpImageOperation::deinitExecution()
150 {
151         this->m_pixelReader = NULL;
152 }
153
154 void PlaneTrackWarpImageOperation::executePixel(float output[4], float x, float y, PixelSampler sampler)
155 {
156         float color_accum[4];
157
158         zero_v4(color_accum);
159         for (int sample = 0; sample < this->m_osa; sample++) {
160                 float current_x = x + this->m_jitter[sample][0],
161                       current_y = y + this->m_jitter[sample][1];
162                 if (isPointInsideQuad(current_x, current_y, this->m_frameSpaceCorners)) {
163                         float current_color[4];
164                         float u, v, dx, dy;
165
166                         resolveUVAndDxDy(current_x, current_y, this->m_frameSpaceCorners, &u, &v, &dx, &dy);
167
168                         u *= this->m_pixelReader->getWidth();
169                         v *= this->m_pixelReader->getHeight();
170
171                         this->m_pixelReader->read(current_color, u, v, dx, dy, COM_PS_NEAREST);
172                         premul_to_straight_v4(current_color);
173                         add_v4_v4(color_accum, current_color);
174                 }
175         }
176
177         mul_v4_v4fl(output, color_accum, 1.0f / this->m_osa);
178         straight_to_premul_v4(output);
179 }
180
181 bool PlaneTrackWarpImageOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
182 {
183         float frame_space_corners[4][2];
184
185         for (int i = 0; i < 4; i++) {
186                 frame_space_corners[i][0] = this->m_corners[i][0] * this->getWidth();
187                 frame_space_corners[i][1] = this->m_corners[i][1] * this->getHeight();
188         }
189
190         float UVs[4][2];
191
192         /* TODO(sergey): figure out proper way to do this. */
193         resolveUV(input->xmin - 2, input->ymin - 2, frame_space_corners, UVs[0]);
194         resolveUV(input->xmax + 2, input->ymin - 2, frame_space_corners, UVs[1]);
195         resolveUV(input->xmax + 2, input->ymax + 2, frame_space_corners, UVs[2]);
196         resolveUV(input->xmin - 2, input->ymax + 2, frame_space_corners, UVs[3]);
197
198         float min[2], max[2];
199         INIT_MINMAX2(min, max);
200         for (int i = 0; i < 4; i++) {
201                 minmax_v2v2_v2(min, max, UVs[i]);
202         }
203
204         rcti newInput;
205
206         newInput.xmin = min[0] * readOperation->getWidth() - 1;
207         newInput.ymin = min[1] * readOperation->getHeight() - 1;
208         newInput.xmax = max[0] * readOperation->getWidth() + 1;
209         newInput.ymax = max[1] * readOperation->getHeight() + 1;
210
211         return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
212 }