remove whitespace at eol
[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(0,
355                                 -1,
356                                 frame->get_w(),
357                                 frame->get_h(),
358                                 frame->get_color_model(),
359                                 -1);
360                 }
361                 ptr = src_frame;
362         }
363
364
365 // Import source frame at half frame rate
366         if(current_frame_number != src_frame_number ||
367 // If same frame was requested, assume it was a configuration change and reprocess.
368                 start_position == last_frame)
369         {
370                 read_frame(ptr,
371                         0,
372                         current_frame_number,
373                         frame_rate / 2,
374                         get_use_opengl());
375                 src_frame_number = current_frame_number;
376                 new_frame = 1;
377         }
378
379
380         if(get_use_opengl())
381         {
382                 run_opengl();
383                 return 0;
384         }
385
386         int row_size = VFrame::calculate_bytes_per_pixel(frame->get_color_model()) *
387                 frame->get_w();
388
389         unsigned char **src_rows = src_frame->get_rows();
390         unsigned char **output_rows = frame->get_rows();
391
392
393 // Even field
394         if(field_number == 0)
395         {
396                 if(config.field_dominance == TOP_FIELD_FIRST)
397                 {
398                         for(int i = 0; i < frame->get_h() - 1; i += 2)
399                         {
400 // Copy even lines of src to both lines of output
401                                 memcpy(output_rows[i],
402                                         src_rows[i],
403                                         row_size);
404                         }
405
406 // Average empty rows
407                         /* if(config.avg) */ average_rows(0, frame);
408                 }
409                 else
410                 {
411                         for(int i = 0; i < frame->get_h() - 1; i += 2)
412                         {
413 // Copy odd lines of current to both lines of output with shift up.
414                                 memcpy(output_rows[i + 1],
415                                         src_rows[i + 1],
416                                         row_size);
417                         }
418
419 // Average empty rows
420                         /* if(config.avg) */ average_rows(1, frame);
421                 }
422         }
423         else
424 // Odd field
425         {
426                 if(config.field_dominance == TOP_FIELD_FIRST)
427                 {
428                         for(int i = 0; i < frame->get_h() - 1; i += 2)
429                         {
430 // Copy odd lines of src to both lines of output
431                                 memcpy(output_rows[i + 1],
432                                         src_rows[i + 1],
433                                         row_size);
434                         }
435
436 // Average empty rows
437                         /* if(config.avg) */ average_rows(1, frame);
438                 }
439                 else
440                 {
441                         for(int i = 0; i < frame->get_h() - 1; i += 2)
442                         {
443 // Copy even lines of src to both lines of output.
444                                 memcpy(output_rows[i],
445                                         src_rows[i],
446                                         row_size);
447                         }
448
449 // Average empty rows
450                         /* if(config.avg) */ average_rows(0, frame);
451                 }
452         }
453
454         last_frame = start_position;
455         return 0;
456 }
457
458
459 // Averaging 2 pixels
460 #define AVERAGE(type, temp_type, components, offset) \
461 { \
462         type **rows = (type**)frame->get_rows(); \
463         int w = frame->get_w(); \
464         int h = frame->get_h(); \
465         int row_size = components * w; \
466         for(int i = offset; i < h - 3; i += 2) \
467         { \
468                 type *row1 = rows[i]; \
469                 type *row2 = rows[i + 1]; \
470                 type *row3 = rows[i + 2]; \
471                 for(int j = 0; j < row_size; j++) \
472                 { \
473                         temp_type sum = (temp_type)*row1++ + (temp_type)*row3++; \
474                         *row2++ = (sum / 2); \
475                 } \
476         } \
477 }
478
479 // Averaging 6 pixels
480 #define AVERAGE_BAK(type, components, offset) \
481 { \
482         type **rows = (type**)frame->get_rows(); \
483         int w = frame->get_w(); \
484         int h = frame->get_h(); \
485         int row_size = w; \
486         for(int i = offset; i < h - 3; i += 2) \
487         { \
488                 type *row1 = rows[i]; \
489                 type *row2 = rows[i + 1]; \
490                 type *row3 = rows[i + 2]; \
491                 int64_t sum; \
492                 int64_t pixel1[4], pixel2[4], pixel3[4]; \
493                 int64_t pixel4[4], pixel5[4], pixel6[4]; \
494  \
495 /* First pixel */ \
496                 for(int j = 0; j < components; j++) \
497                 { \
498                         pixel1[j] = *row1++; \
499                         pixel4[j] = *row3++; \
500                         *row2++ = (pixel1[j] + pixel4[j]) >> 1; \
501                 } \
502  \
503                 for(int j = 2; j < row_size; j++) \
504                 { \
505                         for(int k = 0; k < components; k++) \
506                         { \
507                                 pixel2[k] = *row1++; \
508                                 pixel5[k] = *row3++; \
509                         } \
510  \
511                         for(int k = 0; k < components; k++) \
512                         { \
513                                 pixel3[k] = *row1; \
514                                 pixel6[k] = *row3; \
515                                 *row2++ = (pixel1[k] + \
516                                         pixel2[k] + \
517                                         pixel3[k] + \
518                                         pixel4[k] + \
519                                         pixel5[k] + \
520                                         pixel6[k]) / 6; \
521                                 pixel1[k] = pixel2[k]; \
522                                 pixel4[k] = pixel5[k]; \
523                         } \
524                 } \
525  \
526 /* Last pixel */ \
527                 for(int j = 0; j < components; j++) \
528                 { \
529                         *row2++ = (pixel3[j] + pixel6[j]) >> 1; \
530                 } \
531         } \
532 }
533
534 void FrameField::average_rows(int offset, VFrame *frame)
535 {
536 //printf("FrameField::average_rows 1 %d\n", offset);
537         switch(frame->get_color_model())
538         {
539                 case BC_RGB888:
540                 case BC_YUV888:
541                         AVERAGE(unsigned char, int64_t, 3, offset);
542                         break;
543                 case BC_RGB_FLOAT:
544                         AVERAGE(float, float, 3, offset);
545                         break;
546                 case BC_RGBA8888:
547                 case BC_YUVA8888:
548                         AVERAGE(unsigned char, int64_t, 4, offset);
549                         break;
550                 case BC_RGBA_FLOAT:
551                         AVERAGE(float, float, 4, offset);
552                         break;
553                 case BC_RGB161616:
554                 case BC_YUV161616:
555                         AVERAGE(uint16_t, int64_t, 3, offset);
556                         break;
557                 case BC_RGBA16161616:
558                 case BC_YUVA16161616:
559                         AVERAGE(uint16_t, int64_t, 4, offset);
560                         break;
561         }
562 }
563
564
565
566 const char* FrameField::plugin_title() { return _("Frames to fields"); }
567 int FrameField::is_realtime() { return 1; }
568
569 NEW_WINDOW_MACRO(FrameField, FrameFieldWindow)
570
571 int FrameField::load_configuration()
572 {
573         KeyFrame *prev_keyframe;
574         FrameFieldConfig old_config = config;
575
576         prev_keyframe = get_prev_keyframe(get_source_position());
577         read_data(prev_keyframe);
578
579         return !old_config.equivalent(config);
580 }
581
582
583 void FrameField::save_data(KeyFrame *keyframe)
584 {
585         FileXML output;
586
587 // cause data to be stored directly in text
588         output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
589         output.tag.set_title("FRAME_FIELD");
590         output.tag.set_property("DOMINANCE", config.field_dominance);
591         output.append_tag();
592         output.tag.set_title("/FRAME_FIELD");
593         output.append_tag();
594         output.append_newline();
595         output.terminate_string();
596 }
597
598 void FrameField::read_data(KeyFrame *keyframe)
599 {
600         FileXML input;
601
602         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
603
604         while(!input.read_tag())
605         {
606                 if(input.tag.title_is("FRAME_FIELD"))
607                 {
608                         config.field_dominance = input.tag.get_property("DOMINANCE", config.field_dominance);
609                 }
610         }
611 }
612
613 void FrameField::update_gui()
614 {
615         if(thread)
616         {
617                 if(load_configuration())
618                 {
619                         thread->window->lock_window();
620                         ((FrameFieldWindow*)thread->window)->top->update(config.field_dominance == TOP_FIELD_FIRST);
621                         ((FrameFieldWindow*)thread->window)->bottom->update(config.field_dominance == BOTTOM_FIELD_FIRST);
622                         thread->window->unlock_window();
623                 }
624         }
625 }
626
627 int FrameField::handle_opengl()
628 {
629 #ifdef HAVE_GL
630         static const char *field_frag =
631                 "uniform sampler2D tex;\n"
632                 "uniform float double_line_h;\n"
633                 "uniform float y_offset;\n"
634                 "void main()\n"
635                 "{\n"
636                 "       vec2 coord = gl_TexCoord[0].st;\n"
637 /* Number of double lines + fraction of current double line */
638                 "       float half_y = (coord.y - y_offset) / double_line_h;\n"
639 /* Lines comprising current double line */
640                 "       float line1 = floor(half_y) * double_line_h + y_offset;\n"
641                 "       float line2 = line1 + double_line_h;\n"
642 /* Distance from line1 to line2 */
643                 "       float frac = fract(half_y);\n"
644                 "       gl_FragColor =  mix(\n"
645                 "               texture2D(tex, vec2(coord.x, line1)), \n"
646                 "               texture2D(tex, vec2(coord.x, line2)), \n"
647                 "               frac);\n"
648                 "}\n";
649
650         static const char *_601_to_rgb_frag =
651                 "void main()\n"
652                 "{\n"
653                 "       gl_FragColor.rgb = gl_FragColor.rgb * vec3(1.1644, 1.1644, 1.1644) - vec3(0.0627, 0.0627, 0.0627);\n"
654                 "}\n";
655
656         static const char *_601_to_yuv_frag =
657                 "void main()\n"
658                 "{\n"
659                 "       gl_FragColor.r = gl_FragColor.r * 1.1644 - 0.0627;\n"
660                 "}\n";
661
662         static const char *rgb_to_601_frag =
663                 "void main()\n"
664                 "{\n"
665                 "       gl_FragColor.rgb = gl_FragColor.rgb * vec3(0.8588, 0.8588, 0.8588) + vec3(0.0627, 0.0627, 0.0627);\n"
666                 "}\n";
667
668         static const char *yuv_to_601_frag =
669                 "void main()\n"
670                 "{\n"
671                 "       gl_FragColor.r = gl_FragColor.r * 0.8588 + 0.0627;\n"
672                 "}\n";
673
674
675         if(new_frame)
676         {
677                 if(get_output()->get_opengl_state() != VFrame::SCREEN)
678                 {
679 // Copy new frame to temporary texture
680                         get_output()->to_texture();
681
682 // Draw it only to copy it to the temporary.
683                         get_output()->enable_opengl();
684                         VFrame::init_screen(get_output()->get_w(), get_output()->get_h());
685                         get_output()->bind_texture(0);
686                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
687                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
688                         get_output()->draw_texture();
689                 }
690
691                 get_output()->enable_opengl();
692                 VFrame::init_screen(get_output()->get_w(), get_output()->get_h());
693                 glActiveTexture(GL_TEXTURE0);
694                 BC_Texture::new_texture(&src_texture,
695                         get_output()->get_w(),
696                         get_output()->get_h(),
697                         get_output()->get_color_model());
698                 src_texture->bind(0);
699                 glCopyTexSubImage2D(GL_TEXTURE_2D,
700                         0,
701                         0,
702                         0,
703                         0,
704                         0,
705                         get_output()->get_w(),
706                         get_output()->get_h());
707
708 // Store aggregation state only when reading a frame
709 //printf("FrameField::handle_opengl %p\n", get_output());
710 //get_output()->dump_stacks();
711                 if(prev_effect_is(_("RGB - 601")) ||
712                         next_effect_is(_("RGB - 601")))
713                 {
714                         aggregate_rgb601 = 1;
715                         rgb601_direction = get_output()->get_params()->get("RGB601_DIRECTION", 0);
716                 }
717                 else
718                         aggregate_rgb601 = 0;
719         }
720         else
721         {
722                 get_output()->enable_opengl();
723         }
724
725         unsigned int frag = 0;
726         float y_offset = 0.0;
727         if(field_number == 0)
728         {
729                 if(config.field_dominance == BOTTOM_FIELD_FIRST)
730                         y_offset = 1.0;
731         }
732         else
733         {
734                 if(config.field_dominance == TOP_FIELD_FIRST)
735                         y_offset = 1.0;
736         }
737
738         VFrame::init_screen(get_output()->get_w(), get_output()->get_h());
739         glActiveTexture(GL_TEXTURE0);
740         BC_Texture::new_texture(&src_texture,
741                 get_output()->get_w(),
742                 get_output()->get_h(),
743                 get_output()->get_color_model());
744
745
746         const char *shaders[3] = { 0, 0, 0 };
747         shaders[0] = field_frag;
748
749
750 // Aggregate with other effect
751 //printf("FrameField::handle_opengl %s\n", get_output()->get_next_effect());
752         if(aggregate_rgb601)
753         {
754                 if(rgb601_direction == 1)
755                 {
756                         if(BC_CModels::is_yuv(get_output()->get_color_model()))
757                                 shaders[1] = yuv_to_601_frag;
758                         else
759                                 shaders[1] = rgb_to_601_frag;
760                 }
761                 else
762                 if(rgb601_direction == 2)
763                 {
764                         if(BC_CModels::is_yuv(get_output()->get_color_model()))
765                                 shaders[1] = _601_to_yuv_frag;
766                         else
767                                 shaders[1] = _601_to_rgb_frag;
768                 }
769         }
770
771
772
773         frag = VFrame::make_shader(0, shaders[0], shaders[1], shaders[2], 0);
774
775
776         if(frag)
777         {
778                 glUseProgram(frag);
779                 glUniform1i(glGetUniformLocation(frag, "tex"), 0);
780                 glUniform1f(glGetUniformLocation(frag, "double_line_h"),
781                         2.0 / src_texture->get_texture_h());
782                 glUniform1f(glGetUniformLocation(frag, "y_offset"),
783                         y_offset / src_texture->get_texture_h());
784         }
785
786
787
788         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
789         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
790         get_output()->draw_texture();
791
792         glUseProgram(0);
793         get_output()->set_opengl_state(VFrame::SCREEN);
794
795 // Reset for other operations
796         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
797         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
798
799 #endif
800         return 0;
801 }
802
803