r996: add renderprofiles.C to POTFILES.in
[cinelerra_cv/mob.git] / plugins / timeavg / timeavg.C
blobf9f38f1f30f11357aa36edb6451be9c6de3301e2
1 #include "clip.h"
2 #include "bchash.h"
3 #include "filexml.h"
4 #include "keyframe.h"
5 #include "language.h"
6 #include "picon_png.h"
7 #include "timeavg.h"
8 #include "timeavgwindow.h"
9 #include "vframe.h"
14 #include <stdint.h>
15 #include <string.h>
20 REGISTER_PLUGIN(TimeAvgMain)
26 TimeAvgConfig::TimeAvgConfig()
28         frames = 1;
29         mode = TimeAvgConfig::AVERAGE;
30         paranoid = 0;
31         nosubtract = 0;
34 void TimeAvgConfig::copy_from(TimeAvgConfig *src)
36         this->frames = src->frames;
37         this->mode = src->mode;
38         this->paranoid = src->paranoid;
39         this->nosubtract = src->nosubtract;
42 int TimeAvgConfig::equivalent(TimeAvgConfig *src)
44         return frames == src->frames &&
45                 mode == src->mode &&
46                 paranoid == src->paranoid &&
47                 nosubtract == src->nosubtract;
62 TimeAvgMain::TimeAvgMain(PluginServer *server)
63  : PluginVClient(server)
65         PLUGIN_CONSTRUCTOR_MACRO
66         accumulation = 0;
67         history = 0;
68         history_size = 0;
69         history_start = -0x7fffffff;
70         history_frame = 0;
71         history_valid = 0;
72         prev_frame = -1;
75 TimeAvgMain::~TimeAvgMain()
77         PLUGIN_DESTRUCTOR_MACRO
79         if(accumulation) delete [] accumulation;
80         if(history)
81         {
82                 for(int i = 0; i < config.frames; i++)
83                         delete history[i];
84                 delete [] history;
85         }
86         if(history_frame) delete [] history_frame;
87         if(history_valid) delete [] history_valid;
90 char* TimeAvgMain::plugin_title() { return N_("Time Average"); }
91 int TimeAvgMain::is_realtime() { return 1; }
94 NEW_PICON_MACRO(TimeAvgMain)
96 SHOW_GUI_MACRO(TimeAvgMain, TimeAvgThread)
98 SET_STRING_MACRO(TimeAvgMain)
100 RAISE_WINDOW_MACRO(TimeAvgMain);
104 int TimeAvgMain::process_buffer(VFrame *frame,
105                 int64_t start_position,
106                 double frame_rate)
108         int h = frame->get_h();
109         int w = frame->get_w();
110         int color_model = frame->get_color_model();
112         load_configuration();
114 // Allocate accumulation
115         if(!accumulation)
116         {
117                 accumulation = new unsigned char[w * 
118                         h * 
119                         cmodel_components(color_model) *
120                         MAX(sizeof(float), sizeof(int))];
121                 clear_accum(w, h, color_model);
122         }
124         if(!config.nosubtract)
125         {
126 // Reallocate history
127                 if(history)
128                 {
129                         if(config.frames != history_size)
130                         {
131                                 VFrame **history2;
132                                 int64_t *history_frame2;
133                                 int *history_valid2;
134                                 history2 = new VFrame*[config.frames];
135                                 history_frame2 = new int64_t[config.frames];
136                                 history_valid2 = new int[config.frames];
138 // Copy existing frames over
139                                 int i, j;
140                                 for(i = 0, j = 0; i < config.frames && j < history_size; i++, j++)
141                                 {
142                                         history2[i] = history[j];
143                                         history_frame2[i] = history_frame[i];
144                                         history_valid2[i] = history_valid[i];
145                                 }
147 // Delete extra previous frames and subtract from accumulation
148                                 for( ; j < history_size; j++)
149                                 {
150                                         subtract_accum(history[j]);
151                                         delete history[j];
152                                 }
153                                 delete [] history;
154                                 delete [] history_frame;
155                                 delete [] history_valid;
158 // Create new frames
159                                 for( ; i < config.frames; i++)
160                                 {
161                                         history2[i] = new VFrame(0, w, h, color_model);
162                                         history_frame2[i] = -0x7fffffff;
163                                         history_valid2[i] = 0;
164                                 }
166                                 history = history2;
167                                 history_frame = history_frame2;
168                                 history_valid = history_valid2;
170                                 history_size = config.frames;
171                         }
172                 }
173                 else
174 // Allocate history
175                 {
176                         history = new VFrame*[config.frames];
177                         for(int i = 0; i < config.frames; i++)
178                                 history[i] = new VFrame(0, w, h, color_model);
179                         history_size = config.frames;
180                         history_frame = new int64_t[config.frames];
181                         bzero(history_frame, sizeof(int64_t) * config.frames);
182                         history_valid = new int[config.frames];
183                         bzero(history_valid, sizeof(int) * config.frames);
184                 }
191 // Create new history frames based on current frame
192                 int64_t *new_history_frames = new int64_t[history_size];
193                 for(int i = 0; i < history_size; i++)
194                 {
195                         new_history_frames[history_size - i - 1] = start_position - i;
196                 }
198 // Subtract old history frames which are not in the new vector
199                 int no_change = 1;
200                 for(int i = 0; i < history_size; i++)
201                 {
202 // Old frame is valid
203                         if(history_valid[i])
204                         {
205                                 int got_it = 0;
206                                 for(int j = 0; j < history_size; j++)
207                                 {
208 // Old frame is equal to a new frame
209                                         if(history_frame[i] == new_history_frames[j]) 
210                                         {
211                                                 got_it = 1;
212                                                 break;
213                                         }
214                                 }
216 // Didn't find old frame in new frames
217                                 if(!got_it)
218                                 {
219                                         subtract_accum(history[i]);
220                                         history_valid[i] = 0;
221                                         no_change = 0;
222                                 }
223                         }
224                 }
225 // If all frames are still valid, assume tweek occurred upstream and reload.
226                 if(config.paranoid && no_change)
227                 {
228                         for(int i = 0; i < history_size; i++)
229                         {
230                                 history_valid[i] = 0;
231                         }
232                         clear_accum(w, h, color_model);
233                 }
235 // Add new history frames which are not in the old vector
236                 for(int i = 0; i < history_size; i++)
237                 {
238 // Find new frame in old vector
239                         int got_it = 0;
240                         for(int j = 0; j < history_size; j++)
241                         {
242                                 if(history_valid[j] && history_frame[j] == new_history_frames[i])
243                                 {
244                                         got_it = 1;
245                                         break;
246                                 }
247                         }
249 // Didn't find new frame in old vector
250                         if(!got_it)
251                         {
252 // Get first unused entry
253                                 for(int j = 0; j < history_size; j++)
254                                 {
255                                         if(!history_valid[j])
256                                         {
257 // Load new frame into it
258                                                 history_frame[j] = new_history_frames[i];
259                                                 history_valid[j] = 1;
260                                                 read_frame(history[j],
261                                                         0,
262                                                         history_frame[j],
263                                                         frame_rate);
264                                                 add_accum(history[j]);
265                                                 break;
266                                         }
267                                 }
268                         }
269                 }
270                 delete [] new_history_frames;
271         }
272         else
273 // No subtraction
274         {
275 // Force reload if not repositioned or just started
276                 if(config.paranoid && prev_frame == start_position ||
277                         prev_frame < 0)
278                 {
279                         prev_frame = start_position - config.frames + 1;
280                         prev_frame = MAX(0, prev_frame);
281                         clear_accum(w, h, color_model);
282                 }
284                 for(int64_t i = prev_frame; i <= start_position; i++)
285                 {
286                         read_frame(frame,
287                                 0,
288                                 i,
289                                 frame_rate);
290                         add_accum(frame);
291 printf("TimeAvgMain::process_buffer 1 %lld %lld %lld\n", prev_frame, start_position, i);
292                 }
294                 prev_frame = start_position;
295         }
303 // Transfer accumulation to output with division if average is desired.
304         transfer_accum(frame);
306 printf("TimeAvgMain::process_buffer 2\n");
309         return 0;
321 // Reset accumulation
322 #define CLEAR_ACCUM(type, components, chroma) \
323 { \
324         type *row = (type*)accumulation; \
325         if(chroma) \
326         { \
327                 for(int i = 0; i < w * h; i++) \
328                 { \
329                         *row++ = 0x0; \
330                         *row++ = chroma; \
331                         *row++ = chroma; \
332                         if(components == 4) *row++ = 0x0; \
333                 } \
334         } \
335         else \
336         { \
337                 bzero(row, w * h * sizeof(type) * components); \
338         } \
342 void TimeAvgMain::clear_accum(int w, int h, int color_model)
344         switch(color_model)
345         {
346                 case BC_RGB888:
347                         CLEAR_ACCUM(int, 3, 0x0)
348                         break;
349                 case BC_RGB_FLOAT:
350                         CLEAR_ACCUM(float, 3, 0x0)
351                         break;
352                 case BC_RGBA8888:
353                         CLEAR_ACCUM(int, 4, 0x0)
354                         break;
355                 case BC_RGBA_FLOAT:
356                         CLEAR_ACCUM(float, 4, 0x0)
357                         break;
358                 case BC_YUV888:
359                         CLEAR_ACCUM(int, 3, 0x80)
360                         break;
361                 case BC_YUVA8888:
362                         CLEAR_ACCUM(int, 4, 0x80)
363                         break;
364                 case BC_YUV161616:
365                         CLEAR_ACCUM(int, 3, 0x8000)
366                         break;
367                 case BC_YUVA16161616:
368                         CLEAR_ACCUM(int, 4, 0x8000)
369                         break;
370         }
374 #define SUBTRACT_ACCUM(type, \
375         accum_type, \
376         components, \
377         chroma) \
378 { \
379         if(config.mode == TimeAvgConfig::OR) \
380         { \
381                 for(int i = 0; i < h; i++) \
382                 { \
383                         accum_type *accum_row = (accum_type*)accumulation + \
384                                 i * w * components; \
385                         type *frame_row = (type*)frame->get_rows()[i]; \
386                         for(int j = 0; j < w; j++) \
387                         { \
388                                 if(components == 4) \
389                                 { \
390                                         frame_row += 3; \
391                                         if(*frame_row++) \
392                                         { \
393                                                 *accum_row++ = 0; \
394                                                 *accum_row++ = chroma; \
395                                                 *accum_row++ = chroma; \
396                                                 *accum_row++ = 0; \
397                                         } \
398                                 } \
399                                 else \
400                                 { \
401                                         if(*frame_row++ != 0 || \
402                                                 *frame_row++ != chroma || \
403                                                 *frame_row++ != chroma) \
404                                         { \
405                                                 *accum_row++ = 0; \
406                                                 *accum_row++ = chroma; \
407                                                 *accum_row++ = chroma; \
408                                         } \
409                                 } \
410                         } \
411                 } \
412         } \
413         else \
414         { \
415                 for(int i = 0; i < h; i++) \
416                 { \
417                         accum_type *accum_row = (accum_type*)accumulation + \
418                                 i * w * components; \
419                         type *frame_row = (type*)frame->get_rows()[i]; \
420                         for(int j = 0; j < w; j++) \
421                         { \
422                                 *accum_row++ -= *frame_row++; \
423                                 *accum_row++ -= (accum_type)*frame_row++ - chroma; \
424                                 *accum_row++ -= (accum_type)*frame_row++ - chroma; \
425                                 if(components == 4) *accum_row++ -= *frame_row++; \
426                         } \
427                 } \
428         } \
432 void TimeAvgMain::subtract_accum(VFrame *frame)
434 // Just accumulate
435         if(config.nosubtract) return;
436         int w = frame->get_w();
437         int h = frame->get_h();
439         switch(frame->get_color_model())
440         {
441                 case BC_RGB888:
442                         SUBTRACT_ACCUM(unsigned char, int, 3, 0x0)
443                         break;
444                 case BC_RGB_FLOAT:
445                         SUBTRACT_ACCUM(float, float, 3, 0x0)
446                         break;
447                 case BC_RGBA8888:
448                         SUBTRACT_ACCUM(unsigned char, int, 4, 0x0)
449                         break;
450                 case BC_RGBA_FLOAT:
451                         SUBTRACT_ACCUM(float, float, 4, 0x0)
452                         break;
453                 case BC_YUV888:
454                         SUBTRACT_ACCUM(unsigned char, int, 3, 0x80)
455                         break;
456                 case BC_YUVA8888:
457                         SUBTRACT_ACCUM(unsigned char, int, 4, 0x80)
458                         break;
459                 case BC_YUV161616:
460                         SUBTRACT_ACCUM(uint16_t, int, 3, 0x8000)
461                         break;
462                 case BC_YUVA16161616:
463                         SUBTRACT_ACCUM(uint16_t, int, 4, 0x8000)
464                         break;
465         }
469 // The behavior has to be very specific to the color model because we rely on
470 // the value of full black to determine what pixel to show.
471 #define ADD_ACCUM(type, accum_type, components, chroma, max) \
472 { \
473         if(config.mode == TimeAvgConfig::OR) \
474         { \
475                 for(int i = 0; i < h; i++) \
476                 { \
477                         accum_type *accum_row = (accum_type*)accumulation + \
478                                 i * w * components; \
479                         type *frame_row = (type*)frame->get_rows()[i]; \
480                         for(int j = 0; j < w; j++) \
481                         { \
482                                 if(components == 4) \
483                                 { \
484                                         accum_type opacity = frame_row[3]; \
485                                         accum_type transparency = max - opacity; \
486                                         *accum_row = (opacity * *frame_row + transparency * *accum_row) / max; \
487                                         accum_row++; \
488                                         frame_row++; \
489                                         *accum_row = chroma + (opacity * (*frame_row - chroma) + transparency * (*accum_row - chroma)) / max; \
490                                         accum_row++; \
491                                         frame_row++; \
492                                         *accum_row = chroma + (opacity * (*frame_row - chroma) + transparency * (*accum_row - chroma)) / max; \
493                                         accum_row++; \
494                                         frame_row++; \
495                                         *accum_row = MAX(*frame_row, *accum_row); \
496                                         accum_row++; \
497                                         frame_row++; \
498                                 } \
499                                 else \
500                                 if(sizeof(type) == 4) \
501                                 { \
502                                         if(frame_row[0] > 0.001 || \
503                                                 frame_row[1] > 0.001 || \
504                                                 frame_row[2] > 0.001) \
505                                         { \
506                                                 *accum_row++ = *frame_row++; \
507                                                 *accum_row++ = *frame_row++; \
508                                                 *accum_row++ = *frame_row++; \
509                                         } \
510                                         else \
511                                         { \
512                                                 frame_row += 3; \
513                                                 accum_row += 3; \
514                                         } \
515                                 } \
516                                 else \
517                                 if(chroma) \
518                                 { \
519                                         if(frame_row[0]) \
520                                         { \
521                                                 *accum_row++ = *frame_row++; \
522                                                 *accum_row++ = *frame_row++; \
523                                                 *accum_row++ = *frame_row++; \
524                                         } \
525                                         else \
526                                         { \
527                                                 frame_row += 3; \
528                                                 accum_row += 3; \
529                                         } \
530                                 } \
531                                 else \
532                                 { \
533                                         if(frame_row[0] || \
534                                                 frame_row[1] || \
535                                                 frame_row[2]) \
536                                         { \
537                                                 *accum_row++ = *frame_row++; \
538                                                 *accum_row++ = *frame_row++; \
539                                                 *accum_row++ = *frame_row++; \
540                                         } \
541                                         else \
542                                         { \
543                                                 frame_row += 3; \
544                                                 accum_row += 3; \
545                                         } \
546                                 } \
547                         } \
548                 } \
549         } \
550         else \
551         { \
552                 for(int i = 0; i < h; i++) \
553                 { \
554                         accum_type *accum_row = (accum_type*)accumulation + \
555                                 i * w * components; \
556                         type *frame_row = (type*)frame->get_rows()[i]; \
557                         for(int j = 0; j < w; j++) \
558                         { \
559                                 *accum_row++ += *frame_row++; \
560                                 *accum_row++ += (accum_type)*frame_row++ - chroma; \
561                                 *accum_row++ += (accum_type)*frame_row++ - chroma; \
562                                 if(components == 4) *accum_row++ += *frame_row++; \
563                         } \
564                 } \
565         } \
569 void TimeAvgMain::add_accum(VFrame *frame)
571         int w = frame->get_w();
572         int h = frame->get_h();
574         switch(frame->get_color_model())
575         {
576                 case BC_RGB888:
577                         ADD_ACCUM(unsigned char, int, 3, 0x0, 0xff)
578                         break;
579                 case BC_RGB_FLOAT:
580                         ADD_ACCUM(float, float, 3, 0x0, 1.0)
581                         break;
582                 case BC_RGBA8888:
583                         ADD_ACCUM(unsigned char, int, 4, 0x0, 0xff)
584                         break;
585                 case BC_RGBA_FLOAT:
586                         ADD_ACCUM(float, float, 4, 0x0, 1.0)
587                         break;
588                 case BC_YUV888:
589                         ADD_ACCUM(unsigned char, int, 3, 0x80, 0xff)
590                         break;
591                 case BC_YUVA8888:
592                         ADD_ACCUM(unsigned char, int, 4, 0x80, 0xff)
593                         break;
594                 case BC_YUV161616:
595                         ADD_ACCUM(uint16_t, int, 3, 0x8000, 0xffff)
596                         break;
597                 case BC_YUVA16161616:
598                         ADD_ACCUM(uint16_t, int, 4, 0x8000, 0xffff)
599                         break;
600         }
603 #define TRANSFER_ACCUM(type, accum_type, components, chroma, max) \
604 { \
605         if(config.mode == TimeAvgConfig::AVERAGE) \
606         { \
607                 accum_type denominator = config.frames; \
608                 for(int i = 0; i < h; i++) \
609                 { \
610                         accum_type *accum_row = (accum_type*)accumulation + \
611                                 i * w * components; \
612                         type *frame_row = (type*)frame->get_rows()[i]; \
613                         for(int j = 0; j < w; j++) \
614                         { \
615                                 *frame_row++ = *accum_row++ / denominator; \
616                                 *frame_row++ = (*accum_row++ - chroma) / denominator + chroma; \
617                                 *frame_row++ = (*accum_row++ - chroma) / denominator + chroma; \
618                                 if(components == 4) *frame_row++ = *accum_row++ / denominator; \
619                         } \
620                 } \
621         } \
622         else \
623         if(config.mode == TimeAvgConfig::ACCUMULATE) \
624         { \
625                 for(int i = 0; i < h; i++) \
626                 { \
627                         accum_type *accum_row = (accum_type*)accumulation + \
628                                 i * w * components; \
629                         type *frame_row = (type*)frame->get_rows()[i]; \
630                         for(int j = 0; j < w; j++) \
631                         { \
632                                 if(sizeof(type) < 4) \
633                                 { \
634                                         accum_type r = *accum_row++; \
635                                         accum_type g = *accum_row++ + chroma; \
636                                         accum_type b = *accum_row++ + chroma; \
637                                         *frame_row++ = CLIP(r, 0, max); \
638                                         *frame_row++ = CLIP(g, 0, max); \
639                                         *frame_row++ = CLIP(b, 0, max); \
640                                         if(components == 4) \
641                                         { \
642                                                 accum_type a = *accum_row++; \
643                                                 *frame_row++ = CLIP(a, 0, max); \
644                                         } \
645                                 } \
646                                 else \
647                                 { \
648                                         *frame_row++ = *accum_row++; \
649                                         *frame_row++ = *accum_row++ + chroma; \
650                                         *frame_row++ = *accum_row++ + chroma; \
651                                         if(components == 4) \
652                                         { \
653                                                 *frame_row++ = *accum_row++; \
654                                         } \
655                                 } \
656                         } \
657                 } \
658         } \
659         else \
660         { \
661                 for(int i = 0; i < h; i++) \
662                 { \
663                         accum_type *accum_row = (accum_type*)accumulation + \
664                                 i * w * components; \
665                         type *frame_row = (type*)frame->get_rows()[i]; \
666                         for(int j = 0; j < w; j++) \
667                         { \
668                                 *frame_row++ = *accum_row++; \
669                                 *frame_row++ = *accum_row++; \
670                                 *frame_row++ = *accum_row++; \
671                                 if(components == 4) *frame_row++ = *accum_row++; \
672                         } \
673                 } \
674         } \
678 void TimeAvgMain::transfer_accum(VFrame *frame)
680         int w = frame->get_w();
681         int h = frame->get_h();
683         switch(frame->get_color_model())
684         {
685                 case BC_RGB888:
686                         TRANSFER_ACCUM(unsigned char, int, 3, 0x0, 0xff)
687                         break;
688                 case BC_RGB_FLOAT:
689                         TRANSFER_ACCUM(float, float, 3, 0x0, 1)
690                         break;
691                 case BC_RGBA8888:
692                         TRANSFER_ACCUM(unsigned char, int, 4, 0x0, 0xff)
693                         break;
694                 case BC_RGBA_FLOAT:
695                         TRANSFER_ACCUM(float, float, 4, 0x0, 1)
696                         break;
697                 case BC_YUV888:
698                         TRANSFER_ACCUM(unsigned char, int, 3, 0x80, 0xff)
699                         break;
700                 case BC_YUVA8888:
701                         TRANSFER_ACCUM(unsigned char, int, 4, 0x80, 0xff)
702                         break;
703                 case BC_YUV161616:
704                         TRANSFER_ACCUM(uint16_t, int, 3, 0x8000, 0xffff)
705                         break;
706                 case BC_YUVA16161616:
707                         TRANSFER_ACCUM(uint16_t, int, 4, 0x8000, 0xffff)
708                         break;
709         }
713 int TimeAvgMain::load_defaults()
715         char directory[BCTEXTLEN], string[BCTEXTLEN];
716 // set the default directory
717         sprintf(directory, "%stimeavg.rc", BCASTDIR);
719 // load the defaults
720         defaults = new BC_Hash(directory);
721         defaults->load();
723         config.frames = defaults->get("FRAMES", config.frames);
724         config.mode = defaults->get("MODE", config.mode);
725         config.paranoid = defaults->get("PARANOID", config.paranoid);
726         config.nosubtract = defaults->get("NOSUBTRACT", config.nosubtract);
727         return 0;
730 int TimeAvgMain::save_defaults()
732         defaults->update("FRAMES", config.frames);
733         defaults->update("MODE", config.mode);
734         defaults->update("PARANOID", config.paranoid);
735         defaults->update("NOSUBTRACT", config.nosubtract);
736         defaults->save();
737         return 0;
740 int TimeAvgMain::load_configuration()
742         KeyFrame *prev_keyframe;
743         TimeAvgConfig old_config;
744         old_config.copy_from(&config);
746         prev_keyframe = get_prev_keyframe(get_source_position());
747         read_data(prev_keyframe);
748         return !old_config.equivalent(&config);
751 void TimeAvgMain::save_data(KeyFrame *keyframe)
753         FileXML output;
755 // cause data to be stored directly in text
756         output.set_shared_string(keyframe->data, MESSAGESIZE);
757         output.tag.set_title("TIME_AVERAGE");
758         output.tag.set_property("FRAMES", config.frames);
759         output.tag.set_property("MODE", config.mode);
760         output.tag.set_property("PARANOID", config.paranoid);
761         output.tag.set_property("NOSUBTRACT", config.nosubtract);
762         output.append_tag();
763         output.terminate_string();
766 void TimeAvgMain::read_data(KeyFrame *keyframe)
768         FileXML input;
770         input.set_shared_string(keyframe->data, strlen(keyframe->data));
772         int result = 0;
774         while(!input.read_tag())
775         {
776                 if(input.tag.title_is("TIME_AVERAGE"))
777                 {
778                         config.frames = input.tag.get_property("FRAMES", config.frames);
779                         config.mode = input.tag.get_property("MODE", config.mode);
780                         config.paranoid = input.tag.get_property("PARANOID", config.paranoid);
781                         config.nosubtract = input.tag.get_property("NOSUBTRACT", config.nosubtract);
782                 }
783         }
787 void TimeAvgMain::update_gui()
789         if(thread) 
790         {
791                 if(load_configuration())
792                 {
793                         thread->window->lock_window("TimeAvgMain::update_gui");
794                         thread->window->total_frames->update(config.frames);
795                         thread->window->accum->update(config.mode == TimeAvgConfig::ACCUMULATE);
796                         thread->window->avg->update(config.mode == TimeAvgConfig::AVERAGE);
797                         thread->window->inclusive_or->update(config.mode == TimeAvgConfig::OR);
798                         thread->window->paranoid->update(config.paranoid);
799                         thread->window->no_subtract->update(config.nosubtract);
800                         thread->window->unlock_window();
801                 }
802         }