no longer need ffmpeg patch0 which was for Termux
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / colorspace / colorspace.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2020 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 "bccmodels.h"
23 #include "filexml.h"
24 #include "language.h"
25 #include "colorspace.h"
26 #include "pluginserver.h"
27 #include "preferences.h"
28
29 #include <stdio.h>
30 #include <string.h>
31
32
33 REGISTER_PLUGIN(ColorSpaceMain)
34
35 ColorSpaceConfig::ColorSpaceConfig()
36 {
37         inverse = 0;
38         inp_colorspace = BC_COLORS_BT601_NTSC;
39         inp_colorrange = BC_COLORS_JPEG;
40         out_colorspace = BC_COLORS_BT709;
41         out_colorrange = BC_COLORS_JPEG;
42 }
43
44 ColorSpaceMain::ColorSpaceMain(PluginServer *server)
45  : PluginVClient(server)
46 {
47         inp_color_space = -1;
48         inp_color_range = -1;
49         out_color_space = -1;
50         out_color_range = -1;
51         xtable = 0;
52         engine = 0;
53 }
54
55 ColorSpaceMain::~ColorSpaceMain()
56 {
57         delete xtable;
58         delete engine;
59 }
60
61 const char* ColorSpaceMain::plugin_title() { return N_("ColorSpace"); }
62 int ColorSpaceMain::is_realtime() { return 1; }
63
64
65 NEW_WINDOW_MACRO(ColorSpaceMain, ColorSpaceWindow)
66
67
68 void ColorSpaceMain::update_gui()
69 {
70         if( !thread ) return;
71         load_configuration();
72         ColorSpaceWindow *window = (ColorSpaceWindow *)thread->window;
73         window->lock_window();
74         window->update();
75         window->unlock_window();
76 }
77
78
79 int ColorSpaceMain::load_configuration()
80 {
81         KeyFrame *prev_keyframe = get_prev_keyframe(get_source_position());
82         read_data(prev_keyframe);
83         return 1;
84 }
85
86
87 void ColorSpaceMain::save_data(KeyFrame *keyframe)
88 {
89         FileXML output;
90
91 // cause data to be stored directly in text
92         output.set_shared_output(keyframe->xbuf);
93         output.tag.set_title("COLORSPACE");
94         output.tag.set_property("INVERSE", config.inverse);
95         output.tag.set_property("INP_COLORSPACE", config.inp_colorspace);
96         output.tag.set_property("INP_COLORRANGE", config.inp_colorrange);
97         output.tag.set_property("OUT_COLORSPACE", config.out_colorspace);
98         output.tag.set_property("OUT_COLORRANGE", config.out_colorrange);
99         output.append_tag();
100         output.tag.set_title("/COLORSPACE");
101         output.append_tag();
102         output.append_newline();
103         output.terminate_string();
104 }
105
106 void ColorSpaceMain::read_data(KeyFrame *keyframe)
107 {
108         FileXML input;
109         input.set_shared_input(keyframe->xbuf);
110
111         int result = 0;
112         while( !(result = input.read_tag()) ) {
113                 if( input.tag.title_is("COLORSPACE") ) {
114                         config.inverse = input.tag.
115                                 get_property("INVERSE", config.inverse);
116                         config.inp_colorspace = input.tag.
117                                 get_property("INP_COLORSPACE", config.inp_colorspace);
118                         config.inp_colorrange = input.tag.
119                                 get_property("INP_COLORRANGE", config.inp_colorrange);
120                         config.out_colorspace = input.tag.
121                                 get_property("OUT_COLORSPACE", config.out_colorspace);
122                         config.out_colorrange = input.tag.
123                                 get_property("OUT_COLORRANGE", config.out_colorrange);
124                 }
125         }
126 }
127
128 XTable::XTable()
129 {
130         this->typ = -1;
131         this->len = 0;
132         this->inv = 0;
133         memset(eqns, 0, sizeof(eqns));
134         memset(luts, 0, sizeof(luts));
135         for( int i=0; i<3; ++i ) {
136                 imin[i] = omin[i] = 0;  imax[i] = omax[i] = 0xff;
137                 izro[i] = ozro[i] = 0;  irng[i] = orng[i] = 0xff;
138                 izrf[i] = ozrf[i] = 0;  omnf[i] = 0;  omxf[i] = 1;
139         }
140 }
141
142 XTable::~XTable()
143 {
144         alloc_lut(0);
145 }
146
147 void XTable::create_table(lut_t **lut, int len, float *vars)
148 {
149         int s = len == 0x100 ? (24-8) : (24-16);
150         for( int i=0; i<3; ++i ) {
151                 lut_t imn = imin[i], imx = imax[i];
152                 lut_t omn = omin[i], omx = omax[i];
153                 double irng = imx+1 - imn;
154                 double orng = omx+1 - omn;
155                 double r = vars[i] * orng / irng;
156                 lut_t izr = izro[i], v = r*(imn-izr);
157                 lut_t *tbl = lut[i]; int k;
158                 for( k=0; (k<<s)<imn; ++k ) tbl[k] = v;
159                 for(  ; (v=k<<s)<imx; ++k ) tbl[k] = r*(v-izr);
160                 for( v=r*(imx-izr); k<len; ++k ) tbl[k] = v;
161         }
162 }
163
164 void XTable::create_tables(int len)
165 {
166         for( int i=0; i<3; ++i )
167                 create_table(luts[i], len, eqns[i]);
168 }
169
170
171 void XTable::alloc_lut(int len)
172 {
173         if( this->len == len ) return;
174         for( int i=0; i<3; ++i ) {
175                 for( int j=0; j<3; ++j ) {
176                         delete [] luts[i][j];
177                         luts[i][j] = len>0 ? new lut_t[len] : 0;
178                 }
179         }
180         this->len = len;
181 }
182
183 // these eqns were derived using python sympy
184 // and the conversion forms in bccolors.h
185 /*
186 >>> from sympy import *
187 >>> var('iKr,iKg,iKb,oKr,oKg,oKb,R,G,B,Y,U,V,Py,Pr,Pb')
188 (iKr, iKg, iKb, oKr, oKg, oKb, R, G, B, Y, U, V, Py, Pr, Pb)
189 >>>
190 >>> Y  =   iKr * R  +  iKg * G  +  iKb * B
191 >>> U  = - 0.5*iKr/(1-iKb)*R - 0.5*iKg/(1-iKb)*G + 0.5*B
192 >>> V  =   0.5*R - 0.5*iKg/(1-iKr)*G - 0.5*iKb/(1-iKr)*B
193 >>>
194 >>> r = Y + V * 2*(1-oKr)
195 >>> g = Y - V * 2*oKr*(1-oKr)/oKg - U * 2*oKb*(1-oKb)/oKg
196 >>> b = Y                         + U * 2*(1-oKb)
197 >>>
198 >>> factor(r,(R,G,B))
199 -1.0*(B*(1.0*iKb*iKr - 1.0*iKb*oKr) + G*(1.0*iKg*iKr - 1.0*iKg*oKr)
200  + R*(1.0*iKr**2 - 1.0*iKr*oKr + 1.0*oKr - 1.0))/(1 - iKr)
201 >>> factor((1.0*iKr**2 - 1.0*iKr*oKr + 1.0*oKr - 1.0))
202 1.0*(iKr - 1)*(iKr - oKr + 1)
203 >>> factor((1.0*iKg*iKr - 1.0*iKg*oKr))
204 1.0*iKg*(iKr - oKr)
205 >>> factor((1.0*iKb*iKr - 1.0*iKb*oKr))
206 1.0*iKb*(iKr - oKr)
207 >>>
208 >>> factor(g,(R,G,B))       strlen(result)=778
209 results: eqn terms r*R + g*G + b*B, where r,g,b are the eqns coefs.
210 with some renaming, this can be done symetrically with Y,U,V
211 each coef eqn r,g,b can be reduced using factor([r,g,b])
212 which creates eqns forms that simplify to the used calculation
213 >>> factor(y,(Y,U,V))
214 results in: y*Y + u*U + v*V which y,u,v are the eqns factors
215 with same simplify and use
216 */
217
218 //   yuv->(cs)->rgb->(cs)->yuv
219 int XTable::init_yuv2yuv(double iKr, double iKb, double oKr, double oKb)
220 {
221         double iKg = 1 - iKr - iKb;
222         double oKg = 1 - oKr - oKb;
223         double d = iKg;
224         Yy = iKg*(oKb + oKg + oKr) / d;
225         Uy = 2*(iKb - 1)*(iKb*oKg - oKb*iKg) / d;
226         Vy = -2*(iKr - 1)*(iKg*oKr - oKg*iKr) / d;
227         d = (iKg*(1 - oKb));
228         Yu = -0.5*iKg*(oKb + oKg + oKr - 1) / d;
229         Uu = -(iKb - 1)*(iKb*oKg - oKb*iKg + iKg) / d;
230         Vu = (iKr - 1)*(iKg*oKr - oKg*iKr) / d;
231         d = (iKg*(1 - oKr));
232         Yv = -0.5*iKg*(oKb + oKg + oKr - 1) / d;
233         Uv = -(iKb - 1)*(iKb*oKg - oKb*iKg) / d;
234         Vv = (iKr - 1)*(iKg*oKr - iKg - oKg*iKr) / d;
235         return yuv2yuv;
236 }
237
238 //   rgb->(cs)->yuv
239 int XTable::init_rgb2yuv(double Kr, double Kb)
240 {
241         double Kg = 1 - Kr - Kb;
242         Ry = Kr;
243         Gy = 1 - Kr - Kb;
244         By = Kb;
245         Ru = -0.5*Kr / (1 - Kb);
246         Gu = -0.5*Kg / (1 - Kb);
247         Bu =  0.5;
248         Rv =  0.5;
249         Gv = -0.5*Kg / (1 - Kr);
250         Bv = -0.5*Kb / (1 - Kr);
251         return rgb2yuv;
252 }
253
254 //   yuv->(cs)->rgb
255 int XTable::init_yuv2rgb(double Kr, double Kb)
256 {
257         double Kg = 1 - Kr - Kb;
258         Yr =  1.0;
259         Ur =  2*(1 - Kr);
260         Vr =  0.0;
261         Yg =  1.0;
262         Ug = -2*Kr*(1 - Kr) / Kg;
263         Vg = -2*Kb*(1 - Kb) / Kg;
264         Yb =  1.0;
265         Ub =  0.0;
266         Vb =  2*(1 - Kb);
267         return yuv2rgb;
268 }
269
270 //   rgb->(cs)->yuv->(cs)->rgb
271 int XTable::init_rgb2rgb(double iKr, double iKb, double oKr, double oKb)
272 {
273         double iKg = 1 - iKr - iKb;
274         double oKg = 1 - oKr - oKb;
275         double d = (1 - iKr);
276         Rr = -(iKr - 1)*(iKr - oKr + 1) / d;
277         Gr = -iKg*(iKr - oKr) / d;
278         Br = -iKb*(iKr - oKr) / d;
279         d = (oKg*(1 - iKb)*(1 - iKr));
280         Rg = (iKr - 1)*(iKb*oKg*iKr + iKb*oKr*oKr - iKb*oKr +
281                 oKb*oKb*iKr - oKb*iKr - oKg*iKr - oKr*oKr + oKr) / d;
282         Gg = iKg*(iKb*oKg*iKr - iKb*oKg + iKb*oKr*oKr - iKb*oKr +
283                 oKb*oKb*iKr - oKb*oKb - oKb*iKr + oKb - oKg*iKr +
284                 oKg - oKr*oKr + oKr) / d;
285         Bg = (iKb - 1)*(iKb*oKg*iKr - iKb*oKg + iKb*oKr*oKr -
286                  iKb*oKr + oKb*oKb*iKr - oKb*oKb - oKb*iKr + oKb) / d;
287         d = (1 - iKb);
288         Rb = -iKr*(iKb - oKb) / d;
289         Gb = -iKg*(iKb - oKb) / d;
290         Bb = -(iKb - 1)*(iKb - oKb + 1) / d;
291         return rgb2rgb;
292 }
293
294 void XTable::init(int len, int inv,
295                 int inp_model, int inp_space, int inp_range,
296                 int out_model, int out_space, int out_range)
297 {
298         if( this->typ >= 0 && this->len == len && this->inv == inv &&
299             this->inp_model == inp_model && this->out_model == out_model &&
300             this->inp_space == inp_space && this->out_space == out_space &&
301             this->inp_range == inp_range && this->out_range == out_range )
302                  return;
303
304         alloc_lut(len);
305         this->inv = inv;
306         this->inp_model = inp_model;  this->out_model = out_model;
307         this->inp_space = inp_space;  this->out_space = out_space;
308         this->inp_range = inp_range;  this->out_range = out_range;
309
310         double iKr = BT601_NTSC_Kr, iKb = BT601_NTSC_Kb;
311         double oKr = BT601_NTSC_Kr, oKb = BT601_NTSC_Kb;
312         int impg = 0, ompg = 0;
313         switch( inp_space ) {
314         default:
315         case BC_COLORS_BT601_NTSC:  iKr = BT601_NTSC_Kr;   iKb = BT601_NTSC_Kb;   break;
316         case BC_COLORS_BT601_PAL:  iKr = BT601_PAL_Kr;   iKb = BT601_PAL_Kb;   break;
317         case BC_COLORS_BT709:  iKr = BT709_Kr;   iKb = BT709_Kb;   break;
318         case BC_COLORS_BT2020_NCL: 
319         case BC_COLORS_BT2020_CL: iKr = BT2020_Kr;  iKb = BT2020_Kb;  break;
320         }
321         switch( out_space ) {
322         default:
323         case BC_COLORS_BT601_NTSC:  oKr = BT601_NTSC_Kr;   oKb = BT601_NTSC_Kb;   break;
324         case BC_COLORS_BT601_PAL:  oKr = BT601_PAL_Kr;   oKb = BT601_PAL_Kb;   break;
325         case BC_COLORS_BT709:  oKr = BT709_Kr;   oKb = BT709_Kb;   break;
326         case BC_COLORS_BT2020_NCL: 
327         case BC_COLORS_BT2020_CL: oKr = BT2020_Kr;  oKb = BT2020_Kb;  break;
328         }
329
330         int iyuv = BC_CModels::is_yuv(inp_model);
331         int oyuv = BC_CModels::is_yuv(out_model);
332         this->typ = iyuv ?
333                 (oyuv ? init_yuv2yuv(iKr,iKb, oKr,oKb) :
334                         init_yuv2rgb(iKr,iKb)) :
335                 (oyuv ? init_rgb2yuv(oKr,oKb) :
336                         init_rgb2rgb(iKr,iKb, oKr, oKb));
337
338         switch( inp_range ) {
339         default:
340         case BC_COLORS_JPEG: impg = 0;  break;
341         case BC_COLORS_MPEG: impg = 1;  break;
342         }
343         switch( out_range ) {
344         default:
345         case BC_COLORS_JPEG: ompg = 0;  break;
346         case BC_COLORS_MPEG: ompg = 1;  break;
347         }
348
349 // mpg ? mpeg : jpeg/rgb
350         imin[0] = impg ? 0x100000 : 0x000000;
351         imin[1] = impg ? 0x100000 : 0x000000;
352         imin[2] = impg ? 0x100000 : 0x000000;
353         imax[0] = impg ? 0xebffff : 0xffffff;
354         imax[1] = impg ? 0xf0ffff : 0xffffff;
355         imax[2] = impg ? 0xf0ffff : 0xffffff;
356         omin[0] = ompg ? 0x100000 : 0x000000;
357         omin[1] = ompg ? 0x100000 : 0x000000;
358         omin[2] = ompg ? 0x100000 : 0x000000;
359         omax[0] = ompg ? 0xebffff : 0xffffff;
360         omax[1] = ompg ? 0xf0ffff : 0xffffff;
361         omax[2] = ompg ? 0xf0ffff : 0xffffff;
362         izro[0] = imin[0];
363         izro[1] = iyuv ? 0x800000 : imin[1];
364         izro[2] = iyuv ? 0x800000 : imin[2];
365         ozro[0] = omin[0];
366         ozro[1] = oyuv ? 0x800000 : omin[1];
367         ozro[2] = oyuv ? 0x800000 : omin[2];
368         for( int i=0; i<3; ++i ) {
369                 irng[i] = imax[i]+1 - imin[i];
370                 orng[i] = omax[i]+1 - omin[i];
371                 int sz = 0x1000000;
372                 izrf[i] = (float)izro[i] / sz;
373                 ozrf[i] = (float)ozro[i] / sz;
374                 omnf[i] = (float)omin[i] / sz;
375                 omxf[i] = (float)(omax[i]+1) / sz;
376         }
377         if( inv )
378                 inverse();
379         if( len > 0 )
380                 create_tables(len);
381 // prescale eqns for opengl
382         for( int i=0; i<3; ++i ) {
383                 float *eqn = eqns[i];
384                 float s = (float)orng[i] / irng[i];
385                 for( int j=0; j<3; ++j ) eqn[j] *= s;
386         }
387 #if 0
388 printf("XTable::init len=%06x\n"
389  " impg=%d, ompg=%d, iyuv=%d, oyuv=%d\n"
390  " imin=%06x,%06x,%06x, imax=%06x,%06x,%06x\n"
391  " omin=%06x,%06x,%06x, omax=%06x,%06x,%06x\n"
392  " izro=%06x,%06x,%06x, ozro=%06x,%06x,%06x\n"
393  " izrf=%0.3f,%0.3f,%0.3f, ozrf=%0.3f,%0.3f,%0.3f\n"
394  " omnf=%0.3f,%0.3f,%0.3f, omxf=%0.3f,%0.3f,%0.3f\n"
395  " eqns= %6.3f,%6.3f,%6.3f\n"
396  "       %6.3f,%6.3f,%6.3f\n"
397  "       %6.3f,%6.3f,%6.3f\n",
398  len, impg, ompg, iyuv, oyuv,
399  imin[0], imin[1], imin[2], imax[0], imax[1], imax[2],
400  omin[0], omin[1], omin[2], omax[0], omax[1], omax[2],
401  izro[0], izro[1], izro[2], ozro[0], ozro[1], ozro[2],
402  izrf[0], izrf[0], izrf[0], ozrf[0], ozrf[0], ozrf[0],
403  omnf[0], omnf[0], omnf[0], omxf[0], omxf[0], omxf[0],
404  eqns[0][0], eqns[0][1], eqns[0][2],
405  eqns[1][0], eqns[1][1], eqns[1][2],
406  eqns[2][0], eqns[2][1], eqns[2][2]);
407 #endif
408 }
409
410 /*
411 out = (inp-izro)*eqns + ozro
412 inverse: invert(eqns), swap(inp,out), swap(izro,ozro)
413 inp = (out-ozro)*iqns + izro
414 */
415 int XTable::inverse()
416 {
417 // [[ a b c ],
418 //  [ d e f ],
419 //  [ g h i ]] inverse =
420 // 1/(a(ei-fh) + b(fg-di) + c(dh-eg)) *
421 //    [[ ei-fh, ch-bi, bf-ce ],
422 //     [ fg-di, ai-cg, cd-af ],
423 //     [ dh-eg, bg-ah, ae-bd ]]
424         float a = eqns[0][0], b = eqns[0][1], c = eqns[0][2];
425         float d = eqns[1][0], e = eqns[1][1], f = eqns[1][2];
426         float g = eqns[2][0], h = eqns[2][1], i = eqns[2][2];
427         float s = a*(e*i-f*h) + b*(f*g-d*i) + c*(d*h-e*g);
428         float eps = 1e-4;
429         if( s < eps ) return 1;
430         s = 1.f / s;
431         eqns[0][0] = s*(e*i-f*h);  eqns[0][1] = s*(c*h-b*i);  eqns[0][2] = s*(b*f-c*e);
432         eqns[1][0] = s*(f*g-d*i);  eqns[1][1] = s*(a*i-c*g);  eqns[1][2] = s*(c*d-a*f);
433         eqns[2][0] = s*(d*h-e*g);  eqns[2][1] = s*(b*g-a*h);  eqns[2][2] = s*(a*e-b*d);
434         for( int v,i=0; i<3; ++i ) {
435                 v = imin[i];  imin[i] = omin[i];  omin[i] = v;
436                 v = imax[i];  imax[i] = omax[i];  omax[i] = v;
437                 v = irng[i];  irng[i] = orng[i];  orng[i] = v;
438                 v = izro[i];  izro[i] = ozro[i];  ozro[i] = v;
439                 int sz = 0x1000000;
440                 omnf[i] = (float)omin[i] / sz;
441                 omxf[i] = (float)(omax[i]+1) / sz;
442                 izrf[i] = (float)izro[i] / sz;
443                 ozrf[i] = (float)ozro[i] / sz;
444         }
445         return 0;
446 }
447
448
449 #define PROCESS_LUTS(type, comps) { \
450         for( int y=row1; y<row2; ++y ) { \
451                 type *ip = (type*)irows[y]; \
452                 type *op = (type*)orows[y]; \
453                 for( int x=0; x<w; ++x ) { \
454                         for( int i=0; i<3; ++i ) { \
455                                 lut_t omn = omin[i], omx = omax[i]; \
456                                 lut_t **lut = luts[i], v = ozro[i]; \
457                                 for( int j=0; j<3; ++j ) v += lut[j][ip[j]]; \
458                                 op[i] = (v<omn ? omn : v>omx ? omx : v) >> s; \
459                         } \
460                         ip += comps;  op += comps; \
461                 } \
462         } \
463 }
464
465 #define PROCESS_EQNS(type, comps) { \
466         for( int y=row1; y<row2; ++y ) { \
467                 type *ip = (type*)irows[y]; \
468                 type *op = (type*)orows[y]; \
469                 for( int x=0; x<w; ++x ) { \
470                         for( int i=0; i<3; ++i ) { \
471                                 float omn = omnf[i], omx = omxf[i]; \
472                                 type v = ozrf[i];  float *eqn = eqns[i]; \
473                                 for( int j=0; j<3; ++j ) v += eqn[j]*(ip[j] - izrf[j]); \
474                                 op[i] = v<omn ? omn : v>omx ? omx : v; \
475                         } \
476                         ip += comps;  op += comps; \
477                 } \
478         } \
479 }
480
481 void XTable::process(VFrame *inp, VFrame *out, int row1, int row2)
482 {
483         int s = len == 0x100 ? (24-8) : (24-16);
484         int w = inp->get_w(), comps = 3;
485         uint8_t **irows = inp->get_rows();
486         uint8_t **orows = out->get_rows();
487
488         switch( inp->get_color_model() ) {
489         case BC_RGBA8888:
490         case BC_YUVA8888:
491                 comps = 4;
492         case BC_RGB888:
493         case BC_YUV888:
494                 PROCESS_LUTS(uint8_t, comps);
495                 break;
496         case BC_RGBA16161616:
497         case BC_YUVA16161616:
498                 comps = 4;
499         case BC_RGB161616:
500         case BC_YUV161616:
501                 PROCESS_LUTS(uint16_t, comps);
502                 break;
503         case BC_RGBA_FLOAT:
504                 comps = 4;
505         case BC_RGB_FLOAT:
506                 PROCESS_EQNS(float, comps);
507                 break;
508         }
509 }
510
511 int ColorSpaceMain::process_realtime(VFrame *input, VFrame *output)
512 {
513         load_configuration();
514 //printf(" inv=%d, ispc=%d, irng=%d, ospc=%d, orng=%d\n", config.inverse,
515 // config.inp_colorspace, config.inp_colorrange,
516 // config.out_colorspace, config.out_colorrange);
517         if( input->get_color_model() == output->get_color_model() &&
518             config.inp_colorspace == config.out_colorspace &&
519             config.inp_colorrange == config.out_colorrange )
520                 return 0;
521
522         int color_model = input->get_color_model();
523 // opengl < 0, float == 0, tables > 0
524         int len = get_use_opengl() ? -1 :
525                 BC_CModels::is_float(color_model) ? 0 :
526                 BC_CModels::calculate_pixelsize(color_model)/
527                     BC_CModels::components(color_model) == 2 ?
528                          0x10000 : 0x100;
529         if( !engine && len >= 0 ) {
530                 int cpus = output->get_w()*output->get_h() / 0x80000 + 1;
531                 int max_cpus = BC_Resources::machine_cpus / 2;
532                 if( cpus > max_cpus ) cpus = max_cpus;
533                 if( cpus < 1 ) cpus = 1;
534                 engine = new ColorSpaceEngine(this, cpus);
535         }
536
537         if( !xtable ) xtable = new XTable();
538         xtable->init(len, config.inverse,
539                 color_model, config.inp_colorspace, config.inp_colorrange,
540                 color_model, config.out_colorspace, config.out_colorrange);
541         inp = input;  out = output;
542         if( get_use_opengl() )
543                 run_opengl();
544         else
545                 engine->process_packages();
546         return 0;
547 }
548
549
550 void ColorSpaceUnit::process_package(LoadPackage *package)
551 {
552         ColorSpacePackage *pkg = (ColorSpacePackage*)package;
553         plugin->xtable->process(plugin->inp, plugin->out, pkg->row1, pkg->row2);
554 }
555
556 ColorSpaceEngine::ColorSpaceEngine(ColorSpaceMain *plugin, int cpus)
557 // : LoadServer(1, 1)
558  : LoadServer(cpus, cpus)
559 {
560         this->plugin = plugin;
561 }
562
563 void ColorSpaceEngine::init_packages()
564 {
565         int row = 0, h = plugin->inp->get_h();
566         for( int i=0, n=get_total_packages(); i<n; ) {
567                 ColorSpacePackage *pkg = (ColorSpacePackage*)get_package(i);
568                 pkg->row1 = row;
569                 pkg->row2 = row = (++i * h) / n;
570         }
571 }
572
573 LoadClient* ColorSpaceEngine::new_client()
574 {
575         return new ColorSpaceUnit(plugin, this);
576 }
577
578 LoadPackage* ColorSpaceEngine::new_package()
579 {
580         return new ColorSpacePackage();
581 }
582
583 ColorSpacePackage::ColorSpacePackage()
584  : LoadPackage()
585 {
586 }
587
588 ColorSpaceUnit::ColorSpaceUnit(ColorSpaceMain *plugin, ColorSpaceEngine *engine)
589  : LoadClient(engine)
590 {
591         this->plugin = plugin;
592 }
593
594
595
596 int ColorSpaceMain::handle_opengl()
597 {
598 #ifdef HAVE_GL
599         static const char *colorspace_frag =
600                 "uniform sampler2D tex;\n"
601                 "uniform mat3 eqns;\n"
602                 "uniform vec3 izrf, ozrf;\n"
603                 "uniform vec3 omnf, omxf;\n"
604                 "void main()\n"
605                 "{\n"
606                 "       gl_FragColor = texture2D(tex, gl_TexCoord[0].st);\n"
607                 "       gl_FragColor.rgb = clamp((gl_FragColor.rgb-izrf)*eqns + ozrf, omnf, omxf);\n"
608                 "}\n";
609         inp->to_texture();
610         inp->enable_opengl();
611         inp->bind_texture(0);
612
613         unsigned int frag_shader = VFrame::make_shader(0, colorspace_frag, 0);
614         if( xtable && frag_shader > 0 ) {
615                 glUseProgram(frag_shader);
616                 glUniform1i(glGetUniformLocation(frag_shader, "tex"), 0);
617                 glUniformMatrix3fv(glGetUniformLocation(frag_shader, "eqns"), 1, false, xtable->eqns[0]);
618                 glUniform3fv(glGetUniformLocation(frag_shader, "izrf"), 1, xtable->izrf);
619                 glUniform3fv(glGetUniformLocation(frag_shader, "ozrf"), 1, xtable->ozrf);
620                 glUniform3fv(glGetUniformLocation(frag_shader, "omnf"), 1, xtable->omnf);
621                 glUniform3fv(glGetUniformLocation(frag_shader, "omxf"), 1, xtable->omxf);
622         }
623
624         out->enable_opengl();
625         VFrame::init_screen(out->get_w(), out->get_h());
626         out->draw_texture();
627         glUseProgram(0);
628         out->set_opengl_state(VFrame::SCREEN);
629 #endif
630         return 0;
631 }
632