Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / plugins / sharpen / sharpen.C
diff --git a/cinelerra-5.1/plugins/sharpen/sharpen.C b/cinelerra-5.1/plugins/sharpen/sharpen.C
new file mode 100644 (file)
index 0000000..c838493
--- /dev/null
@@ -0,0 +1,642 @@
+
+/*
+ * CINELERRA
+ * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ */
+
+#include "clip.h"
+#include "bccmodels.h"
+#include "condition.h"
+#include "filexml.h"
+#include "language.h"
+#include "sharpen.h"
+#include "sharpenwindow.h"
+
+#include <stdio.h>
+#include <string.h>
+
+REGISTER_PLUGIN(SharpenMain)
+
+
+
+
+
+
+
+SharpenConfig::SharpenConfig()
+{
+       horizontal = 0;
+       interlace = 0;
+       sharpness = 50;
+       luminance = 0;
+}
+
+void SharpenConfig::copy_from(SharpenConfig &that)
+{
+       horizontal = that.horizontal;
+       interlace = that.interlace;
+       sharpness = that.sharpness;
+       luminance = that.luminance;
+}
+
+int SharpenConfig::equivalent(SharpenConfig &that)
+{
+       return horizontal == that.horizontal &&
+               interlace == that.interlace &&
+               EQUIV(sharpness, that.sharpness) &&
+               luminance == that.luminance;
+}
+
+void SharpenConfig::interpolate(SharpenConfig &prev, 
+       SharpenConfig &next, 
+       long prev_frame, 
+       long next_frame, 
+       long current_frame)
+{
+       double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
+       double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
+       this->sharpness = prev.sharpness * prev_scale + next.sharpness * next_scale;
+       this->interlace = prev.interlace;
+       this->horizontal = prev.horizontal;
+       this->luminance = prev.luminance;
+}
+
+
+
+
+
+
+
+
+
+
+
+SharpenMain::SharpenMain(PluginServer *server)
+ : PluginVClient(server)
+{
+       
+       engine = 0;
+}
+
+SharpenMain::~SharpenMain()
+{
+       
+
+       if(engine)
+       {
+               for(int i = 0; i < total_engines; i++)
+               {
+                       delete engine[i];
+               }
+               delete engine;
+       }
+}
+
+NEW_WINDOW_MACRO(SharpenMain, SharpenWindow)
+
+
+LOAD_CONFIGURATION_MACRO(SharpenMain, SharpenConfig)
+
+const char* SharpenMain::plugin_title() { return _("Sharpen"); }
+int SharpenMain::is_realtime() { return 1; }
+
+
+
+int SharpenMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
+{
+       int j, k;
+       output = output_ptr;
+       input = input_ptr;
+
+       load_configuration();
+       if(!engine)
+       {
+
+               total_engines = PluginClient::smp > 1 ? 2 : 1;
+               engine = new SharpenEngine*[total_engines];
+               for(int i = 0; i < total_engines; i++)
+               {
+                       engine[i] = new SharpenEngine(this);
+                       engine[i]->start();
+               }
+       }
+
+       get_luts(pos_lut, neg_lut, input_ptr->get_color_model());
+
+       if(config.sharpness != 0)
+       {
+// Arm first row
+               row_step = (config.interlace /* || config.horizontal */) ? 2 : 1;
+
+               for(j = 0; j < row_step; j += total_engines)
+               {
+                       for(k = 0; k < total_engines && k + j < row_step; k++)
+                       {
+                               engine[k]->start_process_frame(input_ptr, input_ptr, k + j);
+                       }
+                       for(k = 0; k < total_engines && k + j < row_step; k++)
+                       {
+                               engine[k]->wait_process_frame();
+                       }
+               }
+       }
+       else
+       if(input_ptr->get_rows()[0] != output_ptr->get_rows()[0])
+       {
+               output_ptr->copy_from(input_ptr);
+       }
+       return 0;
+}
+
+void SharpenMain::update_gui()
+{
+       if(thread)
+       {
+               load_configuration();
+               thread->window->lock_window("SharpenMain::update_gui");
+               ((SharpenWindow*)thread->window)->sharpen_slider->update((int)config.sharpness);
+               ((SharpenWindow*)thread->window)->sharpen_interlace->update(config.interlace);
+               ((SharpenWindow*)thread->window)->sharpen_horizontal->update(config.horizontal);
+               ((SharpenWindow*)thread->window)->sharpen_luminance->update(config.luminance);
+               thread->window->unlock_window();
+       }
+}
+
+
+
+
+int SharpenMain::get_luts(int *pos_lut, int *neg_lut, int color_model)
+{
+       int i, inv_sharpness, vmax;
+
+       vmax = BC_CModels::calculate_max(color_model);
+
+       inv_sharpness = (int)(100 - config.sharpness);
+       if(config.horizontal) inv_sharpness /= 2;
+       if(inv_sharpness < 1) inv_sharpness = 1;
+
+       for(i = 0; i < vmax + 1; i++)
+       {
+               pos_lut[i] = 800 * i / inv_sharpness;
+               neg_lut[i] = (4 + pos_lut[i] - (i << 3)) >> 3;
+       }
+
+       return 0;
+}
+
+void SharpenMain::save_data(KeyFrame *keyframe)
+{
+       FileXML output;
+
+// cause data to be stored directly in text
+       output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
+       output.tag.set_title("SHARPNESS");
+       output.tag.set_property("VALUE", config.sharpness);
+       output.tag.set_property("INTERLACE", config.interlace);
+       output.tag.set_property("HORIZONTAL", config.horizontal);
+       output.tag.set_property("LUMINANCE", config.luminance);
+       output.append_tag();
+       output.tag.set_title("/SHARPNESS");
+       output.append_tag();
+       output.append_newline();
+       output.terminate_string();
+}
+
+void SharpenMain::read_data(KeyFrame *keyframe)
+{
+       FileXML input;
+
+       input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
+
+       int result = 0;
+
+       while(!result)
+       {
+               result = input.read_tag();
+
+               if(!result)
+               {
+                       if(input.tag.title_is("SHARPNESS"))
+                       {
+                               config.sharpness = input.tag.get_property("VALUE", config.sharpness);
+                               config.interlace = input.tag.get_property("INTERLACE", config.interlace);
+                               config.horizontal = input.tag.get_property("HORIZONTAL", config.horizontal);
+                               config.luminance = input.tag.get_property("LUMINANCE", config.luminance);
+//printf("SharpenMain::read_data %f\n", sharpness);
+                       }
+               }
+       }
+
+       if(config.sharpness > MAXSHARPNESS) 
+               config.sharpness = MAXSHARPNESS;
+       else
+               if(config.sharpness < 0) config.sharpness = 0;
+}
+
+
+
+
+SharpenEngine::SharpenEngine(SharpenMain *plugin)
+ : Thread(1, 0, 0)
+{
+       this->plugin = plugin;
+       input_lock = new Condition(0,"SharpenEngine::input_lock");
+       output_lock = new Condition(0, "SharpenEngine::output_lock");
+       last_frame = 0;
+       for(int i = 0; i < 4; i++)
+       {
+               neg_rows[i] = new unsigned char[plugin->input->get_w() * 
+                       4 * 
+                       MAX(sizeof(float), sizeof(int))];
+       }
+}
+
+SharpenEngine::~SharpenEngine()
+{
+       last_frame = 1;
+       input_lock->unlock();
+       Thread::join();
+
+       for(int i = 0; i < 4; i++)
+       {
+               delete [] neg_rows[i];
+       }
+       delete input_lock;
+       delete output_lock;
+}
+
+int SharpenEngine::start_process_frame(VFrame *output, VFrame *input, int field)
+{
+       this->output = output;
+       this->input = input;
+       this->field = field;
+
+// Get coefficient for floating point
+       sharpness_coef = 100 - plugin->config.sharpness;
+       if(plugin->config.horizontal) sharpness_coef /= 2;
+       if(sharpness_coef < 1) sharpness_coef = 1;
+       sharpness_coef = 800.0 / sharpness_coef;
+       
+       input_lock->unlock();
+       return 0;
+}
+
+int SharpenEngine::wait_process_frame()
+{
+       output_lock->lock("SharpenEngine::wait_process_frame");
+       return 0;
+}
+
+float SharpenEngine::calculate_pos(float value)
+{
+       return sharpness_coef * value;
+}
+
+float SharpenEngine::calculate_neg(float value)
+{
+       return (calculate_pos(value) - (value * 8)) / 8;
+}
+
+#define FILTER(components, vmax) \
+{ \
+       int *pos_lut = plugin->pos_lut; \
+       const int wordsize = sizeof(*src); \
+ \
+/* Skip first pixel in row */ \
+       memcpy(dst, src, components * wordsize); \
+       dst += components; \
+       src += components; \
+ \
+       w -= 2; \
+ \
+       while(w > 0) \
+       { \
+               long pixel; \
+               pixel = (long)pos_lut[src[0]] -  \
+                       (long)neg0[-components] -  \
+                       (long)neg0[0] -  \
+                       (long)neg0[components] -  \
+                       (long)neg1[-components] -  \
+                       (long)neg1[components] -  \
+                       (long)neg2[-components] -  \
+                       (long)neg2[0] -  \
+                       (long)neg2[components]; \
+               pixel = (pixel + 4) >> 3; \
+               if(pixel < 0) dst[0] = 0; \
+               else \
+               if(pixel > vmax) dst[0] = vmax; \
+               else \
+               dst[0] = pixel; \
+ \
+               pixel = (long)pos_lut[src[1]] -  \
+                       (long)neg0[-components + 1] -  \
+                       (long)neg0[1] -  \
+                       (long)neg0[components + 1] -  \
+                       (long)neg1[-components + 1] -  \
+                       (long)neg1[components + 1] -  \
+                       (long)neg2[-components + 1] -  \
+                       (long)neg2[1] -  \
+                       (long)neg2[components + 1]; \
+               pixel = (pixel + 4) >> 3; \
+               if(pixel < 0) dst[1] = 0; \
+               else \
+               if(pixel > vmax) dst[1] = vmax; \
+               else \
+               dst[1] = pixel; \
+ \
+               pixel = (long)pos_lut[src[2]] -  \
+                       (long)neg0[-components + 2] -  \
+                       (long)neg0[2] -  \
+                       (long)neg0[components + 2] -  \
+                       (long)neg1[-components + 2] -  \
+                       (long)neg1[components + 2] -  \
+                       (long)neg2[-components + 2] -  \
+                       (long)neg2[2] -  \
+                       (long)neg2[components + 2]; \
+               pixel = (pixel + 4) >> 3; \
+               if(pixel < 0) dst[2] = 0; \
+               else \
+               if(pixel > vmax) dst[2] = vmax; \
+               else \
+               dst[2] = pixel; \
+ \
+               src += components; \
+               dst += components; \
+ \
+               neg0 += components; \
+               neg1 += components; \
+               neg2 += components; \
+               w--; \
+       } \
+ \
+/* Skip last pixel in row */ \
+       memcpy(dst, src, components * wordsize); \
+}
+
+void SharpenEngine::filter(int components,
+       int vmax,
+       int w, 
+       u_int16_t *src, 
+       u_int16_t *dst,
+       int *neg0, 
+       int *neg1, 
+       int *neg2)
+{
+       FILTER(components, vmax);
+}
+
+void SharpenEngine::filter(int components,
+       int vmax,
+       int w, 
+       unsigned char *src, 
+       unsigned char *dst,
+       int *neg0, 
+       int *neg1, 
+       int *neg2)
+{
+       FILTER(components, vmax);
+}
+
+void SharpenEngine::filter(int components,
+       int vmax,
+       int w, 
+       float *src, 
+       float *dst,
+       float *neg0, 
+       float *neg1, 
+       float *neg2)
+{
+       const int wordsize = sizeof(float);
+// First pixel in row
+       memcpy(dst, src, components * wordsize);
+       dst += components;
+       src += components;
+
+       w -= 2;
+       while(w > 0)
+       {
+               float pixel;
+               pixel = calculate_pos(src[0]) -
+                       neg0[-components] -
+                       neg0[0] - 
+                       neg0[components] -
+                       neg1[-components] -
+                       neg1[components] -
+                       neg2[-components] -
+                       neg2[0] -
+                       neg2[components];
+               pixel /= 8;
+               dst[0] = pixel;
+
+               pixel = calculate_pos(src[1]) -
+                       neg0[-components + 1] -
+                       neg0[1] - 
+                       neg0[components + 1] -
+                       neg1[-components + 1] -
+                       neg1[components + 1] -
+                       neg2[-components + 1] -
+                       neg2[1] -
+                       neg2[components + 1];
+               pixel /= 8;
+               dst[1] = pixel;
+
+               pixel = calculate_pos(src[2]) -
+                       neg0[-components + 2] -
+                       neg0[2] - 
+                       neg0[components + 2] -
+                       neg1[-components + 2] -
+                       neg1[components + 2] -
+                       neg2[-components + 2] -
+                       neg2[2] -
+                       neg2[components + 2];
+               pixel /= 8;
+               dst[2] = pixel;
+
+               src += components;
+               dst += components;
+               neg0 += components;
+               neg1 += components;
+               neg2 += components;
+               w--;
+       }
+
+/* Last pixel */
+       memcpy(dst, src, components * wordsize);
+}
+
+
+
+
+
+
+
+#define SHARPEN(components, type, temp_type, vmax) \
+{ \
+       int count, row; \
+       int wordsize = sizeof(type); \
+       unsigned char **input_rows, **output_rows; \
+       int w = plugin->input->get_w(); \
+       int h = plugin->input->get_h(); \
+ \
+       input_rows = input->get_rows(); \
+       output_rows = output->get_rows(); \
+       src_rows[0] = input_rows[field]; \
+       src_rows[1] = input_rows[field]; \
+       src_rows[2] = input_rows[field]; \
+       src_rows[3] = input_rows[field]; \
+ \
+       for(int j = 0; j < w; j++) \
+       { \
+               temp_type *neg = (temp_type*)neg_rows[0]; \
+               type *src = (type*)src_rows[0]; \
+               for(int k = 0; k < components; k++) \
+               { \
+                       if(wordsize == 4) \
+                       { \
+                               neg[j * components + k] = \
+                                       (temp_type)calculate_neg(src[j * components + k]); \
+                       } \
+                       else \
+                       { \
+                               neg[j * components + k] = \
+                                       (temp_type)plugin->neg_lut[(int)src[j * components + k]]; \
+                       } \
+               } \
+       } \
+ \
+       row = 1; \
+       count = 1; \
+ \
+       for(int i = field; i < h; i += plugin->row_step) \
+       { \
+               if((i + plugin->row_step) < h) \
+               { \
+                       if(count >= 3) count--; \
+/* Arm next row */ \
+                       src_rows[row] = input_rows[i + plugin->row_step]; \
+/* Calculate neg rows */ \
+                       type *src = (type*)src_rows[row]; \
+                       temp_type *neg = (temp_type*)neg_rows[row]; \
+                       for(int k = 0; k < w; k++) \
+                       { \
+                               for(int j = 0; j < components; j++) \
+                               { \
+                                       if(wordsize == 4) \
+                                       { \
+                                               neg[k * components + j] = \
+                                                       (temp_type)calculate_neg(src[k * components + j]); \
+                                       } \
+                                       else \
+                                       { \
+                                               neg[k * components + j] = \
+                                                       plugin->neg_lut[(int)src[k * components + j]]; \
+                                       } \
+                               } \
+                       } \
+ \
+                       count++; \
+                       row = (row + 1) & 3; \
+               } \
+               else \
+               { \
+                       count--; \
+               } \
+ \
+               dst_row = output_rows[i]; \
+               if(count == 3) \
+               { \
+/* Do the filter */ \
+                       if(plugin->config.horizontal) \
+                               filter(components, \
+                                       vmax, \
+                                       w,  \
+                                       (type*)src_rows[(row + 2) & 3],  \
+                                       (type*)dst_row, \
+                                       (temp_type*)neg_rows[(row + 2) & 3] + components, \
+                                       (temp_type*)neg_rows[(row + 2) & 3] + components, \
+                                       (temp_type*)neg_rows[(row + 2) & 3] + components); \
+                       else \
+                               filter(components, \
+                                       vmax, \
+                                       w,  \
+                                       (type*)src_rows[(row + 2) & 3],  \
+                                       (type*)dst_row, \
+                                       (temp_type*)neg_rows[(row + 1) & 3] + components, \
+                                       (temp_type*)neg_rows[(row + 2) & 3] + components, \
+                                       (temp_type*)neg_rows[(row + 3) & 3] + components); \
+               } \
+               else  \
+               if(count == 2) \
+               { \
+                       if(i == 0) \
+                               memcpy(dst_row, src_rows[0], w * components * wordsize); \
+                       else \
+                               memcpy(dst_row, src_rows[2], w * components * wordsize); \
+               } \
+       } \
+}
+
+
+
+void SharpenEngine::run()
+{
+       while(1)
+       {
+               input_lock->lock("SharpenEngine::run");
+               if(last_frame)
+               {
+                       output_lock->unlock();
+                       return;
+               }
+
+
+               switch(input->get_color_model())
+               {
+                       case BC_RGB_FLOAT:
+                               SHARPEN(3, float, float, 1);
+                               break;
+
+                       case BC_RGB888:
+                       case BC_YUV888:
+                               SHARPEN(3, unsigned char, int, 0xff);
+                               break;
+                       
+                       case BC_RGBA_FLOAT:
+                               SHARPEN(4, float, float, 1);
+                               break;
+
+                       case BC_RGBA8888:
+                       case BC_YUVA8888:
+                               SHARPEN(4, unsigned char, int, 0xff);
+                               break;
+                       
+                       case BC_RGB161616:
+                       case BC_YUV161616:
+                               SHARPEN(3, u_int16_t, int, 0xffff);
+                               break;
+                       
+                       case BC_RGBA16161616:
+                       case BC_YUVA16161616:
+                               SHARPEN(4, u_int16_t, int, 0xffff);
+                               break;
+               }
+
+               output_lock->unlock();
+       }
+}
+