add layout_scale preference, scaling cleanup, rework init bc_resources, init tip_info...
[goodguy/cinelerra.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         xS(210),
191         yS(100),
192         xS(200),
193         yS(100),
194         0)
195 {
196         this->plugin = plugin;
197 }
198
199 void FrameFieldWindow::create_objects()
200 {
201         int xs10 = xS(10);
202         int ys5 = yS(5), ys10 = yS(10);
203         int x = xs10, y = ys10;
204         add_subwindow(top = new FrameFieldTop(plugin, this, x, y));
205         y += top->get_h() + ys5;
206         add_subwindow(bottom = new FrameFieldBottom(plugin, this, x, y));
207         y += bottom->get_h() + ys5;
208         show_window();
209         flush();
210 }
211
212
213
214
215
216
217
218
219
220
221
222
223 FrameFieldTop::FrameFieldTop(FrameField *plugin,
224         FrameFieldWindow *gui,
225         int x,
226         int y)
227  : BC_Radial(x,
228         y,
229         plugin->config.field_dominance == TOP_FIELD_FIRST,
230         _("Top field first"))
231 {
232         this->plugin = plugin;
233         this->gui = gui;
234 }
235
236 int FrameFieldTop::handle_event()
237 {
238         plugin->config.field_dominance = TOP_FIELD_FIRST;
239         gui->bottom->update(0);
240         plugin->send_configure_change();
241         return 1;
242 }
243
244
245
246
247
248 FrameFieldBottom::FrameFieldBottom(FrameField *plugin,
249         FrameFieldWindow *gui,
250         int x,
251         int y)
252  : BC_Radial(x,
253         y,
254         plugin->config.field_dominance == BOTTOM_FIELD_FIRST,
255         _("Bottom field first"))
256 {
257         this->plugin = plugin;
258         this->gui = gui;
259 }
260
261 int FrameFieldBottom::handle_event()
262 {
263         plugin->config.field_dominance = BOTTOM_FIELD_FIRST;
264         gui->top->update(0);
265         plugin->send_configure_change();
266         return 1;
267 }
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291 FrameField::FrameField(PluginServer *server)
292  : PluginVClient(server)
293 {
294
295         field_number = 0;
296         src_frame = 0;
297         src_frame_number = -1;
298         last_frame = -1;
299         src_texture = 0;
300         aggregate_rgb601 = 0;
301         rgb601_direction = 0;
302 }
303
304
305 FrameField::~FrameField()
306 {
307
308
309         if(src_frame) delete src_frame;
310         if(src_texture) delete src_texture;
311 }
312
313
314 // 0 - current frame field 0, prev frame field 1
315 // 1 - current frame field 0, current frame field 1, copy current to prev
316 // 2 - current frame field 0, prev frame field 1
317
318 int FrameField::process_buffer(VFrame *frame,
319         int64_t start_position,
320         double frame_rate)
321 {
322         load_configuration();
323
324         new_frame = 0;
325
326 // Calculate current field based on absolute position so the algorithm isn't
327 // relative to where playback started.
328         field_number = get_source_position() % 2;
329
330         if (get_direction() == PLAY_REVERSE)
331         {
332                 start_position++;
333                 field_number = (field_number + 1) % 2;
334         }
335
336
337         current_frame_number = start_position / 2;
338
339         VFrame *ptr = frame;
340         if(get_use_opengl())
341         {
342 // Read new frames directly into output frame for hardware
343         }
344         else
345         {
346 // Read into temporary for software
347                 if(src_frame &&
348                         src_frame->get_color_model() != frame->get_color_model())
349                 {
350                         delete src_frame;
351                         src_frame = 0;
352                 }
353
354                 if(!src_frame)
355                 {
356                         src_frame = new VFrame(frame->get_w(), frame->get_h(),
357                                 frame->get_color_model(), 0);
358                 }
359                 ptr = src_frame;
360         }
361
362
363 // Import source frame at half frame rate
364         if(current_frame_number != src_frame_number ||
365 // If same frame was requested, assume it was a configuration change and reprocess.
366                 start_position == last_frame)
367         {
368                 read_frame(ptr,
369                         0,
370                         current_frame_number,
371                         frame_rate / 2,
372                         get_use_opengl());
373                 src_frame_number = current_frame_number;
374                 new_frame = 1;
375         }
376
377
378         if(get_use_opengl())
379         {
380                 run_opengl();
381                 return 0;
382         }
383
384         int row_size = VFrame::calculate_bytes_per_pixel(frame->get_color_model()) *
385                 frame->get_w();
386
387         unsigned char **src_rows = src_frame->get_rows();
388         unsigned char **output_rows = frame->get_rows();
389
390
391 // Even field
392         if(field_number == 0)
393         {
394                 if(config.field_dominance == TOP_FIELD_FIRST)
395                 {
396                         for(int i = 0; i < frame->get_h() - 1; i += 2)
397                         {
398 // Copy even lines of src to both lines of output
399                                 memcpy(output_rows[i],
400                                         src_rows[i],
401                                         row_size);
402                         }
403
404 // Average empty rows
405                         /* if(config.avg) */ average_rows(0, frame);
406                 }
407                 else
408                 {
409                         for(int i = 0; i < frame->get_h() - 1; i += 2)
410                         {
411 // Copy odd lines of current to both lines of output with shift up.
412                                 memcpy(output_rows[i + 1],
413                                         src_rows[i + 1],
414                                         row_size);
415                         }
416
417 // Average empty rows
418                         /* if(config.avg) */ average_rows(1, frame);
419                 }
420         }
421         else
422 // Odd field
423         {
424                 if(config.field_dominance == TOP_FIELD_FIRST)
425                 {
426                         for(int i = 0; i < frame->get_h() - 1; i += 2)
427                         {
428 // Copy odd lines of src to both lines of output
429                                 memcpy(output_rows[i + 1],
430                                         src_rows[i + 1],
431                                         row_size);
432                         }
433
434 // Average empty rows
435                         /* if(config.avg) */ average_rows(1, frame);
436                 }
437                 else
438                 {
439                         for(int i = 0; i < frame->get_h() - 1; i += 2)
440                         {
441 // Copy even lines of src to both lines of output.
442                                 memcpy(output_rows[i],
443                                         src_rows[i],
444                                         row_size);
445                         }
446
447 // Average empty rows
448                         /* if(config.avg) */ average_rows(0, frame);
449                 }
450         }
451
452         last_frame = start_position;
453         return 0;
454 }
455
456
457 // Averaging 2 pixels
458 #define AVERAGE(type, temp_type, components, offset) \
459 { \
460         type **rows = (type**)frame->get_rows(); \
461         int w = frame->get_w(); \
462         int h = frame->get_h(); \
463         int row_size = components * w; \
464         for(int i = offset; i < h - 3; i += 2) \
465         { \
466                 type *row1 = rows[i]; \
467                 type *row2 = rows[i + 1]; \
468                 type *row3 = rows[i + 2]; \
469                 for(int j = 0; j < row_size; j++) \
470                 { \
471                         temp_type sum = (temp_type)*row1++ + (temp_type)*row3++; \
472                         *row2++ = (sum / 2); \
473                 } \
474         } \
475 }
476
477 // Averaging 6 pixels
478 #define AVERAGE_BAK(type, components, offset) \
479 { \
480         type **rows = (type**)frame->get_rows(); \
481         int w = frame->get_w(); \
482         int h = frame->get_h(); \
483         int row_size = w; \
484         for(int i = offset; i < h - 3; i += 2) \
485         { \
486                 type *row1 = rows[i]; \
487                 type *row2 = rows[i + 1]; \
488                 type *row3 = rows[i + 2]; \
489                 int64_t sum; \
490                 int64_t pixel1[4], pixel2[4], pixel3[4]; \
491                 int64_t pixel4[4], pixel5[4], pixel6[4]; \
492  \
493 /* First pixel */ \
494                 for(int j = 0; j < components; j++) \
495                 { \
496                         pixel1[j] = *row1++; \
497                         pixel4[j] = *row3++; \
498                         *row2++ = (pixel1[j] + pixel4[j]) >> 1; \
499                 } \
500  \
501                 for(int j = 2; j < row_size; j++) \
502                 { \
503                         for(int k = 0; k < components; k++) \
504                         { \
505                                 pixel2[k] = *row1++; \
506                                 pixel5[k] = *row3++; \
507                         } \
508  \
509                         for(int k = 0; k < components; k++) \
510                         { \
511                                 pixel3[k] = *row1; \
512                                 pixel6[k] = *row3; \
513                                 *row2++ = (pixel1[k] + \
514                                         pixel2[k] + \
515                                         pixel3[k] + \
516                                         pixel4[k] + \
517                                         pixel5[k] + \
518                                         pixel6[k]) / 6; \
519                                 pixel1[k] = pixel2[k]; \
520                                 pixel4[k] = pixel5[k]; \
521                         } \
522                 } \
523  \
524 /* Last pixel */ \
525                 for(int j = 0; j < components; j++) \
526                 { \
527                         *row2++ = (pixel3[j] + pixel6[j]) >> 1; \
528                 } \
529         } \
530 }
531
532 void FrameField::average_rows(int offset, VFrame *frame)
533 {
534 //printf("FrameField::average_rows 1 %d\n", offset);
535         switch(frame->get_color_model())
536         {
537                 case BC_RGB888:
538                 case BC_YUV888:
539                         AVERAGE(unsigned char, int64_t, 3, offset);
540                         break;
541                 case BC_RGB_FLOAT:
542                         AVERAGE(float, float, 3, offset);
543                         break;
544                 case BC_RGBA8888:
545                 case BC_YUVA8888:
546                         AVERAGE(unsigned char, int64_t, 4, offset);
547                         break;
548                 case BC_RGBA_FLOAT:
549                         AVERAGE(float, float, 4, offset);
550                         break;
551                 case BC_RGB161616:
552                 case BC_YUV161616:
553                         AVERAGE(uint16_t, int64_t, 3, offset);
554                         break;
555                 case BC_RGBA16161616:
556                 case BC_YUVA16161616:
557                         AVERAGE(uint16_t, int64_t, 4, offset);
558                         break;
559         }
560 }
561
562
563
564 const char* FrameField::plugin_title() { return N_("Frames to fields"); }
565 int FrameField::is_realtime() { return 1; }
566
567 NEW_WINDOW_MACRO(FrameField, FrameFieldWindow)
568
569 int FrameField::load_configuration()
570 {
571         KeyFrame *prev_keyframe;
572         FrameFieldConfig old_config = config;
573
574         prev_keyframe = get_prev_keyframe(get_source_position());
575         read_data(prev_keyframe);
576
577         return !old_config.equivalent(config);
578 }
579
580
581 void FrameField::save_data(KeyFrame *keyframe)
582 {
583         FileXML output;
584
585 // cause data to be stored directly in text
586         output.set_shared_output(keyframe->xbuf);
587         output.tag.set_title("FRAME_FIELD");
588         output.tag.set_property("DOMINANCE", config.field_dominance);
589         output.append_tag();
590         output.tag.set_title("/FRAME_FIELD");
591         output.append_tag();
592         output.append_newline();
593         output.terminate_string();
594 }
595
596 void FrameField::read_data(KeyFrame *keyframe)
597 {
598         FileXML input;
599
600         input.set_shared_input(keyframe->xbuf);
601
602         while(!input.read_tag())
603         {
604                 if(input.tag.title_is("FRAME_FIELD"))
605                 {
606                         config.field_dominance = input.tag.get_property("DOMINANCE", config.field_dominance);
607                 }
608         }
609 }
610
611 void FrameField::update_gui()
612 {
613         if(thread)
614         {
615                 if(load_configuration())
616                 {
617                         thread->window->lock_window();
618                         ((FrameFieldWindow*)thread->window)->top->update(config.field_dominance == TOP_FIELD_FIRST);
619                         ((FrameFieldWindow*)thread->window)->bottom->update(config.field_dominance == BOTTOM_FIELD_FIRST);
620                         thread->window->unlock_window();
621                 }
622         }
623 }
624
625 int FrameField::handle_opengl()
626 {
627 #ifdef HAVE_GL
628         static const char *field_frag =
629                 "uniform sampler2D tex;\n"
630                 "uniform float double_line_h;\n"
631                 "uniform float y_offset;\n"
632                 "void main()\n"
633                 "{\n"
634                 "       vec2 coord = gl_TexCoord[0].st;\n"
635 /* Number of double lines + fraction of current double line */
636                 "       float half_y = (coord.y - y_offset) / double_line_h;\n"
637 /* Lines comprising current double line */
638                 "       float line1 = floor(half_y) * double_line_h + y_offset;\n"
639                 "       float line2 = line1 + double_line_h;\n"
640 /* Distance from line1 to line2 */
641                 "       float frac = fract(half_y);\n"
642                 "       gl_FragColor =  mix(\n"
643                 "               texture2D(tex, vec2(coord.x, line1)), \n"
644                 "               texture2D(tex, vec2(coord.x, line2)), \n"
645                 "               frac);\n"
646                 "}\n";
647
648         static const char *_601_to_rgb_frag =
649                 "void main()\n"
650                 "{\n"
651                 "       gl_FragColor.rgb = gl_FragColor.rgb * vec3(1.1644, 1.1644, 1.1644) - vec3(0.0627, 0.0627, 0.0627);\n"
652                 "}\n";
653
654         static const char *_601_to_yuv_frag =
655                 "void main()\n"
656                 "{\n"
657                 "       gl_FragColor.r = gl_FragColor.r * 1.1644 - 0.0627;\n"
658                 "}\n";
659
660         static const char *rgb_to_601_frag =
661                 "void main()\n"
662                 "{\n"
663                 "       gl_FragColor.rgb = gl_FragColor.rgb * vec3(0.8588, 0.8588, 0.8588) + vec3(0.0627, 0.0627, 0.0627);\n"
664                 "}\n";
665
666         static const char *yuv_to_601_frag =
667                 "void main()\n"
668                 "{\n"
669                 "       gl_FragColor.r = gl_FragColor.r * 0.8588 + 0.0627;\n"
670                 "}\n";
671
672
673         if(new_frame)
674         {
675                 if(get_output()->get_opengl_state() != VFrame::SCREEN)
676                 {
677 // Copy new frame to temporary texture
678                         get_output()->to_texture();
679
680 // Draw it only to copy it to the temporary.
681                         get_output()->enable_opengl();
682                         VFrame::init_screen(get_output()->get_w(), get_output()->get_h());
683                         get_output()->bind_texture(0);
684                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
685                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
686                         get_output()->draw_texture();
687                 }
688
689                 get_output()->enable_opengl();
690                 VFrame::init_screen(get_output()->get_w(), get_output()->get_h());
691                 glActiveTexture(GL_TEXTURE0);
692                 BC_Texture::new_texture(&src_texture,
693                         get_output()->get_w(),
694                         get_output()->get_h(),
695                         get_output()->get_color_model());
696                 src_texture->bind(0);
697                 glCopyTexSubImage2D(GL_TEXTURE_2D,
698                         0,
699                         0,
700                         0,
701                         0,
702                         0,
703                         get_output()->get_w(),
704                         get_output()->get_h());
705
706 // Store aggregation state only when reading a frame
707 //printf("FrameField::handle_opengl %p\n", get_output());
708 //get_output()->dump_stacks();
709                 if(prev_effect_is(_("RGB - 601")) ||
710                         next_effect_is(_("RGB - 601")))
711                 {
712                         aggregate_rgb601 = 1;
713                         rgb601_direction = get_output()->get_params()->get("RGB601_DIRECTION", 0);
714                 }
715                 else
716                         aggregate_rgb601 = 0;
717         }
718         else
719         {
720                 get_output()->enable_opengl();
721         }
722
723         float y_offset = 0.0;
724         if(field_number == 0)
725         {
726                 if(config.field_dominance == BOTTOM_FIELD_FIRST)
727                         y_offset = 1.0;
728         }
729         else
730         {
731                 if(config.field_dominance == TOP_FIELD_FIRST)
732                         y_offset = 1.0;
733         }
734
735         VFrame::init_screen(get_output()->get_w(), get_output()->get_h());
736         glActiveTexture(GL_TEXTURE0);
737         BC_Texture::new_texture(&src_texture,
738                 get_output()->get_w(),
739                 get_output()->get_h(),
740                 get_output()->get_color_model());
741
742         const char *shader_stack[16];
743         memset(shader_stack,0, sizeof(shader_stack));
744         int current_shader = 0;
745
746         shader_stack[current_shader++] = field_frag;
747
748 // Aggregate with other effect
749 //printf("FrameField::handle_opengl %s\n", get_output()->get_next_effect());
750         if( aggregate_rgb601 ) {
751                 switch( rgb601_direction ) {
752                 case 1:
753                         shader_stack[current_shader++] =
754                                 BC_CModels::is_yuv(get_output()->get_color_model()) ?
755                                          yuv_to_601_frag : rgb_to_601_frag;
756                         break;
757                 case 2:
758                         shader_stack[current_shader++] =
759                                 BC_CModels::is_yuv(get_output()->get_color_model()) ?
760                                         _601_to_yuv_frag : _601_to_rgb_frag;
761                         break;
762                 }
763         }
764
765         shader_stack[current_shader] = 0;
766         unsigned int shader = VFrame::make_shader(shader_stack);
767         if( shader > 0 ) {
768                 glUseProgram(shader);
769                 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
770                 glUniform1f(glGetUniformLocation(shader, "double_line_h"),
771                         2.0 / src_texture->get_texture_h());
772                 glUniform1f(glGetUniformLocation(shader, "y_offset"),
773                         y_offset / src_texture->get_texture_h());
774         }
775
776         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
777         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
778         get_output()->draw_texture();
779
780         glUseProgram(0);
781         get_output()->set_opengl_state(VFrame::SCREEN);
782
783 // Reset for other operations
784         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
785         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
786
787 #endif
788         return 0;
789 }
790
791