4 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
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.
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.
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
23 #include "bccmodels.h"
26 #include "agingwindow.h"
35 REGISTER_PLUGIN(AgingMain)
37 int AgingMain::dx[] = { 1, 1, 0, -1, -1, -1, 0, 1 };
38 int AgingMain::dy[] = { 0, -1, -1, -1, 0, 1, 1, 1 };
40 AgingMain::AgingMain(PluginServer *server)
41 : PluginVClient(server)
48 AgingMain::~AgingMain()
54 const char* AgingMain::plugin_title() { return N_("AgingTV"); }
55 int AgingMain::is_realtime() { return 1; }
57 void AgingConfig::reset()
70 AgingConfig::AgingConfig()
75 AgingConfig::~AgingConfig()
79 int AgingConfig::equivalent(AgingConfig &that)
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 &&
92 void AgingConfig::copy_from(AgingConfig &that)
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;
105 void AgingConfig::interpolate(AgingConfig &prev, AgingConfig &next,
106 int64_t prev_frame, int64_t next_frame, int64_t current_frame)
111 LOAD_CONFIGURATION_MACRO(AgingMain, AgingConfig)
113 void AgingMain::save_data(KeyFrame *keyframe)
116 // cause data to be stored directly in text
117 output.set_shared_output(keyframe->xbuf);
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);
130 output.tag.set_title("/AGING");
132 output.append_newline();
133 output.terminate_string();
136 void AgingMain::read_data(KeyFrame *keyframe)
139 input.set_shared_input(keyframe->xbuf);
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);
158 NEW_WINDOW_MACRO(AgingMain, AgingWindow)
161 int AgingMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
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;
171 aging_server = new AgingServer(this, cpus, cpus);
172 aging_server->process_packages();
173 //printf("AgingMain::process_realtime 2\n");
179 AgingServer::AgingServer(AgingMain *plugin, int total_clients, int total_packages)
180 : LoadServer(1, 1 /* total_clients, total_packages */)
182 this->plugin = plugin;
186 LoadClient* AgingServer::new_client()
188 return new AgingClient(this);
191 LoadPackage* AgingServer::new_package()
193 return new AgingPackage;
196 void AgingServer::init_packages()
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();
206 AgingClient::AgingClient(AgingServer *server)
209 this->plugin = server->plugin;
212 #define COLORAGE(type, components) \
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); \
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; \
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)); \
237 b = (a & 0xff) >> 2; \
238 ((type**)output_rows)[i][j * components + k] = \
239 (type)(a - b + 0x18 + ((EffectTV::fastrand() >> 8) & 0x10)); \
246 void AgingClient::coloraging(unsigned char **output_rows, unsigned char **input_rows,
247 int color_model, int w, int h)
249 switch( color_model ) {
252 COLORAGE(uint8_t, 3);
265 COLORAGE(uint8_t, 4);
270 COLORAGE(uint16_t, 3);
273 case BC_RGBA16161616:
274 case BC_YUVA16161616:
275 COLORAGE(uint16_t, 4);
280 #define SCRATCHES(type, components, chroma) \
282 int i, j, y, y1, y2; \
285 int w_256 = w * 256; \
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; \
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; \
303 plugin->scratches[i].life--; \
304 if( plugin->scratches[i].life ) { \
308 y2 = EffectTV::fastrand() % h; \
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); \
319 p[j] = (type)(a | (b - (b >> 8))) / 0xffff; \
321 else if( sizeof(type) == 2 ) { \
322 int temp = (int)p[j]; \
326 p[j] = (type)(a | (b - (b >> 8))); \
329 int temp = (int)p[j]; \
333 p[j] = (type)(a | (b - (b >> 8))); \
340 p += w * components; \
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; \
355 void AgingClient::scratching(unsigned char **output_rows,
356 int color_model, int w, int h)
358 switch( color_model ) {
360 SCRATCHES(uint8_t, 3, 0);
364 SCRATCHES(float, 3, 0);
368 SCRATCHES(uint8_t, 3, 0x80);
372 SCRATCHES(float, 4, 0);
376 SCRATCHES(uint8_t, 4, 0);
380 SCRATCHES(uint8_t, 4, 0x80);
384 SCRATCHES(uint16_t, 3, 0);
388 SCRATCHES(uint16_t, 3, 0x8000);
391 case BC_RGBA16161616:
392 SCRATCHES(uint16_t, 4, 0);
395 case BC_YUVA16161616:
396 SCRATCHES(uint16_t, 4, 0x8000);
402 #define PITS(type, components, luma, chroma) \
405 int pnum, size, pnumscale; \
407 pnumscale = plugin->config.area_scale * 2; \
408 pnum = EffectTV::fastrand() % (plugin->config.pits_interval+1); \
409 if( plugin->pits_count ) { \
411 --plugin->pits_count; \
414 if( (EffectTV::fastrand() & 0xf8000000) == 0 ) { \
415 plugin->pits_count = (EffectTV::fastrand() >> 28) + 20; \
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; \
431 ((type**)output_rows)[y][x * components + 1] = chroma; \
432 ((type**)output_rows)[y][x * components + 2] = chroma; \
439 void AgingClient::pits(unsigned char **output_rows,
440 int color_model, int w, int h)
442 switch( color_model ) {
444 PITS(uint8_t, 3, 0xc0, 0);
447 PITS(float, 3, (float)0xc0 / 0xff, 0);
450 PITS(uint8_t, 3, 0xc0, 0x80);
454 PITS(float, 4, (float)0xc0 / 0xff, 0);
457 PITS(uint8_t, 4, 0xc0, 0);
460 PITS(uint8_t, 4, 0xc0, 0x80);
464 PITS(uint16_t, 3, 0xc000, 0);
467 PITS(uint16_t, 3, 0xc000, 0x8000);
470 case BC_RGBA16161616:
471 PITS(uint16_t, 4, 0xc000, 0);
473 case BC_YUVA16161616:
474 PITS(uint16_t, 4, 0xc000, 0x8000);
480 #define DUSTS(type, components, luma, chroma) \
486 if( plugin->dust_count == 0 ) { \
487 if( (EffectTV::fastrand() & 0xf0000000) == 0 ) { \
488 plugin->dust_count = EffectTV::fastrand() >> 29; \
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; \
505 ((type**)output_rows)[y][x * components + 1] = chroma; \
506 ((type**)output_rows)[y][x * components + 2] = chroma; \
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; \
515 --plugin->dust_count; \
519 void AgingClient::dusts(unsigned char **output_rows,
520 int color_model, int w, int h)
522 switch( color_model ) {
524 DUSTS(uint8_t, 3, 0x10, 0);
528 DUSTS(float, 3, (float)0x10 / 0xff, 0);
532 DUSTS(uint8_t, 3, 0x10, 0x80);
536 DUSTS(float, 4, (float)0x10 / 0xff, 0);
540 DUSTS(uint8_t, 4, 0x10, 0);
544 DUSTS(uint8_t, 4, 0x10, 0x80);
548 DUSTS(uint16_t, 3, 0x1000, 0);
552 DUSTS(uint16_t, 3, 0x1000, 0x8000);
555 case BC_RGBA16161616:
556 DUSTS(uint16_t, 4, 0x1000, 0);
559 case BC_YUVA16161616:
560 DUSTS(uint16_t, 4, 0x1000, 0x8000);
566 void AgingClient::process_package(LoadPackage *package)
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;
572 if( plugin->config.colorage )
573 coloraging(output_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 )
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 )
590 plugin->input_ptr->get_color_model(),
591 plugin->input_ptr->get_w(),
592 local_package->row2 - local_package->row1);
595 AgingPackage::AgingPackage()