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