Anonymous Contribution of new Mirror plugin
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / mirror / mirror.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 /*
23  * 2023. Derivative by Flip plugin.
24 */
25
26 #include "clip.h"
27 #include "bccmodels.h"
28 #include "bchash.h"
29 #include "filexml.h"
30 #include "mirror.h"
31 #include "mirrorwindow.h"
32 #include "language.h"
33 #include "mwindow.h"
34
35 #include <stdint.h>
36 #include <string.h>
37 #include <math.h>
38
39 REGISTER_PLUGIN(MirrorMain)
40
41
42
43
44
45
46 MirrorConfig::MirrorConfig()
47 {
48         reset(RESET_DEFAULT_SETTINGS);
49 }
50
51 void MirrorConfig::reset(int clear)
52 {
53         switch(clear) {
54                 case RESET_REFLECTION_CENTER_X :
55                         reflection_center_x = 50.00;
56                         mirror_horizontal = 1;
57                         mirror_swaphorizontal = 0;
58                         break;
59                 case RESET_REFLECTION_CENTER_Y :
60                         reflection_center_y = 50.00;
61                         mirror_vertical = 1;
62                         mirror_swapvertical = 0;
63                         break;
64                 case RESET_ALL :
65                 case RESET_DEFAULT_SETTINGS :
66                 default:
67                         mirror_horizontal = 1;
68                         mirror_swaphorizontal = 0;
69                         mirror_vertical = 0;
70                         mirror_swapvertical = 0;
71                         reflection_center_enable = 0;
72                         reflection_center_x = 50.00;
73                         reflection_center_y = 50.00;
74                         break;
75         }
76 }
77
78 void MirrorConfig::copy_from(MirrorConfig &that)
79 {
80         mirror_horizontal = that.mirror_horizontal;
81         mirror_swaphorizontal = that.mirror_swaphorizontal;
82         mirror_vertical = that.mirror_vertical;
83         mirror_swapvertical = that.mirror_swapvertical;
84         reflection_center_enable = that.reflection_center_enable;
85         reflection_center_x = that.reflection_center_x;
86         reflection_center_y = that.reflection_center_y;
87 }
88
89 int MirrorConfig::equivalent(MirrorConfig &that)
90 {
91         return EQUIV(mirror_horizontal, that.mirror_horizontal) &&
92                 EQUIV(mirror_swaphorizontal, that.mirror_swaphorizontal) &&
93                 EQUIV(mirror_vertical, that.mirror_vertical) && 
94                 EQUIV(mirror_swapvertical, that.mirror_swapvertical) &&
95                 EQUIV(reflection_center_enable, that.reflection_center_enable) &&
96                 EQUIV(reflection_center_x, that.reflection_center_x) &&
97                 EQUIV(reflection_center_y, that.reflection_center_y);
98 }
99
100 void MirrorConfig::interpolate(MirrorConfig &prev,
101         MirrorConfig &next,
102         int64_t prev_frame,
103         int64_t next_frame,
104         int64_t current_frame)
105 {
106         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
107         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
108
109         this->mirror_horizontal = prev.mirror_horizontal;
110         this->mirror_swaphorizontal = prev.mirror_swaphorizontal;
111         this->mirror_vertical = prev.mirror_vertical;
112         this->mirror_swapvertical = prev.mirror_swapvertical;
113         this->reflection_center_enable = prev.reflection_center_enable;
114         this->reflection_center_x = prev.reflection_center_x * prev_scale + next.reflection_center_x * next_scale;
115         this->reflection_center_y = prev.reflection_center_y * prev_scale + next.reflection_center_y * next_scale;
116 }
117
118
119
120
121
122
123
124
125
126
127 MirrorMain::MirrorMain(PluginServer *server)
128  : PluginVClient(server)
129 {
130
131 }
132
133 MirrorMain::~MirrorMain()
134 {
135
136 }
137
138 const char* MirrorMain::plugin_title() { return N_("Mirror"); }
139 int MirrorMain::is_realtime() { return 1; }
140
141
142 #define SWAPCOPY_PIXELS(components, in, out) \
143 { \
144         in[0] = out[0]; \
145         in[1] = out[1]; \
146         in[2] = out[2]; \
147         if(components == 4) \
148         { \
149                 in[3] = out[3]; \
150         } \
151 }
152
153
154 #define MIRROR_MACRO(type, components) \
155 { \
156         type **input_rows, **output_rows; \
157         type *input_row, *output_row; \
158         input_rows = ((type**)frame->get_rows()); \
159         output_rows = ((type**)frame->get_rows()); \
160  \
161         if (!config.reflection_center_enable) \
162         { \
163                 /* HORIZONTAL ONLY */ \
164                 if(config.mirror_horizontal && !config.mirror_vertical && !config.mirror_swaphorizontal) \
165                 { \
166                         for(i = 0; i < h; i++) \
167                         { \
168                                 input_row = input_rows[i]; \
169                                 output_row = output_rows[i] + (w - 1) * components; \
170                                 for(k = 0; k < w / 2; k++) \
171                                 { \
172                                         SWAPCOPY_PIXELS(components, output_row, input_row); \
173                                         input_row += components; \
174                                         output_row -= components; \
175                                 } \
176                         } \
177                 } \
178                 /* HORIZONTAL SWAP ONLY */ \
179                 else if(config.mirror_horizontal && !config.mirror_vertical && config.mirror_swaphorizontal) \
180                 { \
181                         for(i = 0; i < h; i++) \
182                         { \
183                                 input_row = input_rows[i]; \
184                                 output_row = output_rows[i] + (w - 1) * components; \
185                                 for(k = 0; k < w / 2; k++) \
186                                 { \
187                                         SWAPCOPY_PIXELS(components, input_row, output_row); \
188                                         input_row += components; \
189                                         output_row -= components; \
190                                 } \
191                         } \
192                 } \
193                 /* VERTICAL ONLY */ \
194                 else if(config.mirror_vertical && !config.mirror_horizontal && !config.mirror_swapvertical) \
195                 { \
196                         for(i = 0, j = h - 1; i < h / 2; i++, j--) \
197                         { \
198                                 input_row = input_rows[i]; \
199                                 output_row = output_rows[j]; \
200                                 for(k = 0; k < w; k++) \
201                                 { \
202                                         SWAPCOPY_PIXELS(components, output_row, input_row); \
203                                         output_row += components; \
204                                         input_row += components; \
205                                 } \
206                         } \
207                 } \
208                 /* VERTICAL SWAP ONLY */ \
209                 else if(config.mirror_vertical && !config.mirror_horizontal && config.mirror_swapvertical) \
210                 { \
211                         for(i = 0, j = h - 1; i < h / 2; i++, j--) \
212                         { \
213                                 input_row = input_rows[i]; \
214                                 output_row = output_rows[j]; \
215                                 for(k = 0; k < w; k++) \
216                                 { \
217                                         SWAPCOPY_PIXELS(components, input_row, output_row); \
218                                         output_row += components; \
219                                         input_row += components; \
220                                 } \
221                         } \
222                 } \
223                 /* HORIZONTAL and VERTICAL */ \
224                 else if(config.mirror_horizontal && config.mirror_vertical && !config.mirror_swaphorizontal && !config.mirror_swapvertical) \
225                 { \
226                         /* HORIZONTAL ONLY */ \
227                         for(i = 0; i < h; i++) \
228                         { \
229                                 input_row = input_rows[i]; \
230                                 output_row = output_rows[i] + (w - 1) * components; \
231                                 for(k = 0; k < w / 2; k++) \
232                                 { \
233                                         SWAPCOPY_PIXELS(components, output_row, input_row); \
234                                         input_row += components; \
235                                         output_row -= components; \
236                                 } \
237                         } \
238                         /* VERTICAL ONLY */ \
239                         for(i = 0, j = h - 1; i < h / 2; i++, j--) \
240                         { \
241                                 input_row = input_rows[i]; \
242                                 output_row = output_rows[j]; \
243                                 for(k = 0; k < w; k++) \
244                                 { \
245                                         SWAPCOPY_PIXELS(components, output_row, input_row); \
246                                         output_row += components; \
247                                         input_row += components; \
248                                 } \
249                         } \
250                 } \
251                 /* HORIZONTAL with SWAP and VERTICAL with SWAP */ \
252                 else if(config.mirror_horizontal && config.mirror_vertical && config.mirror_swaphorizontal && config.mirror_swapvertical) \
253                 { \
254                         /* HORIZONTAL SWAP ONLY */ \
255                         for(i = 0; i < h; i++) \
256                         { \
257                                 input_row = input_rows[i]; \
258                                 output_row = output_rows[i] + (w - 1) * components; \
259                                 for(k = 0; k < w / 2; k++) \
260                                 { \
261                                         SWAPCOPY_PIXELS(components, input_row, output_row); \
262                                         input_row += components; \
263                                         output_row -= components; \
264                                 } \
265                         } \
266                         /* VERTICAL SWAP ONLY */ \
267                         for(i = 0, j = h - 1; i < h / 2; i++, j--) \
268                         { \
269                                 input_row = input_rows[i]; \
270                                 output_row = output_rows[j]; \
271                                 for(k = 0; k < w; k++) \
272                                 { \
273                                         SWAPCOPY_PIXELS(components, input_row, output_row); \
274                                         output_row += components; \
275                                         input_row += components; \
276                                 } \
277                         } \
278                 } \
279                 /* HORIZONTAL with SWAP and VERTICAL ONLY */ \
280                 else if(config.mirror_horizontal && config.mirror_vertical && config.mirror_swaphorizontal && !config.mirror_swapvertical) \
281                 { \
282                         /* HORIZONTAL SWAP ONLY */ \
283                         for(i = 0; i < h; i++) \
284                         { \
285                                 input_row = input_rows[i]; \
286                                 output_row = output_rows[i] + (w - 1) * components; \
287                                 for(k = 0; k < w / 2; k++) \
288                                 { \
289                                         SWAPCOPY_PIXELS(components, input_row, output_row); \
290                                         input_row += components; \
291                                         output_row -= components; \
292                                 } \
293                         } \
294                         /* VERTICAL ONLY */ \
295                         for(i = 0, j = h - 1; i < h / 2; i++, j--) \
296                         { \
297                                 input_row = input_rows[i]; \
298                                 output_row = output_rows[j]; \
299                                 for(k = 0; k < w; k++) \
300                                 { \
301                                         SWAPCOPY_PIXELS(components, output_row, input_row); \
302                                         output_row += components; \
303                                         input_row += components; \
304                                 } \
305                         } \
306                 } \
307                 /* HORIZONTAL ONLY and VERTICAL with SWAP */ \
308                 else if(config.mirror_horizontal && config.mirror_vertical && !config.mirror_swaphorizontal && config.mirror_swapvertical) \
309                 { \
310                         /* HORIZONTAL ONLY */ \
311                         for(i = 0; i < h; i++) \
312                         { \
313                                 input_row = input_rows[i]; \
314                                 output_row = output_rows[i] + (w - 1) * components; \
315                                 for(k = 0; k < w / 2; k++) \
316                                 { \
317                                         SWAPCOPY_PIXELS(components, output_row, input_row); \
318                                         input_row += components; \
319                                         output_row -= components; \
320                                 } \
321                         } \
322                         /* VERTICAL SWAP ONLY */ \
323                         for(i = 0, j = h - 1; i < h / 2; i++, j--) \
324                         { \
325                                 input_row = input_rows[i]; \
326                                 output_row = output_rows[j]; \
327                                 for(k = 0; k < w; k++) \
328                                 { \
329                                         SWAPCOPY_PIXELS(components, input_row, output_row); \
330                                         output_row += components; \
331                                         input_row += components; \
332                                 } \
333                         } \
334                 } \
335         } \
336  \
337         else if(config.reflection_center_enable) \
338         { \
339                 if(config.mirror_vertical) \
340                 { \
341                         int y_pixel = (int)round(config.reflection_center_y / 100.00 * h); \
342                         if(config.reflection_center_y < 50.00) \
343                         { \
344                                 int valueY_source = y_pixel + 1; \
345                                 int valueY_destination = y_pixel - 1; \
346                                 for(int i = 0; i < y_pixel; i++) \
347                                 { \
348                                         input_row = input_rows[valueY_source]; \
349                                         output_row = output_rows[valueY_destination]; \
350                                         for(k = 0; k < w; k++) \
351                                         { \
352                                                 SWAPCOPY_PIXELS(components, output_row, input_row); \
353                                                 output_row += components; \
354                                                 input_row += components; \
355                                         } \
356                                         valueY_source += 1; \
357                                         valueY_destination -= 1; \
358                                 } \
359                         } \
360                         else if(config.reflection_center_y > 50.00) \
361                         { \
362                                 int valueY_source = y_pixel - 1; \
363                                 int valueY_destination = y_pixel + 1; \
364                                 for(int i = 0; i < (h - y_pixel - 1); i++) \
365                                 { \
366                                         input_row = input_rows[valueY_source]; \
367                                         output_row = output_rows[valueY_destination]; \
368                                         for(k = 0; k < w; k++) \
369                                         { \
370                                                 SWAPCOPY_PIXELS(components, output_row, input_row); \
371                                                 output_row += components; \
372                                                 input_row += components; \
373                                         } \
374                                         valueY_source -= 1; \
375                                         valueY_destination += 1; \
376                                 } \
377                         } \
378                         else /* Y_PERCENT == 50.00 */ \
379                         { \
380                                 for(i = 0, j = h - 1; i < h / 2; i++, j--) \
381                                 { \
382                                         input_row = input_rows[i]; \
383                                         output_row = output_rows[j]; \
384                                         for(k = 0; k < w; k++) \
385                                         { \
386                                                 SWAPCOPY_PIXELS(components, output_row, input_row); \
387                                                 output_row += components; \
388                                                 input_row += components; \
389                                         } \
390                                 } \
391                         } \
392                 } \
393  \
394                 if(config.mirror_horizontal) \
395                 { \
396                         int x_pixel = (int)round(config.reflection_center_x / 100.00 * w); \
397                         if(config.reflection_center_x < 50.00) \
398                         { \
399                                 int valueX_source = x_pixel + 1; \
400                                 int valueX_destination = x_pixel - 1; \
401                                 for(i = 0; i < h; i++) \
402                                 { \
403                                         input_row = input_rows[i] + valueX_source * components; \
404                                         output_row = output_rows[i] + valueX_destination * components; \
405                                         for(k = 0; k < x_pixel; k++) \
406                                         { \
407                                                 SWAPCOPY_PIXELS(components, output_row, input_row); \
408                                                 output_row -= components; \
409                                                 input_row += components; \
410                                         } \
411                                 } \
412                         } \
413                         else if(config.reflection_center_x > 50.00) \
414                         { \
415                                 int valueX_source = x_pixel - 1; \
416                                 int valueX_destination = x_pixel + 1; \
417                                 for(i = 0; i < h; i++) \
418                                 { \
419                                         input_row = input_rows[i] + valueX_source * components; \
420                                         output_row = output_rows[i] + valueX_destination * components; \
421                                         for(k = 0; k < (w - x_pixel - 1); k++) \
422                                         { \
423                                                 SWAPCOPY_PIXELS(components, output_row, input_row); \
424                                                 output_row += components; \
425                                                 input_row -= components; \
426                                         } \
427                                 } \
428                         } \
429                         else /* X_PERCENT == 50.00 */ \
430                         { \
431                                 for(i = 0; i < h; i++) \
432                                 { \
433                                         input_row = input_rows[i]; \
434                                         output_row = output_rows[i] + (w - 1) * components; \
435                                         for(k = 0; k < w / 2; k++) \
436                                         { \
437                                                 SWAPCOPY_PIXELS(components, output_row, input_row); \
438                                                 input_row += components; \
439                                                 output_row -= components; \
440                                         } \
441                                 } \
442                         } \
443                 } \
444         } \
445 }
446
447 int MirrorMain::process_buffer(VFrame *frame,
448                 int64_t start_position,
449                 double frame_rate)
450 {
451         int i, j, k;
452         int w = frame->get_w();
453         int h = frame->get_h();
454         int colormodel = frame->get_color_model();
455
456         load_configuration();
457
458 // THIS CODE WAS FOR the FLIP Plugin!!!
459 /*
460         read_frame(frame,
461                 0,
462                 get_source_position(),
463                 get_framerate(),
464                 get_use_opengl());
465
466         if(get_use_opengl())
467         {
468                 if(config.mirror_vertical || config.mirror_horizontal)
469                         return run_opengl();
470                 else
471                         return 0;
472         }
473 */
474         read_frame(frame,
475                 0,
476                 start_position,
477                 frame_rate,
478                 get_use_opengl());
479
480         if(config.mirror_vertical || config.mirror_horizontal)
481                 {
482                 switch(colormodel)
483                 {
484                         case BC_RGB888:
485                         case BC_YUV888:
486                                 MIRROR_MACRO(unsigned char, 3);
487                                 break;
488                         case BC_RGB_FLOAT:
489                                 MIRROR_MACRO(float, 3);
490                                 break;
491                         case BC_RGB161616:
492                         case BC_YUV161616:
493                                 MIRROR_MACRO(uint16_t, 3);
494                                 break;
495                         case BC_RGBA8888:
496                         case BC_YUVA8888:
497                                 MIRROR_MACRO(unsigned char, 4);
498                                 break;
499                         case BC_RGBA_FLOAT:
500                                 MIRROR_MACRO(float, 4);
501                                 break;
502                         case BC_RGBA16161616:
503                         case BC_YUVA16161616:
504                                 MIRROR_MACRO(uint16_t, 4);
505                                 break;
506                 }
507         }
508         return 0;
509 }
510
511
512 NEW_WINDOW_MACRO(MirrorMain, MirrorWindow)
513 LOAD_CONFIGURATION_MACRO(MirrorMain, MirrorConfig)
514
515 void MirrorMain::update_gui()
516 {
517         if(thread)
518         {
519                 load_configuration();
520                 thread->window->lock_window();
521                 ((MirrorWindow*)thread->window)->mirror_horizontal->update((int)config.mirror_horizontal);
522                 ((MirrorWindow*)thread->window)->mirror_swaphorizontal->update((int)config.mirror_swaphorizontal);
523                 ((MirrorWindow*)thread->window)->mirror_vertical->update((int)config.mirror_vertical);
524                 ((MirrorWindow*)thread->window)->mirror_swapvertical->update((int)config.mirror_swapvertical);
525                 ((MirrorWindow*)thread->window)->reflection_center_enable->update(config.reflection_center_enable);
526                 ((MirrorWindow*)thread->window)->reflection_center_x_text->update(config.reflection_center_x);
527                 ((MirrorWindow*)thread->window)->reflection_center_x_slider->update(config.reflection_center_x);
528                 ((MirrorWindow*)thread->window)->reflection_center_y_text->update(config.reflection_center_y);
529                 ((MirrorWindow*)thread->window)->reflection_center_y_slider->update(config.reflection_center_y);
530                 // Needed to update Enable-Disable GUI when "Show controls" is pressed.
531                 ((MirrorWindow*)thread->window)->update_reflection_center_enable();
532                 thread->window->unlock_window();
533         }
534 }
535
536 void MirrorMain::save_data(KeyFrame *keyframe)
537 {
538         FileXML output;
539
540 // cause data to be stored directly in text
541         output.set_shared_output(keyframe->xbuf);
542
543 // Store data
544         output.tag.set_title("MIRROR");
545         output.tag.set_property("HORIZONTAL", config.mirror_horizontal);
546         output.tag.set_property("SWAP_HORIZONTAL", config.mirror_swaphorizontal);
547         output.tag.set_property("VERTICAL", config.mirror_vertical);
548         output.tag.set_property("SWAP_VERTICAL", config.mirror_swapvertical);
549         output.tag.set_property("REFLECTION_CENTER_ENABLE", config.reflection_center_enable);
550         output.tag.set_property("REFLECTION_CENTER_X", config.reflection_center_x);
551         output.tag.set_property("REFLECTION_CENTER_Y", config.reflection_center_y);
552         output.append_tag();
553         output.tag.set_title("/MIRROR");
554         output.append_tag();
555         output.append_newline();
556         output.terminate_string();
557 // data is now in *text
558 }
559
560 void MirrorMain::read_data(KeyFrame *keyframe)
561 {
562         FileXML input;
563
564         input.set_shared_input(keyframe->xbuf);
565
566         int result = 0;
567         config.mirror_vertical = config.mirror_horizontal = 0;
568         config.mirror_swapvertical = config.mirror_swaphorizontal = 0;
569
570         while(!result)
571         {
572                 result = input.read_tag();
573
574                 if(!result)
575                 {
576                         if(input.tag.title_is("MIRROR"))
577                         {
578                                 config.mirror_horizontal = input.tag.get_property("HORIZONTAL", config.mirror_horizontal);
579                                 config.mirror_swaphorizontal = input.tag.get_property("SWAP_HORIZONTAL", config.mirror_swaphorizontal);
580                                 config.mirror_vertical = input.tag.get_property("VERTICAL", config.mirror_vertical);
581                                 config.mirror_swapvertical = input.tag.get_property("SWAP_VERTICAL", config.mirror_swapvertical);
582                                 config.reflection_center_enable = input.tag.get_property("REFLECTION_CENTER_ENABLE", config.reflection_center_enable);
583                                 config.reflection_center_x = input.tag.get_property("REFLECTION_CENTER_X", config.reflection_center_x);
584                                 config.reflection_center_y = input.tag.get_property("REFLECTION_CENTER_Y", config.reflection_center_y);
585                         }
586                 }
587         }
588 }
589
590
591 int MirrorMain::handle_opengl()
592 {
593 #ifdef HAVE_GL
594 // FIXIT. When?! ... THIS CODE WAS FOR the FLIP Plugin!!!
595 /*
596         get_output()->to_texture();
597         get_output()->enable_opengl();
598         get_output()->init_screen();
599         get_output()->bind_texture(0);
600
601         if(config.mirror_vertical && !config.mirror_horizontal)
602         {
603                 get_output()->draw_texture(0,
604                         0,
605                         get_output()->get_w(),
606                         get_output()->get_h(),
607                         0,
608                         get_output()->get_h(),
609                         get_output()->get_w(),
610                         0);
611         }
612
613         if(config.mirror_horizontal && !config.mirror_vertical)
614         {
615                 get_output()->draw_texture(0,
616                         0,
617                         get_output()->get_w(),
618                         get_output()->get_h(),
619                         get_output()->get_w(),
620                         0,
621                         0,
622                         get_output()->get_h());
623         }
624
625         if(config.mirror_vertical && config.mirror_horizontal)
626         {
627                 get_output()->draw_texture(0,
628                         0,
629                         get_output()->get_w(),
630                         get_output()->get_h(),
631                         get_output()->get_w(),
632                         get_output()->get_h(),
633                         0,
634                         0);
635         }
636
637         get_output()->set_opengl_state(VFrame::SCREEN);
638 */
639 #endif
640         return 0;
641 }
642
643
644