Fixed initialisation of tf in file_open(). Without setting the memory to 0,
[cinelerra_cv/mob.git] / plugins / framefield / framefield.C
blobdefd77ec63053c662a96557022e9ab4639b207c2
1 #include "bcdisplayinfo.h"
2 #include "bcsignals.h"
3 #include "bchash.h"
4 #include "filexml.h"
5 #include "guicast.h"
6 #include "keyframe.h"
7 #include "language.h"
8 #include "picon_png.h"
9 #include "pluginvclient.h"
10 #include "transportque.inc"
11 #include "vframe.h"
13 #include <string.h>
14 #include <stdint.h>
17 #define TOP_FIELD_FIRST 0
18 #define BOTTOM_FIELD_FIRST 1
20 class FrameField;
21 class FrameFieldWindow;
25 // 601 to RGB expansion is provided as a convenience for OpenGL users since
26 // frame bobbing is normally done during playback together with 601 to RGB expansion.
27 // It's not optimized for software.
30 class FrameFieldConfig
32 public:
33         FrameFieldConfig();
34         int equivalent(FrameFieldConfig &src);
35         int field_dominance;
41 class FrameFieldTop : public BC_Radial
43 public:
44         FrameFieldTop(FrameField *plugin, FrameFieldWindow *gui, int x, int y);
45         int handle_event();
46         FrameField *plugin;
47         FrameFieldWindow *gui;
51 class FrameFieldBottom : public BC_Radial
53 public:
54         FrameFieldBottom(FrameField *plugin, FrameFieldWindow *gui, int x, int y);
55         int handle_event();
56         FrameField *plugin;
57         FrameFieldWindow *gui;
61 class FrameFieldDouble : public BC_CheckBox
63 public:
64         FrameFieldDouble(FrameField *plugin, FrameFieldWindow *gui, int x, int y);
65         int handle_event();
66         FrameField *plugin;
67         FrameFieldWindow *gui;
70 class FrameFieldShift : public BC_CheckBox
72 public:
73         FrameFieldShift(FrameField *plugin, FrameFieldWindow *gui, int x, int y);
74         int handle_event();
75         FrameField *plugin;
76         FrameFieldWindow *gui;
79 class FrameFieldAvg : public BC_CheckBox
81 public:
82         FrameFieldAvg(FrameField *plugin, FrameFieldWindow *gui, int x, int y);
83         int handle_event();
84         FrameField *plugin;
85         FrameFieldWindow *gui;
88 class FrameFieldWindow : public BC_Window
90 public:
91         FrameFieldWindow(FrameField *plugin, int x, int y);
92         void create_objects();
93         int close_event();
94         FrameField *plugin;
95         FrameFieldTop *top;
96         FrameFieldBottom *bottom;
100 PLUGIN_THREAD_HEADER(FrameField, FrameFieldThread, FrameFieldWindow)
104 class FrameField : public PluginVClient
106 public:
107         FrameField(PluginServer *server);
108         ~FrameField();
110         PLUGIN_CLASS_MEMBERS(FrameFieldConfig, FrameFieldThread);
112         int process_buffer(VFrame *frame,
113                 int64_t start_position,
114                 double frame_rate);
115         int is_realtime();
116         int load_defaults();
117         int save_defaults();
118         void save_data(KeyFrame *keyframe);
119         void read_data(KeyFrame *keyframe);
120         void update_gui();
122 // Constructs odd or even rows from the average of the surrounding rows.
123         void average_rows(int offset, VFrame *frame);
125         int handle_opengl();
127 // Last frame requested
128         int64_t last_frame;
129 // Field needed
130         int64_t field_number;
131 // Frame needed
132         int64_t current_frame_number;
133 // Frame stored
134         int64_t src_frame_number;
135         VFrame *src_frame;
137 // Temporary storage of input frame for OpenGL
138         BC_Texture *src_texture;
139 // Signal OpenGL handler a new frame was read.
140         int new_frame;
141 // Reading frames at a different rate requires us to store the aggregation
142 // state when the frame isn't read.
143         int aggregate_rgb601;
144         int rgb601_direction;
155 REGISTER_PLUGIN(FrameField)
159 FrameFieldConfig::FrameFieldConfig()
161         field_dominance = TOP_FIELD_FIRST;
164 int FrameFieldConfig::equivalent(FrameFieldConfig &src)
166         return src.field_dominance == field_dominance;
176 FrameFieldWindow::FrameFieldWindow(FrameField *plugin, int x, int y)
177  : BC_Window(plugin->gui_string, 
178         x, 
179         y, 
180         210, 
181         160, 
182         200, 
183         160, 
184         0, 
185         0,
186         1)
188         this->plugin = plugin;
191 void FrameFieldWindow::create_objects()
193         int x = 10, y = 10;
194         add_subwindow(top = new FrameFieldTop(plugin, this, x, y));
195         y += top->get_h() + 5;
196         add_subwindow(bottom = new FrameFieldBottom(plugin, this, x, y));
197         y += bottom->get_h() + 5;
198         show_window();
199         flush();
202 WINDOW_CLOSE_EVENT(FrameFieldWindow)
215 FrameFieldTop::FrameFieldTop(FrameField *plugin, 
216         FrameFieldWindow *gui, 
217         int x, 
218         int y)
219  : BC_Radial(x, 
220         y, 
221         plugin->config.field_dominance == TOP_FIELD_FIRST,
222         _("Top field first"))
224         this->plugin = plugin;
225         this->gui = gui;
228 int FrameFieldTop::handle_event()
230         plugin->config.field_dominance = TOP_FIELD_FIRST;
231         gui->bottom->update(0);
232         plugin->send_configure_change();
233         return 1;
240 FrameFieldBottom::FrameFieldBottom(FrameField *plugin, 
241         FrameFieldWindow *gui, 
242         int x, 
243         int y)
244  : BC_Radial(x, 
245         y, 
246         plugin->config.field_dominance == BOTTOM_FIELD_FIRST,
247         _("Bottom field first"))
249         this->plugin = plugin;
250         this->gui = gui;
253 int FrameFieldBottom::handle_event()
255         plugin->config.field_dominance = BOTTOM_FIELD_FIRST;
256         gui->top->update(0);
257         plugin->send_configure_change();
258         return 1;
267 PLUGIN_THREAD_OBJECT(FrameField, FrameFieldThread, FrameFieldWindow)
284 FrameField::FrameField(PluginServer *server)
285  : PluginVClient(server)
287         PLUGIN_CONSTRUCTOR_MACRO
288         field_number = 0;
289         src_frame = 0;
290         src_frame_number = -1;
291         last_frame = -1;
292         src_texture = 0;
293         aggregate_rgb601 = 0;
294         rgb601_direction = 0;
298 FrameField::~FrameField()
300         PLUGIN_DESTRUCTOR_MACRO
302         if(src_frame) delete src_frame;
303         if(src_texture) delete src_texture;
307 // 0 - current frame field 0, prev frame field 1
308 // 1 - current frame field 0, current frame field 1, copy current to prev
309 // 2 - current frame field 0, prev frame field 1
311 int FrameField::process_buffer(VFrame *frame,
312         int64_t start_position,
313         double frame_rate)
315         load_configuration();
317         new_frame = 0;
319 // Calculate current field based on absolute position so the algorithm isn't
320 // relative to where playback started.
321         field_number = get_source_position() % 2;
323         if (get_direction() == PLAY_REVERSE)
324         {
325                 start_position++;
326                 field_number = (field_number + 1) % 2;
327         }
330         current_frame_number = start_position / 2;
332         VFrame *ptr = frame;
333         if(get_use_opengl())
334         {
335 // Read new frames directly into output frame for hardware
336         }
337         else
338         {
339 // Read into temporary for software
340                 if(src_frame &&
341                         src_frame->get_color_model() != frame->get_color_model())
342                 {
343                         delete src_frame;
344                         src_frame = 0;
345                 }
347                 if(!src_frame)
348                 {
349                         src_frame = new VFrame(0, 
350                                 frame->get_w(), 
351                                 frame->get_h(), 
352                                 frame->get_color_model());
353                 }
354                 ptr = src_frame;
355         }
358 // Import source frame at half frame rate
359         if(current_frame_number != src_frame_number ||
360 // If same frame was requested, assume it was a configuration change and reprocess.
361                 start_position == last_frame)
362         {
363                 read_frame(ptr, 
364                         0, 
365                         current_frame_number, 
366                         frame_rate / 2,
367                         get_use_opengl());
368                 src_frame_number = current_frame_number;
369                 new_frame = 1;
370         }
373         if(get_use_opengl())
374         {
375                 run_opengl();
376                 return 0;
377         }
379         int row_size = VFrame::calculate_bytes_per_pixel(frame->get_color_model()) * 
380                 frame->get_w();
381         int start_row;
383         unsigned char **src_rows = src_frame->get_rows();
384         unsigned char **output_rows = frame->get_rows();
387 // Even field
388         if(field_number == 0)
389         {
390                 if(config.field_dominance == TOP_FIELD_FIRST) 
391                 {
392                         for(int i = 0; i < frame->get_h() - 1; i += 2)
393                         {
394 // Copy even lines of src to both lines of output
395                                 memcpy(output_rows[i], 
396                                         src_rows[i], 
397                                         row_size);
398                         }
400 // Average empty rows
401                         /* if(config.avg) */ average_rows(0, frame);
402                 }
403                 else
404                 {
405                         for(int i = 0; i < frame->get_h() - 1; i += 2)
406                         {
407 // Copy odd lines of current to both lines of output with shift up.
408                                 memcpy(output_rows[i + 1], 
409                                         src_rows[i + 1], 
410                                         row_size);
411                         }
413 // Average empty rows
414                         /* if(config.avg) */ average_rows(1, frame);
415                 }
416         }
417         else
418 // Odd field
419         {
420                 if(config.field_dominance == TOP_FIELD_FIRST)
421                 {
422                         for(int i = 0; i < frame->get_h() - 1; i += 2)
423                         {
424 // Copy odd lines of src to both lines of output
425                                 memcpy(output_rows[i + 1], 
426                                         src_rows[i + 1], 
427                                         row_size);
428                         }
430 // Average empty rows
431                         /* if(config.avg) */ average_rows(1, frame);
432                 }
433                 else
434                 {
435                         for(int i = 0; i < frame->get_h() - 1; i += 2)
436                         {
437 // Copy even lines of src to both lines of output.
438                                 memcpy(output_rows[i], 
439                                         src_rows[i], 
440                                         row_size);
441                         }
443 // Average empty rows
444                         /* if(config.avg) */ average_rows(0, frame);
445                 }
446         }
448         last_frame = start_position;
449         return 0;
453 // Averaging 2 pixels
454 #define AVERAGE(type, temp_type, components, offset) \
455 { \
456         type **rows = (type**)frame->get_rows(); \
457         int w = frame->get_w(); \
458         int h = frame->get_h(); \
459         int row_size = components * w; \
460         for(int i = offset; i < h - 3; i += 2) \
461         { \
462                 type *row1 = rows[i]; \
463                 type *row2 = rows[i + 1]; \
464                 type *row3 = rows[i + 2]; \
465                 for(int j = 0; j < row_size; j++) \
466                 { \
467                         temp_type sum = (temp_type)*row1++ + (temp_type)*row3++; \
468                         *row2++ = (sum / 2); \
469                 } \
470         } \
473 // Averaging 6 pixels
474 #define AVERAGE_BAK(type, components, offset) \
475 { \
476         type **rows = (type**)frame->get_rows(); \
477         int w = frame->get_w(); \
478         int h = frame->get_h(); \
479         int row_size = w; \
480         for(int i = offset; i < h - 3; i += 2) \
481         { \
482                 type *row1 = rows[i]; \
483                 type *row2 = rows[i + 1]; \
484                 type *row3 = rows[i + 2]; \
485                 int64_t sum; \
486                 int64_t pixel1[4], pixel2[4], pixel3[4]; \
487                 int64_t pixel4[4], pixel5[4], pixel6[4]; \
489 /* First pixel */ \
490                 for(int j = 0; j < components; j++) \
491                 { \
492                         pixel1[j] = *row1++; \
493                         pixel4[j] = *row3++; \
494                         *row2++ = (pixel1[j] + pixel4[j]) >> 1; \
495                 } \
497                 for(int j = 2; j < row_size; j++) \
498                 { \
499                         for(int k = 0; k < components; k++) \
500                         { \
501                                 pixel2[k] = *row1++; \
502                                 pixel5[k] = *row3++; \
503                         } \
505                         for(int k = 0; k < components; k++) \
506                         { \
507                                 pixel3[k] = *row1; \
508                                 pixel6[k] = *row3; \
509                                 *row2++ = (pixel1[k] + \
510                                         pixel2[k] + \
511                                         pixel3[k] + \
512                                         pixel4[k] + \
513                                         pixel5[k] + \
514                                         pixel6[k]) / 6; \
515                                 pixel1[k] = pixel2[k]; \
516                                 pixel4[k] = pixel5[k]; \
517                         } \
518                 } \
520 /* Last pixel */ \
521                 for(int j = 0; j < components; j++) \
522                 { \
523                         *row2++ = (pixel3[j] + pixel6[j]) >> 1; \
524                 } \
525         } \
528 void FrameField::average_rows(int offset, VFrame *frame)
530 //printf("FrameField::average_rows 1 %d\n", offset);
531         switch(frame->get_color_model())
532         {
533                 case BC_RGB888:
534                 case BC_YUV888:
535                         AVERAGE(unsigned char, int64_t, 3, offset);
536                         break;
537                 case BC_RGB_FLOAT:
538                         AVERAGE(float, float, 3, offset);
539                         break;
540                 case BC_RGBA8888:
541                 case BC_YUVA8888:
542                         AVERAGE(unsigned char, int64_t, 4, offset);
543                         break;
544                 case BC_RGBA_FLOAT:
545                         AVERAGE(float, float, 4, offset);
546                         break;
547                 case BC_RGB161616:
548                 case BC_YUV161616:
549                         AVERAGE(uint16_t, int64_t, 3, offset);
550                         break;
551                 case BC_RGBA16161616:
552                 case BC_YUVA16161616:
553                         AVERAGE(uint16_t, int64_t, 4, offset);
554                         break;
555         }
560 char* FrameField::plugin_title() { return N_("Frames to fields"); }
561 int FrameField::is_realtime() { return 1; }
563 NEW_PICON_MACRO(FrameField) 
565 SHOW_GUI_MACRO(FrameField, FrameFieldThread)
567 RAISE_WINDOW_MACRO(FrameField)
569 SET_STRING_MACRO(FrameField);
571 int FrameField::load_configuration()
573         KeyFrame *prev_keyframe;
574         FrameFieldConfig old_config = config;
576         prev_keyframe = get_prev_keyframe(get_source_position());
577         read_data(prev_keyframe);
579         return !old_config.equivalent(config);
582 int FrameField::load_defaults()
584         char directory[BCTEXTLEN];
585 // set the default directory
586         sprintf(directory, "%sframefield.rc", BCASTDIR);
588 // load the defaults
589         defaults = new BC_Hash(directory);
590         defaults->load();
592         config.field_dominance = defaults->get("DOMINANCE", config.field_dominance);
593         return 0;
596 int FrameField::save_defaults()
598         defaults->update("DOMINANCE", config.field_dominance);
599         defaults->save();
600         return 0;
603 void FrameField::save_data(KeyFrame *keyframe)
605         FileXML output;
607 // cause data to be stored directly in text
608         output.set_shared_string(keyframe->data, MESSAGESIZE);
609         output.tag.set_title("FRAME_FIELD");
610         output.tag.set_property("DOMINANCE", config.field_dominance);
611         output.append_tag();
612         output.terminate_string();
615 void FrameField::read_data(KeyFrame *keyframe)
617         FileXML input;
619         input.set_shared_string(keyframe->data, strlen(keyframe->data));
621         int result = 0;
623         while(!input.read_tag())
624         {
625                 if(input.tag.title_is("FRAME_FIELD"))
626                 {
627                         config.field_dominance = input.tag.get_property("DOMINANCE", config.field_dominance);
628                 }
629         }
632 void FrameField::update_gui()
634         if(thread)
635         {
636                 if(load_configuration())
637                 {
638                         thread->window->lock_window();
639                         thread->window->top->update(config.field_dominance == TOP_FIELD_FIRST);
640                         thread->window->bottom->update(config.field_dominance == BOTTOM_FIELD_FIRST);
641                         thread->window->unlock_window();
642                 }
643         }
646 int FrameField::handle_opengl()
648 #ifdef HAVE_GL
649         static char *field_frag = 
650                 "uniform sampler2D tex;\n"
651                 "uniform float double_line_h;\n"
652                 "uniform float y_offset;\n"
653                 "void main()\n"
654                 "{\n"
655                 "       vec2 coord = gl_TexCoord[0].st;\n"
656 /* Number of double lines + fraction of current double line */
657                 "       float half_y = (coord.y - y_offset) / double_line_h;\n"
658 /* Lines comprising current double line */
659                 "       float line1 = floor(half_y) * double_line_h + y_offset;\n"
660                 "       float line2 = line1 + double_line_h;\n"
661 /* Distance from line1 to line2 */
662                 "       float frac = fract(half_y);\n"
663                 "       gl_FragColor =  mix(\n"
664                 "               texture2D(tex, vec2(coord.x, line1)), \n"
665                 "               texture2D(tex, vec2(coord.x, line2)), \n"
666                 "               frac);\n"
667                 "}\n";
669         static char *_601_to_rgb_frag = 
670                 "void main()\n"
671                 "{\n"
672                 "       gl_FragColor.rgb = gl_FragColor.rgb * vec3(1.1644, 1.1644, 1.1644) - vec3(0.0627, 0.0627, 0.0627);\n"
673                 "}\n";
675         static char *_601_to_yuv_frag = 
676                 "void main()\n"
677                 "{\n"
678                 "       gl_FragColor.r = gl_FragColor.r * 1.1644 - 0.0627;\n"
679                 "}\n";
681         static char *rgb_to_601_frag = 
682                 "void main()\n"
683                 "{\n"
684                 "       gl_FragColor.rgb = gl_FragColor.rgb * vec3(0.8588, 0.8588, 0.8588) + vec3(0.0627, 0.0627, 0.0627);\n"
685                 "}\n";
687         static char *yuv_to_601_frag = 
688                 "void main()\n"
689                 "{\n"
690                 "       gl_FragColor.r = gl_FragColor.r * 0.8588 + 0.0627;\n"
691                 "}\n";
694         if(new_frame)
695         {
696                 if(get_output()->get_opengl_state() != VFrame::SCREEN)
697                 {
698 // Copy new frame to temporary texture
699                         get_output()->to_texture();
701 // Draw it only to copy it to the temporary.
702                         get_output()->enable_opengl();
703                         VFrame::init_screen(get_output()->get_w(), get_output()->get_h());
704                         get_output()->bind_texture(0);
705                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
706                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
707                         get_output()->draw_texture();
708                 }
710                 get_output()->enable_opengl();
711                 VFrame::init_screen(get_output()->get_w(), get_output()->get_h());
712                 glActiveTexture(GL_TEXTURE0);
713                 BC_Texture::new_texture(&src_texture,
714                         get_output()->get_w(), 
715                         get_output()->get_h(), 
716                         get_output()->get_color_model());
717                 src_texture->bind(0);
718                 glCopyTexSubImage2D(GL_TEXTURE_2D,
719                         0,
720                         0,
721                         0,
722                         0,
723                         0,
724                         get_output()->get_w(),
725                         get_output()->get_h());
727 // Store aggregation state only when reading a frame
728 //printf("FrameField::handle_opengl %p\n", get_output());
729 //get_output()->dump_stacks();
730                 if(prev_effect_is("RGB - 601") ||
731                         next_effect_is("RGB - 601"))
732                 {
733                         aggregate_rgb601 = 1;
734                         rgb601_direction = get_output()->get_params()->get("RGB601_DIRECTION", 0);
735                 }
736                 else
737                         aggregate_rgb601 = 0;
738         }
739         else
740         {
741                 get_output()->enable_opengl();
742         }
744         unsigned int frag = 0;
745         float y_offset = 0.0;
746         if(field_number == 0)
747         {
748                 if(config.field_dominance == BOTTOM_FIELD_FIRST)
749                         y_offset = 1.0;
750         }
751         else
752         {
753                 if(config.field_dominance == TOP_FIELD_FIRST)
754                         y_offset = 1.0;
755         }
757         VFrame::init_screen(get_output()->get_w(), get_output()->get_h());
758         glActiveTexture(GL_TEXTURE0);
759         BC_Texture::new_texture(&src_texture,
760                 get_output()->get_w(), 
761                 get_output()->get_h(), 
762                 get_output()->get_color_model());
765         char *shaders[3] = { 0, 0, 0 };
766         shaders[0] = field_frag;
769 // Aggregate with other effect
770 //printf("FrameField::handle_opengl %s\n", get_output()->get_next_effect());
771         if(aggregate_rgb601)
772         {
773                 if(rgb601_direction == 1)
774                 {
775                         if(cmodel_is_yuv(get_output()->get_color_model()))
776                                 shaders[1] = yuv_to_601_frag;
777                         else
778                                 shaders[1] = rgb_to_601_frag;
779                 }
780                 else
781                 if(rgb601_direction == 2)
782                 {
783                         if(cmodel_is_yuv(get_output()->get_color_model()))
784                                 shaders[1] = _601_to_yuv_frag;
785                         else
786                                 shaders[1] = _601_to_rgb_frag;
787                 }
788         }
792         frag = VFrame::make_shader(0, shaders[0], shaders[1], shaders[2], 0);
795         if(frag)
796         {
797                 glUseProgram(frag);
798                 glUniform1i(glGetUniformLocation(frag, "tex"), 0);
799                 glUniform1f(glGetUniformLocation(frag, "double_line_h"), 
800                         2.0 / src_texture->get_texture_h());
801                 glUniform1f(glGetUniformLocation(frag, "y_offset"), 
802                         y_offset / src_texture->get_texture_h());
803         }
807         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
808         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
809         get_output()->draw_texture();
811         glUseProgram(0);
812         get_output()->set_opengl_state(VFrame::SCREEN);
814 // Reset for other operations
815         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
816         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
818 #endif