Credit Andrew - BSD fixes + a little more
[goodguy/cinelerra.git] / cinelerra-5.1 / guicast / bcxfer.C
1 /*
2  * CINELERRA
3  * Copyright (C) 2016-2020 William Morrow
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published
7  * by the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18  * USA
19  */
20
21
22 void BC_Xfer::init(
23         uint8_t **output_rows, int out_colormodel, int out_x, int out_y, int out_w, int out_h,
24                 uint8_t *out_yp, uint8_t *out_up, uint8_t *out_vp, uint8_t *out_ap, int out_rowspan,
25         uint8_t **input_rows, int in_colormodel, int in_x, int in_y, int in_w, int in_h,
26                 uint8_t *in_yp, uint8_t *in_up, uint8_t *in_vp, uint8_t *in_ap, int in_rowspan,
27         int bg_color, int bg_alpha)
28 {
29         if( bg_color >= 0 ) {
30                 this->bg_a = bg_alpha; // only used with init_color
31                 this->bg_r = (bg_color>>16) & 0xff;
32                 this->bg_g = (bg_color>>8) & 0xff;
33                 this->bg_b = (bg_color>>0) & 0xff;
34         }
35         else { // bg_color < 0, no src blending
36                 switch( in_colormodel ) {
37                 case BC_RGBA8888:       in_colormodel = BC_RGBX8888;            break;
38                 case BC_RGBA16161616:   in_colormodel = BC_RGBX16161616;        break;
39                 case BC_YUVA8888:       in_colormodel = BC_YUVX8888;            break;
40                 case BC_YUVA16161616:   in_colormodel = BC_YUVX16161616;        break;
41                 case BC_RGBA_FLOAT:     in_colormodel = BC_RGBX_FLOAT;          break;
42                 }
43                 this->bg_r = this->bg_g = this->bg_b = this->bg_a = 0;
44         }
45         int pmx = BC_CModels::calculate_max(out_colormodel);
46         switch( in_colormodel ) {
47         case BC_TRANSPARENCY:
48                 if( BC_CModels::is_yuv(out_colormodel) ) {
49                         int ry = this->bg_r, gu = this->bg_g, bv = this->bg_b;
50                         YUV::yuv.rgb_to_yuv_8(ry, gu, bv);
51                         this->bg_r = ry, this->bg_g = gu, this->bg_b = bv;
52                 }
53                 if( pmx > 0xff ) {
54                         this->bg_r <<= 8;
55                         this->bg_g <<= 8;
56                         this->bg_b <<= 8;
57                         this->bg_a <<= 8;
58                 }
59                 else
60                         pmx = 0xff;
61                 break;
62         // prevent bounds errors on poorly dimensioned macro pixel formats
63         case BC_UVY422:
64         case BC_YUV422:   in_w &= ~1;               break;  // 2x1
65         case BC_YUV420P:
66         case BC_YUV420PI: in_w &= ~1;  in_h &= ~1;  break;  // 2x2
67         case BC_YUV422P:  in_w &= ~1;               break;  // 2x1
68         case BC_YUV410P:  in_w &= ~3;  in_h &= ~3;  break;  // 4x4
69         case BC_YUV411P:  in_w &= ~3;               break;  // 4x1
70         }
71         switch( out_colormodel ) {
72         case BC_UVY422:
73         case BC_YUV422:  out_w &= ~1;               break;
74         case BC_YUV420P:
75         case BC_YUV420PI: out_w &= ~1; out_h &= ~1; break;
76         case BC_YUV422P: out_w &= ~1;               break;
77         case BC_YUV410P: out_w &= ~3; out_h &= ~3;  break;
78         case BC_YUV411P: out_w &= ~3;               break;
79         }
80         this->output_rows = output_rows;
81         this->input_rows = input_rows;
82         this->out_yp = out_yp;
83         this->out_up = out_up;
84         this->out_vp = out_vp;
85         this->out_ap = out_ap;
86         this->in_yp = in_yp;
87         this->in_up = in_up;
88         this->in_vp = in_vp;
89         this->in_ap = in_ap;
90         this->in_x = in_x;
91         this->in_y = in_y;
92         this->in_w = in_w;
93         this->in_h = in_h;
94         this->out_x = out_x;
95         this->out_y = out_y;
96         this->out_w = out_w;
97         this->out_h = out_h;
98         this->in_colormodel = in_colormodel;
99         switch( in_colormodel ) {
100         case BC_GBRP:
101                 in_rowspan = in_w * sizeof(uint8_t);
102                 break;
103         case BC_RGB_FLOATP:
104         case BC_RGBA_FLOATP:
105                 if( !BC_CModels::has_alpha(out_colormodel) )
106                         this->in_colormodel = BC_RGB_FLOATP;
107                 in_rowspan = in_w * sizeof(float);
108                 break;
109         }
110         this->total_in_w = in_rowspan;
111         this->out_colormodel = out_colormodel;
112         switch( out_colormodel ) {
113         case BC_GBRP:
114                 out_rowspan = out_w * sizeof(uint8_t);
115                 break;
116         case BC_RGB_FLOATP:
117         case BC_RGBA_FLOATP:
118                 out_rowspan = out_w * sizeof(float);
119                 break;
120         }
121         this->total_out_w = out_rowspan;
122         this->in_pixelsize = BC_CModels::calculate_pixelsize(in_colormodel);
123         this->out_pixelsize = BC_CModels::calculate_pixelsize(out_colormodel);
124         if( in_w ) {
125                 this->scale = (out_w != in_w) || (in_x != 0);
126 /* + 1 so we don't overflow when calculating in advance */
127                 column_table = new int[out_w+1];
128                 double hscale = (double)in_w/out_w;
129                 for( int i=0; i<out_w; ++i ) {
130                         column_table[i] = ((int)(hscale * i) + in_x) * in_pixelsize;
131                         if( in_colormodel == BC_YUV422 || in_colormodel == BC_UVY422 )
132                                 column_table[i] &= ~3;
133                 }
134                 double vscale = (double)in_h/out_h;
135                 row_table = new int[out_h];
136                 for( int i=0; i<out_h; ++i )
137                         row_table[i] = (int)(vscale * i) + in_y;
138         }
139         else {
140                 this->scale = 0;
141                 column_table = 0;
142                 row_table = 0;
143         }
144         this->bg_fa = (float)this->bg_a / pmx;
145         this->bg_fr = (float)this->bg_r / pmx;
146         this->bg_fg = (float)this->bg_g / pmx;
147         this->bg_fb = (float)this->bg_b / pmx;
148 }
149
150 BC_Xfer::BC_Xfer(uint8_t **output_rows, uint8_t **input_rows,
151         uint8_t *out_yp, uint8_t *out_up, uint8_t *out_vp,
152         uint8_t *in_yp, uint8_t *in_up, uint8_t *in_vp,
153         int in_x, int in_y, int in_w, int in_h, int out_x, int out_y, int out_w, int out_h,
154         int in_colormodel, int out_colormodel, int bg_color, int bg_alpha,
155         int in_rowspan, int out_rowspan)
156 {
157         init(output_rows, out_colormodel, out_x, out_y, out_w, out_h,
158                 out_yp, out_up, out_vp, 0, out_rowspan,
159              input_rows, in_colormodel, in_x, in_y, in_w, in_h,
160                 in_yp, in_up, in_vp, 0, in_rowspan, bg_color, bg_alpha);
161 }
162
163 BC_Xfer::BC_Xfer(
164         uint8_t **output_ptrs, int out_colormodel,
165                 int out_x, int out_y, int out_w, int out_h, int out_rowspan,
166         uint8_t **input_ptrs, int in_colormodel,
167                 int in_x, int in_y, int in_w, int in_h, int in_rowspan,
168         int bg_color, int bg_alpha)
169 {
170         uint8_t *out_yp = 0, *out_up = 0, *out_vp = 0, *out_ap = 0;
171         uint8_t *in_yp = 0,  *in_up = 0,  *in_vp = 0,  *in_ap = 0;
172         if( BC_CModels::is_planar(in_colormodel) ) {
173                 in_yp = input_ptrs[0]; in_up = input_ptrs[1]; in_vp = input_ptrs[2];
174                 if( BC_CModels::has_alpha(in_colormodel) ) in_ap = input_ptrs[3];
175         }
176         if( BC_CModels::is_planar(out_colormodel) ) {
177                 out_yp = output_ptrs[0]; out_up = output_ptrs[1]; out_vp = output_ptrs[2];
178                 if( BC_CModels::has_alpha(out_colormodel) ) out_ap = output_ptrs[3];
179         }
180         init(output_ptrs, out_colormodel, out_x, out_y, out_w, out_h,
181                  out_yp, out_up, out_vp, out_ap, out_rowspan,
182              input_ptrs, in_colormodel, in_x, in_y, in_w, in_h,
183                  in_yp, in_up, in_vp, in_ap, in_rowspan,  bg_color, bg_alpha);
184 }
185
186 BC_Xfer::~BC_Xfer()
187 {
188         delete [] column_table;
189         delete [] row_table;
190 }
191
192 void BC_CModels::transfer(unsigned char **output_rows, unsigned char **input_rows,
193         unsigned char *out_yp, unsigned char *out_up, unsigned char *out_vp,
194         unsigned char *in_yp, unsigned char *in_up, unsigned char *in_vp,
195         int in_x, int in_y, int in_w, int in_h, int out_x, int out_y, int out_w, int out_h,
196         int in_colormodel, int out_colormodel, int bg_color,
197         int in_rowspan, int out_rowspan)
198 {
199         int ret = 1;
200         if( in_w > 0 && in_h > 0 && out_w > 0 && out_h > 0 ) {
201                 BC_Xfer xfer(output_rows, input_rows,
202                         out_yp, out_up, out_vp, in_yp, in_up, in_vp,
203                         in_x, in_y, in_w, in_h, out_x, out_y, out_w, out_h,
204                         in_colormodel, out_colormodel, bg_color,0xff, in_rowspan, out_rowspan);
205                 ret = xfer.xfer();
206         }
207         if( ret )
208                 printf("BC_CModels::transfer failed:%d %d(%dx%d) to %d(%dx%d)\n", __LINE__,
209                         in_colormodel, in_w, in_h, out_colormodel, out_w, out_h);
210 }
211
212 void BC_CModels::transfer(
213         uint8_t **output_ptrs, int out_colormodel,
214                 int out_x, int out_y, int out_w, int out_h, int out_rowspan,
215         uint8_t **input_ptrs, int in_colormodel,
216                 int in_x, int in_y, int in_w, int in_h, int in_rowspan,
217         int bg_color)
218 {
219         int ret = 1;
220         if( in_w > 0 && in_h > 0 && out_w > 0 && out_h > 0 ) {
221                 BC_Xfer xfer(
222                         output_ptrs, out_colormodel, out_x, out_y, out_w, out_h, out_rowspan,
223                         input_ptrs, in_colormodel, in_x, in_y, in_w, in_h, in_rowspan,
224                         bg_color,0xff);
225                 ret = xfer.xfer();
226         }
227         if( ret )
228                 printf("BC_CModels::transfer failed:%d %d(%dx%d) to %d(%dx%d)\n", __LINE__,
229                         in_colormodel, in_w, in_h, out_colormodel, out_w, out_h);
230 }
231
232 // color is rgb
233 int BC_CModels::init_color(int color, int alpha,
234                 unsigned char **output_rows, int out_colormodel,
235                 unsigned char *out_yp, unsigned char *out_up, unsigned char *out_vp,
236                 int out_x, int out_y, int out_w, int out_h, int out_rowspan)
237 {
238         int ret = 1;
239         if( out_w > 0 && out_h > 0 ) {
240                 BC_Xfer xfer(output_rows, 0, out_yp,out_up,out_vp, 0,0,0,
241                         0,0,0,0, out_x,out_y,out_w,out_h, BC_TRANSPARENCY,
242                         out_colormodel, color,alpha, 0, out_rowspan);
243                 ret = xfer.xfer();
244         }
245         if( ret )
246                 printf("BC_CModels::init_color failed:%d(%dx%d)\n",
247                         out_colormodel, out_w, out_h);
248         return ret;
249 }
250
251 // specialized functions
252
253 //  in bccmdl.py:  specialize("bc_rgba8888", "bc_transparency", "XFER_rgba8888_to_transparency")
254 void BC_Xfer::XFER_rgba8888_to_transparency(unsigned y0, unsigned y1)
255 {
256   for( unsigned i=y0; i<y1; ++i ) {
257     uint8_t *outp = output_rows[i + out_y] + out_x * out_pixelsize;
258     uint8_t *inp_row = input_rows[row_table[i]];
259     int bit_no = 0, bit_vec = 0;
260     for( unsigned j=0; j<out_w; ) {
261       uint8_t *inp = inp_row + column_table[j];
262       if( inp[3] < 127 ) bit_vec |= 0x01 << bit_no;
263       bit_no = ++j & 7;
264       if( !bit_no ) { *outp++ = bit_vec;  bit_vec = 0; }
265     }
266     if( bit_no ) *outp = bit_vec;
267   }
268 }
269
270 BC_Xfer::SlicerList BC_Xfer::slicers;
271
272 BC_Xfer::SlicerList::SlicerList()
273 {
274   count = 0;
275 }
276
277 BC_Xfer::SlicerList::~SlicerList()
278 {
279   reset();
280 }
281
282 void BC_Xfer::SlicerList::reset()
283 {
284   lock("BC_Xfer::SlicerList::reset");
285   while( last ) remove(last);
286   count = 0;
287   unlock();
288 }
289
290 BC_Xfer::Slicer *BC_Xfer::SlicerList::get_slicer(BC_Xfer *xp)
291 {
292   Slicer *slicer = first;
293   if( !slicer ) {
294     if( count < BC_Resources::machine_cpus ) {
295       slicer = new Slicer(xp);
296       ++count;
297     }
298   }
299   else
300     remove_pointer(slicer);
301   return slicer;
302 }
303
304 int BC_Xfer::xfer_slices(int slices)
305 {
306   if( !xfn ) return 1;
307   int max_slices = BC_Resources::machine_cpus/2;
308   if( slices > max_slices ) slices = max_slices;
309   if( slices < 1 ) slices = 1;
310   Slicer *active[slices];
311   unsigned y0 = 0, y1 = out_h;
312   int slices1 = slices-1;
313   if( slices1 > 0 ) {
314     slicers.lock("BC_Xfer::xfer_slices");
315     for( int i=0; i<slices1; y0=y1 ) {
316       Slicer *slicer = slicers.get_slicer(this);
317       if( !slicer ) { slices1 = i;  break; }
318       active[i] = slicer;
319       y1 = out_h * ++i / slices;
320       slicer->slice(this, y0, y1);
321     }
322     slicers.unlock();
323   }
324   (this->*xfn)(y0, out_h);
325   if( slices1 > 0 ) {
326     for( int i=0; i<slices1; ++i )
327       active[i]->complete->lock("BC_Xfer::xfer_slices");
328     slicers.lock("BC_Xfer::xfer_slices");
329     for( int i=0; i<slices1; ++i )
330       slicers.append(active[i]);
331     slicers.unlock();
332   }
333   return 0;
334 }
335
336 BC_Xfer::Slicer::Slicer(BC_Xfer *xp)
337 {
338   this->xp = xp;
339   init = new Condition(0, "BC_Xfer::Slicer::init", 0);
340   complete = new Condition(0, "BC_Xfer::Slicer::complete", 0);
341   done = 0;
342   start();
343 }
344 BC_Xfer::Slicer::~Slicer()
345 {
346   done = 1;
347   init->unlock();
348   join();
349   delete complete;
350   delete init;
351 }
352
353 void BC_Xfer::Slicer::slice(BC_Xfer *xp, unsigned y0, unsigned y1)
354 {
355   this->xp = xp;
356   this->y0 = y0;
357   this->y1 = y1;
358   init->unlock();
359 }
360
361 void BC_Xfer::Slicer::run()
362 {
363   while( !done ) {
364     init->lock("Slicer::run");
365     if( done ) break;
366     xfer_fn xfn = xp->xfn;
367     (xp->*xfn)(y0, y1);
368     complete->unlock();
369   }
370 }
371
372 void BC_CModels::bcxfer_stop_slicers()
373 {
374   BC_Xfer::slicers.reset();
375 }
376