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)
46 memset(scratches, 0, sizeof(scratches));
49 AgingMain::~AgingMain()
55 const char* AgingMain::plugin_title() { return N_("AgingTV"); }
56 int AgingMain::is_realtime() { return 1; }
58 void AgingConfig::reset()
71 AgingConfig::AgingConfig()
76 AgingConfig::~AgingConfig()
80 int AgingConfig::equivalent(AgingConfig &that)
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 &&
93 void AgingConfig::copy_from(AgingConfig &that)
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;
106 void AgingConfig::interpolate(AgingConfig &prev, AgingConfig &next,
107 int64_t prev_frame, int64_t next_frame, int64_t current_frame)
112 LOAD_CONFIGURATION_MACRO(AgingMain, AgingConfig)
114 void AgingMain::save_data(KeyFrame *keyframe)
117 // cause data to be stored directly in text
118 output.set_shared_output(keyframe->xbuf);
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);
131 output.tag.set_title("/AGING");
133 output.append_newline();
134 output.terminate_string();
137 void AgingMain::read_data(KeyFrame *keyframe)
140 input.set_shared_input(keyframe->xbuf);
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);
159 NEW_WINDOW_MACRO(AgingMain, AgingWindow)
162 int AgingMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
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;
172 aging_server = new AgingServer(this, cpus, cpus);
173 aging_server->process_packages();
174 //printf("AgingMain::process_realtime 2\n");
180 AgingServer::AgingServer(AgingMain *plugin, int total_clients, int total_packages)
181 : LoadServer(1, 1 /* total_clients, total_packages */)
183 this->plugin = plugin;
187 LoadClient* AgingServer::new_client()
189 return new AgingClient(this);
192 LoadPackage* AgingServer::new_package()
194 return new AgingPackage;
197 void AgingServer::init_packages()
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();
207 AgingClient::AgingClient(AgingServer *server)
210 this->plugin = server->plugin;
213 #define COLORAGE(type, components) \
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); \
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; \
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)); \
238 b = (a & 0xff) >> 2; \
239 ((type**)output_rows)[i][j * components + k] = \
240 (type)(a - b + 0x18 + ((EffectTV::fastrand() >> 8) & 0x10)); \
247 void AgingClient::coloraging(unsigned char **output_rows, unsigned char **input_rows,
248 int color_model, int w, int h)
250 switch( color_model ) {
253 COLORAGE(uint8_t, 3);
266 COLORAGE(uint8_t, 4);
271 COLORAGE(uint16_t, 3);
274 case BC_RGBA16161616:
275 case BC_YUVA16161616:
276 COLORAGE(uint16_t, 4);
281 #define SCRATCHES(type, components, chroma) \
283 int i, j, y, y1, y2; \
286 int w_256 = w * 256; \
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; \
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; \
304 plugin->scratches[i].life--; \
305 if( plugin->scratches[i].life ) { \
309 y2 = EffectTV::fastrand() % h; \
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); \
320 p[j] = (type)(a | (b - (b >> 8))) / 0xffff; \
322 else if( sizeof(type) == 2 ) { \
323 int temp = (int)p[j]; \
327 p[j] = (type)(a | (b - (b >> 8))); \
330 int temp = (int)p[j]; \
334 p[j] = (type)(a | (b - (b >> 8))); \
341 p += w * components; \
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; \
356 void AgingClient::scratching(unsigned char **output_rows,
357 int color_model, int w, int h)
359 switch( color_model ) {
361 SCRATCHES(uint8_t, 3, 0);
365 SCRATCHES(float, 3, 0);
369 SCRATCHES(uint8_t, 3, 0x80);
373 SCRATCHES(float, 4, 0);
377 SCRATCHES(uint8_t, 4, 0);
381 SCRATCHES(uint8_t, 4, 0x80);
385 SCRATCHES(uint16_t, 3, 0);
389 SCRATCHES(uint16_t, 3, 0x8000);
392 case BC_RGBA16161616:
393 SCRATCHES(uint16_t, 4, 0);
396 case BC_YUVA16161616:
397 SCRATCHES(uint16_t, 4, 0x8000);
403 #define PITS(type, components, luma, chroma) \
406 int pnum, size, pnumscale; \
408 pnumscale = plugin->config.area_scale * 2; \
409 pnum = EffectTV::fastrand() % (plugin->config.pits_interval+1); \
410 if( plugin->pits_count ) { \
412 --plugin->pits_count; \
415 if( (EffectTV::fastrand() & 0xf8000000) == 0 ) { \
416 plugin->pits_count = (EffectTV::fastrand() >> 28) + 20; \
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; \
432 ((type**)output_rows)[y][x * components + 1] = chroma; \
433 ((type**)output_rows)[y][x * components + 2] = chroma; \
440 void AgingClient::pits(unsigned char **output_rows,
441 int color_model, int w, int h)
443 switch( color_model ) {
445 PITS(uint8_t, 3, 0xc0, 0);
448 PITS(float, 3, (float)0xc0 / 0xff, 0);
451 PITS(uint8_t, 3, 0xc0, 0x80);
455 PITS(float, 4, (float)0xc0 / 0xff, 0);
458 PITS(uint8_t, 4, 0xc0, 0);
461 PITS(uint8_t, 4, 0xc0, 0x80);
465 PITS(uint16_t, 3, 0xc000, 0);
468 PITS(uint16_t, 3, 0xc000, 0x8000);
471 case BC_RGBA16161616:
472 PITS(uint16_t, 4, 0xc000, 0);
474 case BC_YUVA16161616:
475 PITS(uint16_t, 4, 0xc000, 0x8000);
481 #define DUSTS(type, components, luma, chroma) \
487 if( plugin->dust_count == 0 ) { \
488 if( (EffectTV::fastrand() & 0xf0000000) == 0 ) { \
489 plugin->dust_count = EffectTV::fastrand() >> 29; \
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; \
506 ((type**)output_rows)[y][x * components + 1] = chroma; \
507 ((type**)output_rows)[y][x * components + 2] = chroma; \
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; \
516 --plugin->dust_count; \
520 void AgingClient::dusts(unsigned char **output_rows,
521 int color_model, int w, int h)
523 switch( color_model ) {
525 DUSTS(uint8_t, 3, 0x10, 0);
529 DUSTS(float, 3, (float)0x10 / 0xff, 0);
533 DUSTS(uint8_t, 3, 0x10, 0x80);
537 DUSTS(float, 4, (float)0x10 / 0xff, 0);
541 DUSTS(uint8_t, 4, 0x10, 0);
545 DUSTS(uint8_t, 4, 0x10, 0x80);
549 DUSTS(uint16_t, 3, 0x1000, 0);
553 DUSTS(uint16_t, 3, 0x1000, 0x8000);
556 case BC_RGBA16161616:
557 DUSTS(uint16_t, 4, 0x1000, 0);
560 case BC_YUVA16161616:
561 DUSTS(uint16_t, 4, 0x1000, 0x8000);
567 void AgingClient::process_package(LoadPackage *package)
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;
573 if( plugin->config.colorage )
574 coloraging(output_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 )
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 )
591 plugin->input_ptr->get_color_model(),
592 plugin->input_ptr->get_w(),
593 local_package->row2 - local_package->row1);
596 AgingPackage::AgingPackage()