Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / plugins / timeavg / timeavg.C
diff --git a/cinelerra-5.1/plugins/timeavg/timeavg.C b/cinelerra-5.1/plugins/timeavg/timeavg.C
new file mode 100644 (file)
index 0000000..548876f
--- /dev/null
@@ -0,0 +1,1092 @@
+
+/*
+ * CINELERRA
+ * Copyright (C) 1997-2011 Adam Williams <broadcast at earthling dot net>
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ */
+
+#include "clip.h"
+#include "bchash.h"
+#include "filexml.h"
+#include "keyframe.h"
+#include "language.h"
+#include "cicolors.h"
+#include "timeavg.h"
+#include "timeavgwindow.h"
+#include "transportque.h"
+#include "vframe.h"
+
+
+
+
+#include <stdint.h>
+#include <string.h>
+
+
+
+
+REGISTER_PLUGIN(TimeAvgMain)
+
+
+
+
+
+TimeAvgConfig::TimeAvgConfig()
+{
+       frames = 1;
+       mode = TimeAvgConfig::AVERAGE;
+       paranoid = 0;
+       nosubtract = 0;
+       threshold = 1;
+       border = 2;
+}
+
+void TimeAvgConfig::copy_from(TimeAvgConfig *src)
+{
+       this->frames = src->frames;
+       this->mode = src->mode;
+       this->paranoid = src->paranoid;
+       this->nosubtract = src->nosubtract;
+       this->threshold = src->threshold;
+       this->border = src->border;
+}
+
+int TimeAvgConfig::equivalent(TimeAvgConfig *src)
+{
+       return frames == src->frames &&
+               mode == src->mode &&
+               paranoid == src->paranoid &&
+               nosubtract == src->nosubtract &&
+               threshold == src->threshold &&
+               border == src->border;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+TimeAvgMain::TimeAvgMain(PluginServer *server)
+ : PluginVClient(server)
+{
+       
+       accumulation = 0;
+       history = 0;
+       history_size = 0;
+       history_start = -0x7fffffff;
+       history_frame = 0;
+       history_valid = 0;
+       prev_frame = -1;
+}
+
+TimeAvgMain::~TimeAvgMain()
+{
+       
+
+       if(accumulation) delete [] accumulation;
+       if(history)
+       {
+               for(int i = 0; i < config.frames; i++)
+                       delete history[i];
+               delete [] history;
+       }
+       if(history_frame) delete [] history_frame;
+       if(history_valid) delete [] history_valid;
+}
+
+const char* TimeAvgMain::plugin_title() { return _("Time Average"); }
+int TimeAvgMain::is_realtime() { return 1; }
+
+
+
+NEW_WINDOW_MACRO(TimeAvgMain, TimeAvgWindow);
+
+
+
+int TimeAvgMain::process_buffer(VFrame *frame,
+               int64_t start_position,
+               double frame_rate)
+{
+       int h = frame->get_h();
+       int w = frame->get_w();
+       int color_model = frame->get_color_model();
+
+       int reset = load_configuration();
+
+// reset buffer on the keyframes
+       int64_t actual_previous_number = start_position;
+       if(get_direction() == PLAY_FORWARD)
+       {
+               actual_previous_number--;
+               if(actual_previous_number < get_source_start())
+                       reset = 1;
+               else
+               {
+                       KeyFrame *keyframe = get_prev_keyframe(start_position, 1);
+                       if(keyframe->position > 0 &&
+                               actual_previous_number < keyframe->position)
+                               reset = 1;
+               }
+       }
+       else
+       {
+               actual_previous_number++;
+               if(actual_previous_number >= get_source_start() + get_total_len())
+                       reset = 1;
+               else
+               {
+                       KeyFrame *keyframe = get_next_keyframe(start_position, 1);
+                       if(keyframe->position > 0 &&
+                               actual_previous_number >= keyframe->position)
+                               reset = 1;
+               }
+       }
+
+// Allocate accumulation
+       if(!accumulation || reset)
+       {
+               if(!accumulation) accumulation = new unsigned char[w * 
+                       h * 
+                       BC_CModels::components(color_model) *
+                       MAX(sizeof(float), sizeof(int))];
+               reset_accum(w, h, color_model);
+       }
+
+       if(!config.nosubtract &&
+               (config.mode == TimeAvgConfig::AVERAGE ||
+               config.mode == TimeAvgConfig::ACCUMULATE ||
+               config.mode == TimeAvgConfig::GREATER ||
+               config.mode == TimeAvgConfig::LESS))
+       {
+// Reallocate history
+               if(history)
+               {
+                       if(config.frames != history_size)
+                       {
+                               VFrame **history2;
+                               int64_t *history_frame2;
+                               int *history_valid2;
+                               history2 = new VFrame*[config.frames];
+                               history_frame2 = new int64_t[config.frames];
+                               history_valid2 = new int[config.frames];
+
+// Copy existing frames over
+                               int i, j;
+                               for(i = 0, j = 0; i < config.frames && j < history_size; i++, j++)
+                               {
+                                       history2[i] = history[j];
+                                       history_frame2[i] = history_frame[i];
+                                       history_valid2[i] = history_valid[i];
+                               }
+
+// Delete extra previous frames and subtract from accumulation
+                               for( ; j < history_size; j++)
+                               {
+                                       subtract_accum(history[j]);
+                                       delete history[j];
+                               }
+                               delete [] history;
+                               delete [] history_frame;
+                               delete [] history_valid;
+
+
+// Create new frames
+                               for( ; i < config.frames; i++)
+                               {
+                                       history2[i] = new VFrame(w, h, color_model);
+                                       history_frame2[i] = -0x7fffffff;
+                                       history_valid2[i] = 0;
+                               }
+
+                               history = history2;
+                               history_frame = history_frame2;
+                               history_valid = history_valid2;
+
+                               history_size = config.frames;
+                       }
+               }
+               else
+// Allocate history
+               {
+                       history = new VFrame*[config.frames];
+                       for(int i = 0; i < config.frames; i++)
+                               history[i] = new VFrame(w, h, color_model);
+                       history_size = config.frames;
+                       history_frame = new int64_t[config.frames];
+                       bzero(history_frame, sizeof(int64_t) * config.frames);
+                       history_valid = new int[config.frames];
+                       bzero(history_valid, sizeof(int) * config.frames);
+               }
+
+//printf("TimeAvgMain::process_buffer %d\n", __LINE__);
+
+
+
+
+// Create new history frames based on current frame
+               int64_t *new_history_frames = new int64_t[history_size];
+               for(int i = 0; i < history_size; i++)
+               {
+                       new_history_frames[history_size - i - 1] = start_position - i;
+               }
+
+// Subtract old history frames from accumulation buffer
+// which are not in the new vector
+               int no_change = 1;
+               for(int i = 0; i < history_size; i++)
+               {
+// Old frame is valid
+                       if(history_valid[i])
+                       {
+                               int got_it = 0;
+                               for(int j = 0; j < history_size; j++)
+                               {
+// Old frame is equal to a new frame
+                                       if(history_frame[i] == new_history_frames[j]) 
+                                       {
+                                               got_it = 1;
+                                               break;
+                                       }
+                               }
+
+// Didn't find old frame in new frames
+                               if(!got_it)
+                               {
+                                       if(config.mode == TimeAvgConfig::AVERAGE ||
+                                               config.mode == TimeAvgConfig::ACCUMULATE)
+                                       {
+                                               subtract_accum(history[i]);
+                                       }
+
+                                       history_valid[i] = 0;
+                                       no_change = 0;
+                               }
+                       }
+               }
+// If all frames are still valid, assume tweek occurred upstream and reload.
+               if(config.paranoid && no_change)
+               {
+                       for(int i = 0; i < history_size; i++)
+                       {
+                               history_valid[i] = 0;
+                       }
+
+                       if(config.mode == TimeAvgConfig::AVERAGE ||
+                               config.mode == TimeAvgConfig::ACCUMULATE)
+                       {
+                               reset_accum(w, h, color_model);
+                       }
+               }
+
+// Add new history frames which are not in the old vector
+               for(int i = 0; i < history_size; i++)
+               {
+// Find new frame in old vector
+                       int got_it = 0;
+                       for(int j = 0; j < history_size; j++)
+                       {
+                               if(history_valid[j] && history_frame[j] == new_history_frames[i])
+                               {
+                                       got_it = 1;
+                                       break;
+                               }
+                       }
+
+// Didn't find new frame in old vector
+                       if(!got_it)
+                       {
+// Get first unused entry
+                               for(int j = 0; j < history_size; j++)
+                               {
+                                       if(!history_valid[j])
+                                       {
+// Load new frame into it
+                                               history_frame[j] = new_history_frames[i];
+                                               history_valid[j] = 1;
+                                               read_frame(history[j],
+                                                       0,
+                                                       history_frame[j],
+                                                       frame_rate,
+                                                       0);
+                                               if(config.mode == TimeAvgConfig::AVERAGE ||
+                                                       config.mode == TimeAvgConfig::ACCUMULATE)
+                                               {
+                                                       add_accum(history[j]);
+                                               }
+                                               break;
+                                       }
+                               }
+                       }
+               }
+               delete [] new_history_frames;
+       }
+       else
+// No history subtraction
+       {
+               if(history)
+               {
+                       for(int i = 0; i < config.frames; i++)
+                               delete history[i];
+                       delete [] history;
+                       history = 0;
+               }
+
+               if(history_frame) delete [] history_frame;
+               if(history_valid) delete [] history_valid;
+               history_frame = 0;
+               history_valid = 0;
+               history_size = 0;
+
+// Clamp prev_frame to history size
+               prev_frame = MAX(start_position - config.frames + 1, prev_frame);
+
+// Force reload if not repositioned or just started
+               if( (config.paranoid && prev_frame == start_position) ||
+                       prev_frame < 0)
+               {
+//printf("TimeAvgMain::process_buffer %d\n", __LINE__);
+                       prev_frame = start_position - config.frames + 1;
+                       prev_frame = MAX(0, prev_frame);
+                       reset_accum(w, h, color_model);
+               }
+
+// printf("TimeAvgMain::process_buffer %d prev_frame=%jd start_position=%jd\n", 
+//   __LINE__, prev_frame, start_position);
+               for(int64_t i = prev_frame; i <= start_position; i++)
+               {
+                       read_frame(frame,
+                               0,
+                               i,
+                               frame_rate,
+                               0);
+                       add_accum(frame);
+printf("TimeAvgMain::process_buffer %d prev_frame=%jd start_position=%jd i=%jd\n", 
+  __LINE__, prev_frame, start_position, i);
+               }
+
+// If we don't add 1, it rereads the frame again
+               prev_frame = start_position + 1;
+       }
+
+
+
+
+
+
+
+// Transfer accumulation to output with division if average is desired.
+       transfer_accum(frame);
+
+//printf("TimeAvgMain::process_buffer %d\n", __LINE__);
+
+
+       return 0;
+}
+
+
+
+
+
+
+
+
+
+
+// Reset accumulation
+#define SET_ACCUM(type, components, luma, chroma) \
+{ \
+       type *row = (type*)accumulation; \
+       if(chroma) \
+       { \
+               for(int i = 0; i < w * h; i++) \
+               { \
+                       *row++ = luma; \
+                       *row++ = chroma; \
+                       *row++ = chroma; \
+                       if(components == 4) *row++ = luma; \
+               } \
+       } \
+       else \
+       { \
+               bzero(row, w * h * sizeof(type) * components); \
+       } \
+}
+
+
+void TimeAvgMain::reset_accum(int w, int h, int color_model)
+{
+       if(config.mode == TimeAvgConfig::LESS)
+       {
+               switch(color_model)
+               {
+                       case BC_RGB888:
+                               SET_ACCUM(int, 3, 0xff, 0xff)
+                               break;
+                       case BC_RGB_FLOAT:
+                               SET_ACCUM(float, 3, 1.0, 1.0)
+                               break;
+                       case BC_RGBA8888:
+                               SET_ACCUM(int, 4, 0xff, 0xff)
+                               break;
+                       case BC_RGBA_FLOAT:
+                               SET_ACCUM(float, 4, 1.0, 1.0)
+                               break;
+                       case BC_YUV888:
+                               SET_ACCUM(int, 3, 0xff, 0x80)
+                               break;
+                       case BC_YUVA8888:
+                               SET_ACCUM(int, 4, 0xff, 0x80)
+                               break;
+                       case BC_YUV161616:
+                               SET_ACCUM(int, 3, 0xffff, 0x8000)
+                               break;
+                       case BC_YUVA16161616:
+                               SET_ACCUM(int, 4, 0xffff, 0x8000)
+                               break;
+               }
+       }
+       else
+       {
+               switch(color_model)
+               {
+                       case BC_RGB888:
+                               SET_ACCUM(int, 3, 0x0, 0x0)
+                               break;
+                       case BC_RGB_FLOAT:
+                               SET_ACCUM(float, 3, 0x0, 0x0)
+                               break;
+                       case BC_RGBA8888:
+                               SET_ACCUM(int, 4, 0x0, 0x0)
+                               break;
+                       case BC_RGBA_FLOAT:
+                               SET_ACCUM(float, 4, 0x0, 0x0)
+                               break;
+                       case BC_YUV888:
+                               SET_ACCUM(int, 3, 0x0, 0x80)
+                               break;
+                       case BC_YUVA8888:
+                               SET_ACCUM(int, 4, 0x0, 0x80)
+                               break;
+                       case BC_YUV161616:
+                               SET_ACCUM(int, 3, 0x0, 0x8000)
+                               break;
+                       case BC_YUVA16161616:
+                               SET_ACCUM(int, 4, 0x0, 0x8000)
+                               break;
+               }
+       }
+}
+
+#define RGB_TO_VALUE(r, g, b) \
+((r) * R_TO_Y + (g) * G_TO_Y + (b) * B_TO_Y)
+
+// Only AVERAGE and ACCUMULATE use this
+#define SUBTRACT_ACCUM(type, \
+       accum_type, \
+       components, \
+       chroma) \
+{ \
+       for(int i = 0; i < h; i++) \
+       { \
+               accum_type *accum_row = (accum_type*)accumulation + \
+                       i * w * components; \
+               type *frame_row = (type*)frame->get_rows()[i]; \
+               for(int j = 0; j < w; j++) \
+               { \
+                       *accum_row++ -= *frame_row++; \
+                       *accum_row++ -= (accum_type)*frame_row++ - chroma; \
+                       *accum_row++ -= (accum_type)*frame_row++ - chroma; \
+                       if(components == 4) *accum_row++ -= *frame_row++; \
+               } \
+       } \
+}
+
+
+void TimeAvgMain::subtract_accum(VFrame *frame)
+{
+// Just accumulate
+       if(config.nosubtract) return;
+       int w = frame->get_w();
+       int h = frame->get_h();
+
+       switch(frame->get_color_model())
+       {
+               case BC_RGB888:
+                       SUBTRACT_ACCUM(unsigned char, int, 3, 0x0)
+                       break;
+               case BC_RGB_FLOAT:
+                       SUBTRACT_ACCUM(float, float, 3, 0x0)
+                       break;
+               case BC_RGBA8888:
+                       SUBTRACT_ACCUM(unsigned char, int, 4, 0x0)
+                       break;
+               case BC_RGBA_FLOAT:
+                       SUBTRACT_ACCUM(float, float, 4, 0x0)
+                       break;
+               case BC_YUV888:
+                       SUBTRACT_ACCUM(unsigned char, int, 3, 0x80)
+                       break;
+               case BC_YUVA8888:
+                       SUBTRACT_ACCUM(unsigned char, int, 4, 0x80)
+                       break;
+               case BC_YUV161616:
+                       SUBTRACT_ACCUM(uint16_t, int, 3, 0x8000)
+                       break;
+               case BC_YUVA16161616:
+                       SUBTRACT_ACCUM(uint16_t, int, 4, 0x8000)
+                       break;
+       }
+}
+
+
+// The behavior has to be very specific to the color model because we rely on
+// the value of full black to determine what pixel to show.
+#define ADD_ACCUM(type, accum_type, components, chroma, max) \
+{ \
+       if(config.mode == TimeAvgConfig::REPLACE) \
+       { \
+               type threshold = config.threshold; \
+               if(sizeof(type) == 4) \
+                       threshold /= 256; \
+/* Compare all pixels if border */ \
+               if(config.border > 0) \
+               { \
+                       int border = config.border; \
+                       int h_border = h - border - 1; \
+                       int w_border = w - border - 1; \
+                       int kernel_size = (border * 2 + 1) * (border * 2 + 1); \
+                       for(int i = border; i < h_border; i++) \
+                       { \
+                               for(int j = border; j < w_border; j++) \
+                               { \
+                                       int copy_it = 0; \
+                                       for(int k = -border; k <= border; k++) \
+                                       { \
+                                               type *frame_row = (type*)frame->get_rows()[i + k]; \
+                                               for(int l = -border; l <= border; l++) \
+                                               { \
+                                                       type *frame_pixel = frame_row + (j + l) * components; \
+/* Compare alpha if 4 channel */ \
+                                                       if(components == 4) \
+                                                       { \
+                                                               if(frame_pixel[3] > threshold) \
+                                                                       copy_it++; \
+                                                       } \
+                                                       else \
+                                                       if(sizeof(type) == 4) \
+                                                       { \
+/* Compare luma if 3 channel */ \
+                                                               if(RGB_TO_VALUE(frame_pixel[0], frame_pixel[1], frame_pixel[2]) >= \
+                                                                       threshold) \
+                                                               { \
+                                                                       copy_it++; \
+                                                               } \
+                                                       } \
+                                                       else \
+                                                       if(chroma) \
+                                                       { \
+                                                               if(frame_pixel[0] >= threshold) \
+                                                               { \
+                                                                       copy_it++; \
+                                                               } \
+                                                       } \
+                                                       else \
+                                                       if(RGB_TO_VALUE(frame_pixel[0], frame_pixel[1], frame_pixel[2]) >= threshold) \
+                                                       { \
+                                                               copy_it++; \
+                                                       } \
+                                               } \
+                                       } \
+ \
+                                       if(copy_it == kernel_size) \
+                                       { \
+                                               accum_type *accum_row = (accum_type*)accumulation + \
+                                                       i * w * components + j * components; \
+                                               type *frame_row = (type*)frame->get_rows()[i] + j * components; \
+                                               *accum_row++ = *frame_row++; \
+                                               *accum_row++ = *frame_row++; \
+                                               *accum_row++ = *frame_row++; \
+                                               if(components == 4) *accum_row++ = *frame_row++; \
+                                       } \
+ \
+                               } \
+                       } \
+               } \
+               else \
+/* Compare only relevant pixel if no border */ \
+               { \
+                       for(int i = 0; i < h; i++) \
+                       { \
+                               accum_type *accum_row = (accum_type*)accumulation + \
+                                       i * w * components; \
+                               type *frame_row = (type*)frame->get_rows()[i]; \
+                               for(int j = 0; j < w; j++) \
+                               { \
+                                       int copy_it = 0; \
+/* Compare alpha if 4 channel */ \
+                                       if(components == 4) \
+                                       { \
+                                               if(frame_row[3] > threshold) \
+                                                       copy_it = 1; \
+                                       } \
+                                       else \
+                                       if(sizeof(type) == 4) \
+                                       { \
+/* Compare luma if 3 channel */ \
+                                               if(RGB_TO_VALUE(frame_row[0], frame_row[1], frame_row[2]) >= \
+                                                       threshold) \
+                                               { \
+                                                       copy_it = 1; \
+                                               } \
+                                       } \
+                                       else \
+                                       if(chroma) \
+                                       { \
+                                               if(frame_row[0] >= threshold) \
+                                               { \
+                                                       copy_it = 1; \
+                                               } \
+                                       } \
+                                       else \
+                                       if(RGB_TO_VALUE(frame_row[0], frame_row[1], frame_row[2]) >= threshold) \
+                                       { \
+                                               copy_it = 1; \
+                                       } \
+ \
+                                       if(copy_it) \
+                                       { \
+                                               *accum_row++ = *frame_row++; \
+                                               *accum_row++ = *frame_row++; \
+                                               *accum_row++ = *frame_row++; \
+                                               if(components == 4) *accum_row++ = *frame_row++; \
+                                       } \
+                                       else \
+                                       { \
+                                               frame_row += components; \
+                                               accum_row += components; \
+                                       } \
+                               } \
+                       } \
+               } \
+       } \
+       else \
+       if(config.mode == TimeAvgConfig::GREATER) \
+       { \
+               for(int i = 0; i < h; i++) \
+               { \
+                       accum_type *accum_row = (accum_type*)accumulation + \
+                               i * w * components; \
+                       type *frame_row = (type*)frame->get_rows()[i]; \
+                       for(int j = 0; j < w; j++) \
+                       { \
+                               int copy_it = 0; \
+/* Compare alpha if 4 channel */ \
+                               if(components == 4) \
+                               { \
+                                       if(frame_row[3] > accum_row[3]) copy_it = 1; \
+                               } \
+                               else \
+                               if(chroma) \
+                               { \
+/* Compare YUV luma if 3 channel */ \
+                                       if(frame_row[0] > accum_row[0]) copy_it = 1; \
+                               } \
+                               else \
+                               { \
+/* Compare RGB luma if 3 channel */ \
+                                       if(RGB_TO_VALUE(frame_row[0], frame_row[1], frame_row[2]) > \
+                                               RGB_TO_VALUE(accum_row[0], accum_row[1], accum_row[2])) \
+                                               copy_it = 1; \
+                               } \
+ \
+                               if(copy_it) \
+                               { \
+                                       *accum_row++ = *frame_row++; \
+                                       *accum_row++ = *frame_row++; \
+                                       *accum_row++ = *frame_row++; \
+                                       if(components == 4) *accum_row++ = *frame_row++; \
+                               } \
+                               else \
+                               { \
+                                       accum_row += components; \
+                                       frame_row += components; \
+                               } \
+                       } \
+               } \
+       } \
+       else \
+       if(config.mode == TimeAvgConfig::LESS) \
+       { \
+               for(int i = 0; i < h; i++) \
+               { \
+                       accum_type *accum_row = (accum_type*)accumulation + \
+                               i * w * components; \
+                       type *frame_row = (type*)frame->get_rows()[i]; \
+                       for(int j = 0; j < w; j++) \
+                       { \
+                               int copy_it = 0; \
+/* Compare alpha if 4 channel */ \
+                               if(components == 4) \
+                               { \
+                                       if(frame_row[3] < accum_row[3]) copy_it = 1; \
+                               } \
+                               else \
+                               if(chroma) \
+                               { \
+/* Compare YUV luma if 3 channel */ \
+                                       if(frame_row[0] < accum_row[0]) copy_it = 1; \
+                               } \
+                               else \
+                               { \
+/* Compare RGB luma if 3 channel */ \
+                                       if(RGB_TO_VALUE(frame_row[0], frame_row[1], frame_row[2]) < \
+                                               RGB_TO_VALUE(accum_row[0], accum_row[1], accum_row[2])) \
+                                               copy_it = 1; \
+                               } \
+ \
+                               if(copy_it) \
+                               { \
+                                       *accum_row++ = *frame_row++; \
+                                       *accum_row++ = *frame_row++; \
+                                       *accum_row++ = *frame_row++; \
+                                       if(components == 4) *accum_row++ = *frame_row++; \
+                               } \
+                               else \
+                               { \
+                                       accum_row += components; \
+                                       frame_row += components; \
+                               } \
+                       } \
+               } \
+       } \
+       else \
+       { \
+               for(int i = 0; i < h; i++) \
+               { \
+                       accum_type *accum_row = (accum_type*)accumulation + \
+                               i * w * components; \
+                       type *frame_row = (type*)frame->get_rows()[i]; \
+                       for(int j = 0; j < w; j++) \
+                       { \
+                               *accum_row++ += *frame_row++; \
+                               *accum_row++ += (accum_type)*frame_row++ - chroma; \
+                               *accum_row++ += (accum_type)*frame_row++ - chroma; \
+                               if(components == 4) *accum_row++ += *frame_row++; \
+                       } \
+               } \
+       } \
+}
+
+
+void TimeAvgMain::add_accum(VFrame *frame)
+{
+       int w = frame->get_w();
+       int h = frame->get_h();
+
+       switch(frame->get_color_model())
+       {
+               case BC_RGB888:
+                       ADD_ACCUM(unsigned char, int, 3, 0x0, 0xff)
+                       break;
+               case BC_RGB_FLOAT:
+                       ADD_ACCUM(float, float, 3, 0x0, 1.0)
+                       break;
+               case BC_RGBA8888:
+                       ADD_ACCUM(unsigned char, int, 4, 0x0, 0xff)
+                       break;
+               case BC_RGBA_FLOAT:
+                       ADD_ACCUM(float, float, 4, 0x0, 1.0)
+                       break;
+               case BC_YUV888:
+                       ADD_ACCUM(unsigned char, int, 3, 0x80, 0xff)
+                       break;
+               case BC_YUVA8888:
+                       ADD_ACCUM(unsigned char, int, 4, 0x80, 0xff)
+                       break;
+               case BC_YUV161616:
+                       ADD_ACCUM(uint16_t, int, 3, 0x8000, 0xffff)
+                       break;
+               case BC_YUVA16161616:
+                       ADD_ACCUM(uint16_t, int, 4, 0x8000, 0xffff)
+                       break;
+       }
+}
+
+#define TRANSFER_ACCUM(type, accum_type, components, chroma, max) \
+{ \
+       if(config.mode == TimeAvgConfig::AVERAGE) \
+       { \
+               accum_type denominator = config.frames; \
+               for(int i = 0; i < h; i++) \
+               { \
+                       accum_type *accum_row = (accum_type*)accumulation + \
+                               i * w * components; \
+                       type *frame_row = (type*)frame->get_rows()[i]; \
+                       for(int j = 0; j < w; j++) \
+                       { \
+                               *frame_row++ = *accum_row++ / denominator; \
+                               *frame_row++ = (*accum_row++ - chroma) / denominator + chroma; \
+                               *frame_row++ = (*accum_row++ - chroma) / denominator + chroma; \
+                               if(components == 4) *frame_row++ = *accum_row++ / denominator; \
+                       } \
+               } \
+       } \
+       else \
+/* Rescan history every time for these modes */ \
+       if(!config.nosubtract && config.mode == TimeAvgConfig::GREATER) \
+       { \
+               frame->copy_from(history[0]); \
+               for(int k = 1; k < config.frames; k++) \
+               { \
+                       VFrame *history_frame = history[k]; \
+ \
+                       for(int i = 0; i < h; i++) \
+                       { \
+                               type *history_row = (type*)history_frame->get_rows()[i]; \
+                               type *frame_row = (type*)frame->get_rows()[i]; \
+ \
+                               for(int j = 0; j < w; j++) \
+                               { \
+                                       int copy_it = 0; \
+/* Compare alpha if 4 channel */ \
+                                       if(components == 4) \
+                                       { \
+                                               if(history_row[3] > frame_row[3]) copy_it = 1; \
+                                       } \
+                                       else \
+                                       if(chroma) \
+                                       { \
+/* Compare YUV luma if 3 channel */ \
+                                               if(history_row[0] > frame_row[0]) copy_it = 1; \
+                                       } \
+                                       else \
+                                       { \
+/* Compare RGB luma if 3 channel */ \
+                                               if(RGB_TO_VALUE(history_row[0], history_row[1], history_row[2]) > \
+                                                       RGB_TO_VALUE(frame_row[0], frame_row[1], frame_row[2])) \
+                                                       copy_it = 1; \
+                                       } \
+ \
+                                       if(copy_it) \
+                                       { \
+                                               *frame_row++ = *history_row++; \
+                                               *frame_row++ = *history_row++; \
+                                               *frame_row++ = *history_row++; \
+                                               if(components == 4) *frame_row++ = *history_row++; \
+                                       } \
+                                       else \
+                                       { \
+                                               frame_row += components; \
+                                               history_row += components; \
+                                       } \
+                               } \
+                       } \
+               } \
+       } \
+       else \
+       if(!config.nosubtract && config.mode == TimeAvgConfig::LESS) \
+       { \
+               frame->copy_from(history[0]); \
+               for(int k = 1; k < config.frames; k++) \
+               { \
+                       VFrame *history_frame = history[k]; \
+ \
+                       for(int i = 0; i < h; i++) \
+                       { \
+                               type *history_row = (type*)history_frame->get_rows()[i]; \
+                               type *frame_row = (type*)frame->get_rows()[i]; \
+ \
+                               for(int j = 0; j < w; j++) \
+                               { \
+                                       int copy_it = 0; \
+/* Compare alpha if 4 channel */ \
+                                       if(components == 4) \
+                                       { \
+                                               if(history_row[3] < frame_row[3]) copy_it = 1; \
+                                       } \
+                                       else \
+                                       if(chroma) \
+                                       { \
+/* Compare YUV luma if 3 channel */ \
+                                               if(history_row[0] < frame_row[0]) copy_it = 1; \
+                                       } \
+                                       else \
+                                       { \
+/* Compare RGB luma if 3 channel */ \
+                                               if(RGB_TO_VALUE(history_row[0], history_row[1], history_row[2]) < \
+                                                       RGB_TO_VALUE(frame_row[0], frame_row[1], frame_row[2])) \
+                                                       copy_it = 1; \
+                                       } \
+ \
+                                       if(copy_it) \
+                                       { \
+                                               *frame_row++ = *history_row++; \
+                                               *frame_row++ = *history_row++; \
+                                               *frame_row++ = *history_row++; \
+                                               if(components == 4) *frame_row++ = *history_row++; \
+                                       } \
+                                       else \
+                                       { \
+                                               frame_row += components; \
+                                               history_row += components; \
+                                       } \
+                               } \
+                       } \
+               } \
+       } \
+       else \
+       { \
+               for(int i = 0; i < h; i++) \
+               { \
+                       accum_type *accum_row = (accum_type*)accumulation + \
+                               i * w * components; \
+                       type *frame_row = (type*)frame->get_rows()[i]; \
+                       for(int j = 0; j < w; j++) \
+                       { \
+                               *frame_row++ = *accum_row++; \
+                               *frame_row++ = *accum_row++; \
+                               *frame_row++ = *accum_row++; \
+                               if(components == 4) *frame_row++ = *accum_row++; \
+                       } \
+               } \
+       } \
+}
+
+
+void TimeAvgMain::transfer_accum(VFrame *frame)
+{
+       int w = frame->get_w();
+       int h = frame->get_h();
+
+       switch(frame->get_color_model())
+       {
+               case BC_RGB888:
+                       TRANSFER_ACCUM(unsigned char, int, 3, 0x0, 0xff)
+                       break;
+               case BC_RGB_FLOAT:
+                       TRANSFER_ACCUM(float, float, 3, 0x0, 1)
+                       break;
+               case BC_RGBA8888:
+                       TRANSFER_ACCUM(unsigned char, int, 4, 0x0, 0xff)
+                       break;
+               case BC_RGBA_FLOAT:
+                       TRANSFER_ACCUM(float, float, 4, 0x0, 1)
+                       break;
+               case BC_YUV888:
+                       TRANSFER_ACCUM(unsigned char, int, 3, 0x80, 0xff)
+                       break;
+               case BC_YUVA8888:
+                       TRANSFER_ACCUM(unsigned char, int, 4, 0x80, 0xff)
+                       break;
+               case BC_YUV161616:
+                       TRANSFER_ACCUM(uint16_t, int, 3, 0x8000, 0xffff)
+                       break;
+               case BC_YUVA16161616:
+                       TRANSFER_ACCUM(uint16_t, int, 4, 0x8000, 0xffff)
+                       break;
+       }
+}
+
+
+
+int TimeAvgMain::load_configuration()
+{
+       KeyFrame *prev_keyframe;
+       TimeAvgConfig old_config;
+       old_config.copy_from(&config);
+
+       prev_keyframe = get_prev_keyframe(get_source_position());
+       read_data(prev_keyframe);
+       return !old_config.equivalent(&config);
+}
+
+void TimeAvgMain::save_data(KeyFrame *keyframe)
+{
+       FileXML output;
+
+// cause data to be stored directly in text
+       output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
+       output.tag.set_title("TIME_AVERAGE");
+       output.tag.set_property("FRAMES", config.frames);
+       output.tag.set_property("MODE", config.mode);
+       output.tag.set_property("PARANOID", config.paranoid);
+       output.tag.set_property("NOSUBTRACT", config.nosubtract);
+       output.tag.set_property("THRESHOLD", config.threshold);
+       output.tag.set_property("BORDER", config.border);
+       output.append_tag();
+       output.tag.set_title("/TIME_AVERAGE");
+       output.append_tag();
+       output.append_newline();
+       output.terminate_string();
+}
+
+void TimeAvgMain::read_data(KeyFrame *keyframe)
+{
+       FileXML input;
+
+       input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
+
+       while(!input.read_tag())
+       {
+               if(input.tag.title_is("TIME_AVERAGE"))
+               {
+                       config.frames = input.tag.get_property("FRAMES", config.frames);
+                       config.mode = input.tag.get_property("MODE", config.mode);
+                       config.paranoid = input.tag.get_property("PARANOID", config.paranoid);
+                       config.nosubtract = input.tag.get_property("NOSUBTRACT", config.nosubtract);
+                       config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
+                       config.border = input.tag.get_property("BORDER", config.border);
+               }
+       }
+}
+
+
+void TimeAvgMain::update_gui()
+{
+       if(thread) 
+       {
+               if(load_configuration())
+               {
+                       thread->window->lock_window("TimeAvgMain::update_gui");
+                       ((TimeAvgWindow*)thread->window)->total_frames->update(config.frames);
+                       ((TimeAvgWindow*)thread->window)->threshold->update(config.threshold);
+                       ((TimeAvgWindow*)thread->window)->update_toggles();
+                       ((TimeAvgWindow*)thread->window)->paranoid->update(config.paranoid);
+                       ((TimeAvgWindow*)thread->window)->no_subtract->update(config.nosubtract);
+                       ((TimeAvgWindow*)thread->window)->threshold->update(config.threshold);
+                       ((TimeAvgWindow*)thread->window)->border->update(config.border);
+                       thread->window->unlock_window();
+               }
+       }
+}
+
+
+
+
+
+
+
+
+
+