add load_file apply button, sketcher colorpicker alpha fix, tweak resize filebox...
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / overlayframe.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  * Copyright (C) 2012 Monty <monty@xiph.org>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  */
22
23 #include "clip.h"
24 #include "edl.inc"
25 #include "mutex.h"
26 #include "overlayframe.h"
27 #include "units.h"
28
29 /*
30  * New resampler code; replace the original somehwat blurry engine
31  * with a fairly standard kernel resampling core.  This could be used
32  * for full affine transformation but only implements scale/translate.
33  * Mostly reuses the old blending macro code.
34  *
35  * Pixel convention:
36  *
37  *  1) Pixels are points, not areas or squares.
38  *
39  *  2) To maintain the usual edge and scaling conventions, pixels are
40  *     set inward from the image edge, eg, the left edge of an image is
41  *     at pixel location x=-.5, not x=0.  Although pixels are not
42  *     squares, the usual way of stating this is 'the pixel is located
43  *     at the center of its square'.
44  *
45  *  3) Because of 1 and 2, we must truncate and weight the kernel
46  *     convolution at the edge of the input area.  Otherwise, all
47  *     resampled areas would be bordered by a transparency halo. E.g.
48  *     in the old engine, upsampling HDV to 1920x1080 results in the
49  *     left and right edges being partially transparent and underlying
50  *     layers shining through.
51  *
52  *   4) The contribution of fractional pixels at the edges of input
53  *     ranges are weighted according to the fraction.  Note that the
54  *     kernel weighting is adjusted, not the opacity.  This is one
55  *     exception to 'pixels have no area'.
56  *
57  *  5) The opacity of fractional pixels at the edges of the output
58  *     range is adjusted according to the fraction. This is the other
59  *     exception to 'pixels have no area'.
60  *
61  * Fractional alpha blending has been modified across the board from:
62  *    output_alpha = input_alpha > output_alpha ? input_alpha : output_alpha;
63  *  to:
64  *    output_alpha = output_alpha + ((max - output_alpha) * input_alpha) / max;
65  */
66
67 /* Sinc needed for Lanczos kernel */
68 static float sinc(const float x)
69 {
70         if(fabsf(x) < TRANSFORM_MIN) return 1.0f;
71         float y = x * M_PI;
72         return sinf(y) / y;
73 }
74
75 /*
76  * All resampling (except Nearest Neighbor) is performed via
77  *   transformed 2D resampling kernels bult from 1D lookups.
78  */
79 OverlayKernel::OverlayKernel(int interpolation_type)
80 {
81         int i;
82         this->type = interpolation_type;
83
84         switch(interpolation_type)
85         {
86         case BILINEAR:
87                 width = 1.f;
88                 lookup = new float[(n = TRANSFORM_SPP) + 1];
89                 for (i = 0; i <= TRANSFORM_SPP; i++)
90                         lookup[i] = (float)(TRANSFORM_SPP - i) / TRANSFORM_SPP;
91                 break;
92
93         /* Use a Catmull-Rom filter (not b-spline) */
94         case BICUBIC:
95                 width = 2.;
96                 lookup = new float[(n = 2 * TRANSFORM_SPP) + 1];
97                 for(i = 0; i <= TRANSFORM_SPP; i++) {
98                         float x = i / (float)TRANSFORM_SPP;
99                         lookup[i] = 1.f - 2.5f * x * x + 1.5f * x * x * x;
100                 }
101                 for(; i <= 2 * TRANSFORM_SPP; i++) {
102                         float x = i / (float)TRANSFORM_SPP;
103                         lookup[i] = 2.f - 4.f * x  + 2.5f * x * x - .5f * x * x * x;
104                 }
105                 break;
106
107         case LANCZOS:
108                 width = 3.;
109                 lookup = new float[(n = 3 * TRANSFORM_SPP) + 1];
110                 for (i = 0; i <= 3 * TRANSFORM_SPP; i++)
111                         lookup[i] = sinc((float)i / TRANSFORM_SPP) *
112                                 sinc((float)i / TRANSFORM_SPP / 3.0f);
113                 break;
114
115         default:
116                 width = 0.;
117                 lookup = 0;
118                 n = 0;
119                 break;
120         }
121 }
122
123 OverlayKernel::~OverlayKernel()
124 {
125         if(lookup) delete [] lookup;
126 }
127
128 OverlayFrame::OverlayFrame(int cpus)
129 {
130         direct_engine = 0;
131         nn_engine = 0;
132         sample_engine = 0;
133         temp_frame = 0;
134         memset(kernel, 0, sizeof(kernel));
135         this->cpus = cpus;
136 }
137
138 OverlayFrame::~OverlayFrame()
139 {
140         if(temp_frame) delete temp_frame;
141
142         if(direct_engine) delete direct_engine;
143         if(nn_engine) delete nn_engine;
144         if(sample_engine) delete sample_engine;
145
146         if(kernel[NEAREST_NEIGHBOR]) delete kernel[NEAREST_NEIGHBOR];
147         if(kernel[BILINEAR]) delete kernel[BILINEAR];
148         if(kernel[BICUBIC]) delete kernel[BICUBIC];
149         if(kernel[LANCZOS]) delete kernel[LANCZOS];
150 }
151
152 static float epsilon_snap(float f)
153 {
154         return rintf(f * 1024) / 1024.;
155 }
156
157 int OverlayFrame::overlay(VFrame *output, VFrame *input,
158         float in_x1, float in_y1, float in_x2, float in_y2,
159         float out_x1, float out_y1, float out_x2, float out_y2,
160         float alpha, int mode, int interpolation_type)
161 {
162         in_x1 = epsilon_snap(in_x1);
163         in_x2 = epsilon_snap(in_x2);
164         in_y1 = epsilon_snap(in_y1);
165         in_y2 = epsilon_snap(in_y2);
166         out_x1 = epsilon_snap(out_x1);
167         out_x2 = epsilon_snap(out_x2);
168         out_y1 = epsilon_snap(out_y1);
169         out_y2 = epsilon_snap(out_y2);
170
171         if (isnan(in_x1) || isnan(in_x2) ||
172                 isnan(in_y1) || isnan(in_y2) ||
173                 isnan(out_x1) || isnan(out_x2) ||
174                 isnan(out_y1) || isnan(out_y2)) return 1;
175
176         if( in_x2 <= in_x1 || in_y2 <= in_y1 ) return 1;
177         if( out_x2 <= out_x1 || out_y2 <= out_y1 ) return 1;
178
179         float xscale = (out_x2 - out_x1) / (in_x2 - in_x1);
180         float yscale = (out_y2 - out_y1) / (in_y2 - in_y1);
181         int in_w = input->get_w(), in_h = input->get_h();
182         int out_w = output->get_w(), out_h = output->get_h();
183
184         if( in_x1 < 0 ) {
185                 out_x1 -= in_x1 * xscale;
186                 in_x1 = 0;
187         }
188         if( in_x2 > in_w ) {
189                 out_x2 -= (in_x2 - in_w) * xscale;
190                 in_x2 = in_w;
191         }
192         if( in_y1 < 0 ) {
193                 out_y1 -= in_y1 * yscale;
194                 in_y1 = 0;
195         }
196         if( in_y2 > in_h ) {
197                 out_y2 -= (in_y2 - in_h) * yscale;
198                 in_y2 = in_h;
199         }
200
201         if( out_x1 < 0 ) {
202                 in_x1 -= out_x1 / xscale;
203                 out_x1 = 0;
204         }
205         if( out_x2 > out_w ) {
206                 in_x2 -= (out_x2 - out_w) / xscale;
207                 out_x2 = out_w;
208         }
209         if( out_y1 < 0 ) {
210                 in_y1 -= out_y1 / yscale;
211                 out_y1 = 0;
212         }
213         if( out_y2 > out_h ) {
214                 in_y2 -= (out_y2 - out_h) / yscale;
215                 out_y2 = out_h;
216         }
217
218         if( in_x1 < 0) in_x1 = 0;
219         if( in_y1 < 0) in_y1 = 0;
220         if( in_x2 > in_w ) in_x2 = in_w;
221         if( in_y2 > in_h ) in_y2 = in_h;
222         if( out_x1 < 0) out_x1 = 0;
223         if( out_y1 < 0) out_y1 = 0;
224         if( out_x2 > out_w ) out_x2 = out_w;
225         if( out_y2 > out_h ) out_y2 = out_h;
226
227         if( in_x2 <= in_x1 || in_y2 <= in_y1 ) return 1;
228         if( out_x2 <= out_x1 || out_y2 <= out_y1 ) return 1;
229         xscale = (out_x2 - out_x1) / (in_x2 - in_x1);
230         yscale = (out_y2 - out_y1) / (in_y2 - in_y1);
231
232         /* don't interpolate integer translations, or scale no-ops */
233         if(xscale == 1. && yscale == 1. &&
234                 (int)in_x1 == in_x1 && (int)in_x2 == in_x2 &&
235                 (int)in_y1 == in_y1 && (int)in_y2 == in_y2 &&
236                 (int)out_x1 == out_x1 && (int)out_x2 == out_x2 &&
237                 (int)out_y1 == out_y1 && (int)out_y2 == out_y2) {
238                 if(!direct_engine) direct_engine = new DirectEngine(cpus);
239
240                 direct_engine->output = output;   direct_engine->input = input;
241                 direct_engine->in_x1 = in_x1;     direct_engine->in_y1 = in_y1;
242                 direct_engine->out_x1 = out_x1;   direct_engine->out_x2 = out_x2;
243                 direct_engine->out_y1 = out_y1;   direct_engine->out_y2 = out_y2;
244                 direct_engine->alpha = alpha;     direct_engine->mode = mode;
245                 direct_engine->process_packages();
246         }
247         else if(interpolation_type == NEAREST_NEIGHBOR) {
248                 if(!nn_engine) nn_engine = new NNEngine(cpus);
249                 nn_engine->output = output;       nn_engine->input = input;
250                 nn_engine->in_x1 = in_x1;         nn_engine->in_x2 = in_x2;
251                 nn_engine->in_y1 = in_y1;         nn_engine->in_y2 = in_y2;
252                 nn_engine->out_x1 = out_x1;       nn_engine->out_x2 = out_x2;
253                 nn_engine->out_y1 = out_y1;       nn_engine->out_y2 = out_y2;
254                 nn_engine->alpha = alpha;         nn_engine->mode = mode;
255                 nn_engine->process_packages();
256         }
257         else {
258                 int xtype = BILINEAR;
259                 int ytype = BILINEAR;
260
261                 switch(interpolation_type)
262                 {
263                 case CUBIC_CUBIC: // Bicubic enlargement and reduction
264                         xtype = ytype = BICUBIC;
265                         break;
266                 case CUBIC_LINEAR: // Bicubic enlargement and bilinear reduction
267                         xtype = xscale > 1. ? BICUBIC : BILINEAR;
268                         ytype = yscale > 1. ? BICUBIC : BILINEAR;
269                         break;
270                 case LINEAR_LINEAR: // Bilinear enlargement and bilinear reduction
271                         xtype = ytype = BILINEAR;
272                         break;
273                 case LANCZOS_LANCZOS: // Because we can
274                         xtype = ytype = LANCZOS;
275                         break;
276                 }
277
278                 if(xscale == 1. && (int)in_x1 == in_x1 && (int)in_x2 == in_x2 &&
279                                 (int)out_x1 == out_x1 && (int)out_x2 == out_x2)
280                         xtype = DIRECT_COPY;
281
282                 if(yscale == 1. && (int)in_y1 == in_y1 && (int)in_y2 == in_y2 &&
283                                 (int)out_y1 == out_y1 && (int)out_y2 == out_y2)
284                         ytype = DIRECT_COPY;
285
286                 if(!kernel[xtype])
287                         kernel[xtype] = new OverlayKernel(xtype);
288                 if(!kernel[ytype])
289                         kernel[ytype] = new OverlayKernel(ytype);
290
291 /*
292  * horizontal and vertical are separately resampled.  First we
293  * resample the input along X into a transposed, temporary frame,
294  * then resample/transpose the temporary space along X into the
295  * output.  Fractional pixels along the edge are handled in the X
296  * direction of each step
297  */
298                 // resampled dimension matches the transposed output space
299                 float temp_y1 = out_x1 - floor(out_x1);
300                 float temp_y2 = temp_y1 + (out_x2 - out_x1);
301                 int temp_h = ceil(temp_y2);
302
303                 // non-resampled dimension merely cropped
304                 float temp_x1 = in_y1 - floor(in_y1);
305                 float temp_x2 = temp_x1 + (in_y2 - in_y1);
306                 int temp_w = ceil(temp_x2);
307
308                 if( temp_frame &&
309                    (temp_frame->get_color_model() != input->get_color_model() ||
310                     temp_frame->get_w() != temp_w || temp_frame->get_h() != temp_h) ) {
311                         delete temp_frame;
312                         temp_frame = 0;
313                 }
314
315                 if(!temp_frame) {
316                         temp_frame =
317                                 new VFrame(temp_w, temp_h, input->get_color_model(), 0);
318                 }
319
320                 temp_frame->clear_frame();
321
322                 if(!sample_engine) sample_engine = new SampleEngine(cpus);
323
324                 sample_engine->output = temp_frame;
325                 sample_engine->input = input;
326                 sample_engine->kernel = kernel[xtype];
327                 sample_engine->col_out1 = 0;
328                 sample_engine->col_out2 = temp_w;
329                 sample_engine->row_in = floor(in_y1);
330
331                 sample_engine->in1 = in_x1;
332                 sample_engine->in2 = in_x2;
333                 sample_engine->out1 = temp_y1;
334                 sample_engine->out2 = temp_y2;
335                 sample_engine->alpha = 1.;
336                 sample_engine->mode = TRANSFER_REPLACE;
337                 sample_engine->process_packages();
338
339                 sample_engine->output = output;
340                 sample_engine->input = temp_frame;
341                 sample_engine->kernel = kernel[ytype];
342                 sample_engine->col_out1 = floor(out_x1);
343                 sample_engine->col_out2 = ceil(out_x2);
344                 sample_engine->row_in = 0;
345
346                 sample_engine->in1 = temp_x1;
347                 sample_engine->in2 = temp_x2;
348                 sample_engine->out1 = out_y1;
349                 sample_engine->out2 = out_y2;
350                 sample_engine->alpha = alpha;
351                 sample_engine->mode = mode;
352                 sample_engine->process_packages();
353         }
354         return 0;
355 }
356
357