update internationalization data
[goodguy/history.git] / cinelerra-5.0 / plugins / sharpen / sharpen.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 "clip.h"
23 #include "colormodels.h"
24 #include "condition.h"
25 #include "filexml.h"
26 #include "language.h"
27 #include "sharpen.h"
28 #include "sharpenwindow.h"
29
30 #include <stdio.h>
31 #include <string.h>
32
33 REGISTER_PLUGIN(SharpenMain)
34
35
36
37
38
39
40
41 SharpenConfig::SharpenConfig()
42 {
43         horizontal = 0;
44         interlace = 0;
45         sharpness = 50;
46         luminance = 0;
47 }
48
49 void SharpenConfig::copy_from(SharpenConfig &that)
50 {
51         horizontal = that.horizontal;
52         interlace = that.interlace;
53         sharpness = that.sharpness;
54         luminance = that.luminance;
55 }
56
57 int SharpenConfig::equivalent(SharpenConfig &that)
58 {
59         return horizontal == that.horizontal &&
60                 interlace == that.interlace &&
61                 EQUIV(sharpness, that.sharpness) &&
62                 luminance == that.luminance;
63 }
64
65 void SharpenConfig::interpolate(SharpenConfig &prev, 
66         SharpenConfig &next, 
67         long prev_frame, 
68         long next_frame, 
69         long current_frame)
70 {
71         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
72         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
73         this->sharpness = prev.sharpness * prev_scale + next.sharpness * next_scale;
74         this->interlace = prev.interlace;
75         this->horizontal = prev.horizontal;
76         this->luminance = prev.luminance;
77 }
78
79
80
81
82
83
84
85
86
87
88
89 SharpenMain::SharpenMain(PluginServer *server)
90  : PluginVClient(server)
91 {
92         
93         engine = 0;
94 }
95
96 SharpenMain::~SharpenMain()
97 {
98         
99
100         if(engine)
101         {
102                 for(int i = 0; i < total_engines; i++)
103                 {
104                         delete engine[i];
105                 }
106                 delete engine;
107         }
108 }
109
110 NEW_WINDOW_MACRO(SharpenMain, SharpenWindow)
111
112
113 LOAD_CONFIGURATION_MACRO(SharpenMain, SharpenConfig)
114
115 const char* SharpenMain::plugin_title() { return _("Sharpen"); }
116 int SharpenMain::is_realtime() { return 1; }
117
118
119
120 int SharpenMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
121 {
122         int j, k;
123         output = output_ptr;
124         input = input_ptr;
125
126         load_configuration();
127         if(!engine)
128         {
129
130                 total_engines = PluginClient::smp > 1 ? 2 : 1;
131                 engine = new SharpenEngine*[total_engines];
132                 for(int i = 0; i < total_engines; i++)
133                 {
134                         engine[i] = new SharpenEngine(this);
135                         engine[i]->start();
136                 }
137         }
138
139         get_luts(pos_lut, neg_lut, input_ptr->get_color_model());
140
141         if(config.sharpness != 0)
142         {
143 // Arm first row
144                 row_step = (config.interlace /* || config.horizontal */) ? 2 : 1;
145
146                 for(j = 0; j < row_step; j += total_engines)
147                 {
148                         for(k = 0; k < total_engines && k + j < row_step; k++)
149                         {
150                                 engine[k]->start_process_frame(input_ptr, input_ptr, k + j);
151                         }
152                         for(k = 0; k < total_engines && k + j < row_step; k++)
153                         {
154                                 engine[k]->wait_process_frame();
155                         }
156                 }
157         }
158         else
159         if(input_ptr->get_rows()[0] != output_ptr->get_rows()[0])
160         {
161                 output_ptr->copy_from(input_ptr);
162         }
163         return 0;
164 }
165
166 void SharpenMain::update_gui()
167 {
168         if(thread)
169         {
170                 load_configuration();
171                 thread->window->lock_window("SharpenMain::update_gui");
172                 ((SharpenWindow*)thread->window)->sharpen_slider->update((int)config.sharpness);
173                 ((SharpenWindow*)thread->window)->sharpen_interlace->update(config.interlace);
174                 ((SharpenWindow*)thread->window)->sharpen_horizontal->update(config.horizontal);
175                 ((SharpenWindow*)thread->window)->sharpen_luminance->update(config.luminance);
176                 thread->window->unlock_window();
177         }
178 }
179
180
181
182
183 int SharpenMain::get_luts(int *pos_lut, int *neg_lut, int color_model)
184 {
185         int i, inv_sharpness, vmax;
186
187         vmax = cmodel_calculate_max(color_model);
188
189         inv_sharpness = (int)(100 - config.sharpness);
190         if(config.horizontal) inv_sharpness /= 2;
191         if(inv_sharpness < 1) inv_sharpness = 1;
192
193         for(i = 0; i < vmax + 1; i++)
194         {
195                 pos_lut[i] = 800 * i / inv_sharpness;
196                 neg_lut[i] = (4 + pos_lut[i] - (i << 3)) >> 3;
197         }
198
199         return 0;
200 }
201
202 void SharpenMain::save_data(KeyFrame *keyframe)
203 {
204         FileXML output;
205
206 // cause data to be stored directly in text
207         output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
208         output.tag.set_title("SHARPNESS");
209         output.tag.set_property("VALUE", config.sharpness);
210         output.append_tag();
211
212         if(config.interlace)
213         {
214                 output.tag.set_title("INTERLACE");
215                 output.append_tag();
216         }
217
218         if(config.horizontal)
219         {
220                 output.tag.set_title("HORIZONTAL");
221                 output.append_tag();
222         }
223
224         if(config.luminance)
225         {
226                 output.tag.set_title("LUMINANCE");
227                 output.append_tag();
228         }
229         output.terminate_string();
230 }
231
232 void SharpenMain::read_data(KeyFrame *keyframe)
233 {
234         FileXML input;
235
236         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
237
238         int result = 0;
239         int new_interlace = 0;
240         int new_horizontal = 0;
241         int new_luminance = 0;
242
243         while(!result)
244         {
245                 result = input.read_tag();
246
247                 if(!result)
248                 {
249                         if(input.tag.title_is("SHARPNESS"))
250                         {
251                                 config.sharpness = input.tag.get_property("VALUE", config.sharpness);
252 //printf("SharpenMain::read_data %f\n", sharpness);
253                         }
254                         else
255                         if(input.tag.title_is("INTERLACE"))
256                         {
257                                 new_interlace = 1;
258                         }
259                         else
260                         if(input.tag.title_is("HORIZONTAL"))
261                         {
262                                 new_horizontal = 1;
263                         }
264                         else
265                         if(input.tag.title_is("LUMINANCE"))
266                         {
267                                 new_luminance = 1;
268                         }
269                 }
270         }
271
272         config.interlace = new_interlace;
273         config.horizontal = new_horizontal;
274         config.luminance = new_luminance;
275
276         if(config.sharpness > MAXSHARPNESS) 
277                 config.sharpness = MAXSHARPNESS;
278         else
279                 if(config.sharpness < 0) config.sharpness = 0;
280 }
281
282
283
284
285 SharpenEngine::SharpenEngine(SharpenMain *plugin)
286  : Thread(1, 0, 0)
287 {
288         this->plugin = plugin;
289         input_lock = new Condition(0,"SharpenEngine::input_lock");
290         output_lock = new Condition(0, "SharpenEngine::output_lock");
291         last_frame = 0;
292         for(int i = 0; i < 4; i++)
293         {
294                 neg_rows[i] = new unsigned char[plugin->input->get_w() * 
295                         4 * 
296                         MAX(sizeof(float), sizeof(int))];
297         }
298 }
299
300 SharpenEngine::~SharpenEngine()
301 {
302         last_frame = 1;
303         input_lock->unlock();
304         Thread::join();
305
306         for(int i = 0; i < 4; i++)
307         {
308                 delete [] neg_rows[i];
309         }
310         delete input_lock;
311         delete output_lock;
312 }
313
314 int SharpenEngine::start_process_frame(VFrame *output, VFrame *input, int field)
315 {
316         this->output = output;
317         this->input = input;
318         this->field = field;
319
320 // Get coefficient for floating point
321         sharpness_coef = 100 - plugin->config.sharpness;
322         if(plugin->config.horizontal) sharpness_coef /= 2;
323         if(sharpness_coef < 1) sharpness_coef = 1;
324         sharpness_coef = 800.0 / sharpness_coef;
325         
326         input_lock->unlock();
327         return 0;
328 }
329
330 int SharpenEngine::wait_process_frame()
331 {
332         output_lock->lock("SharpenEngine::wait_process_frame");
333         return 0;
334 }
335
336 float SharpenEngine::calculate_pos(float value)
337 {
338         return sharpness_coef * value;
339 }
340
341 float SharpenEngine::calculate_neg(float value)
342 {
343         return (calculate_pos(value) - (value * 8)) / 8;
344 }
345
346 #define FILTER(components, vmax) \
347 { \
348         int *pos_lut = plugin->pos_lut; \
349         const int wordsize = sizeof(*src); \
350  \
351 /* Skip first pixel in row */ \
352         memcpy(dst, src, components * wordsize); \
353         dst += components; \
354         src += components; \
355  \
356         w -= 2; \
357  \
358         while(w > 0) \
359         { \
360                 long pixel; \
361                 pixel = (long)pos_lut[src[0]] -  \
362                         (long)neg0[-components] -  \
363                         (long)neg0[0] -  \
364                         (long)neg0[components] -  \
365                         (long)neg1[-components] -  \
366                         (long)neg1[components] -  \
367                         (long)neg2[-components] -  \
368                         (long)neg2[0] -  \
369                         (long)neg2[components]; \
370                 pixel = (pixel + 4) >> 3; \
371                 if(pixel < 0) dst[0] = 0; \
372                 else \
373                 if(pixel > vmax) dst[0] = vmax; \
374                 else \
375                 dst[0] = pixel; \
376  \
377                 pixel = (long)pos_lut[src[1]] -  \
378                         (long)neg0[-components + 1] -  \
379                         (long)neg0[1] -  \
380                         (long)neg0[components + 1] -  \
381                         (long)neg1[-components + 1] -  \
382                         (long)neg1[components + 1] -  \
383                         (long)neg2[-components + 1] -  \
384                         (long)neg2[1] -  \
385                         (long)neg2[components + 1]; \
386                 pixel = (pixel + 4) >> 3; \
387                 if(pixel < 0) dst[1] = 0; \
388                 else \
389                 if(pixel > vmax) dst[1] = vmax; \
390                 else \
391                 dst[1] = pixel; \
392  \
393                 pixel = (long)pos_lut[src[2]] -  \
394                         (long)neg0[-components + 2] -  \
395                         (long)neg0[2] -  \
396                         (long)neg0[components + 2] -  \
397                         (long)neg1[-components + 2] -  \
398                         (long)neg1[components + 2] -  \
399                         (long)neg2[-components + 2] -  \
400                         (long)neg2[2] -  \
401                         (long)neg2[components + 2]; \
402                 pixel = (pixel + 4) >> 3; \
403                 if(pixel < 0) dst[2] = 0; \
404                 else \
405                 if(pixel > vmax) dst[2] = vmax; \
406                 else \
407                 dst[2] = pixel; \
408  \
409                 src += components; \
410                 dst += components; \
411  \
412                 neg0 += components; \
413                 neg1 += components; \
414                 neg2 += components; \
415                 w--; \
416         } \
417  \
418 /* Skip last pixel in row */ \
419         memcpy(dst, src, components * wordsize); \
420 }
421
422 void SharpenEngine::filter(int components,
423         int vmax,
424         int w, 
425         u_int16_t *src, 
426         u_int16_t *dst,
427         int *neg0, 
428         int *neg1, 
429         int *neg2)
430 {
431         FILTER(components, vmax);
432 }
433
434 void SharpenEngine::filter(int components,
435         int vmax,
436         int w, 
437         unsigned char *src, 
438         unsigned char *dst,
439         int *neg0, 
440         int *neg1, 
441         int *neg2)
442 {
443         FILTER(components, vmax);
444 }
445
446 void SharpenEngine::filter(int components,
447         int vmax,
448         int w, 
449         float *src, 
450         float *dst,
451         float *neg0, 
452         float *neg1, 
453         float *neg2)
454 {
455         const int wordsize = sizeof(float);
456 // First pixel in row
457         memcpy(dst, src, components * wordsize);
458         dst += components;
459         src += components;
460
461         w -= 2;
462         while(w > 0)
463         {
464                 float pixel;
465                 pixel = calculate_pos(src[0]) -
466                         neg0[-components] -
467                         neg0[0] - 
468                         neg0[components] -
469                         neg1[-components] -
470                         neg1[components] -
471                         neg2[-components] -
472                         neg2[0] -
473                         neg2[components];
474                 pixel /= 8;
475                 dst[0] = pixel;
476
477                 pixel = calculate_pos(src[1]) -
478                         neg0[-components + 1] -
479                         neg0[1] - 
480                         neg0[components + 1] -
481                         neg1[-components + 1] -
482                         neg1[components + 1] -
483                         neg2[-components + 1] -
484                         neg2[1] -
485                         neg2[components + 1];
486                 pixel /= 8;
487                 dst[1] = pixel;
488
489                 pixel = calculate_pos(src[2]) -
490                         neg0[-components + 2] -
491                         neg0[2] - 
492                         neg0[components + 2] -
493                         neg1[-components + 2] -
494                         neg1[components + 2] -
495                         neg2[-components + 2] -
496                         neg2[2] -
497                         neg2[components + 2];
498                 pixel /= 8;
499                 dst[2] = pixel;
500
501                 src += components;
502                 dst += components;
503                 neg0 += components;
504                 neg1 += components;
505                 neg2 += components;
506                 w--;
507         }
508
509 /* Last pixel */
510         memcpy(dst, src, components * wordsize);
511 }
512
513
514
515
516
517
518
519 #define SHARPEN(components, type, temp_type, vmax) \
520 { \
521         int count, row; \
522         int wordsize = sizeof(type); \
523         unsigned char **input_rows, **output_rows; \
524         int w = plugin->input->get_w(); \
525         int h = plugin->input->get_h(); \
526  \
527         input_rows = input->get_rows(); \
528         output_rows = output->get_rows(); \
529         src_rows[0] = input_rows[field]; \
530         src_rows[1] = input_rows[field]; \
531         src_rows[2] = input_rows[field]; \
532         src_rows[3] = input_rows[field]; \
533  \
534         for(int j = 0; j < w; j++) \
535         { \
536                 temp_type *neg = (temp_type*)neg_rows[0]; \
537                 type *src = (type*)src_rows[0]; \
538                 for(int k = 0; k < components; k++) \
539                 { \
540                         if(wordsize == 4) \
541                         { \
542                                 neg[j * components + k] = \
543                                         (temp_type)calculate_neg(src[j * components + k]); \
544                         } \
545                         else \
546                         { \
547                                 neg[j * components + k] = \
548                                         (temp_type)plugin->neg_lut[(int)src[j * components + k]]; \
549                         } \
550                 } \
551         } \
552  \
553         row = 1; \
554         count = 1; \
555  \
556         for(int i = field; i < h; i += plugin->row_step) \
557         { \
558                 if((i + plugin->row_step) < h) \
559                 { \
560                         if(count >= 3) count--; \
561 /* Arm next row */ \
562                         src_rows[row] = input_rows[i + plugin->row_step]; \
563 /* Calculate neg rows */ \
564                         type *src = (type*)src_rows[row]; \
565                         temp_type *neg = (temp_type*)neg_rows[row]; \
566                         for(int k = 0; k < w; k++) \
567                         { \
568                                 for(int j = 0; j < components; j++) \
569                                 { \
570                                         if(wordsize == 4) \
571                                         { \
572                                                 neg[k * components + j] = \
573                                                         (temp_type)calculate_neg(src[k * components + j]); \
574                                         } \
575                                         else \
576                                         { \
577                                                 neg[k * components + j] = \
578                                                         plugin->neg_lut[(int)src[k * components + j]]; \
579                                         } \
580                                 } \
581                         } \
582  \
583                         count++; \
584                         row = (row + 1) & 3; \
585                 } \
586                 else \
587                 { \
588                         count--; \
589                 } \
590  \
591                 dst_row = output_rows[i]; \
592                 if(count == 3) \
593                 { \
594 /* Do the filter */ \
595                         if(plugin->config.horizontal) \
596                                 filter(components, \
597                                         vmax, \
598                                         w,  \
599                                         (type*)src_rows[(row + 2) & 3],  \
600                                         (type*)dst_row, \
601                                         (temp_type*)neg_rows[(row + 2) & 3] + components, \
602                                         (temp_type*)neg_rows[(row + 2) & 3] + components, \
603                                         (temp_type*)neg_rows[(row + 2) & 3] + components); \
604                         else \
605                                 filter(components, \
606                                         vmax, \
607                                         w,  \
608                                         (type*)src_rows[(row + 2) & 3],  \
609                                         (type*)dst_row, \
610                                         (temp_type*)neg_rows[(row + 1) & 3] + components, \
611                                         (temp_type*)neg_rows[(row + 2) & 3] + components, \
612                                         (temp_type*)neg_rows[(row + 3) & 3] + components); \
613                 } \
614                 else  \
615                 if(count == 2) \
616                 { \
617                         if(i == 0) \
618                                 memcpy(dst_row, src_rows[0], w * components * wordsize); \
619                         else \
620                                 memcpy(dst_row, src_rows[2], w * components * wordsize); \
621                 } \
622         } \
623 }
624
625
626
627 void SharpenEngine::run()
628 {
629         while(1)
630         {
631                 input_lock->lock("SharpenEngine::run");
632                 if(last_frame)
633                 {
634                         output_lock->unlock();
635                         return;
636                 }
637
638
639                 switch(input->get_color_model())
640                 {
641                         case BC_RGB_FLOAT:
642                                 SHARPEN(3, float, float, 1);
643                                 break;
644
645                         case BC_RGB888:
646                         case BC_YUV888:
647                                 SHARPEN(3, unsigned char, int, 0xff);
648                                 break;
649                         
650                         case BC_RGBA_FLOAT:
651                                 SHARPEN(4, float, float, 1);
652                                 break;
653
654                         case BC_RGBA8888:
655                         case BC_YUVA8888:
656                                 SHARPEN(4, unsigned char, int, 0xff);
657                                 break;
658                         
659                         case BC_RGB161616:
660                         case BC_YUV161616:
661                                 SHARPEN(3, u_int16_t, int, 0xffff);
662                                 break;
663                         
664                         case BC_RGBA16161616:
665                         case BC_YUVA16161616:
666                                 SHARPEN(4, u_int16_t, int, 0xffff);
667                                 break;
668                 }
669
670                 output_lock->unlock();
671         }
672 }
673