repair selected_to_clipboard bug
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / photoscale / photoscale.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2011 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 "filexml.h"
24 #include "photoscale.h"
25 #include "bchash.h"
26 #include "keyframe.h"
27 #include "language.h"
28 #include "new.h"
29 #include "theme.h"
30 #include "vframe.h"
31
32 #include <stdint.h>
33 #include <string.h>
34
35 #define MAXBORDER 50
36
37
38
39
40 PhotoScaleWindow::PhotoScaleWindow(PhotoScaleMain *plugin)
41  : PluginClientWindow(plugin,
42         250,
43         200,
44         250,
45         200,
46         0)
47 {
48         this->plugin = plugin;
49 }
50
51 PhotoScaleWindow::~PhotoScaleWindow()
52 {
53 }
54
55 void PhotoScaleWindow::create_objects()
56 {
57         int x = 10, y = 10;
58         BC_Title *title;
59
60         int x2 = x + BC_Title::calculate_w(this, _("Height:")) + plugin->get_theme()->widget_border;
61         add_subwindow(title = new BC_Title(x,
62                 y,
63                 _("Output size:")));
64
65         y += title->get_h() + plugin->get_theme()->widget_border;
66         add_subwindow(title = new BC_Title(x, y, _("Width:")));
67         add_subwindow(output_size[0] = new PhotoScaleSizeText(
68                 plugin,
69                 this,
70                 x2,
71                 y,
72                 100,
73                 &(plugin->config.width)));
74
75         y += output_size[0]->get_h() + plugin->get_theme()->widget_border;
76         add_subwindow(title = new BC_Title(x, y, _("Height:")));
77         add_subwindow(output_size[1] = new PhotoScaleSizeText(
78                 plugin,
79                 this,
80                 x2,
81                 y,
82                 100,
83                 &(plugin->config.height)));
84
85         FrameSizePulldown *pulldown;
86         add_subwindow(pulldown = new FrameSizePulldown(plugin->get_theme(),
87                 output_size[0],
88                 output_size[1],
89                 x + title->get_w() + output_size[1]->get_w() + plugin->get_theme()->widget_border,
90                 y));
91
92         add_subwindow(new PhotoScaleSwapExtents(
93                 plugin,
94                 this,
95                 x + title->get_w() + output_size[1]->get_w() + plugin->get_theme()->widget_border + pulldown->get_w(),
96                 y));
97
98         y += pulldown->get_h() + plugin->get_theme()->widget_border;
99         add_subwindow(file = new PhotoScaleFile(plugin,
100                 this,
101                 x,
102                 y));
103
104         y += file->get_h() + plugin->get_theme()->widget_border;
105         add_subwindow(scan = new PhotoScaleScan(plugin,
106                 this,
107                 x,
108                 y));
109
110         show_window();
111         flush();
112 }
113
114
115
116 PhotoScaleSizeText::PhotoScaleSizeText(PhotoScaleMain *plugin,
117         PhotoScaleWindow *gui,
118         int x,
119         int y,
120         int w,
121         int *output)
122  : BC_TextBox(x, y, w, 1, *output)
123 {
124         this->plugin = plugin;
125         this->gui = gui;
126         this->output = output;
127 }
128
129 PhotoScaleSizeText::~PhotoScaleSizeText()
130 {
131 }
132
133 int PhotoScaleSizeText::handle_event()
134 {
135         *output = atol(get_text());
136         *output /= 2;
137         *output *= 2;
138         if(*output <= 0) *output = 2;
139         if(*output > 10000) *output = 10000;
140         plugin->send_configure_change();
141         return 1;
142 }
143
144
145
146 PhotoScaleFile::PhotoScaleFile(PhotoScaleMain *plugin,
147         PhotoScaleWindow *gui,
148         int x,
149         int y)
150  : BC_Radial(x, y, plugin->config.use_file, _("Override camera"))
151 {
152         this->plugin = plugin;
153         this->gui = gui;
154 }
155
156 int PhotoScaleFile::handle_event()
157 {
158         plugin->config.use_file = 1;
159         gui->scan->update(0);
160         plugin->send_configure_change();
161         return 1;
162 }
163
164
165 PhotoScaleScan::PhotoScaleScan(PhotoScaleMain *plugin,
166         PhotoScaleWindow *gui,
167         int x,
168         int y)
169  : BC_Radial(x, y, !plugin->config.use_file, _("Use alpha/black level"))
170 {
171         this->plugin = plugin;
172         this->gui = gui;
173 }
174
175 int PhotoScaleScan::handle_event()
176 {
177         plugin->config.use_file = 0;
178         gui->file->update(0);
179         plugin->send_configure_change();
180         return 1;
181 }
182
183
184
185
186
187
188
189
190 PhotoScaleSwapExtents::PhotoScaleSwapExtents(PhotoScaleMain *plugin,
191         PhotoScaleWindow *gui,
192         int x,
193         int y)
194  : BC_Button(x, y, plugin->get_theme()->get_image_set("swap_extents"))
195 {
196         this->plugin = plugin;
197         this->gui = gui;
198         set_tooltip(_("Swap dimensions"));
199 }
200
201 int PhotoScaleSwapExtents::handle_event()
202 {
203         int w = plugin->config.width;
204         int h = plugin->config.height;
205         gui->output_size[0]->update((int64_t)h);
206         gui->output_size[1]->update((int64_t)w);
207         plugin->config.width = h;
208         plugin->config.height = w;
209         plugin->send_configure_change();
210         return 1;
211 }
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232 PhotoScaleConfig::PhotoScaleConfig()
233 {
234         width = 640;
235         height = 480;
236         use_file = 1;
237 }
238
239 int PhotoScaleConfig::equivalent(PhotoScaleConfig &that)
240 {
241         return (width == that.width &&
242                 height == that.height &&
243                 use_file == that.use_file);
244 }
245
246 void PhotoScaleConfig::copy_from(PhotoScaleConfig &that)
247 {
248         width = that.width;
249         height = that.height;
250         use_file = that.use_file;
251 }
252
253 void PhotoScaleConfig::interpolate(PhotoScaleConfig &prev,
254         PhotoScaleConfig &next,
255         int64_t prev_frame,
256         int64_t next_frame,
257         int64_t current_frame)
258 {
259         copy_from(next);
260 }
261
262
263
264
265
266
267 REGISTER_PLUGIN(PhotoScaleMain)
268
269
270
271
272
273
274
275
276 PhotoScaleMain::PhotoScaleMain(PluginServer *server)
277  : PluginVClient(server)
278 {
279         need_reconfigure = 1;
280         overlayer = 0;
281         engine = 0;
282 }
283
284 PhotoScaleMain::~PhotoScaleMain()
285 {
286 //printf("PhotoScaleMain::~PhotoScaleMain 1\n");
287         if(overlayer) delete overlayer;
288         if(engine) delete engine;
289 }
290
291 const char* PhotoScaleMain::plugin_title() { return N_("Auto Scale"); }
292 int PhotoScaleMain::is_realtime() { return 1; }
293
294
295 NEW_WINDOW_MACRO(PhotoScaleMain, PhotoScaleWindow)
296
297 LOAD_CONFIGURATION_MACRO(PhotoScaleMain, PhotoScaleConfig)
298
299
300
301
302 int PhotoScaleMain::process_buffer(VFrame *frame,
303         int64_t start_position,
304         double frame_rate)
305 {
306         load_configuration();
307
308         if(config.use_file)
309         {
310                 frame->get_params()->update("AUTOSCALE", 1);
311                 frame->get_params()->update("AUTOSCALE_W", config.width);
312                 frame->get_params()->update("AUTOSCALE_H", config.height);
313                 read_frame(frame,
314                         0,
315                         start_position,
316                         frame_rate,
317                         get_use_opengl());
318                 return 0;
319         }
320         else
321         {
322                 frame->get_params()->update("AUTOSCALE", 0);
323         }
324
325
326         read_frame(frame,
327                 0,
328                 start_position,
329                 frame_rate,
330                 0);
331
332         if(!engine) engine = new PhotoScaleEngine(this,
333                 PluginClient::get_project_smp() + 1);
334         engine->process_packages();
335
336 //      printf("PhotoScaleMain::process_buffer %d %d %d %d %d\n",
337 //              __LINE__,
338 //              engine->top_border,
339 //              engine->bottom_border,
340 //              engine->left_border,
341 //              engine->right_border);
342
343         int in_width = engine->right_border - engine->left_border;
344         int in_height = engine->bottom_border - engine->top_border;
345         if(in_width > 0 &&
346                 in_height > 0 &&
347                 (engine->top_border > 0 ||
348                 engine->left_border > 0 ||
349                 engine->bottom_border < frame->get_h() ||
350                 engine->right_border < frame->get_w()))
351         {
352                 VFrame *temp_frame = new_temp(frame->get_w(),
353                         frame->get_h(),
354                         frame->get_color_model());
355                 temp_frame->copy_from(frame);
356                 if(!overlayer)
357                 {
358                         overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1);
359                 }
360
361                 float scale1 = (float)config.width / in_width;
362                 float scale2 = (float)config.height / in_height;
363                 float out_x1 = 0;
364                 float out_y1 = 0;
365                 float out_x2 = 0;
366                 float out_y2 = 0;
367
368 // printf("PhotoScaleMain::process_buffer %d %d %d %d %d\n",
369 // __LINE__,
370 // engine->left_border,
371 // engine->top_border,
372 // engine->right_border,
373 // engine->bottom_border);
374 // printf("PhotoScaleMain::process_buffer %d %f %f\n", __LINE__, scale1, scale2);
375                 if(scale1 < scale2)
376                 {
377                         out_x1 = (float)frame->get_w() / 2 - config.width / 2;
378                         out_y1 = (float)frame->get_h() / 2 - in_height * scale1 / 2;
379                         out_x2 = (float)frame->get_w() / 2 + config.width / 2;
380                         out_y2 = (float)frame->get_h() / 2 + in_height * scale1 / 2;
381                 }
382                 else
383                 {
384                         out_x1 = (float)frame->get_w() / 2 - in_width * scale2 / 2;
385                         out_y1 = (float)frame->get_h() / 2 - config.height / 2;
386                         out_x2 = (float)frame->get_w() / 2 + in_width * scale2 / 2;
387                         out_y2 = (float)frame->get_h() / 2 + config.height / 2;
388                 }
389
390 // printf("PhotoScaleMain::process_buffer %d %d %d %d %d\n",
391 // __LINE__,
392 // (int)out_x1,
393 // (int)out_y1,
394 // (int)out_x2,
395 // (int)out_y2);
396                 frame->clear_frame();
397                 overlayer->overlay(frame,
398                         temp_frame,
399                         (float)engine->left_border,
400                         (float)engine->top_border,
401                         (float)engine->right_border,
402                         (float)engine->bottom_border,
403                         (float)out_x1,
404                         (float)out_y1,
405                         (float)out_x2,
406                         (float)out_y2,
407                         1,
408                         TRANSFER_REPLACE,
409                         get_interpolation_type());
410
411         }
412
413         return 0;
414 }
415
416
417 void PhotoScaleMain::update_gui()
418 {
419         if(thread)
420         {
421                 int reconfigure = load_configuration();
422                 if(reconfigure)
423                 {
424                         PhotoScaleWindow *window = (PhotoScaleWindow*)thread->window;
425                         window->lock_window("PhotoScaleMain::update_gui");
426                         window->output_size[0]->update((int64_t)config.width);
427                         window->output_size[1]->update((int64_t)config.height);
428                         window->file->update(config.use_file);
429                         window->scan->update(!config.use_file);
430                         window->unlock_window();
431                 }
432         }
433 }
434
435
436
437 void PhotoScaleMain::save_data(KeyFrame *keyframe)
438 {
439         FileXML output;
440
441 // cause data to be stored directly in text
442         output.set_shared_output(keyframe->xbuf);
443         output.tag.set_title("PHOTOSCALE");
444         output.tag.set_property("WIDTH", config.width);
445         output.tag.set_property("HEIGHT", config.height);
446         output.tag.set_property("USE_FILE", config.use_file);
447         output.append_tag();
448         output.tag.set_title("/PHOTOSCALE");
449         output.append_tag();
450         output.append_newline();
451         output.terminate_string();
452 }
453
454 void PhotoScaleMain::read_data(KeyFrame *keyframe)
455 {
456         FileXML input;
457
458         input.set_shared_input(keyframe->xbuf);
459
460         int result = 0;
461
462         while(!result)
463         {
464                 result = input.read_tag();
465
466                 if(!result)
467                 {
468                         if(input.tag.title_is("PHOTOSCALE"))
469                         {
470                                 config.width = input.tag.get_property("WIDTH", config.width);
471                                 config.height = input.tag.get_property("HEIGHT", config.height);
472                                 config.use_file = input.tag.get_property("USE_FILE", config.use_file);
473                         }
474                 }
475         }
476 }
477
478
479
480
481
482 PhotoScaleEngine::PhotoScaleEngine(PhotoScaleMain *plugin, int cpus)
483  : LoadServer(cpus, 4)
484 {
485         this->plugin = plugin;
486 }
487
488 PhotoScaleEngine::~PhotoScaleEngine()
489 {
490 }
491
492 void PhotoScaleEngine::init_packages()
493 {
494         for(int i = 0; i < get_total_packages(); i++)
495         {
496                 PhotoScalePackage *package = (PhotoScalePackage*)get_package(i);
497                 package->side = i;
498         }
499
500         top_border = 0;
501         bottom_border = 0;
502         left_border = 0;
503         right_border = 0;
504 }
505
506 LoadClient* PhotoScaleEngine::new_client()
507 {
508         return new PhotoScaleUnit(this);
509 }
510
511 LoadPackage* PhotoScaleEngine::new_package()
512 {
513         return new PhotoScalePackage;
514 }
515
516
517
518
519
520
521
522 PhotoScalePackage::PhotoScalePackage()
523  : LoadPackage()
524 {
525         side = 0;
526 }
527
528
529
530
531
532
533
534
535 PhotoScaleUnit::PhotoScaleUnit(PhotoScaleEngine *server)
536  : LoadClient(server)
537 {
538         this->server = server;
539 }
540
541 PhotoScaleUnit::~PhotoScaleUnit()
542 {
543 }
544
545 #define TEST_PIXEL(components, is_yuv) \
546         (components == 4 && pixel[3] > 0) || \
547         (components == 3 && is_yuv && pixel[0] > 0) || \
548         (components == 3 && !is_yuv && (pixel[0] > 0 || pixel[1] > 0 || pixel[2] > 0))
549
550 #define SCAN_BORDER(type, components, is_yuv) \
551 { \
552         type **rows = (type**)input->get_rows(); \
553         switch(pkg->side) \
554         { \
555 /* top */ \
556                 case 0: \
557                         for(int i = 0; i < h / 2; i++) \
558                         { \
559                                 type *row = rows[i]; \
560                                 for(int j = 0; j < w; j++) \
561                                 { \
562                                         type *pixel = row + j * components; \
563                                         if(TEST_PIXEL(components, is_yuv)) \
564                                         { \
565                                                 server->top_border = i; \
566                                                 j = w; \
567                                                 i = h; \
568                                         } \
569                                 } \
570                         } \
571                         break; \
572  \
573 /* bottom */ \
574                 case 1: \
575                         for(int i = h - 1; i >= h / 2; i--) \
576                         { \
577                                 type *row = rows[i]; \
578                                 for(int j = 0; j < w; j++) \
579                                 { \
580                                         type *pixel = row + j * components; \
581                                         if(TEST_PIXEL(components, is_yuv)) \
582                                         { \
583                                                 server->bottom_border = i + 1; \
584                                                 j = w; \
585                                                 i = 0; \
586                                         } \
587                                 } \
588                         } \
589                         break; \
590  \
591 /* left */ \
592                 case 2: \
593                         for(int i = 0; i < w / 2; i++) \
594                         { \
595                                 for(int j = 0; j < h; j++) \
596                                 { \
597                                         type *row = rows[j]; \
598                                         type *pixel = row + i * components; \
599                                         if(TEST_PIXEL(components, is_yuv)) \
600                                         { \
601                                                 server->left_border = i; \
602                                                 j = h; \
603                                                 i = w; \
604                                         } \
605                                 } \
606                         } \
607  \
608 /* right */ \
609                 case 3: \
610                         for(int i = w - 1; i >= w / 2; i--) \
611                         { \
612                                 for(int j = 0; j < h; j++) \
613                                 { \
614                                         type *row = rows[j]; \
615                                         type *pixel = row + i * components; \
616                                         if(TEST_PIXEL(components, is_yuv)) \
617                                         { \
618                                                 server->right_border = i + 1; \
619                                                 j = h; \
620                                                 i = 0; \
621                                         } \
622                                 } \
623                         } \
624         } \
625 }
626
627 void PhotoScaleUnit::process_package(LoadPackage *package)
628 {
629         PhotoScalePackage *pkg = (PhotoScalePackage*)package;
630         VFrame *input = server->plugin->get_input();
631         int w = input->get_w();
632         int h = input->get_h();
633
634
635         switch(input->get_color_model())
636         {
637                 case BC_RGB_FLOAT:
638                         SCAN_BORDER(float, 3, 0);
639                         break;
640                 case BC_RGBA_FLOAT:
641                         SCAN_BORDER(float, 4, 0);
642                         break;
643                 case BC_RGB888:
644                         SCAN_BORDER(unsigned char, 3, 0);
645                         break;
646                 case BC_YUV888:
647                         SCAN_BORDER(unsigned char, 3, 1);
648                         break;
649                 case BC_RGBA8888:
650                 case BC_YUVA8888:
651                         SCAN_BORDER(unsigned char, 4, 0);
652                         break;
653         }
654 }
655
656
657
658
659
660
661
662
663
664
665
666
667