update internationalization data
[goodguy/history.git] / cinelerra-5.0 / 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.terminate_string();
593 }
594
595 void FrameField::read_data(KeyFrame *keyframe)
596 {
597         FileXML input;
598
599         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
600
601         while(!input.read_tag())
602         {
603                 if(input.tag.title_is("FRAME_FIELD"))
604                 {
605                         config.field_dominance = input.tag.get_property("DOMINANCE", config.field_dominance);
606                 }
607         }
608 }
609
610 void FrameField::update_gui()
611 {
612         if(thread)
613         {
614                 if(load_configuration())
615                 {
616                         thread->window->lock_window();
617                         ((FrameFieldWindow*)thread->window)->top->update(config.field_dominance == TOP_FIELD_FIRST);
618                         ((FrameFieldWindow*)thread->window)->bottom->update(config.field_dominance == BOTTOM_FIELD_FIRST);
619                         thread->window->unlock_window();
620                 }
621         }
622 }
623
624 int FrameField::handle_opengl()
625 {
626 #ifdef HAVE_GL
627         static const char *field_frag = 
628                 "uniform sampler2D tex;\n"
629                 "uniform float double_line_h;\n"
630                 "uniform float y_offset;\n"
631                 "void main()\n"
632                 "{\n"
633                 "       vec2 coord = gl_TexCoord[0].st;\n"
634 /* Number of double lines + fraction of current double line */
635                 "       float half_y = (coord.y - y_offset) / double_line_h;\n"
636 /* Lines comprising current double line */
637                 "       float line1 = floor(half_y) * double_line_h + y_offset;\n"
638                 "       float line2 = line1 + double_line_h;\n"
639 /* Distance from line1 to line2 */
640                 "       float frac = fract(half_y);\n"
641                 "       gl_FragColor =  mix(\n"
642                 "               texture2D(tex, vec2(coord.x, line1)), \n"
643                 "               texture2D(tex, vec2(coord.x, line2)), \n"
644                 "               frac);\n"
645                 "}\n";
646
647         static const char *_601_to_rgb_frag = 
648                 "void main()\n"
649                 "{\n"
650                 "       gl_FragColor.rgb = gl_FragColor.rgb * vec3(1.1644, 1.1644, 1.1644) - vec3(0.0627, 0.0627, 0.0627);\n"
651                 "}\n";
652
653         static const char *_601_to_yuv_frag = 
654                 "void main()\n"
655                 "{\n"
656                 "       gl_FragColor.r = gl_FragColor.r * 1.1644 - 0.0627;\n"
657                 "}\n";
658
659         static const char *rgb_to_601_frag = 
660                 "void main()\n"
661                 "{\n"
662                 "       gl_FragColor.rgb = gl_FragColor.rgb * vec3(0.8588, 0.8588, 0.8588) + vec3(0.0627, 0.0627, 0.0627);\n"
663                 "}\n";
664
665         static const char *yuv_to_601_frag = 
666                 "void main()\n"
667                 "{\n"
668                 "       gl_FragColor.r = gl_FragColor.r * 0.8588 + 0.0627;\n"
669                 "}\n";
670
671
672         if(new_frame)
673         {
674                 if(get_output()->get_opengl_state() != VFrame::SCREEN)
675                 {
676 // Copy new frame to temporary texture
677                         get_output()->to_texture();
678
679 // Draw it only to copy it to the temporary.
680                         get_output()->enable_opengl();
681                         VFrame::init_screen(get_output()->get_w(), get_output()->get_h());
682                         get_output()->bind_texture(0);
683                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
684                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
685                         get_output()->draw_texture();
686                 }
687
688                 get_output()->enable_opengl();
689                 VFrame::init_screen(get_output()->get_w(), get_output()->get_h());
690                 glActiveTexture(GL_TEXTURE0);
691                 BC_Texture::new_texture(&src_texture,
692                         get_output()->get_w(), 
693                         get_output()->get_h(), 
694                         get_output()->get_color_model());
695                 src_texture->bind(0);
696                 glCopyTexSubImage2D(GL_TEXTURE_2D,
697                         0,
698                         0,
699                         0,
700                         0,
701                         0,
702                         get_output()->get_w(),
703                         get_output()->get_h());
704
705 // Store aggregation state only when reading a frame
706 //printf("FrameField::handle_opengl %p\n", get_output());
707 //get_output()->dump_stacks();
708                 if(prev_effect_is(_("RGB - 601")) ||
709                         next_effect_is(_("RGB - 601")))
710                 {
711                         aggregate_rgb601 = 1;
712                         rgb601_direction = get_output()->get_params()->get("RGB601_DIRECTION", 0);
713                 }
714                 else
715                         aggregate_rgb601 = 0;
716         }
717         else
718         {
719                 get_output()->enable_opengl();
720         }
721
722         unsigned int frag = 0;
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
743         const char *shaders[3] = { 0, 0, 0 };
744         shaders[0] = field_frag;
745
746
747 // Aggregate with other effect
748 //printf("FrameField::handle_opengl %s\n", get_output()->get_next_effect());
749         if(aggregate_rgb601)
750         {
751                 if(rgb601_direction == 1)
752                 {
753                         if(BC_CModels::is_yuv(get_output()->get_color_model()))
754                                 shaders[1] = yuv_to_601_frag;
755                         else
756                                 shaders[1] = rgb_to_601_frag;
757                 }
758                 else
759                 if(rgb601_direction == 2)
760                 {
761                         if(BC_CModels::is_yuv(get_output()->get_color_model()))
762                                 shaders[1] = _601_to_yuv_frag;
763                         else
764                                 shaders[1] = _601_to_rgb_frag;
765                 }
766         }
767
768
769
770         frag = VFrame::make_shader(0, shaders[0], shaders[1], shaders[2], 0);
771
772
773         if(frag)
774         {
775                 glUseProgram(frag);
776                 glUniform1i(glGetUniformLocation(frag, "tex"), 0);
777                 glUniform1f(glGetUniformLocation(frag, "double_line_h"), 
778                         2.0 / src_texture->get_texture_h());
779                 glUniform1f(glGetUniformLocation(frag, "y_offset"), 
780                         y_offset / src_texture->get_texture_h());
781         }
782
783
784
785         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
786         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
787         get_output()->draw_texture();
788
789         glUseProgram(0);
790         get_output()->set_opengl_state(VFrame::SCREEN);
791
792 // Reset for other operations
793         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
794         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
795
796 #endif
797         return 0;
798 }
799
800