merge: mask fixes/upgrades, overlay plugin rework, popup menu tweak
[goodguy/history.git] / cinelerra-5.1 / plugins / edge / edge.C
1 /*
2  * CINELERRA
3  * Copyright (C) 1997-2015 Adam Williams <broadcast at earthling dot net>
4  * 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  * 
19  */
20
21 #include "affine.h"
22 #include "bcsignals.h"
23 #include "clip.h"
24 #include "filexml.h"
25 #include "edge.h"
26 #include "edgewindow.h"
27 #include "language.h"
28 #include "transportque.inc"
29 #include <string.h>
30
31 // Edge detection from the Gimp
32
33 REGISTER_PLUGIN(Edge)
34
35 EdgeConfig::EdgeConfig()
36 {
37         amount = 8;
38 }
39
40 int EdgeConfig::equivalent(EdgeConfig &that)
41 {
42         if(this->amount != that.amount) return 0;
43         return 1;
44 }
45
46 void EdgeConfig::copy_from(EdgeConfig &that)
47 {
48         this->amount = that.amount;
49 }
50
51 void EdgeConfig::interpolate(
52         EdgeConfig &prev, 
53         EdgeConfig &next, 
54         long prev_frame, 
55         long next_frame, 
56         long current_frame)
57 {
58         copy_from(next);
59 }
60
61 void EdgeConfig::limits()
62 {
63         CLAMP(amount, 0, 10);
64 }
65
66
67 Edge::Edge(PluginServer *server)
68  : PluginVClient(server)
69 {
70         engine = 0;
71         temp = 0;
72 }
73
74 Edge::~Edge()
75 {
76         if(engine) delete engine;
77         if(temp) delete temp;
78 }
79
80 const char* Edge::plugin_title() { return _("Edge"); }
81 int Edge::is_realtime() { return 1; }
82
83 NEW_WINDOW_MACRO(Edge, EdgeWindow);
84 LOAD_CONFIGURATION_MACRO(Edge, EdgeConfig)
85
86 void Edge::save_data(KeyFrame *keyframe)
87 {
88         FileXML output;
89
90 // cause data to be stored directly in text
91         output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
92
93         output.tag.set_title("EDGE");
94         output.tag.set_property("AMOUNT", config.amount);
95         output.append_tag();
96         output.append_newline();
97         output.tag.set_title("/EDGE");
98         output.append_tag();
99         output.append_newline();
100         output.terminate_string();
101 }
102
103 void Edge::read_data(KeyFrame *keyframe)
104 {
105         FileXML input;
106
107         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
108
109         int result = 0;
110         while(!result)
111         {
112                 result = input.read_tag();
113
114                 if(!result)
115                 {
116                         if(input.tag.title_is("EDGE"))
117                         {
118                                 config.amount = input.tag.get_property("AMOUNT", config.amount);
119                                 config.limits();
120                         
121                         }
122                         else
123                         if(input.tag.title_is("/EDGE"))
124                         {
125                                 result = 1;
126                         }
127                 }
128         }
129
130 }
131
132 void Edge::update_gui()
133 {
134         if(thread)
135         {
136                 if(load_configuration())
137                 {
138                         thread->window->lock_window("Edge::update_gui");
139                         EdgeWindow *window = (EdgeWindow*)thread->window;
140                         window->flush();
141                         thread->window->unlock_window();
142                 }
143         }
144 }
145
146
147
148 int Edge::process_buffer(VFrame *frame,
149         int64_t start_position,
150         double frame_rate)
151 {
152
153 //      int need_reconfigure =
154         load_configuration();
155         int w = frame->get_w();
156         int h = frame->get_h();
157         int color_model = frame->get_color_model();
158
159 // initialize everything
160         if(!temp)
161         {
162                 engine = new EdgeEngine(this,
163                         PluginClient::get_project_smp() + 1,
164                         PluginClient::get_project_smp() + 1);
165                 
166                 temp = new VFrame(0,
167                         -1,
168                         w,
169                         h,
170                         color_model,
171                         -1);
172                 
173         }
174         
175         read_frame(frame, 
176                 0, 
177                 start_position, 
178                 frame_rate,
179                 0);
180         engine->process(temp, frame);
181         frame->copy_from(temp);
182
183         return 0;
184 }
185
186
187
188
189 EdgePackage::EdgePackage()
190  : LoadPackage()
191 {
192 }
193
194 EdgeUnit::EdgeUnit(EdgeEngine *server) : LoadClient(server)
195 {
196         this->server = server;
197 }
198
199 EdgeUnit::~EdgeUnit() 
200 {
201 }
202
203
204 float EdgeUnit::edge_detect(float *data, float max, int do_max)
205 {
206         const float v_kernel[9] = { 0,  0,  0,
207                                0,  2, -2,
208                                0,  2, -2 };
209         const float h_kernel[9] = { 0,  0,  0,
210                                0, -2, -2,
211                                0,  2,  2 };
212         int i;
213         float v_grad, h_grad;
214         float amount = server->plugin->config.amount;
215
216         for (i = 0, v_grad = 0, h_grad = 0; i < 9; i++)
217     {
218         v_grad += v_kernel[i] * data[i];
219         h_grad += h_kernel[i] * data[i];
220     }
221
222         float result = sqrt (v_grad * v_grad * amount +
223                  h_grad * h_grad * amount);
224         if(do_max)
225                 CLAMP(result, 0, max);
226         return result;
227 }
228
229 #define EDGE_MACRO(type, max, components, is_yuv) \
230 { \
231         type **input_rows = (type**)server->src->get_rows(); \
232         type **output_rows = (type**)server->dst->get_rows(); \
233         int comps = MIN(components, 3); \
234         for(int y = pkg->y1; y < pkg->y2; y++) \
235         { \
236                 for(int x = 0; x < w; x++) \
237                 { \
238 /* kernel is in bounds */ \
239                         if(y > 0 && x > 0 && y < h - 2 && x < w - 2) \
240                         { \
241                                 for(int chan = 0; chan < comps; chan++) \
242                                 { \
243 /* load kernel */ \
244                                         for(int kernel_y = 0; kernel_y < 3; kernel_y++) \
245                                         { \
246                                                 for(int kernel_x = 0; kernel_x < 3; kernel_x++) \
247                                                 { \
248                                                         kernel[3 * kernel_y + kernel_x] = \
249                                                                 (type)input_rows[y - 1 + kernel_y][(x - 1 + kernel_x) * components + chan]; \
250  \
251                                                         if(is_yuv && chan > 0) \
252                                                         { \
253                                                                 kernel[3 * kernel_y + kernel_x] -= 0x80; \
254                                                         } \
255  \
256                                                 } \
257                                         } \
258 /* do the business */ \
259                                         output_rows[y][x * components + chan] = edge_detect(kernel, max, sizeof(type) < 4); \
260                                         if(is_yuv && chan > 0) \
261                                         { \
262                                                 output_rows[y][x * components + chan] += 0x80; \
263                                         } \
264  \
265                                 } \
266  \
267                                 if(components == 4) output_rows[y][x * components + 3] = \
268                                         input_rows[y][x * components + 3]; \
269                         } \
270                         else \
271                         { \
272                                 for(int chan = 0; chan < comps; chan++) \
273                                 { \
274 /* load kernel */ \
275                                         for(int kernel_y = 0; kernel_y < 3; kernel_y++) \
276                                         { \
277                                                 for(int kernel_x = 0; kernel_x < 3; kernel_x++) \
278                                                 { \
279                                                         int in_y = y - 1 + kernel_y; \
280                                                         int in_x = x - 1 + kernel_x; \
281                                                         CLAMP(in_y, 0, h - 1); \
282                                                         CLAMP(in_x, 0, w - 1); \
283                                                         kernel[3 * kernel_y + kernel_x] = \
284                                                                 (type)input_rows[in_y][in_x * components + chan]; \
285                                                         if(is_yuv && chan > 0) \
286                                                         { \
287                                                                 kernel[3 * kernel_y + kernel_x] -= 0x80; \
288                                                         } \
289                                                 } \
290                                         } \
291 /* do the business */ \
292                                         output_rows[y][x * components + chan] = edge_detect(kernel, max, sizeof(type) < 4); \
293                                         if(is_yuv && chan > 0) \
294                                         { \
295                                                 output_rows[y][x * components + chan] += 0x80; \
296                                         } \
297                                 } \
298                                 if(components == 4) output_rows[y][x * components + 3] = \
299                                         input_rows[y][x * components + 3]; \
300                         } \
301                 } \
302         } \
303 }
304
305
306 void EdgeUnit::process_package(LoadPackage *package)
307 {
308         EdgePackage *pkg = (EdgePackage*)package;
309         int w = server->src->get_w();
310         int h = server->src->get_h();
311         float kernel[9];
312         
313         switch(server->src->get_color_model())
314         {
315                 case BC_RGB_FLOAT:
316                         EDGE_MACRO(float, 1, 3, 0);
317                         break;
318                 case BC_RGBA_FLOAT:
319                         EDGE_MACRO(float, 1, 4, 0);
320                         break;
321                 case BC_RGB888:
322                         EDGE_MACRO(unsigned char, 0xff, 3, 0);
323                         break;
324                 case BC_YUV888:
325                         EDGE_MACRO(unsigned char, 0xff, 3, 1);
326                         break;
327                 case BC_RGBA8888:
328                         EDGE_MACRO(unsigned char, 0xff, 4, 0);
329                         break;
330                 case BC_YUVA8888:
331                         EDGE_MACRO(unsigned char, 0xff, 4, 1);
332                         break;
333         }
334 }
335
336
337 EdgeEngine::EdgeEngine(Edge *plugin,
338         int total_clients, 
339         int total_packages)
340  : LoadServer(total_clients, total_packages)
341 {
342         this->plugin = plugin;
343 }
344
345 EdgeEngine::~EdgeEngine()
346 {
347 }
348
349
350 void EdgeEngine::init_packages()
351 {
352         for(int i = 0; i < get_total_packages(); i++)
353         {
354                 EdgePackage *pkg = (EdgePackage*)get_package(i);
355                 pkg->y1 = plugin->get_input(0)->get_h() * i / LoadServer::get_total_packages();
356                 pkg->y2 = plugin->get_input(0)->get_h() * (i + 1) / LoadServer::get_total_packages();
357         }
358 }
359
360 void EdgeEngine::process(VFrame *dst, VFrame *src)
361 {
362         this->dst = dst;
363         this->src = src;
364         process_packages();
365 }
366
367
368 LoadClient* EdgeEngine::new_client()
369 {
370         return new EdgeUnit(this);
371 }
372
373 LoadPackage* EdgeEngine::new_package()
374 {
375         return new EdgePackage;
376 }
377
378