4 * Copyright (C) 2021 Adam Williams <broadcast at earthling dot net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 // output 1 frame for each block of frames
24 // take the most similar frame in the current block of frames to the previous frame
25 // this is for 3D printers where you want the bed in the same position
28 #include "bcdisplayinfo.h"
35 #include "transportque.inc"
36 #include "pluginvclient.h"
44 class TimelapseHelper;
45 class TimelapseHelperWindow;
48 class TimelapseHelperConfig
51 TimelapseHelperConfig();
52 void copy_from(TimelapseHelperConfig &config);
53 int equivalent(TimelapseHelperConfig &config);
54 void interpolate(TimelapseHelperConfig &prev,
55 TimelapseHelperConfig &next,
58 int64_t current_frame);
66 class TimelapseHelperSize : public BC_TumbleTextBox
69 TimelapseHelperSize(TimelapseHelper *plugin,
70 TimelapseHelperWindow *gui,
75 TimelapseHelper *plugin;
76 TimelapseHelperWindow *gui;
80 class TimelapseHelperWindow : public PluginClientWindow
83 TimelapseHelperWindow(TimelapseHelper *plugin);
84 ~TimelapseHelperWindow();
86 void create_objects();
88 TimelapseHelper *plugin;
89 TimelapseHelperSize *size;
96 class TimelapseHelper : public PluginVClient
99 TimelapseHelper(PluginServer *server);
102 PLUGIN_CLASS_MEMBERS(TimelapseHelperConfig)
104 int process_buffer(VFrame *frame,
105 int64_t start_position,
108 void save_data(KeyFrame *keyframe);
109 void read_data(KeyFrame *keyframe);
112 int64_t calculate_difference(VFrame *frame1, VFrame *frame2);
115 // frame used from the last block
130 TimelapseHelperConfig::TimelapseHelperConfig()
135 void TimelapseHelperConfig::copy_from(TimelapseHelperConfig &config)
137 this->block_size = config.block_size;
140 int TimelapseHelperConfig::equivalent(TimelapseHelperConfig &config)
142 return this->block_size == config.block_size;
145 void TimelapseHelperConfig::interpolate(TimelapseHelperConfig &prev,
146 TimelapseHelperConfig &next,
149 int64_t current_frame)
151 this->block_size = next.block_size;
161 TimelapseHelperWindow::TimelapseHelperWindow(TimelapseHelper *plugin)
162 : PluginClientWindow(plugin,
169 this->plugin = plugin;
172 TimelapseHelperWindow::~TimelapseHelperWindow()
176 void TimelapseHelperWindow::create_objects()
178 int margin = client->get_theme()->widget_border;
179 int x = margin, y = margin;
182 add_subwindow(title = new BC_Title(x, y, _("Number of frames per block:")));
183 y += title->get_h() + margin;
184 size = new TimelapseHelperSize(plugin,
188 get_w() - x - margin - xS(20) );
189 size->create_objects();
205 TimelapseHelperSize::TimelapseHelperSize(TimelapseHelper *plugin,
206 TimelapseHelperWindow *gui,
210 : BC_TumbleTextBox(gui,
211 plugin->config.block_size,
218 this->plugin = plugin;
222 int TimelapseHelperSize::handle_event()
224 plugin->config.block_size = atoi(get_text());
225 plugin->send_configure_change();
238 REGISTER_PLUGIN(TimelapseHelper)
245 TimelapseHelper::TimelapseHelper(PluginServer *server)
246 : PluginVClient(server)
252 TimelapseHelper::~TimelapseHelper()
260 #define DIFFERENCE_MACRO(type, temp_type, components) \
262 temp_type result2 = 0; \
263 for(int i = 0; i < h; i++) \
265 type *row1 = (type*)frame1->get_rows()[i]; \
266 type *row2 = (type*)frame2->get_rows()[i]; \
267 for(int j = 0; j < w * components; j++) \
269 temp_type temp = *row1 - *row2; \
270 result2 += (temp > 0 ? temp : -temp); \
275 result = (int64_t)result2; \
278 int64_t TimelapseHelper::calculate_difference(VFrame *frame1, VFrame *frame2)
280 int w = frame1->get_w();
281 int h = frame1->get_h();
283 switch(frame1->get_color_model())
287 DIFFERENCE_MACRO(unsigned char, int64_t, 3);
290 DIFFERENCE_MACRO(float, double, 3);
294 DIFFERENCE_MACRO(unsigned char, int64_t, 4);
297 DIFFERENCE_MACRO(float, double, 4);
301 DIFFERENCE_MACRO(uint16_t, int64_t, 3);
303 case BC_RGBA16161616:
304 case BC_YUVA16161616:
305 DIFFERENCE_MACRO(uint16_t, int64_t, 4);
313 int TimelapseHelper::process_buffer(VFrame *frame,
314 int64_t start_position,
317 load_configuration();
319 // calculate frame positions
320 int64_t ref_position = get_source_start();
323 block_start = ref_position + (start_position - ref_position) * config.block_size;
324 block_end = block_start + config.block_size;
326 // printf("TimelapseHelper::process_buffer %d current_position=%ld ref_position=%ld block_start=%ld block_end=%ld\n",
333 // load initial reference frame from plugin start
340 frame->get_color_model(),
347 frame->copy_from(ref);
350 // compare next block of frames to reference frame
352 VFrame *temp = new_temp(frame->get_w(),
354 frame->get_color_model());
355 int64_t best = 0x7fffffffffffffffLL;
356 for(int64_t i = block_start; i < block_end; i++)
359 if(get_direction() == PLAY_FORWARD)
376 int64_t diff = calculate_difference(temp,
381 frame->copy_from(temp);
385 // replace reference frame with best match
386 ref->copy_from(frame);
394 const char* TimelapseHelper::plugin_title() { return N_("Timelapse Helper"); }
395 int TimelapseHelper::is_realtime() { return 1; }
397 NEW_WINDOW_MACRO(TimelapseHelper, TimelapseHelperWindow)
399 LOAD_CONFIGURATION_MACRO(TimelapseHelper, TimelapseHelperConfig)
402 void TimelapseHelper::save_data(KeyFrame *keyframe)
406 // cause data to be stored directly in text
407 output.set_shared_output(keyframe->xbuf);
408 output.tag.set_title("TIMELAPSEHELPER");
409 output.tag.set_property("BLOCK_SIZE", config.block_size);
411 output.tag.set_title("/TIMELAPSEHELPER");
413 output.append_newline();
414 output.terminate_string();
417 void TimelapseHelper::read_data(KeyFrame *keyframe)
421 input.set_shared_input(keyframe->xbuf);
423 while(!input.read_tag())
425 if(input.tag.title_is("TIMELAPSEHELPER"))
427 config.block_size = input.tag.get_property("BLOCK_SIZE", config.block_size);
432 void TimelapseHelper::update_gui()
436 if(load_configuration())
438 ((TimelapseHelperWindow*)thread->window)->lock_window("TimelapseHelper::update_gui");
439 ((TimelapseHelperWindow*)thread->window)->size->update((int64_t)config.block_size);
440 ((TimelapseHelperWindow*)thread->window)->unlock_window();