mem leaks, fix canvas_h bug, diamond for bezier entpts, aging_plugin segv, grab bound...
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / aging / aging.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 #include "clip.h"
23 #include "bccmodels.h"
24 #include "filexml.h"
25 #include "aging.h"
26 #include "agingwindow.h"
27 #include "effecttv.h"
28 #include "language.h"
29
30 #include <stdint.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <unistd.h>
34
35 REGISTER_PLUGIN(AgingMain)
36
37 int AgingMain::dx[] = { 1, 1, 0, -1, -1, -1,  0, 1 };
38 int AgingMain::dy[] = { 0, -1, -1, -1, 0, 1, 1, 1 };
39
40 AgingMain::AgingMain(PluginServer *server)
41  : PluginVClient(server)
42 {
43         aging_server = 0;
44         pits_count = 0;
45         dust_count = 0;
46         memset(scratches, 0, sizeof(scratches));
47 }
48
49 AgingMain::~AgingMain()
50 {
51
52         delete aging_server;
53 }
54
55 const char* AgingMain::plugin_title() { return N_("AgingTV"); }
56 int AgingMain::is_realtime() { return 1; }
57
58 void AgingConfig::reset()
59 {
60         area_scale = 10;
61         aging_mode = 0;
62         dust_interval = 0;
63         pits_interval = 0;
64         scratch_lines = 7;
65         colorage = 1;
66         scratch = 1;
67         pits = 1;
68         dust = 1;
69 }
70
71 AgingConfig::AgingConfig()
72 {
73         reset();
74 }
75
76 AgingConfig::~AgingConfig()
77 {
78 }
79
80 int AgingConfig::equivalent(AgingConfig &that)
81 {
82         return area_scale == that.area_scale &&
83                 aging_mode == that.aging_mode &&
84                 dust_interval == that.dust_interval &&
85                 pits_interval == that.pits_interval &&
86                 scratch_lines == that.scratch_lines &&
87                 colorage == that.colorage &&
88                 scratch == that.scratch &&
89                 pits == that.pits &&
90                 dust == that.dust;
91 }
92
93 void AgingConfig::copy_from(AgingConfig &that)
94 {
95         area_scale = that.area_scale;
96         aging_mode = that.aging_mode;
97         dust_interval = that.dust_interval;
98         pits_interval = that.pits_interval;
99         scratch_lines = that.scratch_lines;
100         colorage = that.colorage;
101         scratch = that.scratch;
102         pits = that.pits;
103         dust = that.dust;
104 }
105
106 void AgingConfig::interpolate(AgingConfig &prev, AgingConfig &next,
107                 int64_t prev_frame, int64_t next_frame, int64_t current_frame)
108 {
109         copy_from(prev);
110 }
111
112 LOAD_CONFIGURATION_MACRO(AgingMain, AgingConfig)
113
114 void AgingMain::save_data(KeyFrame *keyframe)
115 {
116         FileXML output;
117 // cause data to be stored directly in text
118         output.set_shared_output(keyframe->xbuf);
119 // Store data
120         output.tag.set_title("AGING");
121         output.tag.set_property("AREA_SCALE", config.area_scale);
122         output.tag.set_property("AGING_MODE", config.aging_mode);
123         output.tag.set_property("DUST_INTERVAL", config.dust_interval);
124         output.tag.set_property("PITS_INTERVAL", config.pits_interval);
125         output.tag.set_property("SCRATCH_LINES", config.scratch_lines);
126         output.tag.set_property("COLORAGE", config.colorage);
127         output.tag.set_property("SCRATCH", config.scratch);
128         output.tag.set_property("PITS", config.pits);
129         output.tag.set_property("DUST", config.dust);
130         output.append_tag();
131         output.tag.set_title("/AGING");
132         output.append_tag();
133         output.append_newline();
134         output.terminate_string();
135 }
136
137 void AgingMain::read_data(KeyFrame *keyframe)
138 {
139         FileXML input;
140         input.set_shared_input(keyframe->xbuf);
141
142         int result = 0;
143
144         while( !(result = input.read_tag()) ) {
145                 if( input.tag.title_is("AGING") ) {
146                         config.area_scale    = input.tag.get_property("AREA_SCALE", config.area_scale);
147                         config.aging_mode    = input.tag.get_property("AGING_MODE", config.aging_mode);
148                         config.dust_interval = input.tag.get_property("DUST_INTERVAL", config.dust_interval);
149                         config.pits_interval = input.tag.get_property("PITS_INTERVAL", config.pits_interval);
150                         config.scratch_lines = input.tag.get_property("SCRATCH_LINES", config.scratch_lines);
151                         config.colorage      = input.tag.get_property("COLORAGE", config.colorage);
152                         config.scratch       = input.tag.get_property("SCRATCH", config.scratch);
153                         config.pits          = input.tag.get_property("PITS", config.pits);
154                         config.dust          = input.tag.get_property("DUST", config.dust);
155                 }
156         }
157 }
158
159 NEW_WINDOW_MACRO(AgingMain, AgingWindow)
160
161
162 int AgingMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
163 {
164 //printf("AgingMain::process_realtime 1\n");
165         load_configuration();
166 //printf("AgingMain::process_realtime 1\n");
167         this->input_ptr = input_ptr;
168         this->output_ptr = output_ptr;
169         int cpus = PluginClient::smp + 1;
170         if( cpus > 16 ) cpus = 16;
171         if( !aging_server )
172                 aging_server = new AgingServer(this, cpus, cpus);
173         aging_server->process_packages();
174 //printf("AgingMain::process_realtime 2\n");
175
176         return 0;
177 }
178
179
180 AgingServer::AgingServer(AgingMain *plugin, int total_clients, int total_packages)
181  : LoadServer(1, 1 /* total_clients, total_packages */)
182 {
183         this->plugin = plugin;
184 }
185
186
187 LoadClient* AgingServer::new_client()
188 {
189         return new AgingClient(this);
190 }
191
192 LoadPackage* AgingServer::new_package()
193 {
194         return new AgingPackage;
195 }
196
197 void AgingServer::init_packages()
198 {
199         for( int i = 0; i < get_total_packages(); i++ ) {
200                 AgingPackage *package = (AgingPackage*)get_package(i);
201                 package->row1 = plugin->input_ptr->get_h() * i / get_total_packages();
202                 package->row2 = plugin->input_ptr->get_h() * (i + 1) / get_total_packages();
203         }
204 }
205
206
207 AgingClient::AgingClient(AgingServer *server)
208  : LoadClient(server)
209 {
210         this->plugin = server->plugin;
211 }
212
213 #define COLORAGE(type, components) \
214 { \
215         int a, b; \
216         int i, j, k; \
217  \
218         for( i = 0; i < h; i++ ) { \
219                 for( j = 0; j < w; j++ ) { \
220                         for( k = 0; k < 3; k++ ) { \
221                                 if( sizeof(type) == 4 ) { \
222                                         a = (int)(((type**)input_rows)[i][j * components + k] * 0xffff); \
223                                         CLAMP(a, 0, 0xffff); \
224                                 } \
225                                 else \
226                                         a = (int)((type**)input_rows)[i][j * components + k]; \
227                                 if( sizeof(type) == 4 ) { \
228                                         b = (a & 0xffff) >> 2; \
229                                         ((type**)output_rows)[i][j * components + k] = \
230                                                 (type)(a - b + 0x1800 + (EffectTV::fastrand() & 0x1000)) / 0xffff; \
231                                 } \
232                                 else if( sizeof(type) == 2 ) { \
233                                         b = (a & 0xffff) >> 2; \
234                                         ((type**)output_rows)[i][j * components + k] = \
235                                                 (type)(a - b + 0x1800 + (EffectTV::fastrand() & 0x1000)); \
236                                 } \
237                                 else { \
238                                         b = (a & 0xff) >> 2; \
239                                         ((type**)output_rows)[i][j * components + k] =  \
240                                                 (type)(a - b + 0x18 + ((EffectTV::fastrand() >> 8) & 0x10)); \
241                                 } \
242                         } \
243                 } \
244         } \
245 }
246
247 void AgingClient::coloraging(unsigned char **output_rows, unsigned char **input_rows,
248                 int color_model, int w, int h)
249 {
250         switch( color_model ) {
251         case BC_RGB888:
252         case BC_YUV888:
253                 COLORAGE(uint8_t, 3);
254                 break;
255
256         case BC_RGB_FLOAT:
257                 COLORAGE(float, 3);
258                 break;
259
260         case BC_RGBA_FLOAT:
261                 COLORAGE(float, 4);
262                 break;
263
264         case BC_RGBA8888:
265         case BC_YUVA8888:
266                 COLORAGE(uint8_t, 4);
267                 break;
268
269         case BC_RGB161616:
270         case BC_YUV161616:
271                 COLORAGE(uint16_t, 3);
272                 break;
273
274         case BC_RGBA16161616:
275         case BC_YUVA16161616:
276                 COLORAGE(uint16_t, 4);
277                 break;
278         }
279 }
280
281 #define SCRATCHES(type, components, chroma) \
282 { \
283         int i, j, y, y1, y2; \
284         type *p; \
285         int a, b; \
286         int w_256 = w * 256; \
287  \
288         for( i = 0; i < plugin->config.scratch_lines; i++ ) { \
289                 if( plugin->scratches[i].life )  { \
290                         plugin->scratches[i].x = plugin->scratches[i].x + plugin->scratches[i].dx; \
291                         if( plugin->scratches[i].x < 0 || plugin->scratches[i].x >= w_256 ) { \
292                                 plugin->scratches[i].life = 0; \
293                                 break; \
294                         } \
295                         p = (type*)output_rows[0] + (plugin->scratches[i].x >> 8) * components; \
296                         if( plugin->scratches[i].init ) { \
297                                 y1 = plugin->scratches[i].init; \
298                                 plugin->scratches[i].init = 0; \
299                         } \
300                         else { \
301                                 y1 = 0; \
302                         } \
303 \
304                         plugin->scratches[i].life--; \
305                         if( plugin->scratches[i].life ) { \
306                                 y2 = h; \
307                         }  \
308                         else { \
309                                 y2 = EffectTV::fastrand() % h; \
310                         } \
311  \
312                         for( y = y1; y < y2; y++ ) { \
313                                 for( j = 0; j < (chroma ? 1 : 3); j++ ) { \
314                                         if( sizeof(type) == 4 ) { \
315                                                 int temp = (int)(p[j] * 0xffff); \
316                                                 CLAMP(temp, 0, 0xffff); \
317                                                 a = temp & 0xfeff; \
318                                                 a += 0x2000; \
319                                                 b = a & 0x10000; \
320                                                 p[j] = (type)(a | (b - (b >> 8))) / 0xffff; \
321                                         } \
322                                         else if( sizeof(type) == 2 ) { \
323                                                 int temp = (int)p[j]; \
324                                                 a = temp & 0xfeff; \
325                                                 a += 0x2000; \
326                                                 b = a & 0x10000; \
327                                                 p[j] = (type)(a | (b - (b >> 8))); \
328                                         } \
329                                         else { \
330                                                 int temp = (int)p[j]; \
331                                                 a = temp & 0xfe; \
332                                                 a += 0x20; \
333                                                 b = a & 0x100; \
334                                                 p[j] = (type)(a | (b - (b >> 8))); \
335                                         } \
336                                 } \
337                                 if( chroma ) { \
338                                         p[1] = chroma; \
339                                         p[2] = chroma; \
340                                 } \
341                                 p += w * components; \
342                         } \
343                 }  \
344                 else  { \
345                         if( (EffectTV::fastrand() & 0xf0000000) == 0 ) { \
346                                 plugin->scratches[i].life = 2 + (EffectTV::fastrand() >> 27); \
347                                 plugin->scratches[i].x = EffectTV::fastrand() % (w_256); \
348                                 plugin->scratches[i].dx = ((int)EffectTV::fastrand()) >> 23; \
349                                 plugin->scratches[i].init = (EffectTV::fastrand() % (h - 1)) + 1; \
350                         } \
351                 } \
352         } \
353 }
354
355
356 void AgingClient::scratching(unsigned char **output_rows,
357                 int color_model, int w, int h)
358 {
359         switch( color_model ) {
360         case BC_RGB888:
361                 SCRATCHES(uint8_t, 3, 0);
362                 break;
363
364         case BC_RGB_FLOAT:
365                 SCRATCHES(float, 3, 0);
366                 break;
367
368         case BC_YUV888:
369                 SCRATCHES(uint8_t, 3, 0x80);
370                 break;
371
372         case BC_RGBA_FLOAT:
373                 SCRATCHES(float, 4, 0);
374                 break;
375
376         case BC_RGBA8888:
377                 SCRATCHES(uint8_t, 4, 0);
378                 break;
379
380         case BC_YUVA8888:
381                 SCRATCHES(uint8_t, 4, 0x80);
382                 break;
383
384         case BC_RGB161616:
385                 SCRATCHES(uint16_t, 3, 0);
386                 break;
387
388         case BC_YUV161616:
389                 SCRATCHES(uint16_t, 3, 0x8000);
390                 break;
391
392         case BC_RGBA16161616:
393                 SCRATCHES(uint16_t, 4, 0);
394                 break;
395
396         case BC_YUVA16161616:
397                 SCRATCHES(uint16_t, 4, 0x8000);
398                 break;
399         }
400 }
401
402
403 #define PITS(type, components, luma, chroma) \
404 { \
405         int i, j, k; \
406         int pnum, size, pnumscale; \
407         int x, y; \
408         pnumscale = plugin->config.area_scale * 2; \
409         pnum = EffectTV::fastrand() % (plugin->config.pits_interval+1); \
410         if( plugin->pits_count ) { \
411                 pnum += pnumscale; \
412                 --plugin->pits_count; \
413         }  \
414         else { \
415                 if( (EffectTV::fastrand() & 0xf8000000) == 0 ) { \
416                         plugin->pits_count = (EffectTV::fastrand() >> 28) + 20; \
417                 } \
418         } \
419         for( i = 0; i < pnum; i++ ) { \
420                 x = EffectTV::fastrand() % (w - 1); \
421                 y = EffectTV::fastrand() % (h - 1); \
422                 size = EffectTV::fastrand() >> 28; \
423                 for( j = 0; j < size; j++ ) { \
424                         x = x + EffectTV::fastrand() % 3 - 1; \
425                         y = y + EffectTV::fastrand() % 3 - 1; \
426                         CLAMP(x, 0, w - 1); \
427                         CLAMP(y, 0, h - 1); \
428                         for( k = 0; k < (chroma ? 1 : 3); k++ ) { \
429                                 ((type**)output_rows)[y][x * components + k] = luma; \
430                         } \
431                         if( chroma ) { \
432                                 ((type**)output_rows)[y][x * components + 1] = chroma; \
433                                 ((type**)output_rows)[y][x * components + 2] = chroma; \
434                         } \
435                 } \
436         } \
437 }
438
439
440 void AgingClient::pits(unsigned char **output_rows,
441                 int color_model, int w, int h)
442 {
443         switch( color_model ) {
444                 case BC_RGB888:
445                         PITS(uint8_t, 3, 0xc0, 0);
446                         break;
447                 case BC_RGB_FLOAT:
448                         PITS(float, 3, (float)0xc0 / 0xff, 0);
449                         break;
450                 case BC_YUV888:
451                         PITS(uint8_t, 3, 0xc0, 0x80);
452                         break;
453
454                 case BC_RGBA_FLOAT:
455                         PITS(float, 4, (float)0xc0 / 0xff, 0);
456                         break;
457                 case BC_RGBA8888:
458                         PITS(uint8_t, 4, 0xc0, 0);
459                         break;
460                 case BC_YUVA8888:
461                         PITS(uint8_t, 4, 0xc0, 0x80);
462                         break;
463
464                 case BC_RGB161616:
465                         PITS(uint16_t, 3, 0xc000, 0);
466                         break;
467                 case BC_YUV161616:
468                         PITS(uint16_t, 3, 0xc000, 0x8000);
469                         break;
470
471                 case BC_RGBA16161616:
472                         PITS(uint16_t, 4, 0xc000, 0);
473                         break;
474                 case BC_YUVA16161616:
475                         PITS(uint16_t, 4, 0xc000, 0x8000);
476                         break;
477         }
478 }
479
480
481 #define DUSTS(type, components, luma, chroma) \
482 { \
483         int i, j, k; \
484         int dnum; \
485         int d, len; \
486         int x, y; \
487         if( plugin->dust_count == 0 ) { \
488                 if( (EffectTV::fastrand() & 0xf0000000) == 0 ) { \
489                         plugin->dust_count = EffectTV::fastrand() >> 29; \
490                 } \
491                 return; \
492         } \
493         dnum = plugin->config.area_scale * 4 + EffectTV::fastrand() % (plugin->config.dust_interval+1); \
494         for( i = 0; i < dnum; i++ ) { \
495                 x = EffectTV::fastrand() % w; \
496                 y = EffectTV::fastrand() % h; \
497                 d = EffectTV::fastrand() >> 29; \
498                 len = EffectTV::fastrand() % plugin->config.area_scale + 5; \
499                 for( j = 0; j < len; j++ ) { \
500                         CLAMP(x, 0, w - 1); \
501                         CLAMP(y, 0, h - 1); \
502                         for( k = 0; k < (chroma ? 1 : 3); k++ ) { \
503                                 ((type**)output_rows)[y][x * components + k] = luma; \
504                         } \
505                         if( chroma ) { \
506                                 ((type**)output_rows)[y][x * components + 1] = chroma; \
507                                 ((type**)output_rows)[y][x * components + 2] = chroma; \
508                         } \
509                         y += AgingMain::dy[d]; \
510                         x += AgingMain::dx[d]; \
511                         if( x < 0 || x >= w ) break; \
512                         if( y < 0 || y >= h ) break; \
513                         d = (d + EffectTV::fastrand() % 3 - 1) & 7; \
514                 } \
515         } \
516         --plugin->dust_count; \
517 }
518
519
520 void AgingClient::dusts(unsigned char **output_rows,
521                 int color_model, int w, int h)
522 {
523         switch( color_model ) {
524         case BC_RGB888:
525                 DUSTS(uint8_t, 3, 0x10, 0);
526                 break;
527
528         case BC_RGB_FLOAT:
529                 DUSTS(float, 3, (float)0x10 / 0xff, 0);
530                 break;
531
532         case BC_YUV888:
533                 DUSTS(uint8_t, 3, 0x10, 0x80);
534                 break;
535
536         case BC_RGBA_FLOAT:
537                 DUSTS(float, 4, (float)0x10 / 0xff, 0);
538                 break;
539
540         case BC_RGBA8888:
541                 DUSTS(uint8_t, 4, 0x10, 0);
542                 break;
543
544         case BC_YUVA8888:
545                 DUSTS(uint8_t, 4, 0x10, 0x80);
546                 break;
547
548         case BC_RGB161616:
549                 DUSTS(uint16_t, 3, 0x1000, 0);
550                 break;
551
552         case BC_YUV161616:
553                 DUSTS(uint16_t, 3, 0x1000, 0x8000);
554                 break;
555
556         case BC_RGBA16161616:
557                 DUSTS(uint16_t, 4, 0x1000, 0);
558                 break;
559
560         case BC_YUVA16161616:
561                 DUSTS(uint16_t, 4, 0x1000, 0x8000);
562                 break;
563         }
564 }
565
566
567 void AgingClient::process_package(LoadPackage *package)
568 {
569         AgingPackage *local_package = (AgingPackage*)package;
570         unsigned char **input_rows = plugin->input_ptr->get_rows() + local_package->row1;
571         unsigned char **output_rows = plugin->output_ptr->get_rows() + local_package->row1;
572
573         if( plugin->config.colorage )
574                 coloraging(output_rows,
575                         input_rows,
576                         plugin->input_ptr->get_color_model(),
577                         plugin->input_ptr->get_w(),
578                         local_package->row2 - local_package->row1);
579         if( plugin->config.scratch )
580                 scratching(output_rows,
581                         plugin->input_ptr->get_color_model(),
582                         plugin->input_ptr->get_w(),
583                         local_package->row2 - local_package->row1);
584         if( plugin->config.pits )
585                 pits(output_rows,
586                         plugin->input_ptr->get_color_model(),
587                         plugin->input_ptr->get_w(),
588                         local_package->row2 - local_package->row1);
589         if( plugin->config.dust )
590                 dusts(output_rows,
591                         plugin->input_ptr->get_color_model(),
592                         plugin->input_ptr->get_w(),
593                         local_package->row2 - local_package->row1);
594 }
595
596 AgingPackage::AgingPackage()
597 {
598 }
599