add crop plugin, add timeline bars, render setup err chks, minor tweaks
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / crop / crop.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 /*
23  * 2019. Derivative by Translate plugin. This plugin works also with Proxy.
24  * It uses Percent values instead of Pixel value coordinates.
25 */
26
27 #include "clip.h"
28 #include "filexml.h"
29 #include "language.h"
30 #include "crop.h"
31 #include "cropwin.h"
32
33 #include <string.h>
34
35
36
37
38 REGISTER_PLUGIN(CropMain)
39
40 CropConfig::CropConfig()
41 {
42         reset(RESET_DEFAULT_SETTINGS);
43 }
44
45 void CropConfig::reset(int clear)
46 {
47         switch(clear) {
48                 case RESET_LEFT :
49                         crop_l = 0.0;
50                         break;
51                 case RESET_TOP :
52                         crop_t = 0.0;
53                         break;
54                 case RESET_RIGHT :
55                         crop_r = 0.0;
56                         break;
57                 case RESET_BOTTOM :
58                         crop_b = 0.0;
59                         break;
60                 case RESET_POSITION_X :
61                         position_x = 0.0;
62                         break;
63                 case RESET_POSITION_Y :
64                         position_y = 0.0;
65                         break;
66                 case RESET_ALL :
67                 case RESET_DEFAULT_SETTINGS :
68                 default:
69                         crop_l = 0.0;
70                         crop_t = 0.0;
71                         crop_r = 0.0;
72                         crop_b = 0.0;
73
74                         position_x = 0.0;
75                         position_y = 0.0;
76                         break;
77         }
78 }
79
80
81 int CropConfig::equivalent(CropConfig &that)
82 {
83         return EQUIV(crop_l, that.crop_l) &&
84                 EQUIV(crop_t, that.crop_t) &&
85                 EQUIV(crop_r, that.crop_r) &&
86                 EQUIV(crop_b, that.crop_b) &&
87                 EQUIV(position_x, that.position_x) &&
88                 EQUIV(position_y, that.position_y);
89 }
90
91 void CropConfig::copy_from(CropConfig &that)
92 {
93         crop_l = that.crop_l;
94         crop_t = that.crop_t;
95         crop_r = that.crop_r;
96         crop_b = that.crop_b;
97         position_x = that.position_x;
98         position_y = that.position_y;
99 }
100
101 void CropConfig::interpolate(CropConfig &prev,
102         CropConfig &next,
103         int64_t prev_frame,
104         int64_t next_frame,
105         int64_t current_frame)
106 {
107         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
108         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
109
110         this->crop_l = prev.crop_l * prev_scale + next.crop_l * next_scale;
111         this->crop_t = prev.crop_t * prev_scale + next.crop_t * next_scale;
112         this->crop_r = prev.crop_r * prev_scale + next.crop_r * next_scale;
113         this->crop_b = prev.crop_b * prev_scale + next.crop_b * next_scale;
114         this->position_x = prev.position_x * prev_scale + next.position_x * next_scale;
115         this->position_y = prev.position_y * prev_scale + next.position_y * next_scale;
116 }
117
118
119
120
121
122
123
124
125 CropMain::CropMain(PluginServer *server)
126  : PluginVClient(server)
127 {
128         temp_frame = 0;
129         overlayer = 0;
130
131 }
132
133 CropMain::~CropMain()
134 {
135
136
137         if(temp_frame) delete temp_frame;
138         temp_frame = 0;
139         if(overlayer) delete overlayer;
140         overlayer = 0;
141 }
142
143 const char* CropMain::plugin_title() { return N_("Crop & Position"); }
144 int CropMain::is_realtime() { return 1; }
145
146
147
148 LOAD_CONFIGURATION_MACRO(CropMain, CropConfig)
149
150 void CropMain::save_data(KeyFrame *keyframe)
151 {
152         FileXML output;
153
154 // cause data to be stored directly in text
155         output.set_shared_output(keyframe->xbuf);
156
157 // Store data
158         output.tag.set_title("CROP");
159         output.tag.set_property("LEFT", config.crop_l);
160         output.tag.set_property("TOP", config.crop_t);
161         output.tag.set_property("RIGHT", config.crop_r);
162         output.tag.set_property("BOTTOM", config.crop_b);
163         output.tag.set_property("POSITION_X", config.position_x);
164         output.tag.set_property("POSITION_Y", config.position_y);
165         output.append_tag();
166         output.tag.set_title("/CROP");
167         output.append_tag();
168         output.append_newline();
169         output.terminate_string();
170 // data is now in *text
171 }
172
173 void CropMain::read_data(KeyFrame *keyframe)
174 {
175         FileXML input;
176
177         input.set_shared_input(keyframe->xbuf);
178
179         int result = 0;
180
181         while(!result)
182         {
183                 result = input.read_tag();
184
185                 if(!result)
186                 {
187                         if(input.tag.title_is("CROP"))
188                         {
189                                 config.crop_l = input.tag.get_property("LEFT", config.crop_l);
190                                 config.crop_t = input.tag.get_property("TOP", config.crop_t);
191                                 config.crop_r = input.tag.get_property("RIGHT", config.crop_r);
192                                 config.crop_b = input.tag.get_property("BOTTOM", config.crop_b);
193                                 config.position_x = input.tag.get_property("POSITION_X", config.position_x);
194                                 config.position_y = input.tag.get_property("POSITION_Y", config.position_y);
195                         }
196                 }
197         }
198 }
199
200
201 #define EPSILON 0.001
202
203 int CropMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
204 {
205         VFrame *input = input_ptr;
206         VFrame *output = output_ptr;
207
208         load_configuration();
209
210 //printf("CropMain::process_realtime 1 %p\n", input);
211         if( input->get_rows()[0] == output->get_rows()[0] ) {
212                 if( temp_frame && (
213                     temp_frame->get_w() != input_ptr->get_w() ||
214                     temp_frame->get_h() != input_ptr->get_h() ||
215                     temp_frame->get_color_model() != input_ptr->get_color_model() ) ) {
216                         delete temp_frame;
217                         temp_frame = 0;
218                 }
219                 if(!temp_frame)
220                         temp_frame = new VFrame(input_ptr->get_w(), input_ptr->get_h(),
221                                 input->get_color_model(), 0);
222                 temp_frame->copy_from(input);
223                 input = temp_frame;
224         }
225 //printf("CropMain::process_realtime 2 %p\n", input);
226
227
228         if(!overlayer)
229         {
230                 overlayer = new OverlayFrame(smp + 1);
231         }
232
233         output->clear_frame();
234
235 /* OLD code by "Translate" plugin
236         if( config.in_w < EPSILON ) return 1;
237         if( config.in_h < EPSILON ) return 1;
238         if( config.out_w < EPSILON ) return 1;
239         if( config.out_h < EPSILON ) return 1;
240 */
241
242
243 /*
244    *** Little description of the points ***
245    Points (ix1, iy1) and (ix2, iy2) are the Camera coordinates:
246      (ix1, iy1) is the point on top-left,
247      (ix2, iy2) is the point on bottom-right.
248    Points (ox1, oy1) and (ox2, oy2) are the Projector coordinates:
249      (ox1, oy1) is the point on top-left,
250      (ox2, oy2) is the point on bottom-right.
251 */
252
253 // Convert from Percent to Pixel coordinate (Percent value in plugin GUI)
254 // so it works also for Proxy
255         float ix1 = config.crop_l / 100 * output->get_w();
256         float ox1 = ix1;
257         ox1 += config.position_x / 100 * output->get_w();
258         float ix2 = ((100.00 - config.crop_r) / 100) * output->get_w();
259
260         if( ix1 < 0 ) {
261                 ox1 -= ix1;
262                 ix1 = 0;
263         }
264
265         if(ix2 > output->get_w())
266                 ix2 = output->get_w();
267
268 // Convert from Percent to Pixel coordinate (Percent value in plugin GUI)
269 // so it works also for Proxy
270         float iy1 = config.crop_t / 100 * output->get_h();
271         float oy1 = iy1;
272         oy1 += config.position_y / 100 * output->get_h();
273         float iy2 = ((100.00 - config.crop_b) / 100) * output->get_h();
274
275         if( iy1 < 0 ) {
276                 oy1 -= iy1;
277                 iy1 = 0;
278         }
279
280         if( iy2 > output->get_h() )
281                 iy2 = output->get_h();
282
283 // Scale features: OLD code by "Translate" plugin.
284 // (I leave here for future development)  
285 // Scale_: scale_x=scale_y=1. It means NO SCALE.
286         float cx = 1.00;
287         float cy = cx;
288
289         float ox2 = ox1 + (ix2 - ix1) * cx;
290         float oy2 = oy1 + (iy2 - iy1) * cy;
291
292         if( ox1 < 0 ) {
293                 ix1 += -ox1 / cx;
294                 ox1 = 0;
295         }
296         if( oy1 < 0 ) {
297                 iy1 += -oy1 / cy;
298                 oy1 = 0;
299         }
300         if( ox2 > output->get_w() ) {
301                 ix2 -= (ox2 - output->get_w()) / cx;
302                 ox2 = output->get_w();
303         }
304         if( oy2 > output->get_h() ) {
305                 iy2 -= (oy2 - output->get_h()) / cy;
306                 oy2 = output->get_h();
307         }
308
309         if( ix1 >= ix2 ) return 1;
310         if( iy1 >= iy2 ) return 1;
311         if( ox1 >= ox2 ) return 1;
312         if( oy1 >= oy2 ) return 1;
313
314         overlayer->overlay(output, input,
315                 ix1, iy1, ix2, iy2,
316                 ox1, oy1, ox2, oy2,
317                 1, TRANSFER_REPLACE,
318                 get_interpolation_type());
319         return 0;
320 }
321
322 NEW_WINDOW_MACRO(CropMain, CropWin)
323
324 void CropMain::update_gui()
325 {
326         if( !thread ) return;
327         if( !load_configuration() ) return;
328
329         CropWin *window = (CropWin*)thread->window;
330         window->lock_window();
331 /*
332         window->crop_l->update(config.crop_l);
333         window->crop_t->update(config.crop_t);
334         window->crop_r->update(config.crop_r);
335         window->crop_b->update(config.crop_b);
336         window->position_x->update(config.position_x);
337         window->position_y->update(config.position_y);
338 */
339         window->update(RESET_ALL);
340
341         window->unlock_window();
342 }