--- /dev/null
+
+/*
+ * 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();
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
+