build version update
[goodguy/history.git] / cinelerra-5.1 / 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 "bccmodels.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 N_("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 = BC_CModels::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.tag.set_property("INTERLACE", config.interlace);
211         output.tag.set_property("HORIZONTAL", config.horizontal);
212         output.tag.set_property("LUMINANCE", config.luminance);
213         output.append_tag();
214         output.tag.set_title("/SHARPNESS");
215         output.append_tag();
216         output.append_newline();
217         output.terminate_string();
218 }
219
220 void SharpenMain::read_data(KeyFrame *keyframe)
221 {
222         FileXML input;
223
224         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
225
226         int result = 0;
227
228         while(!result)
229         {
230                 result = input.read_tag();
231
232                 if(!result)
233                 {
234                         if(input.tag.title_is("SHARPNESS"))
235                         {
236                                 config.sharpness = input.tag.get_property("VALUE", config.sharpness);
237                                 config.interlace = input.tag.get_property("INTERLACE", config.interlace);
238                                 config.horizontal = input.tag.get_property("HORIZONTAL", config.horizontal);
239                                 config.luminance = input.tag.get_property("LUMINANCE", config.luminance);
240 //printf("SharpenMain::read_data %f\n", sharpness);
241                         }
242                 }
243         }
244
245         if(config.sharpness > MAXSHARPNESS)
246                 config.sharpness = MAXSHARPNESS;
247         else
248                 if(config.sharpness < 0) config.sharpness = 0;
249 }
250
251
252
253
254 SharpenEngine::SharpenEngine(SharpenMain *plugin)
255  : Thread(1, 0, 0)
256 {
257         this->plugin = plugin;
258         input_lock = new Condition(0,"SharpenEngine::input_lock");
259         output_lock = new Condition(0, "SharpenEngine::output_lock");
260         last_frame = 0;
261         for(int i = 0; i < 4; i++)
262         {
263                 neg_rows[i] = new unsigned char[plugin->input->get_w() *
264                         4 *
265                         MAX(sizeof(float), sizeof(int))];
266         }
267 }
268
269 SharpenEngine::~SharpenEngine()
270 {
271         last_frame = 1;
272         input_lock->unlock();
273         Thread::join();
274
275         for(int i = 0; i < 4; i++)
276         {
277                 delete [] neg_rows[i];
278         }
279         delete input_lock;
280         delete output_lock;
281 }
282
283 int SharpenEngine::start_process_frame(VFrame *output, VFrame *input, int field)
284 {
285         this->output = output;
286         this->input = input;
287         this->field = field;
288
289 // Get coefficient for floating point
290         sharpness_coef = 100 - plugin->config.sharpness;
291         if(plugin->config.horizontal) sharpness_coef /= 2;
292         if(sharpness_coef < 1) sharpness_coef = 1;
293         sharpness_coef = 800.0 / sharpness_coef;
294
295         input_lock->unlock();
296         return 0;
297 }
298
299 int SharpenEngine::wait_process_frame()
300 {
301         output_lock->lock("SharpenEngine::wait_process_frame");
302         return 0;
303 }
304
305 float SharpenEngine::calculate_pos(float value)
306 {
307         return sharpness_coef * value;
308 }
309
310 float SharpenEngine::calculate_neg(float value)
311 {
312         return (calculate_pos(value) - (value * 8)) / 8;
313 }
314
315 #define FILTER(components, vmax) \
316 { \
317         int *pos_lut = plugin->pos_lut; \
318         const int wordsize = sizeof(*src); \
319  \
320 /* Skip first pixel in row */ \
321         memcpy(dst, src, components * wordsize); \
322         dst += components; \
323         src += components; \
324  \
325         w -= 2; \
326  \
327         while(w > 0) \
328         { \
329                 long pixel; \
330                 pixel = (long)pos_lut[src[0]] -  \
331                         (long)neg0[-components] -  \
332                         (long)neg0[0] -  \
333                         (long)neg0[components] -  \
334                         (long)neg1[-components] -  \
335                         (long)neg1[components] -  \
336                         (long)neg2[-components] -  \
337                         (long)neg2[0] -  \
338                         (long)neg2[components]; \
339                 pixel = (pixel + 4) >> 3; \
340                 if(pixel < 0) dst[0] = 0; \
341                 else \
342                 if(pixel > vmax) dst[0] = vmax; \
343                 else \
344                 dst[0] = pixel; \
345  \
346                 pixel = (long)pos_lut[src[1]] -  \
347                         (long)neg0[-components + 1] -  \
348                         (long)neg0[1] -  \
349                         (long)neg0[components + 1] -  \
350                         (long)neg1[-components + 1] -  \
351                         (long)neg1[components + 1] -  \
352                         (long)neg2[-components + 1] -  \
353                         (long)neg2[1] -  \
354                         (long)neg2[components + 1]; \
355                 pixel = (pixel + 4) >> 3; \
356                 if(pixel < 0) dst[1] = 0; \
357                 else \
358                 if(pixel > vmax) dst[1] = vmax; \
359                 else \
360                 dst[1] = pixel; \
361  \
362                 pixel = (long)pos_lut[src[2]] -  \
363                         (long)neg0[-components + 2] -  \
364                         (long)neg0[2] -  \
365                         (long)neg0[components + 2] -  \
366                         (long)neg1[-components + 2] -  \
367                         (long)neg1[components + 2] -  \
368                         (long)neg2[-components + 2] -  \
369                         (long)neg2[2] -  \
370                         (long)neg2[components + 2]; \
371                 pixel = (pixel + 4) >> 3; \
372                 if(pixel < 0) dst[2] = 0; \
373                 else \
374                 if(pixel > vmax) dst[2] = vmax; \
375                 else \
376                 dst[2] = pixel; \
377  \
378                 src += components; \
379                 dst += components; \
380  \
381                 neg0 += components; \
382                 neg1 += components; \
383                 neg2 += components; \
384                 w--; \
385         } \
386  \
387 /* Skip last pixel in row */ \
388         memcpy(dst, src, components * wordsize); \
389 }
390
391 void SharpenEngine::filter(int components,
392         int vmax,
393         int w,
394         u_int16_t *src,
395         u_int16_t *dst,
396         int *neg0,
397         int *neg1,
398         int *neg2)
399 {
400         FILTER(components, vmax);
401 }
402
403 void SharpenEngine::filter(int components,
404         int vmax,
405         int w,
406         unsigned char *src,
407         unsigned char *dst,
408         int *neg0,
409         int *neg1,
410         int *neg2)
411 {
412         FILTER(components, vmax);
413 }
414
415 void SharpenEngine::filter(int components,
416         int vmax,
417         int w,
418         float *src,
419         float *dst,
420         float *neg0,
421         float *neg1,
422         float *neg2)
423 {
424         const int wordsize = sizeof(float);
425 // First pixel in row
426         memcpy(dst, src, components * wordsize);
427         dst += components;
428         src += components;
429
430         w -= 2;
431         while(w > 0)
432         {
433                 float pixel;
434                 pixel = calculate_pos(src[0]) -
435                         neg0[-components] -
436                         neg0[0] -
437                         neg0[components] -
438                         neg1[-components] -
439                         neg1[components] -
440                         neg2[-components] -
441                         neg2[0] -
442                         neg2[components];
443                 pixel /= 8;
444                 dst[0] = pixel;
445
446                 pixel = calculate_pos(src[1]) -
447                         neg0[-components + 1] -
448                         neg0[1] -
449                         neg0[components + 1] -
450                         neg1[-components + 1] -
451                         neg1[components + 1] -
452                         neg2[-components + 1] -
453                         neg2[1] -
454                         neg2[components + 1];
455                 pixel /= 8;
456                 dst[1] = pixel;
457
458                 pixel = calculate_pos(src[2]) -
459                         neg0[-components + 2] -
460                         neg0[2] -
461                         neg0[components + 2] -
462                         neg1[-components + 2] -
463                         neg1[components + 2] -
464                         neg2[-components + 2] -
465                         neg2[2] -
466                         neg2[components + 2];
467                 pixel /= 8;
468                 dst[2] = pixel;
469
470                 src += components;
471                 dst += components;
472                 neg0 += components;
473                 neg1 += components;
474                 neg2 += components;
475                 w--;
476         }
477
478 /* Last pixel */
479         memcpy(dst, src, components * wordsize);
480 }
481
482
483
484
485
486
487
488 #define SHARPEN(components, type, temp_type, vmax) \
489 { \
490         int count, row; \
491         int wordsize = sizeof(type); \
492         unsigned char **input_rows, **output_rows; \
493         int w = plugin->input->get_w(); \
494         int h = plugin->input->get_h(); \
495  \
496         input_rows = input->get_rows(); \
497         output_rows = output->get_rows(); \
498         src_rows[0] = input_rows[field]; \
499         src_rows[1] = input_rows[field]; \
500         src_rows[2] = input_rows[field]; \
501         src_rows[3] = input_rows[field]; \
502  \
503         for(int j = 0; j < w; j++) \
504         { \
505                 temp_type *neg = (temp_type*)neg_rows[0]; \
506                 type *src = (type*)src_rows[0]; \
507                 for(int k = 0; k < components; k++) \
508                 { \
509                         if(wordsize == 4) \
510                         { \
511                                 neg[j * components + k] = \
512                                         (temp_type)calculate_neg(src[j * components + k]); \
513                         } \
514                         else \
515                         { \
516                                 neg[j * components + k] = \
517                                         (temp_type)plugin->neg_lut[(int)src[j * components + k]]; \
518                         } \
519                 } \
520         } \
521  \
522         row = 1; \
523         count = 1; \
524  \
525         for(int i = field; i < h; i += plugin->row_step) \
526         { \
527                 if((i + plugin->row_step) < h) \
528                 { \
529                         if(count >= 3) count--; \
530 /* Arm next row */ \
531                         src_rows[row] = input_rows[i + plugin->row_step]; \
532 /* Calculate neg rows */ \
533                         type *src = (type*)src_rows[row]; \
534                         temp_type *neg = (temp_type*)neg_rows[row]; \
535                         for(int k = 0; k < w; k++) \
536                         { \
537                                 for(int j = 0; j < components; j++) \
538                                 { \
539                                         if(wordsize == 4) \
540                                         { \
541                                                 neg[k * components + j] = \
542                                                         (temp_type)calculate_neg(src[k * components + j]); \
543                                         } \
544                                         else \
545                                         { \
546                                                 neg[k * components + j] = \
547                                                         plugin->neg_lut[(int)src[k * components + j]]; \
548                                         } \
549                                 } \
550                         } \
551  \
552                         count++; \
553                         row = (row + 1) & 3; \
554                 } \
555                 else \
556                 { \
557                         count--; \
558                 } \
559  \
560                 dst_row = output_rows[i]; \
561                 if(count == 3) \
562                 { \
563 /* Do the filter */ \
564                         if(plugin->config.horizontal) \
565                                 filter(components, \
566                                         vmax, \
567                                         w,  \
568                                         (type*)src_rows[(row + 2) & 3],  \
569                                         (type*)dst_row, \
570                                         (temp_type*)neg_rows[(row + 2) & 3] + components, \
571                                         (temp_type*)neg_rows[(row + 2) & 3] + components, \
572                                         (temp_type*)neg_rows[(row + 2) & 3] + components); \
573                         else \
574                                 filter(components, \
575                                         vmax, \
576                                         w,  \
577                                         (type*)src_rows[(row + 2) & 3],  \
578                                         (type*)dst_row, \
579                                         (temp_type*)neg_rows[(row + 1) & 3] + components, \
580                                         (temp_type*)neg_rows[(row + 2) & 3] + components, \
581                                         (temp_type*)neg_rows[(row + 3) & 3] + components); \
582                 } \
583                 else  \
584                 if(count == 2) \
585                 { \
586                         if(i == 0) \
587                                 memcpy(dst_row, src_rows[0], w * components * wordsize); \
588                         else \
589                                 memcpy(dst_row, src_rows[2], w * components * wordsize); \
590                 } \
591         } \
592 }
593
594
595
596 void SharpenEngine::run()
597 {
598         while(1)
599         {
600                 input_lock->lock("SharpenEngine::run");
601                 if(last_frame)
602                 {
603                         output_lock->unlock();
604                         return;
605                 }
606
607
608                 switch(input->get_color_model())
609                 {
610                         case BC_RGB_FLOAT:
611                                 SHARPEN(3, float, float, 1);
612                                 break;
613
614                         case BC_RGB888:
615                         case BC_YUV888:
616                                 SHARPEN(3, unsigned char, int, 0xff);
617                                 break;
618
619                         case BC_RGBA_FLOAT:
620                                 SHARPEN(4, float, float, 1);
621                                 break;
622
623                         case BC_RGBA8888:
624                         case BC_YUVA8888:
625                                 SHARPEN(4, unsigned char, int, 0xff);
626                                 break;
627
628                         case BC_RGB161616:
629                         case BC_YUV161616:
630                                 SHARPEN(3, u_int16_t, int, 0xffff);
631                                 break;
632
633                         case BC_RGBA16161616:
634                         case BC_YUVA16161616:
635                                 SHARPEN(4, u_int16_t, int, 0xffff);
636                                 break;
637                 }
638
639                 output_lock->unlock();
640         }
641 }
642