edl plugin names eng, fix segv for opengl brender, renderfarm rework strategy, perf...
[goodguy/history.git] / cinelerra-5.1 / plugins / histeq / histeq.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008-2012 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 <math.h>
23 #include <stdint.h>
24 #include <string.h>
25 #include <unistd.h>
26
27 #include "histeq.h"
28 #include "filexml.h"
29 #include "language.h"
30 #include "loadbalance.h"
31 #include "bccolors.h"
32 #include "vframe.h"
33
34 class HistEqMain;
35 class HistEqEngine;
36 class HistEqWindow;
37
38 REGISTER_PLUGIN(HistEqMain)
39
40 HistEqConfig::HistEqConfig()
41 {
42         split = 0;
43         plot = 0;
44         blend = 0.5;
45         gain = 1.0;
46 }
47
48 HistEqConfig::~HistEqConfig()
49 {
50 }
51
52 void HistEqConfig::copy_from(HistEqConfig &that)
53 {
54         split = that.split;
55         plot = that.plot;
56         blend = that.blend;
57         gain = that.gain;
58 }
59
60 int HistEqConfig::equivalent(HistEqConfig &that)
61 {
62         return 1;
63 }
64
65 void HistEqConfig::interpolate(HistEqConfig &prev, HistEqConfig &next,
66                 int64_t prev_frame, int64_t next_frame, int64_t current_frame)
67 {
68         copy_from(prev);
69 }
70
71
72 HistEqWindow::HistEqWindow(HistEqMain *plugin)
73  : PluginClientWindow(plugin, plugin->w, plugin->h, plugin->w, plugin->h, 0)
74 {
75         this->plugin = plugin;
76 }
77
78 HistEqWindow::~HistEqWindow()
79 {
80 }
81
82
83 void HistEqWindow::create_objects()
84 {
85         int x = 10, y = 10;
86         int cw = get_w()-2*x, ch = cw*3/4;
87         add_subwindow(canvas = new HistEqCanvas(this, plugin, x, y, cw, ch));
88         y += canvas->get_h() + 10;
89         
90         add_subwindow(split = new HistEqSplit(this, plugin, x, y));
91         y += split->get_h() + 10;
92
93         add_subwindow(plot = new HistEqPlot(this, plugin, x, y));
94         y += plot->get_h() + 10;
95
96         int x1 = x + 60;
97         add_subwindow(new BC_Title(x, y, _("Blend:")));
98         add_subwindow(blend = new HistEqBlend(this, plugin, x1, y));
99         y += blend->get_h() + 10;
100
101         add_subwindow(new BC_Title(x, y, _("Gain:")));
102         add_subwindow(gain = new HistEqGain(this, plugin, x1, y));
103         y += gain->get_h() + 10;
104
105         show_window();
106 }
107
108 void HistEqWindow::update()
109 {
110 }
111
112 HistEqCanvas::HistEqCanvas(HistEqWindow *gui, HistEqMain *plugin,
113                 int x, int y, int w, int h)
114  : BC_SubWindow(x, y, w, h, BLACK)
115 {
116         this->gui = gui;
117         this->plugin = plugin;
118 }
119 void HistEqCanvas::clear()
120 {
121         clear_box(0,0, get_w(),get_h());
122 }
123
124 void HistEqCanvas::draw_bins(HistEqMain *plugin)
125 {
126         set_color(GREEN);
127         int *data = plugin->bins;
128         int n = plugin->binsz, max = 0;
129         for( int i=0; i<n; ++i )
130                 if( max < data[i] ) max = data[i];
131         double lmax = log(max);
132         int cw = get_w(), ch = get_h();
133         int x0 = 0, x = 0;
134         while( x < cw ) {
135                 int mx = data[x0];
136                 int x1 = (n * ++x) / cw;
137                 for( int i=x0; ++i<x1; ) {
138                         int m = data[i];
139                         if( m > mx ) mx = m;
140                 }
141                 double lmx = mx>0 ? log(mx) : 0;
142                 int y1 = ch * (1 - lmx/lmax);
143                 draw_line(x,0, x,y1);
144                 x0 = x1;
145         }
146 }
147
148 void HistEqCanvas::draw_wts(HistEqMain *plugin)
149 {
150         float *wts = plugin->wts;
151         int n = plugin->binsz;
152         set_color(BLUE);
153         int cw1 = get_w()-1, ch1 = get_h()-1;
154         float g0 = plugin->config.gain, g1 = 1-g0;
155         int x1 = 0, y1 = ch1;
156         while( x1 < cw1 ) {
157                 int x0 = x1++, y0 = y1;
158                 int is = (n * x1) / cw1;
159                 float fy = wts[is]*g0 + ((float)x1/cw1)*g1;
160                 y1 = (1-fy) * ch1;
161                 draw_line(x0,y0, x1,y1);
162         }
163 }
164
165 void HistEqCanvas::draw_reg(HistEqMain *plugin)
166 {
167         set_color(WHITE);
168         int cw1 = get_w()-1, ch1 = get_h()-1;
169         float g0 = plugin->config.gain, g1 = 1-g0;
170         int x0 = 0, x1 = plugin->binsz-1;
171         double a = plugin->a, b = plugin->b;
172         float fy0 = (a*x0 + b)*g0 + 0*g1;
173         float fy1 = (a*x1 + b)*g0 + 1*g1;
174         int y0 = (1 - fy0) * ch1;
175         int y1 = (1 - fy1) * ch1;
176         draw_line(0,y0, cw1,y1);
177 }
178
179 void HistEqCanvas::draw_lut(HistEqMain *plugin)
180 {
181         int *lut = plugin->lut;
182         int n = plugin->lutsz-1;
183         double s = 1. / n;
184         set_color(RED);
185         int cw1 = get_w()-1, ch1 = get_h()-1;
186         int x1 = 0, y1 = ch1;
187         while( x1 < cw1 ) {
188                 int x0 = x1++, y0 = y1;
189                 int is = (n * x1) / cw1;
190                 y1 = (1-s*lut[is]) * ch1;
191                 draw_line(x0,y0, x1,y1);
192         }
193 }
194
195 void HistEqCanvas::update(HistEqMain *plugin)
196 {
197         clear();
198         draw_bins(plugin);
199         draw_wts(plugin);
200         draw_reg(plugin);
201         draw_lut(plugin);
202         flash();
203 }
204
205 HistEqSplit::HistEqSplit(HistEqWindow *gui, HistEqMain *plugin, int x, int y)
206  : BC_CheckBox(x, y, plugin->config.split, _("Split output"))
207 {
208         this->gui = gui;
209         this->plugin = plugin;
210 }
211 HistEqSplit::~HistEqSplit()
212 {
213 }
214
215 int HistEqSplit::handle_event()
216 {
217         plugin->config.split = get_value();
218         plugin->send_configure_change();
219         return 1;
220 }
221
222 HistEqPlot::HistEqPlot(HistEqWindow *gui, HistEqMain *plugin, int x, int y)
223  : BC_CheckBox(x, y, plugin->config.plot, _("Plot bins/lut"))
224 {
225         this->gui = gui;
226         this->plugin = plugin;
227 }
228 HistEqPlot::~HistEqPlot()
229 {
230 }
231
232 int HistEqPlot::handle_event()
233 {
234         plugin->config.plot = get_value();
235         plugin->send_configure_change();
236         return 1;
237 }
238
239 HistEqBlend::HistEqBlend(HistEqWindow *gui, HistEqMain *plugin, int x, int y)
240  : BC_FSlider(x, y, 0, 150, 200, 0, 1.0, plugin->config.blend, 0)
241 {
242         this->gui = gui;
243         this->plugin = plugin;
244         set_precision(0.01);
245 }
246 HistEqBlend::~HistEqBlend()
247 {
248 }
249
250 int HistEqBlend::handle_event()
251 {
252         plugin->config.blend = get_value();
253         plugin->send_configure_change();
254         return 1;
255 }
256
257
258 HistEqGain::HistEqGain(HistEqWindow *gui, HistEqMain *plugin, int x, int y)
259  : BC_FSlider(x, y, 0, 150, 200, 0, 1.0, plugin->config.gain, 0)
260 {
261         this->gui = gui;
262         this->plugin = plugin;
263         set_precision(0.01);
264 }
265 HistEqGain::~HistEqGain()
266 {
267 }
268
269 int HistEqGain::handle_event()
270 {
271         plugin->config.gain = get_value();
272         plugin->send_configure_change();
273         return 1;
274 }
275
276
277 HistEqMain::HistEqMain(PluginServer *server)
278  : PluginVClient(server)
279 {
280         w = 300;  h = 375;
281         engine = 0;
282         sz = 0;
283         binsz = bsz = 0;  bins = 0;
284         lutsz = lsz = 0;  lut = 0;
285         wsz = 0;          wts = 0;
286         a = 0;  b = 0;
287 }
288
289 HistEqMain::~HistEqMain()
290 {
291         delete engine;
292         delete [] bins;
293         delete [] lut;
294         delete [] wts;
295 }
296
297 const char* HistEqMain::plugin_title() { return N_("HistEq"); }
298 int HistEqMain::is_realtime() { return 1; }
299
300
301 NEW_WINDOW_MACRO(HistEqMain, HistEqWindow)
302
303 LOAD_CONFIGURATION_MACRO(HistEqMain, HistEqConfig)
304
305 void HistEqMain::update_gui()
306 {
307         if( !thread ) return;
308         if( !load_configuration() ) return;
309         ((HistEqWindow*)thread->window)->lock_window("HistEqMain::update_gui");
310         HistEqWindow* gui = (HistEqWindow*)thread->window;
311         gui->update();
312         gui->unlock_window();
313 }
314
315 void HistEqMain::save_data(KeyFrame *keyframe)
316 {
317         FileXML output;
318         output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
319         output.tag.set_title("HISTEQ");
320         output.tag.set_property("W", w);
321         output.tag.set_property("H", h);
322         output.tag.set_property("SPLIT", config.split);
323         output.tag.set_property("PLOT", config.plot);
324         output.tag.set_property("BLEND", config.blend);
325         output.tag.set_property("GAIN", config.gain);
326         output.append_tag();
327         output.tag.set_title("/HISTEQ");
328         output.append_tag();
329         output.append_newline();
330         output.terminate_string();
331 }
332
333 void HistEqMain::read_data(KeyFrame *keyframe)
334 {
335         FileXML input;
336         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
337         int result = 0;
338         while( !(result=input.read_tag()) ) {
339                 if( input.tag.title_is("HISTEQ") ) {
340                         if(is_defaults()) {
341                                 w = input.tag.get_property("W", w);
342                                 h = input.tag.get_property("H", h);
343                         }
344                         config.split = input.tag.get_property("SPLIT", config.split);
345                         config.plot = input.tag.get_property("PLOT", config.plot);
346                         config.blend = input.tag.get_property("BLEND", config.blend);
347                         config.gain = input.tag.get_property("GAIN", config.gain);
348                 }
349         }
350 }
351
352 void HistEqMain::render_gui(void *data)
353 {
354         if( !thread ) return;
355         ((HistEqWindow*)thread->window)->lock_window("HistEqMain::render_gui 1");
356         HistEqWindow *gui = (HistEqWindow*)thread->window;
357         gui->canvas->update((HistEqMain *)data);
358         gui->unlock_window();
359 }
360
361 // regression line
362 static void fit(float *dat, int n, double &a, double &b)
363 {
364         double sy = 0;
365         for( int i=0; i<n; ++i ) sy += dat[i];
366         double s = 0, mx = (n-1)/2., my = sy / n;
367         for( int i=0; i<n; ++i ) s += (i-mx) * dat[i];
368         double m = (n+1)/2;
369         double ss = 2. * ((m+1)*m*(m-1)/3. + m*(m-1)/2.);
370         a = s / ss;
371         b = my - mx*a;
372 }
373
374 int HistEqMain::process_buffer(VFrame *frame, int64_t start_position, double frame_rate)
375 {
376         load_configuration();
377         read_frame(frame, 0, start_position, frame_rate, 0);
378         this->input = this->output = frame;
379
380         int colormodel = frame->get_color_model();
381         lutsz = 0x10000;
382         binsz = (3*0xffff) + 1;
383         switch( colormodel ) {
384         case BC_RGB888:
385         case BC_RGBA8888:
386                 binsz = (3*0xff) + 1;
387                 lutsz = 0x100;
388                 break;
389         case BC_RGB_FLOAT:
390         case BC_RGBA_FLOAT:
391                 binsz = (3*0x5555) + 1;
392                 break;
393         }
394         if( binsz != bsz ) { delete bins;  bins = 0;  bsz = 0; }
395         if( !bins ) bins = new int[bsz = binsz];
396         if( binsz+1 != wsz ) { delete wts;   wts = 0;   wsz = 0; }
397         if( !wts ) wts = new float[wsz = binsz+1];
398         if( lutsz != lsz ) { delete lut;   lut = 0;   lsz = 0; }
399         if( !lut  ) lut  = new int[lsz = lutsz];
400
401         if(!engine) engine = new HistEqEngine(this,
402                 get_project_smp() + 1, get_project_smp() + 1);
403         engine->process_packages(HistEqEngine::HISTEQ, frame);
404
405 // sum the results, not all clients may have run
406         memset(bins, 0, binsz*sizeof(bins[0]));
407         for( int i=0, n=engine->get_total_clients(); i<n; ++i ) {
408                 HistEqUnit *unit = (HistEqUnit*)engine->get_client(i);
409                 if( !unit->valid ) continue;
410                 for( int i=0; i<binsz; ++i ) bins[i] += unit->bins[i];
411         }
412
413 // Remove top and bottom from calculations.
414 //  Doesn't work in high precision colormodels.
415         int n = frame->get_w() * frame->get_h();
416         int binsz1 = binsz-1, lutsz1 = lutsz-1;
417         n -= bins[0];       bins[0] = 0;
418         n -= bins[binsz1];  bins[binsz1] = 0;
419         sz = n;
420
421 // integrate and normalize
422         for( int i=0,t=0; i<binsz; ++i ) { t += bins[i];  wts[i] = (float)t / n; }
423         wts[binsz] = 1.f;
424 // exclude margins (+-2%)
425         float fmn =  0.02f, fmx = 1. - fmn;
426         int mn = 0;    while( mn < binsz && wts[mn] < fmn ) ++mn;
427         int mx = binsz; while( mx > mn  && wts[mx-1] > fmx ) --mx;
428         n = mx-mn+1;  fit(&wts[mn], n, a, b);
429 // home y intercept
430         b -= a * mn;
431         if( (a*n + b) < 1 ) a = (1 - b) / n;
432         if( b > 0 ) { a = (a*n + b) / n;  b = 0; }
433         double blend = config.blend, blend1 = 1-blend;
434         double r = (double)binsz1 / lutsz1;
435         float g0 = config.gain, g1 = 1-g0;
436         for( int i=0; i<lutsz; ++i ) {
437                 double is = i * r;
438                 int is0 = is, is1 = is0+1;
439                 double s0 = is-is0, s1 = 1-s0;
440 // piecewise linear interp btwn wts[is]..wts[is+1]
441                 float u = wts[is0]*s0 + wts[is1]*s1;
442 // regression line
443                 float v = is*a + b;
444 // blend bins eq with linear regression, add scalar gain
445                 float t = u*blend + v*blend1;
446                 int iy = (t*lutsz1)*g0 + i*g1;
447                 lut[i] = bclip(iy, 0, lutsz1);
448         }
449         if( config.plot && gui_open() )
450                 send_render_gui(this);
451
452         engine->process_packages(HistEqEngine::APPLY, frame);
453         return 0;
454 }
455
456 HistEqPackage::HistEqPackage()
457  : LoadPackage()
458 {
459 }
460
461 HistEqUnit::HistEqUnit(HistEqEngine *server, HistEqMain *plugin)
462  : LoadClient(server)
463 {
464         this->plugin = plugin;
465         this->server = server;
466         bins = 0;
467         binsz = 0;
468         valid = 0;
469 }
470
471 HistEqUnit::~HistEqUnit()
472 {
473         delete [] bins;
474 }
475
476 void HistEqUnit::process_package(LoadPackage *package)
477 {
478         if( binsz != plugin->binsz ) {
479                 delete bins;  bins = 0;  binsz = 0;
480         }
481         if( !bins ) {
482                 bins = new int[binsz = plugin->binsz];
483         }
484         if( !valid ) {
485                 valid = 1;
486                 bzero(bins, binsz*sizeof(bins[0]));
487         }
488
489         HistEqPackage *pkg = (HistEqPackage*)package;
490         switch( server->operation ) {
491         case HistEqEngine::HISTEQ: {
492
493 #define HISTEQ_HEAD(type) { \
494         type **rows = (type**)data->get_rows(); \
495         for( int iy=pkg->y0; iy<pkg->y1; ++iy ) { \
496                 type *row = rows[iy]; \
497                 for( int ix=0; ix<w; ++ix ) {
498
499 #define HISTEQ_TAIL(components) \
500                         ++bins[i]; \
501                         row += components; \
502                 } \
503         } \
504 }
505                 VFrame *data = server->data;
506                 int colormodel = data->get_color_model();
507                 int w = data->get_w(), comps = BC_CModels::components(colormodel);
508
509                 switch( colormodel ) {
510                 case BC_RGB888:
511                 case BC_RGBA8888:
512                         HISTEQ_HEAD(unsigned char)
513                         int r = row[0], g = row[1], b = row[2];
514                         int i = r + g + b;
515                         HISTEQ_TAIL(comps)
516                         break;
517                 case BC_RGB161616:
518                 case BC_RGBA16161616:
519                         HISTEQ_HEAD(uint16_t)
520                         int r = row[0], g = row[1], b = row[2];
521                         int i = r + g + b;
522                         HISTEQ_TAIL(comps)
523                         break;
524                 case BC_RGB_FLOAT:
525                 case BC_RGBA_FLOAT:
526                         HISTEQ_HEAD(float)
527                         int r = (int)(row[0] * 0x5555);
528                         int g = (int)(row[1] * 0x5555);
529                         int b = (int)(row[2] * 0x5555);
530                         int i = r + g + b;  bclamp(i, 0,0xffff);
531                         HISTEQ_TAIL(comps)
532                         break;
533                 case BC_YUV888:
534                 case BC_YUVA8888:
535                         HISTEQ_HEAD(unsigned char)
536                         int y = (row[0]<<8) + row[0];
537                         int u = (row[1]<<8) + row[1];
538                         int v = (row[2]<<8) + row[2];
539                         int r, g, b;
540                         YUV::yuv.yuv_to_rgb_16(r, g, b, y, u, v);
541                         int i = r + g + b;
542                         HISTEQ_TAIL(comps)
543                         break;
544                 case BC_YUV161616:
545                 case BC_YUVA16161616:
546                         HISTEQ_HEAD(uint16_t)
547                         int r, g, b;
548                         YUV::yuv.yuv_to_rgb_16(r, g, b, row[0], row[1], row[2]);
549                         int i = r + g + b;
550                         HISTEQ_TAIL(comps)
551                         break;
552                 }
553                 break; }
554         case HistEqEngine::APPLY: {
555 #define PROCESS_RGB(type, components, max) { \
556         type **rows = (type**)input->get_rows(); \
557         for( int iy=pkg->y0; iy<pkg->y1; ++iy ) { \
558                 type *row = rows[iy]; \
559                 int x1 = !split ? w : (iy * w) / h; \
560                 for( int x=0; x<x1; ++x ) { \
561                         int r = row[0], g = row[1], b = row[2]; \
562                         row[0] = lut[r];  row[1] = lut[g];  row[2] = lut[b]; \
563                         row += components; \
564                 } \
565         } \
566 }
567 #define PROCESS_YUV(type, components, max) { \
568         type **rows = (type**)input->get_rows(); \
569         for( int iy=pkg->y0; iy<pkg->y1; ++iy ) { \
570                 type *row = rows[iy]; \
571                 int x1 = !split ? w : (iy * w) / h; \
572                 for( int ix=0; ix<x1; ++ix ) { \
573                         int r, g, b, y, u, v; \
574                         if( max == 0xff ) { \
575                                 y = (row[0] << 8) | row[0]; \
576                                 u = (row[1] << 8) | row[1]; \
577                                 v = (row[2] << 8) | row[2]; \
578                         } \
579                         else { \
580                                 y = row[0]; u = row[1]; v = row[2]; \
581                         } \
582                         YUV::yuv.yuv_to_rgb_16(r, g, b, y, u, v); \
583                         YUV::yuv.rgb_to_yuv_16(lut[r], lut[g], lut[b], y, u, v); \
584                         if( max == 0xff ) { \
585                                 row[0] = y >> 8; \
586                                 row[1] = u >> 8; \
587                                 row[2] = v >> 8; \
588                         } \
589                         else { \
590                                 row[0] = y; row[1] = u; row[2] = v; \
591                         } \
592                         row += components; \
593                 } \
594         } \
595 }
596 #define PROCESS_FLOAT(components) { \
597         float **rows = (float**)input->get_rows(); \
598         for( int iy=pkg->y0; iy<pkg->y1; ++iy ) { \
599                 float *row = rows[iy]; \
600                 int x1 = !split ? w : (iy * w) / h; \
601                 for( int ix=0; ix<x1; ++ix ) { \
602                         int r = row[0]*0xffff, g = row[1]*0xffff, b = row[2]*0xffff; \
603                         bclamp(r, 0,0xffff);  bclamp(g, 0,0xffff);  bclamp(b, 0,0xffff); \
604                         row[0] = (float)lut[r] / 0xffff; \
605                         row[1] = (float)lut[g] / 0xffff; \
606                         row[2] = (float)lut[b] / 0xffff; \
607                         row += components; \
608                 } \
609         } \
610 }
611
612                 VFrame *input = plugin->input;
613                 int w = input->get_w(), h = input->get_h();
614                 int split = plugin->config.split;
615                 int *lut = plugin->lut;
616
617                 switch(input->get_color_model()) {
618                 case BC_RGB888:
619                         PROCESS_RGB(unsigned char, 3, 0xff)
620                         break;
621                 case BC_RGBA8888:
622                         PROCESS_RGB(unsigned char, 4, 0xff)
623                         break;
624                 case BC_RGB161616:
625                         PROCESS_RGB(uint16_t, 3, 0xffff)
626                         break;
627                 case BC_RGBA16161616:
628                         PROCESS_RGB(uint16_t, 4, 0xffff)
629                         break;
630                 case BC_RGB_FLOAT:
631                         PROCESS_FLOAT(3);
632                         break;
633                 case BC_RGBA_FLOAT:
634                         PROCESS_FLOAT(4);
635                         break;
636                 case BC_YUV888:
637                         PROCESS_YUV(unsigned char, 3, 0xff)
638                         break;
639                 case BC_YUVA8888:
640                         PROCESS_YUV(unsigned char, 4, 0xff)
641                         break;
642                 case BC_YUV161616:
643                         PROCESS_YUV(uint16_t, 3, 0xffff)
644                         break;
645                 case BC_YUVA16161616:
646                         PROCESS_YUV(uint16_t, 4, 0xffff)
647                         break;
648                 }
649                 break; }
650         }
651 }
652
653 HistEqEngine::HistEqEngine(HistEqMain *plugin,
654         int total_clients,
655         int total_packages)
656  : LoadServer(total_clients, total_packages)
657 {
658         this->plugin = plugin;
659 }
660
661 void HistEqEngine::init_packages()
662 {
663         int h = data->get_h();
664         int y0 = 0;
665
666         for( int i=0, n=get_total_packages(); i<n; ) {
667                 HistEqPackage *pkg = (HistEqPackage *)get_package(i);
668                 int y1 = ++i * h / n;
669                 pkg->y0 = y0;  pkg->y1 = y1;
670                 y0 = y1;
671         }
672         for( int i=0, n=get_total_clients(); i<n; ++i ) {
673                 HistEqUnit *unit = (HistEqUnit*)get_client(i);
674                 unit->valid = 0; // set if unit runs
675         }
676 }
677
678 LoadClient* HistEqEngine::new_client()
679 {
680         return new HistEqUnit(this, plugin);
681 }
682
683 LoadPackage* HistEqEngine::new_package()
684 {
685         return new HistEqPackage;
686 }
687
688 void HistEqEngine::process_packages(int operation, VFrame *data)
689 {
690         this->data = data;
691         this->operation = operation;
692
693         LoadServer::process_packages();
694 }
695
696