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