f4ff5487926314b6e00fe4249861228ed76a34a4
[goodguy/history.git] / cinelerra-5.1 / plugins / framefield / framefield.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
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.
10  *
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.
15  *
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
19  *
20  */
21
22 #include "bcdisplayinfo.h"
23 #include "bcsignals.h"
24 #include "bchash.h"
25 #include "filexml.h"
26 #include "guicast.h"
27 #include "keyframe.h"
28 #include "language.h"
29 #include "pluginvclient.h"
30 #include "transportque.inc"
31 #include "vframe.h"
32
33 #include <string.h>
34 #include <stdint.h>
35
36
37 #define TOP_FIELD_FIRST 0
38 #define BOTTOM_FIELD_FIRST 1
39
40 class FrameField;
41 class FrameFieldWindow;
42
43
44
45
46 class FrameFieldConfig
47 {
48 public:
49         FrameFieldConfig();
50         int equivalent(FrameFieldConfig &src);
51         int field_dominance;
52 };
53
54
55
56
57 class FrameFieldTop : public BC_Radial
58 {
59 public:
60         FrameFieldTop(FrameField *plugin, FrameFieldWindow *gui, int x, int y);
61         int handle_event();
62         FrameField *plugin;
63         FrameFieldWindow *gui;
64 };
65
66
67 class FrameFieldBottom : public BC_Radial
68 {
69 public:
70         FrameFieldBottom(FrameField *plugin, FrameFieldWindow *gui, int x, int y);
71         int handle_event();
72         FrameField *plugin;
73         FrameFieldWindow *gui;
74 };
75
76
77 class FrameFieldDouble : public BC_CheckBox
78 {
79 public:
80         FrameFieldDouble(FrameField *plugin, FrameFieldWindow *gui, int x, int y);
81         int handle_event();
82         FrameField *plugin;
83         FrameFieldWindow *gui;
84 };
85
86 class FrameFieldShift : public BC_CheckBox
87 {
88 public:
89         FrameFieldShift(FrameField *plugin, FrameFieldWindow *gui, int x, int y);
90         int handle_event();
91         FrameField *plugin;
92         FrameFieldWindow *gui;
93 };
94
95 class FrameFieldAvg : public BC_CheckBox
96 {
97 public:
98         FrameFieldAvg(FrameField *plugin, FrameFieldWindow *gui, int x, int y);
99         int handle_event();
100         FrameField *plugin;
101         FrameFieldWindow *gui;
102 };
103
104 class FrameFieldWindow : public PluginClientWindow
105 {
106 public:
107         FrameFieldWindow(FrameField *plugin);
108         void create_objects();
109         FrameField *plugin;
110         FrameFieldTop *top;
111         FrameFieldBottom *bottom;
112 };
113
114
115
116
117
118 class FrameField : public PluginVClient
119 {
120 public:
121         FrameField(PluginServer *server);
122         ~FrameField();
123
124         PLUGIN_CLASS_MEMBERS(FrameFieldConfig);
125
126         int process_buffer(VFrame *frame,
127                 int64_t start_position,
128                 double frame_rate);
129         int is_realtime();
130         void save_data(KeyFrame *keyframe);
131         void read_data(KeyFrame *keyframe);
132         void update_gui();
133
134 // Constructs odd or even rows from the average of the surrounding rows.
135         void average_rows(int offset, VFrame *frame);
136
137         int handle_opengl();
138
139 // Last frame requested
140         int64_t last_frame;
141 // Field needed
142         int64_t field_number;
143 // Frame needed
144         int64_t current_frame_number;
145 // Frame stored
146         int64_t src_frame_number;
147         VFrame *src_frame;
148
149 // Temporary storage of input frame for OpenGL
150         BC_Texture *src_texture;
151 // Signal OpenGL handler a new frame was read.
152         int new_frame;
153 // Reading frames at a different rate requires us to store the aggregation
154 // state when the frame isn't read.
155         int aggregate_rgb601;
156         int rgb601_direction;
157 };
158
159
160
161
162
163
164
165
166
167 REGISTER_PLUGIN(FrameField)
168
169
170
171 FrameFieldConfig::FrameFieldConfig()
172 {
173         field_dominance = TOP_FIELD_FIRST;
174 }
175
176 int FrameFieldConfig::equivalent(FrameFieldConfig &src)
177 {
178         return src.field_dominance == field_dominance;
179 }
180
181
182
183
184
185
186
187
188 FrameFieldWindow::FrameFieldWindow(FrameField *plugin)
189  : PluginClientWindow(plugin,
190         210,
191         160,
192         200,
193         160,
194         0)
195 {
196         this->plugin = plugin;
197 }
198
199 void FrameFieldWindow::create_objects()
200 {
201         int x = 10, y = 10;
202         add_subwindow(top = new FrameFieldTop(plugin, this, x, y));
203         y += top->get_h() + 5;
204         add_subwindow(bottom = new FrameFieldBottom(plugin, this, x, y));
205         y += bottom->get_h() + 5;
206         show_window();
207         flush();
208 }
209
210
211
212
213
214
215
216
217
218
219
220
221 FrameFieldTop::FrameFieldTop(FrameField *plugin,
222         FrameFieldWindow *gui,
223         int x,
224         int y)
225  : BC_Radial(x,
226         y,
227         plugin->config.field_dominance == TOP_FIELD_FIRST,
228         _("Top field first"))
229 {
230         this->plugin = plugin;
231         this->gui = gui;
232 }
233
234 int FrameFieldTop::handle_event()
235 {
236         plugin->config.field_dominance = TOP_FIELD_FIRST;
237         gui->bottom->update(0);
238         plugin->send_configure_change();
239         return 1;
240 }
241
242
243
244
245
246 FrameFieldBottom::FrameFieldBottom(FrameField *plugin,
247         FrameFieldWindow *gui,
248         int x,
249         int y)
250  : BC_Radial(x,
251         y,
252         plugin->config.field_dominance == BOTTOM_FIELD_FIRST,
253         _("Bottom field first"))
254 {
255         this->plugin = plugin;
256         this->gui = gui;
257 }
258
259 int FrameFieldBottom::handle_event()
260 {
261         plugin->config.field_dominance = BOTTOM_FIELD_FIRST;
262         gui->top->update(0);
263         plugin->send_configure_change();
264         return 1;
265 }
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289 FrameField::FrameField(PluginServer *server)
290  : PluginVClient(server)
291 {
292
293         field_number = 0;
294         src_frame = 0;
295         src_frame_number = -1;
296         last_frame = -1;
297         src_texture = 0;
298         aggregate_rgb601 = 0;
299         rgb601_direction = 0;
300 }
301
302
303 FrameField::~FrameField()
304 {
305
306
307         if(src_frame) delete src_frame;
308         if(src_texture) delete src_texture;
309 }
310
311
312 // 0 - current frame field 0, prev frame field 1
313 // 1 - current frame field 0, current frame field 1, copy current to prev
314 // 2 - current frame field 0, prev frame field 1
315
316 int FrameField::process_buffer(VFrame *frame,
317         int64_t start_position,
318         double frame_rate)
319 {
320         load_configuration();
321
322         new_frame = 0;
323
324 // Calculate current field based on absolute position so the algorithm isn't
325 // relative to where playback started.
326         field_number = get_source_position() % 2;
327
328         if (get_direction() == PLAY_REVERSE)
329         {
330                 start_position++;
331                 field_number = (field_number + 1) % 2;
332         }
333
334
335         current_frame_number = start_position / 2;
336
337         VFrame *ptr = frame;
338         if(get_use_opengl())
339         {
340 // Read new frames directly into output frame for hardware
341         }
342         else
343         {
344 // Read into temporary for software
345                 if(src_frame &&
346                         src_frame->get_color_model() != frame->get_color_model())
347                 {
348                         delete src_frame;
349                         src_frame = 0;
350                 }
351
352                 if(!src_frame)
353                 {
354                         src_frame = new VFrame(frame->get_w(), frame->get_h(),
355                                 frame->get_color_model(), 0);
356                 }
357                 ptr = src_frame;
358         }
359
360
361 // Import source frame at half frame rate
362         if(current_frame_number != src_frame_number ||
363 // If same frame was requested, assume it was a configuration change and reprocess.
364                 start_position == last_frame)
365         {
366                 read_frame(ptr,
367                         0,
368                         current_frame_number,
369                         frame_rate / 2,
370                         get_use_opengl());
371                 src_frame_number = current_frame_number;
372                 new_frame = 1;
373         }
374
375
376         if(get_use_opengl())
377         {
378                 run_opengl();
379                 return 0;
380         }
381
382         int row_size = VFrame::calculate_bytes_per_pixel(frame->get_color_model()) *
383                 frame->get_w();
384
385         unsigned char **src_rows = src_frame->get_rows();
386         unsigned char **output_rows = frame->get_rows();
387
388
389 // Even field
390         if(field_number == 0)
391         {
392                 if(config.field_dominance == TOP_FIELD_FIRST)
393                 {
394                         for(int i = 0; i < frame->get_h() - 1; i += 2)
395                         {
396 // Copy even lines of src to both lines of output
397                                 memcpy(output_rows[i],
398                                         src_rows[i],
399                                         row_size);
400                         }
401
402 // Average empty rows
403                         /* if(config.avg) */ average_rows(0, frame);
404                 }
405                 else
406                 {
407                         for(int i = 0; i < frame->get_h() - 1; i += 2)
408                         {
409 // Copy odd lines of current to both lines of output with shift up.
410                                 memcpy(output_rows[i + 1],
411                                         src_rows[i + 1],
412                                         row_size);
413                         }
414
415 // Average empty rows
416                         /* if(config.avg) */ average_rows(1, frame);
417                 }
418         }
419         else
420 // Odd field
421         {
422                 if(config.field_dominance == TOP_FIELD_FIRST)
423                 {
424                         for(int i = 0; i < frame->get_h() - 1; i += 2)
425                         {
426 // Copy odd lines of src to both lines of output
427                                 memcpy(output_rows[i + 1],
428                                         src_rows[i + 1],
429                                         row_size);
430                         }
431
432 // Average empty rows
433                         /* if(config.avg) */ average_rows(1, frame);
434                 }
435                 else
436                 {
437                         for(int i = 0; i < frame->get_h() - 1; i += 2)
438                         {
439 // Copy even lines of src to both lines of output.
440                                 memcpy(output_rows[i],
441                                         src_rows[i],
442                                         row_size);
443                         }
444
445 // Average empty rows
446                         /* if(config.avg) */ average_rows(0, frame);
447                 }
448         }
449
450         last_frame = start_position;
451         return 0;
452 }
453
454
455 // Averaging 2 pixels
456 #define AVERAGE(type, temp_type, components, offset) \
457 { \
458         type **rows = (type**)frame->get_rows(); \
459         int w = frame->get_w(); \
460         int h = frame->get_h(); \
461         int row_size = components * w; \
462         for(int i = offset; i < h - 3; i += 2) \
463         { \
464                 type *row1 = rows[i]; \
465                 type *row2 = rows[i + 1]; \
466                 type *row3 = rows[i + 2]; \
467                 for(int j = 0; j < row_size; j++) \
468                 { \
469                         temp_type sum = (temp_type)*row1++ + (temp_type)*row3++; \
470                         *row2++ = (sum / 2); \
471                 } \
472         } \
473 }
474
475 // Averaging 6 pixels
476 #define AVERAGE_BAK(type, components, offset) \
477 { \
478         type **rows = (type**)frame->get_rows(); \
479         int w = frame->get_w(); \
480         int h = frame->get_h(); \
481         int row_size = w; \
482         for(int i = offset; i < h - 3; i += 2) \
483         { \
484                 type *row1 = rows[i]; \
485                 type *row2 = rows[i + 1]; \
486                 type *row3 = rows[i + 2]; \
487                 int64_t sum; \
488                 int64_t pixel1[4], pixel2[4], pixel3[4]; \
489                 int64_t pixel4[4], pixel5[4], pixel6[4]; \
490  \
491 /* First pixel */ \
492                 for(int j = 0; j < components; j++) \
493                 { \
494                         pixel1[j] = *row1++; \
495                         pixel4[j] = *row3++; \
496                         *row2++ = (pixel1[j] + pixel4[j]) >> 1; \
497                 } \
498  \
499                 for(int j = 2; j < row_size; j++) \
500                 { \
501                         for(int k = 0; k < components; k++) \
502                         { \
503                                 pixel2[k] = *row1++; \
504                                 pixel5[k] = *row3++; \
505                         } \
506  \
507                         for(int k = 0; k < components; k++) \
508                         { \
509                                 pixel3[k] = *row1; \
510                                 pixel6[k] = *row3; \
511                                 *row2++ = (pixel1[k] + \
512                                         pixel2[k] + \
513                                         pixel3[k] + \
514                                         pixel4[k] + \
515                                         pixel5[k] + \
516                                         pixel6[k]) / 6; \
517                                 pixel1[k] = pixel2[k]; \
518                                 pixel4[k] = pixel5[k]; \
519                         } \
520                 } \
521  \
522 /* Last pixel */ \
523                 for(int j = 0; j < components; j++) \
524                 { \
525                         *row2++ = (pixel3[j] + pixel6[j]) >> 1; \
526                 } \
527         } \
528 }
529
530 void FrameField::average_rows(int offset, VFrame *frame)
531 {
532 //printf("FrameField::average_rows 1 %d\n", offset);
533         switch(frame->get_color_model())
534         {
535                 case BC_RGB888:
536                 case BC_YUV888:
537                         AVERAGE(unsigned char, int64_t, 3, offset);
538                         break;
539                 case BC_RGB_FLOAT:
540                         AVERAGE(float, float, 3, offset);
541                         break;
542                 case BC_RGBA8888:
543                 case BC_YUVA8888:
544                         AVERAGE(unsigned char, int64_t, 4, offset);
545                         break;
546                 case BC_RGBA_FLOAT:
547                         AVERAGE(float, float, 4, offset);
548                         break;
549                 case BC_RGB161616:
550                 case BC_YUV161616:
551                         AVERAGE(uint16_t, int64_t, 3, offset);
552                         break;
553                 case BC_RGBA16161616:
554                 case BC_YUVA16161616:
555                         AVERAGE(uint16_t, int64_t, 4, offset);
556                         break;
557         }
558 }
559
560
561
562 const char* FrameField::plugin_title() { return _("Frames to fields"); }
563 int FrameField::is_realtime() { return 1; }
564
565 NEW_WINDOW_MACRO(FrameField, FrameFieldWindow)
566
567 int FrameField::load_configuration()
568 {
569         KeyFrame *prev_keyframe;
570         FrameFieldConfig old_config = config;
571
572         prev_keyframe = get_prev_keyframe(get_source_position());
573         read_data(prev_keyframe);
574
575         return !old_config.equivalent(config);
576 }
577
578
579 void FrameField::save_data(KeyFrame *keyframe)
580 {
581         FileXML output;
582
583 // cause data to be stored directly in text
584         output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
585         output.tag.set_title("FRAME_FIELD");
586         output.tag.set_property("DOMINANCE", config.field_dominance);
587         output.append_tag();
588         output.tag.set_title("/FRAME_FIELD");
589         output.append_tag();
590         output.append_newline();
591         output.terminate_string();
592 }
593
594 void FrameField::read_data(KeyFrame *keyframe)
595 {
596         FileXML input;
597
598         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
599
600         while(!input.read_tag())
601         {
602                 if(input.tag.title_is("FRAME_FIELD"))
603                 {
604                         config.field_dominance = input.tag.get_property("DOMINANCE", config.field_dominance);
605                 }
606         }
607 }
608
609 void FrameField::update_gui()
610 {
611         if(thread)
612         {
613                 if(load_configuration())
614                 {
615                         thread->window->lock_window();
616                         ((FrameFieldWindow*)thread->window)->top->update(config.field_dominance == TOP_FIELD_FIRST);
617                         ((FrameFieldWindow*)thread->window)->bottom->update(config.field_dominance == BOTTOM_FIELD_FIRST);
618                         thread->window->unlock_window();
619                 }
620         }
621 }
622
623 int FrameField::handle_opengl()
624 {
625 #ifdef HAVE_GL
626         static const char *field_frag =
627                 "uniform sampler2D tex;\n"
628                 "uniform float double_line_h;\n"
629                 "uniform float y_offset;\n"
630                 "void main()\n"
631                 "{\n"
632                 "       vec2 coord = gl_TexCoord[0].st;\n"
633 /* Number of double lines + fraction of current double line */
634                 "       float half_y = (coord.y - y_offset) / double_line_h;\n"
635 /* Lines comprising current double line */
636                 "       float line1 = floor(half_y) * double_line_h + y_offset;\n"
637                 "       float line2 = line1 + double_line_h;\n"
638 /* Distance from line1 to line2 */
639                 "       float frac = fract(half_y);\n"
640                 "       gl_FragColor =  mix(\n"
641                 "               texture2D(tex, vec2(coord.x, line1)), \n"
642                 "               texture2D(tex, vec2(coord.x, line2)), \n"
643                 "               frac);\n"
644                 "}\n";
645
646         static const char *_601_to_rgb_frag =
647                 "void main()\n"
648                 "{\n"
649                 "       gl_FragColor.rgb = gl_FragColor.rgb * vec3(1.1644, 1.1644, 1.1644) - vec3(0.0627, 0.0627, 0.0627);\n"
650                 "}\n";
651
652         static const char *_601_to_yuv_frag =
653                 "void main()\n"
654                 "{\n"
655                 "       gl_FragColor.r = gl_FragColor.r * 1.1644 - 0.0627;\n"
656                 "}\n";
657
658         static const char *rgb_to_601_frag =
659                 "void main()\n"
660                 "{\n"
661                 "       gl_FragColor.rgb = gl_FragColor.rgb * vec3(0.8588, 0.8588, 0.8588) + vec3(0.0627, 0.0627, 0.0627);\n"
662                 "}\n";
663
664         static const char *yuv_to_601_frag =
665                 "void main()\n"
666                 "{\n"
667                 "       gl_FragColor.r = gl_FragColor.r * 0.8588 + 0.0627;\n"
668                 "}\n";
669
670
671         if(new_frame)
672         {
673                 if(get_output()->get_opengl_state() != VFrame::SCREEN)
674                 {
675 // Copy new frame to temporary texture
676                         get_output()->to_texture();
677
678 // Draw it only to copy it to the temporary.
679                         get_output()->enable_opengl();
680                         VFrame::init_screen(get_output()->get_w(), get_output()->get_h());
681                         get_output()->bind_texture(0);
682                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
683                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
684                         get_output()->draw_texture();
685                 }
686
687                 get_output()->enable_opengl();
688                 VFrame::init_screen(get_output()->get_w(), get_output()->get_h());
689                 glActiveTexture(GL_TEXTURE0);
690                 BC_Texture::new_texture(&src_texture,
691                         get_output()->get_w(),
692                         get_output()->get_h(),
693                         get_output()->get_color_model());
694                 src_texture->bind(0);
695                 glCopyTexSubImage2D(GL_TEXTURE_2D,
696                         0,
697                         0,
698                         0,
699                         0,
700                         0,
701                         get_output()->get_w(),
702                         get_output()->get_h());
703
704 // Store aggregation state only when reading a frame
705 //printf("FrameField::handle_opengl %p\n", get_output());
706 //get_output()->dump_stacks();
707                 if(prev_effect_is(_("RGB - 601")) ||
708                         next_effect_is(_("RGB - 601")))
709                 {
710                         aggregate_rgb601 = 1;
711                         rgb601_direction = get_output()->get_params()->get("RGB601_DIRECTION", 0);
712                 }
713                 else
714                         aggregate_rgb601 = 0;
715         }
716         else
717         {
718                 get_output()->enable_opengl();
719         }
720
721         float y_offset = 0.0;
722         if(field_number == 0)
723         {
724                 if(config.field_dominance == BOTTOM_FIELD_FIRST)
725                         y_offset = 1.0;
726         }
727         else
728         {
729                 if(config.field_dominance == TOP_FIELD_FIRST)
730                         y_offset = 1.0;
731         }
732
733         VFrame::init_screen(get_output()->get_w(), get_output()->get_h());
734         glActiveTexture(GL_TEXTURE0);
735         BC_Texture::new_texture(&src_texture,
736                 get_output()->get_w(),
737                 get_output()->get_h(),
738                 get_output()->get_color_model());
739
740         const char *shader_stack[16];
741         memset(shader_stack,0, sizeof(shader_stack));
742         int current_shader = 0;
743
744         shader_stack[current_shader++] = field_frag;
745
746 // Aggregate with other effect
747 //printf("FrameField::handle_opengl %s\n", get_output()->get_next_effect());
748         if( aggregate_rgb601 ) {
749                 switch( rgb601_direction ) {
750                 case 1:
751                         shader_stack[current_shader++] =
752                                 BC_CModels::is_yuv(get_output()->get_color_model()) ?
753                                          yuv_to_601_frag : rgb_to_601_frag;
754                         break;
755                 case 2:
756                         shader_stack[current_shader++] =
757                                 BC_CModels::is_yuv(get_output()->get_color_model()) ?
758                                         _601_to_yuv_frag : _601_to_rgb_frag;
759                         break;
760                 }
761         }
762
763         shader_stack[current_shader] = 0;
764         unsigned int shader = VFrame::make_shader(shader_stack);
765         if( shader > 0 ) {
766                 glUseProgram(shader);
767                 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
768                 glUniform1f(glGetUniformLocation(shader, "double_line_h"),
769                         2.0 / src_texture->get_texture_h());
770                 glUniform1f(glGetUniformLocation(shader, "y_offset"),
771                         y_offset / src_texture->get_texture_h());
772         }
773
774         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
775         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
776         get_output()->draw_texture();
777
778         glUseProgram(0);
779         get_output()->set_opengl_state(VFrame::SCREEN);
780
781 // Reset for other operations
782         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
783         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
784
785 #endif
786         return 0;
787 }
788
789