1 // colourspace.c
2 // LiVES
3 // (c) G. Finch 2004 - 2020 <salsaman+lives@gmail.com>
4 // Released under the GPL 3 or later
5 // see file ../COPYING for licensing details
6 
7 // code for palette conversions
8 
9 /*
10   This program is free software; you can redistribute it and/or
11   modify it under the terms of the GNU General Public License
12   as published by the Free Software Foundation; either version 2
13   of the License, or (at your option) any later version.
14 
15   This program is distributed in the hope that it will be useful,
16   but WITHOUT ANY WARRANTY; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18   GNU General Public License for more details.
19 
20   You should have received a copy of the GNU General Public License
21   along with this program; if not, write to the Free Software
22   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 */
24 
25 // *
26 // TODO -
27 //      - resizing of single plane (including bicubic) (maybe just triplicate the values and pretend it's RGB)
28 //      - external plugins for palette conversion, resizing
29 //      - RGB(A) float, YUV10, etc.
30 
31 #include <math.h>
32 
33 #include "main.h"
34 
35 boolean weed_palette_is_sane(int pal);
36 
37 #define USE_THREADS 1 ///< set to 0 to disable threading for pixbuf operations, 1 to enable. Other values are invalid.
38 
39 #ifdef USE_SWSCALE
40 
41 #ifdef FF_API_PIX_FMT
42 typedef enum PixelFormat swpixfmt;
43 #else
44 typedef enum AVPixelFormat swpixfmt;
45 #endif
46 
47 #if USE_THREADS
48 #include <pthread.h>
49 typedef struct {
50   volatile boolean in_use;
51   int num;
52   int offset;
53   int iwidth, iheight;
54   int irow[4];
55   swpixfmt ipixfmt;
56   int width, height;
57   int orow[4];
58   swpixfmt opixfmt;
59   int flags;
60   int subspace;
61   int iclamping, oclamp_hint;
62 } swsctx_block;
63 
64 #define MAX_SWS_BLOCKS 8192
65 #define MAX_SWS_CTX 65536
66 
67 static volatile int nb = 0;
68 static volatile int swctx_count = 0;
69 static swsctx_block bloxx[MAX_SWS_BLOCKS];
70 static struct SwsContext *swscalep[MAX_SWS_CTX];
71 static pthread_mutex_t ctxcnt_mutex = PTHREAD_MUTEX_INITIALIZER;
72 
sws_getblock(int nreq,int iwidth,int iheight,int * irow,swpixfmt ipixfmt,int width,int height,int * orow,swpixfmt opixfmt,int flags,int subspace,int iclamping,int oclamp_hint)73 static swsctx_block *sws_getblock(int nreq, int iwidth, int iheight, int *irow, swpixfmt ipixfmt, int width, int height,
74                                   int *orow, swpixfmt opixfmt, int flags, int subspace, int iclamping, int oclamp_hint) {
75   swsctx_block *block, *bestblock;
76   int max = MAX_THREADS + 1, minbnum = max, mingnum = minbnum, minanum = mingnum, num;
77   int i = -1, lastblock = THREADVAR(last_sws_block), j = 0, bestidx = -1;
78 
79   if (lastblock >= 0) j = lastblock;
80   else i = 0;
81 
82   pthread_mutex_lock(&ctxcnt_mutex);
83 
84   for (; i < nb; j = ++i) {
85     block = &bloxx[j];
86     if (!block->in_use && (num = block->num) >= nreq) {
87       if (iwidth == block->iwidth
88           && iheight == block->iheight
89           && ipixfmt == block->ipixfmt
90           && width == block->width
91           && height == block->height
92           && opixfmt == block->opixfmt
93           && flags == block->flags) {
94         if (subspace == block->subspace
95             && iclamping == block->iclamping
96             && oclamp_hint == block->oclamp_hint
97             && irow[0] == block->irow[0]
98             && irow[1] == block->irow[1]
99             && irow[2] == block->irow[2]
100             && irow[3] == block->irow[3]
101             && orow[0] == block->orow[0]
102             && orow[1] == block->orow[1]
103             && orow[2] == block->orow[2]
104             && orow[3] == block->orow[3]
105            ) {
106           if (num < minbnum) {
107             minbnum = num;
108             bestidx = j;
109             //g_print("%d is perfect match !\n", i);
110             if (num == nreq) {
111               //if (i == -1) g_print("BINGO !\n");
112               break;
113             }
114           }
115         } else {
116           if (minbnum == max) {
117             if (num < mingnum) {
118               bestidx = j;
119               mingnum = num;
120 	    // *INDENT-OFF*
121 	    }}}}
122       else {
123 	if (minbnum == max && mingnum == max) {
124 	  if (num < minanum) {
125 	    bestidx = j;
126 	    minanum = num;
127 	}}}}}
128   // *INDENT-ON*
129 
130   if (minbnum < max) {
131     bestblock = &bloxx[bestidx];
132     bestblock->in_use = TRUE;
133     pthread_mutex_unlock(&ctxcnt_mutex);
134     THREADVAR(last_sws_block) = bestidx;
135   } else {
136     int startctx = swctx_count, endctx = startctx + nreq;
137     if (endctx >= MAX_SWS_CTX
138         || nb  >= MAX_SWS_BLOCKS - 1) {
139       if (bestidx == -1) abort();
140       bestblock = &bloxx[bestidx];
141       bestblock->in_use = TRUE;
142       pthread_mutex_unlock(&ctxcnt_mutex);
143       THREADVAR(last_sws_block) = bestidx;
144     } else {
145       bestblock = &bloxx[nb++];
146       bestblock->in_use = TRUE;
147       swctx_count = endctx;
148       pthread_mutex_unlock(&ctxcnt_mutex);
149 
150       bestblock->num = nreq;
151       bestblock->offset = startctx;
152       for (i = startctx; i < endctx; i++) swscalep[i] = NULL;
153     }
154 
155     bestblock->iwidth = iwidth;
156     bestblock->iheight = iheight;
157     bestblock->ipixfmt = ipixfmt;
158     bestblock->width = width;
159     bestblock->height = height;
160     bestblock->opixfmt = opixfmt;
161     bestblock->flags = flags;
162 
163     bestblock->subspace = subspace;
164     bestblock->iclamping = iclamping;
165     bestblock->oclamp_hint = oclamp_hint;
166     for (i = 0; i < 4; i++) {
167       bestblock->irow[i] = irow[i];
168       bestblock->orow[i] = orow[i];
169     }
170   }
171 
172   //g_print("NCTX = %d\n", swctx_count);
173   return bestblock;
174 }
175 
sws_freeblock(swsctx_block * block)176 LIVES_LOCAL_INLINE void sws_freeblock(swsctx_block * block) {
177   block->in_use = FALSE;
178 }
179 
180 #else
181 static struct SwsContext *swscale = NULL;
182 #endif
183 
184 #endif // USE_SWSCALE
185 
186 #include "cvirtual.h"
187 #include "effects-weed.h"
188 
189 static boolean unal_inited = FALSE;
190 
191 #ifdef GUI_GTK
192 // from gdk-pixbuf.c
193 /* Always align rows to 32-bit boundaries */
194 # define get_pixbuf_rowstride_value(rowstride) ((rowstride + 3) & ~3)
195 #else
196 # define get_pixbuf_rowstride_value(rowstride) (rowstride)
197 #endif
198 
199 #ifdef GUI_GTK
200 // from gdkpixbuf
201 #define get_last_pixbuf_rowstride_value(width, nchans) (width * (((nchans << 3) + 7) >> 3))
202 #else
203 #define get_last_pixbuf_rowstride_value(width, nchans) (width * nchans)
204 #endif
205 
206 
lives_free_buffer(uint8_t * pixels,livespointer data)207 static void lives_free_buffer(uint8_t *pixels, livespointer data) {
208   lives_free(pixels);
209 }
210 
211 #define CLAMP0255(a)  ((unsigned char)((((-a) >> 31) & a) | (255 - a) >> 31) )
212 #define CLAMP0255f(a)  (a > 255. ? 255.: a < 0. ? 0. : a)
213 #define CLAMP0255fi(a)  ((int)(a > 255. ? 255.: a < 0. ? 0. : a))
214 
215 /* precomputed tables */
216 
217 // generic
218 static int *Y_R;
219 static int *Y_G;
220 static int *Y_B;
221 static int *Cb_R;
222 static int *Cb_G;
223 static int *Cb_B;
224 static int *Cr_R;
225 static int *Cr_G;
226 static int *Cr_B;
227 
228 // clamped Y'CbCr
229 static int Y_Rc[256];
230 static int Y_Gc[256];
231 static int Y_Bc[256];
232 static int Cb_Rc[256];
233 static int Cb_Gc[256];
234 static int Cb_Bc[256];
235 static int Cr_Rc[256];
236 static int Cr_Gc[256];
237 static int Cr_Bc[256];
238 
239 // unclamped Y'CbCr
240 static int Y_Ru[256];
241 static int Y_Gu[256];
242 static int Y_Bu[256];
243 static int Cb_Ru[256];
244 static int Cb_Gu[256];
245 static int Cb_Bu[256];
246 static int Cr_Ru[256];
247 static int Cr_Gu[256];
248 static int Cr_Bu[256];
249 
250 // clamped BT.709
251 static int HY_Rc[256];
252 static int HY_Gc[256];
253 static int HY_Bc[256];
254 static int HCb_Rc[256];
255 static int HCb_Gc[256];
256 static int HCb_Bc[256];
257 static int HCr_Rc[256];
258 static int HCr_Gc[256];
259 static int HCr_Bc[256];
260 
261 // unclamped BT.709
262 static int HY_Ru[256];
263 static int HY_Gu[256];
264 static int HY_Bu[256];
265 static int HCb_Ru[256];
266 static int HCb_Gu[256];
267 static int HCb_Bu[256];
268 static int HCr_Ru[256];
269 static int HCr_Gu[256];
270 static int HCr_Bu[256];
271 
272 static boolean conv_RY_inited = FALSE;
273 
274 // generic
275 static int *RGB_Y;
276 static int *R_Cr;
277 static int *G_Cb;
278 static int *G_Cr;
279 static int *B_Cb;
280 
281 // clamped Y'CbCr
282 static int RGB_Yc[256];
283 static int R_Crc[256];
284 static int G_Cbc[256];
285 static int G_Crc[256];
286 static int B_Cbc[256];
287 
288 // unclamped Y'CbCr
289 static int RGB_Yu[256];
290 static int R_Cru[256];
291 static int G_Cru[256];
292 static int G_Cbu[256];
293 static int B_Cbu[256];
294 
295 // clamped BT.709
296 static int HRGB_Yc[256];
297 static int HR_Crc[256];
298 static int HG_Crc[256];
299 static int HG_Cbc[256];
300 static int HB_Cbc[256];
301 
302 // unclamped BT.709
303 static int HRGB_Yu[256];
304 static int HR_Cru[256];
305 static int HG_Cru[256];
306 static int HG_Cbu[256];
307 static int HB_Cbu[256];
308 
309 static boolean conv_YR_inited = FALSE;
310 
311 static short min_Y, max_Y, min_UV, max_UV;
312 
313 // averaging
314 static uint8_t *cavg;
315 static uint8_t cavgc[256][256];
316 static uint8_t cavgu[256][256];
317 static uint8_t cavgrgb[256][256];
318 static boolean avg_inited = FALSE;
319 
320 // pre-post multiply alpha
321 static int unal[256][256];
322 static int al[256][256];
323 static int unalcy[256][256];
324 static int alcy[256][256];
325 static int unalcuv[256][256];
326 static int alcuv[256][256];
327 
328 // clamping and subspace converters
329 
330 // generic
331 static uint8_t *Y_to_Y;
332 static uint8_t *U_to_U;
333 static uint8_t *V_to_V;
334 
335 // same subspace, clamped to unclamped
336 static uint8_t Yclamped_to_Yunclamped[256];
337 static uint8_t UVclamped_to_UVunclamped[256];
338 
339 // same subspace, unclamped to clamped
340 static uint8_t Yunclamped_to_Yclamped[256];
341 static uint8_t UVunclamped_to_UVclamped[256];
342 
343 static boolean conv_YY_inited = FALSE;
344 
345 
346 // gamma correction
347 
348 /// linear -> gamma:
349 // x <= b ?  x * c : (a + 1) * powf(x, 1 / G) - a
350 
351 /// gamma to linear:
352 // inv: x < d ? x / c : powf((x + a) / (a + 1), G)
353 
354 /// b = d / c
355 
356 // for sRGB:
357 // a = 0.055, b = 0.0031308, c = 12.92, d = 0.04045, G = 2.4
358 
359 /// for bt709:
360 /// a = 0.099, b = 0.018, c = 4.5, d = 0.081, G = 2.22222222
361 
362 static gamma_const_t gamma_tx[3];
363 
364 static uint8_t *gamma_s2l = NULL;
365 static uint8_t *gamma_l2s = NULL;
366 static uint8_t *gamma_b2l = NULL;
367 static uint8_t *gamma_l2b = NULL;
368 static uint8_t *gamma_s2b = NULL;
369 static uint8_t *gamma_b2s = NULL;
370 
create_gamma_lut(double fileg,int gamma_from,int gamma_to)371 static inline uint8_t *create_gamma_lut(double fileg, int gamma_from, int gamma_to) {
372   uint8_t *gamma_lut;
373   float inv_gamma = 0.;
374   float a, x = 0.;
375   int i;
376 
377   if (fileg == 1.0) {
378     if (gamma_to == WEED_GAMMA_UNKNOWN || gamma_from == WEED_GAMMA_UNKNOWN) return NULL;
379     if (gamma_from == WEED_GAMMA_LINEAR && gamma_to == WEED_GAMMA_SRGB && gamma_l2s) return gamma_l2s;
380     if (gamma_from == WEED_GAMMA_LINEAR && gamma_to == WEED_GAMMA_BT709 && gamma_l2b) return gamma_l2b;
381     if (gamma_from == WEED_GAMMA_SRGB && gamma_to == WEED_GAMMA_LINEAR && gamma_s2l) return gamma_s2l;
382     if (gamma_from == WEED_GAMMA_SRGB && gamma_to == WEED_GAMMA_BT709 && gamma_s2b) return gamma_s2b;
383     if (gamma_from == WEED_GAMMA_BT709 && gamma_to == WEED_GAMMA_LINEAR && gamma_b2l) return gamma_b2l;
384     if (gamma_from == WEED_GAMMA_BT709 && gamma_to == WEED_GAMMA_SRGB && gamma_b2s) return gamma_b2s;
385   }
386 
387   gamma_lut = lives_calloc(4, 64);
388   if (!gamma_lut) return NULL;
389 
390   if (gamma_to == WEED_GAMMA_MONITOR) {
391     inv_gamma = 1. / (float)prefs->screen_gamma;
392   }
393 
394   gamma_lut[0] = 0;
395 
396   for (i = 1; i < 256; ++i) {
397     if (gamma_from == gamma_to && fileg == 1.0) {
398       gamma_lut[i] = i;
399       continue;
400     }
401 
402     x = a = (float)i / 255.;
403 
404     if (fileg != 1.0) {
405       x = powf(a, fileg);
406     }
407 
408     if (1) {
409       switch (gamma_to) {
410       // simple power law transformation
411       case WEED_GAMMA_MONITOR:
412       case WEED_GAMMA_SRGB:
413         // sRGB gamma
414         switch (gamma_from) {
415         case WEED_GAMMA_BT709:
416           // conv to linear first
417           a = (a < gamma_tx[WEED_GAMMA_BT709].thresh) ? a / gamma_tx[WEED_GAMMA_BT709].lin
418               : powf((a + gamma_tx[WEED_GAMMA_BT709].offs) / (1. + gamma_tx[WEED_GAMMA_BT709].offs),
419                      gamma_tx[WEED_GAMMA_BT709].pf);
420         case WEED_GAMMA_LINEAR:
421           x = (a < (gamma_tx[WEED_GAMMA_SRGB].thresh) / gamma_tx[WEED_GAMMA_SRGB].lin)
422               ? a * gamma_tx[WEED_GAMMA_SRGB].lin
423               : powf((1. + gamma_tx[WEED_GAMMA_SRGB].offs) * a,
424                      1. / gamma_tx[WEED_GAMMA_SRGB].pf) - gamma_tx[WEED_GAMMA_SRGB].offs;
425 
426           if (gamma_to == WEED_GAMMA_MONITOR)
427             x = powf(a, inv_gamma);
428           break;
429         case WEED_GAMMA_MONITOR:
430           x = powf(a, prefs->screen_gamma);
431           break;
432         default:
433           break;
434         }
435         break;
436 
437       case WEED_GAMMA_LINEAR:
438         switch (gamma_from) {
439         case WEED_GAMMA_MONITOR:
440           x = powf(a, prefs->screen_gamma);
441           break;
442         case WEED_GAMMA_SRGB:
443           x = (a < gamma_tx[WEED_GAMMA_SRGB].thresh) ? a / gamma_tx[WEED_GAMMA_SRGB].lin
444               : powf((a + gamma_tx[WEED_GAMMA_SRGB].offs) / (1. + gamma_tx[WEED_GAMMA_SRGB].offs),
445                      gamma_tx[WEED_GAMMA_SRGB].pf);
446           break;
447         case WEED_GAMMA_BT709:
448           x = (a < gamma_tx[WEED_GAMMA_BT709].thresh) ? a / gamma_tx[WEED_GAMMA_BT709].lin
449               : powf((a + gamma_tx[WEED_GAMMA_BT709].offs) / (1. + gamma_tx[WEED_GAMMA_BT709].offs),
450                      gamma_tx[WEED_GAMMA_BT709].pf);
451           break;
452         default:
453           break;
454         }
455       // rec 709 gamma
456       case WEED_GAMMA_BT709:
457         switch (gamma_from) {
458         case WEED_GAMMA_MONITOR:
459           x = powf(a, prefs->screen_gamma);
460           break;
461         case WEED_GAMMA_SRGB:
462           // convert first to linear
463           a = (a < gamma_tx[WEED_GAMMA_SRGB].thresh) ? a / gamma_tx[WEED_GAMMA_SRGB].lin
464               : powf((a + gamma_tx[WEED_GAMMA_SRGB].offs) / (1. + gamma_tx[WEED_GAMMA_SRGB].offs),
465                      gamma_tx[WEED_GAMMA_SRGB].pf);
466         case WEED_GAMMA_LINEAR:
467           x = (a < (gamma_tx[WEED_GAMMA_BT709].thresh) / gamma_tx[WEED_GAMMA_BT709].lin)
468               ? a * gamma_tx[WEED_GAMMA_BT709].lin
469               : powf((1. + gamma_tx[WEED_GAMMA_BT709].offs) * a,
470                      1. / gamma_tx[WEED_GAMMA_BT709].pf) - gamma_tx[WEED_GAMMA_BT709].offs;
471         default:
472           break;
473         }
474         break;
475 
476       default:
477         break;
478       }
479     }
480     gamma_lut[i] = CLAMP0255((int32_t)(255. * x + .5));
481   }
482   if (gamma_from == WEED_GAMMA_LINEAR && gamma_to == WEED_GAMMA_SRGB && gamma_l2s)
483     gamma_l2s = gamma_lut;
484   if (gamma_from == WEED_GAMMA_LINEAR && gamma_to == WEED_GAMMA_BT709 && gamma_l2b)
485     gamma_l2b = gamma_lut;
486   if (gamma_from == WEED_GAMMA_SRGB && gamma_to == WEED_GAMMA_LINEAR && gamma_s2l)
487     gamma_s2l = gamma_lut;
488   if (gamma_from == WEED_GAMMA_SRGB && gamma_to == WEED_GAMMA_BT709 && gamma_s2b)
489     gamma_s2b = gamma_lut;
490   if (gamma_from == WEED_GAMMA_BT709 && gamma_to == WEED_GAMMA_LINEAR && gamma_b2l)
491     gamma_b2l = gamma_lut;
492   if (gamma_from == WEED_GAMMA_BT709 && gamma_to == WEED_GAMMA_SRGB && gamma_b2s)
493     gamma_b2s = gamma_lut;
494   return gamma_lut;
495 }
496 
lives_gamma_lut_free(uint8_t * lut)497 static inline void lives_gamma_lut_free(uint8_t *lut) {
498   if (lut && lut != gamma_l2s && lut != gamma_l2b && lut != gamma_s2l && lut != gamma_s2b
499       && lut != gamma_b2s && lut != gamma_b2l) lives_free(lut);
500 }
501 
502 
_spc_rnd(int32_t val,short quality)503 static inline int32_t _spc_rnd(int32_t val, short quality) {
504   // if USE_EXTEND is defined,
505   // instead of shifting right by 16 bits, we multiplied x by scale_factor, ie., 0xFF -> 0xFFFFFF
506   // to convert back we can either shift right 16 bits (less accurate), or divide by scale_factor
507   // i.e divide by 65793 (the default)
508   // We note that 65793 == 241 * 273  = (256 - 16 + 1) * (256 + 16 + 1)
509   // val = x / (256 - 16 + 1)  / (256 + 16 - 1)
510   // = x . (16 + 1) / ((256 - 16 + 1) . (16 + 1))  * (16 - 1) / ((256 + 16 + 1) . (16 - 1))
511   // = x. (16 + 1) / (256 . 16 + 256 - 256 - 16 + 16 + 1)   * (16 - 1) / (256 * 16 - 256 + 256 - 16 + 16 - 1)
512   // (x . 16 + x) / (256 . 16 + 1) . (16 - 1) . (256 * 16 - 1) ~= ((x << 4) + x) >> 12 * (16 - 1) / 256 * 16
513 
514   // let a = (x << 4) + x
515 
516   // (((a >> 12) << 4) - a >> 12) >> 12
517   // ((((x >> 8 + x >> 12) << 4 - x >> 8 - x >> 12) >> 12
518 
519   // (x >> 4 + x >> 8 - x >> 8 - x >> 12) >> 12
520   // (x >> 4 - x >> 12) >> 12
521   // i.e (x  - (x >> 8)) >> 16
522   // the net effect is that the highest bit is subtracted from the bit below the lsb.
523   // e.g. 0xFFFFFF -> 0XFFFFFF - 0X00FFFF >> 16 = 0xFF
524   // 0xA1B2C3 -> 0XA1B2C3 - 0x00A1B2 = 0XA11111 >> 16 = 0xA1
525   // but: 0xB1A1A1 -> 0xB1A1A1 - 0x00B1A1 = 0xB0F000 >> 16 = 0xB0
526   // i.e the lowest bit is rounded rather than simply truncated:
527   // if we are adding several factors we can do the conversion after the addition
528   // i.e the rounding error when converting from RGB to YUV goes from 1. / 255. ~= 0.4 % to half of that, i.e 0.2 %
529 
530   if (quality == PB_QUALITY_LOW) {
531     return val >> FP_BITS;
532   }
533   if (quality == PB_QUALITY_MED) {
534     uint32_t sig = val & 0x80000000;
535     return (((val - (val >> 8)) >> 16) | sig);
536   }
537   return ((float)val / SCALE_FACTOR + .5);
538 }
539 
540 
541 #define spc_rnd(val) (_spc_rnd((val), prefs ? prefs->pb_quality : PB_QUALITY_HIGH))
542 
543 
round_special(int32_t val)544 LIVES_GLOBAL_INLINE int32_t round_special(int32_t val) {
545   return spc_rnd(val);
546 }
547 
548 
get_luma8(uint8_t r,uint8_t g,uint8_t b)549 double get_luma8(uint8_t r, uint8_t g, uint8_t b) {
550   /// return luma value between 0. (black) and 1. (white)
551   short a = _spc_rnd(Y_Ru[r] + Y_Gu[g] + Y_Bu[b], PB_QUALITY_HIGH);
552   if (a > 255) a = 255;
553   return a < 0 ? 0. : (double)a / 255.;
554 }
555 
556 
get_luma16(uint16_t r,uint16_t g,uint16_t b)557 double get_luma16(uint16_t r, uint16_t g, uint16_t b) {
558   /// return luma value between 0. (black) and 1. (white)
559   return get_luma8(r >> 8, g >> 8, b >> 8);
560 }
561 
562 
init_RGB_to_YUV_tables(void)563 static void init_RGB_to_YUV_tables(void) {
564   register int i;
565   // Digital Y'UV proper [ITU-R BT.601-5] for digital NTSC (NTSC analog uses YIQ I think)
566   // a.k.a CCIR 601, aka bt470bg (with gamma = 2.8 ?), bt470m (gamma = 2.2), aka SD
567   // uses Kr = 0.299 and Kb = 0.114
568   // offs U,V = 128
569 
570   // (I call this subspace YUV_SUBSPACE_YCBCR)
571 
572   // this is used for e.g. theora encoding, and for most video cards
573 
574   // input is linear RGB, output is gamma corrected Y'UV
575 
576   // bt.709 (HD)
577 
578   // input is linear RGB, output is gamma corrected Y'UV
579 
580   // except for bt2020 which gamma corrects the Y (only) after conversion (?)
581 
582   // there is also smpte 170 / smpte 240 (NTSC), bt.1886 (?), smpte2084, and bt2020
583 
584   // bt.1886 : gamma 2.4
585 
586   // bt2020: UHD, 10/12 bit colour
587 
588   double fac;
589 
590   for (i = 0; i < 256; i++) {
591     Y_Rc[i] = myround(KR_YCBCR * (double)i * CLAMP_FACTOR_Y * SCALE_FACTOR);   // Kr
592     Y_Gc[i] = myround((1. - KR_YCBCR - KB_YCBCR) * (double)i * CLAMP_FACTOR_Y * SCALE_FACTOR);   // Kb
593     Y_Bc[i] = myround((KB_YCBCR * (double)i * CLAMP_FACTOR_Y + YUV_CLAMP_MIN) * SCALE_FACTOR);
594 
595     fac = .5 / (1. - KB_YCBCR); // .564
596 
597     Cb_Rc[i] = myround(-fac * KR_YCBCR * (double)i * CLAMP_FACTOR_UV  * SCALE_FACTOR); // -.16736
598     Cb_Gc[i] = myround(-fac * (1. - KB_YCBCR - KR_YCBCR)  * (double)i * CLAMP_FACTOR_UV * SCALE_FACTOR); // -.331264
599     Cb_Bc[i] = myround((0.5 * (double)i * CLAMP_FACTOR_UV + UV_BIAS) * SCALE_FACTOR);
600 
601     fac = .5 / (1. - KR_YCBCR); // .713
602 
603     Cr_Rc[i] = myround((0.5 * (double)i * CLAMP_FACTOR_UV + UV_BIAS) * SCALE_FACTOR);
604     Cr_Gc[i] = myround(-fac * (1. - KB_YCBCR - KR_YCBCR) * (double)i * CLAMP_FACTOR_UV * SCALE_FACTOR);
605     Cr_Bc[i] = myround(-fac * KB_YCBCR * (double)i * CLAMP_FACTOR_UV * SCALE_FACTOR);
606   }
607 
608   for (i = 0; i < 256; i++) {
609     Y_Ru[i] = myround(KR_YCBCR * (double)i * SCALE_FACTOR);   // Kr
610     Y_Gu[i] = myround((1. - KR_YCBCR - KB_YCBCR) * (double)i * SCALE_FACTOR);   // Kb
611     Y_Bu[i] = myround(KB_YCBCR * (double)i * SCALE_FACTOR);
612 
613     fac = .5 / (1. - KB_YCBCR); // .564
614 
615     Cb_Ru[i] = myround(-fac * KR_YCBCR * (double)i * SCALE_FACTOR); // -.16736
616     Cb_Gu[i] = myround(-fac * (1. - KB_YCBCR - KR_YCBCR)  * (double)i * SCALE_FACTOR); // -.331264
617     Cb_Bu[i] = myround((0.5 * (double)i + UV_BIAS) * SCALE_FACTOR);
618 
619     fac = .5 / (1. - KR_YCBCR); // .713
620 
621     Cr_Ru[i] = myround((0.5 * (double)i + UV_BIAS) * SCALE_FACTOR);
622     Cr_Gu[i] = myround(-fac * (1. - KB_YCBCR - KR_YCBCR) * (double)i * SCALE_FACTOR);
623     Cr_Bu[i] = myround(-fac * KB_YCBCR * (double)i * SCALE_FACTOR);
624   }
625 
626   // Different values are used for hdtv, I call this subspace YUV_SUBSPACE_BT709
627 
628   // Kr = 0.2126
629   // Kb = 0.0722
630 
631   // converting from one subspace to another is not recommended.
632 
633   for (i = 0; i < 256; i++) {
634     HY_Rc[i] = myround(KR_BT709 * (double)i * CLAMP_FACTOR_Y * SCALE_FACTOR);   // Kr
635     HY_Gc[i] = myround((1. - KR_BT709 - KB_BT709) * (double)i * CLAMP_FACTOR_Y * SCALE_FACTOR);   // Kb
636     HY_Bc[i] = myround((KB_BT709 * (double)i * CLAMP_FACTOR_Y + YUV_CLAMP_MIN) * SCALE_FACTOR);
637 
638     fac = .5 / (1. - KB_BT709);
639 
640     HCb_Rc[i] = myround(-fac * KR_BT709 * (double)i * CLAMP_FACTOR_UV  * SCALE_FACTOR); // -.16736
641     HCb_Gc[i] = myround(-fac * (1. - KB_BT709 - KR_BT709)  * (double)i * CLAMP_FACTOR_UV * SCALE_FACTOR); // -.331264
642     HCb_Bc[i] = myround((0.5 * (double)i * CLAMP_FACTOR_UV + UV_BIAS) * SCALE_FACTOR);
643 
644     fac = .5 / (1. - KR_BT709);
645 
646     HCr_Rc[i] = myround((0.5 * (double)i * CLAMP_FACTOR_UV + UV_BIAS) * SCALE_FACTOR);
647     HCr_Gc[i] = myround(-fac * (1. - KB_BT709 - KR_BT709) * (double)i * CLAMP_FACTOR_UV * SCALE_FACTOR);
648     HCr_Bc[i] = myround(-fac * KB_BT709 * (double)i * CLAMP_FACTOR_UV * SCALE_FACTOR);
649   }
650 
651   for (i = 0; i < 256; i++) {
652     HY_Ru[i] = myround(KR_BT709 * (double)i * SCALE_FACTOR);   // Kr
653     HY_Gu[i] = myround((1. - KR_BT709 - KB_BT709) * (double)i * SCALE_FACTOR);   // Kb
654     HY_Bu[i] = myround(KB_BT709 * (double)i * SCALE_FACTOR);
655 
656     fac = .5 / (1. - KB_BT709);
657 
658     HCb_Ru[i] = myround(-fac * KR_BT709 * (double)i * SCALE_FACTOR); // -.16736
659     HCb_Gu[i] = myround(-fac * (1. - KB_BT709 - KR_BT709)  * (double)i * SCALE_FACTOR); // -.331264
660     HCb_Bu[i] = myround((0.5 * (double)i + UV_BIAS) * SCALE_FACTOR);
661 
662     fac = .5 / (1. - KR_BT709);
663 
664     HCr_Ru[i] = myround((0.5 * (double)i + UV_BIAS) * SCALE_FACTOR);
665     HCr_Gu[i] = myround(-fac * (1. - KB_BT709 - KR_BT709) * (double)i * SCALE_FACTOR);
666     HCr_Bu[i] = myround(-fac * KB_BT709 * (double)i * SCALE_FACTOR);
667   }
668 
669   conv_RY_inited = TRUE;
670 }
671 
init_YUV_to_RGB_tables(void)672 static void init_YUV_to_RGB_tables(void) {
673   register int i;
674 
675   // These values are for what I call YUV_SUBSPACE_YCBCR
676 
677   /* clip Y values under 16 */
678   for (i = 0; i <= YUV_CLAMP_MINI; i++) RGB_Yc[i] = 0;
679 
680   for (; i < Y_CLAMP_MAXI; i++) {
681     RGB_Yc[i] = myround(((double)i - YUV_CLAMP_MIN) / (Y_CLAMP_MAX - YUV_CLAMP_MIN) * 255. * SCALE_FACTOR);
682   }
683   /* clip Y values above 235 */
684   for (; i < 256; i++) RGB_Yc[i] = 255 * SCALE_FACTOR;
685 
686   /* clip Cb/Cr values below 16 */
687   for (i = 0; i <= YUV_CLAMP_MINI; i++) R_Crc[i] = G_Crc[i] = G_Cbc[i] = B_Cbc[i] = 0;
688 
689   for (; i < UV_CLAMP_MAXI; i++) {
690     R_Crc[i] = myround(2. * (1. - KR_YCBCR) * ((((double)i - YUV_CLAMP_MIN) /
691                        (UV_CLAMP_MAX - YUV_CLAMP_MIN) * 255.) - UV_BIAS) * SCALE_FACTOR); // 2*(1-Kr)
692 
693     G_Cbc[i] = myround(-.5 / (1. + KB_YCBCR + KR_YCBCR) * ((((double)i - YUV_CLAMP_MIN) /
694                        (UV_CLAMP_MAX - YUV_CLAMP_MIN) * 255.) - UV_BIAS) * SCALE_FACTOR);
695 
696     G_Crc[i] = myround(-.5 / (1. - KR_YCBCR) * ((((double)i - YUV_CLAMP_MIN) /
697                        (UV_CLAMP_MAX - YUV_CLAMP_MIN) * 255.) - UV_BIAS) * SCALE_FACTOR);
698 
699     B_Cbc[i] = myround(2. * (1. - KB_YCBCR) * ((((double)i - YUV_CLAMP_MIN) /
700                        (UV_CLAMP_MAX - YUV_CLAMP_MIN) * 255.) - UV_BIAS) * SCALE_FACTOR); // 2*(1-Kb)
701   }
702   /* clip Cb/Cr values above 240 */
703   for (; i < 256; i++) {
704     R_Crc[i] = myround(2. * (1. - KR_YCBCR) * (((UV_CLAMP_MAX - YUV_CLAMP_MIN) /
705                        (UV_CLAMP_MAX - YUV_CLAMP_MIN) * 255.) - UV_BIAS) * SCALE_FACTOR); // 2*(1-Kr)
706     G_Crc[i] = myround(-.5 / (1. - KR_YCBCR) * (((UV_CLAMP_MAX - YUV_CLAMP_MIN) /
707                        (UV_CLAMP_MAX - YUV_CLAMP_MIN) * 255.) - UV_BIAS) * SCALE_FACTOR);
708     G_Cbc[i] = myround(-.5 / (1. + KB_YCBCR + KR_YCBCR) * (((UV_CLAMP_MAX - YUV_CLAMP_MIN) /
709                        (UV_CLAMP_MAX - YUV_CLAMP_MIN) * 255.) - UV_BIAS) * SCALE_FACTOR);
710     B_Cbc[i] = myround(2. * (1. - KB_YCBCR) * (((UV_CLAMP_MAX - YUV_CLAMP_MIN) /
711                        (UV_CLAMP_MAX - YUV_CLAMP_MIN) * 255.) - UV_BIAS) * SCALE_FACTOR); // 2*(1-Kb)
712   }
713 
714   // unclamped Y'CbCr
715   for (i = 0; i <= 255; i++) {
716     RGB_Yu[i] = i * SCALE_FACTOR;
717   }
718 
719   for (i = 0; i <= 255; i++) {
720     R_Cru[i] = myround(2. * (1. - KR_YCBCR) * ((double)i - UV_BIAS) * SCALE_FACTOR); // 2*(1-Kr)
721     G_Cru[i] = myround(-.5 / (1. - KR_YCBCR) * ((double)i - UV_BIAS) * SCALE_FACTOR);
722     G_Cbu[i] = myround(-.5 / (1. + KB_YCBCR + KR_YCBCR) * ((double)i - UV_BIAS) * SCALE_FACTOR);
723     B_Cbu[i] = myround(2. * (1. - KB_YCBCR) * ((double)i - UV_BIAS) * SCALE_FACTOR); // 2*(1-Kb)
724   }
725 
726   // These values are for what I call YUV_SUBSPACE_BT709
727 
728   /* clip Y values under 16 */
729   for (i = 0; i <= YUV_CLAMP_MINI; i++) HRGB_Yc[i] = 0;
730 
731   for (; i < Y_CLAMP_MAXI; i++) {
732     HRGB_Yc[i] = myround(((double)i - YUV_CLAMP_MIN) / (Y_CLAMP_MAX - YUV_CLAMP_MIN) * 255. * SCALE_FACTOR);
733   }
734 
735   /* clip Y values above 235 */
736   for (; i < 256; i++) HRGB_Yc[i] = 255 * SCALE_FACTOR;
737 
738   /* clip Cb/Cr values below 16 */
739   for (i = 0; i <= YUV_CLAMP_MINI; i++) HR_Crc[i] = HG_Crc[i] = HG_Cbc[i] = HB_Cbc[i] = 0;
740 
741   for (; i < UV_CLAMP_MAXI; i++) {
742     HR_Crc[i] = myround(2. * (1. - KR_BT709) * ((((double)i - YUV_CLAMP_MIN) /
743                         (UV_CLAMP_MAX - YUV_CLAMP_MIN) * 255.) - UV_BIAS) * SCALE_FACTOR); // 2*(1-Kr)
744     HG_Crc[i] = myround(-.5 / (1. - KR_BT709) * ((((double)i - YUV_CLAMP_MIN) /
745                         (UV_CLAMP_MAX - YUV_CLAMP_MIN) * 255.) - UV_BIAS) * SCALE_FACTOR);
746     HG_Cbc[i] = myround(-.5 / (1. + KB_BT709 + KB_BT709) * ((((double)i - YUV_CLAMP_MIN) /
747                         (UV_CLAMP_MAX - YUV_CLAMP_MIN) * 255.) - UV_BIAS) * SCALE_FACTOR);
748     HB_Cbc[i] = myround(2. * (1. - KB_BT709) * ((((double)i - YUV_CLAMP_MIN) /
749                         (UV_CLAMP_MAX - YUV_CLAMP_MIN) * 255.) - UV_BIAS) * SCALE_FACTOR); // 2*(1-Kb)
750   }
751   /* clip Cb/Cr values above 240 */
752   for (; i < 256; i++) {
753     HR_Crc[i] = myround(2. * (1. - KR_BT709) * (255. - UV_BIAS) * SCALE_FACTOR); // 2*(1-Kr)
754     HG_Crc[i] = myround(-.5 / (1. - KR_BT709) * (255. - UV_BIAS) * SCALE_FACTOR);
755     HG_Cbc[i] = myround(-.5 / (1. + KB_BT709 + KB_BT709) * (255. - UV_BIAS) * SCALE_FACTOR);
756     HB_Cbc[i] = myround(2. * (1. - KB_BT709) * (255. - UV_BIAS) * SCALE_FACTOR); // 2*(1-Kb)
757   }
758 
759   // unclamped Y'CbCr
760   for (i = 0; i <= 255; i++) HRGB_Yu[i] = i * SCALE_FACTOR;
761 
762   for (i = 0; i <= 255; i++) {
763     HR_Cru[i] = myround(2. * (1. - KR_BT709) * ((double)i - UV_BIAS) * SCALE_FACTOR); // 2*(1-Kr)
764     HG_Cru[i] = myround(-.5 / (1. - KR_BT709) * ((double)i - UV_BIAS) * SCALE_FACTOR);
765     HG_Cbu[i] = myround(-.5 / (1. + KB_BT709 + KB_BT709) * ((double)i - UV_BIAS) * SCALE_FACTOR);
766     HB_Cbu[i] = myround(2. * (1. - KB_BT709) * ((double)i - UV_BIAS) * SCALE_FACTOR); // 2*(1-Kb)
767   }
768   conv_YR_inited = TRUE;
769 }
770 
771 
init_YUV_to_YUV_tables(void)772 static void init_YUV_to_YUV_tables(void) {
773   register int i;
774 
775   // init clamped -> unclamped, same subspace
776   for (i = 0; i <= YUV_CLAMP_MINI; i++) {
777     Yclamped_to_Yunclamped[i] = 0;
778   }
779   for (; i < Y_CLAMP_MAXI; i++) {
780     Yclamped_to_Yunclamped[i] = myround((i - YUV_CLAMP_MIN) * 255. / (Y_CLAMP_MAX - YUV_CLAMP_MIN));
781   }
782   for (; i < 256; i++) {
783     Yclamped_to_Yunclamped[i] = 255;
784   }
785 
786   for (i = 0; i < YUV_CLAMP_MINI; i++) {
787     UVclamped_to_UVunclamped[i] = 0;
788   }
789   for (; i < UV_CLAMP_MAXI; i++) {
790     UVclamped_to_UVunclamped[i] = myround((i - YUV_CLAMP_MIN) * 255. / (UV_CLAMP_MAX - YUV_CLAMP_MIN));
791   }
792   for (; i < 256; i++) {
793     UVclamped_to_UVunclamped[i] = 255;
794   }
795 
796   for (i = 0; i < 256; i++) {
797     Yunclamped_to_Yclamped[i] = myround((i / 255.) * (Y_CLAMP_MAX - YUV_CLAMP_MIN) + YUV_CLAMP_MIN);
798     UVunclamped_to_UVclamped[i] = myround((i / 255.) * (UV_CLAMP_MAX - YUV_CLAMP_MIN) + YUV_CLAMP_MIN);
799   }
800 
801   conv_YY_inited = TRUE;
802 }
803 
804 
init_average(void)805 static void init_average(void) {
806   for (int x = 0; x < 256; x++) {
807     float fa = (float)(x - 128.) * 255. / 244.;
808     short sa = (short)(x - 128);
809     for (int y = 0; y < 256; y++) {
810       float fb = (float)(y - 128.) * 255. / 244.;
811       short sb = (short)(y - 128);
812 #ifdef MULT_AVG
813       // values mixed in proportion to strength
814       float fc = (fa + fb - ((fa * fb) >> 8)) * 224. / 512. + 128.;
815       short c = (sa + sb - ((sa * sb) >> 8)) + 128;
816 #else
817       // values mixed equally
818       float fc = (fa + fb) * 224. / 512. + 128.;
819       short c = ((sa + sb) >> 1) + 128;
820 #endif
821       cavgc[x][y] = (uint8_t)(fc > 240. ? 240 : fc < 16. ? 16 : fc);
822       cavgrgb[x][y] = cavgu[x][y] = (uint8_t)(c > 255 ? 255 : c < 0 ? 0 : c);
823     }
824   }
825   avg_inited = TRUE;
826 }
827 
828 
init_unal(void)829 static void init_unal(void) {
830   // premult to postmult and vice-versa
831   for (int i = 0; i < 256; i++) { //alpha val
832     for (int j = 0; j < 256; j++) { // val to be converted
833       unal[i][j] = (float)j * 255. / (float)i;
834       al[i][j] = (float)j * (float)i / 255.;
835 
836       // clamped versions
837       unalcy[i][j] = ((j - YUV_CLAMP_MIN) / (Y_CLAMP_MAX - YUV_CLAMP_MIN)) / (float)i;
838       alcy[i][j] = ((j - YUV_CLAMP_MIN) / (Y_CLAMP_MAX - YUV_CLAMP_MIN)) * (float)i;
839       unalcuv[i][j] = ((j - YUV_CLAMP_MIN) / (UV_CLAMP_MAX - YUV_CLAMP_MIN)) / (float)i;
840       alcuv[i][j] = ((j - YUV_CLAMP_MIN) / (UV_CLAMP_MAX - YUV_CLAMP_MIN)) * (float)i;
841     }
842   }
843   unal_inited = TRUE;
844 }
845 
846 
set_conversion_arrays(int clamping,int subspace)847 static void set_conversion_arrays(int clamping, int subspace) {
848   // set conversion arrays for RGB <-> YUV, also min/max YUV values
849   // depending on clamping and subspace
850 
851   switch (subspace) {
852   case WEED_YUV_SUBSPACE_YUV: // assume YCBCR
853   case WEED_YUV_SUBSPACE_YCBCR:
854     if (clamping == WEED_YUV_CLAMPING_CLAMPED) {
855       Y_R = Y_Rc;
856       Y_G = Y_Gc;
857       Y_B = Y_Bc;
858 
859       Cr_R = Cr_Rc;
860       Cr_G = Cr_Gc;
861       Cr_B = Cr_Bc;
862 
863       Cb_R = Cb_Rc;
864       Cb_G = Cb_Gc;
865       Cb_B = Cb_Bc;
866 
867       RGB_Y = RGB_Yc;
868 
869       R_Cr = R_Crc;
870       G_Cr = G_Crc;
871 
872       G_Cb = G_Cbc;
873       B_Cb = B_Cbc;
874     } else {
875       Y_R = Y_Ru;
876       Y_G = Y_Gu;
877       Y_B = Y_Bu;
878 
879       Cr_R = Cr_Ru;
880       Cr_G = Cr_Gu;
881       Cr_B = Cr_Bu;
882 
883       Cb_R = Cb_Ru;
884       Cb_G = Cb_Gu;
885       Cb_B = Cb_Bu;
886 
887       RGB_Y = RGB_Yu;
888 
889       R_Cr = R_Cru;
890       G_Cr = G_Cru;
891       G_Cb = G_Cbu;
892       B_Cb = B_Cbu;
893     }
894     break;
895   case WEED_YUV_SUBSPACE_BT709:
896     if (clamping == WEED_YUV_CLAMPING_CLAMPED) {
897       Y_R = HY_Rc;
898       Y_G = HY_Gc;
899       Y_B = HY_Bc;
900 
901       Cr_R = HCr_Rc;
902       Cr_G = HCr_Gc;
903       Cr_B = HCr_Bc;
904 
905       Cb_R = HCb_Rc;
906       Cb_G = HCb_Gc;
907       Cb_B = HCb_Bc;
908 
909       RGB_Y = HRGB_Yc;
910 
911       R_Cr = HR_Crc;
912       G_Cr = HG_Crc;
913       G_Cb = HG_Cbc;
914       B_Cb = HB_Cbc;
915     } else {
916       Y_R = HY_Ru;
917       Y_G = HY_Gu;
918       Y_B = HY_Bu;
919 
920       Cr_R = HCr_Ru;
921       Cr_G = HCr_Gu;
922       Cr_B = HCr_Bu;
923 
924       Cb_R = HCb_Ru;
925       Cb_G = HCb_Gu;
926       Cb_B = HCb_Bu;
927 
928       RGB_Y = HRGB_Yu;
929 
930       R_Cr = HR_Cru;
931       G_Cr = HG_Cru;
932       G_Cb = HG_Cbu;
933       B_Cb = HB_Cbu;
934     }
935     break;
936   }
937 
938   if (!avg_inited) init_average();
939 
940   if (clamping == WEED_YUV_CLAMPING_CLAMPED) {
941     min_Y = min_UV = YUV_CLAMP_MIN;
942     max_Y = Y_CLAMP_MAX;
943     max_UV = UV_CLAMP_MAX;
944     cavg = (uint8_t *)cavgc;
945   } else {
946     min_Y = min_UV = 0;
947     max_Y = max_UV = 255;
948     cavg = (uint8_t *)cavgu;
949   }
950 }
951 
952 
get_YUV_to_YUV_conversion_arrays(int iclamping,int isubspace,int oclamping,int osubspace)953 static void get_YUV_to_YUV_conversion_arrays(int iclamping, int isubspace, int oclamping, int osubspace) {
954   // get conversion arrays for YUV -> YUV depending on in/out clamping and subspace
955   // currently only clamped <-> unclamped conversions are catered for, subspace conversions are not yet done
956   char *errmsg = NULL;
957   if (!conv_YY_inited) init_YUV_to_YUV_tables();
958 
959   switch (isubspace) {
960   case WEED_YUV_SUBSPACE_YUV:
961     LIVES_WARN("YUV subspace input not specified, assuming Y'CbCr");
962   case WEED_YUV_SUBSPACE_YCBCR:
963     switch (osubspace) {
964     case WEED_YUV_SUBSPACE_YUV:
965       LIVES_WARN("YUV subspace output not specified, assuming Y'CbCr");
966     case WEED_YUV_SUBSPACE_YCBCR:
967       if (iclamping == WEED_YUV_CLAMPING_CLAMPED) {
968         //Y'CbCr clamped -> Y'CbCr unclamped
969         Y_to_Y = Yclamped_to_Yunclamped;
970         U_to_U = V_to_V = UVclamped_to_UVunclamped;
971       } else {
972         //Y'CbCr unclamped -> Y'CbCr clamped
973         Y_to_Y = Yunclamped_to_Yclamped;
974         U_to_U = V_to_V = UVunclamped_to_UVclamped;
975       }
976       break;
977     // TODO - other subspaces
978     default:
979       errmsg = lives_strdup_printf("Invalid YUV subspace conversion %d to %d", isubspace, osubspace);
980       LIVES_ERROR(errmsg);
981     }
982     break;
983   case WEED_YUV_SUBSPACE_BT709:
984     switch (osubspace) {
985     case WEED_YUV_SUBSPACE_YUV:
986       LIVES_WARN("YUV subspace output not specified, assuming BT709");
987     case WEED_YUV_SUBSPACE_BT709:
988       if (iclamping == WEED_YUV_CLAMPING_CLAMPED) {
989         //BT.709 clamped -> BT.709 unclamped
990         Y_to_Y = Yclamped_to_Yunclamped;
991         U_to_U = V_to_V = UVclamped_to_UVunclamped;
992       } else {
993         //BT.709 unclamped -> BT.709 clamped
994         Y_to_Y = Yunclamped_to_Yclamped;
995         U_to_U = V_to_V = UVunclamped_to_UVclamped;
996       }
997       break;
998     // TODO - other subspaces
999     default:
1000       errmsg = lives_strdup_printf("Invalid YUV subspace conversion %d to %d", isubspace, osubspace);
1001       LIVES_ERROR(errmsg);
1002     }
1003     break;
1004   default:
1005     errmsg = lives_strdup_printf("Invalid YUV subspace conversion %d to %d", isubspace, osubspace);
1006     LIVES_ERROR(errmsg);
1007     break;
1008   }
1009   if (errmsg) lives_free(errmsg);
1010 }
1011 
1012 
rgb2xyz(uint8_t r,uint8_t g,uint8_t b,double * x,double * y,double * z)1013 void rgb2xyz(uint8_t r, uint8_t g, uint8_t b, double *x, double *y, double *z) {
1014   double rr = (double)r / 2.55, gg = (double)g / 2.55, bb = (double)b / 2.55;
1015   *x = rr * 0.4124 + gg * 0.3576 + bb * 0.1805;
1016   *y = rr * 0.2126 + gg * 0.7152 + bb * 0.0722;
1017   *z = rr * 0.0193 + gg * 0.1192 + bb * 0.9505;
1018 }
1019 
1020 // xyz and lab, thanks to
1021 // https://www.emanueleferonato.com/2009/09/08/color-difference-algorithm-part-2
1022 #define LAB0 0.008856
1023 #define LAB1 0.33333333333
1024 #define LAB2 7.787
1025 #define LAB3 0.13793103448 // 16. / 116.
lab_conv(double a)1026 LIVES_LOCAL_INLINE double lab_conv(double a) {return a > LAB0 ? pow(a, LAB1) : a * LAB2 + LAB3;}
1027 
xyz2lab(double x,double y,double z,double * l,double * a,double * b)1028 void xyz2lab(double x, double y, double z, double *l, double *a, double *b) {
1029   x = lab_conv(x); y = lab_conv(y); z = lab_conv(z);
1030   if (l) {*l = 116. * y - 16.;} if (a) {*a = 500. * (x - y);} if (b) {*b = 200. * (y - z);}
1031 }
1032 
1033 #define KL 1.0 // 2.0 for textiles
1034 #define KC 1.0 // default
1035 #define KH 1.0 // default
1036 #define K1 0.045 // graphics arts, 0.048 textiles
1037 #define K2 0.015 // graphics arts, 0.014 textiles
1038 #define RNDFAC 0.0000000001
cdist94lab(double l0,double a0,double b0,double l1,double a1,double b1)1039 static double cdist94lab(double l0, double a0, double b0, double l1, double a1, double b1) {
1040   // CIE94
1041   double dl = l0 - l1;
1042   double c0 = sqrt(a0 * a0 + b0 * b0), c1 = sqrt(a1 * a1 + b1 * b1);
1043   double dc = c0 - c1, da = a0 - a1, db = b0 - b1;
1044   double dh = sqrt(da * da + db * db - dc * dc + RNDFAC);
1045   //dl /= KL;  // 1.0 default, 2.0 textiles
1046   dc /= (1. + K1 * c0);
1047   //dc /= KC;  // 1.0 default
1048   dh /= (1. + K2 * c1);
1049   //dh /= KH;  // 1.0 default
1050   return sqrt(dl * dl + dc * dc + dh * dh);
1051 }
1052 
1053 
get_maxmin_diff(uint8_t a,uint8_t b,uint8_t c,uint8_t * max,uint8_t * min)1054 static uint8_t get_maxmin_diff(uint8_t a, uint8_t b, uint8_t c, uint8_t *max, uint8_t *min) {
1055   uint8_t lmax = a;
1056   uint8_t lmin = a;
1057   if (a > b) {
1058     if (b > c) lmin = c;
1059     else {
1060       lmin = b;
1061       if (c > a) lmax = c;
1062     }
1063   } else {
1064     if (b < c) lmax = c;
1065     else {
1066       lmax = b;
1067       if (c < a) lmin = c;
1068     }
1069   }
1070   if (max) *max = lmax;
1071   if (min) *min = lmin;
1072   return lmax - lmin;
1073 }
1074 
cdist94(uint8_t r0,uint8_t g0,uint8_t b0,uint8_t r1,uint8_t g1,uint8_t b1)1075 double cdist94(uint8_t r0, uint8_t g0, uint8_t b0, uint8_t r1, uint8_t g1, uint8_t b1) {
1076   double dist;
1077   double x0 = 0., y0 = 0., z0 = 0.;
1078   double x1 = 0., y1 = 0., z1 = 0.;
1079   double L0 = 0., A0 = 0., B0 = 0.;
1080   double L1 = 0., A1 = 0., B1 = 0.;
1081   rgb2xyz(r0, g0, b0, &x0, &y0, &z0);
1082   rgb2xyz(r1, g1, b1, &x1, &y1, &z1);
1083   xyz2lab(x0, y0, z0, &L0, &A0, &B0);
1084   xyz2lab(x1, y1, z1, &L1, &A1, &B1);
1085   dist =  cdist94lab(L0, A0, B0, L1, A1, B1);
1086   return dist;
1087 }
1088 
1089 
rgb2hsv(uint8_t r,uint8_t g,uint8_t b,double * h,double * s,double * v)1090 void rgb2hsv(uint8_t r, uint8_t g, uint8_t b, double *h, double *s, double *v) {
1091   // h, s, v = hue, saturation, value
1092   uint8_t cmax = 0, cmin = 0;
1093   uint8_t diff = get_maxmin_diff(r, g, b, &cmax, &cmin);
1094   double ddiff = (double)diff, dcmax = (double)cmax;
1095   if (h) {*h = 0.;} if (s) {*s = 0.;} if (v) {*v = 0.;}
1096   if (h && cmax != cmin) {
1097     if (cmax == r) *h = ((double)g - (double)b) / ddiff;
1098     else if (cmax == g) *h = 2. + ((double)b - (double)r) / ddiff;
1099     else *h = 4. + ((double)r - (double)g) / ddiff;
1100     *h = 60. * (*h < 0. ? (*h + 6.) : *h >= 6. ? (*h - 6.) : *h);
1101   }
1102   if (s && cmax) *s = (ddiff / dcmax) * 100.;
1103   if (v) *v = dcmax / 2.55;
1104 
1105 #if CALC_HSL
1106   register short a;
1107   if ((a = spc_rnd(Y_Ru[r] + Y_Gu[g] + Y_Bu[b])) > 255) a = 255;
1108   if (v) *v = (double)(a < 0 ? 0 : a) / 255.;
1109 #endif
1110 
1111 }
1112 
hsv2rgb(double h,double s,double v,uint8_t * r,uint8_t * g,uint8_t * b)1113 void hsv2rgb(double h, double s, double v, uint8_t *r, uint8_t *g, uint8_t *b) {
1114   if (s < 0.000001) {
1115     *r = *g = *b = (v * 255. + .5);
1116     return;
1117   } else {
1118     int i = (int)h;
1119     double f = h - (double)i;
1120     double p = v * (1. - s);
1121     double dr, dg, db;
1122     if (i & 1) {
1123       double q = v * (1. - (s * f));
1124       switch (i) {
1125       case 1: dr = q; dg = v; db = p; break;
1126       case 3: dr = p; dg = q; db = v; break;
1127       default: dr = v; dg = p; db = q; break;
1128       }
1129     } else {
1130       double t = v * (1. - (s * (1. - f)));
1131       switch (i) {
1132       case 0: dr = v; dg = t; db = p; break;
1133       case 2: dr = p; dg = v; db = t; break;
1134       default: dr = t; dg = p; db = v; break;
1135       }
1136     }
1137     *r = (uint8_t)(dr * 255. + .5); *g = (uint8_t)(dg * 255. + .5); *b = (uint8_t)(db * 255. + .5);
1138   }
1139 }
1140 
1141 
pick_nice_colour(uint8_t r0,uint8_t g0,uint8_t b0,uint8_t * r1,uint8_t * g1,uint8_t * b1,double max,double lmin,double lmax)1142 boolean pick_nice_colour(uint8_t r0, uint8_t g0, uint8_t b0, uint8_t *r1, uint8_t *g1, uint8_t *b1,
1143                          double max, double lmin, double lmax) {
1144   // given 2 colours a and b, calculate the cie94 distance (d) between them, then find a third colour
1145   // first calc the avg, calc d(a, b) with threshold > 1.
1146   // pick a random colour, the dist from avg must be approx. d(a, b) * max
1147   // da / db must be > ,9 and db / da also
1148   // finally luma should be between lmin and lmax
1149   // restrictions are gradually loosened
1150 
1151 #define DIST_THRESH 10.
1152 #define RAT_START .9
1153 #define RAT_TIO  .9999999
1154 #define RAT_MIN .2
1155 
1156   volatile double gmm = 1. + lmax * 2., gmn = 1. + lmin;
1157   volatile uint8_t xr, xb, xg, ar, ag, ab;
1158   volatile uint8_t rmin = MIN(r0, *r1) / 1.5, gmin = MIN(g0, *g1) / gmm, bmin = MIN(b0, *b1) / 1.5;
1159   volatile uint8_t rmax = MAX(r0, *r1), gmax = MAX(g0, *g1), bmax = MAX(b0, *b1);
1160   volatile double da, db, z, rat = RAT_START, d = cdist94(r0, g0, b0, *r1, *g1, *b1);
1161   if (d < DIST_THRESH) d = DIST_THRESH;
1162   max *= d;
1163 
1164   ar = (volatile double)(r0 + *r1) / 2.;
1165   ag = (volatile double)(g0 + *g1) / 2.;
1166   ab = (volatile double)(b0 + *b1) / 2.;
1167 
1168   rmax = (rmax < 128 ? rmax << 1 : 255) - rmin;
1169   gmax = (gmax < 255 / gmn ? gmax *gmn : 255) - gmin;
1170   bmax = (bmax < 128 ? bmax << 1 : 255) - bmin;
1171 
1172   while ((z = rat * RAT_TIO) > RAT_MIN) {
1173     rat = z;
1174     /// pick a random col
1175     xr = fastrand_int(bmax) + bmin;
1176     xg = fastrand_int(gmax) + gmin;
1177     xb = fastrand_int(bmax) + bmin;
1178 
1179     da = cdist94(ar, ag, ab, xr, xg, xb);
1180     if (max * rat > da) continue;
1181     da = cdist94(r0, g0, b0, xr, xg, xb);
1182     db = cdist94(*r1, *g1, *b1, xr, xg, xb);
1183     if (da * rat * lmax > db || db * rat > da * lmax) continue;
1184     else {
1185       double l = get_luma8(xr, xg, xb);
1186       if (l < lmin || l > lmax) continue;
1187       *r1 = xr; *g1 = xg; *b1 = xb;
1188       return TRUE;
1189     }
1190   }
1191   return FALSE;
1192 }
1193 
1194 
1195 //////////////////////////
1196 // pixel conversions
1197 #ifdef WEED_ADVANCED_PALETTES
1198 
1199 static weed_macropixel_t advp[256];
1200 
init_advanced_palettes(void)1201 void init_advanced_palettes(void) {
1202   lives_memset(advp, 0, 256 * sizeof(weed_macropixel_t));
1203 
1204   advp[0] = (weed_macropixel_t) {
1205     WEED_PALETTE_RGB24,
1206     {WEED_VCHAN_red, WEED_VCHAN_green, WEED_VCHAN_blue}
1207   };
1208 
1209   advp[1] = (weed_macropixel_t) {
1210     WEED_PALETTE_BGR24,
1211     {WEED_VCHAN_blue, WEED_VCHAN_green, WEED_VCHAN_red}
1212   };
1213 
1214   advp[2] = (weed_macropixel_t) {
1215     WEED_PALETTE_RGBA32,
1216     {WEED_VCHAN_red, WEED_VCHAN_green, WEED_VCHAN_blue, WEED_VCHAN_alpha}
1217   };
1218 
1219   advp[3] = (weed_macropixel_t) {
1220     WEED_PALETTE_BGRA32,
1221     {WEED_VCHAN_blue, WEED_VCHAN_green, WEED_VCHAN_red, WEED_VCHAN_alpha}
1222   };
1223 
1224   advp[4] = (weed_macropixel_t) {
1225     WEED_PALETTE_ARGB32,
1226     {WEED_VCHAN_alpha, WEED_VCHAN_red, WEED_VCHAN_green, WEED_VCHAN_blue}
1227   };
1228 
1229   advp[5] = (weed_macropixel_t) {
1230     WEED_PALETTE_RGBFLOAT,
1231     {WEED_VCHAN_red, WEED_VCHAN_green, WEED_VCHAN_blue},
1232     WEED_VCHAN_DESC_FP, {0}, {0}, 1, {32, 32, 32}
1233   };
1234 
1235   advp[6] = (weed_macropixel_t) {
1236     WEED_PALETTE_RGBAFLOAT,
1237     {WEED_VCHAN_red, WEED_VCHAN_green, WEED_VCHAN_blue, WEED_VCHAN_alpha},
1238     WEED_VCHAN_DESC_FP, {0}, {0}, 1, {32, 32, 32, 32}
1239   };
1240 
1241   /// yuv planar
1242   advp[7] = (weed_macropixel_t) {
1243     WEED_PALETTE_YUV420P,
1244     {WEED_VCHAN_Y, WEED_VCHAN_U, WEED_VCHAN_V},
1245     WEED_VCHAN_DESC_PLANAR, {1, 2, 2}, {1, 2, 2}
1246   };
1247 
1248   advp[8] = (weed_macropixel_t) {
1249     WEED_PALETTE_YVU420P,
1250     {WEED_VCHAN_Y, WEED_VCHAN_V, WEED_VCHAN_U},
1251     WEED_VCHAN_DESC_PLANAR, {1, 2, 2}, {1, 2, 2}
1252   };
1253 
1254   advp[9] = (weed_macropixel_t) {
1255     WEED_PALETTE_YUV422P,
1256     {WEED_VCHAN_Y, WEED_VCHAN_U, WEED_VCHAN_V},
1257     WEED_VCHAN_DESC_PLANAR, {1, 2, 2}, {1, 1, 1}
1258   };
1259 
1260   advp[10] = (weed_macropixel_t) {
1261     WEED_PALETTE_YUV444P,
1262     {WEED_VCHAN_Y, WEED_VCHAN_U, WEED_VCHAN_V}, WEED_VCHAN_DESC_PLANAR
1263   };
1264 
1265   advp[11] = (weed_macropixel_t) {
1266     WEED_PALETTE_YUVA4444P,
1267     {WEED_VCHAN_Y, WEED_VCHAN_U, WEED_VCHAN_V, WEED_VCHAN_alpha},
1268     WEED_VCHAN_DESC_PLANAR
1269   };
1270 
1271   /// yuv packed
1272   advp[12] = (weed_macropixel_t) {
1273     WEED_PALETTE_UYVY,
1274     {WEED_VCHAN_U, WEED_VCHAN_Y, WEED_VCHAN_V, WEED_VCHAN_Y},
1275     0, {0}, {0}, 2
1276   };
1277 
1278   advp[13] = (weed_macropixel_t) {
1279     WEED_PALETTE_YUYV,
1280     {WEED_VCHAN_Y, WEED_VCHAN_U, WEED_VCHAN_Y, WEED_VCHAN_V},
1281     0, {0}, {0}, 2
1282   };
1283 
1284   advp[14] = (weed_macropixel_t) {WEED_PALETTE_YUV888, {WEED_VCHAN_Y, WEED_VCHAN_U, WEED_VCHAN_V}};
1285 
1286   advp[15] = (weed_macropixel_t) {
1287     WEED_PALETTE_YUVA8888,
1288     {WEED_VCHAN_Y, WEED_VCHAN_U, WEED_VCHAN_V, WEED_VCHAN_alpha}
1289   };
1290 
1291   advp[16] = (weed_macropixel_t) {
1292     WEED_PALETTE_YUV411, {
1293       WEED_VCHAN_U, WEED_VCHAN_Y, WEED_VCHAN_Y,
1294       WEED_VCHAN_V, WEED_VCHAN_Y, WEED_VCHAN_Y
1295     },
1296     0, {0}, {0}, 4
1297   };
1298 
1299   /// alpha
1300   advp[17] = (weed_macropixel_t) {WEED_PALETTE_A8, {WEED_VCHAN_alpha}};
1301 
1302   advp[18] = (weed_macropixel_t) {WEED_PALETTE_A1, {WEED_VCHAN_alpha}, 0, {0}, {0}, 1, {1}};
1303 
1304   advp[19] = (weed_macropixel_t) {
1305     WEED_PALETTE_AFLOAT, {WEED_VCHAN_alpha},
1306                          WEED_VCHAN_DESC_FP, {0}, {0}, 1, {32}
1307   };
1308 
1309   advp[20] = (weed_macropixel_t) {WEED_PALETTE_END};
1310 
1311   // custom palettes (designed for future use or for testing)
1312   advp[21] = (weed_macropixel_t) {
1313     LIVES_PALETTE_ABGR32,
1314     {WEED_VCHAN_alpha, WEED_VCHAN_blue, WEED_VCHAN_green, WEED_VCHAN_red}
1315   };
1316 
1317   advp[22] = (weed_macropixel_t) {
1318     LIVES_PALETTE_YVU422P,
1319     {WEED_VCHAN_Y, WEED_VCHAN_V, WEED_VCHAN_U},
1320     WEED_VCHAN_DESC_PLANAR, {1, 2, 2}, {1, 1, 1}
1321   };
1322 
1323   advp[23] = (weed_macropixel_t) {
1324     LIVES_PALETTE_YUVA420P,
1325     {WEED_VCHAN_Y, WEED_VCHAN_U, WEED_VCHAN_V, WEED_VCHAN_alpha},
1326     WEED_VCHAN_DESC_PLANAR, {1, 2, 2, 1}, {1, 2, 2, 1}
1327   };
1328 
1329   advp[24] = (weed_macropixel_t) {
1330     LIVES_PALETTE_AYUV8888,
1331     {WEED_VCHAN_alpha, WEED_VCHAN_Y, WEED_VCHAN_U, WEED_VCHAN_V}
1332   };
1333 
1334   advp[25] = (weed_macropixel_t) {
1335     LIVES_PALETTE_YUVFLOAT,
1336     {WEED_VCHAN_Y, WEED_VCHAN_U, WEED_VCHAN_V},
1337     WEED_VCHAN_DESC_FP, {0}, {0}, 1, {32, 32, 32}
1338   };
1339 
1340   advp[26] = (weed_macropixel_t) {
1341     LIVES_PALETTE_YUVAFLOAT,
1342     {WEED_VCHAN_Y, WEED_VCHAN_U, WEED_VCHAN_V, WEED_VCHAN_alpha},
1343     WEED_VCHAN_DESC_FP, {0}, {0}, 1, {32, 32, 32, 32}
1344   };
1345 
1346   advp[27] = (weed_macropixel_t) {
1347     LIVES_PALETTE_RGB48,
1348     {WEED_VCHAN_red, WEED_VCHAN_green, WEED_VCHAN_blue},
1349     0, {0}, {0}, 1, {16, 16, 16}
1350   };
1351 
1352   advp[28] = (weed_macropixel_t) {
1353     LIVES_PALETTE_RGBA64, {
1354       WEED_VCHAN_red, WEED_VCHAN_green,
1355       WEED_VCHAN_blue, WEED_VCHAN_alpha
1356     },
1357     0, {0}, {0}, 1, {16, 16, 16, 16}
1358   };
1359 
1360   advp[29] = (weed_macropixel_t) {
1361     LIVES_PALETTE_YUV121010,
1362     {WEED_VCHAN_Y, WEED_VCHAN_U, WEED_VCHAN_V},
1363     0, {0}, {0}, 1, {12, 10, 10}
1364   };
1365 }
1366 
1367 
get_advanced_palette(int weed_palette)1368 LIVES_GLOBAL_INLINE const weed_macropixel_t *get_advanced_palette(int weed_palette) {
1369   for (register int i = 0; advp[i].ext_ref != WEED_PALETTE_END; i++)
1370     if (advp[i].ext_ref == weed_palette) return &advp[i];
1371   return NULL;
1372 }
1373 
weed_palette_is_valid(int pal)1374 LIVES_GLOBAL_INLINE boolean weed_palette_is_valid(int pal) {
1375   return (get_advanced_palette(pal) != NULL);
1376 }
1377 
get_simple_palette(weed_macropixel_t * mpx)1378 LIVES_GLOBAL_INLINE int get_simple_palette(weed_macropixel_t *mpx) {
1379   if (mpx) return mpx->ext_ref;
1380   return WEED_PALETTE_NONE;
1381 }
1382 
is_rgbchan(uint16_t ctype)1383 LIVES_LOCAL_INLINE boolean is_rgbchan(uint16_t ctype) {
1384   return (ctype == WEED_VCHAN_red || ctype == WEED_VCHAN_green || ctype == WEED_VCHAN_blue);
1385 }
1386 
is_yuvchan(uint16_t ctype)1387 LIVES_LOCAL_INLINE boolean is_yuvchan(uint16_t ctype) {
1388   return (ctype == WEED_VCHAN_Y || ctype == WEED_VCHAN_U || ctype == WEED_VCHAN_V);
1389 }
1390 
pixel_size(int pal)1391 LIVES_GLOBAL_INLINE size_t pixel_size(int pal) {
1392   /// This is actually the MACRO pixel size om bytes, to get the real pixel size, divide by weed_palette_pixels_per_macropixel()
1393   const weed_macropixel_t *mpx = get_advanced_palette(pal);
1394   if (!mpx) return 0;
1395   else {
1396     size_t psize = 0;
1397     for (register int i = 0; i < MAXPPLANES && mpx->chantype[i]; i++)
1398       psize += mpx->bitsize[i] == 0 ? 1 : mpx->bitsize[i] / 8;
1399     return psize;
1400   }
1401 }
1402 
weed_palette_get_pixels_per_macropixel(int pal)1403 LIVES_GLOBAL_INLINE int weed_palette_get_pixels_per_macropixel(int pal) {
1404   int npix = 0;
1405   const weed_macropixel_t *mpx = get_advanced_palette(pal);
1406   if (!mpx) return 0;
1407   npix = mpx->npixels;
1408   return !npix ? 1 : npix;
1409 }
1410 
weed_palette_get_bits_per_macropixel(int pal)1411 LIVES_GLOBAL_INLINE int weed_palette_get_bits_per_macropixel(int pal) {
1412   int ppm = weed_palette_get_pixels_per_macropixel(pal);
1413   if (ppm) return pixel_size(pal) * 8;
1414   return 0;
1415 }
1416 
weed_palette_get_nplanes(int pal)1417 LIVES_GLOBAL_INLINE int weed_palette_get_nplanes(int pal) {
1418   const weed_macropixel_t *mpx = get_advanced_palette(pal);
1419   register int i = 0;
1420   if (mpx) {
1421     if (!(mpx->flags & WEED_VCHAN_DESC_PLANAR)) return 1;
1422     for (i = 0; i < MAXPPLANES && mpx->chantype[i]; i++);
1423   }
1424   return i;
1425 }
1426 
weed_palette_is_alpha(int pal)1427 LIVES_GLOBAL_INLINE boolean weed_palette_is_alpha(int pal) {
1428   const weed_macropixel_t *mpx = get_advanced_palette(pal);
1429   if (mpx && mpx->chantype[0] == WEED_VCHAN_alpha && !mpx->chantype[1]) return TRUE;
1430   return FALSE;
1431 }
1432 
weed_palette_red_first(int pal)1433 LIVES_GLOBAL_INLINE boolean weed_palette_red_first(int pal) {
1434   const weed_macropixel_t *mpx = get_advanced_palette(pal);
1435   if (mpx) {
1436     for (register int i = 0; i < MAXPPLANES && mpx->chantype[i]; i++) {
1437       if (mpx->chantype[i] == WEED_VCHAN_red) return TRUE;
1438       if (mpx->chantype[i] == WEED_VCHAN_blue) return FALSE;
1439     }
1440   }
1441   return FALSE;
1442 }
1443 
weed_palettes_rbswapped(int pal0,int pal1)1444 LIVES_GLOBAL_INLINE boolean weed_palettes_rbswapped(int pal0, int pal1) {
1445   return weed_palette_red_first(pal0) != weed_palette_red_first(pal1);
1446 }
1447 
weed_palette_is_rgb(int pal)1448 LIVES_GLOBAL_INLINE boolean weed_palette_is_rgb(int pal) {
1449   const weed_macropixel_t *mpx = get_advanced_palette(pal);
1450   if (mpx) {
1451     for (register int i = 0; i < MAXPPLANES && mpx->chantype[i]; i++)
1452       if (is_rgbchan(mpx->chantype[i])) return TRUE;
1453   }
1454   return FALSE;
1455 }
1456 
weed_palette_is_yuv(int pal)1457 LIVES_GLOBAL_INLINE boolean weed_palette_is_yuv(int pal) {
1458   const weed_macropixel_t *mpx = get_advanced_palette(pal);
1459   if (mpx) {
1460     for (register int i = 0; i < MAXPPLANES && mpx->chantype[i]; i++)
1461       if (is_yuvchan(mpx->chantype[i])) return TRUE;
1462   }
1463   return FALSE;
1464 }
1465 
weed_palette_has_alpha(int pal)1466 LIVES_GLOBAL_INLINE boolean weed_palette_has_alpha(int pal) {
1467   const weed_macropixel_t *mpx = get_advanced_palette(pal);
1468   if (mpx) {
1469     for (register int i = 0; i < MAXPPLANES && mpx->chantype[i]; i++)
1470       if (mpx->chantype[i] == WEED_VCHAN_alpha) return TRUE;
1471   }
1472   return FALSE;
1473 }
1474 
weed_palette_is_float(int pal)1475 LIVES_GLOBAL_INLINE boolean weed_palette_is_float(int pal) {
1476   const weed_macropixel_t *mpx = get_advanced_palette(pal);
1477   return (mpx && (mpx->flags & WEED_VCHAN_DESC_FP));
1478 }
1479 
weed_palette_get_plane_ratio_horizontal(int pal,int plane)1480 LIVES_GLOBAL_INLINE double weed_palette_get_plane_ratio_horizontal(int pal, int plane) {
1481   uint8_t subsam = 0;
1482   const weed_macropixel_t *mpx = get_advanced_palette(pal);
1483   if (mpx) subsam = mpx->hsub[plane];
1484   if (subsam) return 1. / (double)(subsam);
1485   return 1.;
1486 }
1487 
weed_palette_get_plane_ratio_vertical(int pal,int plane)1488 LIVES_GLOBAL_INLINE double weed_palette_get_plane_ratio_vertical(int pal, int plane) {
1489   uint8_t subsam = 0;
1490   const weed_macropixel_t *mpx = get_advanced_palette(pal);
1491   if (mpx) subsam = mpx->vsub[plane];
1492   if (subsam) return 1. / (double)(subsam);
1493   return 1.;
1494 }
1495 
_get_alpha(int pal)1496 LIVES_LOCAL_INLINE int _get_alpha(int pal) {
1497   const weed_macropixel_t *mpx = get_advanced_palette(pal);
1498   if (mpx) {
1499     for (register int i = 0; i < MAXPPLANES && mpx->chantype[i]; i++)
1500       if (mpx->chantype[i] == WEED_VCHAN_alpha) return i;
1501   }
1502   return -1;
1503 }
1504 
weed_palette_get_alpha_plane(int pal)1505 LIVES_GLOBAL_INLINE int weed_palette_get_alpha_plane(int pal) {
1506   if (weed_palette_is_planar(pal)) return _get_alpha(pal);
1507   return -1;
1508 }
1509 
weed_palette_get_alpha_offset(int pal)1510 LIVES_GLOBAL_INLINE int weed_palette_get_alpha_offset(int pal) {
1511   if (!weed_palette_is_planar(pal)) return _get_alpha(pal);
1512   return -1;
1513 }
1514 
weed_palette_has_alpha_first(int pal)1515 LIVES_GLOBAL_INLINE boolean weed_palette_has_alpha_first(int pal) {
1516   return _get_alpha(pal) == 0;
1517 }
1518 
weed_palette_has_alpha_last(int pal)1519 LIVES_GLOBAL_INLINE boolean weed_palette_has_alpha_last(int pal) {
1520   return _get_alpha(pal) == 3;
1521 }
1522 
weed_palette_is_sane(int pal)1523 LIVES_GLOBAL_INLINE boolean weed_palette_is_sane(int pal) {
1524   // first cpt must be alpha, red, blue, y, u, or v
1525   //
1526   // if first was alpha, 2nd must be NULL, red, blue or y, u, v
1527   // if first was red or blue, 2nd must be green
1528   // if first was y, 2nd must be y, u or v
1529   //
1530   // if second was green, 3rd must be blue or red, but != 1st
1531   // if second was y, u, or v, third must be y, u or v
1532   // if second was red or blue, 3rd must be green
1533   //
1534   // if third was red or blue, fourth may be alpha or NULL
1535   // if third was green, fourth must be red or blue but != 2nd
1536   // if third was y, u, or v, fourth may be y, u, v or alpha
1537   //
1538   // if fourth was red, blue or alpha, fifth must be NULL
1539   // if fourth was y, u, or v, fifth must be y, u, v or alpha
1540   //
1541   // if fifth was alpha, sixth must be NULL
1542   // if fifth was y, u, or v, sixth must be y, u, v or alpha
1543   //
1544   // etc. for 7 and 8
1545 
1546   // there must be some symmetry between u and v, e.g. yyuyyv is allowed but not yyuyv or yuy
1547   // 0, 1, or 2 y before / after a u or v, y numbers must match and u / v numbers too
1548 
1549   // future directions: allow single plane / cpt r, g, b or y
1550   boolean red = FALSE, blue = FALSE, alpha = FALSE;
1551   int nseqy = 0, nseqyu = 0, nseqyv = 0, nu = 0, nv = 0;
1552   const weed_macropixel_t *mpx = get_advanced_palette(pal);
1553   if (!mpx) return FALSE;
1554   for (register int i = 0; i < MAXPPLANES && mpx->chantype[i]; i++) {
1555     uint16_t ctype = mpx->chantype[i];
1556     if (i > 3 && alpha) return FALSE;
1557     switch (ctype) {
1558     case WEED_VCHAN_Y:
1559       if (red || blue) return FALSE;
1560       if (++nseqy > 2) return FALSE;
1561       break;
1562     case WEED_VCHAN_U:
1563       if (red || blue) return FALSE;
1564       if (nseqyv && nseqy && nseqy != nseqyv) return FALSE;
1565       nu++;
1566       nseqyu = nseqy;
1567       nseqy = nseqyv = 0;
1568       break;
1569     case WEED_VCHAN_V:
1570       if (red || blue) return FALSE;
1571       if (nseqyu && nseqy && nseqy != nseqyu) return FALSE;
1572       nv++;
1573       nseqyv = nseqy;
1574       nseqy = nseqyu = 0;
1575       break;
1576     default:
1577       switch (i) {
1578       case 0:
1579         switch (ctype) {
1580         case WEED_VCHAN_alpha: alpha = TRUE; break;
1581         case WEED_VCHAN_red: red = TRUE; break;
1582         case WEED_VCHAN_blue: blue = TRUE; break;
1583         default: return FALSE;
1584         }
1585         break;
1586       case 1:
1587         if (nu || nv || nseqy) return FALSE;
1588         switch (ctype) {
1589         case WEED_VCHAN_alpha: return FALSE;
1590         case WEED_VCHAN_green:
1591           if (!red && !blue) return FALSE;
1592           break;
1593         case WEED_VCHAN_red:
1594           if (!alpha) return FALSE;
1595           red = TRUE;
1596           break;
1597         case WEED_VCHAN_blue:
1598           if (!alpha) return FALSE;
1599           blue = TRUE;
1600           break;
1601         default: return FALSE;
1602         }
1603         break;
1604       case 2:
1605         if (nu || nv || nseqy) return FALSE;
1606         switch (ctype) {
1607         case WEED_VCHAN_alpha: return FALSE;
1608         case WEED_VCHAN_green:
1609           if (!red && !blue) return FALSE;
1610           break;
1611         case WEED_VCHAN_red:
1612           if (!blue) return FALSE;
1613           red = TRUE;
1614           break;
1615         case WEED_VCHAN_blue:
1616           if (!red) return FALSE;
1617           blue = TRUE;
1618           break;
1619         default: return FALSE;
1620         }
1621         break;
1622       case 3:
1623         switch (ctype) {
1624         case WEED_VCHAN_alpha:
1625           if (alpha) return FALSE;
1626           alpha = TRUE;
1627           break;
1628         case WEED_VCHAN_red:
1629           if (!blue) return FALSE;
1630           red = TRUE;
1631           break;
1632         case WEED_VCHAN_blue:
1633           if (!red) return FALSE;
1634           blue = TRUE;
1635           break;
1636         default: return FALSE;
1637         }
1638         break;
1639       default:
1640         if (ctype != WEED_VCHAN_alpha) return FALSE;
1641         alpha = TRUE;
1642         break;
1643       }
1644       break;
1645     }
1646   }
1647   if (red != blue || nv != nu || (nseqy != nseqyu && nseqy != nseqyv)) return FALSE;
1648   return TRUE;
1649 }
1650 #endif
1651 
1652 
init_gamma_tx(void)1653 static void init_gamma_tx(void) {
1654   gamma_tx[WEED_GAMMA_SRGB] = (gamma_const_t) {0.055, 12.92, 0.04045, 2.4};
1655   gamma_tx[WEED_GAMMA_BT709] = (gamma_const_t) {0.099, 4.5, 0.081, 1. / .45};
1656 }
1657 
1658 static void rgb2yuv(uint8_t r0, uint8_t g0, uint8_t b0, uint8_t *y, uint8_t *u, uint8_t *v) GNU_HOT;
1659 static void yuv2rgb(uint8_t y, uint8_t u, uint8_t v, uint8_t *r, uint8_t *g, uint8_t *b) GNU_HOT;
1660 
init_colour_engine(void)1661 void init_colour_engine(void) {
1662   init_RGB_to_YUV_tables();
1663   init_YUV_to_RGB_tables();
1664   init_YUV_to_YUV_tables();
1665   init_average();
1666   init_unal();
1667   init_gamma_tx();
1668   init_conversions(LIVES_INTENTION_PLAY);
1669 #ifdef WEED_ADVANCED_PALETTES
1670   init_advanced_palettes();
1671 #endif
1672   //#define TEST_CONV
1673 #ifdef TEST_CONV
1674   if (1) {
1675     int cr, cg, cb;
1676     uint8_t r = 100, g, b, y, u, v, xr, xg, xb;;
1677     set_conversion_arrays(WEED_YUV_CLAMPING_UNCLAMPED, WEED_YUV_SUBSPACE_YCBCR);
1678     //prefs->pb_quality = PB_QUALITY_MED;
1679     for (cr = 100; cr < 256; cr++) {
1680       g = 0;
1681       for (cg = 0; cg < 256; cg++) {
1682         b = 0;
1683         for (cb = 0; cb < 256; cb++) {
1684           g_print("in: %d %d %d\n", r, g, b);
1685           rgb2yuv(r, g, b, &y, &u, &v);
1686           yuv2rgb(y, u, v, &xr, &xg, &xb);
1687           g_print("out: %d %d %d   %d %d %d\n", y, u, v, xr, xg, xb);
1688           b++;
1689         }
1690         g++;
1691       }
1692       r++;
1693     }
1694   }
1695 #endif
1696 }
1697 
1698 // internal thread fns
1699 static void *convert_rgb_to_uyvy_frame_thread(void *cc_params);
1700 static void *convert_bgr_to_uyvy_frame_thread(void *cc_params);
1701 static void *convert_rgb_to_yuyv_frame_thread(void *cc_params);
1702 static void *convert_bgr_to_yuyv_frame_thread(void *cc_params);
1703 static void *convert_argb_to_uyvy_frame_thread(void *cc_params);
1704 static void *convert_argb_to_yuyv_frame_thread(void *cc_params);
1705 
1706 static void *convert_rgb_to_yuv_frame_thread(void *cc_params);
1707 static void *convert_bgr_to_yuv_frame_thread(void *cc_params);
1708 static void *convert_argb_to_yuv_frame_thread(void *cc_params);
1709 static void *convert_rgb_to_yuvp_frame_thread(void *cc_params);
1710 static void *convert_bgr_to_yuvp_frame_thread(void *cc_params);
1711 static void *convert_argb_to_yuvp_frame_thread(void *cc_params);
1712 
1713 static void *convert_uyvy_to_rgb_frame_thread(void *cc_params);
1714 static void *convert_uyvy_to_bgr_frame_thread(void *cc_params);
1715 static void *convert_uyvy_to_argb_frame_thread(void *cc_params);
1716 static void *convert_yuyv_to_rgb_frame_thread(void *cc_params);
1717 static void *convert_yuyv_to_bgr_frame_thread(void *cc_params);
1718 static void *convert_yuyv_to_argb_frame_thread(void *cc_params);
1719 
1720 static void *convert_yuv_planar_to_rgb_frame_thread(void *cc_params);
1721 static void *convert_yuv_planar_to_bgr_frame_thread(void *cc_params);
1722 static void *convert_yuv_planar_to_argb_frame_thread(void *cc_params);
1723 
1724 static void *convert_yuv420p_to_rgb_frame_thread(void *cc_params);
1725 static void *convert_yuv420p_to_bgr_frame_thread(void *cc_params);
1726 static void *convert_yuv420p_to_argb_frame_thread(void *cc_params);
1727 
1728 static void *convert_yuv888_to_rgb_frame_thread(void *cc_params);
1729 static void *convert_yuv888_to_bgr_frame_thread(void *cc_params);
1730 static void *convert_yuv888_to_argb_frame_thread(void *cc_params);
1731 static void *convert_yuva8888_to_rgba_frame_thread(void *cc_params);
1732 static void *convert_yuva8888_to_bgra_frame_thread(void *cc_params);
1733 static void *convert_yuva8888_to_argb_frame_thread(void *cc_params);
1734 
1735 static void *convert_swap3_frame_thread(void *cc_params);
1736 static void *convert_swap4_frame_thread(void *cc_params);
1737 static void *convert_swap3addpost_frame_thread(void *cc_params);
1738 static void *convert_swap3addpre_frame_thread(void *cc_params);
1739 static void *convert_swap3delpost_frame_thread(void *cc_params);
1740 static void *convert_swap3delpre_frame_thread(void *cc_params);
1741 static void *convert_addpre_frame_thread(void *cc_params);
1742 static void *convert_addpost_frame_thread(void *cc_params);
1743 static void *convert_delpre_frame_thread(void *cc_params);
1744 static void *convert_delpost_frame_thread(void *cc_params);
1745 static void *convert_swap3postalpha_frame_thread(void *cc_params);
1746 #ifdef WEED_ADVANCED_PALETTES
1747 static void *convert_swap3prealpha_frame_thread(void *cc_params);
1748 #endif
1749 static void *convert_swapprepost_frame_thread(void *cc_params);
1750 
1751 static void *convert_swab_frame_thread(void *cc_params);
1752 
1753 #if 0
1754 static void rgb2yuv_with_gamma(uint8_t r0, uint8_t g0, uint8_t b0, uint8_t *y, uint8_t *u, uint8_t *v, uint8_t *lut) GNU_HOT;
1755 #endif
1756 static void rgb2uyvy(uint8_t r0, uint8_t g0, uint8_t b0, uint8_t r1, uint8_t g1, uint8_t b1,
1757                      uyvy_macropixel *uyvy) GNU_FLATTEN GNU_HOT;
1758 static void rgb2uyvy_with_gamma(uint8_t r0, uint8_t g0, uint8_t b0, uint8_t r1, uint8_t g1, uint8_t b1,
1759                                 uyvy_macropixel *uyvy, uint8_t *lut) GNU_FLATTEN GNU_HOT;
1760 static void rgb16_2uyvy(uint16_t r0, uint16_t g0, uint16_t b0, uint16_t r1, uint16_t g1, uint16_t b1,
1761                         uyvy_macropixel *uyvy) GNU_FLATTEN GNU_HOT;
1762 #if 0
1763 static void rgb16_2uyvy_with_gamma(uint16_t r0, uint16_t g0, uint16_t b0, uint16_t r1, uint16_t g1, uint16_t b1,
1764                                    uyvy_macropixel *uyvy, uint8_t *lut) GNU_FLATTEN GNU_HOT;
1765 #endif
1766 
1767 static void rgb2yuyv(uint8_t r0, uint8_t g0, uint8_t b0, uint8_t r1, uint8_t g1, uint8_t b1,
1768                      yuyv_macropixel *yuyv) GNU_FLATTEN GNU_HOT;
1769 #if 0
1770 static void rgb2yuyv_with_gamma(uint8_t r0, uint8_t g0, uint8_t b0, uint8_t r1, uint8_t g1, uint8_t b1,
1771                                 yuyv_macropixel *yuyv, uint8_t *lut) GNU_FLATTEN GNU_HOT;
1772 #endif
1773 static void rgb2_411(uint8_t r0, uint8_t g0, uint8_t b0, uint8_t r1, uint8_t g1, uint8_t b1,
1774                      uint8_t r2, uint8_t g2, uint8_t b2, uint8_t r3, uint8_t g3, uint8_t b3, yuv411_macropixel *yuv) GNU_HOT;
1775 
1776 static void yuv2rgb_with_gamma(uint8_t y, uint8_t u, uint8_t v, uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *lut) GNU_HOT;
1777 static void uyvy2rgb(uyvy_macropixel *uyvy, uint8_t *r0, uint8_t *g0, uint8_t *b0,
1778                      uint8_t *r1, uint8_t *g1, uint8_t *b1) GNU_FLATTEN GNU_HOT;
1779 static void yuyv2rgb(yuyv_macropixel *yuyv, uint8_t *r0, uint8_t *g0, uint8_t *b0,
1780                      uint8_t *r1, uint8_t *g1, uint8_t *b1) GNU_FLATTEN GNU_HOT;
1781 static void yuv888_2_rgb(uint8_t *yuv, uint8_t *rgb, boolean add_alpha) GNU_FLATTEN GNU_HOT;
1782 static void yuva8888_2_rgba(uint8_t *yuva, uint8_t *rgba, boolean del_alpha) GNU_FLATTEN GNU_HOT;
1783 static void yuv888_2_bgr(uint8_t *yuv, uint8_t *bgr, boolean add_alpha) GNU_FLATTEN GNU_HOT;
1784 static void yuva8888_2_bgra(uint8_t *yuva, uint8_t *bgra, boolean del_alpha) GNU_FLATTEN GNU_HOT;
1785 static void yuv888_2_argb(uint8_t *yuv, uint8_t *argb) GNU_FLATTEN GNU_HOT;
1786 static void yuva8888_2_argb(uint8_t *yuva, uint8_t *argb) GNU_FLATTEN GNU_HOT;
1787 static void uyvy_2_yuv422(uyvy_macropixel *uyvy, uint8_t *y0, uint8_t *u0, uint8_t *v0, uint8_t *y1) GNU_HOT;
1788 static void yuyv_2_yuv422(yuyv_macropixel *yuyv, uint8_t *y0, uint8_t *u0, uint8_t *v0, uint8_t *y1) GNU_HOT;
1789 
1790 #define avg_chroma(x, y) ((uint8_t)(*(cavg + ((int)(x) << 8) + (int)(y))))
1791 #define avg_chroma_3_1(x, y) ((uint8_t)(avg_chroma(x, avg_chroma(x, y))))
1792 #define avg_chroma_1_3(x, y) ((uint8_t)(avg_chroma(avg_chroma(x, y), y)))
1793 
1794 static uint8_t (*avg_chromaf)(uint8_t x, uint8_t y);
1795 
1796 /* static uint8_t avg_chromaf_high(uint8_t x, uint8_t y) { */
1797 /*   return (((float)(spc_rnd(((x) << 8) + ((y) << 8)))) * 128. + .5); */
1798 /* } */
1799 
avg_chromaf_fast(uint8_t x,uint8_t y)1800 static uint8_t avg_chromaf_fast(uint8_t x, uint8_t y) {
1801   return avg_chroma(x, y);
1802 }
1803 
init_conversions(int intent)1804 LIVES_GLOBAL_INLINE void init_conversions(int intent) {
1805   avg_chromaf = avg_chromaf_fast;
1806   if (intent == LIVES_INTENTION_RENDER || intent == LIVES_INTENTION_TRANSCODE) {
1807     //avg_chromaf = avg_chromaf_high;
1808     if (prefs) prefs->pb_quality = PB_QUALITY_HIGH;
1809     /// set the 'effort' to as low as possible; if using adaptive quality
1810     // this ensures we render at the highest settings
1811     mainw->effort = -EFFORT_RANGE_MAX;
1812   } else {
1813     if (prefs) prefs->pb_quality = future_prefs->pb_quality;
1814   }
1815 }
1816 
1817 #define avg_chroma_3_1f(x, y) ((uint8_t)(avg_chromaf(x, avg_chromaf(x, y))))
1818 #define avg_chroma_1_3f(x, y) ((uint8_t)(avg_chromaf(avg_chromaf(x, y), y)))
1819 
rgb2yuv(uint8_t r0,uint8_t g0,uint8_t b0,uint8_t * y,uint8_t * u,uint8_t * v)1820 LIVES_INLINE void rgb2yuv(uint8_t r0, uint8_t g0, uint8_t b0, uint8_t *y, uint8_t *u, uint8_t *v) {
1821   short a;
1822   if ((a = spc_rnd(Y_R[r0] + Y_G[g0] + Y_B[b0])) > max_Y) * y = max_Y;
1823   else *y = a < min_Y ? min_Y : a;
1824   if ((a = spc_rnd(Cb_R[r0] + Cb_G[g0] + Cb_B[b0])) > max_UV) * u = max_UV;
1825   else *u = a < min_UV ? min_UV : a;
1826   if ((a = spc_rnd(Cr_R[r0] + Cr_G[g0] + Cr_B[b0])) > max_UV) * v = max_UV;
1827   else *v = a < min_UV ? min_UV : a;
1828 }
1829 
1830 #define bgr2yuv(b0, g0, r0, y, u, v) rgb2yuv(r0, g0, b0, y, u, v)
1831 
1832 
rgb2yuv_with_gamma(uint8_t r0,uint8_t g0,uint8_t b0,uint8_t * y,uint8_t * u,uint8_t * v,uint8_t * lut)1833 LIVES_INLINE void rgb2yuv_with_gamma(uint8_t r0, uint8_t g0, uint8_t b0, uint8_t *y, uint8_t *u, uint8_t *v, uint8_t *lut) {
1834   short a;
1835   if ((a = spc_rnd(Y_R[(r0 = lut[r0])] + Y_G[(g0 = lut[g0])] + Y_B[(b0 = lut[b0])])) > max_Y) * y = max_Y;
1836   else *y = a < min_Y ? min_Y : a;
1837   if ((a = spc_rnd(Cb_R[r0] + Cb_G[g0] + Cb_B[b0])) > max_UV) * u = max_UV;
1838   else *u = a < min_UV ? min_UV : a;
1839   if ((a = spc_rnd(Cr_R[r0] + Cr_G[g0] + Cr_B[b0])) > max_UV) * v = max_UV;
1840   else *v = a < min_UV ? min_UV : a;
1841 }
1842 
1843 
1844 #define bgr2yuv_with_gamma(b0, g0, r0, y, u, v) rgb2yuv(r0, g0, b0, y, u, v, lut)
1845 
rgb2uyvy(uint8_t r0,uint8_t g0,uint8_t b0,uint8_t r1,uint8_t g1,uint8_t b1,uyvy_macropixel * uyvy)1846 LIVES_INLINE void rgb2uyvy(uint8_t r0, uint8_t g0, uint8_t b0, uint8_t r1, uint8_t g1, uint8_t b1, uyvy_macropixel *uyvy) {
1847   short a;
1848   if ((a = spc_rnd(Cb_R[r0] + Cb_G[g0] + Cb_B[b0])) > max_UV) uyvy->u0 = max_UV;
1849   else uyvy->u0 = a < min_UV ? min_UV : a;
1850 
1851   if ((a = spc_rnd(Y_R[r0] + Y_G[g0] + Y_B[b0])) > max_Y) uyvy->y0 = max_Y;
1852   else uyvy->y0 = a < min_Y ? min_Y : a;
1853 
1854   if ((a = spc_rnd(Cr_R[r1] + Cr_G[g1] + Cr_B[b1])) > max_UV) uyvy->v0 = max_UV;
1855   else uyvy->v0 = a < min_UV ? min_UV : a;
1856 
1857   if ((a = spc_rnd(Y_R[r1] + Y_G[g1] + Y_B[b1])) > max_Y) uyvy->y1 = max_Y;
1858   else uyvy->y1 = a < min_Y ? min_Y : a;
1859 }
1860 
rgb2uyvy_with_gamma(uint8_t r0,uint8_t g0,uint8_t b0,uint8_t r1,uint8_t g1,uint8_t b1,uyvy_macropixel * uyvy,uint8_t * lut)1861 LIVES_INLINE void rgb2uyvy_with_gamma(uint8_t r0, uint8_t g0, uint8_t b0, uint8_t r1, uint8_t g1, uint8_t b1,
1862                                       uyvy_macropixel *uyvy, uint8_t *lut) {
1863   short a;
1864   if ((a = spc_rnd(Cb_R[(r0 = lut[r0])] + Cb_G[(g0 = lut[g0])] + Cb_B[(b0 = lut[b0])])) > max_UV) uyvy->u0 = max_UV;
1865   else uyvy->u0 = a < min_UV ? min_UV : a;
1866 
1867   if ((a = spc_rnd(Y_R[r0] + Y_G[g0] + Y_B[b0])) > max_Y) uyvy->y0 = max_Y;
1868   else uyvy->y0 = a < min_Y ? min_Y : a;
1869 
1870   if ((a = spc_rnd(Cr_R[(r1 = lut[r1])] + Cr_G[(g1 = lut[g1])] + Cr_B[(b1 = lut[b1])])) > max_UV) uyvy->v0 = max_UV;
1871   else uyvy->v0 = a < min_UV ? min_UV : a;
1872 
1873   if ((a = spc_rnd(Y_R[r1] + Y_G[g1] + Y_B[b1])) > max_Y) uyvy->y1 = max_Y;
1874   else uyvy->y1 = a < min_Y ? min_Y : a;
1875 }
1876 
rgb2yuyv(uint8_t r0,uint8_t g0,uint8_t b0,uint8_t r1,uint8_t g1,uint8_t b1,yuyv_macropixel * yuyv)1877 LIVES_INLINE void rgb2yuyv(uint8_t r0, uint8_t g0, uint8_t b0, uint8_t r1, uint8_t g1, uint8_t b1, yuyv_macropixel *yuyv) {
1878   short a;
1879   if ((a = spc_rnd(Y_R[r0] + Y_G[g0] + Y_B[b0])) > max_Y) yuyv->y0 = max_Y;
1880   else yuyv->y0 = a < min_Y ? min_Y : a;
1881 
1882   if ((a = spc_rnd(Cb_R[r0] + Cb_G[g0] + Cb_B[b0])) > max_UV) yuyv->u0 = max_UV;
1883   yuyv->u0 = a < min_UV ? min_UV : a;
1884 
1885   if ((a = spc_rnd(Y_R[r1] + Y_G[g1] + Y_B[b1])) > max_Y) yuyv->y1 = max_Y;
1886   else yuyv->y1 = a < min_Y ? min_Y : a;
1887 
1888   if ((a = spc_rnd(Cr_R[r1] + Cr_G[g1] + Cr_B[b1])) > max_UV) yuyv->v0 = max_UV;
1889   yuyv->v0 = a < min_UV ? min_UV : a;
1890 }
1891 
1892 
rgb2yuyv_with_gamma(uint8_t r0,uint8_t g0,uint8_t b0,uint8_t r1,uint8_t g1,uint8_t b1,yuyv_macropixel * yuyv,uint8_t * lut)1893 LIVES_INLINE void rgb2yuyv_with_gamma(uint8_t r0, uint8_t g0, uint8_t b0, uint8_t r1, uint8_t g1, uint8_t b1,
1894                                       yuyv_macropixel *yuyv, uint8_t *lut) {
1895   short a;
1896   if ((a = spc_rnd(Y_R[(r0 = lut[r0])] + Y_G[(g0 = lut[g0])] + Y_B[(b0 = lut[b0])])) > max_Y) yuyv->y0 = max_Y;
1897   else yuyv->y0 = a < min_Y ? min_Y : a;
1898 
1899   if ((a = spc_rnd(Cb_R[r0] + Cb_G[g0] + Cb_B[b0])) > max_UV) yuyv->u0 = max_UV;
1900   else yuyv->u0 = a < min_UV ? min_UV : a;
1901 
1902   if ((a = spc_rnd(Y_R[(r1 = lut[r1])] + Y_G[(g1 = lut[g1])] + Y_B[(b1 = lut[b1])])) > max_Y) yuyv->y1 = max_Y;
1903   else yuyv->y1 = a < min_Y ? min_Y : a;
1904 
1905   if ((a = spc_rnd(Cr_R[r1] + Cr_G[g1] + Cr_B[b1])) > max_UV) yuyv->v0  = max_UV;
1906   else yuyv->v0 = a < min_UV ? min_UV : a;
1907 }
1908 
1909 
rgb16_2uyvy(uint16_t r0,uint16_t g0,uint16_t b0,uint16_t r1,uint16_t g1,uint16_t b1,uyvy_macropixel * uyvy)1910 LIVES_INLINE void rgb16_2uyvy(uint16_t r0, uint16_t g0, uint16_t b0, uint16_t r1, uint16_t g1, uint16_t b1,
1911                               uyvy_macropixel *uyvy) {
1912   register short a;
1913   uint8_t rfrac0, gfrac0, bfrac0, rfrac1, gfrac1, bfrac1;
1914   uint8_t rr0, bb0, gg0, rr1, gg1, bb1;
1915   uint8_t *bytes;
1916 
1917   bytes = (uint8_t *)&r0;
1918   rfrac0 = bytes[1];
1919   bytes = (uint8_t *)&g0;
1920   gfrac0 = bytes[1];
1921   bytes = (uint8_t *)&b0;
1922   bfrac0 = bytes[1];
1923   bytes = (uint8_t *)&r1;
1924   rfrac1 = bytes[1];
1925   bytes = (uint8_t *)&g1;
1926   gfrac1 = bytes[1];
1927   bytes = (uint8_t *)&b1;
1928   bfrac1 = bytes[1];
1929 
1930   rr0 = (r0 & 0xFF) == 0xFF ? Y_R[255] : (Y_R[r0 & 0xFF] * rfrac0 + Y_R[(r0 & 0xFF) + 1] * (255 - rfrac0)) >> 8;
1931   gg0 = (g0 & 0xFF) == 0xFF ? Y_G[255] : (Y_G[g0 & 0xFF] * gfrac0 + Y_G[(g0 & 0xFF) + 1] * (255 - gfrac0)) >> 8;
1932   bb0 = (b0 & 0xFF) == 0xFF ? Y_B[255] : (Y_B[b0 & 0xFF] * bfrac0 + Y_B[(b0 & 0xFF) + 1] * (255 - bfrac0)) >> 8;
1933 
1934   if ((a = spc_rnd(rr0 + gg0 + bb0)) > max_Y) uyvy->y0 = max_Y;
1935   else uyvy->y0 = a < min_Y ? min_Y : a;
1936 
1937   rr1 = (r1 & 0xFF) == 0xFF ? Y_R[255] : (Y_R[r1 & 0xFF] * rfrac1 + Y_R[(r1 & 0xFF) + 1] * (255 - rfrac1)) >> 8;
1938   gg1 = (g1 & 0xFF) == 0xFF ? Y_G[255] : (Y_G[g1 & 0xFF] * gfrac1 + Y_G[(g1 & 0xFF) + 1] * (255 - gfrac1)) >> 8;
1939   bb1 = (b1 & 0xFF) == 0xFF ? Y_B[255] : (Y_B[b1 & 0xFF] * bfrac1 + Y_B[(b1 & 0xFF) + 1] * (255 - bfrac1)) >> 8;
1940 
1941   if ((a = spc_rnd(rr0 + gg0 + bb0)) > max_Y) uyvy->y1 = max_Y;
1942   else uyvy->y1 = a < min_Y ? min_Y : a;
1943 
1944   rr0 = (r0 & 0xFF) == 0xFF ? Cb_R[255] : (Cb_R[r0 & 0xFF] * rfrac0 + Cb_R[(r0 & 0xFF) + 1] * (255 - rfrac0)) >> 8;
1945   gg0 = (g0 & 0xFF) == 0xFF ? Cb_G[255] : (Cb_G[g0 & 0xFF] * gfrac0 + Cb_G[(g0 & 0xFF) + 1] * (255 - gfrac0)) >> 8;
1946   bb0 = (b0 & 0xFF) == 0xFF ? Cb_B[255] : (Cb_B[b0 & 0xFF] * bfrac0 + Cb_B[(b0 & 0xFF) + 1] * (255 - bfrac0)) >> 8;
1947 
1948   rr1 = (r1 & 0xFF) == 0xFF ? Cb_R[255] : (Cb_R[r1 & 0xFF] * rfrac1 + Cb_R[(r1 & 0xFF) + 1] * (255 - rfrac1)) >> 8;
1949   gg1 = (g1 & 0xFF) == 0xFF ? Cb_G[255] : (Cb_G[g1 & 0xFF] * gfrac1 + Cb_G[(g1 & 0xFF) + 1] * (255 - gfrac1)) >> 8;
1950   bb1 = (b1 & 0xFF) == 0xFF ? Cb_B[255] : (Cb_B[b1 & 0xFF] * bfrac1 + Cb_B[(b1 & 0xFF) + 1] * (255 - bfrac1)) >> 8;
1951 
1952   uyvy->u0 = avg_chroma_3_1f(rr0 + gg0 + bb0, rr1 + gg1 + bb1);
1953 
1954   rr0 = (r0 & 0xFF) == 0xFF ? Cr_R[255] : (Cr_R[r0 & 0xFF] * rfrac0 + Cr_R[(r0 & 0xFF) + 1] * (255 - rfrac0)) >> 8;
1955   gg0 = (g0 & 0xFF) == 0xFF ? Cr_G[255] : (Cr_G[g0 & 0xFF] * gfrac0 + Cr_G[(g0 & 0xFF) + 1] * (255 - gfrac0)) >> 8;
1956   bb0 = (b0 & 0xFF) == 0xFF ? Cr_B[255] : (Cr_B[b0 & 0xFF] * bfrac0 + Cr_B[(b0 & 0xFF) + 1] * (255 - bfrac0)) >> 8;
1957 
1958   rr1 = (r1 & 0xFF) == 0xFF ? Cr_R[255] : (Cr_R[r1 & 0xFF] * rfrac1 + Cr_R[(r1 & 0xFF) + 1] * (255 - rfrac1)) >> 8;
1959   gg1 = (g1 & 0xFF) == 0xFF ? Cr_G[255] : (Cr_G[g1 & 0xFF] * gfrac1 + Cr_G[(g1 & 0xFF) + 1] * (255 - gfrac1)) >> 8;
1960   bb1 = (b1 & 0xFF) == 0xFF ? Cr_B[255] : (Cr_B[b1 & 0xFF] * bfrac1 + Cr_B[(b1 & 0xFF) + 1] * (255 - bfrac1)) >> 8;
1961 
1962   uyvy->v0 = avg_chroma_1_3f(rr0 + gg0 + bb0, rr1 + gg1 + bb1);
1963 }
1964 
1965 #if 0
1966 LIVES_INLINE void rgb16_2uyvy_with_gamma(uint16_t r0, uint16_t g0, uint16_t b0, uint16_t r1, uint16_t g1, uint16_t b1,
1967     uyvy_macropixel *uyvy, uint8_t *lut) {
1968   register short a;
1969   uint8_t rfrac0, gfrac0, bfrac0, rfrac1, gfrac1, bfrac1;
1970   uint8_t rr0, bb0, gg0, rr1, gg1, bb1;
1971   uint8_t *bytes;
1972 
1973   bytes = (uint8_t *)&r0;
1974   rfrac0 = bytes[1];
1975   bytes = (uint8_t *)&g0;
1976   gfrac0 = bytes[1];
1977   bytes = (uint8_t *)&b0;
1978   bfrac0 = bytes[1];
1979   bytes = (uint8_t *)&r1;
1980   rfrac1 = bytes[1];
1981   bytes = (uint8_t *)&g1;
1982   gfrac1 = bytes[1];
1983   bytes = (uint8_t *)&b1;
1984   bfrac1 = bytes[1];
1985 
1986   rr0 = lut[(r0 & 0xFF) == 0xFF ? Y_R[255] : (Y_R[r0 & 0xFF] * rfrac0 + Y_R[(r0 & 0xFF) + 1] * (255 - rfrac0)) >> 8];
1987   gg0 = lut[(g0 & 0xFF) == 0xFF ? Y_G[255] : (Y_G[g0 & 0xFF] * gfrac0 + Y_G[(g0 & 0xFF) + 1] * (255 - gfrac0)) >> 8];
1988   bb0 = lut[(b0 & 0xFF) == 0xFF ? Y_B[255] : (Y_B[b0 & 0xFF] * bfrac0 + Y_B[(b0 & 0xFF) + 1] * (255 - bfrac0)) >> 8];
1989 
1990   if ((a = spc_rnd(rr0 + gg0 + bb0)) > max_Y) uyvy->y0 = max_Y;
1991   else uyvy->y0 = a < min_Y ? min_Y : a;
1992 
1993   rr1 = lut[(r1 & 0xFF) == 0xFF ? Y_R[255] : (Y_R[r1 & 0xFF] * rfrac1 + Y_R[(r1 & 0xFF) + 1] * (255 - rfrac1)) >> 8];
1994   gg1 = lut[(g1 & 0xFF) == 0xFF ? Y_G[255] : (Y_G[g1 & 0xFF] * gfrac1 + Y_G[(g1 & 0xFF) + 1] * (255 - gfrac1)) >> 8];
1995   bb1 = lut[(b1 & 0xFF) == 0xFF ? Y_B[255] : (Y_B[b1 & 0xFF] * bfrac1 + Y_B[(b1 & 0xFF) + 1] * (255 - bfrac1)) >> 8];
1996 
1997   if ((a = spc_rnd(rr1 + gg1 + bb1)) > max_Y) uyvy->y1 = max_Y;
1998   else uyvy->y1 = a < min_Y ? min_Y : a;
1999 
2000   rr0 = lut[(r0 & 0xFF) == 0xFF ? Cb_R[255] : (Cb_R[r0 & 0xFF] * rfrac0 + Cb_R[(r0 & 0xFF) + 1] * (255 - rfrac0)) >> 8];
2001   gg0 = lut[(g0 & 0xFF) == 0xFF ? Cb_G[255] : (Cb_G[g0 & 0xFF] * gfrac0 + Cb_G[(g0 & 0xFF) + 1] * (255 - gfrac0)) >> 8];
2002   bb0 = lut[(b0 & 0xFF) == 0xFF ? Cb_B[255] : (Cb_B[b0 & 0xFF] * bfrac0 + Cb_B[(b0 & 0xFF) + 1] * (255 - bfrac0)) >> 8];
2003 
2004   rr1 = lut[(r1 & 0xFF) == 0xFF ? Cb_R[255] : (Cb_R[r1 & 0xFF] * rfrac1 + Cb_R[(r1 & 0xFF) + 1] * (255 - rfrac1)) >> 8];
2005   gg1 = lut[(g1 & 0xFF) == 0xFF ? Cb_G[255] : (Cb_G[g1 & 0xFF] * gfrac1 + Cb_G[(g1 & 0xFF) + 1] * (255 - gfrac1)) >> 8];
2006   bb1 = lut[(b1 & 0xFF) == 0xFF ? Cb_B[255] : (Cb_B[b1 & 0xFF] * bfrac1 + Cb_B[(b1 & 0xFF) + 1] * (255 - bfrac1)) >> 8];
2007 
2008   uyvy->u0 = avg_chroma_3_1f(rr0 + gg0 + bb0, rr1 + gg1 + bb1);
2009 
2010   rr0 = lut[(r0 & 0xFF) == 0xFF ? Cr_R[255] : (Cr_R[r0 & 0xFF] * rfrac0 + Cr_R[(r0 & 0xFF) + 1] * (255 - rfrac0)) >> 8];
2011   gg0 = lut[(g0 & 0xFF) == 0xFF ? Cr_G[255] : (Cr_G[g0 & 0xFF] * gfrac0 + Cr_G[(g0 & 0xFF) + 1] * (255 - gfrac0)) >> 8];
2012   bb0 = lut[(b0 & 0xFF) == 0xFF ? Cr_B[255] : (Cr_B[b0 & 0xFF] * bfrac0 + Cr_B[(b0 & 0xFF) + 1] * (255 - bfrac0)) >> 8];
2013 
2014   rr1 = lut[(r1 & 0xFF) == 0xFF ? Cr_R[255] : (Cr_R[r1 & 0xFF] * rfrac1 + Cr_R[(r1 & 0xFF) + 1] * (255 - rfrac1)) >> 8];
2015   gg1 = lut[(g1 & 0xFF) == 0xFF ? Cr_G[255] : (Cr_G[g1 & 0xFF] * gfrac1 + Cr_G[(g1 & 0xFF) + 1] * (255 - gfrac1)) >> 8];
2016   bb1 = lut[(b1 & 0xFF) == 0xFF ? Cr_B[255] : (Cr_B[b1 & 0xFF] * bfrac1 + Cr_B[(b1 & 0xFF) + 1] * (255 - bfrac1)) >> 8];
2017 
2018   uyvy->v0 = avg_chroma_1_3f(rr0 + gg0 + bb0, rr1 + gg1 + bb1);
2019 }
2020 #endif
2021 
rgb2_411(uint8_t r0,uint8_t g0,uint8_t b0,uint8_t r1,uint8_t g1,uint8_t b1,uint8_t r2,uint8_t g2,uint8_t b2,uint8_t r3,uint8_t g3,uint8_t b3,yuv411_macropixel * yuv)2022 LIVES_INLINE void rgb2_411(uint8_t r0, uint8_t g0, uint8_t b0, uint8_t r1, uint8_t g1, uint8_t b1,
2023                            uint8_t r2, uint8_t g2, uint8_t b2, uint8_t r3, uint8_t g3, uint8_t b3, yuv411_macropixel *yuv) {
2024   register int a;
2025   if ((a = ((Y_R[r0] + Y_G[g0] + Y_B[b0]) >> FP_BITS)) > max_Y) yuv->y0 = max_Y;
2026   else yuv->y0 = a < min_Y ? min_Y : a;
2027   if ((a = ((Y_R[r1] + Y_G[g1] + Y_B[b1]) >> FP_BITS)) > max_Y) yuv->y1 = max_Y;
2028   else yuv->y1 = a < min_Y ? min_Y : a;
2029   if ((a = ((Y_R[r2] + Y_G[g2] + Y_B[b2]) >> FP_BITS)) > max_Y) yuv->y2 = max_Y;
2030   else yuv->y2 = a < min_Y ? min_Y : a;
2031   if ((a = ((Y_R[r3] + Y_G[g3] + Y_B[b3]) >> FP_BITS)) > max_Y) yuv->y3 = max_Y;
2032   else yuv->y3 = a < min_Y ? min_Y : a;
2033 
2034   if ((a = ((((Cr_R[r0] + Cr_G[g0] + Cr_B[b0]) >> FP_BITS) + ((Cr_R[r1] + Cr_G[g1] + Cr_B[b1]) >> FP_BITS) +
2035              ((Cr_R[r2] + Cr_G[g2] + Cr_B[b2]) >> FP_BITS) + ((Cr_R[r3] + Cr_G[g3] + Cr_B[b3]) >> FP_BITS)) >> 2)) > max_UV)
2036     yuv->v2 = max_UV;
2037   else yuv->v2 = a < min_UV ? min_UV : a;
2038   if ((a = ((((Cb_R[r0] + Cb_G[g0] + Cb_B[b0]) >> FP_BITS) + ((Cb_R[r1] + Cb_G[g1] + Cb_B[b1]) >> FP_BITS) +
2039              ((Cb_R[r2] + Cb_G[g2] + Cb_B[b2]) >> FP_BITS) + ((Cb_R[r3] + Cb_G[g3] + Cb_B[b3]) >> FP_BITS)) >> 2)) > max_UV)
2040     yuv->u2 = max_UV;
2041   else yuv->u2 = a < min_UV ? min_UV : a;
2042 }
2043 
yuv2rgb(uint8_t y,uint8_t u,uint8_t v,uint8_t * r,uint8_t * g,uint8_t * b)2044 LIVES_INLINE void yuv2rgb(uint8_t y, uint8_t u, uint8_t v, uint8_t *r, uint8_t *g, uint8_t *b) {
2045   *r = CLAMP0255f(spc_rnd(RGB_Y[y] + R_Cr[v]));
2046   *g = CLAMP0255f(spc_rnd(RGB_Y[y] + G_Cb[u] + G_Cr[v]));
2047   *b = CLAMP0255f(spc_rnd(RGB_Y[y] + B_Cb[u]));
2048 }
2049 
2050 #define yuv2bgr(y, u, v, b, g, r) yuv2rgb(y, u, v, r, g, b)
2051 
yuv2rgb_with_gamma(uint8_t y,uint8_t u,uint8_t v,uint8_t * r,uint8_t * g,uint8_t * b,uint8_t * lut)2052 LIVES_INLINE void yuv2rgb_with_gamma(uint8_t y, uint8_t u, uint8_t v, uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *lut) {
2053   *r = lut[(int)CLAMP0255f(spc_rnd(RGB_Y[y] + R_Cr[v]))];
2054   *g = lut[(int)CLAMP0255f(spc_rnd(RGB_Y[y] + G_Cb[u] + G_Cr[v]))];
2055   *b = lut[(int)CLAMP0255f(spc_rnd(RGB_Y[y] + B_Cb[u]))];
2056 }
2057 
2058 #define yuv2bgr_with_gamma(y, u, v, b, g, r, lut) yuv2rgb_with_gamma(y, u, v, r, g, b, lut)
2059 
uyvy2rgb(uyvy_macropixel * uyvy,uint8_t * r0,uint8_t * g0,uint8_t * b0,uint8_t * r1,uint8_t * g1,uint8_t * b1)2060 LIVES_INLINE void uyvy2rgb(uyvy_macropixel *uyvy, uint8_t *r0, uint8_t *g0, uint8_t *b0,
2061                            uint8_t *r1, uint8_t *g1, uint8_t *b1) {
2062   yuv2rgb(uyvy->y0, uyvy->u0, uyvy->v0, r0, g0, b0);
2063   yuv2rgb(uyvy->y1, uyvy->u0, uyvy->v0, r1, g1, b1);
2064   //if (uyvy->y0>240||uyvy->u0>240||uyvy->v0>240||uyvy->y1>240) lives_printerr("got unclamped !\n");
2065 }
2066 
2067 
yuyv2rgb(yuyv_macropixel * yuyv,uint8_t * r0,uint8_t * g0,uint8_t * b0,uint8_t * r1,uint8_t * g1,uint8_t * b1)2068 LIVES_INLINE void yuyv2rgb(yuyv_macropixel *yuyv, uint8_t *r0, uint8_t *g0, uint8_t *b0,
2069                            uint8_t *r1, uint8_t *g1, uint8_t *b1) {
2070   yuv2rgb(yuyv->y0, yuyv->u0, yuyv->v0, r0, g0, b0);
2071   yuv2rgb(yuyv->y1, yuyv->u0, yuyv->v0, r1, g1, b1);
2072 }
2073 
2074 
yuv888_2_rgb(uint8_t * yuv,uint8_t * rgb,boolean add_alpha)2075 LIVES_INLINE void yuv888_2_rgb(uint8_t *yuv, uint8_t *rgb, boolean add_alpha) {
2076   yuv2rgb(yuv[0], yuv[1], yuv[2], &(rgb[0]), &(rgb[1]), &(rgb[2]));
2077   if (add_alpha) rgb[3] = 255;
2078 }
2079 
2080 
yuva8888_2_rgba(uint8_t * yuva,uint8_t * rgba,boolean del_alpha)2081 LIVES_INLINE void yuva8888_2_rgba(uint8_t *yuva, uint8_t *rgba, boolean del_alpha) {
2082   yuv2rgb(yuva[0], yuva[1], yuva[2], &(rgba[0]), &(rgba[1]), &(rgba[2]));
2083   if (!del_alpha) rgba[3] = yuva[3];
2084 }
2085 
2086 
yuv888_2_bgr(uint8_t * yuv,uint8_t * bgr,boolean add_alpha)2087 LIVES_INLINE void yuv888_2_bgr(uint8_t *yuv, uint8_t *bgr, boolean add_alpha) {
2088   yuv2bgr(yuv[0], yuv[1], yuv[2], &(bgr[0]), &(bgr[1]), &(bgr[2]));
2089   if (add_alpha) bgr[3] = 255;
2090 }
2091 
2092 
yuva8888_2_bgra(uint8_t * yuva,uint8_t * bgra,boolean del_alpha)2093 LIVES_INLINE void yuva8888_2_bgra(uint8_t *yuva, uint8_t *bgra, boolean del_alpha) {
2094   yuv2bgr(yuva[0], yuva[1], yuva[2], &(bgra[0]), &(bgra[1]), &(bgra[2]));
2095   if (!del_alpha) bgra[3] = yuva[3];
2096 }
2097 
2098 
yuv888_2_argb(uint8_t * yuv,uint8_t * argb)2099 LIVES_INLINE void yuv888_2_argb(uint8_t *yuv, uint8_t *argb) {
2100   argb[0] = 255;
2101   yuv2rgb(yuv[0], yuv[1], yuv[2], &(argb[1]), &(argb[2]), &(argb[3]));
2102 }
2103 
2104 
yuva8888_2_argb(uint8_t * yuva,uint8_t * argb)2105 LIVES_INLINE void yuva8888_2_argb(uint8_t *yuva, uint8_t *argb) {
2106   argb[0] = yuva[3];
2107   yuv2rgb(yuva[0], yuva[1], yuva[2], &(argb[1]), &(argb[2]), &(argb[3]));
2108 }
2109 
2110 
uyvy_2_yuv422(uyvy_macropixel * uyvy,uint8_t * y0,uint8_t * u0,uint8_t * v0,uint8_t * y1)2111 LIVES_INLINE void uyvy_2_yuv422(uyvy_macropixel *uyvy, uint8_t *y0, uint8_t *u0, uint8_t *v0, uint8_t *y1) {
2112   *u0 = uyvy->u0;
2113   *y0 = uyvy->y0;
2114   *v0 = uyvy->v0;
2115   *y1 = uyvy->y1;
2116 }
2117 
2118 
yuyv_2_yuv422(yuyv_macropixel * yuyv,uint8_t * y0,uint8_t * u0,uint8_t * v0,uint8_t * y1)2119 LIVES_INLINE void yuyv_2_yuv422(yuyv_macropixel *yuyv, uint8_t *y0, uint8_t *u0, uint8_t *v0, uint8_t *y1) {
2120   *y0 = yuyv->y0;
2121   *u0 = yuyv->u0;
2122   *y1 = yuyv->y1;
2123   *v0 = yuyv->v0;
2124 }
2125 
2126 /////////////////////////////////////////////////
2127 //utilities
2128 
2129 
weed_palette_is_painter_palette(int pal)2130 LIVES_GLOBAL_INLINE boolean weed_palette_is_painter_palette(int pal) {
2131 #ifdef LIVES_PAINTER_IS_CAIRO
2132   if (pal == WEED_PALETTE_A8 || pal == WEED_PALETTE_A1) return TRUE;
2133   if (capable->byte_order == LIVES_BIG_ENDIAN) {
2134     if (pal == WEED_PALETTE_ARGB32) return TRUE;
2135   } else {
2136     if (pal == WEED_PALETTE_BGRA32) return TRUE;
2137   }
2138 #endif
2139   return FALSE;
2140 }
2141 
2142 
weed_palette_is_lower_quality(int p1,int p2)2143 boolean weed_palette_is_lower_quality(int p1, int p2) {
2144   // return TRUE if p1 is lower quality than p2
2145   // we don't yet handle float palettes, or RGB or alpha properly
2146 
2147   // currently only works well for YUV palettes
2148 
2149   if ((weed_palette_is_alpha(p1) && !weed_palette_is_alpha(p2)) ||
2150       (weed_palette_is_alpha(p2) && !weed_palette_is_alpha(p1))) return TRUE; // invalid conversion
2151 
2152   if (weed_palette_is_rgb(p1) && weed_palette_is_rgb(p2)) return FALSE;
2153 
2154   switch (p2) {
2155   case WEED_PALETTE_YUVA8888:
2156     if (p1 != WEED_PALETTE_YUVA8888 && p1 != WEED_PALETTE_YUVA4444P) return TRUE;
2157     break;
2158   case WEED_PALETTE_YUVA4444P:
2159     if (p1 != WEED_PALETTE_YUVA8888 && p1 != WEED_PALETTE_YUVA4444P) return TRUE;
2160     break;
2161   case WEED_PALETTE_YUV888:
2162     if (p1 != WEED_PALETTE_YUVA8888 && p1 != WEED_PALETTE_YUVA4444P && p1 != WEED_PALETTE_YUV444P
2163         && p1 != WEED_PALETTE_YUVA4444P)
2164       return TRUE;
2165     break;
2166   case WEED_PALETTE_YUV444P:
2167     if (p1 != WEED_PALETTE_YUVA8888 && p1 != WEED_PALETTE_YUVA4444P && p1 != WEED_PALETTE_YUV444P
2168         && p1 != WEED_PALETTE_YUVA4444P)
2169       return TRUE;
2170     break;
2171 
2172   case WEED_PALETTE_YUV422P:
2173   case WEED_PALETTE_UYVY8888:
2174   case WEED_PALETTE_YUYV8888:
2175     if (p1 != WEED_PALETTE_YUVA8888 && p1 != WEED_PALETTE_YUVA4444P && p1 != WEED_PALETTE_YUV444P &&
2176         p1 != WEED_PALETTE_YUVA4444P && p1 != WEED_PALETTE_YUV422P && p1 != WEED_PALETTE_UYVY8888
2177         && p1 != WEED_PALETTE_YUYV8888)
2178       return TRUE;
2179     break;
2180 
2181   case WEED_PALETTE_YUV420P:
2182   case WEED_PALETTE_YVU420P:
2183     if (p1 == WEED_PALETTE_YUV411) return TRUE;
2184     break;
2185   case WEED_PALETTE_A8:
2186     if (p1 == WEED_PALETTE_A1) return TRUE;
2187   }
2188   return FALSE; // TODO
2189 }
2190 
2191 /////////////////////////////////////////////////////////
2192 
lives_pixbuf_is_all_black(LiVESPixbuf * pixbuf)2193 LIVES_GLOBAL_INLINE boolean lives_pixbuf_is_all_black(LiVESPixbuf *pixbuf) {
2194   int width = lives_pixbuf_get_width(pixbuf);
2195   int height = lives_pixbuf_get_height(pixbuf);
2196   int rstride = lives_pixbuf_get_rowstride(pixbuf);
2197   boolean has_alpha = lives_pixbuf_get_has_alpha(pixbuf);
2198   const uint8_t *pdata = lives_pixbuf_get_pixels_readonly(pixbuf);
2199   uint8_t a, b, c;
2200   int offs = 0;
2201   int psize = has_alpha ? 4 : 3;
2202   register int i, j;
2203 
2204   width *= psize;
2205 
2206   for (j = 0; j < height; j++) {
2207     for (i = offs; i < width; i += psize) {
2208       /** return FALSE if r >= 32, b >= 32 and g >= 24
2209         here we use a, b, and c for the first 3 bytes of the pixel. Since a and c are symmetric and we ignore byte 4,
2210         this will work for RGB, BGR, RGBA and BGRA (we could also check ARGB by setting offs to 1).
2211 
2212         Algorithm:
2213         (a & 0x1F) ^ a - nonzero iff a >= 32
2214         (c & 0x1F) ^ c - nonzero iff c >= 32
2215 
2216         ((a & c) & 0x1F) ^ (a & c) - nonzero only if both are true
2217 
2218         (b & 0x1F) ^ b  - nonzero iff b >= 32
2219         ((b << 1) & 0x1F) ^ (b << 1)  - nonzero iff b >= 16
2220         ((b << 2) & 0x1F) ^ (b << 2)  - nonzero iff b >= 8
2221         b & 0x0F - masks any values >= 32
2222       */
2223       a = pdata[i];
2224       b = pdata[i + 1];
2225       c = pdata[i + 2];
2226 
2227       if (((a & 0x1F) ^ a) & ((c & 0x1F) ^ c) & (((b & 0x1F) ^ b) | ((((b << 1) & 0x1F) ^ (b  << 1))
2228           & ((((b & 0x0F) << 2) & 0x1F) ^ ((b & 0x0F) << 2))))) return FALSE;
2229     }
2230     pdata += rstride;
2231   }
2232   return TRUE;
2233 }
2234 
2235 
pixel_data_planar_from_membuf(void ** pixel_data,void * data,size_t size,int palette,boolean contig)2236 void pixel_data_planar_from_membuf(void **pixel_data, void *data, size_t size, int palette, boolean contig) {
2237   // convert contiguous memory block planes to planar data
2238   // size is the byte size of the Y plane (width*height in pixels)
2239 
2240   switch (palette) {
2241   case WEED_PALETTE_YUV444P:
2242     if (contig) lives_memcpy(pixel_data[0], data, size * 3);
2243     else {
2244       lives_memcpy(pixel_data[0], data, size);
2245       lives_memcpy(pixel_data[1], (uint8_t *)data + size, size);
2246       lives_memcpy(pixel_data[2], (uint8_t *)data + size * 2, size);
2247     }
2248     break;
2249   case WEED_PALETTE_YUVA4444P:
2250     if (contig) lives_memcpy(pixel_data[0], data, size * 4);
2251     else {
2252       lives_memcpy(pixel_data[0], data, size);
2253       lives_memcpy(pixel_data[1], (uint8_t *)data + size, size);
2254       lives_memcpy(pixel_data[2], (uint8_t *)data + size * 2, size);
2255       lives_memcpy(pixel_data[3], (uint8_t *)data + size * 2, size);
2256     }
2257     break;
2258   case WEED_PALETTE_YUV422P:
2259     if (contig) lives_memcpy(pixel_data[0], data, size * 2);
2260     else {
2261       lives_memcpy(pixel_data[0], data, size);
2262       lives_memcpy(pixel_data[1], (uint8_t *)data + size, size / 2);
2263       lives_memcpy(pixel_data[2], (uint8_t *)data + size * 3 / 2, size / 2);
2264     }
2265     break;
2266   case WEED_PALETTE_YUV420P:
2267   case WEED_PALETTE_YVU420P:
2268     if (contig) lives_memcpy(pixel_data[0], data, size * 3 / 2);
2269     else {
2270       lives_memcpy(pixel_data[0], data, size);
2271       lives_memcpy(pixel_data[1], (uint8_t *)data + size, size / 4);
2272       lives_memcpy(pixel_data[2], (uint8_t *)data + size * 5 / 4, size / 4);
2273     }
2274     break;
2275   }
2276 }
2277 
2278 
2279 ///////////////////////////////////////////////////////////
2280 // frame conversions
2281 
convert_yuv888_to_rgb_frame(uint8_t * src,int hsize,int vsize,int irowstride,int orowstride,uint8_t * dest,boolean add_alpha,int clamping,int subspace,int thread_id)2282 static void convert_yuv888_to_rgb_frame(uint8_t *src, int hsize, int vsize, int irowstride,
2283                                         int orowstride, uint8_t *dest, boolean add_alpha, int clamping, int subspace, int thread_id) {
2284   int x, y, i;
2285   size_t offs = 3;
2286 
2287   if (LIVES_UNLIKELY(!conv_YR_inited)) init_YUV_to_RGB_tables();
2288 
2289   if (thread_id == -1) {
2290     set_conversion_arrays(clamping, subspace);
2291 
2292     if (prefs->nfx_threads > 1) {
2293       lives_thread_t threads[prefs->nfx_threads];
2294       uint8_t *end = src + vsize * irowstride;
2295       int nthreads = 1;
2296       int dheight, xdheight;
2297       lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
2298 
2299       xdheight = CEIL((double)vsize / (double)prefs->nfx_threads, 4);
2300       for (i = prefs->nfx_threads - 1; i >= 0; i--) {
2301         dheight = xdheight;
2302 
2303         if ((src + dheight * i * irowstride) < end) {
2304           ccparams[i].src = src + dheight * i * irowstride;
2305           ccparams[i].hsize = hsize;
2306           ccparams[i].dest = dest + dheight * i * orowstride;
2307 
2308           if (dheight * (i + 1) > (vsize - 4)) {
2309             dheight = vsize - (dheight * i);
2310           }
2311 
2312           ccparams[i].vsize = dheight;
2313 
2314           ccparams[i].irowstrides[0] = irowstride;
2315           ccparams[i].orowstrides[0] = orowstride;
2316           ccparams[i].out_alpha = add_alpha;
2317           ccparams[i].in_clamping = clamping;
2318           ccparams[i].in_subspace = subspace;
2319           ccparams[i].thread_id = i;
2320 
2321           if (i == 0) convert_yuv888_to_rgb_frame_thread(&ccparams[i]);
2322           else {
2323             lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_yuv888_to_rgb_frame_thread, &ccparams[i]);
2324             nthreads++;
2325           }
2326         }
2327       }
2328 
2329       for (i = 1; i < nthreads; i++) {
2330         lives_thread_join(threads[i], NULL);
2331       }
2332       lives_free(ccparams);
2333       return;
2334     }
2335   }
2336 
2337   if (add_alpha) offs = 4;
2338   orowstride -= offs * hsize;
2339   irowstride -= hsize * 3;
2340 
2341   for (y = 0; y < vsize; y++) {
2342     for (x = 0; x < hsize; x++) {
2343       yuv888_2_rgb(src, dest, add_alpha);
2344       src += 3;
2345       dest += offs;
2346     }
2347     dest += orowstride;
2348     src += irowstride;
2349   }
2350 }
2351 
2352 
convert_yuv888_to_rgb_frame_thread(void * data)2353 static void *convert_yuv888_to_rgb_frame_thread(void *data) {
2354   lives_cc_params *ccparams = (lives_cc_params *)data;
2355   convert_yuv888_to_rgb_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
2356                               ccparams->orowstrides[0], (uint8_t *)ccparams->dest, ccparams->out_alpha,
2357                               ccparams->in_clamping, ccparams->in_subspace, ccparams->thread_id);
2358   return NULL;
2359 }
2360 
2361 
convert_yuva8888_to_rgba_frame(uint8_t * src,int hsize,int vsize,int irowstride,int orowstride,uint8_t * dest,boolean del_alpha,int clamping,int subspace,int thread_id)2362 static void convert_yuva8888_to_rgba_frame(uint8_t *src, int hsize, int vsize, int irowstride,
2363     int orowstride, uint8_t *dest, boolean del_alpha, int clamping, int subspace, int thread_id) {
2364   register int x, y, i;
2365 
2366   size_t offs = 4;
2367 
2368   if (LIVES_UNLIKELY(!conv_YR_inited)) init_YUV_to_RGB_tables();
2369 
2370   if (thread_id == -1)
2371     set_conversion_arrays(clamping, subspace);
2372 
2373   if (thread_id == -1 && prefs->nfx_threads > 1) {
2374     lives_thread_t threads[prefs->nfx_threads];
2375     uint8_t *end = src + vsize * irowstride;
2376     int nthreads = 1;
2377     int dheight, xdheight;
2378     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
2379 
2380     xdheight = CEIL((double)vsize / (double)prefs->nfx_threads, 4);
2381     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
2382       dheight = xdheight;
2383 
2384       if ((src + dheight * i * irowstride) < end) {
2385         ccparams[i].src = src + dheight * i * irowstride;
2386         ccparams[i].hsize = hsize;
2387         ccparams[i].dest = dest + dheight * i * orowstride;
2388 
2389         if (dheight * (i + 1) > (vsize - 4)) {
2390           dheight = vsize - (dheight * i);
2391         }
2392 
2393         ccparams[i].vsize = dheight;
2394 
2395         ccparams[i].irowstrides[0] = irowstride;
2396         ccparams[i].orowstrides[0] = orowstride;
2397         ccparams[i].out_alpha = !del_alpha;
2398         ccparams[i].in_clamping = clamping;
2399         ccparams[i].in_subspace = subspace;
2400         ccparams[i].thread_id = i;
2401 
2402         if (i == 0) convert_yuva8888_to_rgba_frame_thread(&ccparams[i]);
2403         else {
2404           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_yuva8888_to_rgba_frame_thread, &ccparams[i]);
2405           nthreads++;
2406         }
2407       }
2408     }
2409 
2410     for (i = 1; i < nthreads; i++) {
2411       lives_thread_join(threads[i], NULL);
2412     }
2413     lives_free(ccparams);
2414     return;
2415   }
2416 
2417   if (del_alpha) offs = 3;
2418   orowstride -= offs * hsize;
2419   irowstride -= hsize * 4;
2420 
2421   for (y = 0; y < vsize; y++) {
2422     for (x = 0; x < hsize; x++) {
2423       yuva8888_2_rgba(src, dest, del_alpha);
2424       src += 4;
2425       dest += offs;
2426     }
2427     dest += orowstride;
2428     src += irowstride;
2429   }
2430 }
2431 
2432 
convert_yuva8888_to_rgba_frame_thread(void * data)2433 static void *convert_yuva8888_to_rgba_frame_thread(void *data) {
2434   lives_cc_params *ccparams = (lives_cc_params *)data;
2435   convert_yuva8888_to_rgba_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
2436                                  ccparams->orowstrides[0], (uint8_t *)ccparams->dest, !ccparams->out_alpha,
2437                                  ccparams->in_clamping, ccparams->in_subspace, ccparams->thread_id);
2438   return NULL;
2439 }
2440 
2441 
convert_yuv888_to_bgr_frame(uint8_t * src,int hsize,int vsize,int irowstride,int orowstride,uint8_t * dest,boolean add_alpha,int clamping,int subspace,int thread_id)2442 static void convert_yuv888_to_bgr_frame(uint8_t *src, int hsize, int vsize, int irowstride,
2443                                         int orowstride, uint8_t *dest, boolean add_alpha, int clamping, int subspace, int thread_id) {
2444   register int x, y, i;
2445   size_t offs = 3;
2446 
2447   if (LIVES_UNLIKELY(!conv_YR_inited)) init_YUV_to_RGB_tables();
2448   if (thread_id == -1)
2449     set_conversion_arrays(clamping, subspace);
2450 
2451   if (thread_id == -1 && prefs->nfx_threads > 1) {
2452     lives_thread_t threads[prefs->nfx_threads];
2453     uint8_t *end = src + vsize * irowstride;
2454     int nthreads = 1;
2455     int dheight, xdheight;
2456     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
2457 
2458     xdheight = CEIL((double)vsize / (double)prefs->nfx_threads, 4);
2459     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
2460       dheight = xdheight;
2461 
2462       if ((src + dheight * i * irowstride) < end) {
2463         ccparams[i].src = src + dheight * i * irowstride;
2464         ccparams[i].hsize = hsize;
2465         ccparams[i].dest = dest + dheight * i * orowstride;
2466 
2467         if (dheight * (i + 1) > (vsize - 4)) {
2468           dheight = vsize - (dheight * i);
2469         }
2470 
2471         ccparams[i].vsize = dheight;
2472 
2473         ccparams[i].irowstrides[0] = irowstride;
2474         ccparams[i].orowstrides[0] = orowstride;
2475         ccparams[i].out_alpha = add_alpha;
2476         ccparams[i].in_clamping = clamping;
2477         ccparams[i].in_subspace = subspace;
2478         ccparams[i].thread_id = i;
2479 
2480         if (i == 0) convert_yuv888_to_bgr_frame_thread(&ccparams[i]);
2481         else {
2482           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_yuv888_to_bgr_frame_thread, &ccparams[i]);
2483           nthreads++;
2484         }
2485       }
2486     }
2487 
2488     for (i = 1; i < nthreads; i++) {
2489       lives_thread_join(threads[i], NULL);
2490     }
2491     lives_free(ccparams);
2492     return;
2493   }
2494 
2495   if (add_alpha) offs = 4;
2496   orowstride -= offs * hsize;
2497   irowstride -= hsize * 3;
2498 
2499   for (y = 0; y < vsize; y++) {
2500     for (x = 0; x < hsize; x++) {
2501       yuv888_2_bgr(src, dest, add_alpha);
2502       src += 3;
2503       dest += offs;
2504     }
2505     dest += orowstride;
2506     src += irowstride;
2507   }
2508 }
2509 
2510 
convert_yuv888_to_bgr_frame_thread(void * data)2511 static void *convert_yuv888_to_bgr_frame_thread(void *data) {
2512   lives_cc_params *ccparams = (lives_cc_params *)data;
2513   convert_yuv888_to_bgr_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
2514                               ccparams->orowstrides[0], (uint8_t *)ccparams->dest, ccparams->out_alpha,
2515                               ccparams->in_clamping, ccparams->in_subspace, ccparams->thread_id);
2516   return NULL;
2517 }
2518 
2519 
convert_yuva8888_to_bgra_frame(uint8_t * src,int hsize,int vsize,int irowstride,int orowstride,uint8_t * dest,boolean del_alpha,int clamping,int subspace,int thread_id)2520 static void convert_yuva8888_to_bgra_frame(uint8_t *src, int hsize, int vsize, int irowstride,
2521     int orowstride, uint8_t *dest, boolean del_alpha, int clamping, int subspace, int thread_id) {
2522   register int x, y, i;
2523 
2524   size_t offs = 4;
2525 
2526   if (LIVES_UNLIKELY(!conv_YR_inited)) init_YUV_to_RGB_tables();
2527   if (thread_id == -1)
2528     set_conversion_arrays(clamping, subspace);
2529 
2530   if (thread_id == -1 && prefs->nfx_threads > 1) {
2531     lives_thread_t threads[prefs->nfx_threads];
2532     uint8_t *end = src + vsize * irowstride;
2533     int nthreads = 1;
2534     int dheight, xdheight;
2535     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
2536 
2537     xdheight = CEIL((double)vsize / (double)prefs->nfx_threads, 4);
2538     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
2539       dheight = xdheight;
2540 
2541       if ((src + dheight * i * irowstride) < end) {
2542         ccparams[i].src = src + dheight * i * irowstride;
2543         ccparams[i].hsize = hsize;
2544         ccparams[i].dest = dest + dheight * i * orowstride;
2545 
2546         if (dheight * (i + 1) > (vsize - 4)) {
2547           dheight = vsize - (dheight * i);
2548         }
2549 
2550         ccparams[i].vsize = dheight;
2551 
2552         ccparams[i].irowstrides[0] = irowstride;
2553         ccparams[i].orowstrides[0] = orowstride;
2554         ccparams[i].out_alpha = !del_alpha;
2555         ccparams[i].in_clamping = clamping;
2556         ccparams[i].in_subspace = subspace;
2557         ccparams[i].thread_id = i;
2558 
2559         if (i == 0) convert_yuva8888_to_bgra_frame_thread(&ccparams[i]);
2560         else {
2561           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_yuva8888_to_bgra_frame_thread, &ccparams[i]);
2562           nthreads++;
2563         }
2564       }
2565     }
2566 
2567     for (i = 1; i < nthreads; i++) {
2568       lives_thread_join(threads[i], NULL);
2569     }
2570     lives_free(ccparams);
2571     return;
2572   }
2573 
2574   if (del_alpha) offs = 3;
2575   orowstride -= offs * hsize;
2576   irowstride -= 4 * hsize;
2577 
2578   for (y = 0; y < vsize; y++) {
2579     for (x = 0; x < hsize; x++) {
2580       yuva8888_2_bgra(src, dest, del_alpha);
2581       src += 4;
2582       dest += offs;
2583     }
2584     dest += orowstride;
2585     src += irowstride;
2586   }
2587 }
2588 
2589 
convert_yuva8888_to_bgra_frame_thread(void * data)2590 static void *convert_yuva8888_to_bgra_frame_thread(void *data) {
2591   lives_cc_params *ccparams = (lives_cc_params *)data;
2592   convert_yuva8888_to_bgra_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
2593                                  ccparams->orowstrides[0], (uint8_t *)ccparams->dest, !ccparams->out_alpha,
2594                                  ccparams->in_clamping, ccparams->in_subspace, ccparams->thread_id);
2595   return NULL;
2596 }
2597 
2598 
convert_yuv888_to_argb_frame(uint8_t * src,int hsize,int vsize,int irowstride,int orowstride,uint8_t * dest,int clamping,int subspace,int thread_id)2599 static void convert_yuv888_to_argb_frame(uint8_t *src, int hsize, int vsize, int irowstride,
2600     int orowstride, uint8_t *dest, int clamping, int subspace, int thread_id) {
2601   int x, y, i;
2602   size_t offs = 4;
2603 
2604   if (LIVES_UNLIKELY(!conv_YR_inited)) init_YUV_to_RGB_tables();
2605   if (thread_id == -1)
2606     set_conversion_arrays(clamping, subspace);
2607 
2608   if (thread_id == -1 && prefs->nfx_threads > 1) {
2609     lives_thread_t threads[prefs->nfx_threads];
2610     uint8_t *end = src + vsize * irowstride;
2611     int nthreads = 1;
2612     int dheight, xdheight;
2613     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
2614 
2615     xdheight = CEIL((double)vsize / (double)prefs->nfx_threads, 4);
2616     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
2617       dheight = xdheight;
2618 
2619       if ((src + dheight * i * irowstride) < end) {
2620         ccparams[i].src = src + dheight * i * irowstride;
2621         ccparams[i].hsize = hsize;
2622         ccparams[i].dest = dest + dheight * i * orowstride;
2623 
2624         if (dheight * (i + 1) > (vsize - 4)) {
2625           dheight = vsize - (dheight * i);
2626         }
2627 
2628         ccparams[i].vsize = dheight;
2629 
2630         ccparams[i].irowstrides[0] = irowstride;
2631         ccparams[i].orowstrides[0] = orowstride;
2632         ccparams[i].in_clamping = clamping;
2633         ccparams[i].in_subspace = subspace;
2634         ccparams[i].thread_id = i;
2635 
2636         if (i == 0) convert_yuv888_to_argb_frame_thread(&ccparams[i]);
2637         else {
2638           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_yuv888_to_argb_frame_thread, &ccparams[i]);
2639           nthreads++;
2640         }
2641       }
2642     }
2643 
2644     for (i = 1; i < nthreads; i++) {
2645       lives_thread_join(threads[i], NULL);
2646     }
2647     lives_free(ccparams);
2648     return;
2649   }
2650 
2651   orowstride -= offs * hsize;
2652   irowstride -= hsize * 3;
2653 
2654   for (y = 0; y < vsize; y++) {
2655     for (x = 0; x < hsize; x++) {
2656       yuv888_2_argb(src, dest);
2657       src += 3;
2658       dest += 4;
2659     }
2660     dest += orowstride;
2661     src += irowstride;
2662   }
2663 }
2664 
2665 
convert_yuv888_to_argb_frame_thread(void * data)2666 static void *convert_yuv888_to_argb_frame_thread(void *data) {
2667   lives_cc_params *ccparams = (lives_cc_params *)data;
2668   convert_yuv888_to_argb_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
2669                                ccparams->orowstrides[0], (uint8_t *)ccparams->dest,
2670                                ccparams->in_clamping, ccparams->in_subspace, ccparams->thread_id);
2671   return NULL;
2672 }
2673 
2674 
convert_yuva8888_to_argb_frame(uint8_t * src,int hsize,int vsize,int irowstride,int orowstride,uint8_t * dest,int clamping,int subspace,int thread_id)2675 static void convert_yuva8888_to_argb_frame(uint8_t *src, int hsize, int vsize, int irowstride,
2676     int orowstride, uint8_t *dest, int clamping, int subspace, int thread_id) {
2677   int x, y, i;
2678 
2679   size_t offs = 4;
2680 
2681   if (LIVES_UNLIKELY(!conv_YR_inited)) init_YUV_to_RGB_tables();
2682   if (thread_id == -1)
2683     set_conversion_arrays(clamping, subspace);
2684 
2685   if (thread_id == -1 && prefs->nfx_threads > 1) {
2686     lives_thread_t threads[prefs->nfx_threads];
2687     uint8_t *end = src + vsize * irowstride;
2688     int nthreads = 1;
2689     int dheight, xdheight;
2690     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
2691 
2692     xdheight = CEIL((double)vsize / (double)prefs->nfx_threads, 4);
2693     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
2694       dheight = xdheight;
2695 
2696       if ((src + dheight * i * irowstride) < end) {
2697         ccparams[i].src = src + dheight * i * irowstride;
2698         ccparams[i].hsize = hsize;
2699         ccparams[i].dest = dest + dheight * i * orowstride;
2700 
2701         if (dheight * (i + 1) > (vsize - 4)) {
2702           dheight = vsize - (dheight * i);
2703         }
2704 
2705         ccparams[i].vsize = dheight;
2706 
2707         ccparams[i].irowstrides[0] = irowstride;
2708         ccparams[i].orowstrides[0] = orowstride;
2709         ccparams[i].in_clamping = clamping;
2710         ccparams[i].in_subspace = subspace;
2711         ccparams[i].thread_id = i;
2712 
2713         if (i == 0) convert_yuva8888_to_argb_frame_thread(&ccparams[i]);
2714         else {
2715           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_yuva8888_to_rgba_frame_thread, &ccparams[i]);
2716           nthreads++;
2717         }
2718       }
2719     }
2720 
2721     for (i = 1; i < nthreads; i++) {
2722       lives_thread_join(threads[i], NULL);
2723     }
2724     lives_free(ccparams);
2725     return;
2726   }
2727 
2728   orowstride -= offs * hsize;
2729   irowstride -= hsize * 4;
2730 
2731   for (y = 0; y < vsize; y++) {
2732     for (x = 0; x < hsize; x++) {
2733       yuva8888_2_argb(src, dest);
2734       src += 4;
2735       dest += 4;
2736     }
2737     dest += orowstride;
2738     src += irowstride;
2739   }
2740 }
2741 
2742 
convert_yuva8888_to_argb_frame_thread(void * data)2743 static void *convert_yuva8888_to_argb_frame_thread(void *data) {
2744   lives_cc_params *ccparams = (lives_cc_params *)data;
2745   convert_yuva8888_to_argb_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
2746                                  ccparams->orowstrides[0], (uint8_t *)ccparams->dest,
2747                                  ccparams->in_clamping, ccparams->in_subspace, ccparams->thread_id);
2748   return NULL;
2749 }
2750 
2751 
convert_yuv420p_to_rgb_frame(uint8_t ** src,int width,int height,boolean is_bottom,int * istrides,int orowstride,uint8_t * dest,boolean add_alpha,boolean is_422,int sampling,int clamping,int subspace,int gamma,int tgamma,uint8_t * gamma_lut,int thread_id)2752 static void convert_yuv420p_to_rgb_frame(uint8_t **src, int width, int height, boolean is_bottom, int *istrides, int orowstride,
2753     uint8_t *dest, boolean add_alpha, boolean is_422, int sampling, int clamping, int subspace,
2754     int gamma, int tgamma, uint8_t *gamma_lut, int thread_id) {
2755   int i, j;
2756   uint8_t *s_y = src[0], *s_u = src[1], *s_v = src[2];
2757   int opsize = 3;
2758   int irow = istrides[0] - width;
2759   boolean even = TRUE;
2760   size_t uv_offs = 0;
2761   uint8_t y, u, v, next_u, next_v, last_u, last_v;
2762 
2763   if (thread_id == -1) {
2764     if (LIVES_UNLIKELY(!conv_YR_inited)) init_YUV_to_RGB_tables();
2765 
2766     /// TODO: this is NOT threadsafe !!!!
2767     set_conversion_arrays(clamping, subspace);
2768 
2769     if (tgamma) gamma_lut = create_gamma_lut(1.0, gamma, tgamma);
2770     if (prefs->nfx_threads > 1) {
2771       lives_thread_t threads[prefs->nfx_threads];
2772       uint8_t *end = src[0] + height * istrides[0];
2773       int nthreads = 1;
2774       int dheight, xdheight;
2775       lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
2776 
2777       xdheight = CEIL((double)height / (double)prefs->nfx_threads, 4);
2778       for (i = prefs->nfx_threads - 1; i >= 0; i--) {
2779         dheight = xdheight;
2780 
2781         if ((src[0] + dheight * i * istrides[0]) < end) {
2782           ccparams[i].srcp[0] = src[0] + dheight * i * istrides[0];
2783           ccparams[i].srcp[1] = src[1] + dheight / 2 * i * istrides[1];
2784           ccparams[i].srcp[2] = src[2] + dheight / 2 * i * istrides[2];
2785           ccparams[i].hsize = width;
2786           ccparams[i].dest = dest + dheight * i * orowstride;
2787 
2788           if (dheight * (i + 1) > (height - 4)) {
2789             dheight = height - (dheight * i);
2790           }
2791 
2792           ccparams[i].vsize = dheight;
2793           if (i == prefs->nfx_threads - 1) {
2794             ccparams[i].is_bottom = TRUE;
2795           }
2796           ccparams[i].irowstrides[0] = istrides[0];
2797           ccparams[i].irowstrides[1] = istrides[1];
2798           ccparams[i].irowstrides[2] = istrides[2];
2799           ccparams[i].orowstrides[0] = orowstride;
2800           ccparams[i].out_alpha = add_alpha;
2801           ccparams[i].in_sampling = sampling;
2802           ccparams[i].in_clamping = clamping;
2803           ccparams[i].in_subspace = subspace;
2804           ccparams[i].is_422 = is_422;
2805           ccparams[i].lut = gamma_lut;
2806           ccparams[i].thread_id = i;
2807 
2808           if (i == 0) convert_yuv420p_to_rgb_frame_thread(&ccparams[i]);
2809           else {
2810             lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_yuv420p_to_rgb_frame_thread, &ccparams[i]);
2811             nthreads++;
2812           }
2813         }
2814       }
2815 
2816       for (i = 1; i < nthreads; i++) {
2817         lives_thread_join(threads[i], NULL);
2818       }
2819       lives_free(ccparams);
2820       if (gamma_lut) lives_gamma_lut_free(gamma_lut);
2821       return;
2822     }
2823   }
2824 
2825   if (add_alpha) opsize = 4;
2826   width *= opsize;
2827 
2828   for (i = 0; i < height; i++) {
2829     if (!is_422) {
2830       if (!(i & 1)) even = TRUE;
2831       else even = FALSE;
2832     }
2833 
2834     uv_offs = 0;
2835 
2836     for (j = 0; j < width; j += opsize) {
2837       // process two pixels at a time, and we average the first colour pixel with the last from the previous 2
2838       // we know we can do this because Y must be even width
2839       y = *(s_y++);
2840       /// even row, normal
2841       if (j > 0) {
2842         /// center = 3 : 1, left = only next, right = avg(last, next)
2843         u = avg_chroma_3_1(next_u, last_u);
2844         v = avg_chroma_3_1(next_v, last_v);
2845         last_u = next_u;
2846         last_v = next_v;
2847       } else {
2848         if (even) {
2849           last_u = next_u = u = s_u[uv_offs];
2850           last_v = next_v = v = s_v[uv_offs];
2851         } else {
2852           if (is_bottom && i == height - 1) {
2853             next_u = u = s_u[uv_offs];
2854             next_v = v = s_v[uv_offs];
2855           } else {
2856             next_u = u = avg_chromaf(s_u[uv_offs], s_u[uv_offs + istrides[1]]);
2857             next_v = v = avg_chromaf(s_v[uv_offs], s_v[uv_offs + istrides[2]]);
2858           }
2859         }
2860       }
2861       if (gamma_lut)
2862         yuv2rgb_with_gamma(y, u, v, &dest[j], &dest[j + 1], &dest[j + 2], gamma_lut);
2863       else
2864         yuv2rgb(y, u, v, &dest[j], &dest[j + 1], &dest[j + 2]);
2865       if (add_alpha) dest[j + 3] = 255;
2866 
2867       // second RGB pixel
2868       j += opsize;
2869       y = *(s_y++);
2870 
2871       last_u = next_u;
2872       last_v = next_v;
2873 
2874       if (j < width - 1) {
2875         if (even) {
2876           /// even row, normal
2877           next_u = s_u[uv_offs];
2878           next_v = s_v[uv_offs];
2879         } else {
2880           if (is_bottom && i == height - 1) {
2881             next_u = u = s_u[uv_offs];
2882             next_v = v = s_v[uv_offs];
2883           } else {
2884             //g_print("vals %ld and %d %d %d\n", uv_offs, istrides[2], i, j);
2885             next_u = u = avg_chromaf(s_u[uv_offs], s_u[uv_offs + istrides[1]]);
2886             next_v = v = avg_chromaf(s_v[uv_offs], s_v[uv_offs + istrides[2]]);
2887           }
2888         }
2889         /// center = 3 : 1, left = avg(last, next), right = only next
2890         u = avg_chroma_3_1(next_u, last_u);
2891         v = avg_chroma_3_1(next_v, last_v);
2892       } else {
2893         u = last_u;
2894         v = last_v;
2895       }
2896       if (gamma_lut)
2897         yuv2rgb_with_gamma(y, u, v, &dest[j], &dest[j + 1], &dest[j + 2], gamma_lut);
2898       else
2899         yuv2rgb(y, u, v, &dest[j], &dest[j + 1], &dest[j + 2]);
2900       if (add_alpha) dest[j + 3] = 255;
2901       uv_offs++;
2902     }
2903     s_y += irow;
2904     dest += orowstride;
2905     if (is_422 || !even) {
2906       s_u += istrides[1];
2907       s_v += istrides[2];
2908     }
2909   }
2910 }
2911 
convert_yuv420p_to_rgb_frame_thread(void * data)2912 static void *convert_yuv420p_to_rgb_frame_thread(void *data) {
2913   lives_cc_params *ccparams = (lives_cc_params *)data;
2914   convert_yuv420p_to_rgb_frame((uint8_t **)ccparams->srcp, ccparams->hsize, ccparams->vsize,
2915                                ccparams->is_bottom, ccparams->irowstrides,
2916                                ccparams->orowstrides[0], (uint8_t *)ccparams->dest,
2917                                ccparams->out_alpha, ccparams->is_422, ccparams->in_sampling,
2918                                ccparams->in_clamping, ccparams->in_subspace, 0, 0,
2919                                ccparams->lut, ccparams->thread_id);
2920   return NULL;
2921 }
2922 
2923 
convert_yuv420p_to_bgr_frame(uint8_t ** src,int width,int height,boolean is_bottom,int * istrides,int orowstride,uint8_t * dest,boolean add_alpha,boolean is_422,int sampling,int clamping,int subspace,int gamma,int tgamma,uint8_t * gamma_lut,int thread_id)2924 static void convert_yuv420p_to_bgr_frame(uint8_t **src, int width, int height, boolean is_bottom, int *istrides, int orowstride,
2925     uint8_t *dest, boolean add_alpha, boolean is_422, int sampling, int clamping, int subspace,
2926     int gamma, int tgamma, uint8_t *gamma_lut, int thread_id) {
2927   int i, j;
2928   uint8_t *s_y = src[0], *s_u = src[1], *s_v = src[2];
2929   int opsize = 3;
2930   int irow = istrides[0] - width;
2931   boolean even = TRUE;
2932   uint8_t y, u, v, next_u, next_v, last_u, last_v;
2933   size_t uv_offs = 0;
2934 
2935   if (thread_id == -1) {
2936     if (LIVES_UNLIKELY(!conv_YR_inited)) init_YUV_to_RGB_tables();
2937 
2938     /// TODO: this is NOT threadsafe !!!!
2939     set_conversion_arrays(clamping, subspace);
2940 
2941     if (tgamma) gamma_lut = create_gamma_lut(1.0, gamma, tgamma);
2942     if (prefs->nfx_threads > 1) {
2943       lives_thread_t threads[prefs->nfx_threads];
2944       uint8_t *end = src[0] + height * istrides[0];
2945       int nthreads = 1;
2946       int dheight, xdheight;
2947       lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
2948 
2949       xdheight = CEIL((double)height / (double)prefs->nfx_threads, 4);
2950       for (i = prefs->nfx_threads - 1; i >= 0; i--) {
2951         dheight = xdheight;
2952 
2953         if ((src[0] + dheight * i * istrides[0]) < end) {
2954           ccparams[i].srcp[0] = src[0] + dheight * i * istrides[0];
2955           ccparams[i].srcp[1] = src[1] + dheight / 2 * i * istrides[1];
2956           ccparams[i].srcp[2] = src[2] + dheight / 2 * i * istrides[2];
2957           ccparams[i].hsize = width;
2958           ccparams[i].dest = dest + dheight * i * orowstride;
2959 
2960           if (dheight * (i + 1) > (height - 4)) {
2961             dheight = height - (dheight * i);
2962           }
2963 
2964           ccparams[i].vsize = dheight;
2965           if (i == prefs->nfx_threads - 1) ccparams->is_bottom = TRUE;
2966 
2967           ccparams[i].irowstrides[0] = istrides[0];
2968           ccparams[i].irowstrides[1] = istrides[1];
2969           ccparams[i].irowstrides[2] = istrides[2];
2970           ccparams[i].orowstrides[0] = orowstride;
2971           ccparams[i].out_alpha = add_alpha;
2972           ccparams[i].in_sampling = sampling;
2973           ccparams[i].in_clamping = clamping;
2974           ccparams[i].in_subspace = subspace;
2975           ccparams[i].is_422 = is_422;
2976           ccparams[i].lut = gamma_lut;
2977           ccparams[i].thread_id = i;
2978 
2979           if (i == 0) convert_yuv420p_to_bgr_frame_thread(&ccparams[i]);
2980           else {
2981             lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_yuv420p_to_bgr_frame_thread, &ccparams[i]);
2982             nthreads++;
2983           }
2984         }
2985       }
2986 
2987       for (i = 1; i < nthreads; i++) {
2988         lives_thread_join(threads[i], NULL);
2989       }
2990       lives_free(ccparams);
2991       if (gamma_lut) lives_gamma_lut_free(gamma_lut);
2992       return;
2993     }
2994   }
2995 
2996   if (add_alpha) opsize = 4;
2997   width *= opsize;
2998 
2999   for (i = 0; i < height; i++) {
3000     if (!is_422) {
3001       if (!(i & 1)) even = TRUE;
3002       else even = FALSE;
3003     }
3004 
3005     uv_offs = 0;
3006 
3007     for (j = 0; j < width; j += opsize) {
3008       // process two pixels at a time, and we average the first colour pixel with the last from the previous 2
3009       // we know we can do this because Y must be even width
3010       y = *(s_y++);
3011       /// even row, normal
3012       if (j > 0) {
3013         /// center = 3 : 1, left = only next, right = avg(last, next)
3014         u = avg_chroma_3_1(next_u, last_u);
3015         v = avg_chroma_3_1(next_v, last_v);
3016         last_u = next_u;
3017         last_v = next_v;
3018       } else {
3019         if (even) {
3020           next_u = u = s_u[uv_offs];
3021           next_v = v = s_v[uv_offs];
3022         } else {
3023           if (i == height - 1 && is_bottom) {
3024             next_u = u = s_u[uv_offs];
3025             next_v = v = s_v[uv_offs];
3026           } else {
3027             next_u = u = avg_chromaf(s_u[uv_offs], s_u[uv_offs + istrides[1]]);
3028             next_v = v = avg_chromaf(s_v[uv_offs], s_v[uv_offs + istrides[2]]);
3029           }
3030         }
3031       }
3032       if (gamma_lut)
3033         yuv2bgr_with_gamma(y, u, v, &dest[j], &dest[j + 1], &dest[j + 2], gamma_lut);
3034       else
3035         yuv2bgr(y, u, v, &dest[j], &dest[j + 1], &dest[j + 2]);
3036       if (add_alpha) dest[j + 3] = 255;
3037 
3038       // second RGB pixel
3039       j += opsize;
3040       y = *(s_y++);
3041       last_u = next_u;
3042       last_v = next_v;
3043 
3044       if (j < width - 1) {
3045         if (even) {
3046           /// even row, normal
3047           next_u = s_u[uv_offs];
3048           next_v = s_v[uv_offs];
3049         } else {
3050           if (i == height - 1 && is_bottom) {
3051             next_u = u = s_u[uv_offs];
3052             next_v = v = s_v[uv_offs];
3053           } else {
3054             next_u = u = avg_chromaf(s_u[uv_offs], s_u[uv_offs + istrides[1]]);
3055             next_v = v = avg_chromaf(s_v[uv_offs], s_v[uv_offs + istrides[2]]);
3056           }
3057         }
3058         /// center = 3 : 1, left = avg(last, next), right = only next
3059         u = avg_chroma_3_1(next_u, last_u);
3060         v = avg_chroma_3_1(next_v, last_v);
3061       } else {
3062         u = last_u;
3063         v = last_v;
3064       }
3065       if (gamma_lut)
3066         yuv2bgr_with_gamma(y, u, v, &dest[j], &dest[j + 1], &dest[j + 2], gamma_lut);
3067       else
3068         yuv2bgr(y, u, v, &dest[j], &dest[j + 1], &dest[j + 2]);
3069       if (add_alpha) dest[j + 3] = 255;
3070       uv_offs++;
3071     }
3072     s_y += irow;
3073     dest += orowstride;
3074     if (is_422 || !even) {
3075       s_u += istrides[1];
3076       s_v += istrides[2];
3077     }
3078   }
3079 }
3080 
convert_yuv420p_to_bgr_frame_thread(void * data)3081 static void *convert_yuv420p_to_bgr_frame_thread(void *data) {
3082   lives_cc_params *ccparams = (lives_cc_params *)data;
3083   convert_yuv420p_to_bgr_frame((uint8_t **)ccparams->srcp, ccparams->hsize, ccparams->vsize,
3084                                ccparams->is_bottom, ccparams->irowstrides,
3085                                ccparams->orowstrides[0], (uint8_t *)ccparams->dest,
3086                                ccparams->out_alpha, ccparams->is_422, ccparams->in_sampling,
3087                                ccparams->in_clamping, ccparams->in_subspace, 0, 0,
3088                                ccparams->lut, ccparams->thread_id);
3089   return NULL;
3090 }
3091 
3092 
convert_yuv420p_to_argb_frame(uint8_t ** src,int width,int height,boolean is_bottom,int * istrides,int orowstride,uint8_t * dest,boolean is_422,int sampling,int clamping,int subspace,int gamma,int tgamma,uint8_t * gamma_lut,int thread_id)3093 static void convert_yuv420p_to_argb_frame(uint8_t **src, int width, int height, boolean is_bottom, int *istrides,
3094     int orowstride,
3095     uint8_t *dest, boolean is_422, int sampling, int clamping, int subspace,
3096     int gamma, int tgamma, uint8_t *gamma_lut, int thread_id) {
3097   int i, j;
3098   uint8_t *s_y = src[0], *s_u = src[1], *s_v = src[2];
3099   int opsize = 4;
3100   int irow = istrides[0] - width;
3101   boolean even = TRUE;
3102   uint8_t y, u, v, next_u = 0, next_v = 0, last_u = 0, last_v = 0;
3103   size_t uv_offs = 0;
3104 
3105   if (thread_id == -1) {
3106     if (LIVES_UNLIKELY(!conv_YR_inited)) init_YUV_to_RGB_tables();
3107 
3108     /// TODO: this is NOT threadsafe !!!!
3109     set_conversion_arrays(clamping, subspace);
3110 
3111     if (tgamma) gamma_lut = create_gamma_lut(1.0, gamma, tgamma);
3112     if (prefs->nfx_threads > 1) {
3113       lives_thread_t threads[prefs->nfx_threads];
3114       uint8_t *end = src[0] + height * istrides[0];
3115       int nthreads = 1;
3116       int dheight, xdheight;
3117       lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
3118 
3119       xdheight = CEIL((double)height / (double)prefs->nfx_threads, 4);
3120       for (i = prefs->nfx_threads - 1; i >= 0; i--) {
3121         dheight = xdheight;
3122 
3123         if ((src[0] + dheight * i * istrides[0]) < end) {
3124           ccparams[i].srcp[0] = src[0] + dheight * i * istrides[0];
3125           ccparams[i].srcp[1] = src[1] + dheight / 2 * i * istrides[1];
3126           ccparams[i].srcp[2] = src[2] + dheight / 2 * i * istrides[2];
3127           ccparams[i].hsize = width;
3128           ccparams[i].dest = dest + dheight * i * orowstride;
3129 
3130           if (dheight * (i + 1) > (height - 4)) {
3131             dheight = height - (dheight * i);
3132           }
3133 
3134           ccparams[i].vsize = dheight;
3135           if (i == prefs->nfx_threads - 1) ccparams->is_bottom = TRUE;
3136 
3137           ccparams[i].irowstrides[0] = istrides[0];
3138           ccparams[i].irowstrides[1] = istrides[1];
3139           ccparams[i].irowstrides[2] = istrides[2];
3140           ccparams[i].orowstrides[0] = orowstride;
3141           ccparams[i].in_sampling = sampling;
3142           ccparams[i].in_clamping = clamping;
3143           ccparams[i].in_subspace = subspace;
3144           ccparams[i].is_422 = is_422;
3145           ccparams[i].lut = gamma_lut;
3146           ccparams[i].thread_id = i;
3147 
3148           if (i == 0) convert_yuv420p_to_argb_frame_thread(&ccparams[i]);
3149           else {
3150             lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_yuv420p_to_argb_frame_thread, &ccparams[i]);
3151             nthreads++;
3152           }
3153         }
3154       }
3155 
3156       for (i = 1; i < nthreads; i++) {
3157         lives_thread_join(threads[i], NULL);
3158       }
3159       lives_free(ccparams);
3160       if (gamma_lut) lives_gamma_lut_free(gamma_lut);
3161       return;
3162     }
3163   }
3164 
3165   width *= opsize;
3166 
3167   for (i = 0; i < height; i++) {
3168     if (!is_422) {
3169       if (!(i & 1)) even = TRUE;
3170       else even = FALSE;
3171     }
3172 
3173     uv_offs = 0;
3174 
3175     for (j = 0; j < width; j += opsize) {
3176       // process two pixels at a time, and we average the first colour pixel with the last from the previous 2
3177       // we know we can do this because Y must be even width
3178       dest[j] = 255;
3179       y = *(s_y++);
3180       /// even row, normal
3181       if (j > 0) {
3182         /// center = 3 : 1, left = only next, right = avg(last, next)
3183         u = avg_chroma_3_1(next_u, last_u);
3184         v = avg_chroma_3_1(next_v, last_v);
3185         last_u = next_u;
3186         last_v = next_v;
3187       } else {
3188         if (even) {
3189           next_u = u = s_u[uv_offs];
3190           next_v = v = s_v[uv_offs];
3191         } else {
3192           if (i == height - 1 && is_bottom) {
3193             next_u = u = s_u[uv_offs];
3194             next_v = v = s_v[uv_offs];
3195           } else {
3196             next_u = u = avg_chromaf(s_u[uv_offs], s_u[uv_offs + istrides[1]]);
3197             next_v = v = avg_chromaf(s_v[uv_offs], s_v[uv_offs + istrides[2]]);
3198           }
3199         }
3200       }
3201       if (gamma_lut)
3202         yuv2rgb_with_gamma(y, u, v, &dest[j + 1], &dest[j + 2], &dest[j + 3], gamma_lut);
3203       else
3204         yuv2rgb(y, u, v, &dest[j + 1], &dest[j + 2], &dest[j + 3]);
3205 
3206       // second RGB pixel
3207       j += opsize;
3208       y = *(s_y++);
3209       last_u = next_u;
3210       last_v = next_v;
3211       dest[j] = 255;
3212 
3213       if (j < width - 1) {
3214         if (even) {
3215           /// even row, normal
3216           next_u = s_u[uv_offs];
3217           next_v = s_v[uv_offs];
3218         } else {
3219           if (i == height - 1 && is_bottom) {
3220             next_u = u = s_u[uv_offs];
3221             next_v = v = s_v[uv_offs];
3222           } else {
3223             next_u = u = avg_chromaf(s_u[uv_offs], s_u[uv_offs + istrides[1]]);
3224             next_v = v = avg_chromaf(s_v[uv_offs], s_v[uv_offs + istrides[2]]);
3225           }
3226         }
3227         /// center = 3 : 1, left = avg(last, next), right = only next
3228         u = avg_chroma_3_1(next_u, last_u);
3229         v = avg_chroma_3_1(next_v, last_v);
3230       } else {
3231         u = last_u;
3232         v = last_v;
3233       }
3234       if (gamma_lut)
3235         yuv2rgb_with_gamma(y, u, v, &dest[j + 1], &dest[j + 2], &dest[j + 3], gamma_lut);
3236       else
3237         yuv2rgb(y, u, v, &dest[j + 1], &dest[j + 2], &dest[j + 3]);
3238       uv_offs++;
3239     }
3240     s_y += irow;
3241     dest += orowstride;
3242     if (is_422 || !even) {
3243       s_u += istrides[1];
3244       s_v += istrides[2];
3245     }
3246   }
3247 }
3248 
convert_yuv420p_to_argb_frame_thread(void * data)3249 static void *convert_yuv420p_to_argb_frame_thread(void *data) {
3250   lives_cc_params *ccparams = (lives_cc_params *)data;
3251   convert_yuv420p_to_argb_frame((uint8_t **)ccparams->srcp, ccparams->hsize, ccparams->vsize,
3252                                 ccparams->is_bottom, ccparams->irowstrides,
3253                                 ccparams->orowstrides[0], (uint8_t *)ccparams->dest,
3254                                 ccparams->is_422, ccparams->in_sampling,
3255                                 ccparams->in_clamping, ccparams->in_subspace, 0, 0,
3256                                 ccparams->lut, ccparams->thread_id);
3257   return NULL;
3258 }
3259 
3260 
convert_rgb_to_uyvy_frame(uint8_t * rgbdata,int hsize,int vsize,int rowstride,int orowstride,uyvy_macropixel * u,boolean has_alpha,int clamping,uint8_t * gamma_lut,int thread_id)3261 static void convert_rgb_to_uyvy_frame(uint8_t *rgbdata, int hsize, int vsize, int rowstride, int orowstride,
3262                                       uyvy_macropixel *u, boolean has_alpha, int clamping, uint8_t *gamma_lut, int thread_id) {
3263   // for odd sized widths, cut the rightmost pixel
3264   int hs3, ipsize = 3, ipsize2;
3265   uint8_t *end;
3266   int i;
3267 
3268   int x = 3, y = 4, z = 5;
3269   hsize = (hsize >> 1) << 1;
3270 
3271   if (thread_id == -1) {
3272     if (LIVES_UNLIKELY(!conv_RY_inited)) init_RGB_to_YUV_tables();
3273     set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
3274   }
3275 
3276   end = rgbdata + rowstride * vsize;
3277 
3278   if (thread_id == -1 && prefs->nfx_threads > 1) {
3279     lives_thread_t threads[prefs->nfx_threads];
3280     int nthreads = 1;
3281     int dheight, xdheight;
3282     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
3283 
3284     xdheight = CEIL((double)vsize / (double)prefs->nfx_threads, 4);
3285     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
3286       dheight = xdheight;
3287 
3288       if ((rgbdata + dheight * i * rowstride) < end) {
3289         ccparams[i].src = rgbdata + dheight * i * rowstride;
3290         ccparams[i].hsize = hsize;
3291         ccparams[i].dest = u + dheight * i * orowstride / 4;
3292 
3293         if (dheight * (i + 1) > (vsize - 4)) {
3294           dheight = vsize - (dheight * i);
3295         }
3296 
3297         ccparams[i].vsize = dheight;
3298 
3299         ccparams[i].irowstrides[0] = rowstride;
3300         ccparams[i].orowstrides[0] = orowstride;
3301         ccparams[i].in_alpha = has_alpha;
3302         ccparams[i].out_clamping = clamping;
3303         ccparams[i].lut = gamma_lut;
3304         ccparams[i].thread_id = i;
3305 
3306         if (i == 0) convert_rgb_to_uyvy_frame_thread(&ccparams[i]);
3307         else {
3308           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_rgb_to_uyvy_frame_thread, &ccparams[i]);
3309           nthreads++;
3310         }
3311       }
3312     }
3313 
3314     for (i = 1; i < nthreads; i++) {
3315       lives_thread_join(threads[i], NULL);
3316     }
3317     lives_free(ccparams);
3318     if (gamma_lut) lives_gamma_lut_free(gamma_lut);
3319     return;
3320   }
3321 
3322   if (has_alpha) {
3323     z++;
3324     y++;
3325     x++;
3326     ipsize = 4;
3327   }
3328 
3329   ipsize2 = ipsize * 2;
3330   hs3 = hsize * ipsize;
3331   orowstride = orowstride / 2 - hsize;
3332   for (int k = 0; k < vsize; k++) {
3333     for (i = 0; i < hs3; i += ipsize2) {
3334       // convert 6 RGBRGB bytes to 4 UYVY bytes
3335       if (gamma_lut)
3336         rgb2uyvy_with_gamma(rgbdata[i], rgbdata[i + 1], rgbdata[i + 2], rgbdata[i + x], rgbdata[i + y],
3337                             rgbdata[i + z], u++, gamma_lut);
3338       else
3339         rgb2uyvy(rgbdata[i], rgbdata[i + 1], rgbdata[i + 2], rgbdata[i + x], rgbdata[i + y], rgbdata[i + z], u++);
3340     }
3341     rgbdata += rowstride;
3342     u += orowstride;
3343   }
3344 }
3345 
3346 
convert_rgb_to_uyvy_frame_thread(void * data)3347 static void *convert_rgb_to_uyvy_frame_thread(void *data) {
3348   lives_cc_params *ccparams = (lives_cc_params *)data;
3349   convert_rgb_to_uyvy_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
3350                             ccparams->orowstrides[0],
3351                             (uyvy_macropixel *)ccparams->dest, ccparams->in_alpha, ccparams->out_clamping, ccparams->lut,
3352                             ccparams->thread_id);
3353   return NULL;
3354 }
3355 
3356 
convert_rgb_to_yuyv_frame(uint8_t * rgbdata,int hsize,int vsize,int rowstride,int orowstride,yuyv_macropixel * u,boolean has_alpha,int clamping,uint8_t * gamma_lut,int thread_id)3357 static void convert_rgb_to_yuyv_frame(uint8_t *rgbdata, int hsize, int vsize, int rowstride, int orowstride,
3358                                       yuyv_macropixel *u, boolean has_alpha, int clamping, uint8_t *gamma_lut, int thread_id) {
3359   // for odd sized widths, cut the rightmost pixel
3360   int hs3, ipsize = 3, ipsize2;
3361   uint8_t *end = rgbdata + rowstride * vsize;
3362   int i;
3363 
3364   int x = 3, y = 4, z = 5;
3365   hsize = (hsize >> 1) << 1;
3366 
3367   if (LIVES_UNLIKELY(!conv_RY_inited)) init_RGB_to_YUV_tables();
3368   if (thread_id == -1)
3369     set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
3370 
3371   if (thread_id == -1 && prefs->nfx_threads > 1) {
3372     lives_thread_t threads[prefs->nfx_threads];
3373     int nthreads = 1;
3374     int dheight, xdheight;
3375     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
3376 
3377     xdheight = CEIL((double)vsize / (double)prefs->nfx_threads, 4);
3378     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
3379       dheight = xdheight;
3380 
3381       if ((rgbdata + dheight * i * rowstride) < end) {
3382         ccparams[i].src = rgbdata + dheight * i * rowstride;
3383         ccparams[i].hsize = hsize;
3384         ccparams[i].dest = u + dheight * i * orowstride / 4;
3385 
3386         if (dheight * (i + 1) > (vsize - 4)) {
3387           dheight = vsize - (dheight * i);
3388         }
3389 
3390         ccparams[i].vsize = dheight;
3391 
3392         ccparams[i].irowstrides[0] = rowstride;
3393         ccparams[i].orowstrides[0] = orowstride;
3394         ccparams[i].in_alpha = has_alpha;
3395         ccparams[i].out_clamping = clamping;
3396         ccparams[i].lut = gamma_lut;
3397         ccparams[i].thread_id = i;
3398 
3399         if (i == 0) convert_rgb_to_yuyv_frame_thread(&ccparams[i]);
3400         else {
3401           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_rgb_to_yuyv_frame_thread, &ccparams[i]);
3402           nthreads++;
3403         }
3404       }
3405     }
3406 
3407     for (i = 1; i < nthreads; i++) {
3408       lives_thread_join(threads[i], NULL);
3409     }
3410     lives_free(ccparams);
3411     if (gamma_lut) lives_gamma_lut_free(gamma_lut);
3412     return;
3413   }
3414 
3415   if (has_alpha) {
3416     z++;
3417     y++;
3418     x++;
3419     ipsize = 4;
3420   }
3421 
3422   ipsize2 = ipsize * 2;
3423   hs3 = hsize * ipsize;
3424   orowstride = orowstride / 2 - hsize;
3425 
3426   for (; rgbdata < end; rgbdata += rowstride) {
3427     for (i = 0; i < hs3; i += ipsize2) {
3428       // convert 6 RGBRGB bytes to 4 YUYV bytes
3429       if (gamma_lut)
3430         rgb2yuyv_with_gamma(rgbdata[i], rgbdata[i + 1], rgbdata[i + 2], rgbdata[i + x], rgbdata[i + y],
3431                             rgbdata[i + z], u++, gamma_lut);
3432       else
3433         rgb2yuyv(rgbdata[i], rgbdata[i + 1], rgbdata[i + 2], rgbdata[i + x], rgbdata[i + y], rgbdata[i + z], u++);
3434     }
3435     u += orowstride;
3436   }
3437 }
3438 
3439 
convert_rgb_to_yuyv_frame_thread(void * data)3440 static void *convert_rgb_to_yuyv_frame_thread(void *data) {
3441   lives_cc_params *ccparams = (lives_cc_params *)data;
3442   convert_rgb_to_yuyv_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
3443                             ccparams->orowstrides[0],
3444                             (yuyv_macropixel *)ccparams->dest, ccparams->in_alpha, ccparams->out_clamping, ccparams->lut, ccparams->thread_id);
3445   return NULL;
3446 }
3447 
3448 
convert_bgr_to_uyvy_frame(uint8_t * rgbdata,int hsize,int vsize,int rowstride,int orowstride,uyvy_macropixel * u,boolean has_alpha,int clamping,uint8_t * gamma_lut,int thread_id)3449 static void convert_bgr_to_uyvy_frame(uint8_t *rgbdata, int hsize, int vsize, int rowstride, int orowstride,
3450                                       uyvy_macropixel *u, boolean has_alpha, int clamping, uint8_t *gamma_lut, int thread_id) {
3451   // for odd sized widths, cut the rightmost pixel
3452   int hs3, ipsize = 3, ipsize2;
3453   uint8_t *end = rgbdata + rowstride * vsize;
3454   int i;
3455 
3456   int x = 3, y = 4, z = 5;
3457   hsize = (hsize >> 1) << 1;
3458 
3459   if (LIVES_UNLIKELY(!conv_RY_inited)) init_RGB_to_YUV_tables();
3460   if (thread_id == -1)
3461     set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
3462 
3463   if (thread_id == -1 && prefs->nfx_threads > 1) {
3464     lives_thread_t threads[prefs->nfx_threads];
3465     int nthreads = 1;
3466     int dheight, xdheight;
3467     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
3468 
3469     xdheight = CEIL((double)vsize / (double)prefs->nfx_threads, 4);
3470     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
3471       dheight = xdheight;
3472 
3473       if ((rgbdata + dheight * i * rowstride) < end) {
3474         ccparams[i].src = rgbdata + dheight * i * rowstride;
3475         ccparams[i].hsize = hsize;
3476         ccparams[i].dest = u + dheight * i * orowstride / 4;
3477 
3478         if (dheight * (i + 1) > (vsize - 4)) {
3479           dheight = vsize - (dheight * i);
3480         }
3481 
3482         ccparams[i].vsize = dheight;
3483 
3484         ccparams[i].irowstrides[0] = rowstride;
3485         ccparams[i].orowstrides[0] = orowstride;
3486         ccparams[i].in_alpha = has_alpha;
3487         ccparams[i].out_clamping = clamping;
3488         ccparams[i].lut = gamma_lut;
3489         ccparams[i].thread_id = i;
3490 
3491         if (i == 0) convert_bgr_to_uyvy_frame_thread(&ccparams[i]);
3492         else {
3493           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_bgr_to_uyvy_frame_thread, &ccparams[i]);
3494           nthreads++;
3495         }
3496       }
3497     }
3498 
3499     for (i = 1; i < nthreads; i++) {
3500       lives_thread_join(threads[i], NULL);
3501     }
3502     lives_free(ccparams);
3503     if (gamma_lut) lives_gamma_lut_free(gamma_lut);
3504     return;
3505   }
3506 
3507   if (has_alpha) {
3508     z++;
3509     y++;
3510     x++;
3511     ipsize = 4;
3512   }
3513 
3514   ipsize2 = ipsize * 2;
3515   hs3 = hsize * ipsize;
3516   orowstride = orowstride / 2 - hsize;
3517 
3518   for (; rgbdata < end; rgbdata += rowstride) {
3519     for (i = 0; i < hs3; i += ipsize2) {
3520       //convert 6 RGBRGB bytes to 4 UYVY bytes
3521       if (gamma_lut)
3522         rgb2uyvy_with_gamma(rgbdata[i + 2], rgbdata[i + 1], rgbdata[i], rgbdata[i + z], rgbdata[i + y],
3523                             rgbdata[i + x], u++, gamma_lut);
3524       else
3525         rgb2uyvy(rgbdata[i + 2], rgbdata[i + 1], rgbdata[i], rgbdata[i + z], rgbdata[i + y], rgbdata[i + x], u++);
3526     }
3527     u += orowstride;
3528   }
3529 }
3530 
3531 
convert_bgr_to_uyvy_frame_thread(void * data)3532 static void *convert_bgr_to_uyvy_frame_thread(void *data) {
3533   lives_cc_params *ccparams = (lives_cc_params *)data;
3534   convert_bgr_to_uyvy_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
3535                             ccparams->orowstrides[0],
3536                             (uyvy_macropixel *)ccparams->dest, ccparams->in_alpha, ccparams->out_clamping, ccparams->lut, ccparams->thread_id);
3537   return NULL;
3538 }
3539 
3540 
convert_bgr_to_yuyv_frame(uint8_t * rgbdata,int hsize,int vsize,int rowstride,int orowstride,yuyv_macropixel * u,boolean has_alpha,int clamping,uint8_t * gamma_lut,int thread_id)3541 static void convert_bgr_to_yuyv_frame(uint8_t *rgbdata, int hsize, int vsize, int rowstride, int orowstride,
3542                                       yuyv_macropixel *u, boolean has_alpha, int clamping, uint8_t *gamma_lut, int thread_id) {
3543   // for odd sized widths, cut the rightmost pixel
3544   int hs3, ipsize = 3, ipsize2;
3545 
3546   uint8_t *end = rgbdata + rowstride * vsize;
3547   int i;
3548 
3549   int x = 3, y = 4, z = 5;
3550 
3551   hsize = (hsize >> 1) << 1;
3552 
3553   if (LIVES_UNLIKELY(!conv_RY_inited)) init_RGB_to_YUV_tables();
3554   if (thread_id == -1)
3555     set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
3556 
3557   if (thread_id == -1 && prefs->nfx_threads > 1) {
3558     lives_thread_t threads[prefs->nfx_threads];
3559     int nthreads = 1;
3560     int dheight, xdheight;
3561     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
3562 
3563     xdheight = CEIL((double)vsize / (double)prefs->nfx_threads, 4);
3564     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
3565       dheight = xdheight;
3566 
3567       if ((rgbdata + dheight * i * rowstride) < end) {
3568         ccparams[i].src = rgbdata + dheight * i * rowstride;
3569         ccparams[i].hsize = hsize;
3570         ccparams[i].dest = u + dheight * i * orowstride / 4;
3571 
3572         if (dheight * (i + 1) > (vsize - 4)) {
3573           dheight = vsize - (dheight * i);
3574         }
3575 
3576         ccparams[i].vsize = dheight;
3577 
3578         ccparams[i].irowstrides[0] = rowstride;
3579         ccparams[i].orowstrides[0] = orowstride;
3580         ccparams[i].in_alpha = has_alpha;
3581         ccparams[i].out_clamping = clamping;
3582         ccparams[i].lut = gamma_lut;
3583         ccparams[i].thread_id = i;
3584 
3585         if (i == 0) convert_bgr_to_yuyv_frame_thread(&ccparams[i]);
3586         else {
3587           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_bgr_to_yuyv_frame_thread, &ccparams[i]);
3588           nthreads++;
3589         }
3590       }
3591     }
3592 
3593     for (i = 1; i < nthreads; i++) {
3594       lives_thread_join(threads[i], NULL);
3595     }
3596     lives_free(ccparams);
3597     if (gamma_lut) lives_gamma_lut_free(gamma_lut);
3598     return;
3599   }
3600 
3601   if (has_alpha) {
3602     z++;
3603     y++;
3604     x++;
3605     ipsize = 4;
3606   }
3607 
3608   ipsize2 = ipsize * 2;
3609   hs3 = hsize * ipsize;
3610   orowstride = orowstride / 2 - hsize;
3611 
3612   for (; rgbdata < end; rgbdata += rowstride) {
3613     for (i = 0; i < hs3; i += ipsize2) {
3614       // convert 6 RGBRGB bytes to 4 UYVY bytes
3615       if (gamma_lut)
3616         rgb2yuyv_with_gamma(rgbdata[i + 2], rgbdata[i + 1], rgbdata[i], rgbdata[i + z], rgbdata[i + y],
3617                             rgbdata[i + x], u++, gamma_lut);
3618       else
3619         rgb2yuyv(rgbdata[i + 2], rgbdata[i + 1], rgbdata[i], rgbdata[i + z], rgbdata[i + y], rgbdata[i + x], u++);
3620     }
3621     u += orowstride;
3622   }
3623 }
3624 
3625 
convert_bgr_to_yuyv_frame_thread(void * data)3626 static void *convert_bgr_to_yuyv_frame_thread(void *data) {
3627   lives_cc_params *ccparams = (lives_cc_params *)data;
3628   convert_bgr_to_yuyv_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
3629                             ccparams->orowstrides[0],
3630                             (yuyv_macropixel *)ccparams->dest, ccparams->in_alpha, ccparams->out_clamping, ccparams->lut, ccparams->thread_id);
3631   return NULL;
3632 }
3633 
3634 
convert_argb_to_uyvy_frame(uint8_t * rgbdata,int hsize,int vsize,int rowstride,int orowstride,uyvy_macropixel * u,int clamping,uint8_t * gamma_lut,int thread_id)3635 static void convert_argb_to_uyvy_frame(uint8_t *rgbdata, int hsize, int vsize, int rowstride, int orowstride,
3636                                        uyvy_macropixel *u, int clamping, uint8_t *gamma_lut, int thread_id) {
3637   // for odd sized widths, cut the rightmost pixel
3638   int hs3, ipsize = 4, ipsize2;
3639   uint8_t *end;
3640   int i;
3641 
3642   if (LIVES_UNLIKELY(!conv_RY_inited)) init_RGB_to_YUV_tables();
3643   if (thread_id == -1)
3644     set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
3645 
3646   end = rgbdata + rowstride * vsize;
3647   hsize = (hsize >> 1) << 1;
3648 
3649   if (thread_id == -1 && prefs->nfx_threads > 1) {
3650     lives_thread_t threads[prefs->nfx_threads];
3651     int nthreads = 1;
3652     int dheight, xdheight;
3653     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
3654 
3655     xdheight = CEIL((double)vsize / (double)prefs->nfx_threads, 4);
3656     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
3657       dheight = xdheight;
3658 
3659       if ((rgbdata + dheight * i * rowstride) < end) {
3660         ccparams[i].src = rgbdata + dheight * i * rowstride;
3661         ccparams[i].hsize = hsize;
3662         ccparams[i].dest = u + dheight * i * orowstride / 4;
3663 
3664         if (dheight * (i + 1) > (vsize - 4)) {
3665           dheight = vsize - (dheight * i);
3666         }
3667 
3668         ccparams[i].vsize = dheight;
3669 
3670         ccparams[i].irowstrides[0] = rowstride;
3671         ccparams[i].orowstrides[0] = orowstride;
3672         ccparams[i].out_clamping = clamping;
3673         ccparams[i].lut = gamma_lut;
3674         ccparams[i].thread_id = i;
3675 
3676         if (i == 0) convert_argb_to_uyvy_frame_thread(&ccparams[i]);
3677         else {
3678           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_argb_to_uyvy_frame_thread, &ccparams[i]);
3679           nthreads++;
3680         }
3681       }
3682     }
3683 
3684     for (i = 1; i < nthreads; i++) {
3685       lives_thread_join(threads[i], NULL);
3686     }
3687     lives_free(ccparams);
3688     if (gamma_lut) lives_gamma_lut_free(gamma_lut);
3689     return;
3690   }
3691 
3692   ipsize2 = ipsize * 2;
3693   hs3 = hsize * ipsize;
3694   orowstride = orowstride / 2 - hsize;
3695 
3696   for (; rgbdata < end; rgbdata += rowstride) {
3697     for (i = 0; i < hs3; i += ipsize2) {
3698       // convert 6 RGBRGB bytes to 4 UYVY bytes
3699       if (gamma_lut)
3700         rgb2uyvy_with_gamma(rgbdata[i + 1], rgbdata[i + 2], rgbdata[i + 3], rgbdata[i + 5], rgbdata[i + 6],
3701                             rgbdata[i + 7], u++, gamma_lut);
3702       else
3703         rgb2uyvy(rgbdata[i + 1], rgbdata[i + 2], rgbdata[i + 3], rgbdata[i + 5], rgbdata[i + 6], rgbdata[i + 7], u++);
3704     }
3705     u += orowstride;
3706   }
3707 }
3708 
3709 
convert_argb_to_uyvy_frame_thread(void * data)3710 static void *convert_argb_to_uyvy_frame_thread(void *data) {
3711   lives_cc_params *ccparams = (lives_cc_params *)data;
3712   convert_argb_to_uyvy_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
3713                              ccparams->orowstrides[0],
3714                              (uyvy_macropixel *)ccparams->dest, ccparams->out_clamping, ccparams->lut, ccparams->thread_id);
3715   return NULL;
3716 }
3717 
3718 
convert_argb_to_yuyv_frame(uint8_t * rgbdata,int hsize,int vsize,int rowstride,int orowstride,yuyv_macropixel * u,int clamping,uint8_t * gamma_lut,int thread_id)3719 static void convert_argb_to_yuyv_frame(uint8_t *rgbdata, int hsize, int vsize, int rowstride, int orowstride,
3720                                        yuyv_macropixel *u, int clamping, uint8_t *gamma_lut, int thread_id) {
3721   // for odd sized widths, cut the rightmost pixel
3722   int hs3, ipsize = 4, ipsize2;
3723   uint8_t *end;
3724   register int i;
3725 
3726   if (LIVES_UNLIKELY(!conv_RY_inited)) init_RGB_to_YUV_tables();
3727   if (thread_id == -1)
3728     set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
3729 
3730   end = rgbdata + rowstride * vsize;
3731   hsize = (hsize >> 1) << 1;
3732 
3733   if (thread_id == -1 && prefs->nfx_threads > 1) {
3734     lives_thread_t threads[prefs->nfx_threads];
3735     int nthreads = 1;
3736     int dheight, xdheight;
3737     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
3738 
3739     xdheight = CEIL((double)vsize / (double)prefs->nfx_threads, 4);
3740     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
3741       dheight = xdheight;
3742 
3743       if ((rgbdata + dheight * i * rowstride) < end) {
3744         ccparams[i].src = rgbdata + dheight * i * rowstride;
3745         ccparams[i].hsize = hsize;
3746         ccparams[i].dest = u + dheight * i * orowstride / 4;
3747 
3748         if (dheight * (i + 1) > (vsize - 4)) {
3749           dheight = vsize - (dheight * i);
3750         }
3751 
3752         ccparams[i].vsize = dheight;
3753 
3754         ccparams[i].irowstrides[0] = rowstride;
3755         ccparams[i].orowstrides[0] = orowstride;
3756         ccparams[i].out_clamping = clamping;
3757         ccparams[i].lut = gamma_lut;
3758         ccparams[i].thread_id = i;
3759 
3760         if (i == 0) convert_argb_to_yuyv_frame_thread(&ccparams[i]);
3761         else {
3762           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_argb_to_yuyv_frame_thread, &ccparams[i]);
3763           nthreads++;
3764         }
3765       }
3766     }
3767 
3768     for (i = 1; i < nthreads; i++) {
3769       lives_thread_join(threads[i], NULL);
3770     }
3771     lives_free(ccparams);
3772     if (gamma_lut) lives_gamma_lut_free(gamma_lut);
3773     return;
3774   }
3775 
3776   ipsize2 = ipsize * 2;
3777   hs3 = hsize * ipsize;
3778   orowstride = orowstride / 2 - hsize;
3779   for (; rgbdata < end; rgbdata += rowstride) {
3780     for (i = 0; i < hs3; i += ipsize2) {
3781       // convert 6 RGBRGB bytes to 4 UYVY bytes
3782       if (gamma_lut)
3783         rgb2yuyv_with_gamma(rgbdata[i + 1], rgbdata[i + 2], rgbdata[i + 3], rgbdata[i + 5], rgbdata[i + 6],
3784                             rgbdata[i + 7], u++, gamma_lut);
3785       else
3786         rgb2yuyv(rgbdata[i + 1], rgbdata[i + 2], rgbdata[i + 3], rgbdata[i + 5], rgbdata[i + 6], rgbdata[i + 7], u++);
3787     }
3788     u += orowstride;
3789   }
3790 }
3791 
3792 
convert_argb_to_yuyv_frame_thread(void * data)3793 static void *convert_argb_to_yuyv_frame_thread(void *data) {
3794   lives_cc_params *ccparams = (lives_cc_params *)data;
3795   convert_argb_to_yuyv_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
3796                              ccparams->orowstrides[0],
3797                              (yuyv_macropixel *)ccparams->dest, ccparams->out_clamping, ccparams->lut, ccparams->thread_id);
3798   return NULL;
3799 }
3800 
3801 
convert_rgb_to_yuv_frame(uint8_t * rgbdata,int hsize,int vsize,int rowstride,int orow,uint8_t * u,boolean in_has_alpha,boolean out_has_alpha,int clamping,int thread_id)3802 static void convert_rgb_to_yuv_frame(uint8_t *rgbdata, int hsize, int vsize, int rowstride, int orow,
3803                                      uint8_t *u, boolean in_has_alpha, boolean out_has_alpha, int clamping, int thread_id) {
3804   int ipsize = 3, opsize = 3;
3805   int iwidth;
3806   uint8_t *end = rgbdata + (rowstride * vsize);
3807   register int i;
3808   uint8_t in_alpha = 255;
3809 
3810   if (LIVES_UNLIKELY(!conv_RY_inited)) init_RGB_to_YUV_tables();
3811   if (thread_id == -1)
3812     set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
3813 
3814   if (thread_id == -1 && prefs->nfx_threads > 1) {
3815     lives_thread_t threads[prefs->nfx_threads];
3816     int nthreads = 1;
3817     int dheight, xdheight;
3818     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
3819 
3820     xdheight = CEIL((double)vsize / (double)prefs->nfx_threads, 4);
3821     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
3822       dheight = xdheight;
3823 
3824       if ((rgbdata + dheight * i * rowstride) < end) {
3825         ccparams[i].src = rgbdata + dheight * i * rowstride;
3826         ccparams[i].hsize = hsize;
3827         ccparams[i].dest = u + dheight * i * orow;
3828 
3829         if (dheight * (i + 1) > (vsize - 4)) {
3830           dheight = vsize - (dheight * i);
3831         }
3832 
3833         ccparams[i].vsize = dheight;
3834 
3835         ccparams[i].irowstrides[0] = rowstride;
3836         ccparams[i].orowstrides[0] = orow;
3837         ccparams[i].in_alpha = in_has_alpha;
3838         ccparams[i].out_alpha = out_has_alpha;
3839         ccparams[i].out_clamping = clamping;
3840         ccparams[i].thread_id = i;
3841 
3842         if (i == 0) convert_rgb_to_yuv_frame_thread(&ccparams[i]);
3843         else {
3844           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_rgb_to_yuv_frame_thread, &ccparams[i]);
3845           nthreads++;
3846         }
3847       }
3848     }
3849 
3850     for (i = 1; i < nthreads; i++) {
3851       lives_thread_join(threads[i], NULL);
3852     }
3853     lives_free(ccparams);
3854     return;
3855   }
3856 
3857   if (in_has_alpha) ipsize = 4;
3858   if (out_has_alpha) opsize = 4;
3859 
3860   hsize = (hsize >> 1) << 1;
3861   iwidth = hsize * ipsize;
3862   orow -= hsize * opsize;
3863 
3864   for (; rgbdata < end; rgbdata += rowstride) {
3865     for (i = 0; i < iwidth; i += ipsize) {
3866       if (in_has_alpha) in_alpha = rgbdata[i + 3];
3867       if (out_has_alpha) u[3] = in_alpha;
3868       rgb2yuv(rgbdata[i], rgbdata[i + 1], rgbdata[i + 2], &(u[0]), &(u[1]), &(u[2]));
3869       u += opsize;
3870     }
3871     u += orow;
3872   }
3873 }
3874 
3875 
convert_rgb_to_yuv_frame_thread(void * data)3876 static void *convert_rgb_to_yuv_frame_thread(void *data) {
3877   lives_cc_params *ccparams = (lives_cc_params *)data;
3878   convert_rgb_to_yuv_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
3879                            ccparams->orowstrides[0],
3880                            (uint8_t *)ccparams->dest, ccparams->in_alpha, ccparams->out_alpha, ccparams->out_clamping,
3881                            ccparams->thread_id);
3882   return NULL;
3883 }
3884 
3885 
convert_rgb_to_yuvp_frame(uint8_t * rgbdata,int hsize,int vsize,int rowstride,int orow,uint8_t ** yuvp,boolean in_has_alpha,boolean out_has_alpha,int clamping,int thread_id)3886 static void convert_rgb_to_yuvp_frame(uint8_t *rgbdata, int hsize, int vsize, int rowstride, int orow,
3887                                       uint8_t **yuvp, boolean in_has_alpha, boolean out_has_alpha, int clamping, int thread_id) {
3888   int ipsize = 3;
3889   int iwidth;
3890   uint8_t *end = rgbdata + (rowstride * vsize);
3891   register int i;
3892   uint8_t in_alpha = 255, *a = NULL;
3893 
3894   uint8_t *y, *u, *v;
3895 
3896   if (LIVES_UNLIKELY(!conv_RY_inited)) init_RGB_to_YUV_tables();
3897   if (thread_id == -1)
3898     set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
3899 
3900   y = yuvp[0];
3901   u = yuvp[1];
3902   v = yuvp[2];
3903   if (out_has_alpha) a = yuvp[3];
3904 
3905   if (thread_id == -1 && prefs->nfx_threads > 1) {
3906     lives_thread_t threads[prefs->nfx_threads];
3907     int nthreads = 1;
3908     int dheight, xdheight;
3909     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
3910 
3911     xdheight = CEIL((double)vsize / (double)prefs->nfx_threads, 4);
3912     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
3913       dheight = xdheight;
3914 
3915       if ((rgbdata + dheight * i * rowstride) < end) {
3916         ccparams[i].src = rgbdata + dheight * i * rowstride;
3917         ccparams[i].hsize = hsize;
3918 
3919         ccparams[i].destp[0] = y + dheight * i * orow;
3920         ccparams[i].destp[1] = u + dheight * i * orow;
3921         ccparams[i].destp[2] = v + dheight * i * orow;
3922         if (out_has_alpha) ccparams[i].destp[3] = a + dheight * i * orow;
3923 
3924         if (dheight * (i + 1) > (vsize - 4)) {
3925           dheight = vsize - (dheight * i);
3926         }
3927 
3928         ccparams[i].vsize = dheight;
3929 
3930         ccparams[i].irowstrides[0] = rowstride;
3931         ccparams[i].orowstrides[0] = orow;
3932         ccparams[i].in_alpha = in_has_alpha;
3933         ccparams[i].out_alpha = out_has_alpha;
3934         ccparams[i].out_clamping = clamping;
3935         ccparams[i].thread_id = i;
3936 
3937         if (i == 0) convert_rgb_to_yuvp_frame_thread(&ccparams[i]);
3938         else {
3939           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_rgb_to_yuvp_frame_thread, &ccparams[i]);
3940           nthreads++;
3941         }
3942       }
3943     }
3944 
3945     for (i = 1; i < nthreads; i++) {
3946       lives_thread_join(threads[i], NULL);
3947     }
3948     lives_free(ccparams);
3949     return;
3950   }
3951 
3952   if (in_has_alpha) ipsize = 4;
3953 
3954   hsize = (hsize >> 1) << 1;
3955   iwidth = hsize * ipsize;
3956   orow -= hsize;
3957 
3958   for (; rgbdata < end; rgbdata += rowstride) {
3959     for (i = 0; i < iwidth; i += ipsize) {
3960       if (in_has_alpha) in_alpha = rgbdata[i + 3];
3961       if (out_has_alpha) *(a++) = in_alpha;
3962       rgb2yuv(rgbdata[i], rgbdata[i + 1], rgbdata[i + 2], y, u, v);
3963       y++;
3964       u++;
3965       v++;
3966     }
3967     y += orow;
3968     u += orow;
3969     v += orow;
3970     if (out_has_alpha) a += orow;
3971   }
3972 }
3973 
3974 
convert_rgb_to_yuvp_frame_thread(void * data)3975 static void *convert_rgb_to_yuvp_frame_thread(void *data) {
3976   lives_cc_params *ccparams = (lives_cc_params *)data;
3977   convert_rgb_to_yuvp_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
3978                             ccparams->orowstrides[0],
3979                             (uint8_t **)ccparams->destp, ccparams->in_alpha, ccparams->out_alpha, ccparams->out_clamping,
3980                             ccparams->thread_id);
3981   return NULL;
3982 }
3983 
3984 
convert_bgr_to_yuv_frame(uint8_t * rgbdata,int hsize,int vsize,int rowstride,int orow,uint8_t * u,boolean in_has_alpha,boolean out_has_alpha,int clamping,int thread_id)3985 static void convert_bgr_to_yuv_frame(uint8_t *rgbdata, int hsize, int vsize, int rowstride, int orow,
3986                                      uint8_t *u, boolean in_has_alpha, boolean out_has_alpha, int clamping, int thread_id) {
3987   int ipsize = 3, opsize = 3;
3988   int iwidth;
3989   uint8_t *end = rgbdata + (rowstride * vsize);
3990   register int i;
3991   uint8_t in_alpha = 255;
3992 
3993   if (LIVES_UNLIKELY(!conv_RY_inited)) init_RGB_to_YUV_tables();
3994   if (thread_id == -1)
3995     set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
3996 
3997   if (thread_id == -1 && prefs->nfx_threads > 1) {
3998     lives_thread_t threads[prefs->nfx_threads];
3999     int nthreads = 1;
4000     int dheight, xdheight;
4001     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
4002 
4003     xdheight = CEIL((double)vsize / (double)prefs->nfx_threads, 4);
4004     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
4005       dheight = xdheight;
4006 
4007       if ((rgbdata + dheight * i * rowstride) < end) {
4008         ccparams[i].src = rgbdata + dheight * i * rowstride;
4009         ccparams[i].hsize = hsize;
4010         ccparams[i].dest = u + dheight * i * orow;
4011 
4012         if (dheight * (i + 1) > (vsize - 4)) {
4013           dheight = vsize - (dheight * i);
4014         }
4015 
4016         ccparams[i].vsize = dheight;
4017 
4018         ccparams[i].irowstrides[0] = rowstride;
4019         ccparams[i].in_alpha = in_has_alpha;
4020         ccparams[i].out_alpha = out_has_alpha;
4021         ccparams[i].out_clamping = clamping;
4022         ccparams[i].thread_id = i;
4023 
4024         if (i == 0) convert_bgr_to_yuv_frame_thread(&ccparams[i]);
4025         else {
4026           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_bgr_to_yuv_frame_thread, &ccparams[i]);
4027           nthreads++;
4028         }
4029       }
4030     }
4031 
4032     for (i = 1; i < nthreads; i++) {
4033       lives_thread_join(threads[i], NULL);
4034     }
4035     lives_free(ccparams);
4036     return;
4037   }
4038 
4039   if (in_has_alpha) ipsize = 4;
4040   if (out_has_alpha) opsize = 4;
4041 
4042   hsize = (hsize >> 1) << 1;
4043   iwidth = hsize * ipsize;
4044   orow -= hsize * opsize;
4045 
4046   for (; rgbdata < end; rgbdata += rowstride) {
4047     for (i = 0; i < iwidth; i += ipsize) {
4048       bgr2yuv(rgbdata[i], rgbdata[i + 1], rgbdata[i + 2], &(u[0]), &(u[1]), &(u[2]));
4049       if (in_has_alpha) in_alpha = rgbdata[i + 3];
4050       if (out_has_alpha) u[3] = in_alpha;
4051       u += opsize;
4052     }
4053     u += orow;
4054   }
4055 }
4056 
4057 
convert_bgr_to_yuv_frame_thread(void * data)4058 static void *convert_bgr_to_yuv_frame_thread(void *data) {
4059   lives_cc_params *ccparams = (lives_cc_params *)data;
4060   convert_bgr_to_yuv_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
4061                            ccparams->orowstrides[0],
4062                            (uint8_t *)ccparams->dest, ccparams->in_alpha, ccparams->out_alpha, ccparams->out_clamping, ccparams->thread_id);
4063   return NULL;
4064 }
4065 
4066 
convert_bgr_to_yuvp_frame(uint8_t * rgbdata,int hsize,int vsize,int rowstride,int orow,uint8_t ** yuvp,boolean in_has_alpha,boolean out_has_alpha,int clamping,int thread_id)4067 static void convert_bgr_to_yuvp_frame(uint8_t *rgbdata, int hsize, int vsize, int rowstride, int orow,
4068                                       uint8_t **yuvp, boolean in_has_alpha, boolean out_has_alpha, int clamping, int thread_id) {
4069   // TESTED !
4070 
4071   int ipsize = 3;
4072   int iwidth;
4073   uint8_t *end = rgbdata + (rowstride * vsize);
4074   register int i;
4075   uint8_t in_alpha = 255, *a = NULL;
4076 
4077   uint8_t *y, *u, *v;
4078 
4079   if (LIVES_UNLIKELY(!conv_RY_inited)) init_RGB_to_YUV_tables();
4080   if (thread_id == -1)
4081     set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
4082 
4083   y = yuvp[0];
4084   u = yuvp[1];
4085   v = yuvp[2];
4086   if (out_has_alpha) a = yuvp[3];
4087 
4088   if (thread_id == -1 && prefs->nfx_threads > 1) {
4089     lives_thread_t threads[prefs->nfx_threads];
4090     int nthreads = 1;
4091     int dheight, xdheight;
4092     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
4093 
4094     xdheight = CEIL((double)vsize / (double)prefs->nfx_threads, 4);
4095     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
4096       dheight = xdheight;
4097 
4098       if ((rgbdata + dheight * i * rowstride) < end) {
4099         ccparams[i].src = rgbdata + dheight * i * rowstride;
4100         ccparams[i].hsize = hsize;
4101 
4102         ccparams[i].destp[0] = y + dheight * i * orow;
4103         ccparams[i].destp[1] = u + dheight * i * orow;
4104         ccparams[i].destp[2] = v + dheight * i * orow;
4105         if (out_has_alpha) ccparams[i].destp[3] = a + dheight * i * orow;
4106 
4107         if (dheight * (i + 1) > (vsize - 4)) {
4108           dheight = vsize - (dheight * i);
4109         }
4110 
4111         ccparams[i].vsize = dheight;
4112 
4113         ccparams[i].irowstrides[0] = rowstride;
4114         ccparams[i].orowstrides[0] = orow;
4115         ccparams[i].in_alpha = in_has_alpha;
4116         ccparams[i].out_alpha = out_has_alpha;
4117         ccparams[i].out_clamping = clamping;
4118         ccparams[i].thread_id = i;
4119 
4120         if (i == 0) convert_bgr_to_yuvp_frame_thread(&ccparams[i]);
4121         else {
4122           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_bgr_to_yuvp_frame_thread, &ccparams[i]);
4123           nthreads++;
4124         }
4125       }
4126     }
4127 
4128     for (i = 1; i < nthreads; i++) {
4129       lives_thread_join(threads[i], NULL);
4130     }
4131     lives_free(ccparams);
4132     return;
4133   }
4134 
4135   if (in_has_alpha) ipsize = 4;
4136 
4137   hsize = (hsize >> 1) << 1;
4138   iwidth = hsize * ipsize;
4139   orow -= hsize;
4140 
4141   for (; rgbdata < end; rgbdata += rowstride) {
4142     for (i = 0; i < iwidth; i += ipsize) {
4143       bgr2yuv(rgbdata[i], rgbdata[i + 1], rgbdata[i + 2], &(y[0]), &(u[0]), &(v[0]));
4144       if (in_has_alpha) in_alpha = rgbdata[i + 3];
4145       if (out_has_alpha) *(a++) = in_alpha;
4146       y++;
4147       u++;
4148       v++;
4149     }
4150     y += orow;
4151     u += orow;
4152     v += orow;
4153     if (out_has_alpha) a += orow;
4154   }
4155 }
4156 
4157 
convert_bgr_to_yuvp_frame_thread(void * data)4158 static void *convert_bgr_to_yuvp_frame_thread(void *data) {
4159   lives_cc_params *ccparams = (lives_cc_params *)data;
4160   convert_bgr_to_yuvp_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
4161                             ccparams->orowstrides[0],
4162                             (uint8_t **)ccparams->destp, ccparams->in_alpha, ccparams->out_alpha, ccparams->out_clamping,
4163                             ccparams->thread_id);
4164   return NULL;
4165 }
4166 
4167 
convert_argb_to_yuv_frame(uint8_t * rgbdata,int hsize,int vsize,int rowstride,int orow,uint8_t * u,boolean out_has_alpha,int clamping,int thread_id)4168 static void convert_argb_to_yuv_frame(uint8_t *rgbdata, int hsize, int vsize, int rowstride, int orow,
4169                                       uint8_t *u, boolean out_has_alpha, int clamping, int thread_id) {
4170   int ipsize = 4, opsize = 3;
4171   int iwidth;
4172   uint8_t *end = rgbdata + (rowstride * vsize);
4173   register int i;
4174 
4175   if (LIVES_UNLIKELY(!conv_RY_inited)) init_RGB_to_YUV_tables();
4176   if (thread_id == -1)
4177     set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
4178 
4179   if (thread_id == -1 && prefs->nfx_threads > 1) {
4180     lives_thread_t threads[prefs->nfx_threads];
4181     int nthreads = 1;
4182     int dheight, xdheight;
4183     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
4184 
4185     xdheight = CEIL((double)vsize / (double)prefs->nfx_threads, 4);
4186     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
4187       dheight = xdheight;
4188 
4189       if ((rgbdata + dheight * i * rowstride) < end) {
4190         ccparams[i].src = rgbdata + dheight * i * rowstride;
4191         ccparams[i].hsize = hsize;
4192 
4193         ccparams[i].dest = u + dheight * i * orow;
4194 
4195         if (dheight * (i + 1) > (vsize - 4)) {
4196           dheight = vsize - (dheight * i);
4197         }
4198 
4199         ccparams[i].vsize = dheight;
4200 
4201         ccparams[i].irowstrides[0] = rowstride;
4202         ccparams[i].orowstrides[0] = orow;
4203         ccparams[i].out_alpha = out_has_alpha;
4204         ccparams[i].out_clamping = clamping;
4205         ccparams[i].thread_id = i;
4206 
4207         if (i == 0) convert_rgb_to_yuv_frame_thread(&ccparams[i]);
4208         else {
4209           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_argb_to_yuv_frame_thread, &ccparams[i]);
4210           nthreads++;
4211         }
4212       }
4213     }
4214 
4215     for (i = 1; i < nthreads; i++) {
4216       lives_thread_join(threads[i], NULL);
4217     }
4218     lives_free(ccparams);
4219     return;
4220   }
4221 
4222   if (out_has_alpha) opsize = 4;
4223 
4224   hsize = (hsize >> 1) << 1;
4225   iwidth = hsize * ipsize;
4226   orow -= hsize * opsize;
4227 
4228   for (; rgbdata < end; rgbdata += rowstride) {
4229     for (i = 0; i < iwidth; i += ipsize) {
4230       if (out_has_alpha) u[3] = rgbdata[i];
4231       rgb2yuv(rgbdata[i + 1], rgbdata[i + 2], rgbdata[i + 3], &(u[0]), &(u[1]), &(u[2]));
4232       u += opsize;
4233     }
4234     u += orow;
4235   }
4236 }
4237 
4238 
convert_argb_to_yuv_frame_thread(void * data)4239 static void *convert_argb_to_yuv_frame_thread(void *data) {
4240   lives_cc_params *ccparams = (lives_cc_params *)data;
4241   convert_argb_to_yuv_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
4242                             ccparams->orowstrides[0],
4243                             (uint8_t *)ccparams->dest, ccparams->out_alpha, ccparams->out_clamping, ccparams->thread_id);
4244   return NULL;
4245 }
4246 
4247 
convert_argb_to_yuvp_frame(uint8_t * rgbdata,int hsize,int vsize,int rowstride,int orow,uint8_t ** yuvp,boolean out_has_alpha,int clamping,int thread_id)4248 static void convert_argb_to_yuvp_frame(uint8_t *rgbdata, int hsize, int vsize, int rowstride, int orow,
4249                                        uint8_t **yuvp, boolean out_has_alpha, int clamping, int thread_id) {
4250   int ipsize = 4;
4251   int iwidth;
4252   uint8_t *end = rgbdata + (rowstride * vsize);
4253   register int i;
4254   uint8_t *a = NULL;
4255   uint8_t *y, *u, *v;
4256 
4257   if (LIVES_UNLIKELY(!conv_RY_inited)) init_RGB_to_YUV_tables();
4258   if (thread_id == -1)
4259     set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
4260 
4261   y = yuvp[0];
4262   u = yuvp[1];
4263   v = yuvp[2];
4264   if (out_has_alpha) a = yuvp[3];
4265 
4266   if (thread_id == -1 && prefs->nfx_threads > 1) {
4267     lives_thread_t threads[prefs->nfx_threads];
4268     int nthreads = 1;
4269     int dheight, xdheight;
4270     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
4271 
4272     xdheight = CEIL((double)vsize / (double)prefs->nfx_threads, 4);
4273     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
4274       dheight = xdheight;
4275 
4276       if ((rgbdata + dheight * i * rowstride) < end) {
4277         ccparams[i].src = rgbdata + dheight * i * rowstride;
4278         ccparams[i].hsize = hsize;
4279 
4280         ccparams[i].destp[0] = y + dheight * i * orow;
4281         ccparams[i].destp[1] = u + dheight * i * orow;
4282         ccparams[i].destp[2] = v + dheight * i * orow;
4283         if (out_has_alpha) ccparams[i].destp[3] = a + dheight * i * orow;
4284 
4285         if (dheight * (i + 1) > (vsize - 4)) {
4286           dheight = vsize - (dheight * i);
4287         }
4288 
4289         ccparams[i].vsize = dheight;
4290 
4291         ccparams[i].irowstrides[0] = rowstride;
4292         ccparams[i].orowstrides[0] = orow;
4293         ccparams[i].out_alpha = out_has_alpha;
4294         ccparams[i].out_clamping = clamping;
4295         ccparams[i].thread_id = i;
4296 
4297         if (i == 0) convert_argb_to_yuvp_frame_thread(&ccparams[i]);
4298         else {
4299           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_argb_to_yuvp_frame_thread, &ccparams[i]);
4300           nthreads++;
4301         }
4302       }
4303     }
4304 
4305     for (i = 1; i < nthreads; i++) {
4306       lives_thread_join(threads[i], NULL);
4307     }
4308     lives_free(ccparams);
4309     return;
4310   }
4311 
4312   hsize = (hsize >> 1) << 1;
4313   iwidth = hsize * ipsize;
4314   orow -= hsize;
4315 
4316   for (; rgbdata < end; rgbdata += rowstride) {
4317     for (i = 0; i < iwidth; i += ipsize) {
4318       if (out_has_alpha) *(a++) = rgbdata[i];
4319       rgb2yuv(rgbdata[i + 1], rgbdata[i + 2], rgbdata[i + 3], y, u, v);
4320       y++;
4321       u++;
4322       v++;
4323     }
4324     y += orow;
4325     u += orow;
4326     v += orow;
4327     if (out_has_alpha) a += orow;
4328   }
4329 }
4330 
4331 
convert_argb_to_yuvp_frame_thread(void * data)4332 static void *convert_argb_to_yuvp_frame_thread(void *data) {
4333   lives_cc_params *ccparams = (lives_cc_params *)data;
4334   convert_argb_to_yuvp_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
4335                              ccparams->orowstrides[0],
4336                              (uint8_t **)ccparams->destp, ccparams->out_alpha, ccparams->out_clamping,
4337                              ccparams->thread_id);
4338   return NULL;
4339 }
4340 
4341 
convert_rgb_to_yuv420_frame(uint8_t * rgbdata,int hsize,int vsize,int rowstride,int * ostrides,uint8_t ** dest,boolean is_422,boolean has_alpha,int subspace,int clamping)4342 static void convert_rgb_to_yuv420_frame(uint8_t *rgbdata, int hsize, int vsize, int rowstride, int *ostrides,
4343                                         uint8_t **dest, boolean is_422, boolean has_alpha, int subspace, int clamping) {
4344   // for odd sized widths, cut the rightmost pixel
4345   // TODO - handle different out sampling types
4346   uint16_t *rgbdata16 = NULL;
4347   uint8_t *y, *Cb, *Cr;
4348   uyvy_macropixel u;
4349   boolean chroma_row = TRUE;
4350   size_t hhsize;
4351   int hs3;
4352   int ipsize = 3, ipsize2;
4353   boolean is16bit = FALSE;
4354   register int i, j;
4355 
4356   if (LIVES_UNLIKELY(!conv_RY_inited)) init_RGB_to_YUV_tables();
4357 
4358   set_conversion_arrays(clamping, subspace);
4359 
4360   if (has_alpha) ipsize = 4;
4361   if (hsize < 0) {
4362     is16bit = TRUE;
4363     hsize = -hsize;
4364     rgbdata16 = (uint16_t *)rgbdata;
4365   }
4366 
4367   // ensure width and height are both divisible by two
4368   hsize = (hsize >> 1) << 1;
4369   vsize = (vsize >> 1) << 1;
4370 
4371   y = dest[0];
4372   Cb = dest[1];
4373   Cr = dest[2];
4374 
4375   hhsize = hsize >> 1;
4376   ipsize2 = ipsize * 2;
4377   hs3 = (hsize * ipsize) - (ipsize2 - 1);
4378 
4379   for (i = 0; i < vsize; i++) {
4380     for (j = 0; j < hs3; j += ipsize2) {
4381       // mpeg style, Cb and Cr are co-located
4382       // convert 6 RGBRGB bytes to 4 UYVY bytes
4383 
4384       // TODO: for mpeg use rgb2yuv and write alternate u and v
4385       if (is16bit) {
4386         rgb16_2uyvy(rgbdata16[j], rgbdata16[j + 1], rgbdata16[j + 2], rgbdata16[j + ipsize], rgbdata16[j + ipsize + 1],
4387                     rgbdata16[j + ipsize + 2], &u);
4388       } else rgb2uyvy(rgbdata[j], rgbdata[j + 1], rgbdata[j + 2], rgbdata[j + ipsize], rgbdata[j + ipsize + 1],
4389                         rgbdata[j + ipsize + 2], &u);
4390 
4391       *(y++) = u.y0;
4392       *(y++) = u.y1;
4393       *(Cb++) = u.u0;
4394       *(Cr++) = u.v0;
4395 
4396       if (!is_422 && chroma_row && i > 0) {
4397         // average two rows
4398         Cb[-1 - ostrides[1]] = avg_chromaf(Cb[-1], Cb[-1 - ostrides[1]]);
4399         Cr[-1 - ostrides[1]] = avg_chromaf(Cr[-1], Cr[-1 - ostrides[1]]);
4400       }
4401 
4402     }
4403     if (!is_422) {
4404       if (chroma_row) {
4405         Cb -= hhsize;
4406         Cr -= hhsize;
4407       }
4408       chroma_row = !chroma_row;
4409     }
4410     rgbdata += rowstride;
4411   }
4412 }
4413 
4414 
convert_argb_to_yuv420_frame(uint8_t * rgbdata,int hsize,int vsize,int rowstride,int * ostrides,uint8_t ** dest,boolean is_422,int subspace,int clamping)4415 static void convert_argb_to_yuv420_frame(uint8_t *rgbdata, int hsize, int vsize, int rowstride, int *ostrides,
4416     uint8_t **dest, boolean is_422, int subspace, int clamping) {
4417   // for odd sized widths, cut the rightmost pixel
4418   // TODO - handle different out sampling types
4419   int hs3;
4420 
4421   uint8_t *y, *Cb, *Cr;
4422   uyvy_macropixel u;
4423   register int i, j;
4424   boolean chroma_row = TRUE;
4425 
4426   int ipsize = 4, ipsize2;
4427 
4428   if (LIVES_UNLIKELY(!conv_RY_inited)) init_RGB_to_YUV_tables();
4429 
4430   set_conversion_arrays(clamping, subspace);
4431 
4432   // ensure width and height are both divisible by two
4433   hsize = (hsize >> 1) << 1;
4434   vsize = (vsize >> 1) << 1;
4435 
4436   y = dest[0];
4437   Cb = dest[1];
4438   Cr = dest[2];
4439 
4440   ipsize2 = ipsize * 2;
4441   hs3 = (hsize * ipsize) - (ipsize2 - 1);
4442 
4443   for (i = 0; i < vsize; i++) {
4444     for (j = 0; j < hs3; j += ipsize2) {
4445       // mpeg style, Cb and Cr are co-located
4446       // convert 6 RGBRGB bytes to 4 UYVY bytes
4447 
4448       // TODO: for mpeg use rgb2yuv and write alternate u and v
4449 
4450       rgb2uyvy(rgbdata[j + 1], rgbdata[j + 2], rgbdata[j + 3], rgbdata[j + 1 + ipsize], rgbdata[j + 2 + ipsize + 1],
4451                rgbdata[j + 3 + ipsize + 2], &u);
4452 
4453       *(y++) = u.y0;
4454       *(y++) = u.y1;
4455       *(Cb++) = u.u0;
4456       *(Cr++) = u.v0;
4457 
4458       if (!is_422 && chroma_row && i > 0) {
4459         // average two rows
4460         Cb[-1 - ostrides[1]] = avg_chromaf(Cb[-1], Cb[-1 - ostrides[1]]);
4461         Cr[-1 - ostrides[2]] = avg_chromaf(Cr[-1], Cr[-1 - ostrides[2]]);
4462       }
4463 
4464     }
4465     if (!is_422) {
4466       if (chroma_row) {
4467         Cb -= ostrides[1];
4468         Cr -= ostrides[2];
4469       }
4470       chroma_row = !chroma_row;
4471     }
4472     rgbdata += rowstride;
4473   }
4474 }
4475 
4476 
convert_bgr_to_yuv420_frame(uint8_t * rgbdata,int hsize,int vsize,int rowstride,int * ostrides,uint8_t ** dest,boolean is_422,boolean has_alpha,int subspace,int clamping)4477 static void convert_bgr_to_yuv420_frame(uint8_t *rgbdata, int hsize, int vsize, int rowstride, int *ostrides,
4478                                         uint8_t **dest, boolean is_422, boolean has_alpha, int subspace, int clamping) {
4479   // for odd sized widths, cut the rightmost pixel
4480   // TODO - handle different out sampling types
4481   int hs3;
4482 
4483   uint8_t *y, *Cb, *Cr;
4484   uyvy_macropixel u;
4485   register int i, j;
4486   int chroma_row = TRUE;
4487   int ipsize = 3, ipsize2;
4488 
4489   if (LIVES_UNLIKELY(!conv_RY_inited)) init_RGB_to_YUV_tables();
4490 
4491   set_conversion_arrays(clamping, subspace);
4492 
4493   if (has_alpha) ipsize = 4;
4494 
4495   // ensure width and height are both divisible by two
4496   hsize = (hsize >> 1) << 1;
4497   vsize = (vsize >> 1) << 1;
4498 
4499   y = dest[0];
4500   Cb = dest[1];
4501   Cr = dest[2];
4502 
4503   ipsize2 = ipsize * 2;
4504   hs3 = (hsize * ipsize) - (ipsize2 - 1);
4505   for (i = 0; i < vsize; i++) {
4506     for (j = 0; j < hs3; j += ipsize2) {
4507       // convert 6 RGBRGB bytes to 4 UYVY bytes
4508       rgb2uyvy(rgbdata[j + 2], rgbdata[j + 1], rgbdata[j], rgbdata[j + ipsize + 2],
4509                rgbdata[j + ipsize + 1], rgbdata[j + ipsize], &u);
4510 
4511       *(y++) = u.y0;
4512       *(y++) = u.y1;
4513       *(Cb++) = u.u0;
4514       *(Cr++) = u.v0;
4515 
4516       if (!is_422 && chroma_row && i > 0) {
4517         // average two rows
4518         Cb[-1 - ostrides[1]] = avg_chromaf(Cb[-1], Cb[-1 - ostrides[1]]);
4519         Cr[-1 - ostrides[2]] = avg_chromaf(Cr[-1], Cr[-1 - ostrides[2]]);
4520       }
4521     }
4522     if (!is_422) {
4523       if (chroma_row) {
4524         Cb -= ostrides[1];
4525         Cr -= ostrides[1];
4526       }
4527       chroma_row = !chroma_row;
4528     }
4529     rgbdata += rowstride;
4530   }
4531 }
4532 
4533 
convert_yuv422p_to_uyvy_frame(uint8_t ** src,int width,int height,int * irows,int orow,uint8_t * dest)4534 static void convert_yuv422p_to_uyvy_frame(uint8_t **src, int width, int height, int *irows, int orow, uint8_t *dest) {
4535   // TODO - handle different in sampling types
4536   uint8_t *src_y = src[0];
4537   uint8_t *src_u = src[1];
4538   uint8_t *src_v = src[2];
4539   int i, j;
4540 
4541   irows[0] -= width;
4542   irows[1] -= width >> 1;
4543   irows[2] -= width >> 1;
4544   orow -= width * 4;
4545 
4546   for (i = 0; i < height; i++) {
4547     for (j = 0; j < width; j++) {
4548       *(dest++) = *(src_u++);
4549       *(dest++) = *(src_y++);
4550       *(dest++) = *(src_v++);
4551       *(dest++) = *(src_y++);
4552     }
4553     src_y += irows[0];
4554     src_u += irows[1];
4555     src_v += irows[2];
4556     dest += orow;
4557   }
4558 }
4559 
4560 
convert_yuv422p_to_yuyv_frame(uint8_t ** src,int width,int height,int * irows,int orow,uint8_t * dest)4561 static void convert_yuv422p_to_yuyv_frame(uint8_t **src, int width, int height, int *irows, int orow, uint8_t *dest) {
4562   // TODO - handle different in sampling types
4563 
4564   uint8_t *src_y = src[0];
4565   uint8_t *src_u = src[1];
4566   uint8_t *src_v = src[2];
4567   int i, j;
4568 
4569   irows[0] -= width;
4570   irows[1] -= width >> 1;
4571   irows[2] -= width >> 1;
4572   orow -= width * 4;
4573 
4574   for (i = 0; i < height; i++) {
4575     for (j = 0; j < width; j++) {
4576       *(dest++) = *(src_y++);
4577       *(dest++) = *(src_u++);
4578       *(dest++) = *(src_y++);
4579       *(dest++) = *(src_v++);
4580     }
4581     src_y += irows[0];
4582     src_u += irows[1];
4583     src_v += irows[2];
4584     dest += orow;
4585   }
4586 }
4587 
4588 
convert_rgb_to_yuv411_frame(uint8_t * rgbdata,int hsize,int vsize,int rowstride,yuv411_macropixel * u,boolean has_alpha,int clamping)4589 static void convert_rgb_to_yuv411_frame(uint8_t *rgbdata, int hsize, int vsize, int rowstride,
4590                                         yuv411_macropixel *u, boolean has_alpha, int clamping) {
4591   // for odd sized widths, cut the rightmost one, two or three pixels. Widths should be divisible by 4.
4592   // TODO - handle different out sampling types
4593   int hs3 = (int)(hsize >> 2) * 12, ipstep = 12;
4594 
4595   uint8_t *end;
4596   register int i;
4597 
4598   int x = 3, y = 4, z = 5, a = 6, b = 7, c = 8, d = 9, e = 10, f = 11;
4599 
4600   if (LIVES_UNLIKELY(!conv_RY_inited)) init_RGB_to_YUV_tables();
4601 
4602   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
4603 
4604   if (has_alpha) {
4605     z++;
4606     y++;
4607     x++;
4608     a += 2;
4609     b += 2;
4610     c += 2;
4611     d += 3;
4612     e += 3;
4613     f += 3;
4614     hs3 = (int)(hsize >> 2) * 16;
4615     ipstep = 16;
4616   }
4617   end = rgbdata + (rowstride * vsize) + 1 - ipstep;
4618   hs3 -= (ipstep - 1);
4619 
4620   for (; rgbdata < end; rgbdata += rowstride) {
4621     for (i = 0; i < hs3; i += ipstep) {
4622       // convert 12 RGBRGBRGBRGB bytes to 6 UYYVYY bytes
4623       rgb2_411(rgbdata[i], rgbdata[i + 1], rgbdata[i + 2], rgbdata[i + x], rgbdata[i + y], rgbdata[i + z], rgbdata[i + a],
4624                rgbdata[i + b],
4625                rgbdata[i + c], rgbdata[i + d],
4626                rgbdata[i + e], rgbdata[i + f], u++);
4627     }
4628   }
4629 }
4630 
4631 
convert_bgr_to_yuv411_frame(uint8_t * rgbdata,int hsize,int vsize,int rowstride,yuv411_macropixel * u,boolean has_alpha,int clamping)4632 static void convert_bgr_to_yuv411_frame(uint8_t *rgbdata, int hsize, int vsize, int rowstride,
4633                                         yuv411_macropixel *u, boolean has_alpha, int clamping) {
4634   // for odd sized widths, cut the rightmost one, two or three pixels
4635   // TODO - handle different out sampling types
4636   int hs3 = (int)(hsize >> 2) * 12, ipstep = 12;
4637 
4638   uint8_t *end;
4639   register int i;
4640 
4641   int x = 3, y = 4, z = 5, a = 6, b = 7, c = 8, d = 9, e = 10, f = 11;
4642 
4643   if (LIVES_UNLIKELY(!conv_RY_inited)) init_RGB_to_YUV_tables();
4644 
4645   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
4646 
4647   if (has_alpha) {
4648     z++;
4649     y++;
4650     x++;
4651     a += 2;
4652     b += 2;
4653     c += 2;
4654     d += 3;
4655     e += 3;
4656     f += 3;
4657     hs3 = (int)(hsize >> 2) * 16;
4658     ipstep = 16;
4659   }
4660   end = rgbdata + (rowstride * vsize) + 1 - ipstep;
4661   hs3 -= (ipstep - 1);
4662 
4663   for (; rgbdata < end; rgbdata += rowstride) {
4664     for (i = 0; i < hs3; i += ipstep) {
4665       // convert 12 RGBRGBRGBRGB bytes to 6 UYYVYY bytes
4666       rgb2_411(rgbdata[i + 2], rgbdata[i + 1], rgbdata[i], rgbdata[i + z], rgbdata[i + y], rgbdata[i + x], rgbdata[i + c],
4667                rgbdata[i + b],
4668                rgbdata[i + a], rgbdata[i + f],
4669                rgbdata[i + e], rgbdata[i + d], u++);
4670     }
4671   }
4672 }
4673 
4674 
convert_argb_to_yuv411_frame(uint8_t * rgbdata,int hsize,int vsize,int rowstride,yuv411_macropixel * u,int clamping)4675 static void convert_argb_to_yuv411_frame(uint8_t *rgbdata, int hsize, int vsize, int rowstride,
4676     yuv411_macropixel *u, int clamping) {
4677   // for odd sized widths, cut the rightmost one, two or three pixels. Widths should be divisible by 4.
4678   // TODO - handle different out sampling types
4679   int hs3 = (int)(hsize >> 2) * 12, ipstep = 12;
4680 
4681   uint8_t *end;
4682   register int i;
4683 
4684   if (LIVES_UNLIKELY(!conv_RY_inited)) init_RGB_to_YUV_tables();
4685 
4686   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
4687 
4688   hs3 = (int)(hsize >> 2) * 16;
4689   ipstep = 16;
4690 
4691   end = rgbdata + (rowstride * vsize) + 1 - ipstep;
4692   hs3 -= (ipstep - 1);
4693 
4694   for (; rgbdata < end; rgbdata += rowstride) {
4695     for (i = 0; i < hs3; i += ipstep) {
4696       // convert 12 RGBRGBRGBRGB bytes to 6 UYYVYY bytes
4697       rgb2_411(rgbdata[i + 1], rgbdata[i + 2], rgbdata[i + 3], rgbdata[i + 5], rgbdata[i + 6], rgbdata[i + 7], rgbdata[i + 9],
4698                rgbdata[i + 10],
4699                rgbdata[i + 11],
4700                rgbdata[i + 13], rgbdata[i + 14], rgbdata[i + 15], u++);
4701     }
4702   }
4703 }
4704 
4705 
convert_uyvy_to_rgb_frame(uyvy_macropixel * src,int width,int height,int irow,int orowstride,uint8_t * dest,boolean add_alpha,int clamping,int subspace,int thread_id)4706 static void convert_uyvy_to_rgb_frame(uyvy_macropixel *src, int width, int height, int irow, int orowstride,
4707                                       uint8_t *dest, boolean add_alpha, int clamping, int  subspace, int thread_id) {
4708   register int i, j;
4709   int psize = 6;
4710   int a = 3, b = 4, c = 5;
4711 
4712   if (LIVES_UNLIKELY(!conv_YR_inited)) init_YUV_to_RGB_tables();
4713   if (thread_id == -1)
4714     set_conversion_arrays(clamping, subspace);
4715 
4716   if (thread_id == -1 && prefs->nfx_threads > 1) {
4717     lives_thread_t threads[prefs->nfx_threads];
4718     int nthreads = 1;
4719     int dheight, xdheight;
4720     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
4721 
4722     xdheight = CEIL((double)height / (double)prefs->nfx_threads, 4);
4723     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
4724       dheight = xdheight;
4725 
4726       if ((dheight * i) < height) {
4727         ccparams[i].src = src + dheight * i * irow / 4;
4728         ccparams[i].hsize = width;
4729         ccparams[i].dest = dest + dheight * i * orowstride;
4730 
4731         if (dheight * (i + 1) > (height - 4)) {
4732           dheight = height - (dheight * i);
4733         }
4734 
4735         ccparams[i].vsize = dheight;
4736 
4737         ccparams[i].irowstrides[0] = irow;
4738         ccparams[i].orowstrides[0] = orowstride;
4739         ccparams[i].out_alpha = add_alpha;
4740         ccparams[i].in_clamping = clamping;
4741         ccparams[i].in_subspace = subspace;
4742         ccparams[i].thread_id = i;
4743 
4744         if (i == 0) convert_uyvy_to_rgb_frame_thread(&ccparams[i]);
4745         else {
4746           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_uyvy_to_rgb_frame_thread, &ccparams[i]);
4747           nthreads++;
4748         }
4749       }
4750     }
4751 
4752     for (i = 1; i < nthreads; i++) {
4753       lives_thread_join(threads[i], NULL);
4754     }
4755     lives_free(ccparams);
4756     return;
4757   }
4758 
4759   if (add_alpha) {
4760     psize = 8;
4761     a = 4;
4762     b = 5;
4763     c = 6;
4764   }
4765 
4766   orowstride -= width * psize;
4767   irow = irow / 4 - width;
4768   for (i = 0; i < height; i++) {
4769     for (j = 0; j < width; j++) {
4770       uyvy2rgb(src, &dest[0], &dest[1], &dest[2], &dest[a], &dest[b], &dest[c]);
4771       if (add_alpha) dest[3] = dest[7] = 255;
4772       dest += psize;
4773       src++;
4774     }
4775     src += irow;
4776     dest += orowstride;
4777   }
4778 }
4779 
4780 
convert_uyvy_to_rgb_frame_thread(void * data)4781 static void *convert_uyvy_to_rgb_frame_thread(void *data) {
4782   lives_cc_params *ccparams = (lives_cc_params *)data;
4783   convert_uyvy_to_rgb_frame((uyvy_macropixel *)ccparams->src, ccparams->hsize, ccparams->vsize,
4784                             ccparams->irowstrides[0], ccparams->orowstrides[0],
4785                             (uint8_t *)ccparams->dest, ccparams->out_alpha, ccparams->in_clamping,
4786                             ccparams->in_subspace, ccparams->thread_id);
4787   return NULL;
4788 }
4789 
4790 
convert_uyvy_to_bgr_frame(uyvy_macropixel * src,int width,int height,int irow,int orowstride,uint8_t * dest,boolean add_alpha,int clamping,int thread_id)4791 static void convert_uyvy_to_bgr_frame(uyvy_macropixel *src, int width, int height, int irow, int orowstride,
4792                                       uint8_t *dest, boolean add_alpha, int clamping, int thread_id) {
4793   register int i, j;
4794   int psize = 6;
4795 
4796   int a = 3, b = 4, c = 5;
4797 
4798   if (LIVES_UNLIKELY(!conv_YR_inited)) init_YUV_to_RGB_tables();
4799   if (thread_id == -1)
4800     set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
4801 
4802   if (thread_id == -1 && prefs->nfx_threads > 1) {
4803     lives_thread_t threads[prefs->nfx_threads];
4804     int nthreads = 1;
4805     int dheight, xdheight;
4806     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
4807 
4808     xdheight = CEIL((double)height / (double)prefs->nfx_threads, 4);
4809     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
4810       dheight = xdheight;
4811 
4812       if ((dheight * i) < height) {
4813         ccparams[i].src = src + dheight * i * irow / 4;
4814         ccparams[i].hsize = width;
4815         ccparams[i].dest = dest + dheight * i * orowstride;
4816 
4817         if (dheight * (i + 1) > (height - 4)) {
4818           dheight = height - (dheight * i);
4819         }
4820 
4821         ccparams[i].vsize = dheight;
4822 
4823         ccparams[i].irowstrides[0] = irow;
4824         ccparams[i].orowstrides[0] = orowstride;
4825         ccparams[i].out_alpha = add_alpha;
4826         ccparams[i].in_clamping = clamping;
4827         ccparams[i].thread_id = i;
4828 
4829         if (i == 0) convert_uyvy_to_bgr_frame_thread(&ccparams[i]);
4830         else {
4831           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_uyvy_to_bgr_frame_thread, &ccparams[i]);
4832           nthreads++;
4833         }
4834       }
4835     }
4836 
4837     for (i = 1; i < nthreads; i++) {
4838       lives_thread_join(threads[i], NULL);
4839     }
4840     lives_free(ccparams);
4841     return;
4842   }
4843 
4844   if (add_alpha) {
4845     psize = 8;
4846     a = 4;
4847     b = 5;
4848     c = 6;
4849   }
4850 
4851   orowstride -= width * psize;
4852   irow = irow / 4 - width;
4853   for (i = 0; i < height; i++) {
4854     for (j = 0; j < width; j++) {
4855       uyvy2rgb(src, &dest[2], &dest[1], &dest[0], &dest[c], &dest[b], &dest[a]);
4856       if (add_alpha) dest[3] = dest[7] = 255;
4857       dest += psize;
4858       src++;
4859     }
4860     src += irow;
4861     dest += orowstride;
4862   }
4863 }
4864 
4865 
convert_uyvy_to_bgr_frame_thread(void * data)4866 static void *convert_uyvy_to_bgr_frame_thread(void *data) {
4867   lives_cc_params *ccparams = (lives_cc_params *)data;
4868   convert_uyvy_to_bgr_frame((uyvy_macropixel *)ccparams->src, ccparams->hsize, ccparams->vsize,
4869                             ccparams->irowstrides[0], ccparams->orowstrides[0],
4870                             (uint8_t *)ccparams->dest, ccparams->out_alpha, ccparams->in_clamping, ccparams->thread_id);
4871   return NULL;
4872 }
4873 
4874 
convert_uyvy_to_argb_frame(uyvy_macropixel * src,int width,int height,int irow,int orowstride,uint8_t * dest,int clamping,int thread_id)4875 static void convert_uyvy_to_argb_frame(uyvy_macropixel *src, int width, int height, int irow, int orowstride,
4876                                        uint8_t *dest, int clamping, int thread_id) {
4877   register int i, j;
4878   int psize = 8;
4879 
4880   if (LIVES_UNLIKELY(!conv_YR_inited)) init_YUV_to_RGB_tables();
4881   if (thread_id == -1)
4882     set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
4883 
4884   if (thread_id == -1 && prefs->nfx_threads > 1) {
4885     lives_thread_t threads[prefs->nfx_threads];
4886     int nthreads = 1;
4887     int dheight, xdheight;
4888     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
4889 
4890     xdheight = CEIL((double)height / (double)prefs->nfx_threads, 4);
4891     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
4892       dheight = xdheight;
4893 
4894       if ((dheight * i) < height) {
4895         ccparams[i].src = src + dheight * i * irow / 4;
4896         ccparams[i].hsize = width;
4897         ccparams[i].dest = dest + dheight * i * orowstride;
4898 
4899         if (dheight * (i + 1) > (height - 4)) {
4900           dheight = height - (dheight * i);
4901         }
4902 
4903         ccparams[i].vsize = dheight;
4904 
4905         ccparams[i].irowstrides[0] = irow;
4906         ccparams[i].orowstrides[0] = orowstride;
4907         ccparams[i].in_clamping = clamping;
4908         ccparams[i].thread_id = i;
4909 
4910         if (i == 0) convert_uyvy_to_argb_frame_thread(&ccparams[i]);
4911         else {
4912           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_uyvy_to_argb_frame_thread, &ccparams[i]);
4913           nthreads++;
4914         }
4915       }
4916     }
4917 
4918     for (i = 1; i < nthreads; i++) {
4919       lives_thread_join(threads[i], NULL);
4920     }
4921     lives_free(ccparams);
4922     return;
4923   }
4924 
4925   orowstride -= width * psize;
4926   irow = irow / 4 - width;
4927   for (i = 0; i < height; i++) {
4928     for (j = 0; j < width; j++) {
4929       uyvy2rgb(src, &dest[1], &dest[2], &dest[3], &dest[5], &dest[6], &dest[7]);
4930       dest[0] = dest[4] = 255;
4931       dest += psize;
4932       src++;
4933     }
4934     src += irow;
4935     dest += orowstride;
4936   }
4937 }
4938 
4939 
convert_uyvy_to_argb_frame_thread(void * data)4940 static void *convert_uyvy_to_argb_frame_thread(void *data) {
4941   lives_cc_params *ccparams = (lives_cc_params *)data;
4942   convert_uyvy_to_argb_frame((uyvy_macropixel *)ccparams->src, ccparams->hsize, ccparams->vsize,
4943                              ccparams->irowstrides[0], ccparams->orowstrides[0],
4944                              (uint8_t *)ccparams->dest, ccparams->in_clamping, ccparams->thread_id);
4945   return NULL;
4946 }
4947 
4948 
convert_yuyv_to_rgb_frame(yuyv_macropixel * src,int width,int height,int irow,int orowstride,uint8_t * dest,boolean add_alpha,int clamping,int thread_id)4949 static void convert_yuyv_to_rgb_frame(yuyv_macropixel *src, int width, int height, int irow, int orowstride,
4950                                       uint8_t *dest, boolean add_alpha, int clamping, int thread_id) {
4951   register int i, j;
4952   int psize = 6;
4953   int a = 3, b = 4, c = 5;
4954 
4955   if (LIVES_UNLIKELY(!conv_YR_inited)) init_YUV_to_RGB_tables();
4956   if (thread_id == -1)
4957     set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
4958 
4959   if (thread_id == -1 && prefs->nfx_threads > 1) {
4960     lives_thread_t threads[prefs->nfx_threads];
4961     int nthreads = 1;
4962     int dheight, xdheight;
4963     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
4964 
4965     xdheight = CEIL((double)height / (double)prefs->nfx_threads, 4);
4966     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
4967       dheight = xdheight;
4968 
4969       if ((dheight * i) < height) {
4970         ccparams[i].src = src + dheight * i * irow / 4;
4971         ccparams[i].hsize = width;
4972         ccparams[i].dest = dest + dheight * i * orowstride;
4973 
4974         if (dheight * (i + 1) > (height - 4)) {
4975           dheight = height - (dheight * i);
4976         }
4977 
4978         ccparams[i].vsize = dheight;
4979         ccparams[i].irowstrides[0] = irow;
4980         ccparams[i].orowstrides[0] = orowstride;
4981         ccparams[i].out_alpha = add_alpha;
4982         ccparams[i].in_clamping = clamping;
4983         ccparams[i].thread_id = i;
4984 
4985         if (i == 0) convert_yuyv_to_rgb_frame_thread(&ccparams[i]);
4986         else {
4987           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_yuyv_to_rgb_frame_thread, &ccparams[i]);
4988           nthreads++;
4989         }
4990       }
4991     }
4992 
4993     for (i = 1; i < nthreads; i++) {
4994       lives_thread_join(threads[i], NULL);
4995     }
4996     lives_free(ccparams);
4997     return;
4998   }
4999 
5000   if (add_alpha) {
5001     psize = 8;
5002     a = 4;
5003     b = 5;
5004     c = 6;
5005   }
5006 
5007   orowstride -= width * psize;
5008   irow = irow / 4 - width;
5009   for (i = 0; i < height; i++) {
5010     for (j = 0; j < width; j++) {
5011       yuyv2rgb(src, &dest[0], &dest[1], &dest[2], &dest[a], &dest[b], &dest[c]);
5012       if (add_alpha) dest[3] = dest[7] = 255;
5013       dest += psize;
5014       src++;
5015     }
5016     src += irow;
5017     dest += orowstride;
5018   }
5019 }
5020 
5021 
convert_yuyv_to_rgb_frame_thread(void * data)5022 static void *convert_yuyv_to_rgb_frame_thread(void *data) {
5023   lives_cc_params *ccparams = (lives_cc_params *)data;
5024   convert_yuyv_to_rgb_frame((yuyv_macropixel *)ccparams->src, ccparams->hsize, ccparams->vsize,
5025                             ccparams->irowstrides[0], ccparams->orowstrides[0],
5026                             (uint8_t *)ccparams->dest, ccparams->out_alpha, ccparams->in_clamping, ccparams->thread_id);
5027   return NULL;
5028 }
5029 
5030 
convert_yuyv_to_bgr_frame(yuyv_macropixel * src,int width,int height,int irow,int orowstride,uint8_t * dest,boolean add_alpha,int clamping,int thread_id)5031 static void convert_yuyv_to_bgr_frame(yuyv_macropixel *src, int width, int height, int irow, int orowstride,
5032                                       uint8_t *dest, boolean add_alpha, int clamping, int thread_id) {
5033   register int i, j;
5034   int psize = 6;
5035   int a = 3, b = 4, c = 5;
5036 
5037   if (LIVES_UNLIKELY(!conv_YR_inited)) init_YUV_to_RGB_tables();
5038   if (thread_id == -1)
5039     set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
5040 
5041   if (thread_id == -1 && prefs->nfx_threads > 1) {
5042     lives_thread_t threads[prefs->nfx_threads];
5043     int nthreads = 1;
5044     int dheight, xdheight;
5045     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
5046 
5047     xdheight = CEIL((double)height / (double)prefs->nfx_threads, 4);
5048     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
5049       dheight = xdheight;
5050 
5051       if ((dheight * i) < height) {
5052         ccparams[i].src = src + dheight * i * irow / 4;
5053         ccparams[i].hsize = width;
5054         ccparams[i].dest = dest + dheight * i * orowstride;
5055 
5056         if (dheight * (i + 1) > (height - 4)) {
5057           dheight = height - (dheight * i);
5058         }
5059 
5060         ccparams[i].vsize = dheight;
5061 
5062         ccparams[i].irowstrides[0] = irow;
5063         ccparams[i].orowstrides[0] = orowstride;
5064         ccparams[i].out_alpha = add_alpha;
5065         ccparams[i].in_clamping = clamping;
5066         ccparams[i].thread_id = i;
5067 
5068         if (i == 0) convert_yuyv_to_bgr_frame_thread(&ccparams[i]);
5069         else {
5070           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_yuyv_to_bgr_frame_thread, &ccparams[i]);
5071           nthreads++;
5072         }
5073       }
5074     }
5075 
5076     for (i = 1; i < nthreads; i++) {
5077       lives_thread_join(threads[i], NULL);
5078     }
5079     lives_free(ccparams);
5080     return;
5081   }
5082 
5083   if (add_alpha) {
5084     psize = 8;
5085     a = 4;
5086     b = 5;
5087     c = 6;
5088   }
5089 
5090   orowstride -= width * psize;
5091   irow = irow / 4 - width;
5092   for (i = 0; i < height; i++) {
5093     for (j = 0; j < width; j++) {
5094       yuyv2rgb(src, &dest[2], &dest[1], &dest[0], &dest[c], &dest[b], &dest[a]);
5095       if (add_alpha) dest[3] = dest[7] = 255;
5096       dest += psize;
5097       src++;
5098     }
5099     src += irow;
5100     dest += orowstride;
5101   }
5102 }
5103 
5104 
convert_yuyv_to_bgr_frame_thread(void * data)5105 static void *convert_yuyv_to_bgr_frame_thread(void *data) {
5106   lives_cc_params *ccparams = (lives_cc_params *)data;
5107   convert_yuyv_to_bgr_frame((yuyv_macropixel *)ccparams->src, ccparams->hsize, ccparams->vsize,
5108                             ccparams->irowstrides[0], ccparams->orowstrides[0],
5109                             (uint8_t *)ccparams->dest, ccparams->out_alpha, ccparams->in_clamping, ccparams->thread_id);
5110   return NULL;
5111 }
5112 
5113 
convert_yuyv_to_argb_frame(yuyv_macropixel * src,int width,int height,int irow,int orowstride,uint8_t * dest,int clamping,int thread_id)5114 static void convert_yuyv_to_argb_frame(yuyv_macropixel *src, int width, int height, int irow, int orowstride,
5115                                        uint8_t *dest, int clamping, int thread_id) {
5116   register int i, j;
5117   int psize = 8;
5118 
5119   if (LIVES_UNLIKELY(!conv_YR_inited)) init_YUV_to_RGB_tables();
5120   if (thread_id == -1)
5121     set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
5122 
5123   if (thread_id == -1 && prefs->nfx_threads > 1) {
5124     lives_thread_t threads[prefs->nfx_threads];
5125     int nthreads = 1;
5126     int dheight, xdheight;
5127     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
5128 
5129     xdheight = CEIL((double)height / (double)prefs->nfx_threads, 4);
5130     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
5131       dheight = xdheight;
5132 
5133       if ((dheight * i) < height) {
5134         ccparams[i].src = src + dheight * i * irow / 4;
5135         ccparams[i].hsize = width;
5136         ccparams[i].dest = dest + dheight * i * orowstride;
5137 
5138         if (dheight * (i + 1) > (height - 4)) {
5139           dheight = height - (dheight * i);
5140         }
5141 
5142         ccparams[i].vsize = dheight;
5143 
5144         ccparams[i].irowstrides[0] = irow;
5145         ccparams[i].orowstrides[0] = orowstride;
5146         ccparams[i].in_clamping = clamping;
5147         ccparams[i].thread_id = i;
5148 
5149         if (i == 0) convert_yuyv_to_argb_frame_thread(&ccparams[i]);
5150         else {
5151           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_yuyv_to_argb_frame_thread, &ccparams[i]);
5152           nthreads++;
5153         }
5154       }
5155     }
5156 
5157     for (i = 1; i < nthreads; i++) {
5158       lives_thread_join(threads[i], NULL);
5159     }
5160     lives_free(ccparams);
5161     return;
5162   }
5163 
5164   orowstride -= width * psize;
5165   irow = irow / 4 - width;
5166   for (i = 0; i < height; i++) {
5167     for (j = 0; j < width; j++) {
5168       yuyv2rgb(src, &dest[1], &dest[2], &dest[3], &dest[5], &dest[6], &dest[7]);
5169       dest[0] = dest[4] = 255;
5170       dest += psize;
5171       src++;
5172     }
5173     src += irow;
5174     dest += orowstride;
5175   }
5176 }
5177 
5178 
convert_yuyv_to_argb_frame_thread(void * data)5179 static void *convert_yuyv_to_argb_frame_thread(void *data) {
5180   lives_cc_params *ccparams = (lives_cc_params *)data;
5181   convert_yuyv_to_argb_frame((yuyv_macropixel *)ccparams->src, ccparams->hsize, ccparams->vsize,
5182                              ccparams->irowstrides[0], ccparams->orowstrides[0],
5183                              (uint8_t *)ccparams->dest, ccparams->in_clamping, ccparams->thread_id);
5184   return NULL;
5185 }
5186 
5187 
convert_yuv420_to_uyvy_frame(uint8_t ** src,int width,int height,int * irows,int orow,uyvy_macropixel * dest,int clamping)5188 static void convert_yuv420_to_uyvy_frame(uint8_t **src, int width, int height, int *irows, int orow,
5189     uyvy_macropixel *dest, int clamping) {
5190   register int i = 0, j;
5191   uint8_t *y, *u, *v, *end;
5192   int hwidth = width >> 1;
5193   boolean chroma = TRUE;
5194 
5195   // TODO - hasndle different in sampling types
5196   if (!avg_inited) init_average();
5197 
5198   y = src[0];
5199   u = src[1];
5200   v = src[2];
5201 
5202   end = y + height * irows[0];
5203   orow = orow / 4 - hwidth;
5204   irows[0] -= width;
5205 
5206   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
5207 
5208   while (y < end) {
5209     for (j = 0; j < hwidth; j++) {
5210       dest->u0 = u[0];
5211       dest->y0 = y[0];
5212       dest->v0 = v[0];
5213       dest->y1 = y[1];
5214 
5215       if (chroma && i > 0) {
5216         dest[-hwidth].u0 = avg_chromaf(dest[-hwidth].u0, u[0]);
5217         dest[-hwidth].v0 = avg_chromaf(dest[-hwidth].v0, v[0]);
5218       }
5219 
5220       dest++;
5221       y += 2;
5222       u++;
5223       v++;
5224     }
5225     if (chroma) {
5226       u -= irows[1];
5227       v -= irows[2];
5228     }
5229     chroma = !chroma;
5230     y += irows[0];
5231     dest += orow;
5232   }
5233 }
5234 
5235 
convert_yuv420_to_yuyv_frame(uint8_t ** src,int width,int height,int * irows,int orow,yuyv_macropixel * dest,int clamping)5236 static void convert_yuv420_to_yuyv_frame(uint8_t **src, int width, int height, int *irows, int orow, yuyv_macropixel *dest,
5237     int clamping) {
5238   register int i = 0, j;
5239   uint8_t *y, *u, *v, *end;
5240   int hwidth = width >> 1;
5241   boolean chroma = TRUE;
5242 
5243   // TODO - handle different in sampling types
5244   if (!avg_inited) init_average();
5245 
5246   y = src[0];
5247   u = src[1];
5248   v = src[2];
5249 
5250   end = y + height * irows[0];
5251   orow = orow / 4 - hwidth;
5252   irows[0] -= width;
5253 
5254   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
5255 
5256   while (y < end) {
5257     for (j = 0; j < hwidth; j++) {
5258       dest->y0 = y[0];
5259       dest->u0 = u[0];
5260       dest->y1 = y[1];
5261       dest->v0 = v[0];
5262 
5263       if (chroma && i > 0) {
5264         dest[-hwidth].u0 = avg_chromaf(dest[-hwidth].u0, u[0]);
5265         dest[-hwidth].v0 = avg_chromaf(dest[-hwidth].v0, v[0]);
5266       }
5267 
5268       dest++;
5269       y += 2;
5270       u++;
5271       v++;
5272     }
5273     if (chroma) {
5274       u -= irows[1];
5275       v -= irows[2];
5276     }
5277     chroma = !chroma;
5278     dest += orow;
5279   }
5280 }
5281 
5282 
convert_yuv_planar_to_rgb_frame(uint8_t ** src,int width,int height,int irowstride,int orowstride,uint8_t * dest,boolean in_alpha,boolean out_alpha,int clamping,int thread_id)5283 static void convert_yuv_planar_to_rgb_frame(uint8_t **src, int width, int height, int irowstride, int orowstride, uint8_t *dest,
5284     boolean in_alpha, boolean out_alpha, int clamping, int thread_id) {
5285   uint8_t *y = src[0];
5286   uint8_t *u = src[1];
5287   uint8_t *v = src[2];
5288   uint8_t *a = NULL;
5289 
5290   uint8_t *end = y + irowstride * height;
5291 
5292   size_t opstep = 3;
5293   register int i, j;
5294 
5295   if (LIVES_UNLIKELY(!conv_YR_inited)) init_YUV_to_RGB_tables();
5296   if (thread_id == -1)
5297     set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
5298 
5299   if (in_alpha) a = src[3];
5300 
5301   if (thread_id == -1 && prefs->nfx_threads > 1) {
5302     lives_thread_t threads[prefs->nfx_threads];
5303     int nthreads = 1;
5304     int dheight, xdheight;
5305     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
5306 
5307     xdheight = CEIL((double)height / (double)prefs->nfx_threads, 4);
5308     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
5309       dheight = xdheight;
5310 
5311       if ((y + dheight * i * irowstride) < end) {
5312         ccparams[i].hsize = width;
5313 
5314         ccparams[i].srcp[0] = y + dheight * i * irowstride;
5315         ccparams[i].srcp[1] = u + dheight * i * irowstride;
5316         ccparams[i].srcp[2] = v + dheight * i * irowstride;
5317         if (in_alpha) ccparams[i].srcp[3] = a + dheight * i * irowstride;
5318 
5319         ccparams[i].dest = dest + dheight * i * orowstride;
5320 
5321         if (dheight * (i + 1) > (height - 4)) {
5322           dheight = height - (dheight * i);
5323         }
5324 
5325         ccparams[i].vsize = dheight;
5326 
5327         ccparams[i].irowstrides[0] = irowstride;
5328         ccparams[i].orowstrides[0] = orowstride;
5329         ccparams[i].in_alpha = in_alpha;
5330         ccparams[i].out_alpha = out_alpha;
5331         ccparams[i].out_clamping = clamping;
5332         ccparams[i].thread_id = i;
5333 
5334         if (i == 0) convert_yuv_planar_to_rgb_frame_thread(&ccparams[i]);
5335         else {
5336           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_yuv_planar_to_rgb_frame_thread, &ccparams[i]);
5337           nthreads++;
5338         }
5339       }
5340     }
5341 
5342     for (i = 1; i < nthreads; i++) {
5343       lives_thread_join(threads[i], NULL);
5344     }
5345     lives_free(ccparams);
5346     return;
5347   }
5348 
5349   if (out_alpha) opstep = 4;
5350 
5351   orowstride -= width * opstep;
5352   irowstride -= width;
5353 
5354   for (i = 0; i < height; i++) {
5355     for (j = 0; j < width; j++) {
5356       yuv2rgb(*(y++), *(u++), *(v++), &dest[0], &dest[1], &dest[2]);
5357       if (out_alpha) {
5358         if (in_alpha) {
5359           dest[3] = *(a++);
5360         } else dest[3] = 255;
5361       }
5362       dest += opstep;
5363     }
5364     dest += orowstride;
5365     y += irowstride;
5366     u += irowstride;
5367     v += irowstride;
5368     if (a) a += irowstride;
5369   }
5370 }
5371 
5372 
convert_yuv_planar_to_rgb_frame_thread(void * data)5373 static void *convert_yuv_planar_to_rgb_frame_thread(void *data) {
5374   lives_cc_params *ccparams = (lives_cc_params *)data;
5375   convert_yuv_planar_to_rgb_frame((uint8_t **)ccparams->srcp, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
5376                                   ccparams->orowstrides[0],
5377                                   (uint8_t *)ccparams->dest, ccparams->in_alpha, ccparams->out_alpha,
5378                                   ccparams->in_clamping, ccparams->thread_id);
5379   return NULL;
5380 }
5381 
5382 
convert_yuv_planar_to_bgr_frame(uint8_t ** src,int width,int height,int irowstride,int orowstride,uint8_t * dest,boolean in_alpha,boolean out_alpha,int clamping,int thread_id)5383 static void convert_yuv_planar_to_bgr_frame(uint8_t **src, int width, int height, int irowstride, int orowstride, uint8_t *dest,
5384     boolean in_alpha, boolean out_alpha, int clamping, int thread_id) {
5385   uint8_t *y = src[0];
5386   uint8_t *u = src[1];
5387   uint8_t *v = src[2];
5388   uint8_t *a = NULL;
5389 
5390   uint8_t *end = y + irowstride * height;
5391 
5392   size_t opstep = 4;
5393   int i, j;
5394 
5395   if (LIVES_UNLIKELY(!conv_YR_inited)) init_YUV_to_RGB_tables();
5396   if (thread_id == -1)
5397     set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
5398 
5399   if (in_alpha) a = src[3];
5400 
5401   if (thread_id == -1 && prefs->nfx_threads > 1) {
5402     lives_thread_t threads[prefs->nfx_threads];
5403     int nthreads = 1;
5404     int dheight, xdheight;
5405     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
5406 
5407     xdheight = CEIL((double)height / (double)prefs->nfx_threads, 4);
5408 
5409     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
5410       dheight = xdheight;
5411 
5412       if ((y + dheight * i * irowstride) < end) {
5413         ccparams[i].hsize = width;
5414 
5415         ccparams[i].srcp[0] = y + dheight * i * irowstride;
5416         ccparams[i].srcp[1] = u + dheight * i * irowstride;
5417         ccparams[i].srcp[2] = v + dheight * i * irowstride;
5418         if (in_alpha) ccparams[i].srcp[3] = a + dheight * i * irowstride;
5419 
5420         ccparams[i].dest = dest + dheight * i * orowstride;
5421 
5422         if (dheight * (i + 1) > (height - 4)) {
5423           dheight = height - (dheight * i);
5424         }
5425 
5426         ccparams[i].vsize = dheight;
5427 
5428         ccparams[i].irowstrides[0] = irowstride;
5429         ccparams[i].orowstrides[0] = orowstride;
5430         ccparams[i].in_alpha = in_alpha;
5431         ccparams[i].out_alpha = out_alpha;
5432         ccparams[i].out_clamping = clamping;
5433         ccparams[i].thread_id = i;
5434 
5435         if (i == 0) convert_yuv_planar_to_bgr_frame_thread(&ccparams[i]);
5436         else {
5437           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_yuv_planar_to_bgr_frame_thread, &ccparams[i]);
5438           nthreads++;
5439         }
5440       }
5441     }
5442 
5443     for (i = 1; i < nthreads; i++) {
5444       lives_thread_join(threads[i], NULL);
5445     }
5446     lives_free(ccparams);
5447     return;
5448   }
5449 
5450   orowstride -= width * opstep;
5451   irowstride -= width;
5452 
5453   for (i = 0; i < height; i++) {
5454     for (j = 0; j < width; j++) {
5455       yuv2bgr(*(y++), *(u++), *(v++), &dest[0], &dest[1], &dest[2]);
5456       if (out_alpha) {
5457         if (in_alpha) {
5458           dest[3] = *(a++);
5459         } else dest[3] = 255;
5460       }
5461       dest += opstep;
5462     }
5463     dest += orowstride;
5464     y += irowstride;
5465     u += irowstride;
5466     v += irowstride;
5467     if (a) a += irowstride;
5468   }
5469 }
5470 
5471 
convert_yuv_planar_to_bgr_frame_thread(void * data)5472 static void *convert_yuv_planar_to_bgr_frame_thread(void *data) {
5473   lives_cc_params *ccparams = (lives_cc_params *)data;
5474   convert_yuv_planar_to_bgr_frame((uint8_t **)ccparams->srcp, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
5475                                   ccparams->orowstrides[0],
5476                                   (uint8_t *)ccparams->dest, ccparams->in_alpha, ccparams->out_alpha,
5477                                   ccparams->in_clamping, ccparams->thread_id);
5478   return NULL;
5479 }
5480 
5481 
convert_yuv_planar_to_argb_frame(uint8_t ** src,int width,int height,int irowstride,int orowstride,uint8_t * dest,boolean in_alpha,int clamping,int thread_id)5482 static void convert_yuv_planar_to_argb_frame(uint8_t **src, int width, int height, int irowstride, int orowstride,
5483     uint8_t *dest,
5484     boolean in_alpha, int clamping, int thread_id) {
5485   uint8_t *y = src[0];
5486   uint8_t *u = src[1];
5487   uint8_t *v = src[2];
5488   uint8_t *a = NULL;
5489 
5490   uint8_t *end = y + irowstride * height;
5491 
5492   size_t opstep = 4;
5493   register int i, j;
5494 
5495   if (LIVES_UNLIKELY(!conv_YR_inited)) init_YUV_to_RGB_tables();
5496 
5497   if (in_alpha) a = src[3];
5498 
5499   if (thread_id == -1 && prefs->nfx_threads > 1) {
5500     lives_thread_t threads[prefs->nfx_threads];
5501     int nthreads = 1;
5502     int dheight, xdheight;
5503     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
5504 
5505     xdheight = CEIL((double)height / (double)prefs->nfx_threads, 4);
5506     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
5507       dheight = xdheight;
5508 
5509       if ((y + dheight * i * irowstride) < end) {
5510         ccparams[i].hsize = width;
5511 
5512         ccparams[i].srcp[0] = y + dheight * i * irowstride;
5513         ccparams[i].srcp[1] = u + dheight * i * irowstride;
5514         ccparams[i].srcp[2] = v + dheight * i * irowstride;
5515         if (in_alpha) ccparams[i].srcp[3] = a + dheight * i * irowstride;
5516 
5517         ccparams[i].dest = dest + dheight * i * orowstride;
5518 
5519         if (dheight * (i + 1) > (height - 4)) {
5520           dheight = height - (dheight * i);
5521         }
5522 
5523         ccparams[i].vsize = dheight;
5524 
5525         ccparams[i].irowstrides[0] = irowstride;
5526         ccparams[i].orowstrides[0] = orowstride;
5527         ccparams[i].in_alpha = in_alpha;
5528         ccparams[i].out_clamping = clamping;
5529         ccparams[i].thread_id = i;
5530 
5531         if (i == 0) convert_yuv_planar_to_argb_frame_thread(&ccparams[i]);
5532         else {
5533           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_yuv_planar_to_argb_frame_thread, &ccparams[i]);
5534           nthreads++;
5535         }
5536       }
5537     }
5538 
5539     for (i = 1; i < nthreads; i++) {
5540       lives_thread_join(threads[i], NULL);
5541     }
5542     lives_free(ccparams);
5543     return;
5544   }
5545 
5546   orowstride -= width * opstep;
5547   orowstride -= width;
5548 
5549   for (i = 0; i < height; i++) {
5550     for (j = 0; j < width; j++) {
5551       yuv2rgb(*(y++), *(u++), *(v++), &dest[1], &dest[2], &dest[3]);
5552       if (in_alpha) {
5553         dest[0] = *(a++);
5554       } else dest[0] = 255;
5555       dest += opstep;
5556     }
5557     dest += orowstride;
5558     y += irowstride;
5559     u += irowstride;
5560     v += irowstride;
5561     if (a) a += irowstride;
5562   }
5563 }
5564 
5565 
convert_yuv_planar_to_argb_frame_thread(void * data)5566 static void *convert_yuv_planar_to_argb_frame_thread(void *data) {
5567   lives_cc_params *ccparams = (lives_cc_params *)data;
5568   convert_yuv_planar_to_argb_frame((uint8_t **)ccparams->srcp, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
5569                                    ccparams->orowstrides[0],
5570                                    (uint8_t *)ccparams->dest, ccparams->in_alpha, ccparams->in_clamping, ccparams->thread_id);
5571   return NULL;
5572 }
5573 
5574 
convert_yuv_planar_to_uyvy_frame(uint8_t ** src,int width,int height,int irowstride,int orowstride,uyvy_macropixel * uyvy,int clamping)5575 static void convert_yuv_planar_to_uyvy_frame(uint8_t **src, int width, int height, int irowstride, int orowstride,
5576     uyvy_macropixel *uyvy, int clamping) {
5577   register int x, k;
5578   int size = (width * height) >> 1;
5579 
5580   uint8_t *y = src[0];
5581   uint8_t *u = src[1];
5582   uint8_t *v = src[2];
5583 
5584   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
5585 
5586   if (irowstride == width && orowstride == width << 1) {
5587     for (x = 0; x < size; x++) {
5588       // subsample two u pixels
5589       uyvy->u0 = avg_chromaf(u[0], u[1]);
5590       u += 2;
5591       uyvy->y0 = *(y++);
5592       // subsample 2 v pixels
5593       uyvy->v0 = avg_chromaf(v[0], v[1]);
5594       v += 2;
5595       uyvy->y1 = *(y++);
5596       uyvy++;
5597     }
5598     return;
5599   }
5600   irowstride -= width;
5601   orowstride -= width << 1;
5602   for (k = 0; k < height; k++) {
5603     for (x = 0; x < width; x++) {
5604       // subsample two u pixels
5605       uyvy->u0 = avg_chromaf(u[0], u[1]);
5606       u += 2;
5607       uyvy->y0 = *(y++);
5608       // subsample 2 v pixels
5609       uyvy->v0 = avg_chromaf(v[0], v[1]);
5610       v += 2;
5611       uyvy->y1 = *(y++);
5612       uyvy++;
5613     }
5614     y += irowstride;
5615     u += irowstride;
5616     v += irowstride;
5617     uyvy += orowstride;
5618   }
5619 }
5620 
5621 
convert_yuv_planar_to_yuyv_frame(uint8_t ** src,int width,int height,int irowstride,int orowstride,yuyv_macropixel * yuyv,int clamping)5622 static void convert_yuv_planar_to_yuyv_frame(uint8_t **src, int width, int height, int irowstride, int orowstride,
5623     yuyv_macropixel *yuyv, int clamping) {
5624   register int x, k;
5625   int hsize = (width * height) >> 1;
5626 
5627   uint8_t *y = src[0];
5628   uint8_t *u = src[1];
5629   uint8_t *v = src[2];
5630 
5631   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
5632 
5633   if (irowstride == width && orowstride == (width << 1)) {
5634     for (x = 0; x < hsize; x++) {
5635       yuyv->y0 = *(y++);
5636       yuyv->u0 = avg_chromaf(u[0], u[1]);
5637       u += 2;
5638       yuyv->y1 = *(y++);
5639       yuyv->v0 = avg_chromaf(v[0], v[1]);
5640       v += 2;
5641       yuyv++;
5642     }
5643     return;
5644   }
5645 
5646   irowstride -= width;
5647   orowstride = orowstride / 4 - width;
5648   for (k = 0; k < height; k++) {
5649     for (x = 0; x < width; x++) {
5650       yuyv->y0 = *(y++);
5651       yuyv->u0 = avg_chromaf(u[0], u[1]);
5652       u += 2;
5653       yuyv->y1 = *(y++);
5654       yuyv->v0 = avg_chromaf(v[0], v[1]);
5655       v += 2;
5656       yuyv++;
5657     }
5658     y += irowstride;
5659     u += irowstride;
5660     v += irowstride;
5661     yuyv += orowstride;
5662   }
5663 }
5664 
5665 
convert_combineplanes_frame(uint8_t ** src,int width,int height,int irowstride,int orowstride,uint8_t * dest,boolean in_alpha,boolean out_alpha)5666 static void convert_combineplanes_frame(uint8_t **src, int width, int height, int irowstride,
5667                                         int orowstride, uint8_t *dest, boolean in_alpha, boolean out_alpha) {
5668   // turn 3 or 4 planes into packed pixels, src and dest can have alpha
5669 
5670   // e.g yuv444(4)p to yuv888(8)
5671 
5672   int size = width * height;
5673 
5674   uint8_t *y = src[0];
5675   uint8_t *u = src[1];
5676   uint8_t *v = src[2];
5677   uint8_t *a = NULL;
5678   int opsize = 3;
5679   register int x, k;
5680 
5681   if (in_alpha) a = src[3];
5682   if (out_alpha) opsize = 4;
5683 
5684   if (irowstride == width && orowstride == width * opsize) {
5685     for (x = 0; x < size; x++) {
5686       *(dest++) = *(y++);
5687       *(dest++) = *(u++);
5688       *(dest++) = *(v++);
5689       if (out_alpha) {
5690         if (in_alpha) *(dest++) = *(a++);
5691         else *(dest++) = 255;
5692       }
5693     }
5694   } else {
5695     irowstride -= width;
5696     orowstride -= width * opsize;
5697     for (k = 0; k < height; k++) {
5698       for (x = 0; x < width; x++) {
5699         *(dest++) = *(y++);
5700         *(dest++) = *(u++);
5701         *(dest++) = *(v++);
5702         if (out_alpha) {
5703           if (in_alpha) *(dest++) = *(a++);
5704           else *(dest++) = 255;
5705         }
5706       }
5707       dest += orowstride;
5708       y += irowstride;
5709       u += irowstride;
5710       v += irowstride;
5711     }
5712   }
5713 }
5714 
5715 
convert_yuvap_to_yuvp_frame(uint8_t ** src,int width,int height,int irowstride,int orowstride,uint8_t ** dest)5716 static void convert_yuvap_to_yuvp_frame(uint8_t **src, int width, int height, int irowstride, int orowstride, uint8_t **dest) {
5717   size_t size = irowstride * height;
5718 
5719   uint8_t *ys = src[0];
5720   uint8_t *us = src[1];
5721   uint8_t *vs = src[2];
5722 
5723   uint8_t *yd = dest[0];
5724   uint8_t *ud = dest[1];
5725   uint8_t *vd = dest[2];
5726 
5727   register int y;
5728 
5729   if (orowstride == irowstride) {
5730     if (yd != ys) lives_memcpy(yd, ys, size);
5731     if (ud != us) lives_memcpy(ud, us, size);
5732     if (vd != vs) lives_memcpy(vd, vs, size);
5733     return;
5734   }
5735   for (y = 0; y < height; y++) {
5736     if (yd != ys) {
5737       lives_memcpy(yd, ys, width);
5738       yd += orowstride;
5739       ys += irowstride;
5740     }
5741     if (ud != us) {
5742       lives_memcpy(ud, us, width);
5743       ud += orowstride;
5744       us += irowstride;
5745     }
5746     if (vd != vs) {
5747       lives_memcpy(vd, vs, width);
5748       vd += orowstride;
5749       vs += irowstride;
5750     }
5751   }
5752 }
5753 
5754 
convert_yuvp_to_yuvap_frame(uint8_t ** src,int width,int height,int irowstride,int orowstride,uint8_t ** dest)5755 static void convert_yuvp_to_yuvap_frame(uint8_t **src, int width, int height, int irowstride, int orowstride,  uint8_t **dest) {
5756   convert_yuvap_to_yuvp_frame(src, width, height, irowstride, orowstride, dest);
5757   lives_memset(dest[3], 255, orowstride * height);
5758 }
5759 
5760 
convert_yuvp_to_yuv420_frame(uint8_t ** src,int width,int height,int * irows,int * orows,uint8_t ** dest,int clamping)5761 static void convert_yuvp_to_yuv420_frame(uint8_t **src, int width, int height, int *irows, int *orows, uint8_t **dest,
5762     int clamping) {
5763   // halve the chroma samples vertically and horizontally, with sub-sampling
5764 
5765   // convert 444p to 420p
5766 
5767   // TODO - handle different output sampling types
5768 
5769   // y-plane should be copied before entering here
5770 
5771   register int i, j;
5772   uint8_t *d_u, *d_v, *s_u = src[1], *s_v = src[2];
5773   register short x_u, x_v;
5774   boolean chroma = FALSE;
5775 
5776   int hwidth = width >> 1;
5777 
5778   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
5779 
5780   if (dest[0] != src[0]) {
5781     if (irows[0] == orows[0]) {
5782       lives_memcpy(dest[0], src[0], irows[0] * height);
5783     } else {
5784       uint8_t *d_y = dest[0];
5785       uint8_t *s_y = src[0];
5786       for (i = 0; i < height; i++) {
5787         lives_memcpy(d_y, s_y, width);
5788         d_y += orows[0];
5789         s_y += irows[0];
5790       }
5791     }
5792   }
5793 
5794   d_u = dest[1];
5795   d_v = dest[2];
5796 
5797   for (i = 0; i < height; i++) {
5798     for (j = 0; j < hwidth; j++) {
5799       if (!chroma) {
5800         // pass 1, copy row
5801         // average two dest pixels
5802         d_u[j] = avg_chromaf(s_u[j * 2], s_u[j * 2 + 1]);
5803         d_v[j] = avg_chromaf(s_v[j * 2], s_v[j * 2 + 1]);
5804       } else {
5805         // pass 2
5806         // average two dest pixels
5807         x_u = avg_chromaf(s_u[j * 2], s_u[j * 2 + 1]);
5808         x_v = avg_chromaf(s_v[j * 2], s_v[j * 2 + 1]);
5809         // average two dest rows
5810         d_u[j] = avg_chromaf(d_u[j], x_u);
5811         d_v[j] = avg_chromaf(d_v[j], x_v);
5812       }
5813     }
5814     if (chroma) {
5815       d_u += orows[1];
5816       d_v += orows[2];
5817     }
5818     chroma = !chroma;
5819     s_u += irows[1];
5820     s_v += irows[2];
5821   }
5822 }
5823 
5824 
convert_yuvp_to_yuv411_frame(uint8_t ** src,int width,int height,int irowstride,yuv411_macropixel * yuv,int clamping)5825 static void convert_yuvp_to_yuv411_frame(uint8_t **src, int width, int height, int irowstride,
5826     yuv411_macropixel *yuv, int clamping) {
5827   // quarter the chroma samples horizontally, with sub-sampling
5828 
5829   // convert 444p to 411 packed
5830   // TODO - handle different output sampling types
5831 
5832   register int i, j;
5833   uint8_t *s_y = src[0], *s_u = src[1], *s_v = src[2];
5834   register short x_u, x_v;
5835 
5836   int widtha = (width >> 1) << 1; // cut rightmost odd bytes
5837   int cbytes = width - widtha;
5838 
5839   irowstride -= width + cbytes;
5840 
5841   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
5842 
5843   for (i = 0; i < height; i++) {
5844     for (j = 0; j < widtha; j += 4) {
5845       // average four dest pixels
5846       yuv->u2 = avg_chromaf(s_u[0], s_u[1]);
5847       x_u = avg_chromaf(s_u[2], s_u[3]);
5848       yuv->u2 = avg_chromaf(yuv->u2, x_u);
5849 
5850       s_u += 4;
5851 
5852       yuv->y0 = *(s_y++);
5853       yuv->y1 = *(s_y++);
5854 
5855       yuv->v2 = avg_chromaf(s_v[0], s_v[1]);
5856       x_v = avg_chromaf(s_v[2], s_v[3]);
5857       yuv->v2 = avg_chromaf(yuv->v2, x_v);
5858 
5859       s_v += 4;
5860 
5861       yuv->y2 = *(s_y++);
5862       yuv->y3 = *(s_y++);
5863     }
5864     s_u += irowstride;
5865     s_v += irowstride;
5866   }
5867 }
5868 
5869 
convert_uyvy_to_yuvp_frame(uyvy_macropixel * uyvy,int width,int height,int irow,int * orow,uint8_t ** dest,boolean add_alpha)5870 static void convert_uyvy_to_yuvp_frame(uyvy_macropixel *uyvy, int width, int height, int irow, int *orow, uint8_t **dest,
5871                                        boolean add_alpha) {
5872   // TODO - avg_chroma
5873 
5874   uint8_t *y = dest[0];
5875   uint8_t *u = dest[1];
5876   uint8_t *v = dest[2];
5877 
5878   irow /= 4;
5879 
5880   for (int k = 0; k < height; k++) {
5881     for (int x = 0, x1 = 0; x < width; x++, x1 += 2) {
5882       u[k * orow[0] + x1] = u[k * orow[0] + x1 + 1] = uyvy[k * irow + x].u0;
5883       y[k * orow[1] + x1] = uyvy[k * irow + x].y0;
5884       v[k * orow[2] + x1] = v[k * orow[2] + x1 + 1] = uyvy[k * irow + x].v0;
5885       y[k * orow[0] + x1 + 1] = uyvy[k * irow + x].y1;
5886     }
5887   }
5888   if (add_alpha) lives_memset(dest[3], 255, orow[3] * height);
5889 }
5890 
5891 
convert_yuyv_to_yuvp_frame(yuyv_macropixel * yuyv,int width,int height,int irow,int * orow,uint8_t ** dest,boolean add_alpha)5892 static void convert_yuyv_to_yuvp_frame(yuyv_macropixel *yuyv, int width, int height, int irow, int *orow,
5893                                        uint8_t **dest, boolean add_alpha) {
5894   // TODO - avg_chroma
5895 
5896   uint8_t *y = dest[0];
5897   uint8_t *u = dest[1];
5898   uint8_t *v = dest[2];
5899 
5900   irow /= 4;
5901 
5902   for (int k = 0; k < height; k++) {
5903     for (int x = 0, x1 = 0; x < width; x++, x1 += 2) {
5904       y[k * orow[1] + x1] = yuyv[k * irow + x].y0;
5905       u[k * orow[0] + x1] = u[k * orow[0] + x1 + 1] = yuyv[k * irow + x].u0;
5906       y[k * orow[0] + x1 + 1] = yuyv[k * irow + x].y1;
5907       v[k * orow[2] + x1] = v[k * orow[2] + x1 + 1] = yuyv[k * irow + x].v0;
5908     }
5909   }
5910   if (add_alpha) lives_memset(dest[3], 255, orow[3] * height);
5911 }
5912 
5913 
convert_uyvy_to_yuv888_frame(uyvy_macropixel * uyvy,int width,int height,int irow,int orow,uint8_t * yuv,boolean add_alpha)5914 static void convert_uyvy_to_yuv888_frame(uyvy_macropixel *uyvy, int width, int height, int irow, int orow,
5915     uint8_t *yuv, boolean add_alpha) {
5916   // no subsampling : TODO
5917 
5918   irow /= 4;
5919 
5920   for (int y = 0; y < height; y++) {
5921     for (int x = 0, x1 = 0; x < width; x++) {
5922       yuv[y * orow + x1++] = uyvy[y * irow + x].y0;
5923       yuv[y * orow + x1++] = uyvy[y * irow + x].u0;
5924       yuv[y * orow + x1++] = uyvy[y * irow + x].v0;
5925       if (add_alpha) yuv[y * orow + x1++] = 255;
5926       yuv[y * orow + x1++] = uyvy[y * irow + x].y1;
5927       yuv[y * orow + x1++] = uyvy[y * irow + x].u0;
5928       yuv[y * orow + x1++] = uyvy[y * irow + x].v0;
5929       if (add_alpha) yuv[y * orow + x1++] = 255;
5930     }
5931   }
5932 }
5933 
5934 
convert_yuyv_to_yuv888_frame(yuyv_macropixel * yuyv,int width,int height,int irow,int orow,uint8_t * yuv,boolean add_alpha)5935 static void convert_yuyv_to_yuv888_frame(yuyv_macropixel *yuyv, int width, int height, int irow, int orow,
5936     uint8_t *yuv, boolean add_alpha) {
5937   // no subsampling : TODO
5938 
5939   irow /= 4;
5940 
5941   for (int y = 0; y < height; y++) {
5942     for (int x = 0, x1 = 0; x < width; x++) {
5943       yuv[y * orow + x1++] = yuyv[y * irow + x].y0;
5944       yuv[y * orow + x1++] = yuyv[y * irow + x].u0;
5945       yuv[y * orow + x1++] = yuyv[y * irow + x].v0;
5946       if (add_alpha) yuv[y * orow + x1++] = 255;
5947       yuv[y * orow + x1++] = yuyv[y * irow + x].y1;
5948       yuv[y * orow + x1++] = yuyv[y * irow + x].u0;
5949       yuv[y * orow + x1++] = yuyv[y * irow + x].v0;
5950       if (add_alpha) yuv[y * orow + x1++] = 255;
5951     }
5952   }
5953 }
5954 
5955 
convert_uyvy_to_yuv420_frame(uyvy_macropixel * uyvy,int width,int height,uint8_t ** yuv,int clamping)5956 static void convert_uyvy_to_yuv420_frame(uyvy_macropixel *uyvy, int width, int height, uint8_t **yuv, int clamping) {
5957   // subsample vertically
5958 
5959   // TODO - handle different sampling types
5960 
5961   register int j;
5962 
5963   uint8_t *y = yuv[0];
5964   uint8_t *u = yuv[1];
5965   uint8_t *v = yuv[2];
5966 
5967   boolean chroma = TRUE;
5968 
5969   uint8_t *end = y + width * height * 2;
5970 
5971   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
5972 
5973   while (y < end) {
5974     for (j = 0; j < width; j++) {
5975       if (chroma) *(u++) = uyvy->u0;
5976       else {
5977         *u = avg_chromaf(*u, uyvy->u0);
5978         u++;
5979       }
5980       *(y++) = uyvy->y0;
5981       if (chroma) *(v++) = uyvy->v0;
5982       else {
5983         *v = avg_chromaf(*v, uyvy->v0);
5984         v++;
5985       }
5986       *(y++) = uyvy->y1;
5987       uyvy++;
5988     }
5989     if (chroma) {
5990       u -= width;
5991       v -= width;
5992     }
5993     chroma = !chroma;
5994   }
5995 }
5996 
5997 
convert_yuyv_to_yuv420_frame(yuyv_macropixel * yuyv,int width,int height,uint8_t ** yuv,int clamping)5998 static void convert_yuyv_to_yuv420_frame(yuyv_macropixel *yuyv, int width, int height, uint8_t **yuv, int clamping) {
5999   // subsample vertically
6000 
6001   // TODO - handle different sampling types
6002 
6003   register int j;
6004 
6005   uint8_t *y = yuv[0];
6006   uint8_t *u = yuv[1];
6007   uint8_t *v = yuv[2];
6008 
6009   boolean chroma = TRUE;
6010 
6011   uint8_t *end = y + width * height * 2;
6012 
6013   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
6014 
6015   while (y < end) {
6016     for (j = 0; j < width; j++) {
6017       *(y++) = yuyv->y0;
6018       if (chroma) *(u++) = yuyv->u0;
6019       else {
6020         *u = avg_chromaf(*u, yuyv->u0);
6021         u++;
6022       }
6023       *(y++) = yuyv->y1;
6024       if (chroma) *(v++) = yuyv->v0;
6025       else {
6026         *v = avg_chromaf(*v, yuyv->v0);
6027         v++;
6028       }
6029       yuyv++;
6030     }
6031     if (chroma) {
6032       u -= width;
6033       v -= width;
6034     }
6035     chroma = !chroma;
6036   }
6037 }
6038 
6039 
convert_uyvy_to_yuv411_frame(uyvy_macropixel * uyvy,int width,int height,yuv411_macropixel * yuv,int clamping)6040 static void convert_uyvy_to_yuv411_frame(uyvy_macropixel *uyvy, int width, int height, yuv411_macropixel *yuv, int clamping) {
6041   // subsample chroma horizontally
6042 
6043   uyvy_macropixel *end = uyvy + width * height;
6044   register int x;
6045 
6046   int widtha = (width << 1) >> 1;
6047   size_t cbytes = width - widtha;
6048 
6049   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
6050 
6051   for (; uyvy < end; uyvy += cbytes) {
6052     for (x = 0; x < widtha; x += 2) {
6053       yuv->u2 = avg_chromaf(uyvy[0].u0, uyvy[1].u0);
6054 
6055       yuv->y0 = uyvy[0].y0;
6056       yuv->y1 = uyvy[0].y1;
6057 
6058       yuv->v2 = avg_chromaf(uyvy[0].v0, uyvy[1].v0);
6059 
6060       yuv->y2 = uyvy[1].y0;
6061       yuv->y3 = uyvy[1].y1;
6062 
6063       uyvy += 2;
6064       yuv++;
6065     }
6066   }
6067 }
6068 
6069 
convert_yuyv_to_yuv411_frame(yuyv_macropixel * yuyv,int width,int height,yuv411_macropixel * yuv,int clamping)6070 static void convert_yuyv_to_yuv411_frame(yuyv_macropixel *yuyv, int width, int height, yuv411_macropixel *yuv, int clamping) {
6071   // subsample chroma horizontally
6072 
6073   // TODO - handle different sampling types
6074 
6075   yuyv_macropixel *end = yuyv + width * height;
6076   register int x;
6077 
6078   int widtha = (width << 1) >> 1;
6079   size_t cybtes = width - widtha;
6080 
6081   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
6082 
6083   for (; yuyv < end; yuyv += cybtes) {
6084     for (x = 0; x < widtha; x += 2) {
6085       yuv->u2 = avg_chromaf(yuyv[0].u0, yuyv[1].u0);
6086 
6087       yuv->y0 = yuyv[0].y0;
6088       yuv->y1 = yuyv[0].y1;
6089 
6090       yuv->v2 = avg_chromaf(yuyv[0].v0, yuyv[1].v0);
6091 
6092       yuv->y2 = yuyv[1].y0;
6093       yuv->y3 = yuyv[1].y1;
6094 
6095       yuyv += 2;
6096       yuv++;
6097     }
6098   }
6099 }
6100 
6101 
convert_yuv888_to_yuv420_frame(uint8_t * yuv8,int width,int height,int irowstride,int * orows,uint8_t ** yuv4,boolean src_alpha,int clamping)6102 static void convert_yuv888_to_yuv420_frame(uint8_t *yuv8, int width, int height, int irowstride, int *orows,
6103     uint8_t **yuv4, boolean src_alpha, int clamping) {
6104   // subsample vertically and horizontally
6105 
6106   //
6107 
6108   // yuv888(8) packed to 420p
6109 
6110   // TODO - handle different sampling types
6111 
6112   // TESTED !
6113 
6114   register int j;
6115   register short x_u, x_v;
6116 
6117   uint8_t *d_y, *d_u, *d_v, *end;
6118 
6119   boolean chroma = TRUE;
6120 
6121   size_t ipsize = 3, ipsize2;
6122   int widthx;
6123 
6124   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
6125 
6126   if (src_alpha) ipsize = 4;
6127 
6128   d_y = yuv4[0];
6129   d_u = yuv4[1];
6130   d_v = yuv4[2];
6131 
6132   end = d_y + width * height;
6133   ipsize2 = ipsize * 2;
6134   widthx = width * ipsize;
6135 
6136   while (d_y < end) {
6137     for (j = 0; j < widthx; j += ipsize2) {
6138       *(d_y++) = yuv8[j];
6139       *(d_y++) = yuv8[j + ipsize];
6140       if (chroma) {
6141         *(d_u++) = avg_chromaf(yuv8[j + 1], yuv8[j + 1 + ipsize]);
6142         *(d_v++) = avg_chromaf(yuv8[j + 2], yuv8[j + 2 + ipsize]);
6143       } else {
6144         x_u = avg_chromaf(yuv8[j + 1], yuv8[j + 1 + ipsize]);
6145         *d_u = avg_chromaf(*d_u, x_u);
6146         d_u++;
6147         x_v = avg_chromaf(yuv8[j + 2], yuv8[j + 2 + ipsize]);
6148         *d_v = avg_chromaf(*d_v, x_v);
6149         d_v++;
6150       }
6151     }
6152     if (chroma) {
6153       d_u -= orows[1];
6154       d_v -= orows[2];
6155     }
6156     chroma = !chroma;
6157     yuv8 += irowstride;
6158   }
6159 }
6160 
6161 
convert_uyvy_to_yuv422_frame(uyvy_macropixel * uyvy,int width,int height,uint8_t ** yuv)6162 static void convert_uyvy_to_yuv422_frame(uyvy_macropixel *uyvy, int width, int height, uint8_t **yuv) {
6163   int size = width * height; // y is twice this, u and v are equal
6164 
6165   uint8_t *y = yuv[0];
6166   uint8_t *u = yuv[1];
6167   uint8_t *v = yuv[2];
6168 
6169   register int x;
6170 
6171   for (x = 0; x < size; x++) {
6172     uyvy_2_yuv422(uyvy, y, u, v, y + 1);
6173     y += 2;
6174     u++;
6175     v++;
6176   }
6177 }
6178 
6179 
convert_yuyv_to_yuv422_frame(yuyv_macropixel * yuyv,int width,int height,uint8_t ** yuv)6180 static void convert_yuyv_to_yuv422_frame(yuyv_macropixel *yuyv, int width, int height, uint8_t **yuv) {
6181   int size = width * height; // y is twice this, u and v are equal
6182 
6183   uint8_t *y = yuv[0];
6184   uint8_t *u = yuv[1];
6185   uint8_t *v = yuv[2];
6186 
6187   register int x;
6188 
6189   for (x = 0; x < size; x++) {
6190     yuyv_2_yuv422(yuyv, y, u, v, y + 1);
6191     y += 2;
6192     u++;
6193     v++;
6194   }
6195 }
6196 
6197 
convert_yuv888_to_yuv422_frame(uint8_t * yuv8,int width,int height,int irowstride,int * ostrides,uint8_t ** yuv4,boolean has_alpha,int clamping)6198 static void convert_yuv888_to_yuv422_frame(uint8_t *yuv8, int width, int height, int irowstride, int *ostrides,
6199     uint8_t **yuv4, boolean has_alpha, int clamping) {
6200   // 888(8) packed to 422p
6201 
6202   // TODO - handle different sampling types
6203 
6204   int size = width * height; // y is equal this, u and v are half, chroma subsampled horizontally
6205 
6206   uint8_t *y = yuv4[0];
6207   uint8_t *u = yuv4[1];
6208   uint8_t *v = yuv4[2];
6209 
6210   register int x, i, j;
6211 
6212   int offs = 0;
6213   size_t ipsize;
6214 
6215   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
6216 
6217   if (has_alpha) offs = 1;
6218 
6219   ipsize = (3 + offs) << 1;
6220 
6221   if ((irowstride << 1) == width * ipsize && ostrides[0] == width && ostrides[1] == (width >> 1)) {
6222     for (x = 0; x < size; x += 2) {
6223       *(y++) = yuv8[0];
6224       *(y++) = yuv8[3 + offs];
6225       *(u++) = avg_chromaf(yuv8[1], yuv8[4 + offs]);
6226       *(v++) = avg_chromaf(yuv8[2], yuv8[5 + offs]);
6227       yuv8 += ipsize;
6228     }
6229   } else {
6230     width >>= 1;
6231     irowstride -= width * ipsize;
6232     ostrides[0] -= width;
6233     ostrides[1] -= width >> 1;
6234     ostrides[2] -= width >> 1;
6235 
6236     for (i = 0; i < height; i++) {
6237       for (j = 0; j < width; j++) {
6238         *(y++) = yuv8[0];
6239         *(y++) = yuv8[3 + offs];
6240         *(u++) = avg_chromaf(yuv8[1], yuv8[4 + offs]);
6241         *(v++) = avg_chromaf(yuv8[2], yuv8[5 + offs]);
6242         yuv8 += ipsize;
6243       }
6244       yuv8 += irowstride;
6245       y += ostrides[0];
6246       u += ostrides[1];
6247       v += ostrides[2];
6248     }
6249   }
6250 }
6251 
6252 
convert_yuv888_to_uyvy_frame(uint8_t * yuv,int width,int height,int irowstride,int orow,uyvy_macropixel * uyvy,boolean has_alpha,int clamping)6253 static void convert_yuv888_to_uyvy_frame(uint8_t *yuv, int width, int height, int irowstride, int orow,
6254     uyvy_macropixel *uyvy, boolean has_alpha, int clamping) {
6255   int size = width * height;
6256 
6257   register int x, i, j;
6258 
6259   int offs = 0;
6260   size_t ipsize;
6261 
6262   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
6263 
6264   if (has_alpha) offs = 1;
6265 
6266   ipsize = (3 + offs) << 1;
6267 
6268   if ((irowstride << 1) == width * ipsize && (width << 1) == orow) {
6269     for (x = 0; x < size; x += 2) {
6270       uyvy->u0 = avg_chromaf(yuv[1], yuv[4 + offs]);
6271       uyvy->y0 = yuv[0];
6272       uyvy->v0 = avg_chromaf(yuv[2], yuv[5 + offs]);
6273       uyvy->y1 = yuv[3 + offs];
6274       yuv += ipsize;
6275       uyvy++;
6276     }
6277   } else {
6278     orow -= width << 1;
6279     width >>= 1;
6280     irowstride -= width * ipsize;
6281     for (i = 0; i < height; i++) {
6282       for (j = 0; j < width; j++) {
6283         uyvy->u0 = avg_chromaf(yuv[1], yuv[4 + offs]);
6284         uyvy->y0 = yuv[0];
6285         uyvy->v0 = avg_chromaf(yuv[2], yuv[5 + offs]);
6286         uyvy->y1 = yuv[3 + offs];
6287         yuv += ipsize;
6288         uyvy++;
6289       }
6290       uyvy += orow;
6291       yuv += irowstride;
6292     }
6293   }
6294 }
6295 
6296 
convert_yuv888_to_yuyv_frame(uint8_t * yuv,int width,int height,int irowstride,int orow,yuyv_macropixel * yuyv,boolean has_alpha,int clamping)6297 static void convert_yuv888_to_yuyv_frame(uint8_t *yuv, int width, int height, int irowstride, int orow,
6298     yuyv_macropixel *yuyv, boolean has_alpha, int clamping) {
6299   int size = width * height;
6300 
6301   register int x, i, j;
6302 
6303   int offs = 0;
6304   size_t ipsize;
6305 
6306   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
6307 
6308   if (has_alpha) offs = 1;
6309 
6310   ipsize = (3 + offs) << 1;
6311 
6312   if (irowstride << 1 == width * ipsize && (width << 1) == orow) {
6313     for (x = 0; x < size; x += 2) {
6314       yuyv->y0 = yuv[0];
6315       yuyv->u0 = avg_chromaf(yuv[1], yuv[4 + offs]);
6316       yuyv->y1 = yuv[3 + offs];
6317       yuyv->v0 = avg_chromaf(yuv[2], yuv[5 + offs]);
6318       yuv += ipsize;
6319       yuyv++;
6320     }
6321   } else {
6322     orow -= width << 1;
6323     width >>= 1;
6324     irowstride -= width * ipsize;
6325     for (i = 0; i < height; i++) {
6326       for (j = 0; j < width; j++) {
6327         yuyv->y0 = yuv[0];
6328         yuyv->u0 = avg_chromaf(yuv[1], yuv[4 + offs]);
6329         yuyv->y1 = yuv[3 + offs];
6330         yuyv->v0 = avg_chromaf(yuv[2], yuv[5 + offs]);
6331         yuv += ipsize;
6332         yuyv++;
6333       }
6334       yuyv += orow;
6335       yuv += irowstride;
6336     }
6337   }
6338 }
6339 
6340 
convert_yuv888_to_yuv411_frame(uint8_t * yuv8,int width,int height,int irowstride,yuv411_macropixel * yuv411,boolean has_alpha)6341 static void convert_yuv888_to_yuv411_frame(uint8_t *yuv8, int width, int height, int irowstride,
6342     yuv411_macropixel *yuv411, boolean has_alpha) {
6343   // yuv 888(8) packed to yuv411. Chroma pixels are averaged.
6344 
6345   // TODO - handle different sampling types
6346 
6347   uint8_t *end = yuv8 + width * height;
6348   register int x;
6349   size_t ipsize = 3;
6350   int widtha = (width >> 1) << 1; // cut rightmost odd bytes
6351   int cbytes = width - widtha;
6352 
6353   if (has_alpha) ipsize = 4;
6354 
6355   irowstride -= widtha * ipsize;
6356 
6357   for (; yuv8 < end; yuv8 += cbytes) {
6358     for (x = 0; x < widtha; x += 4) { // process 4 input pixels for one output macropixel
6359       yuv411->u2 = (yuv8[1] + yuv8[ipsize + 1] + yuv8[2 * ipsize + 1] + yuv8[3 * ipsize + 1]) >> 2;
6360       yuv411->y0 = yuv8[0];
6361       yuv411->y1 = yuv8[ipsize];
6362       yuv411->v2 = (yuv8[2] + yuv8[ipsize + 2] + yuv8[2 * ipsize + 2] + yuv8[3 * ipsize + 2]) >> 2;
6363       yuv411->y2 = yuv8[ipsize * 2];
6364       yuv411->y3 = yuv8[ipsize * 3];
6365 
6366       yuv411++;
6367       yuv8 += ipsize * 4;
6368     }
6369     yuv8 += irowstride;
6370   }
6371 }
6372 
6373 
convert_yuv411_to_rgb_frame(yuv411_macropixel * yuv411,int width,int height,int orowstride,uint8_t * dest,boolean add_alpha,int clamping)6374 static void convert_yuv411_to_rgb_frame(yuv411_macropixel *yuv411, int width, int height, int orowstride,
6375                                         uint8_t *dest, boolean add_alpha, int clamping) {
6376   uyvy_macropixel uyvy;
6377   int m = 3, n = 4, o = 5;
6378   uint8_t u, v, h_u, h_v, q_u, q_v, y0, y1;
6379   register int j;
6380   yuv411_macropixel *end = yuv411 + width * height;
6381   size_t psize = 3, psize2;
6382 
6383   if (LIVES_UNLIKELY(!conv_YR_inited)) init_YUV_to_RGB_tables();
6384 
6385   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
6386 
6387   if (add_alpha) {
6388     m = 4;
6389     n = 5;
6390     o = 6;
6391     psize = 4;
6392   }
6393 
6394   orowstride -= width * 4 * psize;
6395   psize2 = psize << 1;
6396 
6397   while (yuv411 < end) {
6398     // write 2 RGB pixels
6399     if (add_alpha) dest[3] = dest[7] = 255;
6400 
6401     uyvy.y0 = yuv411[0].y0;
6402     uyvy.y1 = yuv411[0].y1;
6403     uyvy.u0 = yuv411[0].u2;
6404     uyvy.v0 = yuv411[0].v2;
6405     uyvy2rgb(&uyvy, &dest[0], &(dest[1]), &dest[2], &dest[m], &dest[n], &dest[o]);
6406     dest += psize2;
6407 
6408     for (j = 1; j < width; j++) {
6409       // convert 6 yuv411 bytes to 4 rgb(a) pixels
6410 
6411       // average first 2 RGB pixels of this block and last 2 RGB pixels of previous block
6412 
6413       y0 = yuv411[j - 1].y2;
6414       y1 = yuv411[j - 1].y3;
6415 
6416       h_u = avg_chromaf(yuv411[j - 1].u2, yuv411[j].u2);
6417       h_v = avg_chromaf(yuv411[j - 1].v2, yuv411[j].v2);
6418 
6419       // now we have 1/2, 1/2
6420 
6421       // average last pixel again to get 1/4, 1/2
6422 
6423       q_u = avg_chromaf(h_u, yuv411[j - 1].u2);
6424       q_v = avg_chromaf(h_v, yuv411[j - 1].v2);
6425 
6426       // average again to get 1/8, 3/8
6427 
6428       u = avg_chromaf(q_u, yuv411[j - 1].u2);
6429       v = avg_chromaf(q_v, yuv411[j - 1].v2);
6430 
6431       yuv2rgb(y0, u, v, &dest[0], &dest[1], &dest[2]);
6432 
6433       u = avg_chromaf(q_u, yuv411[j].u2);
6434       v = avg_chromaf(q_v, yuv411[j].v2);
6435 
6436       yuv2rgb(y1, u, v, &dest[m], &dest[n], &dest[o]);
6437 
6438       dest += psize2;
6439 
6440       // set first 2 RGB pixels of this block
6441 
6442       y0 = yuv411[j].y0;
6443       y1 = yuv411[j].y1;
6444 
6445       // avg to get 3/4, 1/2
6446 
6447       q_u = avg_chromaf(h_u, yuv411[j].u2);
6448       q_v = avg_chromaf(h_v, yuv411[j].v2);
6449 
6450       // average again to get 5/8, 7/8
6451 
6452       u = avg_chromaf(q_u, yuv411[j - 1].u2);
6453       v = avg_chromaf(q_v, yuv411[j - 1].v2);
6454 
6455       yuv2rgb(y0, u, v, &dest[0], &dest[1], &dest[2]);
6456 
6457       u = avg_chromaf(q_u, yuv411[j].u2);
6458       v = avg_chromaf(q_v, yuv411[j].v2);
6459 
6460       yuv2rgb(y1, u, v, &dest[m], &dest[n], &dest[o]);
6461 
6462       if (add_alpha) dest[3] = dest[7] = 255;
6463       dest += psize2;
6464 
6465     }
6466     // write last 2 pixels
6467 
6468     if (add_alpha) dest[3] = dest[7] = 255;
6469 
6470     uyvy.y0 = yuv411[j - 1].y2;
6471     uyvy.y1 = yuv411[j - 1].y3;
6472     uyvy.u0 = yuv411[j - 1].u2;
6473     uyvy.v0 = yuv411[j - 1].v2;
6474     uyvy2rgb(&uyvy, &dest[0], &(dest[1]), &dest[2], &dest[m], &dest[n], &dest[o]);
6475 
6476     dest += psize2 + orowstride;
6477     yuv411 += width;
6478   }
6479 }
6480 
6481 
convert_yuv411_to_bgr_frame(yuv411_macropixel * yuv411,int width,int height,int orowstride,uint8_t * dest,boolean add_alpha,int clamping)6482 static void convert_yuv411_to_bgr_frame(yuv411_macropixel *yuv411, int width, int height, int orowstride,
6483                                         uint8_t *dest, boolean add_alpha, int clamping) {
6484   uyvy_macropixel uyvy;
6485   int m = 3, n = 4, o = 5;
6486   uint8_t u, v, h_u, h_v, q_u, q_v, y0, y1;
6487   register int j;
6488   yuv411_macropixel *end = yuv411 + width * height;
6489   size_t psize = 3, psize2;
6490 
6491   if (LIVES_UNLIKELY(!conv_YR_inited)) init_YUV_to_RGB_tables();
6492 
6493   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
6494 
6495   if (add_alpha) {
6496     m = 4;
6497     n = 5;
6498     o = 6;
6499     psize = 4;
6500   }
6501 
6502   orowstride -= width * 4 * psize;
6503 
6504   psize2 = psize << 1;
6505 
6506   while (yuv411 < end) {
6507     // write 2 RGB pixels
6508     if (add_alpha) dest[3] = dest[7] = 255;
6509 
6510     uyvy.y0 = yuv411[0].y0;
6511     uyvy.y1 = yuv411[0].y1;
6512     uyvy.u0 = yuv411[0].u2;
6513     uyvy.v0 = yuv411[0].v2;
6514     uyvy2rgb(&uyvy, &dest[0], &(dest[1]), &dest[2], &dest[o], &dest[n], &dest[m]);
6515     dest += psize2;
6516 
6517     for (j = 1; j < width; j++) {
6518       // convert 6 yuv411 bytes to 4 rgb(a) pixels
6519 
6520       // average first 2 RGB pixels of this block and last 2 RGB pixels of previous block
6521 
6522       y0 = yuv411[j - 1].y2;
6523       y1 = yuv411[j - 1].y3;
6524 
6525       h_u = avg_chromaf(yuv411[j - 1].u2, yuv411[j].u2);
6526       h_v = avg_chromaf(yuv411[j - 1].v2, yuv411[j].v2);
6527 
6528       // now we have 1/2, 1/2
6529 
6530       // average last pixel again to get 1/4, 1/2
6531 
6532       q_u = avg_chromaf(h_u, yuv411[j - 1].u2);
6533       q_v = avg_chromaf(h_v, yuv411[j - 1].v2);
6534 
6535       // average again to get 1/8, 3/8
6536 
6537       u = avg_chromaf(q_u, yuv411[j - 1].u2);
6538       v = avg_chromaf(q_v, yuv411[j - 1].v2);
6539 
6540       yuv2bgr(y0, u, v, &dest[0], &dest[1], &dest[2]);
6541 
6542       u = avg_chromaf(q_u, yuv411[j].u2);
6543       v = avg_chromaf(q_v, yuv411[j].v2);
6544 
6545       yuv2bgr(y1, u, v, &dest[m], &dest[n], &dest[o]);
6546 
6547       dest += psize2;
6548 
6549       // set first 2 RGB pixels of this block
6550 
6551       y0 = yuv411[j].y0;
6552       y1 = yuv411[j].y1;
6553 
6554       // avg to get 3/4, 1/2
6555 
6556       q_u = avg_chromaf(h_u, yuv411[j].u2);
6557       q_v = avg_chromaf(h_v, yuv411[j].v2);
6558 
6559       // average again to get 5/8, 7/8
6560 
6561       u = avg_chromaf(q_u, yuv411[j - 1].u2);
6562       v = avg_chromaf(q_v, yuv411[j - 1].v2);
6563 
6564       yuv2bgr(y0, u, v, &dest[0], &dest[1], &dest[2]);
6565 
6566       u = avg_chromaf(q_u, yuv411[j].u2);
6567       v = avg_chromaf(q_v, yuv411[j].v2);
6568 
6569       yuv2bgr(y1, u, v, &dest[m], &dest[n], &dest[o]);
6570 
6571       if (add_alpha) dest[3] = dest[7] = 255;
6572       dest += psize2;
6573 
6574     }
6575     // write last 2 pixels
6576 
6577     if (add_alpha) dest[3] = dest[7] = 255;
6578 
6579     uyvy.y0 = yuv411[j - 1].y2;
6580     uyvy.y1 = yuv411[j - 1].y3;
6581     uyvy.u0 = yuv411[j - 1].u2;
6582     uyvy.v0 = yuv411[j - 1].v2;
6583     uyvy2rgb(&uyvy, &dest[0], &(dest[1]), &dest[2], &dest[m], &dest[n], &dest[o]);
6584 
6585     dest += psize2 + orowstride;
6586     yuv411 += width;
6587   }
6588 }
6589 
6590 
convert_yuv411_to_argb_frame(yuv411_macropixel * yuv411,int width,int height,int orowstride,uint8_t * dest,int clamping)6591 static void convert_yuv411_to_argb_frame(yuv411_macropixel *yuv411, int width, int height, int orowstride,
6592     uint8_t *dest, int clamping) {
6593   uyvy_macropixel uyvy;
6594   uint8_t u, v, h_u, h_v, q_u, q_v, y0, y1;
6595   register int j;
6596   yuv411_macropixel *end = yuv411 + width * height;
6597   size_t psize = 4, psize2;
6598 
6599   if (LIVES_UNLIKELY(!conv_YR_inited)) init_YUV_to_RGB_tables();
6600 
6601   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
6602 
6603   orowstride -= width * 4 * psize;
6604   psize2 = psize << 1;
6605 
6606   while (yuv411 < end) {
6607     // write 2 ARGB pixels
6608     dest[0] = dest[4] = 255;
6609 
6610     uyvy.y0 = yuv411[0].y0;
6611     uyvy.y1 = yuv411[0].y1;
6612     uyvy.u0 = yuv411[0].u2;
6613     uyvy.v0 = yuv411[0].v2;
6614     uyvy2rgb(&uyvy, &dest[1], &(dest[2]), &dest[3], &dest[5], &dest[6], &dest[7]);
6615     dest += psize2;
6616 
6617     for (j = 1; j < width; j++) {
6618       // convert 6 yuv411 bytes to 4 argb pixels
6619 
6620       // average first 2 ARGB pixels of this block and last 2 ARGB pixels of previous block
6621 
6622       y0 = yuv411[j - 1].y2;
6623       y1 = yuv411[j - 1].y3;
6624 
6625       h_u = avg_chromaf(yuv411[j - 1].u2, yuv411[j].u2);
6626       h_v = avg_chromaf(yuv411[j - 1].v2, yuv411[j].v2);
6627 
6628       // now we have 1/2, 1/2
6629 
6630       // average last pixel again to get 1/4, 1/2
6631 
6632       q_u = avg_chromaf(h_u, yuv411[j - 1].u2);
6633       q_v = avg_chromaf(h_v, yuv411[j - 1].v2);
6634 
6635       // average again to get 1/8, 3/8
6636 
6637       u = avg_chromaf(q_u, yuv411[j - 1].u2);
6638       v = avg_chromaf(q_v, yuv411[j - 1].v2);
6639 
6640       yuv2rgb(y0, u, v, &dest[1], &dest[2], &dest[3]);
6641 
6642       u = avg_chromaf(q_u, yuv411[j].u2);
6643       v = avg_chromaf(q_v, yuv411[j].v2);
6644 
6645       yuv2rgb(y1, u, v, &dest[5], &dest[6], &dest[7]);
6646 
6647       dest += psize2;
6648 
6649       // set first 2 ARGB pixels of this block
6650 
6651       y0 = yuv411[j].y0;
6652       y1 = yuv411[j].y1;
6653 
6654       // avg to get 3/4, 1/2
6655 
6656       q_u = avg_chromaf(h_u, yuv411[j].u2);
6657       q_v = avg_chromaf(h_v, yuv411[j].v2);
6658 
6659       // average again to get 5/8, 7/8
6660 
6661       u = avg_chromaf(q_u, yuv411[j - 1].u2);
6662       v = avg_chromaf(q_v, yuv411[j - 1].v2);
6663 
6664       yuv2rgb(y0, u, v, &dest[1], &dest[2], &dest[3]);
6665 
6666       u = avg_chromaf(q_u, yuv411[j].u2);
6667       v = avg_chromaf(q_v, yuv411[j].v2);
6668 
6669       yuv2rgb(y1, u, v, &dest[5], &dest[6], &dest[7]);
6670 
6671       dest[0] = dest[4] = 255;
6672       dest += psize2;
6673 
6674     }
6675     // write last 2 pixels
6676 
6677     dest[0] = dest[4] = 255;
6678 
6679     uyvy.y0 = yuv411[j - 1].y2;
6680     uyvy.y1 = yuv411[j - 1].y3;
6681     uyvy.u0 = yuv411[j - 1].u2;
6682     uyvy.v0 = yuv411[j - 1].v2;
6683     uyvy2rgb(&uyvy, &dest[1], &(dest[2]), &dest[3], &dest[5], &dest[6], &dest[7]);
6684 
6685     dest += psize2 + orowstride;
6686     yuv411 += width;
6687   }
6688 }
6689 
6690 
convert_yuv411_to_yuv888_frame(yuv411_macropixel * yuv411,int width,int height,uint8_t * dest,boolean add_alpha,int clamping)6691 static void convert_yuv411_to_yuv888_frame(yuv411_macropixel *yuv411, int width, int height,
6692     uint8_t *dest, boolean add_alpha, int clamping) {
6693   size_t psize = 3;
6694   register int j;
6695   yuv411_macropixel *end = yuv411 + width * height;
6696   uint8_t u, v, h_u, h_v, q_u, q_v, y0, y1;
6697 
6698   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
6699 
6700   if (add_alpha) psize = 4;
6701 
6702   while (yuv411 < end) {
6703     // write 2 RGB pixels
6704     if (add_alpha) dest[3] = dest[7] = 255;
6705 
6706     // write first 2 pixels
6707     dest[0] = yuv411[0].y0;
6708     dest[1] = yuv411[0].u2;
6709     dest[2] = yuv411[0].v2;
6710     dest += psize;
6711 
6712     dest[0] = yuv411[0].y1;
6713     dest[1] = yuv411[0].u2;
6714     dest[2] = yuv411[0].v2;
6715     dest += psize;
6716 
6717     for (j = 1; j < width; j++) {
6718       // convert 6 yuv411 bytes to 4 rgb(a) pixels
6719 
6720       // average first 2 RGB pixels of this block and last 2 RGB pixels of previous block
6721 
6722       y0 = yuv411[j - 1].y2;
6723       y1 = yuv411[j - 1].y3;
6724 
6725       h_u = avg_chromaf(yuv411[j - 1].u2, yuv411[j].u2);
6726       h_v = avg_chromaf(yuv411[j - 1].v2, yuv411[j].v2);
6727 
6728       // now we have 1/2, 1/2
6729 
6730       // average last pixel again to get 1/4, 1/2
6731 
6732       q_u = avg_chromaf(h_u, yuv411[j - 1].u2);
6733       q_v = avg_chromaf(h_v, yuv411[j - 1].v2);
6734 
6735       // average again to get 1/8, 3/8
6736 
6737       u = avg_chromaf(q_u, yuv411[j - 1].u2);
6738       v = avg_chromaf(q_v, yuv411[j - 1].v2);
6739 
6740       dest[0] = y0;
6741       dest[1] = u;
6742       dest[2] = v;
6743       if (add_alpha) dest[3] = 255;
6744 
6745       dest += psize;
6746 
6747       u = avg_chromaf(q_u, yuv411[j].u2);
6748       v = avg_chromaf(q_v, yuv411[j].v2);
6749 
6750       dest[0] = y1;
6751       dest[1] = u;
6752       dest[2] = v;
6753       if (add_alpha) dest[3] = 255;
6754 
6755       dest += psize;
6756 
6757       // set first 2 RGB pixels of this block
6758 
6759       y0 = yuv411[j].y0;
6760       y1 = yuv411[j].y1;
6761 
6762       // avg to get 3/4, 1/2
6763 
6764       q_u = avg_chromaf(h_u, yuv411[j].u2);
6765       q_v = avg_chromaf(h_v, yuv411[j].v2);
6766 
6767       // average again to get 5/8, 7/8
6768 
6769       u = avg_chromaf(q_u, yuv411[j - 1].u2);
6770       v = avg_chromaf(q_v, yuv411[j - 1].v2);
6771 
6772       dest[0] = y0;
6773       dest[1] = u;
6774       dest[2] = v;
6775 
6776       if (add_alpha) dest[3] = 255;
6777       dest += psize;
6778 
6779       u = avg_chromaf(q_u, yuv411[j].u2);
6780       v = avg_chromaf(q_v, yuv411[j].v2);
6781 
6782       dest[0] = y1;
6783       dest[1] = u;
6784       dest[2] = v;
6785 
6786       if (add_alpha) dest[3] = 255;
6787       dest += psize;
6788     }
6789     // write last 2 pixels
6790 
6791     if (add_alpha) dest[3] = dest[7] = 255;
6792 
6793     dest[0] = yuv411[j - 1].y2;
6794     dest[1] = yuv411[j - 1].u2;
6795     dest[2] = yuv411[j - 1].v2;
6796     dest += psize;
6797 
6798     dest[0] = yuv411[j - 1].y3;
6799     dest[1] = yuv411[j - 1].u2;
6800     dest[2] = yuv411[j - 1].v2;
6801 
6802     dest += psize;
6803     yuv411 += width;
6804   }
6805 }
6806 
6807 
convert_yuv411_to_yuvp_frame(yuv411_macropixel * yuv411,int width,int height,uint8_t ** dest,boolean add_alpha,int clamping)6808 static void convert_yuv411_to_yuvp_frame(yuv411_macropixel *yuv411, int width, int height, uint8_t **dest,
6809     boolean add_alpha, int clamping) {
6810   register int j;
6811   yuv411_macropixel *end = yuv411 + width * height;
6812   uint8_t u, v, h_u, h_v, q_u, q_v, y0;
6813 
6814   uint8_t *d_y = dest[0];
6815   uint8_t *d_u = dest[1];
6816   uint8_t *d_v = dest[2];
6817   uint8_t *d_a = dest[3];
6818 
6819   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
6820 
6821   while (yuv411 < end) {
6822     // write first 2 pixels
6823     *(d_y++) = yuv411[0].y0;
6824     *(d_u++) = yuv411[0].u2;
6825     *(d_v++) = yuv411[0].v2;
6826     if (add_alpha) *(d_a++) = 255;
6827 
6828     *(d_y++) = yuv411[0].y0;
6829     *(d_u++) = yuv411[0].u2;
6830     *(d_v++) = yuv411[0].v2;
6831     if (add_alpha) *(d_a++) = 255;
6832 
6833     for (j = 1; j < width; j++) {
6834       // convert 6 yuv411 bytes to 4 rgb(a) pixels
6835 
6836       // average first 2 RGB pixels of this block and last 2 RGB pixels of previous block
6837 
6838       y0 = yuv411[j - 1].y2;
6839 
6840       h_u = avg_chromaf(yuv411[j - 1].u2, yuv411[j].u2);
6841       h_v = avg_chromaf(yuv411[j - 1].v2, yuv411[j].v2);
6842 
6843       // now we have 1/2, 1/2
6844 
6845       // average last pixel again to get 1/4, 1/2
6846 
6847       q_u = avg_chromaf(h_u, yuv411[j - 1].u2);
6848       q_v = avg_chromaf(h_v, yuv411[j - 1].v2);
6849 
6850       // average again to get 1/8, 3/8
6851 
6852       u = avg_chromaf(q_u, yuv411[j - 1].u2);
6853       v = avg_chromaf(q_v, yuv411[j - 1].v2);
6854 
6855       *(d_y++) = y0;
6856       *(d_u++) = u;
6857       *(d_v++) = v;
6858       if (add_alpha) *(d_a++) = 255;
6859 
6860       u = avg_chromaf(q_u, yuv411[j].u2);
6861       v = avg_chromaf(q_v, yuv411[j].v2);
6862 
6863       *(d_y++) = y0;
6864       *(d_u++) = u;
6865       *(d_v++) = v;
6866       if (add_alpha) *(d_a++) = 255;
6867 
6868       // set first 2 RGB pixels of this block
6869 
6870       y0 = yuv411[j].y0;
6871 
6872       // avg to get 3/4, 1/2
6873 
6874       q_u = avg_chromaf(h_u, yuv411[j].u2);
6875       q_v = avg_chromaf(h_v, yuv411[j].v2);
6876 
6877       // average again to get 5/8, 7/8
6878 
6879       u = avg_chromaf(q_u, yuv411[j - 1].u2);
6880       v = avg_chromaf(q_v, yuv411[j - 1].v2);
6881 
6882       *(d_y++) = y0;
6883       *(d_u++) = u;
6884       *(d_v++) = v;
6885       if (add_alpha) *(d_a++) = 255;
6886 
6887       u = avg_chromaf(q_u, yuv411[j].u2);
6888       v = avg_chromaf(q_v, yuv411[j].v2);
6889 
6890       *(d_y++) = y0;
6891       *(d_u++) = u;
6892       *(d_v++) = v;
6893       if (add_alpha) *(d_a++) = 255;
6894     }
6895     // write last 2 pixels
6896     *(d_y++) = yuv411[j - 1].y2;
6897     *(d_u++) = yuv411[j - 1].u2;
6898     *(d_v++) = yuv411[j - 1].v2;
6899     if (add_alpha) *(d_a++) = 255;
6900 
6901     *(d_y++) = yuv411[j - 1].y3;
6902     *(d_u++) = yuv411[j - 1].u2;
6903     *(d_v++) = yuv411[j - 1].v2;
6904     if (add_alpha) *(d_a++) = 255;
6905 
6906     yuv411 += width;
6907   }
6908 }
6909 
6910 
convert_yuv411_to_uyvy_frame(yuv411_macropixel * yuv411,int width,int height,uyvy_macropixel * uyvy,int clamping)6911 static void convert_yuv411_to_uyvy_frame(yuv411_macropixel *yuv411, int width, int height,
6912     uyvy_macropixel *uyvy, int clamping) {
6913   register int j;
6914   yuv411_macropixel *end = yuv411 + width * height;
6915   uint8_t u, v, h_u, h_v, y0;
6916 
6917   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
6918 
6919   while (yuv411 < end) {
6920     // write first uyvy pixel
6921     uyvy->u0 = yuv411->u2;
6922     uyvy->y0 = yuv411->y0;
6923     uyvy->v0 = yuv411->v2;
6924     uyvy->y1 = yuv411->y1;
6925 
6926     uyvy++;
6927 
6928     for (j = 1; j < width; j++) {
6929       // convert 6 yuv411 bytes to 2 uyvy macro pixels
6930 
6931       // average first 2 RGB pixels of this block and last 2 RGB pixels of previous block
6932 
6933       y0 = yuv411[j - 1].y2;
6934 
6935       h_u = avg_chromaf(yuv411[j - 1].u2, yuv411[j].u2);
6936       h_v = avg_chromaf(yuv411[j - 1].v2, yuv411[j].v2);
6937 
6938       // now we have 1/2, 1/2
6939 
6940       // average last pixel again to get 1/4
6941 
6942       u = avg_chromaf(h_u, yuv411[j - 1].u2);
6943       v = avg_chromaf(h_v, yuv411[j - 1].v2);
6944 
6945       uyvy->u0 = u;
6946       uyvy->y0 = y0;
6947       uyvy->v0 = v;
6948       uyvy->y1 = y0;
6949 
6950       uyvy++;
6951 
6952       // average last pixel again to get 3/4
6953 
6954       u = avg_chromaf(h_u, yuv411[j].u2);
6955       v = avg_chromaf(h_v, yuv411[j].v2);
6956 
6957       // set first uyvy macropixel of this block
6958 
6959       y0 = yuv411[j].y0;
6960 
6961       uyvy->u0 = u;
6962       uyvy->y0 = y0;
6963       uyvy->v0 = v;
6964       uyvy->y1 = y0;
6965 
6966       uyvy++;
6967     }
6968     // write last uyvy macro pixel
6969     uyvy->u0 = yuv411[j - 1].u2;
6970     uyvy->y0 = yuv411[j - 1].y2;
6971     uyvy->v0 = yuv411[j - 1].v2;
6972     uyvy->y1 = yuv411[j - 1].y3;
6973 
6974     uyvy++;
6975 
6976     yuv411 += width;
6977   }
6978 }
6979 
6980 
convert_yuv411_to_yuyv_frame(yuv411_macropixel * yuv411,int width,int height,yuyv_macropixel * yuyv,int clamping)6981 static void convert_yuv411_to_yuyv_frame(yuv411_macropixel *yuv411, int width, int height, yuyv_macropixel *yuyv,
6982     int clamping) {
6983   register int j;
6984   yuv411_macropixel *end = yuv411 + width * height;
6985   uint8_t u, v, h_u, h_v, y0;
6986 
6987   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
6988 
6989   while (yuv411 < end) {
6990     // write first yuyv pixel
6991     yuyv->y0 = yuv411->y0;
6992     yuyv->u0 = yuv411->u2;
6993     yuyv->y1 = yuv411->y1;
6994     yuyv->v0 = yuv411->v2;
6995 
6996     yuyv++;
6997 
6998     for (j = 1; j < width; j++) {
6999       // convert 6 yuv411 bytes to 2 yuyv macro pixels
7000 
7001       // average first 2 RGB pixels of this block and last 2 RGB pixels of previous block
7002 
7003       y0 = yuv411[j - 1].y2;
7004 
7005       h_u = avg_chromaf(yuv411[j - 1].u2, yuv411[j].u2);
7006       h_v = avg_chromaf(yuv411[j - 1].v2, yuv411[j].v2);
7007 
7008       // now we have 1/2, 1/2
7009 
7010       // average last pixel again to get 1/4
7011 
7012       u = avg_chromaf(h_u, yuv411[j - 1].u2);
7013       v = avg_chromaf(h_v, yuv411[j - 1].v2);
7014 
7015       yuyv->y0 = y0;
7016       yuyv->u0 = u;
7017       yuyv->y1 = y0;
7018       yuyv->v0 = v;
7019 
7020       yuyv++;
7021 
7022       // average last pixel again to get 3/4
7023 
7024       u = avg_chromaf(h_u, yuv411[j].u2);
7025       v = avg_chromaf(h_v, yuv411[j].v2);
7026 
7027       // set first yuyv macropixel of this block
7028 
7029       y0 = yuv411[j].y0;
7030 
7031       yuyv->y0 = y0;
7032       yuyv->u0 = u;
7033       yuyv->y1 = y0;
7034       yuyv->v0 = v;
7035 
7036       yuyv++;
7037     }
7038     // write last yuyv macro pixel
7039     yuyv->y0 = yuv411[j - 1].y2;
7040     yuyv->u0 = yuv411[j - 1].u2;
7041     yuyv->y1 = yuv411[j - 1].y3;
7042     yuyv->v0 = yuv411[j - 1].v2;
7043 
7044     yuyv++;
7045 
7046     yuv411 += width;
7047   }
7048 }
7049 
7050 
convert_yuv411_to_yuv422_frame(yuv411_macropixel * yuv411,int width,int height,uint8_t ** dest,int clamping)7051 static void convert_yuv411_to_yuv422_frame(yuv411_macropixel *yuv411, int width, int height, uint8_t **dest, int clamping) {
7052   register int j;
7053   yuv411_macropixel *end = yuv411 + width * height;
7054   uint8_t h_u, h_v;
7055 
7056   uint8_t *d_y = dest[0];
7057   uint8_t *d_u = dest[1];
7058   uint8_t *d_v = dest[2];
7059 
7060   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
7061 
7062   while (yuv411 < end) {
7063     // write first 2 y and 1 uv pixel
7064     *(d_y++) = yuv411->y0;
7065     *(d_y++) = yuv411->y1;
7066     *(d_u++) = yuv411->u2;
7067     *(d_v++) = yuv411->v2;
7068 
7069     for (j = 1; j < width; j++) {
7070       // convert 6 yuv411 bytes to 2 yuyv macro pixels
7071 
7072       // average first 2 RGB pixels of this block and last 2 RGB pixels of previous block
7073 
7074       *(d_y++) = yuv411[j - 1].y2;
7075       *(d_y++) = yuv411[j - 1].y3;
7076 
7077       h_u = avg_chromaf(yuv411[j - 1].u2, yuv411[j].u2);
7078       h_v = avg_chromaf(yuv411[j - 1].v2, yuv411[j].v2);
7079 
7080       // now we have 1/2, 1/2
7081 
7082       // average last pixel again to get 1/4
7083 
7084       *(d_u++) = avg_chromaf(h_u, yuv411[j - 1].u2);
7085       *(d_v++) = avg_chromaf(h_v, yuv411[j - 1].v2);
7086 
7087       // average first pixel to get 3/4
7088 
7089       *(d_y++) = yuv411[j].y0;
7090       *(d_y++) = yuv411[j].y1;
7091 
7092       *(d_u++) = avg_chromaf(h_u, yuv411[j].u2);
7093       *(d_v++) = avg_chromaf(h_v, yuv411[j].v2);
7094 
7095     }
7096     // write last pixels
7097     *(d_y++) = yuv411[j - 1].y2;
7098     *(d_y++) = yuv411[j - 1].y3;
7099     *(d_u++) = yuv411[j - 1].u2;
7100     *(d_v++) = yuv411[j - 1].v2;
7101 
7102     yuv411 += width;
7103   }
7104 }
7105 
7106 
convert_yuv411_to_yuv420_frame(yuv411_macropixel * yuv411,int width,int height,uint8_t ** dest,boolean is_yvu,int clamping)7107 static void convert_yuv411_to_yuv420_frame(yuv411_macropixel *yuv411, int width, int height, uint8_t **dest,
7108     boolean is_yvu, int clamping) {
7109   register int j;
7110   yuv411_macropixel *end = yuv411 + width * height;
7111   uint8_t h_u, h_v, u, v;
7112 
7113   uint8_t *d_y = dest[0];
7114   uint8_t *d_u;
7115   uint8_t *d_v;
7116 
7117   boolean chroma = FALSE;
7118 
7119   size_t width2 = width << 1;
7120 
7121   if (!is_yvu) {
7122     d_u = dest[1];
7123     d_v = dest[2];
7124   } else {
7125     d_u = dest[2];
7126     d_v = dest[1];
7127   }
7128 
7129   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
7130 
7131   while (yuv411 < end) {
7132     // write first 2 y and 1 uv pixel
7133     *(d_y++) = yuv411->y0;
7134     *(d_y++) = yuv411->y1;
7135 
7136     u = yuv411->u2;
7137     v = yuv411->v2;
7138 
7139     if (!chroma) {
7140       *(d_u++) = u;
7141       *(d_v++) = v;
7142     } else {
7143       *d_u = avg_chromaf(*d_u, u);
7144       *d_v = avg_chromaf(*d_v, v);
7145     }
7146 
7147     for (j = 1; j < width; j++) {
7148       // convert 6 yuv411 bytes to 2 yuyv macro pixels
7149 
7150       // average first 2 RGB pixels of this block and last 2 RGB pixels of previous block
7151 
7152       *(d_y++) = yuv411[j - 1].y2;
7153       *(d_y++) = yuv411[j - 1].y3;
7154 
7155       h_u = avg_chromaf(yuv411[j - 1].u2, yuv411[j].u2);
7156       h_v = avg_chromaf(yuv411[j - 1].v2, yuv411[j].v2);
7157 
7158       // now we have 1/2, 1/2
7159 
7160       // average last pixel again to get 1/4
7161 
7162       u = avg_chromaf(h_u, yuv411[j - 1].u2);
7163       v = avg_chromaf(h_v, yuv411[j - 1].v2);
7164 
7165       if (!chroma) {
7166         *(d_u++) = u;
7167         *(d_v++) = v;
7168       } else {
7169         *d_u = avg_chromaf(*d_u, u);
7170         *d_v = avg_chromaf(*d_v, v);
7171       }
7172 
7173       // average first pixel to get 3/4
7174 
7175       *(d_y++) = yuv411[j].y0;
7176       *(d_y++) = yuv411[j].y1;
7177 
7178       u = avg_chromaf(h_u, yuv411[j].u2);
7179       v = avg_chromaf(h_v, yuv411[j].v2);
7180 
7181       if (!chroma) {
7182         *(d_u++) = u;
7183         *(d_v++) = v;
7184       } else {
7185         *d_u = avg_chromaf(*d_u, u);
7186         *d_v = avg_chromaf(*d_v, v);
7187       }
7188 
7189     }
7190 
7191     // write last pixels
7192     *(d_y++) = yuv411[j - 1].y2;
7193     *(d_y++) = yuv411[j - 1].y3;
7194 
7195     u = yuv411[j - 1].u2;
7196     v = yuv411[j - 1].v2;
7197 
7198     if (!chroma) {
7199       *(d_u++) = u;
7200       *(d_v++) = v;
7201 
7202       d_u -= width2;
7203       d_v -= width2;
7204 
7205     } else {
7206       *d_u = avg_chromaf(*d_u, u);
7207       *d_v = avg_chromaf(*d_v, v);
7208     }
7209 
7210     chroma = !chroma;
7211     yuv411 += width;
7212   }
7213 }
7214 
7215 
convert_yuv420_to_yuv411_frame(uint8_t ** src,int hsize,int vsize,yuv411_macropixel * dest,boolean is_422,int clamping)7216 static void convert_yuv420_to_yuv411_frame(uint8_t **src, int hsize, int vsize, yuv411_macropixel *dest,
7217     boolean is_422, int clamping) {
7218   // TODO -handle various sampling types
7219 
7220   register int i = 0, j;
7221   uint8_t *y, *u, *v, *end;
7222   boolean chroma = TRUE;
7223 
7224   size_t qwidth, hwidth;
7225 
7226   // TODO - handle different in sampling types
7227   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
7228 
7229   y = src[0];
7230   u = src[1];
7231   v = src[2];
7232 
7233   end = y + hsize * vsize;
7234 
7235   hwidth = hsize >> 1;
7236   qwidth = hwidth >> 1;
7237 
7238   while (y < end) {
7239     for (j = 0; j < qwidth; j++) {
7240       dest->u2 = avg_chromaf(u[0], u[1]);
7241       dest->y0 = y[0];
7242       dest->y1 = y[1];
7243       dest->v2 = avg_chromaf(v[0], v[1]);
7244       dest->y2 = y[2];
7245       dest->y3 = y[3];
7246 
7247       if (!is_422 && chroma && i > 0) {
7248         dest[-qwidth].u2 = avg_chromaf(dest[-qwidth].u2, dest->u2);
7249         dest[-qwidth].v2 = avg_chromaf(dest[-qwidth].v2, dest->v2);
7250       }
7251       dest++;
7252       y += 4;
7253       u += 2;
7254       v += 2;
7255     }
7256     chroma = !chroma;
7257     if (!chroma && !is_422) {
7258       u -= hwidth;
7259       v -= hwidth;
7260     }
7261     i++;
7262   }
7263 }
7264 
7265 
convert_splitplanes_frame(uint8_t * src,int width,int height,int irowstride,int * orowstrides,uint8_t ** dest,boolean src_alpha,boolean dest_alpha)7266 static void convert_splitplanes_frame(uint8_t *src, int width, int height, int irowstride, int *orowstrides,
7267                                       uint8_t **dest, boolean src_alpha, boolean dest_alpha) {
7268   // TODO - orowstrides
7269   // convert 888(8) packed to 444(4)P planar
7270   size_t size = width * height;
7271   int ipsize = 3;
7272 
7273   uint8_t *y = dest[0];
7274   uint8_t *u = dest[1];
7275   uint8_t *v = dest[2];
7276   uint8_t *a = dest_alpha ? dest[3] : NULL;
7277 
7278   uint8_t *end;
7279 
7280   register int i, j;
7281 
7282   if (src_alpha) ipsize = 4;
7283 
7284   if (irowstride == ipsize * width && irowstride == orowstrides[0] && irowstride == orowstrides[1]
7285       && irowstride == orowstrides[2] && (!dest_alpha || irowstride == orowstrides[3])) {
7286     for (end = src + size * ipsize; src < end;) {
7287       *(y++) = *(src++);
7288       *(u++) = *(src++);
7289       *(v++) = *(src++);
7290       if (dest_alpha) {
7291         if (src_alpha) *(a++) = *(src++);
7292         else *(a++) = 255;
7293       }
7294     }
7295   } else {
7296     orowstrides[0] -= width;
7297     orowstrides[1] -= width;
7298     orowstrides[2] -= width;
7299     width *= ipsize;
7300     irowstride -= width;
7301     if (dest_alpha) orowstrides[3] -= width;
7302     for (i = 0; i < height; i++) {
7303       for (j = 0; j < width; j += ipsize) {
7304         *(y++) = *(src++);
7305         *(u++) = *(src++);
7306         *(v++) = *(src++);
7307         if (dest_alpha) {
7308           if (src_alpha) *(a++) = *(src++);
7309           else *(a++) = 255;
7310         }
7311       }
7312       y += orowstrides[0];
7313       u += orowstrides[1];
7314       v += orowstrides[2];
7315       if (dest_alpha) {
7316         a += orowstrides[3];
7317       }
7318       src += irowstride;
7319     }
7320   }
7321 }
7322 
7323 
7324 /////////////////////////////////////////////////////////////////
7325 // RGB palette conversions
7326 
convert_swap3_frame(uint8_t * src,int width,int height,int irowstride,int orowstride,uint8_t * dest,uint8_t * gamma_lut,int thread_id)7327 static void convert_swap3_frame(uint8_t *src, int width, int height, int irowstride, int orowstride,
7328                                 uint8_t *dest, uint8_t *gamma_lut, int thread_id) {
7329   // swap 3 byte palette
7330   uint8_t *end = src + height * irowstride;
7331   register int i;
7332 
7333   if (thread_id == -1 && prefs->nfx_threads > 1) {
7334     lives_thread_t threads[prefs->nfx_threads];
7335     int nthreads = 1;
7336     int dheight, xdheight;
7337     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
7338 
7339     xdheight = CEIL((double)height / (double)prefs->nfx_threads, 4);
7340     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
7341       dheight = xdheight;
7342 
7343       if ((src + dheight * i * irowstride) < end) {
7344         ccparams[i].src = src + dheight * i * irowstride;
7345         ccparams[i].hsize = width;
7346         ccparams[i].dest = dest + dheight * i * orowstride;
7347 
7348         if (dheight * (i + 1) > (height - 4)) {
7349           dheight = height - (dheight * i);
7350         }
7351 
7352         ccparams[i].vsize = dheight;
7353 
7354         ccparams[i].irowstrides[0] = irowstride;
7355         ccparams[i].orowstrides[0] = orowstride;
7356         ccparams[i].lut = gamma_lut;
7357         ccparams[i].thread_id = i;
7358 
7359         if (i == 0) convert_swap3_frame_thread(&ccparams[i]);
7360         else {
7361           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_swap3_frame_thread, &ccparams[i]);
7362           nthreads++;
7363         }
7364       }
7365     }
7366 
7367     for (i = 1; i < nthreads; i++) {
7368       lives_thread_join(threads[i], NULL);
7369     }
7370     lives_free(ccparams);
7371     if (gamma_lut) lives_gamma_lut_free(gamma_lut);
7372     return;
7373   }
7374 
7375   if (src == dest) {
7376     uint8_t tmp;
7377     int width3 = width * 3;
7378     orowstride -= width3;
7379     for (; src < end; src += irowstride) {
7380       for (i = 0; i < width3; i += 3) {
7381         tmp = src[i];
7382         if (!gamma_lut) {
7383           dest[0] = src[i + 2]; // red
7384           dest[2] = tmp; // blue
7385         } else {
7386           dest[0] = gamma_lut[src[i + 2]]; // red
7387           dest[1] = gamma_lut[src[i + 1]]; // red
7388           dest[2] = gamma_lut[tmp]; // blue
7389         }
7390         dest += 3;
7391       }
7392       dest += orowstride;
7393     }
7394     return;
7395   }
7396 
7397   if ((irowstride == width * 3) && (orowstride == irowstride)) {
7398     // quick version
7399 #ifdef ENABLE_OIL
7400     if (!gamma_lut) {
7401       oil_rgb2bgr(dest, src, width * height);
7402       return;
7403     }
7404 #endif
7405     for (; src < end; src += 3) {
7406       if (!gamma_lut) {
7407         *(dest++) = src[2]; // red
7408         *(dest++) = src[1]; // green
7409         *(dest++) = src[0]; // blue
7410       } else {
7411         *(dest++) = gamma_lut[src[2]]; // red
7412         *(dest++) = gamma_lut[src[1]]; // green
7413         *(dest++) = gamma_lut[src[0]]; // blue
7414       }
7415     }
7416   } else {
7417     int width3 = width * 3;
7418     orowstride -= width3;
7419     for (; src < end; src += irowstride) {
7420       for (i = 0; i < width3; i += 3) {
7421         if (!gamma_lut) {
7422           *(dest++) = src[i + 2]; // red
7423           *(dest++) = src[i + 1]; // green
7424           *(dest++) = src[i]; // blue
7425         } else {
7426           *(dest++) = gamma_lut[src[2]]; // red
7427           *(dest++) = gamma_lut[src[1]]; // green
7428           *(dest++) = gamma_lut[src[0]]; // blue
7429         }
7430       }
7431       dest += orowstride;
7432     }
7433   }
7434 }
7435 
7436 
convert_swap3_frame_thread(void * data)7437 static void *convert_swap3_frame_thread(void *data) {
7438   lives_cc_params *ccparams = (lives_cc_params *)data;
7439   convert_swap3_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
7440                       ccparams->orowstrides[0], (uint8_t *)ccparams->dest, ccparams->lut, ccparams->thread_id);
7441   return NULL;
7442 }
7443 
7444 
convert_swap4_frame(uint8_t * src,int width,int height,int irowstride,int orowstride,uint8_t * dest,int thread_id)7445 static void convert_swap4_frame(uint8_t *src, int width, int height, int irowstride, int orowstride,
7446                                 uint8_t *dest, int thread_id) {
7447   // swap 4 byte palette
7448   uint8_t *end = src + height * irowstride;
7449   register int i;
7450 
7451   if (thread_id == -1 && prefs->nfx_threads > 1) {
7452     lives_thread_t threads[prefs->nfx_threads];
7453     int nthreads = 1;
7454     int dheight, xdheight;
7455     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
7456 
7457     xdheight = CEIL((double)height / (double)prefs->nfx_threads, 4);
7458     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
7459       dheight = xdheight;
7460 
7461       if ((src + dheight * i * irowstride) < end) {
7462         ccparams[i].src = src + dheight * i * irowstride;
7463         ccparams[i].hsize = width;
7464         ccparams[i].dest = dest + dheight * i * orowstride;
7465 
7466         if ((height - dheight * i) < dheight) dheight = height - (dheight * i);
7467 
7468         ccparams[i].vsize = dheight;
7469 
7470         ccparams[i].irowstrides[0] = irowstride;
7471         ccparams[i].orowstrides[0] = orowstride;
7472         ccparams[i].thread_id = i;
7473 
7474         if (i == 0) convert_swap4_frame_thread(&ccparams[i]);
7475         else {
7476           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_swap4_frame_thread, &ccparams[i]);
7477           nthreads++;
7478         }
7479       }
7480     }
7481 
7482     for (i = 1; i < nthreads; i++) {
7483       lives_thread_join(threads[i], NULL);
7484     }
7485     lives_free(ccparams);
7486     return;
7487   }
7488 
7489   if (src == dest) {
7490     uint8_t tmp[4];
7491     int width4 = width * 4;
7492     orowstride -= width4;
7493     for (; src < end; src += irowstride) {
7494       for (i = 0; i < width4; i += 4) {
7495         tmp[0] = src[i + 3]; // alpha
7496         tmp[1] = src[i + 2]; // red
7497         tmp[2] = src[i + 1]; // green
7498         tmp[3] = src[i]; // blue
7499         lives_memcpy(dest, tmp, 4);
7500         dest += 4;
7501       }
7502       dest += orowstride;
7503     }
7504     return;
7505   }
7506 
7507   if ((irowstride == width * 4) && (orowstride == irowstride)) {
7508     // quick version
7509     for (; src < end; src += 4) {
7510       *(dest++) = src[3]; // alpha
7511       *(dest++) = src[2]; // red
7512       *(dest++) = src[1]; // green
7513       *(dest++) = src[0]; // blue
7514     }
7515   } else {
7516     int width4 = width * 4;
7517     orowstride -= width4;
7518     for (; src < end; src += irowstride) {
7519       for (i = 0; i < width4; i += 4) {
7520         *(dest++) = src[i + 3]; // alpha
7521         *(dest++) = src[i + 2]; // red
7522         *(dest++) = src[i + 1]; // green
7523         *(dest++) = src[i]; // blue
7524       }
7525       dest += orowstride;
7526     }
7527   }
7528 }
7529 
7530 
convert_swap4_frame_thread(void * data)7531 static void *convert_swap4_frame_thread(void *data) {
7532   lives_cc_params *ccparams = (lives_cc_params *)data;
7533   convert_swap4_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
7534                       ccparams->orowstrides[0], (uint8_t *)ccparams->dest, ccparams->thread_id);
7535   return NULL;
7536 }
7537 
7538 
convert_swap3addpost_frame(uint8_t * src,int width,int height,int irowstride,int orowstride,uint8_t * dest,int thread_id)7539 static void convert_swap3addpost_frame(uint8_t *src, int width, int height, int irowstride, int orowstride,
7540                                        uint8_t *dest, int thread_id) {
7541   // swap 3 bytes, add post alpha
7542   uint8_t *end = src + height * irowstride;
7543   register int i;
7544 
7545   if (thread_id == -1 && prefs->nfx_threads > 1) {
7546     lives_thread_t threads[prefs->nfx_threads];
7547     int nthreads = 1;
7548     int dheight, xdheight;
7549     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
7550 
7551     xdheight = CEIL((double)height / (double)prefs->nfx_threads, 4);
7552     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
7553       dheight = xdheight;
7554 
7555       if ((src + dheight * i * irowstride) < end) {
7556         ccparams[i].src = src + dheight * i * irowstride;
7557         ccparams[i].hsize = width;
7558         ccparams[i].dest = dest + dheight * i * orowstride;
7559 
7560         if (dheight * (i + 1) > (height - 4)) {
7561           dheight = height - (dheight * i);
7562         }
7563 
7564         ccparams[i].vsize = dheight;
7565 
7566         ccparams[i].irowstrides[0] = irowstride;
7567         ccparams[i].orowstrides[0] = orowstride;
7568         ccparams[i].thread_id = i;
7569 
7570         if (i == 0) convert_swap3addpost_frame_thread(&ccparams[i]);
7571         else {
7572           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_swap3addpost_frame_thread, &ccparams[i]);
7573           nthreads++;
7574         }
7575       }
7576     }
7577 
7578     for (i = 1; i < nthreads; i++) {
7579       lives_thread_join(threads[i], NULL);
7580     }
7581     lives_free(ccparams);
7582     return;
7583   }
7584 
7585   if ((irowstride == width * 3) && (orowstride == width * 4)) {
7586     // quick version
7587     for (; src < end; src += 3) {
7588       *(dest++) = src[2]; // red
7589       *(dest++) = src[1]; // green
7590       *(dest++) = src[0]; // blue
7591       *(dest++) = 255; // alpha
7592     }
7593   } else {
7594     int width3 = width * 3;
7595     orowstride -= width * 4;
7596     for (; src < end; src += irowstride) {
7597       for (i = 0; i < width3; i += 3) {
7598         *(dest++) = src[i + 2]; // red
7599         *(dest++) = src[i + 1]; // green
7600         *(dest++) = src[i]; // blue
7601         *(dest++) = 255; // alpha
7602       }
7603       dest += orowstride;
7604     }
7605   }
7606 }
7607 
7608 
convert_swap3addpost_frame_thread(void * data)7609 static void *convert_swap3addpost_frame_thread(void *data) {
7610   lives_cc_params *ccparams = (lives_cc_params *)data;
7611   convert_swap3addpost_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
7612                              ccparams->orowstrides[0], (uint8_t *)ccparams->dest, ccparams->thread_id);
7613   return NULL;
7614 }
7615 
7616 
convert_swap3addpre_frame(uint8_t * src,int width,int height,int irowstride,int orowstride,uint8_t * dest,int thread_id)7617 static void convert_swap3addpre_frame(uint8_t *src, int width, int height, int irowstride, int orowstride,
7618                                       uint8_t *dest, int thread_id) {
7619   // swap 3 bytes, add pre alpha
7620   uint8_t *end = src + height * irowstride;
7621   register int i;
7622 
7623   if (thread_id == -1 && prefs->nfx_threads > 1) {
7624     lives_thread_t threads[prefs->nfx_threads];
7625     int nthreads = 1;
7626     int dheight, xdheight;
7627     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
7628 
7629     xdheight = CEIL((double)height / (double)prefs->nfx_threads, 4);
7630     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
7631       dheight = xdheight;
7632 
7633       if ((src + dheight * i * irowstride) < end) {
7634         ccparams[i].src = src + dheight * i * irowstride;
7635         ccparams[i].hsize = width;
7636         ccparams[i].dest = dest + dheight * i * orowstride;
7637 
7638         if (dheight * (i + 1) > (height - 4)) {
7639           dheight = height - (dheight * i);
7640         }
7641 
7642         ccparams[i].vsize = dheight;
7643 
7644         ccparams[i].irowstrides[0] = irowstride;
7645         ccparams[i].orowstrides[0] = orowstride;
7646         ccparams[i].thread_id = i;
7647 
7648         if (i == 0) convert_swap3addpre_frame_thread(&ccparams[i]);
7649         else {
7650           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_swap3addpre_frame_thread, &ccparams[i]);
7651           nthreads++;
7652         }
7653       }
7654     }
7655 
7656     for (i = 1; i < nthreads; i++) {
7657       lives_thread_join(threads[i], NULL);
7658     }
7659     lives_free(ccparams);
7660     return;
7661   }
7662   if ((irowstride == width * 3) && (orowstride == width * 4)) {
7663     // quick version
7664     for (; src < end; src += 3) {
7665       *(dest++) = 255; // alpha
7666       *(dest++) = src[2]; // red
7667       *(dest++) = src[1]; // green
7668       *(dest++) = src[0]; // blue
7669     }
7670   } else {
7671     int width3 = width * 3;
7672     orowstride -= width * 4;
7673     for (; src < end; src += irowstride) {
7674       for (i = 0; i < width3; i += 3) {
7675         *(dest++) = 255; // alpha
7676         *(dest++) = src[i + 2]; // red
7677         *(dest++) = src[i + 1]; // green
7678         *(dest++) = src[i]; // blue
7679       }
7680       dest += orowstride;
7681     }
7682   }
7683 }
7684 
7685 
convert_swap3addpre_frame_thread(void * data)7686 static void *convert_swap3addpre_frame_thread(void *data) {
7687   lives_cc_params *ccparams = (lives_cc_params *)data;
7688   convert_swap3addpre_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
7689                             ccparams->orowstrides[0], (uint8_t *)ccparams->dest, ccparams->thread_id);
7690   return NULL;
7691 }
7692 
7693 
convert_swap3postalpha_frame(uint8_t * src,int width,int height,int rowstride,int thread_id)7694 static void convert_swap3postalpha_frame(uint8_t *src, int width, int height, int rowstride,
7695     int thread_id) {
7696   // swap 3 bytes, leave alpha
7697   uint8_t *end = src + height * rowstride, tmp;
7698   register int i;
7699 
7700   if (thread_id == -1 && prefs->nfx_threads > 1) {
7701     lives_thread_t threads[prefs->nfx_threads];
7702     int nthreads = 1;
7703     int dheight, xdheight;
7704     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
7705 
7706     xdheight = CEIL((double)height / (double)prefs->nfx_threads, 4);
7707     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
7708       dheight = xdheight;
7709 
7710       if ((src + dheight * i * rowstride) < end) {
7711         ccparams[i].src = src + dheight * i * rowstride;
7712         ccparams[i].hsize = width;
7713 
7714         if (dheight * (i + 1) > (height - 4)) {
7715           dheight = height - (dheight * i);
7716         }
7717 
7718         ccparams[i].vsize = dheight;
7719 
7720         ccparams[i].irowstrides[0] = rowstride;
7721         ccparams[i].thread_id = i;
7722 
7723         if (i == 0) convert_swap3postalpha_frame_thread(&ccparams[i]);
7724         else {
7725           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_swap3postalpha_frame_thread, &ccparams[i]);
7726           nthreads++;
7727         }
7728       }
7729     }
7730 
7731     for (i = 1; i < nthreads; i++) {
7732       lives_thread_join(threads[i], NULL);
7733     }
7734     lives_free(ccparams);
7735     return;
7736   }
7737 
7738   rowstride -= width << 2;
7739   for (; src < end; src += rowstride) {
7740     for (i = 0; i < width; i++) {
7741       tmp = src[0];
7742       src[0] = src[2];
7743       src[2] = tmp;
7744       src += 4;
7745     }
7746   }
7747 }
7748 
7749 
convert_swap3postalpha_frame_thread(void * data)7750 static void *convert_swap3postalpha_frame_thread(void *data) {
7751   lives_cc_params *ccparams = (lives_cc_params *)data;
7752   convert_swap3postalpha_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
7753                                ccparams->thread_id);
7754   return NULL;
7755 }
7756 
7757 #ifdef WEED_ADVANCED_PALETTES
convert_swap3prealpha_frame(uint8_t * src,int width,int height,int rowstride,int thread_id)7758 static void convert_swap3prealpha_frame(uint8_t *src, int width, int height, int rowstride,
7759                                         int thread_id) {
7760   // swap 3 bytes, leave alpha
7761   uint8_t *end = src + height * rowstride, tmp;
7762   register int i;
7763 
7764   if (thread_id == -1 && prefs->nfx_threads > 1) {
7765     lives_thread_t threads[prefs->nfx_threads];
7766     int nthreads = 1;
7767     int dheight, xdheight;
7768     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
7769 
7770     xdheight = CEIL((double)height / (double)prefs->nfx_threads, 4);
7771     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
7772       dheight = xdheight;
7773 
7774       if ((src + dheight * i * rowstride) < end) {
7775         ccparams[i].src = src + dheight * i * rowstride;
7776         ccparams[i].hsize = width;
7777 
7778         if (dheight * (i + 1) > (height - 4)) {
7779           dheight = height - (dheight * i);
7780         }
7781 
7782         ccparams[i].vsize = dheight;
7783 
7784         ccparams[i].irowstrides[0] = rowstride;
7785         ccparams[i].thread_id = i;
7786 
7787         if (i == 0) convert_swap3prealpha_frame_thread(&ccparams[i]);
7788         else {
7789           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_swap3prealpha_frame_thread, &ccparams[i]);
7790           nthreads++;
7791         }
7792       }
7793     }
7794 
7795     for (i = 1; i < nthreads; i++) {
7796       lives_thread_join(threads[i], NULL);
7797     }
7798     lives_free(ccparams);
7799     return;
7800   }
7801 
7802   rowstride -= width << 2;
7803 
7804   for (; src < end; src += rowstride) {
7805     for (i = 0; i < width; i++) {
7806       tmp = src[1];
7807       src[1] = src[3];
7808       src[3] = tmp;
7809       src += 4;
7810     }
7811   }
7812 }
7813 
7814 
convert_swap3prealpha_frame_thread(void * data)7815 static void *convert_swap3prealpha_frame_thread(void *data) {
7816   lives_cc_params *ccparams = (lives_cc_params *)data;
7817   convert_swap3prealpha_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize,
7818                               ccparams->irowstrides[0], ccparams->thread_id);
7819   return NULL;
7820 }
7821 #endif
7822 
convert_addpost_frame(uint8_t * src,int width,int height,int irowstride,int orowstride,uint8_t * dest,uint8_t * gamma_lut,int thread_id)7823 static void convert_addpost_frame(uint8_t *src, int width, int height, int irowstride, int orowstride,
7824                                   uint8_t *dest, uint8_t *gamma_lut, int thread_id) {
7825   // add post alpha
7826   uint8_t *end = src + height * irowstride;
7827   register int i;
7828 
7829   if (thread_id == -1 && prefs->nfx_threads > 1) {
7830     lives_thread_t threads[prefs->nfx_threads];
7831     int nthreads = 1;
7832     int dheight, xdheight;
7833     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
7834 
7835     xdheight = CEIL((double)height / (double)prefs->nfx_threads, 4);
7836     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
7837       dheight = xdheight;
7838 
7839       if ((src + dheight * i * irowstride) < end) {
7840         ccparams[i].src = src + dheight * i * irowstride;
7841         ccparams[i].hsize = width;
7842         ccparams[i].dest = dest + dheight * i * orowstride;
7843 
7844         if (dheight * (i + 1) > (height - 4)) {
7845           dheight = height - (dheight * i);
7846         }
7847 
7848         ccparams[i].vsize = dheight;
7849 
7850         ccparams[i].irowstrides[0] = irowstride;
7851         ccparams[i].orowstrides[0] = orowstride;
7852         ccparams[i].lut = gamma_lut;
7853         ccparams[i].thread_id = i;
7854 
7855         if (i == 0) convert_addpost_frame_thread(&ccparams[i]);
7856         else {
7857           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_addpost_frame_thread, &ccparams[i]);
7858           nthreads++;
7859         }
7860       }
7861     }
7862 
7863     for (i = 1; i < nthreads; i++) {
7864       lives_thread_join(threads[i], NULL);
7865     }
7866     lives_free(ccparams);
7867     if (gamma_lut) lives_gamma_lut_free(gamma_lut);
7868     return;
7869   }
7870 
7871   if ((irowstride == width * 3) && (orowstride == width * 4)) {
7872     // quick version
7873 #ifdef ENABLE_OIL
7874     if (!gamma_lut) {
7875       oil_rgb2rgba(dest, src, width * height);
7876       return;
7877     }
7878 #endif
7879     for (; src < end; src += 3) {
7880       if (!gamma_lut) {
7881         lives_memcpy(dest, src, 3);
7882         dest += 3;
7883       } else {
7884         *(dest++) = gamma_lut[src[0]];
7885         *(dest++) = gamma_lut[src[1]];
7886         *(dest++) = gamma_lut[src[2]];
7887       }
7888       *(dest++) = 255; // alpha
7889     }
7890   } else {
7891     int width3 = width * 3;
7892     orowstride -= width * 4;
7893     for (; src < end; src += irowstride) {
7894       for (i = 0; i < width3; i += 3) {
7895         if (!gamma_lut) {
7896           lives_memcpy(dest, src + i, 3);
7897           dest += 3;
7898         } else {
7899           *(dest++) = gamma_lut[src[i]];
7900           *(dest++) = gamma_lut[src[i + 1]];
7901           *(dest++) = gamma_lut[src[i + 2]];
7902         }
7903         *(dest++) = 255; // alpha
7904       }
7905       dest += orowstride;
7906     }
7907   }
7908 }
7909 
7910 
convert_addpost_frame_thread(void * data)7911 static void *convert_addpost_frame_thread(void *data) {
7912   lives_cc_params *ccparams = (lives_cc_params *)data;
7913   convert_addpost_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
7914                         ccparams->orowstrides[0], (uint8_t *)ccparams->dest, ccparams->lut, ccparams->thread_id);
7915   return NULL;
7916 }
7917 
7918 
convert_addpre_frame(uint8_t * src,int width,int height,int irowstride,int orowstride,uint8_t * dest,int thread_id)7919 static void convert_addpre_frame(uint8_t *src, int width, int height, int irowstride, int orowstride,
7920                                  uint8_t *dest, int thread_id) {
7921   // add pre alpha
7922   uint8_t *end = src + height * irowstride;
7923   register int i;
7924 
7925   if (thread_id == -1 && prefs->nfx_threads > 1) {
7926     lives_thread_t threads[prefs->nfx_threads];
7927     int nthreads = 1;
7928     int dheight, xdheight;
7929     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
7930 
7931     xdheight = CEIL((double)height / (double)prefs->nfx_threads, 4);
7932     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
7933       dheight = xdheight;
7934 
7935       if ((src + dheight * i * irowstride) < end) {
7936         ccparams[i].src = src + dheight * i * irowstride;
7937         ccparams[i].hsize = width;
7938         ccparams[i].dest = dest + dheight * i * orowstride;
7939 
7940         if (dheight * (i + 1) > (height - 4)) {
7941           dheight = height - (dheight * i);
7942         }
7943 
7944         ccparams[i].vsize = dheight;
7945 
7946         ccparams[i].irowstrides[0] = irowstride;
7947         ccparams[i].orowstrides[0] = orowstride;
7948         ccparams[i].thread_id = i;
7949 
7950         if (i == 0) convert_addpre_frame_thread(&ccparams[i]);
7951         else {
7952           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_addpre_frame_thread, &ccparams[i]);
7953           nthreads++;
7954         }
7955       }
7956     }
7957 
7958     for (i = 1; i < nthreads; i++) {
7959       lives_thread_join(threads[i], NULL);
7960     }
7961     lives_free(ccparams);
7962     return;
7963   }
7964 
7965   if ((irowstride == width * 3) && (orowstride == width * 4)) {
7966     // quick version
7967     for (; src < end; src += 3) {
7968       *(dest++) = 255; // alpha
7969       lives_memcpy(dest, src, 3);
7970       dest += 3;
7971     }
7972   } else {
7973     int width3 = width * 3;
7974     orowstride -= width * 4;
7975     for (; src < end; src += irowstride) {
7976       for (i = 0; i < width3; i += 3) {
7977         *(dest++) = 255; // alpha
7978         lives_memcpy(dest, src + i, 3);
7979         dest += 3;
7980       }
7981       dest += orowstride;
7982     }
7983   }
7984 }
7985 
7986 
convert_addpre_frame_thread(void * data)7987 static void *convert_addpre_frame_thread(void *data) {
7988   lives_cc_params *ccparams = (lives_cc_params *)data;
7989   convert_addpre_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
7990                        ccparams->orowstrides[0], (uint8_t *)ccparams->dest, ccparams->thread_id);
7991   return NULL;
7992 }
7993 
7994 
convert_swap3delpost_frame(uint8_t * src,int width,int height,int irowstride,int orowstride,uint8_t * dest,int thread_id)7995 static void convert_swap3delpost_frame(uint8_t *src, int width, int height, int irowstride, int orowstride,
7996                                        uint8_t *dest, int thread_id) {
7997   // swap 3 bytes, delete post alpha
7998   uint8_t *end = src + height * irowstride;
7999   register int i;
8000 
8001   if (thread_id == -1 && prefs->nfx_threads > 1) {
8002     lives_thread_t threads[prefs->nfx_threads];
8003     int nthreads = 1;
8004     int dheight, xdheight;
8005     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
8006 
8007     xdheight = CEIL((double)height / (double)prefs->nfx_threads, 4);
8008     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
8009       dheight = xdheight;
8010 
8011       if ((src + dheight * i * irowstride) < end) {
8012         ccparams[i].src = src + dheight * i * irowstride;
8013         ccparams[i].hsize = width;
8014         ccparams[i].dest = dest + dheight * i * orowstride;
8015 
8016         if (dheight * (i + 1) > (height - 4)) {
8017           dheight = height - (dheight * i);
8018         }
8019 
8020         ccparams[i].vsize = dheight;
8021 
8022         ccparams[i].irowstrides[0] = irowstride;
8023         ccparams[i].orowstrides[0] = orowstride;
8024         ccparams[i].thread_id = i;
8025 
8026         if (i == 0) convert_swap3delpost_frame_thread(&ccparams[i]);
8027         else {
8028           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_swap3delpost_frame_thread, &ccparams[i]);
8029           nthreads++;
8030         }
8031       }
8032     }
8033 
8034     for (i = 1; i < nthreads; i++) {
8035       lives_thread_join(threads[i], NULL);
8036     }
8037     lives_free(ccparams);
8038     return;
8039   }
8040 
8041   if ((irowstride == width * 4) && (orowstride == width * 3)) {
8042     // quick version
8043     for (; src < end; src += 4) {
8044       *(dest++) = src[2]; // red
8045       *(dest++) = src[1]; // green
8046       *(dest++) = src[0]; // blue
8047     }
8048   } else {
8049     int width4 = width * 4;
8050     orowstride -= width * 3;
8051     for (; src < end; src += irowstride) {
8052       for (i = 0; i < width4; i += 4) {
8053         *(dest++) = src[i + 2]; // red
8054         *(dest++) = src[i + 1]; // green
8055         *(dest++) = src[i]; // blue
8056       }
8057       dest += orowstride;
8058     }
8059   }
8060 }
8061 
8062 
convert_swap3delpost_frame_thread(void * data)8063 static void *convert_swap3delpost_frame_thread(void *data) {
8064   lives_cc_params *ccparams = (lives_cc_params *)data;
8065   convert_swap3delpost_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
8066                              ccparams->orowstrides[0], (uint8_t *)ccparams->dest, ccparams->thread_id);
8067   return NULL;
8068 }
8069 
8070 
convert_delpost_frame(uint8_t * src,int width,int height,int irowstride,int orowstride,uint8_t * dest,uint8_t * gamma_lut,int thread_id)8071 static void convert_delpost_frame(uint8_t *src, int width, int height, int irowstride, int orowstride,
8072                                   uint8_t *dest, uint8_t *gamma_lut, int thread_id) {
8073   // delete post alpha
8074   uint8_t *end = src + height * irowstride;
8075   register int i;
8076 
8077   if (thread_id == -1 && prefs->nfx_threads > 1) {
8078     lives_thread_t threads[prefs->nfx_threads];
8079     int nthreads = 1;
8080     int dheight, xdheight;
8081     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
8082 
8083     xdheight = CEIL((double)height / (double)prefs->nfx_threads, 4);
8084     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
8085       dheight = xdheight;
8086 
8087       if ((src + dheight * i * irowstride) < end) {
8088         ccparams[i].src = src + dheight * i * irowstride;
8089         ccparams[i].hsize = width;
8090         ccparams[i].dest = dest + dheight * i * orowstride;
8091 
8092         if (dheight * (i + 1) > (height - 4)) {
8093           dheight = height - (dheight * i);
8094         }
8095 
8096         ccparams[i].vsize = dheight;
8097 
8098         ccparams[i].irowstrides[0] = irowstride;
8099         ccparams[i].orowstrides[0] = orowstride;
8100         ccparams[i].lut = gamma_lut;
8101         ccparams[i].thread_id = i;
8102 
8103         if (i == 0) convert_delpost_frame_thread(&ccparams[i]);
8104         else {
8105           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_delpost_frame_thread, &ccparams[i]);
8106           nthreads++;
8107         }
8108       }
8109     }
8110 
8111     for (i = 1; i < nthreads; i++) {
8112       lives_thread_join(threads[i], NULL);
8113     }
8114     lives_free(ccparams);
8115     if (gamma_lut) lives_gamma_lut_free(gamma_lut);
8116     return;
8117   }
8118 
8119   if ((irowstride == width * 4) && (orowstride == width * 3)) {
8120     // quick version
8121     for (; src < end; src += 4) {
8122       if (!gamma_lut) {
8123         lives_memcpy(dest, src, 3);
8124         dest += 3;
8125       } else {
8126         *(dest++) = gamma_lut[src[0]];
8127         *(dest++) = gamma_lut[src[1]];
8128         *(dest++) = gamma_lut[src[2]];
8129       }
8130     }
8131   } else {
8132     int width4 = width * 4;
8133     orowstride -= width * 3;
8134     for (; src < end; src += irowstride) {
8135       for (i = 0; i < width4; i += 4) {
8136         if (!gamma_lut) {
8137           lives_memcpy(dest, src + i, 3);
8138           dest += 3;
8139         } else {
8140           *(dest++) = gamma_lut[src[i]];
8141           *(dest++) = gamma_lut[src[i + 1]];
8142           *(dest++) = gamma_lut[src[i + 2]];
8143         }
8144       }
8145       dest += orowstride;
8146     }
8147   }
8148 }
8149 
8150 
convert_delpost_frame_thread(void * data)8151 static void *convert_delpost_frame_thread(void *data) {
8152   lives_cc_params *ccparams = (lives_cc_params *)data;
8153   convert_delpost_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
8154                         ccparams->orowstrides[0], (uint8_t *)ccparams->dest, ccparams->lut, ccparams->thread_id);
8155   return NULL;
8156 }
8157 
8158 
convert_delpre_frame(uint8_t * src,int width,int height,int irowstride,int orowstride,uint8_t * dest,int thread_id)8159 static void convert_delpre_frame(uint8_t *src, int width, int height, int irowstride, int orowstride,
8160                                  uint8_t *dest, int thread_id) {
8161   // delete pre alpha
8162   uint8_t *end = src + height * irowstride;
8163   register int i;
8164 
8165   if (thread_id == -1 && prefs->nfx_threads > 1) {
8166     lives_thread_t threads[prefs->nfx_threads];
8167     int nthreads = 1;
8168     int dheight, xdheight;
8169     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
8170 
8171     xdheight = CEIL((double)height / (double)prefs->nfx_threads, 4);
8172     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
8173       dheight = xdheight;
8174 
8175       if ((src + dheight * i * irowstride) < end) {
8176         ccparams[i].src = src + dheight * i * irowstride;
8177         ccparams[i].hsize = width;
8178         ccparams[i].dest = dest + dheight * i * orowstride;
8179 
8180         if (dheight * (i + 1) > (height - 4)) {
8181           dheight = height - (dheight * i);
8182         }
8183 
8184         ccparams[i].vsize = dheight;
8185 
8186         ccparams[i].irowstrides[0] = irowstride;
8187         ccparams[i].orowstrides[0] = orowstride;
8188         ccparams[i].thread_id = i;
8189 
8190         if (i == 0) convert_delpre_frame_thread(&ccparams[i]);
8191         else {
8192           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_delpre_frame_thread, &ccparams[i]);
8193           nthreads++;
8194         }
8195       }
8196     }
8197 
8198     for (i = 1; i < nthreads; i++) {
8199       lives_thread_join(threads[i], NULL);
8200     }
8201     lives_free(ccparams);
8202     return;
8203   }
8204 
8205   src++;
8206 
8207   if ((irowstride == width * 4) && (orowstride == width * 3)) {
8208     // quick version
8209     for (; src < end; src += 4) {
8210       lives_memcpy(dest, src, 3);
8211       dest += 3;
8212     }
8213   } else {
8214     int width4 = width * 4;
8215     orowstride -= width * 3;
8216     for (; src < end; src += irowstride) {
8217       for (i = 0; i < width4; i += 4) {
8218         lives_memcpy(dest, src + i, 3);
8219         dest += 3;
8220       }
8221       dest += orowstride;
8222     }
8223   }
8224 }
8225 
8226 
convert_delpre_frame_thread(void * data)8227 static void *convert_delpre_frame_thread(void *data) {
8228   lives_cc_params *ccparams = (lives_cc_params *)data;
8229   convert_delpre_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
8230                        ccparams->orowstrides[0], (uint8_t *)ccparams->dest, ccparams->thread_id);
8231   return NULL;
8232 }
8233 
8234 
convert_swap3delpre_frame(uint8_t * src,int width,int height,int irowstride,int orowstride,uint8_t * dest,int thread_id)8235 static void convert_swap3delpre_frame(uint8_t *src, int width, int height, int irowstride, int orowstride,
8236                                       uint8_t *dest, int thread_id) {
8237   // delete pre alpha, swap last 3
8238   uint8_t *end = src + height * irowstride;
8239   register int i;
8240 
8241   if (thread_id == -1 && prefs->nfx_threads > 1) {
8242     lives_thread_t threads[prefs->nfx_threads];
8243     int nthreads = 1;
8244     int dheight, xdheight;
8245     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
8246 
8247     xdheight = CEIL((double)height / (double)prefs->nfx_threads, 4);
8248     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
8249       dheight = xdheight;
8250 
8251       if ((src + dheight * i * irowstride) < end) {
8252         ccparams[i].src = src + dheight * i * irowstride;
8253         ccparams[i].hsize = width;
8254         ccparams[i].dest = dest + dheight * i * orowstride;
8255 
8256         if (dheight * (i + 1) > (height - 4)) {
8257           dheight = height - (dheight * i);
8258         }
8259 
8260         ccparams[i].vsize = dheight;
8261 
8262         ccparams[i].irowstrides[0] = irowstride;
8263         ccparams[i].orowstrides[0] = orowstride;
8264         ccparams[i].thread_id = i;
8265 
8266         if (i == 0) convert_swap3delpre_frame_thread(&ccparams[i]);
8267         else {
8268           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_swap3delpre_frame_thread, &ccparams[i]);
8269           nthreads++;
8270         }
8271       }
8272     }
8273 
8274     for (i = 1; i < nthreads; i++) {
8275       lives_thread_join(threads[i], NULL);
8276     }
8277     lives_free(ccparams);
8278     return;
8279   }
8280 
8281   if ((irowstride == width * 4) && (orowstride == width * 3)) {
8282     // quick version
8283     for (; src < end; src += 4) {
8284       *(dest++) = src[3]; // red
8285       *(dest++) = src[2]; // green
8286       *(dest++) = src[1]; // blue
8287     }
8288   } else {
8289     int width4 = width * 4;
8290     orowstride -= width * 3;
8291     for (; src < end; src += irowstride) {
8292       for (i = 0; i < width4; i += 4) {
8293         *(dest++) = src[i + 3]; // red
8294         *(dest++) = src[i + 2]; // green
8295         *(dest++) = src[i + 1]; // blue
8296       }
8297       dest += orowstride;
8298     }
8299   }
8300 }
8301 
8302 
convert_swap3delpre_frame_thread(void * data)8303 static void *convert_swap3delpre_frame_thread(void *data) {
8304   lives_cc_params *ccparams = (lives_cc_params *)data;
8305   convert_swap3delpre_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
8306                             ccparams->orowstrides[0], (uint8_t *)ccparams->dest, ccparams->thread_id);
8307   return NULL;
8308 }
8309 
8310 
convert_swapprepost_frame(uint8_t * src,int width,int height,int irowstride,int orowstride,uint8_t * dest,int thread_id)8311 static void convert_swapprepost_frame(uint8_t *src, int width, int height, int irowstride, int orowstride,
8312                                       uint8_t *dest, int thread_id) {
8313   // swap first and last bytes in a 4 byte palette
8314   uint64_t *uup;
8315   uint8_t *end = src + height * irowstride;
8316   register int i;
8317 
8318   if (thread_id == -1 && prefs->nfx_threads > 1) {
8319     lives_thread_t threads[prefs->nfx_threads];
8320     int nthreads = 1;
8321     int dheight, xdheight;
8322     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
8323 
8324     xdheight = CEIL((double)height / (double)prefs->nfx_threads, 4);
8325     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
8326       dheight = xdheight;
8327 
8328       if ((src + dheight * i * irowstride) < end) {
8329         ccparams[i].src = src + dheight * i * irowstride;
8330         ccparams[i].hsize = width;
8331         ccparams[i].dest = dest + dheight * i * orowstride;
8332 
8333         if (dheight * (i + 1) > (height - 4)) {
8334           dheight = height - (dheight * i);
8335         }
8336 
8337         ccparams[i].vsize = dheight;
8338 
8339         ccparams[i].irowstrides[0] = irowstride;
8340         ccparams[i].orowstrides[0] = orowstride;
8341         ccparams[i].thread_id = i;
8342 
8343         if (i == 0) convert_swapprepost_frame_thread(&ccparams[i]);
8344         else {
8345           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_swapprepost_frame_thread, &ccparams[i]);
8346           nthreads++;
8347         }
8348       }
8349     }
8350 
8351     for (i = 1; i < nthreads; i++) {
8352       lives_thread_join(threads[i], NULL);
8353     }
8354     lives_free(ccparams);
8355     return;
8356   }
8357 
8358   uup = (uint64_t *)src;
8359   if ((void *)uup == (void *)src) {
8360     uint64_t uu;
8361     int width8 = width >> 3;
8362     orowstride -= width * 4;
8363     for (; src < end; src += irowstride) {
8364       for (i = 0; i < width8; i++) {
8365         uu = ((*uup & 0xFF000000FF000000) >> 24);
8366         uu |= ((*uup & 0x00FFFFFF00FFFFFF) << 8);
8367         lives_memcpy(dest, &uu, 8);
8368         dest += 8;
8369         uup++;
8370       }
8371       dest += orowstride;
8372     }
8373     return;
8374   }
8375 
8376   if (src == dest) {
8377     uint8_t tmp;
8378     int width4 = width << 2;
8379     orowstride -= width4;
8380     for (; src < end; src += irowstride) {
8381       for (i = 0; i < width4; i += 4) {
8382         tmp = dest[i];
8383         lives_memmove(&dest[i], &dest[i + 1], 3);
8384         dest[i + 3] = tmp;
8385       }
8386       dest += orowstride;
8387     }
8388     return;
8389   } else {
8390     uint8_t tmp;
8391     int width4 = width << 2;
8392     orowstride -= width4;
8393     for (; src < end; src += irowstride) {
8394       for (i = 0; i < width4; i += 4) {
8395         tmp = src[i];
8396         lives_memcpy(&dest[i], &src[i + 1], 3);
8397         dest[i + 3] = tmp;
8398       }
8399       dest += orowstride;
8400     }
8401   }
8402 }
8403 
8404 
convert_swapprepost_frame_thread(void * data)8405 static void *convert_swapprepost_frame_thread(void *data) {
8406   lives_cc_params *ccparams = (lives_cc_params *)data;
8407   convert_swapprepost_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize, ccparams->irowstrides[0],
8408                             ccparams->orowstrides[0], (uint8_t *)ccparams->dest, ccparams->thread_id);
8409   return NULL;
8410 }
8411 
8412 
8413 //////////////////////////
8414 // genric YUV
8415 
convert_swab_frame(uint8_t * src,int width,int height,int irow,int orow,uint8_t * dest,int thread_id)8416 static void convert_swab_frame(uint8_t *src, int width, int height, int irow, int orow, uint8_t *dest, int thread_id) {
8417   register int i;
8418   int width4 = width * 4;
8419   uint8_t *end = src + height * irow;
8420 
8421   if (thread_id == -1 && prefs->nfx_threads > 1) {
8422     lives_thread_t threads[prefs->nfx_threads];
8423     int nthreads = 1;
8424     int dheight, xdheight;
8425     lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(prefs->nfx_threads, sizeof(lives_cc_params));
8426 
8427     xdheight = CEIL((double)height / (double)prefs->nfx_threads, 4);
8428     for (i = prefs->nfx_threads - 1; i >= 0; i--) {
8429       dheight = xdheight;
8430 
8431       if ((src + dheight * i * width4) < end) {
8432         ccparams[i].src = src + dheight * i * irow;
8433         ccparams[i].hsize = width;
8434         ccparams[i].dest = dest + dheight * i * orow;
8435 
8436         if (dheight * (i + 1) > (height - 4)) {
8437           dheight = height - (dheight * i);
8438         }
8439 
8440         ccparams[i].vsize = dheight;
8441         ccparams[i].irowstrides[0] = irow;
8442         ccparams[i].orowstrides[0] = orow;
8443 
8444         ccparams[i].thread_id = i;
8445 
8446         if (i == 0) convert_swab_frame_thread(&ccparams[i]);
8447         else {
8448           lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, convert_swab_frame_thread, &ccparams[i]);
8449           nthreads++;
8450         }
8451       }
8452     }
8453 
8454     for (i = 1; i < nthreads; i++) {
8455       lives_thread_join(threads[i], NULL);
8456     }
8457     lives_free(ccparams);
8458     return;
8459   }
8460 
8461   for (; src < end; src += irow) {
8462     for (i = 0; i < width4; i += 4) {
8463       swab4(&src[i], &dest[i], 1);
8464     }
8465     dest += orow;
8466   }
8467 }
8468 
8469 
convert_swab_frame_thread(void * data)8470 static void *convert_swab_frame_thread(void *data) {
8471   lives_cc_params *ccparams = (lives_cc_params *)data;
8472   convert_swab_frame((uint8_t *)ccparams->src, ccparams->hsize, ccparams->vsize,
8473                      ccparams->irowstrides[0], ccparams->orowstrides[0],
8474                      (uint8_t *)ccparams->dest, ccparams->thread_id);
8475   return NULL;
8476 }
8477 
8478 
convert_halve_chroma(uint8_t ** src,int width,int height,int * istrides,int * ostrides,uint8_t ** dest,int clamping)8479 static void convert_halve_chroma(uint8_t **src, int width, int height, int *istrides, int *ostrides, uint8_t **dest,
8480                                  int clamping) {
8481   // width and height here are width and height of src *chroma* planes, in bytes
8482 
8483   // halve the chroma samples vertically, with sub-sampling, e.g. 422p to 420p
8484 
8485   // TODO : handle different sampling methods in and out
8486 
8487   register int i, j;
8488   uint8_t *d_u = dest[1], *d_v = dest[2], *s_u = src[1], *s_v = src[2];
8489   boolean chroma = FALSE;
8490 
8491   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
8492 
8493   for (i = 0; i < height; i++) {
8494     for (j = 0; j < width; j++) {
8495       if (!chroma) {
8496         // pass 1, copy row
8497         lives_memcpy(d_u, s_u, width);
8498         lives_memcpy(d_v, s_v, width);
8499       } else {
8500         // pass 2
8501         // average two dest rows
8502         d_u[j] = avg_chromaf(d_u[j], s_u[j]);
8503         d_v[j] = avg_chromaf(d_v[j], s_v[j]);
8504       }
8505     }
8506     if (chroma) {
8507       d_u += ostrides[1];
8508       d_v += ostrides[2];
8509     }
8510     chroma = !chroma;
8511     s_u += istrides[1];
8512     s_v += istrides[2];
8513   }
8514 }
8515 
8516 
convert_double_chroma(uint8_t ** src,int width,int height,int * istrides,int * ostrides,uint8_t ** dest,int clamping)8517 static void convert_double_chroma(uint8_t **src, int width, int height, int *istrides, int *ostrides, uint8_t **dest,
8518                                   int clamping) {
8519   // width and height here are width and height of src *chroma* planes, in bytes
8520   // double two chroma planes vertically, with interpolation: eg: 420p to 422p
8521 
8522   register int i, j;
8523   uint8_t *d_u = dest[1], *d_v = dest[2], *s_u = src[1], *s_v = src[2];
8524   boolean chroma = FALSE;
8525   int height2 = height << 1;
8526 
8527   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
8528 
8529   for (i = 0; i < height2; i++) {
8530     for (j = 0; j < width; j++) {
8531       lives_memcpy(d_u, s_u, width);
8532       lives_memcpy(d_v, s_v, width);
8533 
8534       if (!chroma && i > 0) {
8535         // pass 2
8536         // average two src rows
8537         d_u[j - ostrides[1]] = avg_chromaf(s_u[j], d_u[j - ostrides[1]]);
8538         d_v[j - ostrides[2]] = avg_chromaf(s_v[j], d_v[j - ostrides[2]]);
8539       }
8540     }
8541     if (chroma) {
8542       s_u += istrides[1];
8543       s_v += istrides[2];
8544     }
8545     chroma = !chroma;
8546     d_u += ostrides[1];
8547     d_v += ostrides[2];
8548   }
8549 }
8550 
8551 
convert_quad_chroma(uint8_t ** src,int width,int height,int * istrides,int ostride,uint8_t ** dest,boolean add_alpha,int sampling,int clamping)8552 static void convert_quad_chroma(uint8_t **src, int width, int height, int *istrides, int ostride, uint8_t **dest,
8553                                 boolean add_alpha, int sampling, int clamping) {
8554   // width and height here are width and height of dest chroma planes, in bytes
8555   // double the chroma samples vertically and horizontally, with interpolation
8556   // output to planes, eg. 420p to 444p
8557 
8558   register int i, j;
8559   uint8_t *d_u = dest[1], *d_v = dest[2], *s_u = src[1], *s_v = src[2];
8560   int uv_offs, lastrow;
8561 
8562   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
8563 
8564   width = (width >> 1) << 1;
8565   lastrow = (height >> 1) << 1;
8566 
8567   for (i = 0; i < height; i++) {
8568     uv_offs = 0;
8569     for (j = 0; j < width; j++) {
8570       if (!(i & 1) || i == lastrow) {
8571         if (uv_offs > 0) {
8572           // uv supersampling
8573           if (sampling == WEED_YUV_SAMPLING_JPEG) {
8574             d_u[j] = avg_chromaf(s_u[uv_offs - 1], s_u[uv_offs]);
8575             d_v[j] = avg_chromaf(s_v[uv_offs - 1], s_v[uv_offs]);
8576           } else {
8577             d_u[j] = avg_chroma_3_1f(s_u[uv_offs - 1], s_u[uv_offs]);
8578             d_v[j] = avg_chroma_1_3f(s_v[uv_offs - 1], s_v[uv_offs]);
8579           }
8580         } else {
8581           d_u[j] = s_u[uv_offs];
8582           d_v[j] = s_v[uv_offs];
8583         }
8584         ++uv_offs;
8585         j++;
8586         if (sampling == WEED_YUV_SAMPLING_JPEG) {
8587           d_u[j] = avg_chromaf(s_u[uv_offs - 1], s_u[uv_offs]);
8588           d_v[j] = avg_chromaf(s_v[uv_offs - 1], s_v[uv_offs]);
8589         } else {
8590           d_u[j] = avg_chroma_1_3f(s_u[uv_offs - 1], s_u[uv_offs]);
8591           d_v[j] = avg_chroma_3_1f(s_v[uv_offs - 1], s_v[uv_offs]);
8592         }
8593       } else if (i > 1) {
8594         // on odd rows we average row - 1 with row - 3  ==> row - 2
8595         int jj = j - (ostride << 1);
8596         d_u[jj] = avg_chromaf(d_u[jj + ostride], d_u[jj - ostride]);
8597         d_v[jj] = avg_chromaf(d_v[jj + ostride], d_v[jj - ostride]);
8598         jj++;
8599         d_u[jj] = avg_chromaf(d_u[jj + ostride], d_u[jj - ostride]);
8600         d_v[jj] = avg_chromaf(d_v[jj + ostride], d_v[jj - ostride]);
8601       }
8602     }
8603 
8604     if (i & 1) {
8605       // after an odd row we advance u, v
8606       s_u += istrides[1];
8607       s_v += istrides[2];
8608     }
8609     d_u += ostride;
8610     d_v += ostride;
8611 
8612   }
8613   if (i > lastrow) {
8614     // TRUE if we finished on an even row
8615     for (j = 0; j < width; j++) {
8616       d_u[j - ostride * 2] = avg_chromaf(d_u[j - ostride * 3], d_u[j - ostride]);
8617       d_v[j - ostride * 2] = avg_chromaf(d_v[j - ostride * 3], d_v[j - ostride]);
8618     }
8619   }
8620   if (add_alpha) lives_memset(dest[3], 255, ostride * height);
8621 }
8622 
8623 
convert_quad_chroma_packed(uint8_t ** src,int width,int height,int * istrides,int ostride,uint8_t * dest,boolean add_alpha,int sampling,int clamping)8624 static void convert_quad_chroma_packed(uint8_t **src, int width, int height, int *istrides, int ostride,
8625                                        uint8_t *dest, boolean add_alpha, int sampling, int clamping) {
8626   // width and height here are width and height of dest chroma planes, in bytes
8627   // stretch (double) the chroma samples vertically and horizontally, with interpolation
8628   // ouput to packed pixels
8629 
8630   // e.g: 420p to 888(8)
8631 
8632   int i, j;
8633   int irow = istrides[0] - width;
8634   uint8_t *s_y = src[0], *s_u = src[1], *s_v = src[2];
8635   int opsize = 3, uv_offs;
8636   int lastrow = (height >> 1) << 1; // height if even, height - 1 if odd
8637 
8638   //int count;
8639   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
8640 
8641   if (add_alpha) opsize = 4;
8642 
8643   width = ((width >> 1) << 1) * opsize;
8644 
8645   for (i = 0; i < height; i++) {
8646     uv_offs = 0;
8647     for (j = 0; j < width; j += opsize) {
8648       // implements jpeg / mpeg style subsampling : TODO - dvpal style
8649       if (!(i & 1) || i == lastrow) {
8650         // even rows (0, 2, 4, ...) are normal
8651         dest[j] = *(s_y++);
8652         if (uv_offs > 0) {
8653           // uv supersampling
8654           if (sampling == WEED_YUV_SAMPLING_JPEG) {
8655             dest[j + 1] = avg_chromaf(s_u[uv_offs - 1], s_u[uv_offs]);
8656             dest[j + 2] = avg_chromaf(s_v[uv_offs - 1], s_v[uv_offs]);
8657           } else {
8658             dest[j + 1] = avg_chroma_3_1f(s_u[uv_offs - 1], s_u[uv_offs]);
8659             dest[j + 2] = avg_chroma_1_3f(s_v[uv_offs - 1], s_v[uv_offs]);
8660           }
8661         } else {
8662           dest[j + 1] = s_u[uv_offs];
8663           dest[j + 2] = s_v[uv_offs];
8664         }
8665 
8666         if (add_alpha) dest[j + 3] = 255;
8667         ++uv_offs;
8668         j += opsize;
8669         dest[j] = *(s_y++);
8670 
8671         if (sampling == WEED_YUV_SAMPLING_JPEG) {
8672           dest[j + 1] = avg_chromaf(s_u[uv_offs - 1], s_u[uv_offs]);
8673           dest[j + 2] = avg_chromaf(s_v[uv_offs - 1], s_v[uv_offs]);
8674         } else {
8675           dest[j + 1] = avg_chroma_1_3f(s_u[uv_offs - 1], s_u[uv_offs]);
8676           dest[j + 2] = avg_chroma_3_1f(s_v[uv_offs - 1], s_v[uv_offs]);
8677         }
8678         if (add_alpha) dest[j + 3] = 255;
8679       } else {
8680         int jj = j - (ostride << 1);
8681         // y part is normal
8682         dest[j] = *(s_y++);
8683         if (i > 1) { // i >= 3
8684           // chroma part:
8685           // on odd rows we average row - 1 with row - 3  ==> row - 2
8686           dest[jj + 1] = avg_chroma(dest[jj + 1 + ostride], dest[jj + 1 - ostride]);
8687           dest[jj + 2] = avg_chroma(dest[jj + 2 + ostride], dest[jj + 2 - ostride]);
8688           jj += opsize;
8689         }
8690         j += opsize;
8691         dest[j] = *(s_y++);
8692         if (i > 1) {
8693           dest[jj + 1] = avg_chroma(dest[jj + 1 + ostride], dest[jj + 1 - ostride]);
8694           dest[jj + 2] = avg_chroma(dest[jj + 2 + ostride], dest[jj + 2 - ostride]);
8695         }
8696       }
8697     }
8698 
8699     if (i & 1) {
8700       // after an odd row we advance u, v
8701       s_u += istrides[1];
8702       s_v += istrides[2];
8703     }
8704     // y advances on every row
8705     s_y += irow;
8706     dest += ostride;
8707   }
8708   if (i > lastrow) {
8709     // TRUE if we finished on an even row
8710     for (j = 0; j < width; j += opsize) {
8711       // we would have done this on the next row, but there is no next row
8712       int jj = j - ostride;
8713       dest[jj + 1] = avg_chromaf(dest[jj + 1 + ostride], dest[jj + 1 - ostride]);
8714       dest[jj + 2] = avg_chromaf(dest[jj + 2 + ostride], dest[jj + 2 - ostride]);
8715     }
8716   }
8717 }
8718 
8719 
convert_double_chroma_packed(uint8_t ** src,int width,int height,int * istrides,int ostride,uint8_t * dest,boolean add_alpha,int sampling,int clamping)8720 static void convert_double_chroma_packed(uint8_t **src, int width, int height, int *istrides, int ostride, uint8_t *dest,
8721     boolean add_alpha, int sampling, int clamping) {
8722   // width and height here are width and height of dest chroma planes, in bytes
8723   // double the chroma samples horizontally, with interpolation
8724 
8725   // output to packed pixels
8726 
8727   // e.g 422p to 888(8)
8728 
8729   int i, j;
8730   uint8_t *s_y = src[0], *s_u = src[1], *s_v = src[2];
8731   int irow = istrides[0] - width;
8732   int opsize = 3, uv_offs;
8733 
8734   set_conversion_arrays(clamping, WEED_YUV_SUBSPACE_YCBCR);
8735 
8736   if (add_alpha) opsize = 4;
8737   width *= opsize;
8738 
8739   for (i = 0; i < height; i++) {
8740     uv_offs = 0;
8741     for (j = 0; j < width; j += opsize) {
8742       dest[j] = *(s_y++);
8743       if (uv_offs > 0) {
8744         // uv supersampling
8745         if (sampling == WEED_YUV_SAMPLING_JPEG) {
8746           dest[j + 1] = avg_chromaf(s_u[uv_offs - 1], s_u[uv_offs]);
8747           dest[j + 2] = avg_chromaf(s_v[uv_offs - 1], s_v[uv_offs]);
8748         } else {
8749           dest[j + 1] = avg_chroma_3_1f(s_u[uv_offs - 1], s_u[uv_offs]);
8750           dest[j + 2] = avg_chroma_1_3f(s_v[uv_offs - 1], s_v[uv_offs]);
8751         }
8752       } else {
8753         dest[j + 1] = s_u[uv_offs];
8754         dest[j + 2] = s_v[uv_offs];
8755       }
8756       if (add_alpha) dest[j + 3] = 255;
8757 
8758       j += opsize;
8759       ++uv_offs;
8760 
8761       dest[j] = *(s_y++);
8762       if (uv_offs > 0) {
8763         // uv supersampling
8764         if (sampling == WEED_YUV_SAMPLING_JPEG) {
8765           dest[j + 1] = avg_chromaf(s_u[uv_offs - 1], s_u[uv_offs]);
8766           dest[j + 2] = avg_chromaf(s_v[uv_offs - 1], s_v[uv_offs]);
8767         } else {
8768           dest[j + 1] = avg_chroma_3_1f(s_u[uv_offs - 1], s_u[uv_offs]);
8769           dest[j + 2] = avg_chroma_1_3f(s_v[uv_offs - 1], s_v[uv_offs]);
8770         }
8771       } else {
8772         dest[j + 1] = s_u[uv_offs];
8773         dest[j + 2] = s_v[uv_offs];
8774       }
8775     }
8776     s_y += irow;
8777     s_u += istrides[1];
8778     s_v += istrides[2];
8779     dest += ostride;
8780   }
8781 }
8782 
8783 
switch_yuv_sampling(weed_layer_t * layer)8784 static void switch_yuv_sampling(weed_layer_t *layer) {
8785   int sampling, clamping, subspace;
8786   int palette = weed_layer_get_palette_yuv(layer, &clamping, &sampling, &subspace);
8787   int width = weed_layer_get_width(layer) >> 1;
8788   int height = weed_layer_get_height(layer) >> 1;
8789   unsigned char **pixel_data, *dst;
8790   register int i, j, k;
8791 
8792   if (palette != WEED_PALETTE_YUV420P) return;
8793 
8794   pixel_data = (unsigned char **)weed_layer_get_pixel_data(layer, NULL);
8795 
8796   if (sampling == WEED_YUV_SAMPLING_MPEG) {
8797     // jpeg is located centrally between Y, mpeg(2) and some flv are located on the left Y
8798     // so here we just set dst[0]=avg(src[0],src[1]), dst[1]=avg(src[1],src[2]), etc.
8799     // the last value is repeated once
8800 
8801     // however, I think the values alternate so u : 0, 2, 4....v: 1, 3, 5...
8802     // and we want u = 0.5, 2.5, 4.5....
8803     // so, starting from 0 u = 3/4 x + 1 /4 x + 1
8804     // and v = 1/4 x + 3/4 x + 1
8805 
8806     width--;
8807     for (k = 1; k < 3; k++) {
8808       dst = pixel_data[k];
8809       for (j = 0; j < height; j++) {
8810         for (i = 0; i < width; i++) {
8811           if (k == 1) dst[i] = avg_chroma_3_1f(dst[i], avg_chromaf(dst[i], dst[i + 1]));
8812           else dst[i] = avg_chroma_1_3f(avg_chromaf(dst[i], dst[i + 1]), dst[i + 1]);
8813         }
8814         dst += width + 1;
8815       }
8816     }
8817     weed_set_int_value(layer, WEED_LEAF_YUV_SAMPLING, WEED_YUV_SAMPLING_JPEG);
8818   } else if (sampling == WEED_YUV_SAMPLING_JPEG) {
8819     // the other way round is just the inverse
8820     width--;
8821     for (k = 1; k < 3; k++) {
8822       dst = pixel_data[k];
8823       for (j = 0; j < height; j++) {
8824         for (i = 0; i < width; i++) {
8825           if (k == 2) dst[i] = avg_chromaf(dst[i], avg_chromaf(dst[i], dst[i + 1]));
8826           else dst[i] = avg_chromaf(avg_chromaf(dst[i], dst[i + 1]), dst[i + 1]);
8827         }
8828         dst += width + 1;
8829       }
8830     }
8831     weed_set_int_value(layer, WEED_LEAF_YUV_SAMPLING, WEED_YUV_SAMPLING_MPEG);
8832   }
8833   lives_free(pixel_data);
8834 }
8835 
8836 
switch_yuv_clamping_and_subspace(weed_layer_t * layer,int oclamping,int osubspace)8837 static void switch_yuv_clamping_and_subspace(weed_layer_t *layer, int oclamping, int osubspace) {
8838   // currently subspace conversions are not performed - TODO
8839   // we assume subspace Y'CbCr
8840   int iclamping = weed_get_int_value(layer, WEED_LEAF_YUV_CLAMPING, NULL);
8841   int isubspace = weed_get_int_value(layer, WEED_LEAF_YUV_SUBSPACE, NULL);
8842 
8843   int palette = weed_get_int_value(layer, WEED_LEAF_CURRENT_PALETTE, NULL);
8844   int height = weed_get_int_value(layer, WEED_LEAF_HEIGHT, NULL);
8845   int rowstride = weed_layer_get_rowstride(layer);
8846   void **pixel_data = weed_get_voidptr_array(layer, WEED_LEAF_PIXEL_DATA, NULL);
8847 
8848   uint8_t *src, *src1, *src2, *end;
8849 
8850   get_YUV_to_YUV_conversion_arrays(iclamping, isubspace, oclamping, osubspace);
8851 
8852   switch (palette) {
8853   case WEED_PALETTE_YUVA8888:
8854     src = (uint8_t *)pixel_data[0];
8855     end = src + height * rowstride;
8856     while (src < end) {
8857       *src = Y_to_Y[*src];
8858       src++;
8859       *src = U_to_U[*src];
8860       src++;
8861       *src = V_to_V[*src];
8862       src += 2;
8863     }
8864     break;
8865   case WEED_PALETTE_YUV888:
8866     src = (uint8_t *)pixel_data[0];
8867     end = src + height * rowstride;
8868     while (src < end) {
8869       *src = Y_to_Y[*src];
8870       src++;
8871       *src = U_to_U[*src];
8872       src++;
8873       *src = V_to_V[*src];
8874       src++;
8875     }
8876     break;
8877   case WEED_PALETTE_YUVA4444P:
8878   case WEED_PALETTE_YUV444P:
8879     src = (uint8_t *)pixel_data[0];
8880     src1 = (uint8_t *)pixel_data[1];
8881     src2 = (uint8_t *)pixel_data[2];
8882     end = src + height * rowstride;
8883     while (src < end) {
8884       *src = Y_to_Y[*src];
8885       src++;
8886       *src1 = U_to_U[*src1];
8887       src1++;
8888       *src2 = V_to_V[*src2];
8889       src2++;
8890     }
8891     break;
8892   case WEED_PALETTE_UYVY:
8893     src = (uint8_t *)pixel_data[0];
8894     end = src + height * rowstride;
8895     while (src < end) {
8896       *src = U_to_U[*src];
8897       src++;
8898       *src = Y_to_Y[*src];
8899       src++;
8900       *src = V_to_V[*src];
8901       src++;
8902       *src = Y_to_Y[*src];
8903       src++;
8904     }
8905     break;
8906   case WEED_PALETTE_YUYV:
8907     src = (uint8_t *)pixel_data[0];
8908     end = src + height * rowstride;
8909     while (src < end) {
8910       *src = Y_to_Y[*src];
8911       src++;
8912       *src = U_to_U[*src];
8913       src++;
8914       *src = Y_to_Y[*src];
8915       src++;
8916       *src = V_to_V[*src];
8917       src++;
8918     }
8919     break;
8920   case WEED_PALETTE_YUV422P:
8921     src = (uint8_t *)pixel_data[0];
8922     src1 = (uint8_t *)pixel_data[1];
8923     src2 = (uint8_t *)pixel_data[2];
8924     end = src + height * rowstride;
8925     // TODO: u, v rowstrides
8926     while (src < end) {
8927       *src = Y_to_Y[*src];
8928       src++;
8929       *src = Y_to_Y[*src];
8930       src++;
8931       *src1 = U_to_U[*src1];
8932       src1++;
8933       *src2 = V_to_V[*src2];
8934       src2++;
8935     }
8936     break;
8937   case WEED_PALETTE_YVU420P:
8938     src = (uint8_t *)pixel_data[0];
8939     src1 = (uint8_t *)pixel_data[2];
8940     src2 = (uint8_t *)pixel_data[1];
8941     end = src + height * rowstride;
8942     // TODO: u, v rowstrides
8943     while (src < end) {
8944       *src = Y_to_Y[*src];
8945       src++;
8946       *src = Y_to_Y[*src];
8947       src++;
8948       *src = Y_to_Y[*src];
8949       src++;
8950       *src = Y_to_Y[*src];
8951       src++;
8952       *src1 = U_to_U[*src1];
8953       src1++;
8954       *src2 = V_to_V[*src2];
8955       src2++;
8956     }
8957     break;
8958   case WEED_PALETTE_YUV420P:
8959     src = (uint8_t *)pixel_data[0];
8960     src1 = (uint8_t *)pixel_data[1];
8961     src2 = (uint8_t *)pixel_data[2];
8962     end = src + height * rowstride;
8963     // TODO: u, v rowstrides
8964     while (src < end) {
8965       *src = Y_to_Y[*src];
8966       src++;
8967       *src = Y_to_Y[*src];
8968       src++;
8969       *src = Y_to_Y[*src];
8970       src++;
8971       *src = Y_to_Y[*src];
8972       src++;
8973       *src1 = U_to_U[*src1];
8974       src1++;
8975       *src2 = V_to_V[*src2];
8976       src2++;
8977     }
8978     break;
8979   case WEED_PALETTE_YUV411:
8980     src = (uint8_t *)pixel_data[0];
8981     end = src + height * rowstride;
8982     while (src < end) {
8983       *src = U_to_U[*src];
8984       src++;
8985       *src = Y_to_Y[*src];
8986       src++;
8987       *src = Y_to_Y[*src];
8988       src++;
8989       *src = V_to_V[*src];
8990       src++;
8991       *src = Y_to_Y[*src];
8992       src++;
8993       *src = Y_to_Y[*src];
8994       src++;
8995     }
8996     break;
8997   }
8998   weed_set_int_value(layer, WEED_LEAF_YUV_CLAMPING, oclamping);
8999   lives_free(pixel_data);
9000 }
9001 
9002 
9003 ////////////////////////////////////////////////////////////////////////////////////////
9004 // TODO - move into layers.c
9005 
9006 /**
9007    a "layer" is CHANNEL type plant which is not created from a plugin CHANNEL_TEMPLATE.
9008    When we pass this to a plugin, we need to adjust it depending
9009    on the plugin's CHANNEL_TEMPLATE to which we will assign it.
9010 
9011    e.g.: memory may need aligning afterwards for particular plugins which set channel template flags:
9012    layer palette may need changing, layer may need resizing */
9013 
9014 /**
9015    @brief fills the plane pointed to by ptr with bpix
9016 
9017    psize is sizeof(bpix), width, height and rowstride are the dimensions of the target plane
9018 */
fill_plane(uint8_t * ptr,int psize,int width,int height,int rowstride,unsigned char * bpix)9019 LIVES_INLINE void fill_plane(uint8_t *ptr, int psize, int width, int height, int rowstride, unsigned char *bpix) {
9020   register int i, j;
9021   uint8_t *ptr2 = ptr;
9022   for (j = width; j > 0; j--) {
9023     lives_memcpy(ptr2, bpix, psize);
9024     ptr2 += psize;
9025   }
9026   ptr2 += rowstride - width * psize;
9027   for (i = height - 1; i > 0; i--) {
9028     lives_memcpy(ptr2, ptr, rowstride);
9029     ptr += rowstride;
9030     ptr2 += rowstride;
9031   }
9032 }
9033 
9034 #define SHIFTVAL sbits
9035 #define ALIGN_SIZE (1 << SHIFTVAL)
9036 
9037 /**
9038    @brief creates pixel data for layer
9039 
9040    @returns FALSE on memory error
9041 
9042    width, height, and current_palette must be pre-set in layer; width is in (macro) pixels of the palette
9043    width and height may be adjusted (rounded) in the function
9044    rowstrides will be set, and each plane will be aligned depending on THREADVAR(rowstride_alignment)
9045    if THREADVAR(rowstride_alignment_hint) is non 0 it will set THREADVAR(rowstride_alignment), which must be a power of 2
9046    the special value -1 for the hint will create compact frames (rowstride = width * pixel_size)
9047 
9048    if black_fill is set, fill with opaque black in the specified palette: for yuv palettes, YUV_clamping may be pre-set
9049    otherwise it will be set to WEED_YUV_CLAMPING_CLAMPED.
9050 
9051    may_contig should normally be set to TRUE, except for special uses during palette conversion
9052    if set, then for planar palettes, only plane 0 will be allocated, so only this value should be freed
9053    in this case, the leaf WEED_LEAF_HOST_PIXEL_DATA_CONTIGUOUS will be set to WEED_TRUE
9054 
9055    the allocated frames will be aligned to the pixel size for whatever palette and may be padded with extra bytes
9056    to guard against accidental overwrites
9057 */
create_empty_pixel_data(weed_layer_t * layer,boolean black_fill,boolean may_contig)9058 boolean create_empty_pixel_data(weed_layer_t *layer, boolean black_fill, boolean may_contig) {
9059   int palette = weed_layer_get_palette(layer);
9060   int width = weed_layer_get_width(layer);
9061   int height = weed_layer_get_height(layer);
9062   int rowstride, *rowstrides;
9063   int *fixed_rs = NULL;
9064 
9065   uint32_t pflags;
9066   int clamping = WEED_YUV_CLAMPING_CLAMPED;
9067   boolean compact = FALSE;
9068 
9069   uint8_t *pixel_data = NULL;
9070   uint8_t *memblock;
9071   uint8_t **pd_array;
9072 
9073   unsigned char black[6] = {0, 0, 0, 255, 255, 255};
9074   unsigned char yuv_black[6] = {16, 128, 128, 255, 255, 255};
9075   float blackf[4] = {0., 0., 0., 1.};
9076 
9077   size_t framesize, framesize2;
9078 
9079   // max is 128 min is 32,  and it must be a power of 2 (i.e 32, 64, 128)
9080   int sbits = 7, al, r;
9081   int rowstride_alignment = 16;
9082 
9083   if (width <= 0 || height <= 0) return FALSE;
9084 
9085   if (!weed_plant_has_leaf(layer, WEED_LEAF_NATURAL_SIZE)) {
9086     int nsize[2];
9087     // set "natural_size" in case a filter needs it
9088     nsize[0] = width;
9089     nsize[1] = height;
9090     weed_set_int_array(layer, WEED_LEAF_NATURAL_SIZE, 2, nsize);
9091   }
9092 
9093   if (weed_leaf_get_flags(layer, WEED_LEAF_ROWSTRIDES) & LIVES_FLAG_MAINTAIN_VALUE) {
9094     /// force use of fixed rowstrides, eg. decoder plugin
9095     fixed_rs = weed_layer_get_rowstrides(layer, NULL);
9096   } else {
9097     if (THREADVAR(rowstride_alignment) < ALIGN_DEF) THREADVAR(rowstride_alignment) = ALIGN_DEF;
9098     rowstride_alignment = THREADVAR(rowstride_alignment);
9099 
9100     if (THREADVAR(rowstride_alignment_hint) > 0) {
9101       r = rowstride_alignment = THREADVAR(rowstride_alignment_hint);
9102       for (al = 1 << sbits; (al > ALIGN_MIN && !(al & r)); al >>= 1) sbits--;
9103       rowstride_alignment = al;
9104     }
9105     if (THREADVAR(rowstride_alignment_hint) < 0 || (weed_palette_is_alpha(palette) && THREADVAR(rowstride_alignment_hint) == 0)) {
9106       compact = TRUE;
9107       rowstride_alignment = 1;
9108     }
9109     THREADVAR(rowstride_alignment_hint) = 0;
9110 
9111     for (sbits = 7; (1 << sbits) > rowstride_alignment; sbits--);
9112   }
9113 
9114   if (weed_plant_has_leaf(layer, WEED_LEAF_HOST_PIXBUF_SRC)) {
9115     weed_leaf_delete(layer, WEED_LEAF_HOST_PIXBUF_SRC);
9116   }
9117   if (weed_plant_has_leaf(layer, WEED_LEAF_HOST_SURFACE_SRC)) {
9118     weed_leaf_delete(layer, WEED_LEAF_HOST_SURFACE_SRC);
9119   }
9120   if (weed_plant_has_leaf(layer, WEED_LEAF_HOST_SURFACE_SRC)) {
9121     weed_leaf_delete(layer, WEED_LEAF_HOST_SURFACE_SRC);
9122   }
9123   pflags = weed_leaf_get_flags(layer, WEED_LEAF_PIXEL_DATA);
9124   weed_leaf_set_flags(layer, WEED_LEAF_PIXEL_DATA, pflags & ~LIVES_FLAG_MAINTAIN_VALUE);
9125 
9126   if (black_fill) {
9127     if (weed_plant_has_leaf(layer, WEED_LEAF_YUV_CLAMPING))
9128       clamping = weed_get_int_value(layer, WEED_LEAF_YUV_CLAMPING, NULL);
9129     if (clamping != WEED_YUV_CLAMPING_CLAMPED) yuv_black[0] = 0;
9130   }
9131 
9132   switch (palette) {
9133   case WEED_PALETTE_RGBA32:
9134   case WEED_PALETTE_BGRA32:
9135   case WEED_PALETTE_ARGB32:
9136     if (fixed_rs) rowstride = fixed_rs[0];
9137     else {
9138       rowstride = width * 4;
9139       if (!compact) rowstride = ALIGN_CEIL(rowstride, rowstride_alignment);
9140     }
9141     framesize = ALIGN_CEIL(rowstride * height, ALIGN_SIZE) + EXTRA_BYTES;
9142     pixel_data = (uint8_t *)lives_calloc(framesize >> SHIFTVAL, ALIGN_SIZE);
9143     if (!pixel_data) return FALSE;
9144     weed_set_int_value(layer, WEED_LEAF_ROWSTRIDES, rowstride);
9145     weed_set_voidptr_value(layer, WEED_LEAF_PIXEL_DATA, pixel_data);
9146     if (black_fill) {
9147       if (palette == WEED_PALETTE_ARGB32) {
9148         black[3] = black[0];
9149         black[0] = 255;
9150       }
9151       fill_plane(pixel_data, 4, width, height, rowstride, black);
9152     }
9153     weed_set_int_value(layer, WEED_LEAF_ROWSTRIDES, rowstride);
9154     weed_set_voidptr_value(layer, WEED_LEAF_PIXEL_DATA, pixel_data);
9155     break;
9156 
9157   case WEED_PALETTE_RGB24:
9158   case WEED_PALETTE_BGR24:
9159     if (fixed_rs) rowstride = fixed_rs[0];
9160     else {
9161       rowstride = width * 3;
9162       if (!compact) rowstride = ALIGN_CEIL(rowstride, rowstride_alignment);
9163     }
9164     framesize = ALIGN_CEIL(rowstride * height, ALIGN_SIZE) + EXTRA_BYTES;
9165     pixel_data = (uint8_t *)lives_calloc(framesize >> SHIFTVAL, ALIGN_SIZE);
9166     if (!pixel_data) return FALSE;
9167     weed_set_voidptr_value(layer, WEED_LEAF_PIXEL_DATA, pixel_data);
9168     weed_set_int_value(layer, WEED_LEAF_ROWSTRIDES, rowstride);
9169     break;
9170 
9171   case WEED_PALETTE_YUV888:
9172     if (fixed_rs) rowstride = fixed_rs[0];
9173     else {
9174       rowstride = width * 3;
9175       if (!compact) rowstride = ALIGN_CEIL(rowstride, rowstride_alignment);
9176     }
9177     framesize = ALIGN_CEIL(rowstride * height, ALIGN_SIZE) + EXTRA_BYTES;
9178     pixel_data = (uint8_t *)lives_calloc(framesize >> SHIFTVAL, ALIGN_SIZE);
9179     if (!pixel_data) return FALSE;
9180     if (black_fill) fill_plane(pixel_data, 3, width, height, rowstride, yuv_black);
9181     weed_set_int_value(layer, WEED_LEAF_ROWSTRIDES, rowstride);
9182     weed_set_voidptr_value(layer, WEED_LEAF_PIXEL_DATA, pixel_data);
9183     break;
9184 
9185   case WEED_PALETTE_YUVA8888:
9186     if (fixed_rs) rowstride = fixed_rs[0];
9187     else {
9188       rowstride = width * 4;
9189       if (!compact) rowstride = ALIGN_CEIL(rowstride, rowstride_alignment);
9190     }
9191     framesize = ALIGN_CEIL(rowstride * height, ALIGN_SIZE) + EXTRA_BYTES;
9192     pixel_data = (uint8_t *)lives_calloc(framesize >> SHIFTVAL, ALIGN_SIZE);
9193     if (!pixel_data) return FALSE;
9194     if (black_fill) fill_plane(pixel_data, 4, width, height, rowstride, yuv_black);
9195     weed_set_int_value(layer, WEED_LEAF_ROWSTRIDES, rowstride);
9196     weed_set_voidptr_value(layer, WEED_LEAF_PIXEL_DATA, pixel_data);
9197     break;
9198 
9199   case WEED_PALETTE_UYVY:
9200     if (fixed_rs) rowstride = fixed_rs[0];
9201     else {
9202       rowstride = width * 4;
9203       if (!compact) rowstride = ALIGN_CEIL(rowstride, rowstride_alignment);
9204     }
9205     framesize = ALIGN_CEIL(rowstride * height, ALIGN_SIZE) + EXTRA_BYTES;
9206     pixel_data = (uint8_t *)lives_calloc(framesize >> SHIFTVAL, ALIGN_SIZE);
9207     if (!pixel_data) return FALSE;
9208     if (black_fill) {
9209       yuv_black[1] = yuv_black[3] = yuv_black[0];
9210       yuv_black[0] = yuv_black[2];
9211       fill_plane(pixel_data, 4, width, height, rowstride, yuv_black);
9212     }
9213     weed_set_int_value(layer, WEED_LEAF_ROWSTRIDES, rowstride);
9214     weed_set_voidptr_value(layer, WEED_LEAF_PIXEL_DATA, pixel_data);
9215     break;
9216 
9217   case WEED_PALETTE_YUYV:
9218     if (fixed_rs) rowstride = fixed_rs[0];
9219     else {
9220       rowstride = width * 4;
9221       if (!compact) rowstride = ALIGN_CEIL(rowstride, rowstride_alignment);
9222     }
9223     framesize = ALIGN_CEIL(rowstride * height, ALIGN_SIZE) + EXTRA_BYTES;
9224     pixel_data = (uint8_t *)lives_calloc(framesize >> SHIFTVAL, ALIGN_SIZE);
9225     if (!pixel_data) return FALSE;
9226     if (black_fill) {
9227       yuv_black[2] = yuv_black[0];
9228       black[3] = yuv_black[1];
9229       fill_plane(pixel_data, 4, width, height, rowstride, yuv_black);
9230     }
9231     weed_set_int_value(layer, WEED_LEAF_ROWSTRIDES, rowstride);
9232     weed_set_voidptr_value(layer, WEED_LEAF_PIXEL_DATA, pixel_data);
9233     break;
9234 
9235   case WEED_PALETTE_YUV420P:
9236   case WEED_PALETTE_YVU420P:
9237     width = (width >> 1) << 1;
9238     height = (height >> 1) << 1;
9239     weed_layer_set_size(layer, width, height);
9240     if (fixed_rs) rowstride = fixed_rs[0];
9241     else {
9242       rowstride = width;
9243       if (!compact) rowstride = ALIGN_CEIL(rowstride, rowstride_alignment);
9244     }
9245     framesize = ALIGN_CEIL(rowstride * height, ALIGN_SIZE);
9246     rowstrides = (int *)lives_malloc(sizint * 3);
9247     if (fixed_rs) {
9248       rowstrides[0] = fixed_rs[0];
9249       rowstride = rowstrides[1] = fixed_rs[1];
9250       rowstrides[2] = fixed_rs[2];
9251     } else {
9252       rowstrides[0] = rowstride;
9253       rowstride >>= 1;
9254       rowstrides[1] = rowstrides[2] = rowstride;
9255     }
9256     //if (!compact) rowstride = ALIGN_CEIL(rowstride, rowstride_alignment);
9257     framesize2 = ALIGN_CEIL(rowstride * (height >> 1), ALIGN_SIZE);
9258     weed_set_int_array(layer, WEED_LEAF_ROWSTRIDES, 3, rowstrides);
9259     lives_free(rowstrides);
9260 
9261     pd_array = (uint8_t **)lives_malloc(3 * sizeof(uint8_t *));
9262 
9263     if (!may_contig) {
9264       weed_leaf_delete(layer, WEED_LEAF_HOST_PIXEL_DATA_CONTIGUOUS);
9265       pd_array[0] = (uint8_t *)lives_calloc((framesize + EXTRA_BYTES) >> SHIFTVAL, ALIGN_SIZE);
9266       if (!pd_array[0]) {
9267         lives_free(pd_array);
9268         return FALSE;
9269       }
9270       pd_array[1] = (uint8_t *)lives_calloc((framesize2 + EXTRA_BYTES) >> SHIFTVAL, ALIGN_SIZE);
9271       if (!pd_array[1]) {
9272         lives_free(pd_array[0]);
9273         lives_free(pd_array);
9274         return FALSE;
9275       }
9276       pd_array[2] = (uint8_t *)lives_calloc((framesize2  + EXTRA_BYTES) >> SHIFTVAL, ALIGN_SIZE);
9277       if (!pd_array[2]) {
9278         lives_free(pd_array[1]);
9279         lives_free(pd_array[0]);
9280         lives_free(pd_array);
9281         return FALSE;
9282       }
9283     } else {
9284       weed_set_boolean_value(layer, WEED_LEAF_HOST_PIXEL_DATA_CONTIGUOUS, WEED_TRUE);
9285       memblock = (uint8_t *)lives_calloc((framesize + framesize2 * 2 + EXTRA_BYTES) >> SHIFTVAL, ALIGN_SIZE);
9286       if (!memblock) return FALSE;
9287       pd_array[0] = (uint8_t *)memblock;
9288       pd_array[1] = (uint8_t *)(memblock + framesize);
9289       pd_array[2] = (uint8_t *)(memblock + framesize + framesize2);
9290     }
9291     if (black_fill) {
9292       if (yuv_black[0] != 0) lives_memset(pd_array[0], yuv_black[0], framesize);
9293       if (may_contig) {
9294         lives_memset(pd_array[1], yuv_black[1], framesize2 * 2); // fill both planes
9295       } else {
9296         lives_memset(pd_array[1], yuv_black[1], framesize2);
9297         lives_memset(pd_array[2], yuv_black[2], framesize2);
9298       }
9299     }
9300 
9301     weed_set_voidptr_array(layer, WEED_LEAF_PIXEL_DATA, 3, (void **)pd_array);
9302     lives_free(pd_array);
9303     break;
9304 
9305   case WEED_PALETTE_YUV422P:
9306     width = (width >> 1) << 1;
9307     weed_layer_set_width(layer, width);
9308     if (fixed_rs) rowstride = fixed_rs[0];
9309     else {
9310       rowstride = width;
9311       if (!compact) rowstride = ALIGN_CEIL(rowstride, rowstride_alignment);
9312     }
9313     framesize = ALIGN_CEIL(rowstride * height, ALIGN_SIZE);
9314     rowstrides = (int *)lives_malloc(sizint * 3);
9315     if (fixed_rs) rowstride = fixed_rs[1];
9316     else {
9317       rowstrides[0] = rowstride;
9318       rowstride = width >> 1;
9319       if (!compact) rowstride = ALIGN_CEIL(rowstride, rowstride_alignment);
9320     }
9321     framesize2 = ALIGN_CEIL(rowstride * height, ALIGN_SIZE);
9322     if (fixed_rs) {
9323       rowstrides[0] = fixed_rs[0];
9324       rowstrides[1] = fixed_rs[1];
9325       rowstrides[2] = fixed_rs[2];
9326     } else  rowstrides[1] = rowstrides[2] = rowstride;
9327     weed_set_int_array(layer, WEED_LEAF_ROWSTRIDES, 3, rowstrides);
9328     lives_free(rowstrides);
9329     pd_array = (uint8_t **)lives_malloc(3 * sizeof(uint8_t *));
9330 
9331     if (!may_contig) {
9332       weed_leaf_delete(layer, WEED_LEAF_HOST_PIXEL_DATA_CONTIGUOUS);
9333       pd_array[0] = (uint8_t *)lives_calloc(framesize >> SHIFTVAL, ALIGN_SIZE);
9334       if (!pd_array[0]) {
9335         lives_free(pd_array);
9336         return FALSE;
9337       }
9338       pd_array[1] = (uint8_t *)lives_calloc(framesize2 >> SHIFTVAL, ALIGN_SIZE);
9339       if (!pd_array[1]) {
9340         lives_free(pd_array[0]);
9341         lives_free(pd_array);
9342         return FALSE;
9343       }
9344       pd_array[2] = (uint8_t *)lives_calloc((framesize2 + EXTRA_BYTES) >> SHIFTVAL, ALIGN_SIZE);
9345       if (!pd_array[2]) {
9346         lives_free(pd_array[1]);
9347         lives_free(pd_array[0]);
9348         lives_free(pd_array);
9349         return FALSE;
9350       }
9351     } else {
9352       weed_set_boolean_value(layer, WEED_LEAF_HOST_PIXEL_DATA_CONTIGUOUS, WEED_TRUE);
9353       memblock = (uint8_t *)lives_calloc((framesize + framesize2 * 2 + EXTRA_BYTES) >> SHIFTVAL, ALIGN_SIZE);
9354       if (!memblock) return FALSE;
9355       pd_array[0] = (uint8_t *)memblock;
9356       pd_array[1] = (uint8_t *)(memblock + framesize);
9357       pd_array[2] = (uint8_t *)(memblock + framesize + framesize2);
9358     }
9359     if (black_fill) {
9360       if (yuv_black[0] != 0) lives_memset(pd_array[0], yuv_black[0], framesize);
9361       if (may_contig) {
9362         lives_memset(pd_array[1], yuv_black[1], framesize2 * 2);
9363       } else {
9364         lives_memset(pd_array[1], yuv_black[1], framesize2);
9365         lives_memset(pd_array[2], yuv_black[2], framesize2);
9366       }
9367     }
9368     weed_set_voidptr_array(layer, WEED_LEAF_PIXEL_DATA, 3, (void **)pd_array);
9369     lives_free(pd_array);
9370     break;
9371 
9372   case WEED_PALETTE_YUV444P:
9373     if (!fixed_rs) {
9374       rowstride = width;
9375       if (!compact) rowstride = ALIGN_CEIL(rowstride, rowstride_alignment);
9376     }
9377     rowstrides = (int *)lives_malloc(sizint * 3);
9378     if (fixed_rs) {
9379       rowstride = rowstrides[0] = fixed_rs[0];
9380       rowstrides[1] = fixed_rs[1];
9381       rowstrides[2] = fixed_rs[2];
9382     } else rowstrides[0] = rowstrides[1] = rowstrides[2] = rowstride;
9383     weed_set_int_array(layer, WEED_LEAF_ROWSTRIDES, 3, rowstrides);
9384     lives_free(rowstrides);
9385     pd_array = (uint8_t **)lives_malloc(3 * sizeof(uint8_t *));
9386     framesize = ALIGN_CEIL(rowstride * height, ALIGN_SIZE);
9387 
9388     if (!may_contig) {
9389       weed_leaf_delete(layer, WEED_LEAF_HOST_PIXEL_DATA_CONTIGUOUS);
9390       pd_array[0] = (uint8_t *)lives_calloc(framesize >> SHIFTVAL, ALIGN_SIZE);
9391       if (!pd_array[0]) {
9392         lives_free(pd_array);
9393         return FALSE;
9394       }
9395       pd_array[1] = (uint8_t *)lives_calloc(framesize >> SHIFTVAL, ALIGN_SIZE);
9396       if (!pd_array[1]) {
9397         lives_free(pd_array[0]);
9398         lives_free(pd_array);
9399         return FALSE;
9400       }
9401       pd_array[2] = (uint8_t *)lives_calloc((framesize + EXTRA_BYTES) >> SHIFTVAL, ALIGN_SIZE);
9402       if (!pd_array[2]) {
9403         lives_free(pd_array[1]);
9404         lives_free(pd_array[0]);
9405         lives_free(pd_array);
9406         return FALSE;
9407       }
9408     } else {
9409       weed_set_boolean_value(layer, WEED_LEAF_HOST_PIXEL_DATA_CONTIGUOUS, WEED_TRUE);
9410       memblock = (uint8_t *)lives_calloc((framesize * 3 + EXTRA_BYTES) >> SHIFTVAL, ALIGN_SIZE);
9411       if (!memblock) return FALSE;
9412       pd_array[0] = memblock;
9413       pd_array[1] = memblock + framesize;
9414       pd_array[2] = memblock + framesize * 2;
9415     }
9416     if (black_fill) {
9417       if (yuv_black[0] != 0) lives_memset(pd_array[0], yuv_black[0], framesize);
9418       if (may_contig) {
9419         lives_memset(pd_array[1], yuv_black[1], framesize * 2);
9420       } else {
9421         lives_memset(pd_array[1], yuv_black[1], framesize);
9422         lives_memset(pd_array[2], yuv_black[2], framesize);
9423       }
9424     }
9425     weed_set_voidptr_array(layer, WEED_LEAF_PIXEL_DATA, 3, (void **)pd_array);
9426     lives_free(pd_array);
9427     break;
9428 
9429   case WEED_PALETTE_YUVA4444P:
9430     if (!fixed_rs) {
9431       rowstride = width;
9432       if (!compact) rowstride = ALIGN_CEIL(rowstride, rowstride_alignment);
9433     }
9434     rowstrides = (int *)lives_malloc(sizint * 4);
9435     if (fixed_rs) {
9436       rowstride = rowstrides[0] = fixed_rs[0];
9437       rowstrides[1] = fixed_rs[1];
9438       rowstrides[2] = fixed_rs[2];
9439       rowstrides[3] = fixed_rs[3];
9440     } else rowstrides[0] = rowstrides[1] = rowstrides[2] = rowstrides[3] = rowstride;
9441     weed_set_int_array(layer, WEED_LEAF_ROWSTRIDES, 4, rowstrides);
9442     lives_free(rowstrides);
9443     pd_array = (uint8_t **)lives_malloc(4 * sizeof(uint8_t *));
9444     framesize = ALIGN_CEIL(rowstride * height, ALIGN_SIZE);
9445 
9446     if (!may_contig) {
9447       weed_leaf_delete(layer, WEED_LEAF_HOST_PIXEL_DATA_CONTIGUOUS);
9448       pd_array[0] = (uint8_t *)lives_calloc((framesize + EXTRA_BYTES) >> SHIFTVAL, ALIGN_SIZE);
9449       if (!pd_array[0]) {
9450         lives_free(pd_array);
9451         return FALSE;
9452       }
9453       pd_array[1] = (uint8_t *)lives_calloc((framesize + EXTRA_BYTES) >> SHIFTVAL, ALIGN_SIZE);
9454       if (!pd_array[1]) {
9455         lives_free(pd_array[0]);
9456         lives_free(pd_array);
9457         return FALSE;
9458       }
9459       pd_array[2] = (uint8_t *)lives_calloc((framesize + EXTRA_BYTES) >> SHIFTVAL, ALIGN_SIZE);
9460       if (!pd_array[2]) {
9461         lives_free(pd_array[1]);
9462         lives_free(pd_array[0]);
9463         lives_free(pd_array);
9464         return FALSE;
9465       }
9466       pd_array[3] = (uint8_t *)lives_calloc((framesize + EXTRA_BYTES) >> SHIFTVAL, ALIGN_SIZE);
9467       if (!pd_array[3]) {
9468         lives_free(pd_array[2]);
9469         lives_free(pd_array[1]);
9470         lives_free(pd_array[0]);
9471         lives_free(pd_array);
9472         return FALSE;
9473       }
9474     } else {
9475       weed_set_boolean_value(layer, WEED_LEAF_HOST_PIXEL_DATA_CONTIGUOUS, WEED_TRUE);
9476       memblock = (uint8_t *)lives_calloc((framesize * 4 + EXTRA_BYTES) >> SHIFTVAL, ALIGN_SIZE);
9477       if (!memblock) return FALSE;
9478       pd_array[0] = memblock;
9479       pd_array[1] = memblock + framesize;
9480       pd_array[2] = memblock + framesize * 2;
9481       pd_array[3] = memblock + framesize * 3;
9482     }
9483     if (black_fill) {
9484       if (yuv_black[0] != 0) {
9485         lives_memset(pd_array[0], yuv_black[0], framesize * 2);
9486       }
9487       if (may_contig) {
9488         lives_memset(pd_array[1], yuv_black[1], framesize * 2);
9489       } else {
9490         lives_memset(pd_array[1], yuv_black[1], framesize);
9491         lives_memset(pd_array[2], yuv_black[2], framesize);
9492       }
9493       lives_memset(pd_array[3], 255, framesize);
9494     }
9495     weed_set_voidptr_array(layer, WEED_LEAF_PIXEL_DATA, 4, (void **)pd_array);
9496     lives_free(pd_array);
9497     break;
9498 
9499   case WEED_PALETTE_YUV411:
9500     if (fixed_rs) rowstride = fixed_rs[0];
9501     else {
9502       rowstride = width * 6; // a macro-pixel is 6 bytes, and contains 4 real pixels
9503       if (!compact) rowstride = ALIGN_CEIL(rowstride, rowstride_alignment);
9504     }
9505     weed_layer_set_width(layer, width);
9506     framesize = ALIGN_CEIL(rowstride * height, ALIGN_SIZE);
9507     pixel_data = (uint8_t *)lives_calloc((framesize + EXTRA_BYTES) >> SHIFTVAL, ALIGN_SIZE);
9508     if (!pixel_data) return FALSE;
9509     if (black_fill) {
9510       yuv_black[3] = yuv_black[1];
9511       yuv_black[1] = yuv_black[2] = yuv_black[4] = yuv_black[5] = yuv_black[0];
9512       yuv_black[0] = yuv_black[3];
9513       pixel_data = (uint8_t *)lives_calloc((framesize + EXTRA_BYTES) >> SHIFTVAL, ALIGN_SIZE);
9514       if (black_fill) {
9515         fill_plane(pixel_data, 6, width, height, rowstride, black);
9516       }
9517     }
9518     if (!pixel_data) return FALSE;
9519     weed_set_voidptr_value(layer, WEED_LEAF_PIXEL_DATA, pixel_data);
9520     weed_set_int_value(layer, WEED_LEAF_ROWSTRIDES, rowstride);
9521     break;
9522 
9523   case WEED_PALETTE_RGBFLOAT:
9524     if (fixed_rs) rowstride = fixed_rs[0];
9525     else {
9526       rowstride = width * 3 * sizeof(float);
9527       if (!compact) rowstride = ALIGN_CEIL(rowstride, rowstride_alignment);
9528     }
9529     pixel_data = (uint8_t *)lives_calloc((rowstride * height + EXTRA_BYTES) >> SHIFTVAL, ALIGN_SIZE);
9530     if (!pixel_data) return FALSE;
9531     weed_set_voidptr_value(layer, WEED_LEAF_PIXEL_DATA, pixel_data);
9532     weed_set_int_value(layer, WEED_LEAF_ROWSTRIDES, rowstride);
9533     break;
9534 
9535   case WEED_PALETTE_RGBAFLOAT:
9536     if (fixed_rs) rowstride = fixed_rs[0];
9537     else {
9538       rowstride = width * 4 * sizeof(float);
9539       if (!compact) rowstride = ALIGN_CEIL(rowstride, rowstride_alignment);
9540     }
9541     pixel_data = (uint8_t *)lives_calloc((rowstride * height + EXTRA_BYTES), ALIGN_SIZE);
9542     if (black_fill) {
9543       fill_plane(pixel_data, 4 * sizeof(float), width, height, rowstride, (uint8_t *)blackf);
9544     }
9545     if (!pixel_data) return FALSE;
9546     weed_set_voidptr_value(layer, WEED_LEAF_PIXEL_DATA, pixel_data);
9547     weed_set_int_value(layer, WEED_LEAF_ROWSTRIDES, rowstride);
9548     break;
9549 
9550   case WEED_PALETTE_AFLOAT:
9551     if (fixed_rs) rowstride = fixed_rs[0];
9552     else {
9553       rowstride = width * sizeof(float);
9554       if (!compact) rowstride = ALIGN_CEIL(rowstride, rowstride_alignment);
9555     }
9556     pixel_data = (uint8_t *)lives_calloc((width * height + EXTRA_BYTES) >> SHIFTVAL, ALIGN_SIZE);
9557     if (!pixel_data) return FALSE;
9558     if (black_fill) {
9559       blackf[0] = 1.;
9560       fill_plane(pixel_data, sizeof(float), width, height, rowstride, (uint8_t *)blackf);
9561     }
9562     weed_set_voidptr_value(layer, WEED_LEAF_PIXEL_DATA, pixel_data);
9563     weed_set_int_value(layer, WEED_LEAF_ROWSTRIDES, rowstride);
9564     break;
9565 
9566   case WEED_PALETTE_A8:
9567     if (fixed_rs) rowstride = fixed_rs[0];
9568     else {
9569       rowstride = width;
9570       if (!compact) rowstride = ALIGN_CEIL(rowstride, rowstride_alignment);
9571     }
9572     framesize = ALIGN_CEIL((rowstride * height + EXTRA_BYTES), ALIGN_SIZE);
9573     pixel_data = (uint8_t *)lives_calloc(framesize >> SHIFTVAL, ALIGN_SIZE);
9574     if (!pixel_data) return FALSE;
9575     if (black_fill) {
9576       lives_memset(pixel_data, 255, rowstride * height);
9577     }
9578     if (!pixel_data) return FALSE;
9579     weed_set_voidptr_value(layer, WEED_LEAF_PIXEL_DATA, pixel_data);
9580     weed_set_int_value(layer, WEED_LEAF_ROWSTRIDES, rowstride);
9581     break;
9582 
9583   case WEED_PALETTE_A1:
9584     if (fixed_rs) rowstride = fixed_rs[0];
9585     else rowstride = (width + 7) >> 3;
9586     framesize = ALIGN_CEIL(rowstride * height, ALIGN_SIZE);
9587     pixel_data = (uint8_t *)lives_calloc((framesize + EXTRA_BYTES) >> SHIFTVAL, ALIGN_SIZE);
9588     if (!pixel_data) return FALSE;
9589     lives_memset(pixel_data, 255, rowstride * height);
9590     weed_set_voidptr_value(layer, WEED_LEAF_PIXEL_DATA, pixel_data);
9591     weed_set_int_value(layer, WEED_LEAF_ROWSTRIDES, rowstride);
9592     break;
9593 
9594   default:
9595     lives_printerr("Warning: asked to create empty pixel_data for palette %d !\n", palette);
9596     break_me("create_empty_pixel_data w. unknown pal");
9597   }
9598   return TRUE;
9599 }
9600 
9601 
9602 /**
9603    @brief fills layer with default values.
9604 
9605    If either width or height are zero, then dimensions will be taken from the layer or
9606    defaults used
9607    if layer has a palette set, that will be maintained, else it will be set to target_palette
9608    if targette palette is WEED_PALETTE_END then default will be set depending on image_ext
9609    if this is "jpg" then it will be RGB24, otherwise RGBA32
9610    finally we create the pixel data for layer */
create_blank_layer(weed_layer_t * layer,const char * image_ext,int width,int height,int target_palette)9611 weed_layer_t *create_blank_layer(weed_layer_t *layer, const char *image_ext, int width, int height, int target_palette) {
9612   if (!layer) layer = weed_layer_new(WEED_LAYER_TYPE_VIDEO);
9613   else {
9614     if (!width) width = weed_layer_get_width(layer);
9615     if (!height) height = weed_layer_get_height(layer);
9616     if (!width || !height) {
9617       int clip = lives_layer_get_clip(layer);
9618       if (clip && IS_VALID_CLIP(clip)) {
9619         width = mainw->files[clip]->hsize;
9620         height = mainw->files[clip]->vsize;
9621       }
9622     }
9623   }
9624   if (width == 0) width = DEF_FRAME_HSIZE_UNSCALED;
9625   if (height == 0) height = DEF_FRAME_VSIZE_UNSCALED;
9626   weed_layer_set_size(layer, width, height);
9627   if (!weed_plant_has_leaf(layer, WEED_LEAF_CURRENT_PALETTE)) {
9628     if (target_palette != WEED_PALETTE_END) weed_layer_set_palette(layer, target_palette);
9629     else {
9630       if (!image_ext || !strcmp(image_ext, LIVES_FILE_EXT_JPG))
9631         weed_layer_set_palette(layer, WEED_PALETTE_RGB24);
9632       else weed_layer_set_palette(layer, WEED_PALETTE_RGBA32);
9633     }
9634   }
9635   if (!create_empty_pixel_data(layer, TRUE, TRUE)) weed_layer_nullify_pixel_data(layer);
9636   if (!weed_plant_has_leaf(layer, WEED_LEAF_GAMMA_TYPE)) {
9637     int clip = lives_layer_get_clip(layer);
9638     if (clip && IS_VALID_CLIP(clip))
9639       weed_layer_set_gamma(layer, mainw->files[clip]->gamma_type);
9640     else
9641       weed_layer_set_gamma(layer, WEED_GAMMA_SRGB);
9642   }
9643   return layer;
9644 }
9645 
9646 
rowstrides_differ(int n1,int * n1_array,int n2,int * n2_array)9647 LIVES_GLOBAL_INLINE boolean rowstrides_differ(int n1, int *n1_array, int n2, int *n2_array) {
9648   // returns TRUE if the rowstrides differ
9649   if (!n1_array || !n2_array || n1 != n2) return TRUE;
9650   for (int i = 0; i < n1; i++) if (n1_array[i] != n2_array[i]) return TRUE;
9651   return FALSE;
9652 }
9653 
9654 
weed_layer_new(int layer_type)9655 LIVES_GLOBAL_INLINE weed_layer_t *weed_layer_new(int layer_type) {
9656   weed_layer_t *layer = weed_plant_new(WEED_PLANT_LAYER);
9657   weed_set_int_value(layer, WEED_LEAF_LAYER_TYPE, layer_type);
9658   weed_set_int_value(layer, WEED_LEAF_HOST_REFS, 1);
9659   return layer;
9660 }
9661 
9662 
weed_layer_get_type(weed_layer_t * layer)9663 LIVES_GLOBAL_INLINE int weed_layer_get_type(weed_layer_t *layer) {
9664   if (!layer || !WEED_IS_LAYER(layer)) return WEED_LAYER_TYPE_NONE;
9665   return weed_get_int_value(layer, WEED_LEAF_LAYER_TYPE, NULL);
9666 }
9667 
9668 
weed_layer_is_video(weed_layer_t * layer)9669 LIVES_GLOBAL_INLINE int weed_layer_is_video(weed_layer_t *layer) {
9670   if (layer && WEED_IS_LAYER(layer) && weed_layer_get_type(layer) == WEED_LAYER_TYPE_VIDEO) return WEED_TRUE;
9671   return WEED_FALSE;
9672 }
9673 
9674 
weed_layer_is_audio(weed_layer_t * layer)9675 LIVES_GLOBAL_INLINE int weed_layer_is_audio(weed_layer_t *layer) {
9676   if (layer && WEED_IS_LAYER(layer) && weed_layer_get_type(layer) == WEED_LAYER_TYPE_AUDIO) return WEED_TRUE;
9677   return WEED_FALSE;
9678 }
9679 
9680 
weed_layer_set_audio_data(weed_layer_t * layer,float ** data,int arate,int naudchans,weed_size_t nsamps)9681 LIVES_GLOBAL_INLINE weed_layer_t *weed_layer_set_audio_data(weed_layer_t *layer, float **data,
9682     int arate, int naudchans, weed_size_t nsamps) {
9683   if (!layer || !WEED_IS_LAYER(layer)) return NULL;
9684   weed_set_voidptr_array(layer, WEED_LEAF_AUDIO_DATA, naudchans, (void **)data);
9685   weed_set_int_value(layer, WEED_LEAF_AUDIO_RATE, arate);
9686   weed_set_int_value(layer, WEED_LEAF_AUDIO_DATA_LENGTH, nsamps);
9687   weed_set_int_value(layer, WEED_LEAF_AUDIO_CHANNELS, naudchans);
9688   return layer;
9689 }
9690 
9691 
weed_layer_set_flags(weed_layer_t * layer,int flags)9692 LIVES_GLOBAL_INLINE weed_layer_t *weed_layer_set_flags(weed_layer_t *layer, int flags) {
9693   if (!layer || !WEED_IS_LAYER(layer)) return NULL;
9694   weed_set_int_value(layer, WEED_LEAF_FLAGS, flags);
9695   return layer;
9696 }
9697 
9698 
weed_layer_get_flags(weed_layer_t * layer)9699 LIVES_GLOBAL_INLINE int weed_layer_get_flags(weed_layer_t *layer) {
9700   if (!layer || !WEED_IS_LAYER(layer)) return 0;
9701   return weed_get_int_value(layer, WEED_LEAF_FLAGS, NULL);
9702 }
9703 
9704 
lives_layer_get_clip(weed_layer_t * layer)9705 LIVES_GLOBAL_INLINE int lives_layer_get_clip(weed_layer_t *layer) {
9706   if (!layer || !WEED_IS_LAYER(layer)) return 0;
9707   return weed_get_int_value(layer, WEED_LEAF_CLIP, NULL);
9708 }
9709 
9710 
lives_layer_get_frame(weed_layer_t * layer)9711 LIVES_GLOBAL_INLINE frames_t lives_layer_get_frame(weed_layer_t *layer) {
9712   if (!layer || !WEED_IS_LAYER(layer)) return 0;
9713   return weed_get_int_value(layer, WEED_LEAF_FRAME, NULL);
9714 }
9715 
9716 
weed_layer_set_width(weed_layer_t * layer,int width)9717 LIVES_GLOBAL_INLINE weed_layer_t *weed_layer_set_width(weed_layer_t *layer, int width) {
9718   if (!layer || !WEED_IS_LAYER(layer)) return NULL;
9719   weed_set_int_value(layer, WEED_LEAF_WIDTH, width);
9720   return layer;
9721 }
9722 
9723 
weed_layer_set_height(weed_layer_t * layer,int height)9724 LIVES_GLOBAL_INLINE weed_layer_t *weed_layer_set_height(weed_layer_t *layer, int height) {
9725   if (!layer || !WEED_IS_LAYER(layer)) return NULL;
9726   weed_set_int_value(layer, WEED_LEAF_HEIGHT, height);
9727   return layer;
9728 }
9729 
9730 
weed_layer_set_size(weed_layer_t * layer,int width,int height)9731 LIVES_GLOBAL_INLINE weed_layer_t *weed_layer_set_size(weed_layer_t *layer, int width, int height) {
9732   if (!layer || !WEED_IS_LAYER(layer)) return NULL;
9733   weed_layer_set_width(layer, width);
9734   weed_layer_set_height(layer, height);
9735   return layer;
9736 }
9737 
9738 
weed_layer_set_pixel_data(weed_layer_t * layer,void ** pixel_data,int nplanes)9739 LIVES_GLOBAL_INLINE weed_layer_t *weed_layer_set_pixel_data(weed_layer_t *layer, void **pixel_data, int nplanes) {
9740   if (!layer || !WEED_IS_LAYER(layer)) return NULL;
9741   weed_set_voidptr_array(layer, WEED_LEAF_PIXEL_DATA, nplanes, pixel_data);
9742   return layer;
9743 }
9744 
9745 
weed_layer_set_pixel_data_packed(weed_layer_t * layer,void * pixel_data)9746 LIVES_GLOBAL_INLINE weed_layer_t *weed_layer_set_pixel_data_packed(weed_layer_t *layer, void *pixel_data) {
9747   if (!layer || !WEED_IS_LAYER(layer)) return NULL;
9748   weed_set_voidptr_value(layer, WEED_LEAF_PIXEL_DATA, pixel_data);
9749   return layer;
9750 }
9751 
9752 
weed_layer_nullify_pixel_data(weed_layer_t * layer)9753 LIVES_GLOBAL_INLINE weed_layer_t *weed_layer_nullify_pixel_data(weed_layer_t *layer) {
9754   if (!layer || !WEED_IS_LAYER(layer)) return NULL;
9755   weed_set_voidptr_array(layer, WEED_LEAF_PIXEL_DATA, 0, NULL);
9756   weed_leaf_delete(layer, WEED_LEAF_HOST_PIXEL_DATA_CONTIGUOUS);
9757   weed_leaf_delete(layer, WEED_LEAF_HOST_PIXBUF_SRC);
9758   weed_leaf_delete(layer, WEED_LEAF_HOST_SURFACE_SRC);
9759   return layer;
9760 }
9761 
9762 
weed_layer_set_rowstrides(weed_layer_t * layer,int * rowstrides,int nplanes)9763 LIVES_GLOBAL_INLINE weed_layer_t *weed_layer_set_rowstrides(weed_layer_t *layer, int *rowstrides, int nplanes) {
9764   if (!layer || !WEED_IS_LAYER(layer)) return NULL;
9765   weed_set_int_array(layer, WEED_LEAF_ROWSTRIDES, nplanes, rowstrides);
9766   return layer;
9767 }
9768 
9769 
weed_layer_set_rowstride(weed_layer_t * layer,int rowstride)9770 LIVES_GLOBAL_INLINE weed_layer_t *weed_layer_set_rowstride(weed_layer_t *layer, int rowstride) {
9771   if (!layer || !WEED_IS_LAYER(layer)) return NULL;
9772   weed_set_int_value(layer, WEED_LEAF_ROWSTRIDES, rowstride);
9773   return layer;
9774 }
9775 
9776 
weed_layer_set_palette(weed_layer_t * layer,int palette)9777 LIVES_GLOBAL_INLINE weed_layer_t *weed_layer_set_palette(weed_layer_t *layer, int palette) {
9778   if (!layer || !WEED_IS_LAYER(layer)) return NULL;
9779   weed_set_int_value(layer, WEED_LEAF_CURRENT_PALETTE, palette);
9780   return layer;
9781 }
9782 
9783 
weed_layer_set_gamma(weed_layer_t * layer,int gamma_type)9784 LIVES_GLOBAL_INLINE weed_layer_t *weed_layer_set_gamma(weed_layer_t *layer, int gamma_type) {
9785   if (!layer || !WEED_IS_LAYER(layer)) return NULL;
9786   weed_set_int_value(layer, WEED_LEAF_GAMMA_TYPE, gamma_type);
9787   return layer;
9788 }
9789 
9790 
weed_layer_set_yuv_clamping(weed_layer_t * layer,int clamping)9791 LIVES_GLOBAL_INLINE weed_layer_t *weed_layer_set_yuv_clamping(weed_layer_t *layer, int clamping) {
9792   if (!layer || !WEED_IS_LAYER(layer)) return NULL;
9793   weed_set_int_value(layer, WEED_LEAF_YUV_CLAMPING, clamping);
9794   return layer;
9795 }
9796 
9797 
weed_layer_set_yuv_sampling(weed_layer_t * layer,int sampling)9798 LIVES_GLOBAL_INLINE weed_layer_t *weed_layer_set_yuv_sampling(weed_layer_t *layer, int sampling) {
9799   if (!layer || !WEED_IS_LAYER(layer)) return NULL;
9800   weed_set_int_value(layer, WEED_LEAF_YUV_SAMPLING, sampling);
9801   return layer;
9802 }
9803 
9804 
weed_layer_set_yuv_subspace(weed_layer_t * layer,int subspace)9805 LIVES_GLOBAL_INLINE weed_layer_t *weed_layer_set_yuv_subspace(weed_layer_t *layer, int subspace) {
9806   if (!layer || !WEED_IS_LAYER(layer)) return NULL;
9807   weed_set_int_value(layer, WEED_LEAF_YUV_SUBSPACE, subspace);
9808   return layer;
9809 }
9810 
9811 
weed_layer_set_palette_yuv(weed_layer_t * layer,int palette,int clamping,int sampling,int subspace)9812 LIVES_GLOBAL_INLINE weed_layer_t *weed_layer_set_palette_yuv(weed_layer_t *layer, int palette,
9813     int clamping, int sampling, int subspace) {
9814   if (!weed_layer_set_palette(layer, palette)) return NULL;
9815   weed_layer_set_yuv_clamping(layer, clamping);
9816   weed_layer_set_yuv_sampling(layer, sampling);
9817   weed_layer_set_yuv_subspace(layer, subspace);
9818   return layer;
9819 }
9820 
9821 
lives_layer_set_frame(weed_layer_t * layer,frames_t frame)9822 LIVES_GLOBAL_INLINE void lives_layer_set_frame(weed_layer_t *layer, frames_t frame) {
9823   // TODO -> int64
9824   weed_set_int_value(layer, WEED_LEAF_FRAME, frame);
9825 }
9826 
9827 
lives_layer_set_clip(weed_layer_t * layer,int clip)9828 LIVES_GLOBAL_INLINE void lives_layer_set_clip(weed_layer_t *layer, int clip) {
9829   weed_set_int_value(layer, WEED_LEAF_CLIP, clip);
9830 }
9831 
9832 
lives_layer_new_for_frame(int clip,frames_t frame)9833 LIVES_GLOBAL_INLINE weed_layer_t *lives_layer_new_for_frame(int clip, frames_t frame) {
9834   // create a layer ready to receive a frame from a clip
9835   weed_layer_t *layer = weed_layer_new(WEED_LAYER_TYPE_VIDEO);
9836   lives_layer_set_clip(layer, clip);
9837   lives_layer_set_frame(layer, frame);
9838   return layer;
9839 }
9840 
9841 
9842 // returns TRUE on success
copy_pixel_data(weed_layer_t * layer,weed_layer_t * old_layer,size_t alignment)9843 boolean copy_pixel_data(weed_layer_t *layer, weed_layer_t *old_layer, size_t alignment) {
9844   // copy (deep) old_layer -> layer
9845 
9846   int numplanes, xheight, xwidth;
9847   int *orowstrides = weed_layer_get_rowstrides(old_layer, &numplanes), *rowstrides;
9848   void **pixel_data, **npixel_data;
9849   int pal = weed_layer_get_palette(layer);
9850   int width = weed_layer_get_width(layer);
9851   int height = weed_layer_get_height(layer);
9852   int psize = pixel_size(pal);
9853   int i = numplanes, j;
9854   boolean newdata = FALSE;
9855 
9856   if (alignment != 0 && !old_layer) {
9857     while (i > 0) if (orowstrides[--i] % alignment != 0) i = -1;
9858     if (i == 0) return TRUE;
9859   }
9860 
9861   if (!old_layer) {
9862     newdata = TRUE;
9863     old_layer = weed_layer_new(WEED_LAYER_TYPE_VIDEO);
9864     weed_layer_copy(old_layer, layer);
9865   }
9866   pixel_data = weed_layer_get_pixel_data(old_layer, &numplanes);
9867   if (!pixel_data || !pixel_data[0]) {
9868     if (newdata) {
9869       weed_layer_nullify_pixel_data(old_layer);
9870       weed_layer_free(old_layer);
9871     }
9872     return FALSE;
9873   }
9874 
9875   weed_layer_nullify_pixel_data(layer);
9876 
9877   if (alignment != 0) THREADVAR(rowstride_alignment_hint) = alignment;
9878 
9879   if (!create_empty_pixel_data(layer, FALSE, TRUE)) {
9880     if (newdata) {
9881       weed_layer_copy(layer, old_layer);
9882       weed_layer_nullify_pixel_data(old_layer);
9883       weed_layer_free(old_layer);
9884     }
9885     return FALSE;
9886   }
9887 
9888   rowstrides = weed_layer_get_rowstrides(layer, &numplanes);
9889   npixel_data = weed_layer_get_pixel_data(layer, &numplanes);
9890   width = weed_layer_get_width(layer);
9891   height = weed_layer_get_height(layer);
9892 
9893   for (i = 0; i < numplanes; i++) {
9894     xheight = height * weed_palette_get_plane_ratio_vertical(pal, i);
9895     if (rowstrides[i] == orowstrides[i])
9896       lives_memcpy(npixel_data[i], pixel_data[i], xheight *  rowstrides[i]);
9897     else {
9898       uint8_t *dst = (uint8_t *)npixel_data[i];
9899       uint8_t *src = (uint8_t *)pixel_data[i];
9900       xwidth = width * psize * weed_palette_get_plane_ratio_horizontal(pal, i);
9901       for (j = 0; j < xheight; j++) {
9902         lives_memcpy(dst, src, xwidth);
9903         src += orowstrides[i];
9904         dst += rowstrides[i];
9905       }
9906     }
9907   }
9908 
9909   weed_leaf_dup(layer, old_layer, WEED_LEAF_NATURAL_SIZE);
9910 
9911   if (newdata) weed_layer_free(old_layer);
9912   lives_freep((void **)&npixel_data);
9913   lives_freep((void **)&pixel_data);
9914   lives_freep((void **)&orowstrides);
9915   lives_freep((void **)&rowstrides);
9916   return TRUE;
9917 }
9918 
9919 
9920 /** (un)premultply alpha using a lookup table
9921 
9922     if un is FALSE we go the other way, and do a pre-multiplication */
alpha_unpremult(weed_layer_t * layer,boolean un)9923 void alpha_unpremult(weed_layer_t *layer, boolean un) {
9924   /// this is only used when going from palette with alpha to one without
9925   int error;
9926   int aoffs, coffs, psize, psizel, widthx;
9927   int alpha;
9928   int flags = 0;
9929   int width = weed_get_int_value(layer, WEED_LEAF_WIDTH, NULL);
9930   int height = weed_get_int_value(layer, WEED_LEAF_HEIGHT, NULL);
9931   int rowstride = weed_get_int_value(layer, WEED_LEAF_ROWSTRIDES, NULL);
9932   int pal = weed_get_int_value(layer, WEED_LEAF_CURRENT_PALETTE, NULL);
9933 
9934   int *rows;
9935 
9936   unsigned char *ptr;
9937   unsigned char **ptrp;
9938 
9939   boolean clamped;
9940 
9941   int i, j, p;
9942 
9943   if (!unal_inited) init_unal();
9944 
9945   if (weed_plant_has_leaf(layer, WEED_LEAF_YUV_CLAMPING))
9946     clamped = (weed_get_int_value(layer, WEED_LEAF_YUV_CLAMPING, &error) == WEED_YUV_CLAMPING_CLAMPED);
9947   else clamped = TRUE;
9948 
9949   switch (pal) {
9950   case WEED_PALETTE_RGBA32:
9951   case WEED_PALETTE_BGRA32:
9952     clamped = FALSE;
9953   case WEED_PALETTE_YUVA8888:
9954     widthx = width * 4;
9955     psize = 4;
9956     psizel = 3;
9957     coffs = 0;
9958     aoffs = 3;
9959     break;
9960   case WEED_PALETTE_ARGB32:
9961     widthx = width * 4;
9962     psize = 4;
9963     psizel = 4;
9964     coffs = 1;
9965     aoffs = 0;
9966     clamped = FALSE;
9967     break;
9968   case WEED_PALETTE_YUVA4444P:
9969     /// special case - planar with alpha
9970     ptrp = (unsigned char **)weed_get_voidptr_array(layer, WEED_LEAF_PIXEL_DATA, &error);
9971     rows = weed_get_int_array(layer, WEED_LEAF_ROWSTRIDES, &error);
9972 
9973     if (!clamped) {
9974       if (un) {
9975         for (i = 0; i < height; i++) {
9976           for (j = 0; j < width; j++) {
9977             alpha = ptrp[3][j];
9978             for (p = 0; p < 3; p++) {
9979               ptrp[p][j] = unal[alpha][ptrp[p][j]];
9980             }
9981           }
9982           for (p = 0; p < 4; p++) {
9983             ptrp[p] += rows[p];
9984           }
9985         }
9986       } else {
9987         for (i = 0; i < height; i++) {
9988           for (j = 0; j < width; j++) {
9989             alpha = ptrp[3][j];
9990             for (p = 0; p < 3; p++) {
9991               ptrp[p][j] = al[alpha][ptrp[p][j]];
9992             }
9993           }
9994           for (p = 0; p < 4; p++) {
9995             ptrp[p] += rows[p];
9996           }
9997         }
9998       }
9999     } else {
10000       if (un) {
10001         for (i = 0; i < height; i++) {
10002           for (j = 0; j < width; j++) {
10003             alpha = ptrp[3][j];
10004             ptrp[0][j] = unalcy[alpha][ptrp[0][j]];
10005             ptrp[1][j] = unalcuv[alpha][ptrp[0][j]];
10006             ptrp[2][j] = unalcuv[alpha][ptrp[0][j]];
10007           }
10008           for (p = 0; p < 4; p++) {
10009             ptrp[p] += rows[p];
10010           }
10011         }
10012       } else {
10013         for (i = 0; i < height; i++) {
10014           for (j = 0; j < width; j++) {
10015             alpha = ptrp[3][j];
10016             ptrp[0][j] = alcy[alpha][ptrp[0][j]];
10017             ptrp[1][j] = alcuv[alpha][ptrp[0][j]];
10018             ptrp[2][j] = alcuv[alpha][ptrp[0][j]];
10019           }
10020           for (p = 0; p < 4; p++) {
10021             ptrp[p] += rows[p];
10022 	    // *INDENT-OFF*
10023 	  }}}}
10024     // *INDENT-ON*
10025 
10026     return;
10027   default:
10028     return;
10029   }
10030 
10031   ptr = (unsigned char *)weed_get_voidptr_value(layer, WEED_LEAF_PIXEL_DATA, &error);
10032 
10033   if (!clamped) {
10034     if (un) {
10035       for (i = 0; i < height; i++) {
10036         for (j = 0; j < widthx; j += psize) {
10037           alpha = ptr[j + aoffs];
10038           for (p = coffs; p < psizel; p++) {
10039             ptr[j + p] = unal[alpha][ptr[j + p]];
10040           }
10041         }
10042         ptr += rowstride;
10043       }
10044     } else {
10045       for (i = 0; i < height; i++) {
10046         for (j = 0; j < widthx; j += psize) {
10047           alpha = ptr[j + aoffs];
10048           for (p = coffs; p < psizel; p++) {
10049             ptr[j + p] = al[alpha][ptr[j + p]];
10050           }
10051         }
10052         ptr += rowstride;
10053       }
10054     }
10055   } else {
10056     /// unclamped YUVA8888 (packed)
10057     if (un) {
10058       for (i = 0; i < height; i++) {
10059         for (j = 0; j < widthx; j += psize) {
10060           alpha = ptr[j + 3];
10061           ptr[j] = unalcy[alpha][ptr[j]];
10062           ptr[j + 1] = unalcuv[alpha][ptr[j]];
10063           ptr[j + 2] = unalcuv[alpha][ptr[j]];
10064         }
10065         ptr += rowstride;
10066       }
10067     } else {
10068       for (i = 0; i < height; i++) {
10069         for (j = 0; j < widthx; j += psize) {
10070           alpha = ptr[j + 3];
10071           ptr[j] = alcy[alpha][ptr[j]];
10072           ptr[j + 1] = alcuv[alpha][ptr[j]];
10073           ptr[j + 2] = alcuv[alpha][ptr[j]];
10074         }
10075         ptr += rowstride;
10076       }
10077     }
10078   }
10079 
10080   flags = weed_layer_get_flags(layer);
10081 
10082   if (!un) flags |= WEED_LAYER_ALPHA_PREMULT;
10083   else if (flags & WEED_LAYER_ALPHA_PREMULT) flags ^= WEED_LAYER_ALPHA_PREMULT;
10084 
10085   if (flags == 0) weed_leaf_delete(layer, WEED_LEAF_FLAGS);
10086   else weed_set_int_value(layer, WEED_LEAF_FLAGS, flags);
10087 }
10088 
10089 
swap_chroma_planes(weed_layer_t * layer)10090 static void swap_chroma_planes(weed_layer_t *layer) {
10091   int nplanes;
10092   void **pd_array = weed_layer_get_pixel_data(layer, &nplanes);
10093   int *rowstrides, rtmp;
10094   uint8_t *tmp;
10095   if (nplanes < 3) return;
10096   tmp = pd_array[1];
10097   pd_array[1] = pd_array[2];
10098   pd_array[2] = tmp;
10099   weed_layer_set_pixel_data(layer, pd_array, nplanes);
10100   lives_free(pd_array);
10101   rowstrides = weed_layer_get_rowstrides(layer, NULL);
10102   rtmp = rowstrides[1];
10103   rowstrides[1] = rowstrides[2];
10104   rowstrides[2] = rtmp;
10105 }
10106 
10107 
can_inline_gamma(int inpl,int opal)10108 LIVES_LOCAL_INLINE boolean can_inline_gamma(int inpl, int opal) {
10109   // TODO: rgb <-> bgra, bgr <-> rgba,
10110   if (inpl == WEED_PALETTE_YUV420P || inpl == WEED_PALETTE_YUV420P || inpl == WEED_PALETTE_YUV420P) {
10111     if (opal == WEED_PALETTE_RGB24 || opal == WEED_PALETTE_BGR24 || opal == WEED_PALETTE_RGBA32
10112         || opal == WEED_PALETTE_ARGB32) return TRUE;
10113   }
10114   if (opal == WEED_PALETTE_UYVY || opal == WEED_PALETTE_YUYV) {
10115     if (inpl == WEED_PALETTE_RGB24 || inpl == WEED_PALETTE_RGBA32
10116         || inpl == WEED_PALETTE_BGR24 || inpl == WEED_PALETTE_BGRA32
10117         || inpl == WEED_PALETTE_ARGB32
10118        ) return TRUE;
10119   }
10120 
10121   if ((inpl == WEED_PALETTE_RGB24 && opal == WEED_PALETTE_BGR24) || (inpl == WEED_PALETTE_BGR24
10122       && opal == WEED_PALETTE_RGB24)) return TRUE;
10123   if ((inpl == WEED_PALETTE_RGB24 && opal == WEED_PALETTE_RGBA32) || (inpl == WEED_PALETTE_BGR24
10124       && opal == WEED_PALETTE_BGRA32)) return TRUE;
10125   if ((inpl == WEED_PALETTE_RGBA32 && opal == WEED_PALETTE_RGB24) || (inpl == WEED_PALETTE_BGRA32
10126       && opal == WEED_PALETTE_BGR24)) return TRUE;
10127 
10128   return FALSE;
10129 }
10130 
10131 
10132 /**
10133    @brief convert the palette of a layer
10134 
10135    convert to/from the 5 non-float RGB palettes and 10 YUV palettes
10136    giving a total of 15*14=210 conversions
10137 
10138    in addition YUV can be converted from clamped to unclamped and vice-versa
10139 
10140    chroma sub and supersampling is implemented, and threading is used wherever possible
10141 
10142    all conversions are performed via lookup tables
10143 
10144    NOTE - if converting to YUV411, we cut pixels so (RGB) width is divisible by 4
10145    if converting to YUV420 or YVU420, we cut pixels so (RGB) width is divisible by 2
10146    if converting to YUV420 or YVU420, we cut pixels so height is divisible by 2
10147 
10148    returns FALSE if the palette conversion fails or if layer is NULL
10149 
10150    - original palette pixel_data is free()d (unless converting between YUV420 and YVU420, there the u and v pointers are
10151    simply swapped).
10152 
10153    current limitations:
10154    - chroma is assumed centred between luma for input and output
10155    - bt709 yuv is only implemented for conversions to / from rgb palettes and yuv420 / yvu420
10156    - rowstride values may be ignored for UYVY, YUYV and YUV411 planar palettes.
10157    - RGB float palettes not yet implemented
10158 
10159 */
convert_layer_palette_full(weed_layer_t * layer,int outpl,int oclamping,int osampling,int osubspace,int tgamma)10160 boolean convert_layer_palette_full(weed_layer_t *layer, int outpl, int oclamping, int osampling, int osubspace, int tgamma) {
10161   // TODO: allow plugin candidates/delegates
10162   weed_layer_t *orig_layer;
10163   uint8_t *gusrc = NULL, **gusrc_array = NULL, *gudest = NULL, **gudest_array = NULL, *tmp;
10164   int width, height, orowstride, irowstride, *istrides, *ostrides = NULL;
10165   int nplanes;
10166   int error, inpl, flags = 0;
10167   int isampling, isubspace;
10168   int new_gamma_type = WEED_GAMMA_UNKNOWN;
10169   int iclamping;
10170   boolean contig = FALSE;
10171 
10172   if (!layer || !weed_layer_get_pixel_data_packed(layer)) return FALSE;
10173 
10174   inpl = weed_layer_get_palette(layer);
10175 
10176   if (weed_plant_has_leaf(layer, WEED_LEAF_YUV_SAMPLING))
10177     isampling = weed_layer_get_yuv_sampling(layer);
10178   else isampling = WEED_YUV_SAMPLING_DEFAULT;
10179 
10180   if (weed_plant_has_leaf(layer, WEED_LEAF_YUV_CLAMPING))
10181     iclamping = weed_layer_get_yuv_clamping(layer);
10182   else iclamping = oclamping;
10183 
10184   if (weed_plant_has_leaf(layer, WEED_LEAF_YUV_SUBSPACE))
10185     isubspace = weed_layer_get_yuv_subspace(layer);
10186   else isubspace = WEED_YUV_SUBSPACE_YUV;
10187 
10188   width = weed_layer_get_width(layer);
10189   height = weed_layer_get_height(layer);
10190 
10191   //    #define DEBUG_PCONV
10192 #ifdef DEBUG_PCONV
10193   g_print("converting %d X %d palette %s(%s) to %s(%s)\n", width, height, weed_palette_get_name(inpl),
10194           weed_yuv_clamping_get_name(iclamping),
10195           weed_palette_get_name(outpl),
10196           weed_yuv_clamping_get_name(oclamping));
10197 #endif
10198 
10199   istrides = weed_layer_get_rowstrides(layer, &nplanes);
10200   if (!istrides) return FALSE;
10201 
10202   if (weed_palette_is_yuv(inpl) && weed_palette_is_yuv(outpl) && (iclamping != oclamping || isubspace != osubspace)) {
10203     if (isubspace == osubspace) {
10204 #ifdef DEBUG_PCONV
10205       lives_printerr("converting clamping %d to %d\n", iclamping, oclamping);
10206 #endif
10207       switch_yuv_clamping_and_subspace(layer, oclamping, osubspace);
10208       iclamping = oclamping;
10209     } else {
10210       // convert first to RGB(A)
10211       if (weed_palette_has_alpha(inpl)) {
10212         if (!convert_layer_palette(layer, WEED_PALETTE_RGBA32, 0)) goto memfail;
10213       } else {
10214         if (!convert_layer_palette(layer, WEED_PALETTE_RGB24, 0)) goto memfail;
10215       }
10216       inpl = weed_layer_get_palette(layer);
10217       isubspace = osubspace;
10218       isampling = osampling;
10219       iclamping = oclamping;
10220 #ifdef DEBUG_PCONV
10221       g_print("subspace conversion via palette %s\n", weed_palette_get_name(inpl));
10222 #endif
10223     }
10224   }
10225 
10226   if (inpl == outpl) {
10227 #ifdef DEBUG_PCONV
10228     lives_printerr("not converting palette\n");
10229 #endif
10230     if (!weed_palette_is_yuv(inpl) || (isampling == osampling &&
10231                                        (isubspace == osubspace || (osubspace != WEED_YUV_SUBSPACE_BT709)))) {
10232       if (inpl == WEED_PALETTE_YUV420P && ((isampling == WEED_YUV_SAMPLING_JPEG
10233                                             && osampling == WEED_YUV_SAMPLING_MPEG) ||
10234                                            (isampling == WEED_YUV_SAMPLING_MPEG && osampling == WEED_YUV_SAMPLING_JPEG))) {
10235         switch_yuv_sampling(layer);
10236       } else {
10237         char *tmp2 = lives_strdup_printf("Switch sampling types (%d %d) or subspace(%d %d): (%d) conversion not yet written !\n",
10238                                          isampling, osampling, isubspace, osubspace, inpl);
10239         LIVES_DEBUG(tmp2);
10240         lives_free(tmp2);
10241         lives_free(istrides);
10242         return TRUE;
10243       }
10244     }
10245   }
10246 
10247   flags = weed_layer_get_flags(layer);
10248 
10249   if (prefs->alpha_post) {
10250     if ((flags & WEED_LAYER_ALPHA_PREMULT) &&
10251         (weed_palette_has_alpha(inpl) && !(weed_palette_has_alpha(outpl)))) {
10252       // if we have pre-multiplied alpha, remove it when removing alpha channel
10253       alpha_unpremult(layer, TRUE);
10254     }
10255   } else {
10256     if (!weed_palette_has_alpha(inpl) && weed_palette_has_alpha(outpl)) {
10257       flags |= WEED_LAYER_ALPHA_PREMULT;
10258       weed_set_int_value(layer, WEED_LEAF_FLAGS, flags);
10259     }
10260   }
10261 
10262   if (weed_palette_has_alpha(inpl) && !(weed_palette_has_alpha(outpl)) && (flags & WEED_LAYER_ALPHA_PREMULT)) {
10263     flags ^= WEED_LAYER_ALPHA_PREMULT;
10264     if (flags == 0) weed_leaf_delete(layer, WEED_LEAF_FLAGS);
10265     else weed_set_int_value(layer, WEED_LEAF_FLAGS, flags);
10266   }
10267 
10268   if (weed_get_boolean_value(layer, WEED_LEAF_HOST_PIXEL_DATA_CONTIGUOUS, &error) == WEED_TRUE)
10269     contig = TRUE;
10270 
10271   width = weed_layer_get_width(layer);
10272   height = weed_layer_get_height(layer);
10273 
10274   if (prefs->apply_gamma) {
10275     // gamma correction
10276     if (tgamma != WEED_GAMMA_UNKNOWN) {
10277       new_gamma_type = tgamma;
10278     } else {
10279       if (weed_palette_is_rgb(inpl) && !weed_palette_is_rgb(outpl)) {
10280         // gamma correction
10281         if (osubspace == WEED_YUV_SUBSPACE_BT709) {
10282           new_gamma_type = WEED_GAMMA_BT709;
10283         } else new_gamma_type = WEED_GAMMA_SRGB;
10284       } else  new_gamma_type = weed_layer_get_gamma(layer);
10285     }
10286     if (weed_palette_is_rgb(inpl) && !weed_palette_is_rgb(outpl)) {
10287       if (!can_inline_gamma(inpl, outpl)) {
10288         gamma_convert_layer(new_gamma_type, layer);
10289         new_gamma_type = WEED_GAMMA_UNKNOWN;
10290       }
10291     }
10292   }
10293 
10294   lives_free(istrides);
10295   istrides = weed_layer_get_rowstrides(layer, &nplanes);
10296   if (!istrides) return FALSE;
10297 
10298   irowstride = istrides[0];
10299   weed_layer_set_palette(layer, outpl);
10300   weed_layer_set_size(layer, width * weed_palette_get_pixels_per_macropixel(inpl)
10301                       / weed_palette_get_pixels_per_macropixel(outpl), height);
10302   // TODO: rowstrides for uyvy, yuyv, 422P, 411
10303 
10304   /// if V plane is before U, swap the pointers
10305 #ifdef WEED_ADVANCED_PALETTES
10306   if (!weed_palette_is_sane(inpl) || !weed_palette_is_sane(outpl)) {
10307     if (!weed_palette_is_sane(outpl)) g_print("BAD pal %d\n", outpl);
10308     if (!weed_palette_is_sane(inpl)) g_print("BAD pal %d\n", inpl);
10309     return FALSE;
10310   }
10311   if (get_advanced_palette(inpl)->chantype[1] == WEED_VCHAN_V) swap_chroma_planes(layer);
10312 #else
10313   if (inpl == WEED_PALETTE_YVU420P) swap_chroma_planes(layer);
10314 #endif
10315 
10316   orig_layer = weed_layer_new(WEED_LAYER_TYPE_VIDEO);
10317   weed_layer_copy(orig_layer, layer);
10318 
10319 #ifdef WEED_ADVANCED_PALETTES
10320   if (weed_palette_is_rgb(inpl) && weed_palette_is_rgb(outpl)) {
10321     gusrc = weed_layer_get_pixel_data_packed(layer);
10322     if (!weed_palette_has_alpha_first(inpl)) {
10323       if (!weed_palette_has_alpha_last(inpl)) {
10324         if (!weed_palette_has_alpha_first(outpl)) {
10325           if (!weed_palette_has_alpha_last(outpl)) {
10326             convert_swap3_frame(gusrc, width, height, irowstride, irowstride, gusrc,
10327                                 create_gamma_lut(1.0, weed_layer_get_gamma(layer), new_gamma_type),
10328                                 -USE_THREADS);
10329             weed_layer_nullify_pixel_data(orig_layer);
10330           } else {
10331             // add post
10332             if (weed_palettes_rbswapped(inpl, outpl)) {
10333               if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10334               orowstride = weed_layer_get_rowstride(layer);
10335               gudest = weed_layer_get_pixel_data_packed(layer);
10336               convert_swap3addpost_frame(gusrc, width, height, irowstride, orowstride, gudest,
10337                                          -USE_THREADS);
10338             } else {
10339               if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10340               orowstride = weed_layer_get_rowstride(layer);
10341               gudest = weed_layer_get_pixel_data_packed(layer);
10342               convert_addpost_frame(gusrc, width, height, irowstride, orowstride, gudest,
10343                                     create_gamma_lut(1.0, weed_layer_get_gamma(layer), new_gamma_type),
10344                                     -USE_THREADS);
10345             }
10346           }
10347         } else {
10348           /// add pre
10349           if (weed_palettes_rbswapped(inpl, outpl)) {
10350             if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10351             orowstride = weed_layer_get_rowstride(layer);
10352             gudest = weed_layer_get_pixel_data_packed(layer);
10353             convert_swap3addpre_frame(gusrc, width, height, irowstride, orowstride, gudest,
10354                                       -USE_THREADS);
10355           } else {
10356             if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10357             orowstride = weed_layer_get_rowstride(layer);
10358             gudest = weed_layer_get_pixel_data_packed(layer);
10359             convert_addpre_frame(gusrc, width, height, irowstride, orowstride, gudest, -USE_THREADS);
10360           }
10361         }
10362       } else {
10363         /// inpl has post
10364         if (!weed_palette_has_alpha_first(outpl)) {
10365           if (!weed_palette_has_alpha_last(outpl)) {
10366             if (weed_palettes_rbswapped(inpl, outpl)) {
10367               if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10368               orowstride = weed_layer_get_rowstride(layer);
10369               gudest = weed_layer_get_pixel_data_packed(layer);
10370               convert_swap3delpost_frame(gusrc, width, height, irowstride, orowstride, gudest,
10371                                          -USE_THREADS);
10372             } else {
10373               if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10374               orowstride = weed_layer_get_rowstride(layer);
10375               gudest = weed_layer_get_pixel_data_packed(layer);
10376               convert_delpost_frame(gusrc, width, height, irowstride, orowstride, gudest,
10377                                     create_gamma_lut(1.0, weed_layer_get_gamma(layer), new_gamma_type),
10378                                     -USE_THREADS);
10379             }
10380           } else {
10381             /// outpl has post
10382             convert_swap3postalpha_frame(gusrc, width, height, irowstride, -USE_THREADS);
10383             weed_layer_nullify_pixel_data(orig_layer);
10384           }
10385         } else {
10386           /// outpl has pre
10387           if (weed_palettes_rbswapped(inpl, outpl)) {
10388             convert_swap4_frame(gusrc, width, height, irowstride, irowstride, gusrc, -USE_THREADS);
10389             weed_layer_nullify_pixel_data(orig_layer);
10390           } else {
10391             convert_swapprepost_frame(gusrc, width, height, irowstride, irowstride, gusrc,
10392                                       -USE_THREADS);
10393             weed_layer_nullify_pixel_data(orig_layer);
10394           }
10395         }
10396       }
10397     } else {
10398       // inpl has pre
10399       if (!weed_palette_has_alpha_first(outpl)) {
10400         if (!weed_palette_has_alpha_last(outpl)) {
10401           if (weed_palettes_rbswapped(inpl, outpl)) {
10402             if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10403             orowstride = weed_layer_get_rowstride(layer);
10404             gudest = weed_layer_get_pixel_data_packed(layer);
10405             convert_swap3delpre_frame(gusrc, width, height, irowstride, orowstride, gudest,
10406                                       -USE_THREADS);
10407           } else {
10408             if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10409             orowstride = weed_layer_get_rowstride(layer);
10410             gudest = weed_layer_get_pixel_data_packed(layer);
10411             convert_delpre_frame(gusrc, width, height, irowstride, orowstride, gudest, -USE_THREADS);
10412           }
10413         } else {
10414           /// outpl has post
10415           if (weed_palettes_rbswapped(inpl, outpl)) {
10416             convert_swap4_frame(gusrc, width, height, irowstride, irowstride, gusrc, -USE_THREADS);
10417             weed_layer_nullify_pixel_data(orig_layer);
10418           } else {
10419             convert_swapprepost_frame(gusrc, width, height, irowstride, irowstride, gusrc,
10420                                       -USE_THREADS);
10421             weed_layer_nullify_pixel_data(orig_layer);
10422           }
10423         }
10424       } else {
10425         /// outpl has pre
10426         convert_swap3prealpha_frame(gusrc, width, height, irowstride, -USE_THREADS);
10427         weed_layer_nullify_pixel_data(orig_layer);
10428       }
10429     }
10430     goto conv_done;
10431   }
10432 #endif
10433 
10434   switch (inpl) {
10435   case WEED_PALETTE_BGR24:
10436     gusrc = weed_layer_get_pixel_data_packed(layer);
10437     switch (outpl) {
10438     case WEED_PALETTE_RGBA32:
10439       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10440       orowstride = weed_layer_get_rowstride(layer);
10441       gudest = weed_layer_get_pixel_data_packed(layer);
10442       convert_swap3addpost_frame(gusrc, width, height, irowstride, orowstride, gudest, -USE_THREADS);
10443       break;
10444     case WEED_PALETTE_RGB24:
10445       convert_swap3_frame(gusrc, width, height, irowstride, irowstride, gusrc,
10446                           create_gamma_lut(1.0, weed_layer_get_gamma(layer), new_gamma_type),
10447                           -USE_THREADS);
10448       weed_layer_nullify_pixel_data(orig_layer);
10449       break;
10450     case WEED_PALETTE_BGRA32:
10451       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10452       orowstride = weed_layer_get_rowstride(layer);
10453       gudest = weed_layer_get_pixel_data_packed(layer);
10454       convert_addpost_frame(gusrc, width, height, irowstride, orowstride, gudest,
10455                             create_gamma_lut(1.0, weed_layer_get_gamma(layer), new_gamma_type),
10456                             -USE_THREADS);
10457       break;
10458     case WEED_PALETTE_ARGB32:
10459       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10460       orowstride = weed_layer_get_rowstride(layer);
10461       gudest = weed_layer_get_pixel_data_packed(layer);
10462       convert_swap3addpre_frame(gusrc, width, height, irowstride, orowstride, gudest, -USE_THREADS);
10463       break;
10464     case WEED_PALETTE_UYVY8888:
10465       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 1);
10466       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10467       orowstride = weed_layer_get_rowstride(layer);
10468       gudest = weed_layer_get_pixel_data_packed(layer);
10469       convert_bgr_to_uyvy_frame(gusrc, width, height, irowstride, orowstride,
10470                                 (uyvy_macropixel *)gudest, FALSE, oclamping,
10471                                 create_gamma_lut(1.0, weed_layer_get_gamma(layer), new_gamma_type),
10472                                 -USE_THREADS);
10473       break;
10474     case WEED_PALETTE_YUYV8888:
10475       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 1);
10476       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10477       orowstride = weed_layer_get_rowstride(layer);
10478       gudest = weed_layer_get_pixel_data_packed(layer);
10479       convert_bgr_to_yuyv_frame(gusrc, width, height, irowstride, orowstride,
10480                                 (yuyv_macropixel *)gudest, FALSE, oclamping,
10481                                 create_gamma_lut(1.0, weed_layer_get_gamma(layer), new_gamma_type),
10482                                 -USE_THREADS);
10483       break;
10484     case WEED_PALETTE_YUV888:
10485       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10486       gudest = weed_layer_get_pixel_data_packed(layer);
10487       orowstride = weed_layer_get_rowstride(layer);
10488       convert_bgr_to_yuv_frame(gusrc, width, height, irowstride, orowstride, gudest, FALSE, FALSE,
10489                                oclamping, -USE_THREADS);
10490       break;
10491     case WEED_PALETTE_YUVA8888:
10492       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10493       gudest = weed_layer_get_pixel_data_packed(layer);
10494       orowstride = weed_layer_get_rowstride(layer);
10495       convert_bgr_to_yuv_frame(gusrc, width, height, irowstride, orowstride, gudest, FALSE, TRUE,
10496                                oclamping, -USE_THREADS);
10497       break;
10498     case WEED_PALETTE_YUV422P:
10499       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10500       gudest_array = (uint8_t **)weed_get_voidptr_array(layer, WEED_LEAF_PIXEL_DATA, &error);
10501       ostrides = weed_layer_get_rowstrides(layer, NULL);
10502       convert_bgr_to_yuv420_frame(gusrc, width, height, irowstride, ostrides, gudest_array, TRUE,
10503                                   FALSE, WEED_YUV_SAMPLING_DEFAULT, oclamping);
10504       weed_set_int_value(layer, WEED_LEAF_YUV_SAMPLING, WEED_YUV_SAMPLING_DEFAULT);
10505       break;
10506     case WEED_PALETTE_YVU420P:
10507     case WEED_PALETTE_YUV420P:
10508       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10509       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
10510       ostrides = weed_layer_get_rowstrides(layer, NULL);
10511       convert_bgr_to_yuv420_frame(gusrc, width, height, irowstride, ostrides, gudest_array, FALSE,
10512                                   FALSE, osubspace, oclamping);
10513       weed_set_int_value(layer, WEED_LEAF_YUV_SAMPLING, WEED_YUV_SAMPLING_DEFAULT);
10514       break;
10515     case WEED_PALETTE_YUV444P:
10516       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10517       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
10518       orowstride = weed_layer_get_rowstride(layer);
10519       convert_bgr_to_yuvp_frame(gusrc, width, height, irowstride, orowstride, gudest_array, FALSE,
10520                                 FALSE, oclamping, -USE_THREADS);
10521       break;
10522     case WEED_PALETTE_YUVA4444P:
10523       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10524       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
10525       orowstride = weed_layer_get_rowstride(layer);
10526       convert_bgr_to_yuvp_frame(gusrc, width, height, irowstride, orowstride, gudest_array, FALSE,
10527                                 TRUE, oclamping, -USE_THREADS);
10528       break;
10529     case WEED_PALETTE_YUV411:
10530       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 2);
10531       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10532       gudest = weed_layer_get_pixel_data_packed(layer);
10533       convert_bgr_to_yuv411_frame(gusrc, width, height, irowstride, (yuv411_macropixel *)gudest,
10534                                   FALSE, oclamping);
10535       break;
10536     default:
10537       lives_printerr("Invalid palette conversion: %s to %s not written yet !!\n",
10538                      weed_palette_get_name(inpl),
10539                      weed_palette_get_name(outpl));
10540       goto memfail;
10541     }
10542     break;
10543   case WEED_PALETTE_RGBA32:
10544     gusrc = weed_layer_get_pixel_data_packed(layer);
10545     switch (outpl) {
10546     case WEED_PALETTE_BGR24:
10547       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10548       orowstride = weed_layer_get_rowstride(layer);
10549       gudest = weed_layer_get_pixel_data_packed(layer);
10550       convert_swap3delpost_frame(gusrc, width, height, irowstride, orowstride, gudest, -USE_THREADS);
10551       break;
10552     case WEED_PALETTE_RGB24:
10553       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10554       orowstride = weed_layer_get_rowstride(layer);
10555       gudest = weed_layer_get_pixel_data_packed(layer);
10556       convert_delpost_frame(gusrc, width, height, irowstride, orowstride, gudest,
10557                             create_gamma_lut(1.0, weed_layer_get_gamma(layer), new_gamma_type),
10558                             -USE_THREADS);
10559       break;
10560     case WEED_PALETTE_BGRA32:
10561       convert_swap3postalpha_frame(gusrc, width, height, irowstride, -USE_THREADS);
10562       weed_layer_nullify_pixel_data(orig_layer);
10563       break;
10564     case WEED_PALETTE_ARGB32:
10565       convert_swapprepost_frame(gusrc, width, height, irowstride, irowstride, gusrc, -USE_THREADS);
10566       weed_layer_nullify_pixel_data(orig_layer);
10567       break;
10568     case WEED_PALETTE_UYVY8888:
10569       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 1);
10570       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10571       orowstride = weed_layer_get_rowstride(layer);
10572       gudest = weed_layer_get_pixel_data_packed(layer);
10573       convert_rgb_to_uyvy_frame(gusrc, width, height, irowstride, orowstride, (uyvy_macropixel *)gudest, TRUE,
10574                                 oclamping,  create_gamma_lut(1.0, weed_layer_get_gamma(layer), new_gamma_type), -USE_THREADS);
10575       break;
10576     case WEED_PALETTE_YUYV8888:
10577       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 1);
10578       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10579       orowstride = weed_layer_get_rowstride(layer);
10580       gudest = weed_layer_get_pixel_data_packed(layer);
10581       convert_rgb_to_yuyv_frame(gusrc, width, height, irowstride, orowstride, (yuyv_macropixel *)gudest, TRUE,
10582                                 oclamping,
10583                                 create_gamma_lut(1.0, weed_layer_get_gamma(layer), new_gamma_type),
10584                                 -USE_THREADS);
10585       break;
10586     case WEED_PALETTE_YUV888:
10587       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10588       gudest = weed_layer_get_pixel_data_packed(layer);
10589       orowstride = weed_layer_get_rowstride(layer);
10590       convert_rgb_to_yuv_frame(gusrc, width, height, irowstride, orowstride, gudest, TRUE, FALSE, oclamping, -USE_THREADS);
10591       break;
10592     case WEED_PALETTE_YUVA8888:
10593       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10594       gudest = weed_layer_get_pixel_data_packed(layer);
10595       orowstride = weed_layer_get_rowstride(layer);
10596       convert_rgb_to_yuv_frame(gusrc, width, height, irowstride, orowstride, gudest, TRUE, TRUE, oclamping, -USE_THREADS);
10597       break;
10598     case WEED_PALETTE_YUV422P:
10599       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10600       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
10601       ostrides = weed_layer_get_rowstrides(layer, NULL);
10602       convert_rgb_to_yuv420_frame(gusrc, width, height, irowstride, ostrides, gudest_array, TRUE, TRUE,
10603                                   WEED_YUV_SAMPLING_DEFAULT, oclamping);
10604       weed_set_int_value(layer, WEED_LEAF_YUV_SAMPLING, WEED_YUV_SAMPLING_DEFAULT);
10605       break;
10606     case WEED_PALETTE_YUV420P:
10607     case WEED_PALETTE_YVU420P:
10608       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10609       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
10610       ostrides = weed_layer_get_rowstrides(layer, NULL);
10611       convert_rgb_to_yuv420_frame(gusrc, width, height, irowstride, ostrides, gudest_array, FALSE, TRUE, osubspace, oclamping);
10612       weed_set_int_value(layer, WEED_LEAF_YUV_SAMPLING, WEED_YUV_SAMPLING_DEFAULT);
10613       break;
10614     case WEED_PALETTE_YUV444P:
10615       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10616       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
10617       orowstride = weed_layer_get_rowstride(layer);
10618       convert_rgb_to_yuvp_frame(gusrc, width, height, irowstride, orowstride, gudest_array, TRUE, FALSE, oclamping, -USE_THREADS);
10619       break;
10620     case WEED_PALETTE_YUVA4444P:
10621       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10622       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
10623       orowstride = weed_layer_get_rowstride(layer);
10624       convert_rgb_to_yuvp_frame(gusrc, width, height, irowstride, orowstride, gudest_array, TRUE, TRUE, oclamping, -USE_THREADS);
10625       break;
10626     case WEED_PALETTE_YUV411:
10627       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 2);
10628       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10629       gudest = weed_layer_get_pixel_data_packed(layer);
10630       convert_rgb_to_yuv411_frame(gusrc, width, height, irowstride, (yuv411_macropixel *)gudest, TRUE, oclamping);
10631       break;
10632     default:
10633       lives_printerr("Invalid palette conversion: %s to %s not written yet !!\n", weed_palette_get_name(inpl),
10634                      weed_palette_get_name(outpl));
10635       goto memfail;
10636     }
10637     break;
10638   case WEED_PALETTE_RGB24:
10639     gusrc = weed_layer_get_pixel_data_packed(layer);
10640     switch (outpl) {
10641     case WEED_PALETTE_BGR24:
10642       convert_swap3_frame(gusrc, width, height, irowstride, irowstride, gusrc,
10643                           create_gamma_lut(1.0, weed_layer_get_gamma(layer), new_gamma_type), -USE_THREADS);
10644       weed_layer_nullify_pixel_data(orig_layer);
10645       break;
10646     case WEED_PALETTE_RGBA32:
10647       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10648       orowstride = weed_layer_get_rowstride(layer);
10649       gudest = weed_layer_get_pixel_data_packed(layer);
10650       convert_addpost_frame(gusrc, width, height, irowstride, orowstride, gudest,
10651                             create_gamma_lut(1.0, weed_layer_get_gamma(layer), new_gamma_type), -USE_THREADS);
10652       break;
10653     case WEED_PALETTE_BGRA32:
10654       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10655       orowstride = weed_layer_get_rowstride(layer);
10656       gudest = weed_layer_get_pixel_data_packed(layer);
10657       convert_swap3addpost_frame(gusrc, width, height, irowstride, orowstride, gudest, -USE_THREADS);
10658       break;
10659     case WEED_PALETTE_ARGB32:
10660       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10661       orowstride = weed_layer_get_rowstride(layer);
10662       gudest = weed_layer_get_pixel_data_packed(layer);
10663       convert_addpre_frame(gusrc, width, height, irowstride, orowstride, gudest, -USE_THREADS);
10664       break;
10665     case WEED_PALETTE_UYVY8888:
10666       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 1);
10667       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10668       orowstride = weed_layer_get_rowstride(layer);
10669       gudest = weed_layer_get_pixel_data_packed(layer);
10670       convert_rgb_to_uyvy_frame(gusrc, width, height, irowstride, orowstride, (uyvy_macropixel *)gudest, FALSE,
10671                                 oclamping,
10672                                 create_gamma_lut(1.0, weed_layer_get_gamma(layer), new_gamma_type),
10673                                 -USE_THREADS);
10674       break;
10675     case WEED_PALETTE_YUYV8888:
10676       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 1);
10677       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10678       orowstride = weed_layer_get_rowstride(layer);
10679       gudest = weed_layer_get_pixel_data_packed(layer);
10680       convert_rgb_to_yuyv_frame(gusrc, width, height, irowstride, orowstride, (yuyv_macropixel *)gudest, FALSE,
10681                                 oclamping,
10682                                 create_gamma_lut(1.0, weed_layer_get_gamma(layer), new_gamma_type),
10683                                 -USE_THREADS);
10684       break;
10685     case WEED_PALETTE_YUV888:
10686       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10687       gudest = weed_layer_get_pixel_data_packed(layer);
10688       orowstride = weed_layer_get_rowstride(layer);
10689       convert_rgb_to_yuv_frame(gusrc, width, height, irowstride, orowstride, gudest, FALSE, FALSE, oclamping, -USE_THREADS);
10690       break;
10691     case WEED_PALETTE_YUVA8888:
10692       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10693       gudest = weed_layer_get_pixel_data_packed(layer);
10694       orowstride = weed_layer_get_rowstride(layer);
10695       convert_rgb_to_yuv_frame(gusrc, width, height, irowstride, orowstride, gudest, FALSE, TRUE, oclamping, -USE_THREADS);
10696       break;
10697     case WEED_PALETTE_YUV422P:
10698       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10699       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
10700       ostrides = weed_layer_get_rowstrides(layer, NULL);
10701       convert_rgb_to_yuv420_frame(gusrc, width, height, irowstride, ostrides, gudest_array, TRUE, FALSE, osubspace, oclamping);
10702       break;
10703     case WEED_PALETTE_YUV420P:
10704     case WEED_PALETTE_YVU420P:
10705       if (weed_get_int_value(layer, WEED_LEAF_PIXEL_BITS, NULL) == 16) width >>= 1;
10706       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10707       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
10708       ostrides = weed_layer_get_rowstrides(layer, NULL);
10709       if (weed_get_int_value(layer, WEED_LEAF_PIXEL_BITS, NULL) == 16) width = -width;
10710       convert_rgb_to_yuv420_frame(gusrc, width, height, irowstride, ostrides, gudest_array, FALSE,
10711                                   FALSE, WEED_YUV_SAMPLING_DEFAULT, oclamping);
10712       weed_set_int_value(layer, WEED_LEAF_YUV_SAMPLING, WEED_YUV_SAMPLING_DEFAULT);
10713       break;
10714     case WEED_PALETTE_YUV444P:
10715       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10716       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
10717       orowstride = weed_layer_get_rowstride(layer);
10718       convert_rgb_to_yuvp_frame(gusrc, width, height, irowstride, orowstride, gudest_array, FALSE, FALSE, oclamping, -USE_THREADS);
10719       break;
10720     case WEED_PALETTE_YUVA4444P:
10721       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10722       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
10723       orowstride = weed_layer_get_rowstride(layer);
10724       convert_rgb_to_yuvp_frame(gusrc, width, height, irowstride, orowstride, gudest_array, FALSE, TRUE, oclamping, -USE_THREADS);
10725       break;
10726     case WEED_PALETTE_YUV411:
10727       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 2);
10728       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10729       gudest = weed_layer_get_pixel_data_packed(layer);
10730       convert_rgb_to_yuv411_frame(gusrc, width, height, irowstride, (yuv411_macropixel *)gudest, FALSE, oclamping);
10731       break;
10732     default:
10733       lives_printerr("Invalid palette conversion: %s to %s not written yet !!\n", weed_palette_get_name(inpl),
10734                      weed_palette_get_name(outpl));
10735       goto memfail;
10736     }
10737     break;
10738   case WEED_PALETTE_BGRA32:
10739     gusrc = weed_layer_get_pixel_data_packed(layer);
10740     switch (outpl) {
10741     case WEED_PALETTE_BGR24:
10742       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10743       orowstride = weed_layer_get_rowstride(layer);
10744       gudest = weed_layer_get_pixel_data_packed(layer);
10745       convert_delpost_frame(gusrc, width, height, irowstride, orowstride, gudest,
10746                             create_gamma_lut(1.0, weed_layer_get_gamma(layer), new_gamma_type), -USE_THREADS);
10747       break;
10748     case WEED_PALETTE_RGB24:
10749       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10750       orowstride = weed_layer_get_rowstride(layer);
10751       gudest = weed_layer_get_pixel_data_packed(layer);
10752       convert_swap3delpost_frame(gusrc, width, height, irowstride, orowstride, gudest, -USE_THREADS);
10753       break;
10754     case WEED_PALETTE_RGBA32:
10755       convert_swap3postalpha_frame(gusrc, width, height, irowstride, -USE_THREADS);
10756       weed_layer_nullify_pixel_data(orig_layer);
10757       break;
10758     case WEED_PALETTE_ARGB32:
10759       convert_swap4_frame(gusrc, width, height, irowstride, irowstride, gusrc, -USE_THREADS);
10760       weed_layer_nullify_pixel_data(orig_layer);
10761       break;
10762     case WEED_PALETTE_UYVY8888:
10763       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 1);
10764       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10765       orowstride = weed_layer_get_rowstride(layer);
10766       gudest = weed_layer_get_pixel_data_packed(layer);
10767       convert_bgr_to_uyvy_frame(gusrc, width, height, irowstride, orowstride, (uyvy_macropixel *)gudest, TRUE,
10768                                 oclamping,
10769                                 create_gamma_lut(1.0, weed_layer_get_gamma(layer), new_gamma_type),
10770                                 -USE_THREADS);
10771       break;
10772     case WEED_PALETTE_YUYV8888:
10773       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 1);
10774       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10775       orowstride = weed_layer_get_rowstride(layer);
10776       gudest = weed_layer_get_pixel_data_packed(layer);
10777       convert_bgr_to_yuyv_frame(gusrc, width, height, irowstride, orowstride, (yuyv_macropixel *)gudest, TRUE,
10778                                 oclamping,
10779                                 create_gamma_lut(1.0, weed_layer_get_gamma(layer), new_gamma_type),
10780                                 -USE_THREADS);
10781       break;
10782     case WEED_PALETTE_YUV888:
10783       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10784       gudest = weed_layer_get_pixel_data_packed(layer);
10785       orowstride = weed_layer_get_rowstride(layer);
10786       convert_bgr_to_yuv_frame(gusrc, width, height, irowstride, orowstride, gudest, TRUE, FALSE, oclamping, -USE_THREADS);
10787       break;
10788     case WEED_PALETTE_YUVA8888:
10789       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10790       gudest = weed_layer_get_pixel_data_packed(layer);
10791       orowstride = weed_layer_get_rowstride(layer);
10792       convert_bgr_to_yuv_frame(gusrc, width, height, irowstride, orowstride, gudest, TRUE, TRUE, oclamping, -USE_THREADS);
10793       break;
10794     case WEED_PALETTE_YUV422P:
10795       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10796       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
10797       ostrides = weed_layer_get_rowstrides(layer, NULL);
10798       convert_bgr_to_yuv420_frame(gusrc, width, height, irowstride, ostrides, gudest_array, TRUE, TRUE,
10799                                   WEED_YUV_SAMPLING_DEFAULT, oclamping);
10800       weed_set_int_value(layer, WEED_LEAF_YUV_SAMPLING, WEED_YUV_SAMPLING_DEFAULT);
10801       break;
10802     case WEED_PALETTE_YVU420P:
10803     case WEED_PALETTE_YUV420P:
10804       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10805       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
10806       ostrides = weed_layer_get_rowstrides(layer, NULL);
10807       convert_bgr_to_yuv420_frame(gusrc, width, height, irowstride, ostrides, gudest_array, FALSE, TRUE, osubspace, oclamping);
10808       weed_set_int_value(layer, WEED_LEAF_YUV_SAMPLING, WEED_YUV_SAMPLING_DEFAULT);
10809       break;
10810     case WEED_PALETTE_YUV444P:
10811       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10812       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
10813       orowstride = weed_layer_get_rowstride(layer);
10814       convert_bgr_to_yuvp_frame(gusrc, width, height, irowstride, orowstride, gudest_array, TRUE, FALSE, oclamping, -USE_THREADS);
10815       break;
10816     case WEED_PALETTE_YUVA4444P:
10817       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10818       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
10819       orowstride = weed_layer_get_rowstride(layer);
10820       convert_bgr_to_yuvp_frame(gusrc, width, height, irowstride, orowstride, gudest_array, TRUE, TRUE, oclamping, -USE_THREADS);
10821       break;
10822     case WEED_PALETTE_YUV411:
10823       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 2);
10824       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10825       gudest = weed_layer_get_pixel_data_packed(layer);
10826       convert_bgr_to_yuv411_frame(gusrc, width, height, irowstride, (yuv411_macropixel *)gudest, TRUE, oclamping);
10827       break;
10828     default:
10829       lives_printerr("Invalid palette conversion: %s to %s not written yet !!\n", weed_palette_get_name(inpl),
10830                      weed_palette_get_name(outpl));
10831       goto memfail;
10832     }
10833     break;
10834   case WEED_PALETTE_ARGB32:
10835     gusrc = weed_layer_get_pixel_data_packed(layer);
10836     switch (outpl) {
10837     case WEED_PALETTE_BGR24:
10838       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10839       orowstride = weed_layer_get_rowstride(layer);
10840       gudest = weed_layer_get_pixel_data_packed(layer);
10841       convert_swap3delpre_frame(gusrc, width, height, irowstride, orowstride, gudest, -USE_THREADS);
10842       break;
10843     case WEED_PALETTE_RGB24:
10844       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10845       orowstride = weed_layer_get_rowstride(layer);
10846       gudest = weed_layer_get_pixel_data_packed(layer);
10847       convert_delpre_frame(gusrc, width, height, irowstride, orowstride, gudest, -USE_THREADS);
10848       break;
10849     case WEED_PALETTE_RGBA32:
10850       convert_swapprepost_frame(gusrc, width, height, irowstride, irowstride, gusrc, -USE_THREADS);
10851       weed_layer_nullify_pixel_data(orig_layer);
10852       break;
10853     case WEED_PALETTE_BGRA32:
10854       convert_swap4_frame(gusrc, width, height, irowstride, irowstride, gusrc, -USE_THREADS);
10855       weed_layer_nullify_pixel_data(orig_layer);
10856       break;
10857     case WEED_PALETTE_UYVY8888:
10858       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 1);
10859       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10860       orowstride = weed_layer_get_rowstride(layer);
10861       gudest = weed_layer_get_pixel_data_packed(layer);
10862       convert_argb_to_uyvy_frame(gusrc, width, height, irowstride, orowstride, (uyvy_macropixel *)gudest, oclamping,
10863                                  create_gamma_lut(1.0, weed_layer_get_gamma(layer), new_gamma_type),
10864                                  -USE_THREADS);
10865       break;
10866     case WEED_PALETTE_YUYV8888:
10867       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 1);
10868       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10869       orowstride = weed_layer_get_rowstride(layer);
10870       gudest = weed_layer_get_pixel_data_packed(layer);
10871       convert_argb_to_yuyv_frame(gusrc, width, height, irowstride, orowstride, (yuyv_macropixel *)gudest, oclamping,
10872                                  create_gamma_lut(1.0, weed_layer_get_gamma(layer), new_gamma_type),
10873                                  -USE_THREADS);
10874       break;
10875     case WEED_PALETTE_YUV888:
10876       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10877       gudest = weed_layer_get_pixel_data_packed(layer);
10878       orowstride = weed_layer_get_rowstride(layer);
10879       convert_argb_to_yuv_frame(gusrc, width, height, irowstride, orowstride, gudest, FALSE, oclamping, -USE_THREADS);
10880       break;
10881     case WEED_PALETTE_YUVA8888:
10882       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10883       gudest = weed_layer_get_pixel_data_packed(layer);
10884       orowstride = weed_layer_get_rowstride(layer);
10885       convert_argb_to_yuv_frame(gusrc, width, height, irowstride, orowstride, gudest, TRUE, oclamping, -USE_THREADS);
10886       break;
10887     case WEED_PALETTE_YUV444P:
10888       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10889       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
10890       orowstride = weed_layer_get_rowstride(layer);
10891       convert_argb_to_yuvp_frame(gusrc, width, height, irowstride, orowstride, gudest_array, FALSE, oclamping, -USE_THREADS);
10892       break;
10893     case WEED_PALETTE_YUVA4444P:
10894       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10895       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
10896       orowstride = weed_layer_get_rowstride(layer);
10897       convert_argb_to_yuvp_frame(gusrc, width, height, irowstride, orowstride, gudest_array, TRUE, oclamping, -USE_THREADS);
10898       break;
10899     case WEED_PALETTE_YUV422P:
10900       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10901       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
10902       ostrides = weed_layer_get_rowstrides(layer, NULL);
10903       convert_argb_to_yuv420_frame(gusrc, width, height, irowstride, ostrides, gudest_array, TRUE,
10904                                    WEED_YUV_SAMPLING_DEFAULT, oclamping);
10905       weed_set_int_value(layer, WEED_LEAF_YUV_SAMPLING, WEED_YUV_SAMPLING_DEFAULT);
10906       break;
10907     case WEED_PALETTE_YUV420P:
10908     case WEED_PALETTE_YVU420P:
10909       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10910       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
10911       ostrides = weed_layer_get_rowstrides(layer, NULL);
10912       convert_argb_to_yuv420_frame(gusrc, width, height, irowstride, ostrides, gudest_array, FALSE, osubspace, oclamping);
10913       weed_set_int_value(layer, WEED_LEAF_YUV_SAMPLING, WEED_YUV_SAMPLING_DEFAULT);
10914       break;
10915     case WEED_PALETTE_YUV411:
10916       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 2);
10917       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10918       gudest = weed_layer_get_pixel_data_packed(layer);
10919       convert_argb_to_yuv411_frame(gusrc, width, height, irowstride, (yuv411_macropixel *)gudest, oclamping);
10920       break;
10921     default:
10922       lives_printerr("Invalid palette conversion: %s to %s not written yet !!\n", weed_palette_get_name(inpl),
10923                      weed_palette_get_name(outpl));
10924       goto memfail;
10925     }
10926     break;
10927   case WEED_PALETTE_YUV444P:
10928     gusrc_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
10929     switch (outpl) {
10930     case WEED_PALETTE_YUV422P:
10931       if (!create_empty_pixel_data(layer, FALSE, FALSE)) goto memfail;
10932       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
10933       lives_free(gudest_array[0]);
10934       gudest_array[0] = gusrc_array[0];
10935       weed_set_voidptr_array(layer, WEED_LEAF_PIXEL_DATA, 3, (void **)gudest_array);
10936       ostrides = weed_layer_get_rowstrides(layer, NULL);
10937       convert_halve_chroma(gusrc_array, width, height, istrides, ostrides, gudest_array, iclamping);
10938       gusrc_array[0] = NULL;
10939       weed_layer_set_pixel_data(orig_layer, (void **)gusrc_array, 3);
10940       break;
10941     case WEED_PALETTE_RGB24:
10942       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10943       orowstride = weed_layer_get_rowstride(layer);
10944       gudest = weed_layer_get_pixel_data_packed(layer);
10945       convert_yuv_planar_to_rgb_frame(gusrc_array, width, height, irowstride, orowstride, gudest, FALSE, FALSE, iclamping,
10946                                       -USE_THREADS);
10947       break;
10948     case WEED_PALETTE_RGBA32:
10949       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10950       orowstride = weed_layer_get_rowstride(layer);
10951       gudest = weed_layer_get_pixel_data_packed(layer);
10952       convert_yuv_planar_to_rgb_frame(gusrc_array, width, height, irowstride, orowstride, gudest, FALSE, TRUE, iclamping,
10953                                       -USE_THREADS);
10954       break;
10955     case WEED_PALETTE_BGR24:
10956       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10957       orowstride = weed_layer_get_rowstride(layer);
10958       gudest = weed_layer_get_pixel_data_packed(layer);
10959       convert_yuv_planar_to_bgr_frame(gusrc_array, width, height, irowstride, orowstride, gudest, FALSE, FALSE, iclamping,
10960                                       -USE_THREADS);
10961       break;
10962     case WEED_PALETTE_BGRA32:
10963       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10964       orowstride = weed_layer_get_rowstride(layer);
10965       gudest = weed_layer_get_pixel_data_packed(layer);
10966       convert_yuv_planar_to_bgr_frame(gusrc_array, width, height, irowstride, orowstride, gudest, FALSE, TRUE, iclamping,
10967                                       -USE_THREADS);
10968       break;
10969     case WEED_PALETTE_ARGB32:
10970       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10971       orowstride = weed_layer_get_rowstride(layer);
10972       gudest = weed_layer_get_pixel_data_packed(layer);
10973       convert_yuv_planar_to_argb_frame(gusrc_array, width, height, irowstride, orowstride, gudest, FALSE, iclamping, -USE_THREADS);
10974       break;
10975     case WEED_PALETTE_UYVY8888:
10976       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 1);
10977       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10978       orowstride = weed_layer_get_rowstride(layer);
10979       gudest = weed_layer_get_pixel_data_packed(layer);
10980       convert_yuv_planar_to_uyvy_frame(gusrc_array, width, height, irowstride, orowstride, (uyvy_macropixel *)gudest, iclamping);
10981       break;
10982     case WEED_PALETTE_YUYV8888:
10983       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 1);
10984       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10985       orowstride = weed_layer_get_rowstride(layer);
10986       gudest = weed_layer_get_pixel_data_packed(layer);
10987       convert_yuv_planar_to_yuyv_frame(gusrc_array, width, height, irowstride, orowstride, (yuyv_macropixel *)gudest, iclamping);
10988       break;
10989     case WEED_PALETTE_YUV888:
10990       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10991       gudest = weed_layer_get_pixel_data_packed(layer);
10992       orowstride = weed_layer_get_rowstride(layer);
10993       convert_combineplanes_frame(gusrc_array, width, height, irowstride, orowstride, gudest, FALSE, FALSE);
10994       break;
10995     case WEED_PALETTE_YUVA8888:
10996       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
10997       gudest = weed_layer_get_pixel_data_packed(layer);
10998       orowstride = weed_layer_get_rowstride(layer);
10999       convert_combineplanes_frame(gusrc_array, width, height, irowstride, orowstride, gudest, FALSE, TRUE);
11000       break;
11001     case WEED_PALETTE_YUVA4444P:
11002       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11003       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11004       orowstride = weed_layer_get_rowstride(layer);
11005       convert_yuvp_to_yuvap_frame(gusrc_array, width, height, irowstride, orowstride, gudest_array);
11006       break;
11007     case WEED_PALETTE_YUV420P:
11008     case WEED_PALETTE_YVU420P:
11009       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11010       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11011       ostrides = weed_layer_get_rowstrides(layer, NULL);
11012       convert_yuvp_to_yuv420_frame(gusrc_array, width, height, istrides, ostrides, gudest_array, iclamping);
11013       weed_set_int_value(layer, WEED_LEAF_YUV_SAMPLING, WEED_YUV_SAMPLING_DEFAULT);
11014       break;
11015     case WEED_PALETTE_YUV411:
11016       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 2);
11017       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11018       gudest = weed_layer_get_pixel_data_packed(layer);
11019       convert_yuvp_to_yuv411_frame(gusrc_array, width, height, irowstride, (yuv411_macropixel *)gudest, iclamping);
11020       break;
11021     default:
11022       lives_printerr("Invalid palette conversion: %s to %s not written yet !!\n", weed_palette_get_name(inpl),
11023                      weed_palette_get_name(outpl));
11024       goto memfail;
11025     }
11026     break;
11027   case WEED_PALETTE_YUVA4444P:
11028     gusrc_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11029     switch (outpl) {
11030     case WEED_PALETTE_YUV422P:
11031       if (!create_empty_pixel_data(layer, FALSE, FALSE)) goto memfail;
11032       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11033       lives_free(gudest_array[0]);
11034       gudest_array[0] = gusrc_array[0];
11035       weed_set_voidptr_array(layer, WEED_LEAF_PIXEL_DATA, 3, (void **)gudest_array);
11036       ostrides = weed_layer_get_rowstrides(layer, NULL);
11037       convert_halve_chroma(gusrc_array, width, height, istrides, ostrides, gudest_array, iclamping);
11038       gusrc_array[0] = NULL;
11039       weed_layer_set_pixel_data(orig_layer, (void **)gusrc_array, 4);
11040       break;
11041     case WEED_PALETTE_RGB24:
11042       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11043       orowstride = weed_layer_get_rowstride(layer);
11044       gudest = weed_layer_get_pixel_data_packed(layer);
11045       convert_yuv_planar_to_rgb_frame(gusrc_array, width, height, irowstride, orowstride, gudest, TRUE, FALSE, iclamping,
11046                                       -USE_THREADS);
11047       break;
11048     case WEED_PALETTE_RGBA32:
11049       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11050       orowstride = weed_layer_get_rowstride(layer);
11051       gudest = weed_layer_get_pixel_data_packed(layer);
11052       convert_yuv_planar_to_rgb_frame(gusrc_array, width, height, irowstride, orowstride, gudest, TRUE, TRUE, iclamping,
11053                                       -USE_THREADS);
11054       break;
11055     case WEED_PALETTE_BGR24:
11056       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11057       orowstride = weed_layer_get_rowstride(layer);
11058       gudest = weed_layer_get_pixel_data_packed(layer);
11059       convert_yuv_planar_to_bgr_frame(gusrc_array, width, height, irowstride, orowstride, gudest, TRUE, FALSE, iclamping,
11060                                       -USE_THREADS);
11061       break;
11062     case WEED_PALETTE_BGRA32:
11063       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11064       orowstride = weed_layer_get_rowstride(layer);
11065       gudest = weed_layer_get_pixel_data_packed(layer);
11066       convert_yuv_planar_to_bgr_frame(gusrc_array, width, height, irowstride, orowstride, gudest, TRUE, TRUE, iclamping,
11067                                       -USE_THREADS);
11068       break;
11069     case WEED_PALETTE_ARGB32:
11070       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11071       orowstride = weed_layer_get_rowstride(layer);
11072       gudest = weed_layer_get_pixel_data_packed(layer);
11073       convert_yuv_planar_to_argb_frame(gusrc_array, width, height, irowstride, orowstride, gudest, TRUE, iclamping, -USE_THREADS);
11074       break;
11075     case WEED_PALETTE_UYVY8888:
11076       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 1);
11077       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11078       orowstride = weed_layer_get_rowstride(layer);
11079       gudest = weed_layer_get_pixel_data_packed(layer);
11080       convert_yuv_planar_to_uyvy_frame(gusrc_array, width, height, irowstride, orowstride, (uyvy_macropixel *)gudest, iclamping);
11081       break;
11082     case WEED_PALETTE_YUYV8888:
11083       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 1);
11084       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11085       orowstride = weed_layer_get_rowstride(layer);
11086       gudest = weed_layer_get_pixel_data_packed(layer);
11087       convert_yuv_planar_to_yuyv_frame(gusrc_array, width, height, irowstride, orowstride, (yuyv_macropixel *)gudest, iclamping);
11088       break;
11089     case WEED_PALETTE_YUV888:
11090       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11091       gudest = weed_layer_get_pixel_data_packed(layer);
11092       orowstride = weed_layer_get_rowstride(layer);
11093       convert_combineplanes_frame(gusrc_array, width, height, irowstride, orowstride, gudest, TRUE, FALSE);
11094       break;
11095     case WEED_PALETTE_YUVA8888:
11096       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11097       gudest = weed_layer_get_pixel_data_packed(layer);
11098       orowstride = weed_layer_get_rowstride(layer);
11099       convert_combineplanes_frame(gusrc_array, width, height, irowstride, orowstride, gudest, TRUE, TRUE);
11100       break;
11101     case WEED_PALETTE_YUV444P:
11102       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11103       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11104       orowstride = weed_layer_get_rowstride(layer);
11105       convert_yuvap_to_yuvp_frame(gusrc_array, width, height, irowstride, orowstride, gudest_array);
11106       break;
11107     case WEED_PALETTE_YUV420P:
11108     case WEED_PALETTE_YVU420P:
11109       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11110       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11111       ostrides = weed_layer_get_rowstrides(layer, NULL);
11112       convert_yuvp_to_yuv420_frame(gusrc_array, width, height, istrides, ostrides, gudest_array, iclamping);
11113       weed_set_int_value(layer, WEED_LEAF_YUV_SAMPLING, WEED_YUV_SAMPLING_DEFAULT);
11114       break;
11115     case WEED_PALETTE_YUV411:
11116       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 2);
11117       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11118       gudest = weed_layer_get_pixel_data_packed(layer);
11119       convert_yuvp_to_yuv411_frame(gusrc_array, width, height, irowstride, (yuv411_macropixel *)gudest, iclamping);
11120       break;
11121     default:
11122       lives_printerr("Invalid palette conversion: %s to %s not written yet !!\n", weed_palette_get_name(inpl),
11123                      weed_palette_get_name(outpl));
11124       goto memfail;
11125     }
11126     break;
11127   case WEED_PALETTE_UYVY8888:
11128     gusrc = weed_layer_get_pixel_data_packed(layer);
11129     switch (outpl) {
11130     case WEED_PALETTE_YUYV8888:
11131       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11132       orowstride = weed_layer_get_rowstride(layer);
11133       gudest = weed_layer_get_pixel_data_packed(layer);
11134       convert_swab_frame(gusrc, width, height, irowstride, orowstride, gudest, -USE_THREADS);
11135       break;
11136     case WEED_PALETTE_YUV422P:
11137       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 1);
11138       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11139       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11140       convert_uyvy_to_yuv422_frame((uyvy_macropixel *)gusrc, width, height, gudest_array);
11141       break;
11142     case WEED_PALETTE_RGB24:
11143       //weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 1);
11144       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11145       gudest = weed_layer_get_pixel_data_packed(layer);
11146       orowstride = weed_layer_get_rowstride(layer);
11147       convert_uyvy_to_rgb_frame((uyvy_macropixel *)gusrc, width, height, irowstride, orowstride, gudest,
11148                                 FALSE, iclamping, isubspace, -USE_THREADS);
11149       break;
11150     case WEED_PALETTE_RGBA32:
11151       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 1);
11152       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11153       gudest = weed_layer_get_pixel_data_packed(layer);
11154       orowstride = weed_layer_get_rowstride(layer);
11155       convert_uyvy_to_rgb_frame((uyvy_macropixel *)gusrc, width, height, irowstride, orowstride, gudest,
11156                                 TRUE, iclamping, isampling, -USE_THREADS);
11157       break;
11158     case WEED_PALETTE_BGR24:
11159       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 1);
11160       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11161       gudest = weed_layer_get_pixel_data_packed(layer);
11162       orowstride = weed_layer_get_rowstride(layer);
11163       convert_uyvy_to_bgr_frame((uyvy_macropixel *)gusrc, width, height, irowstride, orowstride, gudest,
11164                                 FALSE, iclamping, -USE_THREADS);
11165       break;
11166     case WEED_PALETTE_BGRA32:
11167       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 1);
11168       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11169       gudest = weed_layer_get_pixel_data_packed(layer);
11170       orowstride = weed_layer_get_rowstride(layer);
11171       convert_uyvy_to_bgr_frame((uyvy_macropixel *)gusrc, width, height, irowstride, orowstride, gudest,
11172                                 TRUE, iclamping, -USE_THREADS);
11173       break;
11174     case WEED_PALETTE_ARGB32:
11175       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 1);
11176       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11177       gudest = weed_layer_get_pixel_data_packed(layer);
11178       orowstride = weed_layer_get_rowstride(layer);
11179       convert_uyvy_to_argb_frame((uyvy_macropixel *)gusrc, width, height, irowstride, orowstride,
11180                                  gudest, iclamping, -USE_THREADS);
11181       break;
11182     case WEED_PALETTE_YUV444P:
11183       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 1);
11184       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11185       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11186       ostrides = weed_layer_get_rowstrides(layer, NULL);
11187       convert_uyvy_to_yuvp_frame((uyvy_macropixel *)gusrc, width, height, irowstride, ostrides, gudest_array, FALSE);
11188       break;
11189     case WEED_PALETTE_YUVA4444P:
11190       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 1);
11191       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11192       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11193       ostrides = weed_layer_get_rowstrides(layer, NULL);
11194       convert_uyvy_to_yuvp_frame((uyvy_macropixel *)gusrc, width, height, irowstride, ostrides, gudest_array, TRUE);
11195       break;
11196     case WEED_PALETTE_YUV888:
11197       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 1);
11198       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11199       gudest = weed_layer_get_pixel_data_packed(layer);
11200       orowstride = weed_layer_get_rowstride(layer);
11201       convert_uyvy_to_yuv888_frame((uyvy_macropixel *)gusrc, width, height, irowstride, orowstride, gudest, FALSE);
11202       break;
11203     case WEED_PALETTE_YUVA8888:
11204       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 1);
11205       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11206       gudest = weed_layer_get_pixel_data_packed(layer);
11207       orowstride = weed_layer_get_rowstride(layer);
11208       convert_uyvy_to_yuv888_frame((uyvy_macropixel *)gusrc, width, height, irowstride, orowstride, gudest, TRUE);
11209       break;
11210     case WEED_PALETTE_YUV420P:
11211     case WEED_PALETTE_YVU420P:
11212       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 1);
11213       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11214       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11215       convert_uyvy_to_yuv420_frame((uyvy_macropixel *)gusrc, width, height, gudest_array, iclamping);
11216       weed_set_int_value(layer, WEED_LEAF_YUV_SAMPLING, WEED_YUV_SAMPLING_DEFAULT);
11217       break;
11218     case WEED_PALETTE_YUV411:
11219       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 1);
11220       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11221       gudest = weed_layer_get_pixel_data_packed(layer);
11222       convert_uyvy_to_yuv411_frame((uyvy_macropixel *)gusrc, width, height, (yuv411_macropixel *)gudest, iclamping);
11223       break;
11224     default:
11225       lives_printerr("Invalid palette conversion: %s to %s not written yet !!\n", weed_palette_get_name(inpl),
11226                      weed_palette_get_name(outpl));
11227       goto memfail;
11228     }
11229     break;
11230   case WEED_PALETTE_YUYV8888:
11231     gusrc = weed_layer_get_pixel_data_packed(layer);
11232     switch (outpl) {
11233     case WEED_PALETTE_UYVY8888:
11234       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11235       orowstride = weed_layer_get_rowstride(layer);
11236       gudest = weed_layer_get_pixel_data_packed(layer);
11237       convert_swab_frame(gusrc, width, height, irowstride, orowstride, gudest, -USE_THREADS);
11238       break;
11239     case WEED_PALETTE_YUV422P:
11240       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 1);
11241       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11242       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11243       convert_yuyv_to_yuv422_frame((yuyv_macropixel *)gusrc, width, height, gudest_array);
11244       break;
11245     case WEED_PALETTE_RGB24:
11246       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 1);
11247       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11248       gudest = weed_layer_get_pixel_data_packed(layer);
11249       orowstride = weed_layer_get_rowstride(layer);
11250       convert_yuyv_to_rgb_frame((yuyv_macropixel *)gusrc, width, height, irowstride, orowstride, gudest,
11251                                 FALSE, iclamping, -USE_THREADS);
11252       break;
11253     case WEED_PALETTE_RGBA32:
11254       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 1);
11255       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11256       gudest = weed_layer_get_pixel_data_packed(layer);
11257       orowstride = weed_layer_get_rowstride(layer);
11258       convert_yuyv_to_rgb_frame((yuyv_macropixel *)gusrc, width, height, irowstride, orowstride, gudest,
11259                                 TRUE, iclamping, -USE_THREADS);
11260       break;
11261     case WEED_PALETTE_BGR24:
11262       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 1);
11263       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11264       gudest = weed_layer_get_pixel_data_packed(layer);
11265       orowstride = weed_layer_get_rowstride(layer);
11266       convert_yuyv_to_bgr_frame((yuyv_macropixel *)gusrc, width, height, irowstride, orowstride, gudest,
11267                                 FALSE, iclamping, -USE_THREADS);
11268       break;
11269     case WEED_PALETTE_BGRA32:
11270       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 1);
11271       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11272       gudest = weed_layer_get_pixel_data_packed(layer);
11273       orowstride = weed_layer_get_rowstride(layer);
11274       convert_yuyv_to_bgr_frame((yuyv_macropixel *)gusrc, width, height, irowstride, orowstride, gudest,
11275                                 TRUE, iclamping, -USE_THREADS);
11276       break;
11277     case WEED_PALETTE_ARGB32:
11278       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 1);
11279       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11280       gudest = weed_layer_get_pixel_data_packed(layer);
11281       orowstride = weed_layer_get_rowstride(layer);
11282       convert_yuyv_to_argb_frame((yuyv_macropixel *)gusrc, width, height, irowstride, orowstride,
11283                                  gudest, iclamping, -USE_THREADS);
11284       break;
11285     case WEED_PALETTE_YUV444P:
11286       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 1);
11287       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11288       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11289       ostrides = weed_layer_get_rowstrides(layer, NULL);
11290       convert_yuyv_to_yuvp_frame((yuyv_macropixel *)gusrc, width, height, irowstride, ostrides, gudest_array, FALSE);
11291       break;
11292     case WEED_PALETTE_YUVA4444P:
11293       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 1);
11294       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11295       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11296       ostrides = weed_layer_get_rowstrides(layer, NULL);
11297       convert_yuyv_to_yuvp_frame((yuyv_macropixel *)gusrc, width, height, irowstride, ostrides, gudest_array, TRUE);
11298       break;
11299     case WEED_PALETTE_YUV888:
11300       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 1);
11301       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11302       gudest = weed_layer_get_pixel_data_packed(layer);
11303       orowstride = weed_layer_get_rowstride(layer);
11304       convert_yuyv_to_yuv888_frame((yuyv_macropixel *)gusrc, width, height, irowstride, orowstride, gudest, FALSE);
11305       break;
11306     case WEED_PALETTE_YUVA8888:
11307       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 1);
11308       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11309       gudest = weed_layer_get_pixel_data_packed(layer);
11310       orowstride = weed_layer_get_rowstride(layer);
11311       convert_yuyv_to_yuv888_frame((yuyv_macropixel *)gusrc, width, height, irowstride, orowstride, gudest, TRUE);
11312       break;
11313     case WEED_PALETTE_YUV420P:
11314     case WEED_PALETTE_YVU420P:
11315       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 1);
11316       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11317       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11318       convert_yuyv_to_yuv420_frame((yuyv_macropixel *)gusrc, width, height, gudest_array, iclamping);
11319       weed_set_int_value(layer, WEED_LEAF_YUV_SAMPLING, WEED_YUV_SAMPLING_DEFAULT);
11320       break;
11321     case WEED_PALETTE_YUV411:
11322       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 1);
11323       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11324       gudest = weed_layer_get_pixel_data_packed(layer);
11325       convert_yuyv_to_yuv411_frame((yuyv_macropixel *)gusrc, width, height, (yuv411_macropixel *)gudest, iclamping);
11326       break;
11327     default:
11328       lives_printerr("Invalid palette conversion: %s to %s not written yet !!\n", weed_palette_get_name(inpl),
11329                      weed_palette_get_name(outpl));
11330       goto memfail;
11331     }
11332     break;
11333   case WEED_PALETTE_YUV888:
11334     // need to check rowstrides (may have been resized)
11335     gusrc = weed_layer_get_pixel_data_packed(layer);
11336     switch (outpl) {
11337     case WEED_PALETTE_YUVA8888:
11338       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11339       gudest = weed_layer_get_pixel_data_packed(layer);
11340       orowstride = weed_layer_get_rowstride(layer);
11341       convert_addpost_frame(gusrc, width, height, irowstride, orowstride, gudest, NULL, -USE_THREADS);
11342       break;
11343     case WEED_PALETTE_YUV444P:
11344       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11345       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11346       ostrides = weed_layer_get_rowstrides(layer, NULL);
11347       convert_splitplanes_frame(gusrc, width, height, irowstride, ostrides, gudest_array, FALSE, FALSE);
11348       break;
11349     case WEED_PALETTE_YUVA4444P:
11350       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11351       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11352       ostrides = weed_layer_get_rowstrides(layer, NULL);
11353       convert_splitplanes_frame(gusrc, width, height, irowstride, ostrides, gudest_array, FALSE, TRUE);
11354       break;
11355     case WEED_PALETTE_RGB24:
11356       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11357       orowstride = weed_layer_get_rowstride(layer);
11358       gudest = weed_layer_get_pixel_data_packed(layer);
11359       convert_yuv888_to_rgb_frame(gusrc, width, height, irowstride, orowstride, gudest, FALSE, iclamping, isampling, -USE_THREADS);
11360       break;
11361     case WEED_PALETTE_RGBA32:
11362       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11363       orowstride = weed_layer_get_rowstride(layer);
11364       gudest = weed_layer_get_pixel_data_packed(layer);
11365       convert_yuv888_to_rgb_frame(gusrc, width, height, irowstride, orowstride, gudest, TRUE, iclamping, isampling, -USE_THREADS);
11366       break;
11367     case WEED_PALETTE_BGR24:
11368       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11369       orowstride = weed_layer_get_rowstride(layer);
11370       gudest = weed_layer_get_pixel_data_packed(layer);
11371       convert_yuv888_to_bgr_frame(gusrc, width, height, irowstride, orowstride, gudest, FALSE, iclamping, isampling, -USE_THREADS);
11372       break;
11373     case WEED_PALETTE_BGRA32:
11374       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11375       orowstride = weed_layer_get_rowstride(layer);
11376       gudest = weed_layer_get_pixel_data_packed(layer);
11377       convert_yuv888_to_bgr_frame(gusrc, width, height, irowstride, orowstride, gudest, TRUE, iclamping, isampling, -USE_THREADS);
11378       break;
11379     case WEED_PALETTE_ARGB32:
11380       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11381       orowstride = weed_layer_get_rowstride(layer);
11382       gudest = weed_layer_get_pixel_data_packed(layer);
11383       convert_yuv888_to_argb_frame(gusrc, width, height, irowstride, orowstride, gudest, iclamping, isampling, -USE_THREADS);
11384       break;
11385     case WEED_PALETTE_YVU420P:
11386     case WEED_PALETTE_YUV420P:
11387       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11388       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11389       ostrides = weed_get_int_array(layer, WEED_LEAF_ROWSTRIDES, &error);
11390       convert_yuv888_to_yuv420_frame(gusrc, width, height, irowstride, ostrides, gudest_array, FALSE, iclamping);
11391       weed_set_int_value(layer, WEED_LEAF_YUV_SAMPLING, WEED_YUV_SAMPLING_DEFAULT);
11392       //weed_set_int_value(layer,WEED_LEAF_YUV_SAMPLING,osampling);
11393       break;
11394     case WEED_PALETTE_YUV422P:
11395       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11396       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11397       ostrides = weed_get_int_array(layer, WEED_LEAF_ROWSTRIDES, &error);
11398       convert_yuv888_to_yuv422_frame(gusrc, width, height, irowstride, ostrides, gudest_array, FALSE, iclamping);
11399       break;
11400     case WEED_PALETTE_UYVY8888:
11401       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 1);
11402       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11403       orowstride = weed_layer_get_rowstride(layer);
11404       gudest = weed_layer_get_pixel_data_packed(layer);
11405       convert_yuv888_to_uyvy_frame(gusrc, width, height, irowstride, orowstride, (uyvy_macropixel *)gudest, FALSE, iclamping);
11406       break;
11407     case WEED_PALETTE_YUYV8888:
11408       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 1);
11409       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11410       orowstride = weed_layer_get_rowstride(layer);
11411       gudest = weed_layer_get_pixel_data_packed(layer);
11412       convert_yuv888_to_yuyv_frame(gusrc, width, height, irowstride, orowstride, (yuyv_macropixel *)gudest, FALSE, iclamping);
11413       break;
11414     case WEED_PALETTE_YUV411:
11415       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 2);
11416       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11417       gudest = weed_layer_get_pixel_data_packed(layer);
11418       convert_yuv888_to_yuv411_frame(gusrc, width, height, irowstride, (yuv411_macropixel *)gudest, FALSE);
11419       break;
11420     default:
11421       lives_printerr("Invalid palette conversion: %s to %s not written yet !!\n", weed_palette_get_name(inpl),
11422                      weed_palette_get_name(outpl));
11423       goto memfail;
11424     }
11425     break;
11426   case WEED_PALETTE_YUVA8888:
11427     gusrc = weed_layer_get_pixel_data_packed(layer);
11428     switch (outpl) {
11429     case WEED_PALETTE_YUV888:
11430       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11431       gudest = weed_layer_get_pixel_data_packed(layer);
11432       orowstride = weed_layer_get_rowstride(layer);
11433       convert_delpost_frame(gusrc, width, height, irowstride, orowstride, gudest, NULL, -USE_THREADS);
11434       break;
11435     case WEED_PALETTE_YUVA4444P:
11436       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11437       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11438       ostrides = weed_layer_get_rowstrides(layer, NULL);
11439       convert_splitplanes_frame(gusrc, width, height, irowstride, ostrides, gudest_array, TRUE, TRUE);
11440       break;
11441     case WEED_PALETTE_YUV444P:
11442       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11443       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11444       ostrides = weed_layer_get_rowstrides(layer, NULL);
11445       convert_splitplanes_frame(gusrc, width, height, irowstride, ostrides, gudest_array, TRUE, FALSE);
11446       break;
11447     case WEED_PALETTE_RGB24:
11448       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11449       orowstride = weed_layer_get_rowstride(layer);
11450       gudest = weed_layer_get_pixel_data_packed(layer);
11451       convert_yuva8888_to_rgba_frame(gusrc, width, height, irowstride, orowstride, gudest, TRUE, iclamping, isampling, -USE_THREADS);
11452       break;
11453     case WEED_PALETTE_RGBA32:
11454       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11455       orowstride = weed_layer_get_rowstride(layer);
11456       gudest = weed_layer_get_pixel_data_packed(layer);
11457       convert_yuva8888_to_rgba_frame(gusrc, width, height, irowstride, orowstride, gudest, FALSE, iclamping, isampling, -USE_THREADS);
11458       break;
11459     case WEED_PALETTE_BGR24:
11460       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11461       orowstride = weed_layer_get_rowstride(layer);
11462       gudest = weed_layer_get_pixel_data_packed(layer);
11463       convert_yuva8888_to_bgra_frame(gusrc, width, height, irowstride, orowstride, gudest, TRUE, iclamping, isampling, -USE_THREADS);
11464       break;
11465     case WEED_PALETTE_BGRA32:
11466       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11467       orowstride = weed_layer_get_rowstride(layer);
11468       gudest = weed_layer_get_pixel_data_packed(layer);
11469       convert_yuva8888_to_bgra_frame(gusrc, width, height, irowstride, orowstride, gudest, FALSE, iclamping, isampling, -USE_THREADS);
11470       break;
11471     case WEED_PALETTE_ARGB32:
11472       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11473       orowstride = weed_layer_get_rowstride(layer);
11474       gudest = weed_layer_get_pixel_data_packed(layer);
11475       convert_yuva8888_to_argb_frame(gusrc, width, height, irowstride, orowstride, gudest, iclamping, isampling, -USE_THREADS);
11476       break;
11477     case WEED_PALETTE_YUV420P:
11478     case WEED_PALETTE_YVU420P:
11479       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11480       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11481       ostrides = weed_layer_get_rowstrides(layer, NULL);
11482       convert_yuv888_to_yuv420_frame(gusrc, width, height, irowstride, ostrides, gudest_array, TRUE, iclamping);
11483       weed_set_int_value(layer, WEED_LEAF_YUV_SAMPLING, WEED_YUV_SAMPLING_DEFAULT);
11484       //weed_set_int_value(layer,WEED_LEAF_YUV_SAMPLING,osampling);
11485       break;
11486     case WEED_PALETTE_YUV422P:
11487       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11488       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11489       ostrides = weed_layer_get_rowstrides(layer, NULL);
11490       convert_yuv888_to_yuv422_frame(gusrc, width, height, irowstride, ostrides, gudest_array, TRUE, iclamping);
11491       break;
11492     case WEED_PALETTE_UYVY8888:
11493       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 1);
11494       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11495       orowstride = weed_layer_get_rowstride(layer);
11496       gudest = weed_layer_get_pixel_data_packed(layer);
11497       convert_yuv888_to_uyvy_frame(gusrc, width, height, irowstride, orowstride, (uyvy_macropixel *)gudest, TRUE, iclamping);
11498       break;
11499     case WEED_PALETTE_YUYV8888:
11500       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 1);
11501       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11502       orowstride = weed_layer_get_rowstride(layer);
11503       gudest = weed_layer_get_pixel_data_packed(layer);
11504       convert_yuv888_to_yuyv_frame(gusrc, width, height, irowstride, orowstride, (yuyv_macropixel *)gudest, TRUE, iclamping);
11505       break;
11506     case WEED_PALETTE_YUV411:
11507       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 2);
11508       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11509       gudest = weed_layer_get_pixel_data_packed(layer);
11510       convert_yuv888_to_yuv411_frame(gusrc, width, height, irowstride, (yuv411_macropixel *)gudest, TRUE);
11511       break;
11512     default:
11513       lives_printerr("Invalid palette conversion: %s to %s not written yet !!\n", weed_palette_get_name(inpl),
11514                      weed_palette_get_name(outpl));
11515       goto memfail;
11516     }
11517     break;
11518   case WEED_PALETTE_YVU420P:
11519   case WEED_PALETTE_YUV420P:
11520     gusrc_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11521     switch (outpl) {
11522     case WEED_PALETTE_RGB24:
11523       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11524       orowstride = weed_layer_get_rowstride(layer);
11525       gudest = weed_layer_get_pixel_data_packed(layer);
11526       convert_yuv420p_to_rgb_frame(gusrc_array, width, height, TRUE, istrides, orowstride, gudest, FALSE, FALSE,
11527                                    isampling, iclamping, isubspace, weed_layer_get_gamma(layer), new_gamma_type, NULL, -USE_THREADS);
11528       break;
11529     case WEED_PALETTE_RGBA32:
11530       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11531       orowstride = weed_layer_get_rowstride(layer);
11532       gudest = weed_layer_get_pixel_data_packed(layer);
11533       convert_yuv420p_to_rgb_frame(gusrc_array, width, height, TRUE, istrides, orowstride, gudest, TRUE, FALSE,
11534                                    isampling, iclamping, isubspace, weed_layer_get_gamma(layer), new_gamma_type, NULL, -USE_THREADS);
11535       break;
11536     case WEED_PALETTE_BGR24:
11537       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11538       orowstride = weed_layer_get_rowstride(layer);
11539       gudest = weed_layer_get_pixel_data_packed(layer);
11540       convert_yuv420p_to_bgr_frame(gusrc_array, width, height, TRUE, istrides, orowstride, gudest, FALSE, FALSE,
11541                                    isampling, iclamping, isubspace, weed_layer_get_gamma(layer), new_gamma_type, NULL, -USE_THREADS);
11542       break;
11543     case WEED_PALETTE_BGRA32:
11544       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11545       orowstride = weed_layer_get_rowstride(layer);
11546       gudest = weed_layer_get_pixel_data_packed(layer);
11547       convert_yuv420p_to_bgr_frame(gusrc_array, width, height, TRUE, istrides, orowstride, gudest, TRUE, FALSE,
11548                                    isampling, iclamping, isubspace, weed_layer_get_gamma(layer), new_gamma_type, NULL, -USE_THREADS);
11549       break;
11550     case WEED_PALETTE_ARGB32:
11551       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11552       orowstride = weed_layer_get_rowstride(layer);
11553       gudest = weed_layer_get_pixel_data_packed(layer);
11554       convert_yuv420p_to_argb_frame(gusrc_array, width, height, TRUE, istrides, orowstride, gudest, FALSE,
11555                                     isampling, iclamping, isubspace, weed_layer_get_gamma(layer), new_gamma_type, NULL, -USE_THREADS);
11556       break;
11557     case WEED_PALETTE_UYVY8888:
11558       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 1);
11559       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11560       orowstride = weed_layer_get_rowstride(layer);
11561       gudest = weed_layer_get_pixel_data_packed(layer);
11562       convert_yuv420_to_uyvy_frame(gusrc_array, width, height, istrides, orowstride, (uyvy_macropixel *)gudest, iclamping);
11563       break;
11564     case WEED_PALETTE_YUYV8888:
11565       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 1);
11566       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11567       orowstride = weed_layer_get_rowstride(layer);
11568       gudest = weed_layer_get_pixel_data_packed(layer);
11569       convert_yuv420_to_yuyv_frame(gusrc_array, width, height, istrides, orowstride, (yuyv_macropixel *)gudest, iclamping);
11570       break;
11571     case WEED_PALETTE_YUV422P:
11572       create_empty_pixel_data(layer, FALSE, FALSE);
11573       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11574       lives_free(gudest_array[0]);
11575       gudest_array[0] = gusrc_array[0];
11576       weed_set_voidptr_array(layer, WEED_LEAF_PIXEL_DATA, 3, (void **)gudest_array);
11577       ostrides = weed_layer_get_rowstrides(layer, NULL);
11578       convert_double_chroma(gusrc_array, width >> 1, height >> 1, istrides, ostrides, gudest_array, iclamping);
11579       gusrc_array[0] = NULL;
11580       weed_layer_set_pixel_data(orig_layer, (void **)gusrc_array, 3);
11581       break;
11582     case WEED_PALETTE_YUV444P:
11583       create_empty_pixel_data(layer, FALSE, FALSE);
11584       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11585       lives_free(gudest_array[0]);
11586       gudest_array[0] = gusrc_array[0];
11587       weed_set_voidptr_array(layer, WEED_LEAF_PIXEL_DATA, 3, (void **)gudest_array);
11588       orowstride = weed_layer_get_rowstride(layer);
11589       convert_quad_chroma(gusrc_array, width, height, istrides, orowstride, gudest_array, FALSE, isampling, iclamping);
11590       gusrc_array[0] = NULL;
11591       weed_layer_set_pixel_data(orig_layer, (void **)gusrc_array, 3);
11592       break;
11593     case WEED_PALETTE_YUVA4444P:
11594       create_empty_pixel_data(layer, FALSE, FALSE);
11595       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11596       lives_free(gudest_array[0]);
11597       gudest_array[0] = gusrc_array[0];
11598       weed_set_voidptr_array(layer, WEED_LEAF_PIXEL_DATA, 4, (void **)gudest_array);
11599       orowstride = weed_layer_get_rowstride(layer);
11600       convert_quad_chroma(gusrc_array, width, height, istrides, orowstride, gudest_array, TRUE, isampling, iclamping);
11601       gusrc_array[0] = NULL;
11602       weed_layer_set_pixel_data(orig_layer, (void **)gusrc_array, 3);
11603       break;
11604     case WEED_PALETTE_YVU420P:
11605     case WEED_PALETTE_YUV420P:
11606       if (contig && istrides[1] != istrides[2]) {
11607         uint8_t *gd0, *gd1, *gd2, *gs0 = gusrc_array[0], *gs1 = gusrc_array[1], *gs2 = gusrc_array[2];
11608         size_t hwidth = width >> 1;
11609         size_t ir0 = istrides[0] - width, ir1 = istrides[1] - hwidth, ir2 = istrides[2] - hwidth, or0, or1, or2;
11610         if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11611         ostrides = weed_layer_get_rowstrides(layer, NULL);
11612         or0 = ostrides[0] - width;
11613         or1 = ostrides[1] - hwidth;
11614         or2 = ostrides[2] - hwidth;
11615         gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11616         gd0 = gudest_array[0];
11617         gd1 = gudest_array[1];
11618         gd2 = gudest_array[2];
11619         for (int i = 0; i < height; i++) {
11620           lives_memcpy(gd0, gs0, width);
11621           gd0 += or0;
11622           gs0 += ir0;
11623           lives_memcpy(gd1, gs1, hwidth);
11624           gd1 += or1;
11625           gs1 += ir1;
11626           lives_memcpy(gd2, gs2, hwidth);
11627           gd2 += or2;
11628           gs2 += ir2;
11629           i++;
11630           lives_memcpy(gd0, gs0, width);
11631           gd0 += or0;
11632           gs0 += ir0;
11633         }
11634         break;
11635       } else {
11636         /// if we came in as YVU, we got swapped to YUV
11637         size_t frmsz = istrides[1] * (height >> 1);
11638         /// swap (again) and both become YVU
11639         swap_chroma_planes(layer);
11640         if (!(tmp = lives_calloc_safety(frmsz, 1))) goto memfail;
11641         /// swap again and both become YUV (YVU out will get swapped on more time)
11642         lives_memcpy(tmp, (void **)gusrc_array[1], frmsz);  // v plane
11643         lives_memcpy(gusrc_array[1], (void **)gusrc_array[2], frmsz);  //u -> v
11644         lives_memcpy(gusrc_array[2], tmp, frmsz); // v -> u
11645         lives_free(tmp);
11646       }
11647       break;
11648     case WEED_PALETTE_YUV888:
11649       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11650       gudest = weed_layer_get_pixel_data_packed(layer);
11651       orowstride = weed_layer_get_rowstride(layer);
11652       convert_quad_chroma_packed(gusrc_array, width, height, istrides, orowstride, gudest, FALSE, isampling, iclamping);
11653       break;
11654     case WEED_PALETTE_YUVA8888:
11655       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11656       gudest = weed_layer_get_pixel_data_packed(layer);
11657       orowstride = weed_layer_get_rowstride(layer);
11658       convert_quad_chroma_packed(gusrc_array, width, height, istrides, orowstride, gudest, TRUE, isampling, iclamping);
11659       break;
11660     case WEED_PALETTE_YUV411:
11661       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 2);
11662       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11663       gudest = weed_layer_get_pixel_data_packed(layer);
11664       convert_yuv420_to_yuv411_frame(gusrc_array, width, height, (yuv411_macropixel *)gudest, FALSE, iclamping);
11665       break;
11666     default:
11667       lives_printerr("Invalid palette conversion: %s to %s not written yet !!\n", weed_palette_get_name(inpl),
11668                      weed_palette_get_name(outpl));
11669       goto memfail;
11670     }
11671     break;
11672   case WEED_PALETTE_YUV422P:
11673     gusrc_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11674     switch (outpl) {
11675     case WEED_PALETTE_RGB24:
11676       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11677       orowstride = weed_layer_get_rowstride(layer);
11678       gudest = weed_layer_get_pixel_data_packed(layer);
11679       convert_yuv420p_to_rgb_frame(gusrc_array, width, height, TRUE, istrides, orowstride, gudest, FALSE, TRUE,
11680                                    isampling, iclamping, isubspace, weed_layer_get_gamma(layer), new_gamma_type, NULL, -USE_THREADS);
11681       break;
11682     case WEED_PALETTE_RGBA32:
11683       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11684       orowstride = weed_layer_get_rowstride(layer);
11685       gudest = weed_layer_get_pixel_data_packed(layer);
11686       convert_yuv420p_to_rgb_frame(gusrc_array, width, height, TRUE, istrides, orowstride, gudest, TRUE, TRUE,
11687                                    isampling, iclamping, isubspace, weed_layer_get_gamma(layer), new_gamma_type, NULL, -USE_THREADS);
11688       break;
11689     case WEED_PALETTE_BGR24:
11690       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11691       orowstride = weed_layer_get_rowstride(layer);
11692       gudest = weed_layer_get_pixel_data_packed(layer);
11693       convert_yuv420p_to_bgr_frame(gusrc_array, width, height, TRUE, istrides, orowstride, gudest, FALSE, TRUE,
11694                                    isampling, iclamping, isubspace, weed_layer_get_gamma(layer), new_gamma_type, NULL, -USE_THREADS);
11695       break;
11696     case WEED_PALETTE_BGRA32:
11697       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11698       orowstride = weed_layer_get_rowstride(layer);
11699       gudest = weed_layer_get_pixel_data_packed(layer);
11700       convert_yuv420p_to_bgr_frame(gusrc_array, width, height, TRUE, istrides, orowstride, gudest, TRUE, TRUE,
11701                                    isampling, iclamping, isubspace, weed_layer_get_gamma(layer), new_gamma_type, NULL, -USE_THREADS);
11702       break;
11703     case WEED_PALETTE_ARGB32:
11704       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11705       orowstride = weed_layer_get_rowstride(layer);
11706       gudest = weed_layer_get_pixel_data_packed(layer);
11707       convert_yuv420p_to_argb_frame(gusrc_array, width, height, TRUE, istrides, orowstride, gudest, TRUE,
11708                                     isampling, iclamping, isubspace, weed_layer_get_gamma(layer), new_gamma_type, NULL, -USE_THREADS);
11709       break;
11710     case WEED_PALETTE_UYVY8888:
11711       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 1);
11712       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11713       orowstride = weed_layer_get_rowstride(layer);
11714       gudest = weed_layer_get_pixel_data_packed(layer);
11715       convert_yuv422p_to_uyvy_frame(gusrc_array, width, height, istrides, orowstride, gudest);
11716       break;
11717     case WEED_PALETTE_YUYV8888:
11718       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 1);
11719       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11720       orowstride = weed_layer_get_rowstride(layer);
11721       gudest = weed_layer_get_pixel_data_packed(layer);
11722       convert_yuv422p_to_yuyv_frame(gusrc_array, width, height, istrides, orowstride, gudest);
11723       break;
11724     case WEED_PALETTE_YUV420P:
11725     case WEED_PALETTE_YVU420P:
11726       create_empty_pixel_data(layer, FALSE, FALSE);
11727       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11728       lives_free(gudest_array[0]);
11729       gudest_array[0] = gusrc_array[0];
11730       weed_set_voidptr_array(layer, WEED_LEAF_PIXEL_DATA, 3, (void **)gudest_array);
11731       ostrides = weed_layer_get_rowstrides(layer, NULL);
11732       convert_halve_chroma(gusrc_array, width >> 1, height >> 1, istrides, ostrides, gudest_array, iclamping);
11733       gusrc_array[0] = NULL;
11734       weed_layer_set_pixel_data(orig_layer, (void **)gusrc_array, 3);
11735       weed_set_int_value(layer, WEED_LEAF_YUV_SAMPLING, isampling);
11736       break;
11737     case WEED_PALETTE_YUV444P:
11738       create_empty_pixel_data(layer, FALSE, FALSE);
11739       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11740       lives_free(gudest_array[0]);
11741       gudest_array[0] = gusrc_array[0];
11742       weed_set_voidptr_array(layer, WEED_LEAF_PIXEL_DATA, 3, (void **)gudest_array);
11743       ostrides = weed_layer_get_rowstrides(layer, NULL);
11744       convert_double_chroma(gusrc_array, width >> 1, height >> 1, istrides, ostrides, gudest_array, iclamping);
11745       gusrc_array[0] = NULL;
11746       weed_layer_set_pixel_data(orig_layer, (void **)gusrc_array, 3);
11747       break;
11748     case WEED_PALETTE_YUVA4444P:
11749       create_empty_pixel_data(layer, FALSE, FALSE);
11750       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11751       lives_free(gudest_array[0]);
11752       gudest_array[0] = gusrc_array[0];
11753       weed_set_voidptr_array(layer, WEED_LEAF_PIXEL_DATA, 4, (void **)gudest_array);
11754       ostrides = weed_layer_get_rowstrides(layer, NULL);
11755       convert_double_chroma(gusrc_array, width >> 1, height >> 1, istrides, ostrides, gudest_array, iclamping);
11756       lives_memset(gudest_array[3], 255, width * height);
11757       gusrc_array[0] = NULL;
11758       weed_layer_set_pixel_data(orig_layer, (void **)gusrc_array, 3);
11759       break;
11760     case WEED_PALETTE_YUV888:
11761       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11762       gudest = weed_layer_get_pixel_data_packed(layer);
11763       orowstride = weed_layer_get_rowstride(layer);
11764       convert_double_chroma_packed(gusrc_array, width, height, istrides, orowstride, gudest, FALSE, isampling, iclamping);
11765       break;
11766     case WEED_PALETTE_YUVA8888:
11767       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11768       gudest = weed_layer_get_pixel_data_packed(layer);
11769       orowstride = weed_layer_get_rowstride(layer);
11770       convert_double_chroma_packed(gusrc_array, width, height, istrides, orowstride, gudest, TRUE, isampling, iclamping);
11771       break;
11772     case WEED_PALETTE_YUV411:
11773       weed_set_int_value(layer, WEED_LEAF_WIDTH, width >> 2);
11774       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11775       gudest = weed_layer_get_pixel_data_packed(layer);
11776       convert_yuv420_to_yuv411_frame(gusrc_array, width, height, (yuv411_macropixel *)gudest, TRUE, iclamping);
11777       break;
11778     default:
11779       lives_printerr("Invalid palette conversion: %s to %s not written yet !!\n", weed_palette_get_name(inpl),
11780                      weed_palette_get_name(outpl));
11781       goto memfail;
11782     }
11783     break;
11784   case WEED_PALETTE_YUV411:
11785     gusrc = weed_layer_get_pixel_data_packed(layer);
11786     switch (outpl) {
11787     case WEED_PALETTE_RGB24:
11788       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 2);
11789       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11790       orowstride = weed_layer_get_rowstride(layer);
11791       gudest = weed_layer_get_pixel_data_packed(layer);
11792       convert_yuv411_to_rgb_frame((yuv411_macropixel *)gusrc, width, height, orowstride, gudest, FALSE, iclamping);
11793       break;
11794     case WEED_PALETTE_RGBA32:
11795       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 2);
11796       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11797       orowstride = weed_layer_get_rowstride(layer);
11798       gudest = weed_layer_get_pixel_data_packed(layer);
11799       convert_yuv411_to_rgb_frame((yuv411_macropixel *)gusrc, width, height, orowstride, gudest, TRUE, iclamping);
11800       break;
11801     case WEED_PALETTE_BGR24:
11802       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 2);
11803       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11804       orowstride = weed_layer_get_rowstride(layer);
11805       gudest = weed_layer_get_pixel_data_packed(layer);
11806       convert_yuv411_to_bgr_frame((yuv411_macropixel *)gusrc, width, height, orowstride, gudest, FALSE, iclamping);
11807       break;
11808     case WEED_PALETTE_BGRA32:
11809       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 2);
11810       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11811       orowstride = weed_layer_get_rowstride(layer);
11812       gudest = weed_layer_get_pixel_data_packed(layer);
11813       convert_yuv411_to_bgr_frame((yuv411_macropixel *)gusrc, width, height, orowstride, gudest, TRUE, iclamping);
11814       break;
11815     case WEED_PALETTE_ARGB32:
11816       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 2);
11817       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11818       orowstride = weed_layer_get_rowstride(layer);
11819       gudest = weed_layer_get_pixel_data_packed(layer);
11820       convert_yuv411_to_argb_frame((yuv411_macropixel *)gusrc, width, height, orowstride, gudest, iclamping);
11821       break;
11822     case WEED_PALETTE_YUV888:
11823       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 2);
11824       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11825       gudest = weed_layer_get_pixel_data_packed(layer);
11826       convert_yuv411_to_yuv888_frame((yuv411_macropixel *)gusrc, width, height, gudest, FALSE, iclamping);
11827       break;
11828     case WEED_PALETTE_YUVA8888:
11829       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 2);
11830       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11831       gudest = weed_layer_get_pixel_data_packed(layer);
11832       convert_yuv411_to_yuv888_frame((yuv411_macropixel *)gusrc, width, height, gudest, TRUE, iclamping);
11833       break;
11834     case WEED_PALETTE_YUV444P:
11835       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 2);
11836       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11837       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11838       convert_yuv411_to_yuvp_frame((yuv411_macropixel *)gusrc, width, height, gudest_array, FALSE, iclamping);
11839       break;
11840     case WEED_PALETTE_YUVA4444P:
11841       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 2);
11842       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11843       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11844       convert_yuv411_to_yuvp_frame((yuv411_macropixel *)gusrc, width, height, gudest_array, TRUE, iclamping);
11845       break;
11846     case WEED_PALETTE_UYVY8888:
11847       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 1);
11848       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11849       gudest = weed_layer_get_pixel_data_packed(layer);
11850       convert_yuv411_to_uyvy_frame((yuv411_macropixel *)gusrc, width, height, (uyvy_macropixel *)gudest, iclamping);
11851       break;
11852     case WEED_PALETTE_YUYV8888:
11853       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 1);
11854       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11855       gudest = weed_layer_get_pixel_data_packed(layer);
11856       convert_yuv411_to_yuyv_frame((yuv411_macropixel *)gusrc, width, height, (yuyv_macropixel *)gudest, iclamping);
11857       break;
11858     case WEED_PALETTE_YUV422P:
11859       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 2);
11860       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11861       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11862       convert_yuv411_to_yuv422_frame((yuv411_macropixel *)gusrc, width, height, gudest_array, iclamping);
11863       break;
11864     case WEED_PALETTE_YUV420P:
11865       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 2);
11866       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11867       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11868       convert_yuv411_to_yuv420_frame((yuv411_macropixel *)gusrc, width, height, gudest_array, FALSE, iclamping);
11869       break;
11870     case WEED_PALETTE_YVU420P:
11871       weed_set_int_value(layer, WEED_LEAF_WIDTH, width << 2);
11872       if (!create_empty_pixel_data(layer, FALSE, TRUE)) goto memfail;
11873       gudest_array = (uint8_t **)weed_layer_get_pixel_data(layer, NULL);
11874       convert_yuv411_to_yuv420_frame((yuv411_macropixel *)gusrc, width, height, gudest_array, TRUE, iclamping);
11875       break;
11876     default:
11877       lives_printerr("Invalid palette conversion: %s to %s not written yet !!\n", weed_palette_get_name(inpl),
11878                      weed_palette_get_name(outpl));
11879       goto memfail;
11880     }
11881     break;
11882   default:
11883     lives_printerr("Invalid palette conversion: %s to %s not written yet !!\n", weed_palette_get_name(inpl),
11884                    weed_palette_get_name(outpl));
11885     goto memfail;
11886   }
11887 
11888 #ifdef WEED_ADVANCED_PALETTES
11889 conv_done:
11890 #endif
11891 
11892   lives_freep((void **)&ostrides);
11893   lives_freep((void **)&gusrc_array);
11894 
11895   //lives_freep((void **)&gudest_array);
11896 
11897   if (orig_layer) weed_layer_free(orig_layer);
11898 
11899   if (new_gamma_type != WEED_GAMMA_UNKNOWN && can_inline_gamma(inpl, outpl)) {
11900     weed_layer_set_gamma(layer, new_gamma_type);
11901   }
11902 
11903   if (weed_palette_is_rgb(outpl)) {
11904     weed_leaf_delete(layer, WEED_LEAF_YUV_CLAMPING);
11905     weed_leaf_delete(layer, WEED_LEAF_YUV_SUBSPACE);
11906     weed_leaf_delete(layer, WEED_LEAF_YUV_SAMPLING);
11907   } else {
11908     weed_set_int_value(layer, WEED_LEAF_YUV_CLAMPING, oclamping);
11909     if (weed_palette_is_rgb(inpl)) {
11910       if (weed_layer_get_gamma(layer) == WEED_GAMMA_BT709)
11911         weed_set_int_value(layer, WEED_LEAF_YUV_SUBSPACE, WEED_YUV_SUBSPACE_BT709);
11912       else
11913         weed_set_int_value(layer, WEED_LEAF_YUV_SUBSPACE, WEED_YUV_SUBSPACE_YCBCR);
11914     }
11915     if (!weed_plant_has_leaf(layer, WEED_LEAF_YUV_SAMPLING)) weed_set_int_value(layer,
11916           WEED_LEAF_YUV_SAMPLING, WEED_YUV_SAMPLING_DEFAULT);
11917   }
11918 
11919   /// if V plane is before U, swap the pointers
11920 #ifdef WEED_ADVANCED_PALETTES
11921   if (get_advanced_palette(outpl)->chantype[1] == WEED_VCHAN_V) swap_chroma_planes(layer);
11922 #else
11923   if (outpl == WEED_PALETTE_YVU420P) swap_chroma_planes(layer);
11924 #endif
11925 
11926   lives_free(istrides);
11927   return TRUE;
11928 
11929 memfail:
11930   lives_freep((void **)&gudest_array);
11931   weed_layer_set_palette(layer, inpl);
11932   weed_layer_set_size(layer, width, height);
11933   if (gusrc) {
11934     weed_layer_set_pixel_data_packed(layer, gusrc);
11935     weed_layer_set_rowstride(layer, istrides[0]);
11936   } else if (gusrc_array) {
11937     weed_layer_set_pixel_data(layer, (void **)gusrc_array, nplanes);
11938     weed_layer_set_rowstrides(layer, istrides, nplanes);
11939   }
11940   lives_free(istrides);
11941   return FALSE;
11942 }
11943 
11944 
convert_layer_palette(weed_layer_t * layer,int outpl,int op_clamping)11945 boolean convert_layer_palette(weed_layer_t *layer, int outpl, int op_clamping) {
11946   return convert_layer_palette_full(layer, outpl, op_clamping, WEED_YUV_SAMPLING_DEFAULT, WEED_YUV_SUBSPACE_YUV,
11947                                     WEED_GAMMA_UNKNOWN);
11948 }
11949 
11950 /////////////////////////////////////////////////////////////////////////////////////
11951 
11952 
lives_pixbuf_new_blank(int width,int height,int palette)11953 LiVESPixbuf *lives_pixbuf_new_blank(int width, int height, int palette) {
11954   LiVESPixbuf *pixbuf;
11955   int rowstride;
11956   uint8_t *pixels;
11957   size_t size;
11958 
11959   switch (palette) {
11960   case WEED_PALETTE_RGB24:
11961   case WEED_PALETTE_BGR24:
11962     pixbuf = lives_pixbuf_new(FALSE, width, height);
11963     rowstride = lives_pixbuf_get_rowstride(pixbuf);
11964     pixels = lives_pixbuf_get_pixels(pixbuf);
11965     size = rowstride * (height - 1) + get_last_pixbuf_rowstride_value(width, 3);
11966     lives_memset(pixels, 0, size);
11967     break;
11968   case WEED_PALETTE_RGBA32:
11969   case WEED_PALETTE_BGRA32:
11970     pixbuf = lives_pixbuf_new(TRUE, width, height);
11971     rowstride = lives_pixbuf_get_rowstride(pixbuf);
11972     pixels = lives_pixbuf_get_pixels(pixbuf);
11973     size = rowstride * (height - 1) + get_last_pixbuf_rowstride_value(width, 4);
11974     lives_memset(pixels, 0, size);
11975     break;
11976   case WEED_PALETTE_ARGB32:
11977     pixbuf = lives_pixbuf_new(TRUE, width, height);
11978     rowstride = lives_pixbuf_get_rowstride(pixbuf);
11979     pixels = lives_pixbuf_get_pixels(pixbuf);
11980     size = rowstride * (height - 1) + get_last_pixbuf_rowstride_value(width, 4);
11981     lives_memset(pixels, 0, size);
11982     break;
11983   default:
11984     return NULL;
11985   }
11986   return pixbuf;
11987 }
11988 
11989 
lives_pixbuf_cheat(boolean has_alpha,int width,int height,uint8_t * buf)11990 LIVES_INLINE LiVESPixbuf *lives_pixbuf_cheat(boolean has_alpha, int width, int height, uint8_t *buf) {
11991   // we can cheat if our buffer is correctly sized
11992   LiVESPixbuf *pixbuf;
11993   int channels = has_alpha ? 4 : 3;
11994   int rowstride = get_pixbuf_rowstride_value(width * channels);
11995 
11996   pixbuf = lives_pixbuf_new_from_data(buf, has_alpha, width, height, rowstride,
11997                                       (LiVESPixbufDestroyNotify)lives_free_buffer, NULL);
11998   return pixbuf;
11999 }
12000 
12001 
weed_layer_get_gamma(weed_layer_t * layer)12002 int weed_layer_get_gamma(weed_layer_t *layer) {
12003   int gamma_type = WEED_GAMMA_UNKNOWN;
12004   int error;
12005   if (prefs->apply_gamma) {
12006     if (weed_plant_has_leaf(layer, WEED_LEAF_GAMMA_TYPE)) {
12007       gamma_type = weed_get_int_value(layer, WEED_LEAF_GAMMA_TYPE, &error);
12008     }
12009     if (gamma_type == WEED_GAMMA_UNKNOWN) {
12010       break_me("weed_layer_get_gamma with unk. gamma");
12011       LIVES_WARN("Layer with unknown gamma !!");
12012       gamma_type = WEED_GAMMA_SRGB;
12013     }
12014   }
12015   return gamma_type;
12016 }
12017 
12018 
gamma_conv_params(int gamma_type,weed_layer_t * inst,boolean is_in)12019 void gamma_conv_params(int gamma_type, weed_layer_t *inst, boolean is_in) {
12020   if (!prefs->apply_gamma) return;
12021   else {
12022     // convert colour param values to gamma_type (only integer values)
12023     weed_layer_t **params;
12024     weed_layer_t *ptmpl, *param;
12025     uint8_t *gamma_lut = NULL;
12026     const char *type = is_in ? WEED_LEAF_IN_PARAMETERS : WEED_LEAF_OUT_PARAMETERS;
12027 
12028     int *ivals;
12029     int ogamma_type, oogamma_type = WEED_GAMMA_UNKNOWN;
12030     int pptype, pcspace, ptype, nvals, qvals;
12031     int nparms;
12032 
12033     if (!inst) return;
12034     params = weed_get_plantptr_array_counted(inst, type, &nparms);
12035     if (!nparms) return;
12036 
12037     for (int i = 0; i < nparms; i++) {
12038       param = params[i];
12039       if (!(nvals = weed_leaf_num_elements(param, WEED_LEAF_VALUE))) continue;
12040       ptmpl = weed_get_plantptr_value(param, WEED_LEAF_TEMPLATE, NULL);
12041       pptype = weed_paramtmpl_get_type(ptmpl);
12042       if (pptype != WEED_PARAM_COLOR) continue;
12043 
12044       ptype = weed_leaf_seed_type(ptmpl, WEED_LEAF_DEFAULT);
12045 
12046       if (ptype != WEED_SEED_INT) gamma_type = WEED_GAMMA_SRGB;
12047 
12048       if (!prefs->apply_gamma || !weed_plant_has_leaf(param, WEED_LEAF_GAMMA_TYPE)) {
12049         ogamma_type = WEED_GAMMA_SRGB;
12050       } else {
12051         ogamma_type = weed_get_int_value(param, WEED_LEAF_GAMMA_TYPE, NULL);
12052       }
12053 
12054       if (ogamma_type != oogamma_type && gamma_type != ogamma_type) {
12055         if (gamma_lut) lives_gamma_lut_free(gamma_lut);
12056         gamma_lut = create_gamma_lut(1.0, ogamma_type, gamma_type);
12057         if (!gamma_lut) break;
12058         oogamma_type = ogamma_type;
12059       }
12060 
12061       weed_set_int_value(param, WEED_LEAF_GAMMA_TYPE, gamma_type);
12062       weed_leaf_set_flags(param, WEED_LEAF_GAMMA_TYPE, (weed_leaf_get_flags(param, WEED_LEAF_GAMMA_TYPE) |
12063                           WEED_FLAG_IMMUTABLE | WEED_FLAG_UNDELETABLE));
12064 
12065       // no change needed
12066       if (gamma_type == ogamma_type) continue;
12067 
12068       qvals = 3;
12069       pcspace = weed_get_int_value(ptmpl, WEED_LEAF_COLORSPACE, NULL);
12070       if (pcspace == WEED_COLORSPACE_RGBA) qvals = 4;
12071       ivals = weed_get_int_array(param, WEED_LEAF_VALUE, NULL);
12072       if (gamma_lut) {
12073         for (int j = 0; j < nvals; j += qvals) {
12074           for (int k = 0; k < 3; k++) {
12075             ivals[j + k] = gamma_lut[ivals[j + k]];
12076           }
12077         }
12078         lives_gamma_lut_free(gamma_lut);
12079         weed_set_int_array(param, WEED_LEAF_VALUE, nvals, ivals);
12080         lives_free(ivals);
12081         weed_set_int_value(param, WEED_LEAF_GAMMA_TYPE, gamma_type);
12082         weed_leaf_set_flags(param, WEED_LEAF_GAMMA_TYPE, (weed_leaf_get_flags(param, WEED_LEAF_GAMMA_TYPE) |
12083                             WEED_FLAG_IMMUTABLE | WEED_FLAG_UNDELETABLE));
12084       }
12085     }
12086     lives_free(params);
12087     if (gamma_lut) lives_gamma_lut_free(gamma_lut);
12088   }
12089 }
12090 
12091 
gamma_convert_layer_thread(void * data)12092 static void *gamma_convert_layer_thread(void *data) {
12093   lives_cc_params *ccparams = (lives_cc_params *)data;
12094   uint8_t *pixels = (uint8_t *)ccparams->src;
12095   int rowstride = ccparams->orowstrides[0];
12096   uint8_t *end = pixels + ccparams->vsize * rowstride;
12097   int psize = ccparams->psize, px = 3;
12098   int widthx = ccparams->hsize * psize;
12099   int start = ccparams->xoffset;
12100   uint8_t *gamma_lut = ccparams->lut;
12101   if (!gamma_lut) return NULL;
12102 
12103   if (psize < px) px = psize;
12104 
12105   if (ccparams->alpha_first) start += 1;
12106   for (; pixels < end; pixels += rowstride) {
12107     //g_print("\n");
12108     for (int j = start; j < widthx; j += psize) {
12109       for (int k = 0; k < px; k++) {
12110         //g_print("  PX %p + %d , %d  = %d\t", pixels, j + k, pixels[j + k], gamma_lut[pixels[k + j]]);
12111         pixels[j + k] = gamma_lut[pixels[j + k]];
12112       }
12113       //g_print("\n");
12114     }
12115   }
12116   return NULL;
12117 }
12118 
12119 
12120 /**
12121    @brief alter the transfer function of a Weed layer, from current value to gamma_type
12122 
12123 */
gamma_convert_sub_layer(int gamma_type,double fileg,weed_layer_t * layer,int x,int y,int width,int height,boolean may_thread)12124 boolean gamma_convert_sub_layer(int gamma_type, double fileg, weed_layer_t *layer, int x, int y, int width, int height,
12125                                 boolean may_thread) {
12126   if (!prefs->apply_gamma) return TRUE;
12127   else {
12128     // convert layer from current gamma to target
12129     int pal = weed_layer_get_palette(layer);
12130     if (!weed_palette_is_rgb(pal)) return FALSE; //  dont know how to convert in yuv space
12131     else {
12132       int lgamma_type = weed_layer_get_gamma(layer);
12133       //g_print("gam from %d to %d with fileg %f\n", lgamma_type, gamma_type, fileg);
12134       if (gamma_type == lgamma_type && fileg == 1.0) return TRUE;
12135       else {
12136         lives_thread_t threads[prefs->nfx_threads];
12137         int nfx_threads = may_thread ? prefs->nfx_threads : 1;
12138         uint8_t *pixels = weed_layer_get_pixel_data_packed(layer);
12139         uint8_t *gamma_lut;
12140         int orowstride = weed_layer_get_rowstride(layer);
12141         int nthreads = 1;
12142         int dheight;
12143         int psize = pixel_size(pal);
12144         lives_cc_params *ccparams = (lives_cc_params *)lives_calloc(nfx_threads,
12145                                     sizeof(lives_cc_params));
12146         int xdheight = CEIL((double)height / (double)nfx_threads, 4);
12147         uint8_t *end = pixels + (y + height) * orowstride;
12148 
12149         pixels += y * orowstride;
12150 
12151         if (gamma_type == WEED_GAMMA_VARIANT)
12152           gamma_lut = create_gamma_lut(fileg, lgamma_type, gamma_type);
12153         else
12154           gamma_lut = create_gamma_lut(1.0, lgamma_type, gamma_type);
12155 
12156         //for (int i = nfx_threads - 1; i >= 0; i--) {
12157         for (int i = nfx_threads - 1; i >= 0; i--) {
12158           dheight = xdheight;
12159 
12160           if ((pixels + dheight * i * orowstride) < end) {
12161             ccparams[i].src = pixels + dheight * i * orowstride;
12162             ccparams[i].hsize = width;
12163             ccparams[i].xoffset = x * psize;
12164 
12165             if (dheight * (i + 1) > (height - 4)) {
12166               dheight = height - (dheight * i);
12167             }
12168             ccparams[i].vsize = dheight;
12169             ccparams[i].psize = psize;
12170             ccparams[i].orowstrides[0] = orowstride;
12171             if (pal == WEED_PALETTE_ARGB32) ccparams->alpha_first = TRUE;
12172             ccparams[i].lut = (void *)gamma_lut;
12173             ccparams[i].thread_id = i;
12174             if (i == 0) gamma_convert_layer_thread(&ccparams[i]);
12175             else {
12176               lives_thread_create(&threads[i], LIVES_THRDATTR_NONE, gamma_convert_layer_thread, &ccparams[i]);
12177               nthreads++;
12178             }
12179           }
12180         }
12181         for (int i = 1; i < nthreads; i++) {
12182           lives_thread_join(threads[i], NULL);
12183         }
12184         lives_free(ccparams);
12185         lives_gamma_lut_free(gamma_lut);
12186         if (gamma_type != WEED_GAMMA_VARIANT)
12187           weed_set_int_value(layer, WEED_LEAF_GAMMA_TYPE, gamma_type);
12188         return TRUE;
12189 	// *INDENT-OFF*
12190       }}}
12191   // *INDENT-ON*
12192 }
12193 
12194 
gamma_convert_layer(int gamma_type,weed_layer_t * layer)12195 LIVES_GLOBAL_INLINE boolean gamma_convert_layer(int gamma_type, weed_layer_t *layer) {
12196   int width = weed_layer_get_width(layer);
12197   int height = weed_layer_get_height(layer);
12198   return gamma_convert_sub_layer(gamma_type, 1.0, layer, 0, 0, width, height, TRUE);
12199 }
12200 
12201 
gamma_convert_layer_variant(double file_gamma,int tgamma,weed_layer_t * layer)12202 LIVES_GLOBAL_INLINE boolean gamma_convert_layer_variant(double file_gamma, int tgamma, weed_layer_t *layer) {
12203   int width = weed_layer_get_width(layer);
12204   int height = weed_layer_get_height(layer);
12205   weed_set_int_value(layer, WEED_LEAF_GAMMA_TYPE, WEED_GAMMA_LINEAR);
12206   return gamma_convert_sub_layer(tgamma, file_gamma, layer, 0, 0, width, height, TRUE);
12207 }
12208 
12209 
layer_to_pixbuf(weed_layer_t * layer,boolean realpalette,boolean fordisplay)12210 LiVESPixbuf *layer_to_pixbuf(weed_layer_t *layer, boolean realpalette, boolean fordisplay) {
12211   // create a gdkpixbuf from a weed layer
12212   // layer "pixel_data" is then either copied to the pixbuf pixels, or the contents shared with the pixbuf and array value set to NULL
12213   // layer may safely be passed to weed_layer_free() since if the pixel data is shared then it will be set to NULL in the layer.
12214   // pixbuf should be unreffed after use as per normal
12215 
12216   LiVESPixbuf *pixbuf;
12217 
12218   uint8_t *pixel_data, *pixels, *end;
12219 
12220   boolean cheat = FALSE, done;
12221 
12222   int palette, xpalette;
12223   int width;
12224   int height;
12225   int irowstride;
12226   int rowstride, orowstride;
12227   int n_channels;
12228 
12229   if (!layer) return NULL;
12230 
12231   palette = weed_layer_get_palette(layer);
12232 
12233   if (weed_plant_has_leaf(layer, WEED_LEAF_HOST_PIXBUF_SRC) && (!realpalette || weed_palette_is_pixbuf_palette(palette))) {
12234     // our layer pixel_data originally came from a pixbuf, so just free the layer and return the pixbuf
12235     pixbuf = (LiVESPixbuf *)weed_get_voidptr_value(layer, WEED_LEAF_HOST_PIXBUF_SRC, NULL);
12236     weed_layer_nullify_pixel_data(layer);
12237     weed_leaf_delete(layer, WEED_LEAF_HOST_PIXBUF_SRC);
12238     return pixbuf;
12239   }
12240 
12241   // otherwise we need to steal or copy the pixel_data
12242 
12243   if (!weed_layer_get_pixel_data_packed(layer)) {
12244     layer = create_blank_layer(layer, NULL, 0, 0, WEED_PALETTE_END);
12245   }
12246 
12247   do {
12248     width = weed_layer_get_width(layer) * weed_palette_get_pixels_per_macropixel(palette);
12249     height = weed_layer_get_height(layer);
12250     irowstride = weed_layer_get_rowstride(layer);
12251     pixel_data = (uint8_t *)weed_get_voidptr_value(layer, WEED_LEAF_PIXEL_DATA, NULL);
12252     done = TRUE;
12253     xpalette = palette;
12254     if (realpalette) {
12255       if (!weed_palette_is_pixbuf_palette(palette)) {
12256         // force conversion to RGB24 or RGBA32
12257         xpalette = WEED_PALETTE_END;
12258       } else {
12259         if (prefs->apply_gamma) {
12260           gamma_convert_layer(WEED_GAMMA_SRGB, layer);
12261           if (fordisplay && prefs->use_screen_gamma)
12262             gamma_convert_layer(WEED_GAMMA_MONITOR, layer);
12263         }
12264       }
12265     }
12266     switch (xpalette) {
12267     case WEED_PALETTE_RGB24:
12268     case WEED_PALETTE_BGR24:
12269     case WEED_PALETTE_YUV888:
12270 #ifndef GUI_QT
12271       if (irowstride == get_pixbuf_rowstride_value(width * 3)) {
12272         // rowstrides are OK, we can just steal the pixel_data
12273         pixbuf = lives_pixbuf_cheat(FALSE, width, height, pixel_data);
12274         weed_layer_nullify_pixel_data(layer);
12275         cheat = TRUE;
12276       } else {
12277 #endif
12278         // otherwise we need to copy the data
12279         pixbuf = lives_pixbuf_new(FALSE, width, height);
12280       }
12281       n_channels = 3;
12282       break;
12283     case WEED_PALETTE_RGBA32:
12284     case WEED_PALETTE_BGRA32:
12285 #ifdef USE_SWSCALE
12286     case WEED_PALETTE_ARGB32:
12287 #else
12288 #ifdef GUI_QT
12289     case WEED_PALETTE_ARGB32:
12290 #endif
12291 #endif
12292     case WEED_PALETTE_YUVA8888:
12293 #ifndef GUI_QT
12294       if (irowstride == get_pixbuf_rowstride_value(width * 4)) {
12295         // rowstrides are OK, we can just steal the pixel_data
12296         pixbuf = lives_pixbuf_cheat(TRUE, width, height, pixel_data);
12297         weed_layer_nullify_pixel_data(layer);
12298         cheat = TRUE;
12299       } else
12300 #endif
12301         // otherwise we need to copy the data
12302         pixbuf = lives_pixbuf_new(TRUE, width, height);
12303       n_channels = 4;
12304       break;
12305     default:
12306       if (weed_palette_has_alpha(palette)) {
12307         if (!convert_layer_palette(layer, WEED_PALETTE_RGBA32, 0)) return NULL;
12308         palette = WEED_PALETTE_RGBA32;
12309       } else {
12310         if (!convert_layer_palette(layer, WEED_PALETTE_RGB24, 0)) return NULL;
12311         palette = WEED_PALETTE_RGB24;
12312       }
12313       done = FALSE;
12314     }
12315   } while (!done);
12316 
12317   if (!cheat && LIVES_IS_PIXBUF(pixbuf)) {
12318     // copy the pixel data
12319     boolean done = FALSE;
12320     pixels = lives_pixbuf_get_pixels(pixbuf);
12321     orowstride = lives_pixbuf_get_rowstride(pixbuf);
12322 
12323     if (irowstride > orowstride) rowstride = orowstride;
12324     else rowstride = irowstride;
12325     end = pixels + orowstride * height;
12326 
12327     for (; pixels < end && !done; pixels += orowstride) {
12328       if (pixels + orowstride >= end) {
12329         orowstride = rowstride = get_last_pixbuf_rowstride_value(width, n_channels);
12330         done = TRUE;
12331       }
12332       lives_memcpy(pixels, pixel_data, rowstride);
12333       if (rowstride < orowstride) lives_memset(pixels + rowstride, 255, orowstride - rowstride);
12334       pixel_data += irowstride;
12335     }
12336     weed_layer_pixel_data_free(layer);
12337   }
12338 
12339   return pixbuf;
12340 }
12341 
12342 
weed_palette_is_resizable(int pal,int clamped,boolean in_out)12343 LIVES_INLINE boolean weed_palette_is_resizable(int pal, int clamped, boolean in_out) {
12344   // in_out is TRUE for input, FALSE for output
12345 
12346   // in future we may also have resize candidates/delegates for other palettes
12347   // we will need to check for these
12348 
12349   if (pal == WEED_PALETTE_YUV888 && clamped == WEED_YUV_CLAMPING_UNCLAMPED) pal = WEED_PALETTE_RGB24;
12350   if (pal == WEED_PALETTE_YUVA8888 && clamped == WEED_YUV_CLAMPING_UNCLAMPED) pal = WEED_PALETTE_RGBA32;
12351 
12352 #ifdef USE_SWSCALE
12353   if (in_out && sws_isSupportedInput(weed_palette_to_avi_pix_fmt(pal, &clamped))) return TRUE;
12354   else if (sws_isSupportedOutput(weed_palette_to_avi_pix_fmt(pal, &clamped))) return TRUE;
12355 #endif
12356   if (pal == WEED_PALETTE_RGB24 || pal == WEED_PALETTE_RGBA32 || pal == WEED_PALETTE_BGR24 ||
12357       pal == WEED_PALETTE_BGRA32) return TRUE;
12358   return FALSE;
12359 }
12360 
12361 
lives_pixbuf_set_opaque(LiVESPixbuf * pixbuf)12362 void lives_pixbuf_set_opaque(LiVESPixbuf * pixbuf) {
12363   unsigned char *pdata = lives_pixbuf_get_pixels(pixbuf);
12364   int row = lives_pixbuf_get_rowstride(pixbuf);
12365   int height = lives_pixbuf_get_height(pixbuf);
12366   int offs;
12367 #ifdef GUI_GTK
12368   offs = 3;
12369 #endif
12370 
12371 #ifdef GUI_QT
12372   offs = 0;
12373 #endif
12374 
12375   register int i, j;
12376   for (i = 0; i < height; i++) {
12377     for (j = offs; j < row; j += 4) {
12378       pdata[j] = 255;
12379     }
12380     pdata += row;
12381   }
12382 }
12383 
12384 
lives_layer_set_opaque(weed_layer_t * layer)12385 void lives_layer_set_opaque(weed_layer_t *layer) {
12386   int offs;
12387   boolean planar = FALSE;
12388   int pal = weed_layer_get_palette(layer);
12389   if (weed_palette_is_planar(pal)) {
12390     offs = weed_palette_get_alpha_plane(pal);
12391     planar = TRUE;
12392   } else offs = weed_palette_get_alpha_offset(pal);
12393 
12394   if (offs >= 0) {
12395     int width = weed_layer_get_width(layer);
12396     int height = weed_layer_get_height(layer);
12397     int rowstride;
12398 
12399     if (planar) {
12400       int *rowstrides = weed_layer_get_rowstrides(layer, NULL);
12401       void **pixel_data = weed_layer_get_pixel_data(layer, NULL);
12402       rowstride = rowstrides[offs];
12403       height *= weed_palette_get_plane_ratio_vertical(pal, offs);
12404       lives_memset(pixel_data, 255, rowstride * height);
12405       lives_free(rowstrides);
12406       lives_free(pixel_data);
12407     } else {
12408       ssize_t frsize, psize = pixel_size(pal);
12409       uint8_t *pixel_data = weed_layer_get_pixel_data_packed(layer);
12410       rowstride = weed_layer_get_rowstride(layer);
12411       frsize = height * rowstride;
12412       width *= psize;
12413       for (register int i = 0; i < frsize; i += rowstride) {
12414         for (register int j = offs; j < width; j += psize) {
12415           pixel_data[i + j] = 255;
12416 	  // *INDENT-OFF*
12417 	}}}}
12418   // *INDENT-ON*
12419 }
12420 
12421 
compact_rowstrides(weed_layer_t * layer)12422 boolean compact_rowstrides(weed_layer_t *layer) {
12423   // remove any extra padding after the image data
12424   weed_layer_t *old_layer;
12425   void **pixel_data, **new_pixel_data;
12426   int *rowstrides = weed_layer_get_rowstrides(layer, NULL), *orows;
12427   int pal = weed_layer_get_palette(layer);
12428   int width = weed_layer_get_width(layer);
12429   int height = weed_layer_get_height(layer);
12430   int xheight;
12431   int crow = width * weed_palette_get_bits_per_macropixel(pal) / 8;
12432   int cxrow;
12433   int nplanes;
12434   boolean needs_change = FALSE;
12435   register int i, j;
12436 
12437   pixel_data = weed_layer_get_pixel_data(layer, &nplanes);
12438 
12439   for (i = 0; i < nplanes; i++) {
12440     cxrow = crow * weed_palette_get_plane_ratio_horizontal(pal, i);
12441     if (cxrow != rowstrides[i]) {
12442       // nth plane has extra padding
12443       needs_change = TRUE;
12444     }
12445   }
12446 
12447   if (!needs_change) {
12448     lives_free(pixel_data);
12449     lives_free(rowstrides);
12450     return TRUE;
12451   }
12452 
12453   old_layer = weed_layer_new(WEED_LAYER_TYPE_VIDEO);
12454   if (!old_layer) return FALSE;
12455   if (!weed_layer_copy(old_layer, layer)) return FALSE;
12456 
12457   THREADVAR(rowstride_alignment_hint) = -1;
12458   weed_layer_nullify_pixel_data(layer);
12459 
12460   if (!create_empty_pixel_data(layer, FALSE, TRUE)) {
12461     weed_layer_copy(layer, old_layer);
12462     weed_layer_nullify_pixel_data(old_layer);
12463     weed_layer_free(old_layer);
12464     return FALSE;
12465   }
12466 
12467   new_pixel_data = weed_layer_get_pixel_data(layer, &nplanes);
12468 
12469   if (!new_pixel_data) {
12470     weed_layer_free(old_layer);
12471     lives_free(pixel_data);
12472     lives_free(rowstrides);
12473     return FALSE;
12474   }
12475 
12476   orows = weed_layer_get_rowstrides(layer, NULL);
12477 
12478   for (i = 0; i < nplanes; i++) {
12479     xheight = height * weed_palette_get_plane_ratio_vertical(pal, i);
12480     cxrow = orows[i];
12481     for (j = 0; j < xheight; j++) {
12482       lives_memcpy((uint8_t *)new_pixel_data[i] + j * cxrow, (uint8_t *)pixel_data[i] + j * rowstrides[i], cxrow);
12483       //for (int k = 3; k < cxrow; k += 4) ((uint8_t *)new_pixel_data[i])[j * cxrow + k] = 0;
12484     }
12485   }
12486 
12487   if (nplanes > 1) weed_set_boolean_value(layer, WEED_LEAF_HOST_PIXEL_DATA_CONTIGUOUS, WEED_TRUE);
12488   weed_layer_free(old_layer);
12489   lives_free(pixel_data);
12490   lives_free(new_pixel_data);
12491   lives_free(rowstrides);
12492   lives_free(orows);
12493   return TRUE;
12494 }
12495 
12496 
12497 #ifdef USE_SWSCALE
12498 #if USE_THREADS
swscale_threadfunc(void * arg)12499 static void *swscale_threadfunc(void *arg) {
12500   lives_sw_params *swparams = (lives_sw_params *)arg;
12501 #ifdef USE_RESTHREAD
12502   int scan = 0;
12503   if (swparams->layer) {
12504     int last = swparams->iheight * (swparams->thread_id + 1);
12505     while ((scan = weed_get_int_value(swparams->layer, WEED_LEAF_PROGSCAN, NULL)) > 0
12506            && scan < last) {
12507       lives_nanosleep(100);
12508     }
12509   }
12510 #endif
12511   swparams->ret = sws_scale(swparams->swscale, (const uint8_t *const *)swparams->ipd, swparams->irw,
12512                             0, swparams->iheight, (uint8_t *const *)swparams->opd, swparams->orw);
12513 
12514   if (swparams->file_gamma != 1.) {
12515     gamma_convert_sub_layer(WEED_GAMMA_SRGB, 1. / swparams->file_gamma, swparams->layer, 0, 0, swparams->width,
12516                             swparams->ret, FALSE);
12517   }
12518 
12519   return NULL;
12520 }
12521 #endif
12522 #endif
12523 
12524 /**
12525    @brief resize a layer
12526 
12527    width is in PIXELS (not macropixels)
12528 
12529    opal_hint and oclamp_hint may be set to hint the desired output palette and YUV clamping
12530    this is simply to ensure more efficient resizing, and may be ignored
12531 
12532    - setting opal_hint to WEED_PALETTE_END will attempt to minimise palette changes
12533 
12534    "current_palette" should be checked on return as it may change
12535 
12536    @return FALSE if we were unable to resize */
resize_layer(weed_layer_t * layer,int width,int height,LiVESInterpType interp,int opal_hint,int oclamp_hint)12537 boolean resize_layer(weed_layer_t *layer, int width, int height, LiVESInterpType interp, int opal_hint, int oclamp_hint) {
12538   // TODO ** - see if there is a resize plugin candidate/delegate which supports this palette :
12539   // this allows e.g libabl or a hardware rescaler
12540   LiVESPixbuf *pixbuf = NULL;
12541   LiVESPixbuf *new_pixbuf = NULL;
12542 
12543   boolean retval = TRUE;
12544 
12545   int palette = weed_layer_get_palette(layer);
12546 
12547   // original width and height (in pixels)
12548   int iwidth = weed_layer_get_width_pixels(layer);
12549   int iheight = weed_layer_get_height(layer);
12550   int iclamping = weed_layer_get_yuv_clamping(layer);
12551   int new_gamma_type = weed_layer_get_gamma(layer);
12552 
12553 #ifdef USE_SWSCALE
12554   boolean resolved = FALSE;
12555   int xpalette, xopal_hint;
12556 #endif
12557 #ifdef USE_RESTHREAD
12558   boolean progscan = FALSE;
12559   if (weed_get_int_value(layer, WEED_LEAF_PROGSCAN, NULL) > 0) progscan = TRUE;
12560 #endif
12561   if (!weed_plant_has_leaf(layer, WEED_LEAF_PIXEL_DATA)) {
12562     weed_layer_set_size(layer, width / weed_palette_get_pixels_per_macropixel(opal_hint), height);
12563     weed_layer_set_palette(layer, opal_hint);
12564     weed_layer_set_yuv_clamping(layer, oclamp_hint);
12565     return FALSE;
12566   }
12567 
12568   if (width <= 0 || height <= 0) {
12569     char *msg = lives_strdup_printf("unable to scale layer to %d x %d for palette %d\n", width, height, palette);
12570     LIVES_DEBUG(msg);
12571     lives_free(msg);
12572     return FALSE;
12573   }
12574   //      #define DEBUG_RESIZE
12575 #ifdef DEBUG_RESIZE
12576   g_print("resizing layer size %d X %d with palette %s to %d X %d, hinted %s\n", iwidth, iheight,
12577           weed_palette_get_name_full(palette,
12578                                      iclamping, 0), width, height, weed_palette_get_name_full(opal_hint, oclamp_hint, 0));
12579 #endif
12580 
12581   iwidth = (iwidth >> 1) << 1;
12582   iheight = (iheight >> 1) << 1;
12583 
12584   if (width < 4) width = 4;
12585   if (height < 4) height = 4;
12586 
12587   if (iwidth != width || iheight != height) {
12588     // prevent a crash in swscale
12589     width = (width >> 1) << 1;
12590     height = (height >> 1) << 1;
12591   }
12592   if (iwidth == width && iheight == height) return TRUE; // no resize needed
12593 
12594   // if in palette is a YUV palette which we cannot scale, convert to YUV888 (unclamped) or YUVA8888 (unclamped)
12595   // we can always scale these by pretending they are RGB24 and RGBA32 respectively
12596   if (weed_palette_is_yuv(palette)) {
12597     if (!weed_palette_is_resizable(palette, iclamping, TRUE)) {
12598       if (opal_hint == WEED_PALETTE_END || weed_palette_is_yuv(opal_hint)) {
12599         if (weed_palette_has_alpha(palette)) {
12600           convert_layer_palette(layer, WEED_PALETTE_YUVA8888, WEED_YUV_CLAMPING_UNCLAMPED);
12601         } else {
12602           convert_layer_palette(layer, WEED_PALETTE_YUV888, WEED_YUV_CLAMPING_UNCLAMPED);
12603         }
12604         oclamp_hint = iclamping = (weed_get_int_value(layer, WEED_LEAF_YUV_CLAMPING, NULL));
12605       } else {
12606         if (weed_palette_has_alpha(palette)) {
12607           convert_layer_palette(layer, WEED_PALETTE_RGBA32, 0);
12608         } else {
12609           convert_layer_palette(layer, WEED_PALETTE_RGB24, 0);
12610         }
12611       }
12612       palette = weed_layer_get_palette(layer);
12613       iwidth = weed_layer_get_width_pixels(layer);
12614       iheight = weed_layer_get_height(layer);
12615       if (iwidth == width && iheight == height) return TRUE; // no resize needed
12616 #ifdef DEBUG_RESIZE
12617       g_print("intermediate conversion 1 to %s\n", weed_palette_get_name_full(palette, iclamping, 0));
12618 #endif
12619     }
12620   }
12621 
12622   // check if we can also convert to the output palette
12623 #ifdef USE_SWSCALE
12624   // only swscale can convert and resize together
12625   if (opal_hint == WEED_PALETTE_END || !weed_palette_is_resizable(opal_hint, oclamp_hint, FALSE)) {
12626 #endif
12627     opal_hint = palette;
12628     oclamp_hint = iclamping;
12629 #ifdef USE_SWSCALE
12630   }
12631 #endif
12632 
12633   // check if we can convert to the target palette/clamping
12634   if (!weed_palette_is_resizable(opal_hint, oclamp_hint, FALSE)) {
12635     opal_hint = palette;
12636     oclamp_hint = iclamping;
12637   }
12638 
12639 #ifdef USE_SWSCALE
12640   // sws doesn't understand YUV888 or YUVA888, but if the output palette is also YUV888 or YUVA8888
12641   // then we can use unclamped values and  pretend they are RGB24 and RGBA32.
12642   // Otherwise we need to convert to YUV444P and YUVA4444P.
12643 
12644   // lookup values for av_pix_fmt
12645   xpalette = palette;
12646   xopal_hint = opal_hint;
12647 
12648   if (palette == WEED_PALETTE_YUV888) {
12649     if (opal_hint == WEED_PALETTE_YUV888 || opal_hint == WEED_PALETTE_YUVA8888) {
12650       if (iclamping == WEED_YUV_CLAMPING_CLAMPED) {
12651         convert_layer_palette(layer, WEED_PALETTE_YUV888, WEED_YUV_CLAMPING_UNCLAMPED);
12652         palette = weed_layer_get_palette_yuv(layer, &iclamping, NULL, NULL);
12653 #ifdef DEBUG_RESIZE
12654         g_print("intermediate conversion 2 to %s\n", weed_palette_get_name_full(palette, iclamping, 0));
12655 #endif
12656       }
12657       if (iclamping == WEED_YUV_CLAMPING_UNCLAMPED) {
12658         xpalette = WEED_PALETTE_RGB24;
12659         oclamp_hint = WEED_YUV_CLAMPING_UNCLAMPED;
12660         resolved = TRUE;
12661         if (opal_hint == WEED_PALETTE_YUV888) {
12662           xopal_hint = WEED_PALETTE_RGB24;
12663         } else {
12664           xopal_hint = WEED_PALETTE_RGBA32;
12665         }
12666 #ifdef DEBUG_RESIZE
12667         g_print("masquerading as %s\n", weed_palette_get_name_full(xpalette, oclamp_hint, 0));
12668 #endif
12669       }
12670     }
12671     if (!resolved) {
12672       convert_layer_palette(layer, WEED_PALETTE_YUV444P, iclamping);
12673       xpalette = palette = weed_layer_get_palette_yuv(layer, &iclamping, NULL, NULL);
12674 #ifdef DEBUG_RESIZE
12675       g_print("intermediate conversion 3 to %s\n", weed_palette_get_name_full(xpalette, iclamping, 0));
12676 #endif
12677     }
12678   } else if (palette == WEED_PALETTE_YUVA8888) {
12679     if (opal_hint == WEED_PALETTE_YUV888 || opal_hint == WEED_PALETTE_YUVA8888) {
12680       if (iclamping == WEED_YUV_CLAMPING_CLAMPED) {
12681         convert_layer_palette(layer, WEED_PALETTE_YUVA8888, WEED_YUV_CLAMPING_UNCLAMPED);
12682         xpalette = palette = weed_layer_get_palette_yuv(layer, &iclamping, NULL, NULL);
12683       }
12684       if (iclamping == WEED_YUV_CLAMPING_UNCLAMPED) {
12685         xpalette = WEED_PALETTE_RGBA32;
12686         oclamp_hint = WEED_YUV_CLAMPING_UNCLAMPED;
12687         resolved = TRUE;
12688         if (opal_hint == WEED_PALETTE_YUVA8888) {
12689           xopal_hint = WEED_PALETTE_RGBA32;
12690         } else {
12691           xopal_hint = WEED_PALETTE_RGB24;
12692         }
12693 #ifdef DEBUG_RESIZE
12694         g_print("masquerading as %s\n", weed_palette_get_name_full(xpalette, oclamp_hint, 0));
12695 #endif
12696       }
12697     }
12698     if (!resolved) {
12699       convert_layer_palette(layer, WEED_PALETTE_YUVA4444P, iclamping);
12700       xpalette = palette = weed_layer_get_palette_yuv(layer, &iclamping, NULL, NULL);
12701 #ifdef DEBUG_RESIZE
12702       g_print("intermediate conversion 4 to %s\n", weed_palette_get_name_full(xpalette, iclamping, 0));
12703 #endif
12704     }
12705   }
12706 
12707   // reget these after conversion, convert width from macropixels to pixels
12708   iwidth = weed_layer_get_width(layer) * weed_palette_get_pixels_per_macropixel(palette);
12709   iheight = weed_layer_get_height(layer);
12710   if (iwidth == width && iheight == height) return TRUE; // no resize needed
12711 
12712   if (opal_hint == WEED_PALETTE_YUV888) opal_hint = xopal_hint = WEED_PALETTE_YUV444P;
12713   else if (opal_hint == WEED_PALETTE_YUVA8888) opal_hint = xopal_hint = WEED_PALETTE_YUVA4444P;
12714 
12715   if (iwidth > 1 && iheight > 1 && weed_palette_is_resizable(palette, iclamping, TRUE) &&
12716       weed_palette_is_resizable(xopal_hint, oclamp_hint, FALSE)) {
12717     weed_layer_t *old_layer;
12718 
12719     void **in_pixel_data, **out_pixel_data;
12720 
12721     int *irowstrides, *orowstrides;
12722 
12723     //boolean store_ctx = FALSE;
12724 
12725     swpixfmt ipixfmt, opixfmt;
12726 
12727     int flags;
12728 
12729     const uint8_t *ipd[4], *opd[4];
12730     int irw[4], orw[4];
12731 
12732     int i;
12733 #if USE_THREADS
12734     lives_sw_params *swparams;
12735     lives_thread_t threads[prefs->nfx_threads];
12736     swsctx_block *ctxblock;
12737     int offset;
12738     int nthrds = 1;
12739 #else
12740     int scan;
12741 #endif
12742     int subspace = WEED_YUV_SUBSPACE_YUV;
12743     int inplanes, oplanes;
12744 
12745     /// old layer will hold a ref to the original pixel_data. We will free it at the end since the pixel_data
12746     /// of layer will be recreated when we calll create_empty_pixel_data()
12747 
12748     // get current values
12749     in_pixel_data = weed_layer_get_pixel_data(layer, &inplanes);
12750     irowstrides = weed_layer_get_rowstrides(layer, NULL);
12751 
12752     old_layer = weed_layer_new(WEED_LAYER_TYPE_VIDEO);
12753     weed_layer_copy(old_layer, layer);
12754 
12755     av_log_set_level(AV_LOG_FATAL);
12756 
12757     THREADVAR(rowstride_alignment_hint) = 16;
12758 
12759     if (interp == LIVES_INTERP_BEST) {
12760       if (width > iwidth || height > iheight)
12761         flags = SWS_LANCZOS; // other opts SINC, LANCZOS, SPLINE; ACCURATE_RND, BITEXACT. ERROR_DIFFUSION
12762       else
12763         flags = SWS_BICUBIC;
12764     } else if (interp == LIVES_INTERP_FAST) flags = SWS_FAST_BILINEAR;
12765     else flags = SWS_BILINEAR;
12766 
12767     ipixfmt = weed_palette_to_avi_pix_fmt(xpalette, &iclamping);
12768     opixfmt = weed_palette_to_avi_pix_fmt(xopal_hint, &oclamp_hint);
12769 
12770     for (i = 0; i < 4; i++) {
12771       // swscale always likes 4 elements, even if fewer planes are used
12772       if (i < inplanes) {
12773         ipd[i] = in_pixel_data[i];
12774         irw[i] = irowstrides[i];
12775       } else {
12776         ipd[i] = NULL;
12777         irw[i] = 0;
12778       }
12779     }
12780 
12781     if (weed_palette_is_rgb(palette) && !weed_palette_is_rgb(opal_hint) &&
12782         weed_layer_get_gamma(layer) == WEED_GAMMA_LINEAR) {
12783       // gamma correction
12784       if (prefs->apply_gamma) {
12785         gamma_convert_layer(WEED_GAMMA_SRGB, layer);
12786       }
12787     }
12788     new_gamma_type = weed_layer_get_gamma(layer);
12789 
12790     // set new values
12791 
12792     if (palette != opal_hint) {
12793       weed_layer_set_palette(layer, opal_hint);
12794     }
12795 
12796     if (weed_palette_is_yuv(opal_hint)) weed_layer_set_yuv_clamping(layer, oclamp_hint);
12797     //weed_layer_set_size(layer, width, height);
12798     weed_layer_set_size(layer, width / weed_palette_get_pixels_per_macropixel(opal_hint), height);
12799     weed_layer_nullify_pixel_data(layer);
12800 
12801     if (!create_empty_pixel_data(layer, FALSE, TRUE)) {
12802       weed_layer_copy(layer, old_layer);
12803       weed_layer_nullify_pixel_data(old_layer);
12804       weed_layer_free(old_layer);
12805       return FALSE;
12806     }
12807 
12808     weed_layer_set_gamma(layer, new_gamma_type);
12809     out_pixel_data = weed_layer_get_pixel_data(layer, &oplanes);
12810     orowstrides = weed_layer_get_rowstrides(layer, NULL);
12811 
12812     if (weed_palette_is_rgb(palette)) {
12813       if (new_gamma_type == WEED_GAMMA_BT709) subspace = WEED_YUV_SUBSPACE_BT709;
12814     } else if (weed_palette_is_yuv(palette)) {
12815       if (new_gamma_type == WEED_GAMMA_BT709 || weed_layer_get_yuv_subspace(old_layer) == WEED_YUV_SUBSPACE_BT709)
12816         subspace = WEED_YUV_SUBSPACE_BT709;
12817     }
12818 
12819     for (i = 0; i < 4; i++) {
12820       // swscale always likes 4 elements, even if fewer planes are used
12821       if (i < oplanes) {
12822         opd[i] = out_pixel_data[i];
12823         orw[i] = orowstrides[i];
12824       } else {
12825         opd[i] = NULL;
12826         orw[i] = 0;
12827       }
12828     }
12829 
12830     //width /= weed_palette_get_pixels_per_macropixel(palette);
12831 
12832 #if USE_THREADS
12833     while ((nthrds << 1) <= prefs->nfx_threads) {
12834       if ((height | iheight) & 3) break;
12835       nthrds <<= 1;
12836       iheight >>= 1;
12837       height >>= 1;
12838     }
12839     swparams = (lives_sw_params *)lives_calloc(nthrds, sizeof(lives_sw_params));
12840 #else
12841     // TODO - can we set the gamma ?
12842     //g_print("iht is %d, height = %d\n", iheight, height);
12843     swscale = sws_getCachedContext(swscale, iwidth, iheight, ipixfmt, width, height, opixfmt, flags, NULL, NULL, NULL);
12844     sws_setColorspaceDetails(swscale, sws_getCoefficients((subspace == WEED_YUV_SUBSPACE_BT709)
12845                              ? SWS_CS_ITU709 : SWS_CS_ITU601), iclamping,
12846                              sws_getCoefficients((subspace == WEED_YUV_SUBSPACE_BT709)
12847                                  ? SWS_CS_ITU709 : SWS_CS_ITU601), oclamp_hint,  0, 1 << 16, 1 << 16);
12848 
12849     if (!swscale) {
12850       LIVES_DEBUG("swscale is NULL !!");
12851     } else {
12852 #endif
12853 
12854 #ifdef DEBUG_RESIZE
12855     g_print("before resize with swscale: layer size %d X %d with palette %s to %d X %d, hinted %s,\n"
12856             "masquerading as %s (avpixfmt %d to avpixfmt %d)\n",
12857             iwidth, iheight, weed_palette_get_name_full(palette, iclamping, 0), width, height,
12858             weed_palette_get_name_full(opal_hint,
12859                                        oclamp_hint, 0),
12860             weed_palette_get_name_full(xopal_hint, oclamp_hint, 0), ipixfmt, opixfmt);
12861 #endif
12862 #if USE_THREADS
12863     ctxblock = sws_getblock(nthrds, iwidth, iheight, irw, ipixfmt, width, height, orw, opixfmt, flags,
12864                             subspace, iclamping, oclamp_hint);
12865     offset = ctxblock->offset;
12866     for (int sl = 0; sl < nthrds; sl++) {
12867       swparams[sl].thread_id = sl;
12868       swparams[sl].iheight = iheight;
12869       swparams[sl].width = width;
12870       swparams[sl].file_gamma = 1.;
12871       swparams[sl].swscale = swscalep[sl + offset] =
12872                                sws_getCachedContext(swscalep[sl + offset], iwidth, iheight, ipixfmt, width,
12873                                    height, opixfmt, flags, NULL, NULL, NULL);
12874 
12875       if (!swparams[sl].swscale) {
12876         LIVES_DEBUG("swscale is NULL !!");
12877       } else {
12878         //if (progscan) {
12879         swparams[sl].layer = layer;
12880         swparams[sl].file_gamma = weed_get_double_value(layer, "file_gamma", NULL);
12881         if (swparams[sl].file_gamma == 0.) swparams[sl].file_gamma = 1.;
12882         //} else swparams[sl].layer = NULL;
12883         sws_setColorspaceDetails(swparams[sl].swscale,
12884                                  sws_getCoefficients((subspace == WEED_YUV_SUBSPACE_BT709)
12885                                      ? SWS_CS_ITU709 : SWS_CS_ITU601), iclamping,
12886                                  sws_getCoefficients((subspace == WEED_YUV_SUBSPACE_BT709)
12887                                      ? SWS_CS_ITU709 : SWS_CS_ITU601), oclamp_hint,  0, 65536, 65536);
12888         for (i = 0; i < 4; i++) {
12889           if (i < inplanes)
12890             swparams[sl].ipd[i] =
12891               ipd[i] + (size_t)(sl * irw[i] * iheight
12892                                 * weed_palette_get_plane_ratio_vertical(palette, i));
12893           else
12894             swparams[sl].ipd[i] = NULL;
12895 
12896           if (i < oplanes)
12897             swparams[sl].opd[i] =
12898               opd[i] + (size_t)(sl * orw[i] * height
12899                                 * weed_palette_get_plane_ratio_vertical(opal_hint, i));
12900           else
12901             swparams[sl].opd[i] = NULL;
12902         }
12903         swparams[sl].irw = irw;
12904         swparams[sl].orw = orw;
12905         if (sl < nthrds - 1) lives_thread_create(&threads[sl], LIVES_THRDATTR_NONE, swscale_threadfunc, &swparams[sl]);
12906         else swscale_threadfunc(&swparams[sl]);
12907       }
12908     }
12909     iheight = height;
12910     // height = 0;
12911     for (int sl = 0; sl < nthrds - 1; sl++) {
12912       if (swparams[sl].swscale) {
12913         lives_thread_join(threads[sl], NULL);
12914         height += swparams[sl].ret;
12915       } else height += iheight;
12916     }
12917 
12918     sws_freeblock(ctxblock);
12919     lives_free(swparams);
12920 
12921 #else
12922 #ifdef USE_RESTHREAD
12923     if (progscan)
12924       while ((scan = weed_get_int_value(layer, WEED_LEAF_PROGSCAN, NULL)) > 0 && scan < iheight)
12925         lives_nanosleep(100);
12926 #endif
12927     height = sws_scale(swscale, (const uint8_t *const *)ipd, irw, 0, iheight,
12928                        (uint8_t *const *)opd, orw);
12929   }
12930 #endif
12931 
12932 #ifdef DEBUG_RESIZE
12933     g_print("after resize with swscale: layer size %d X %d, palette %s (assumed succesful)\n",
12934             width, height, weed_palette_get_name_full(opal_hint, oclamp_hint, 0));
12935 #endif
12936     //if (store_ctx) swscale_add_context(iwidth, iheight, width, height, ipixfmt, opixfmt, flags, swscale);
12937     //weed_layer_set_width(layer, width);
12938     weed_layer_set_height(layer, height);
12939 
12940     /* for (int gg = 0; gg < height; gg++) { */
12941     /*   memset(&((uint8_t *)out_pixel_data[0])[gg * orowstrides[0]], 255, width * 3 / 2); */
12942     /* } */
12943 
12944     // this will properly free() in_pixel_data
12945     weed_layer_free(old_layer);
12946     lives_free(out_pixel_data);
12947     lives_free(orowstrides);
12948     lives_free(irowstrides);
12949     lives_free(in_pixel_data);
12950     return TRUE;
12951   }
12952 #endif
12953 
12954   // reget these after conversion, convert width from macropixels to pixels
12955   iwidth = weed_layer_get_width(layer) * weed_palette_get_pixels_per_macropixel(palette);
12956   iheight = weed_layer_get_height(layer);
12957   if (iwidth == width && iheight == height) return TRUE; // no resize needed
12958 
12959   switch (palette) {
12960   // anything with 3 or 4 channels (alpha must be last)
12961 
12962   case WEED_PALETTE_YUV888:
12963   case WEED_PALETTE_YUVA8888:
12964     if (iclamping == WEED_YUV_CLAMPING_CLAMPED) {
12965       if (weed_palette_has_alpha(palette)) {
12966         convert_layer_palette(layer, WEED_PALETTE_YUVA8888, WEED_YUV_CLAMPING_UNCLAMPED);
12967       } else {
12968         convert_layer_palette(layer, WEED_PALETTE_YUV888, WEED_YUV_CLAMPING_UNCLAMPED);
12969       }
12970     }
12971 
12972   case WEED_PALETTE_RGB24:
12973   case WEED_PALETTE_BGR24:
12974   case WEED_PALETTE_RGBA32:
12975   case WEED_PALETTE_BGRA32:
12976 
12977     // create a new pixbuf
12978     //gamma_convert_layer(cfile->gamma_type, layer);
12979     pixbuf = layer_to_pixbuf(layer, FALSE, FALSE);
12980 
12981     threaded_dialog_spin(0.);
12982     new_pixbuf = lives_pixbuf_scale_simple(pixbuf, width, height, interp);
12983     threaded_dialog_spin(0.);
12984     if (new_pixbuf) {
12985       weed_layer_set_size(layer, lives_pixbuf_get_width(new_pixbuf), lives_pixbuf_get_height(new_pixbuf));
12986       weed_layer_set_rowstride(layer, lives_pixbuf_get_rowstride(new_pixbuf));
12987     }
12988 
12989     lives_widget_object_unref(pixbuf);
12990 
12991     break;
12992   default:
12993     lives_printerr("Warning: resizing unknown palette %d\n", palette);
12994     break_me("resize_layer with unk. pal");
12995     retval = FALSE;
12996   }
12997 
12998   if (!new_pixbuf || (width != weed_layer_get_width(layer) ||
12999                       height != weed_layer_get_height(layer)))  {
13000     lives_printerr("unable to scale layer to %d x %d for palette %d\n", width, height, palette);
13001     retval = FALSE;
13002   } else {
13003     if (weed_plant_has_leaf(layer, WEED_LEAF_HOST_ORIG_PDATA))
13004       weed_leaf_delete(layer, WEED_LEAF_HOST_ORIG_PDATA);
13005   }
13006 
13007   if (new_pixbuf) {
13008     if (!pixbuf_to_layer(layer, new_pixbuf)) lives_widget_object_unref(new_pixbuf);
13009   }
13010 
13011   return retval;
13012 }
13013 
13014 
letterbox_layer(weed_layer_t * layer,int nwidth,int nheight,int width,int height,LiVESInterpType interp,int tpal,int tclamp)13015 boolean letterbox_layer(weed_layer_t *layer, int nwidth, int nheight, int width, int height,
13016                         LiVESInterpType interp, int tpal, int tclamp) {
13017   // stretch or shrink layer to width/height, then overlay it in a black rectangle size nwidth/nheight
13018   // width, nwidth should be in pixels
13019   weed_layer_t *old_layer;
13020   int *rowstrides, *irowstrides;
13021   void **pixel_data;
13022   void **new_pixel_data;
13023   uint8_t *dst, *src;
13024   int lwidth, lheight;
13025   int offs_x = 0, offs_y = 0;
13026   int pal;
13027 
13028   int i;
13029 
13030   if (!width || !height || !nwidth || !nheight) return TRUE;
13031   if (nwidth < width) nwidth = width;
13032   if (nheight < height) nheight = height;
13033 
13034   /// no letterboxing needed - resize and return
13035   if (nheight == height && nwidth == width) {
13036     resize_layer(layer, width, height, interp, tpal, tclamp);
13037     return TRUE;
13038   }
13039 
13040   pal = weed_layer_get_palette(layer);
13041   lwidth = weed_layer_get_width_pixels(layer);
13042   lheight = weed_layer_get_height(layer);
13043 
13044   if (lwidth != width || lheight != height) {
13045     /// resize the inner rectangle
13046     if (!resize_layer(layer, width, height, interp, tpal, tclamp)) return FALSE;
13047     pal = weed_layer_get_palette(layer);
13048     lwidth = weed_layer_get_width_pixels(layer);
13049     lheight = weed_layer_get_height(layer);
13050   }
13051 
13052   // old layer will hold pointers to the original pixel data for layer
13053   old_layer = weed_layer_new(WEED_LAYER_TYPE_VIDEO);
13054 
13055   if (!old_layer) return FALSE;
13056   if (!weed_layer_copy(old_layer, layer)) return FALSE;
13057 
13058   pixel_data = weed_layer_get_pixel_data(old_layer, NULL);
13059 
13060   if (!pixel_data) {
13061     weed_layer_free(old_layer);
13062     return FALSE;
13063   }
13064 
13065   width = lwidth;
13066   height = lheight;
13067   irowstrides = weed_layer_get_rowstrides(layer, NULL);
13068 
13069   /// create the outer rectangle in layer
13070   weed_layer_set_size(layer, nwidth / weed_palette_get_pixels_per_macropixel(pal), nheight);
13071   weed_layer_nullify_pixel_data(layer);
13072   if (!create_empty_pixel_data(layer, TRUE, TRUE)) goto memfail2;
13073 
13074   new_pixel_data = weed_layer_get_pixel_data(layer, NULL);
13075 
13076   /// get the actual size after any adjustments
13077   nwidth = weed_layer_get_width(layer) * weed_palette_get_pixels_per_macropixel(pal);
13078   nheight = weed_layer_get_height(layer);
13079 
13080   if (nwidth < width || nheight < height || !new_pixel_data) {
13081     /// this shouldnt happen, but if  the outer rectangle is smaller than the inner we have to abort
13082     goto memfail2;
13083   }
13084 
13085   offs_x = (nwidth - width + 1) >> 1;
13086   offs_y = (nheight - height + 1) >> 1;
13087 
13088   rowstrides = weed_layer_get_rowstrides(layer, NULL);
13089 
13090   switch (pal) {
13091   // 3 byte pixels, packed
13092   case WEED_PALETTE_RGB24:
13093   case WEED_PALETTE_BGR24:
13094   case WEED_PALETTE_YUV888:
13095     width *= 3;
13096     dst = (uint8_t *)new_pixel_data[0] + offs_y * rowstrides[0] + offs_x * 3;
13097     src = (uint8_t *)pixel_data[0];
13098     for (i = 0; i < height; i++) {
13099       lives_memcpy(dst, src, width);
13100       dst += rowstrides[0];
13101       src += irowstrides[0];
13102     }
13103     break;
13104 
13105   // 4 byte pixels, packed
13106   case WEED_PALETTE_UYVY:
13107   case WEED_PALETTE_YUYV:
13108     offs_x >>= 1;
13109     width >>= 1;
13110 
13111   case WEED_PALETTE_RGBA32:
13112   case WEED_PALETTE_BGRA32:
13113   case WEED_PALETTE_ARGB32:
13114   case WEED_PALETTE_YUVA8888:
13115 
13116     width *= 4;
13117     dst = (uint8_t *)new_pixel_data[0] + offs_y * rowstrides[0] + offs_x * 4;
13118     src = (uint8_t *)pixel_data[0];
13119     for (i = 0; i < height; i++) {
13120       lives_memcpy(dst, src, width);
13121       dst += rowstrides[0];
13122       src += irowstrides[0];
13123     }
13124     break;
13125 
13126   case WEED_PALETTE_YUV411:
13127     width *= 6;
13128     dst = (uint8_t *)new_pixel_data[0] + offs_y * rowstrides[0] + offs_x * 6;
13129     src = (uint8_t *)pixel_data[0];
13130     for (i = 0; i < height; i++) {
13131       lives_memcpy(dst, src, width);
13132       dst += rowstrides[0];
13133       src += irowstrides[0];
13134     }
13135     break;
13136 
13137   case WEED_PALETTE_YUV444P:
13138     dst = (uint8_t *)new_pixel_data[0] + offs_y * rowstrides[0] + offs_x;
13139     src = (uint8_t *)pixel_data[0];
13140     for (i = 0; i < height; i++) {
13141       lives_memcpy(dst, src, width);
13142       dst += rowstrides[0];
13143       src += irowstrides[0];
13144     }
13145     dst = (uint8_t *)new_pixel_data[1] + offs_y * rowstrides[1] + offs_x;
13146     src = (uint8_t *)pixel_data[1];
13147     for (i = 0; i < height; i++) {
13148       lives_memcpy(dst, src, width);
13149       dst += rowstrides[1];
13150       src += irowstrides[1];
13151     }
13152     dst = (uint8_t *)new_pixel_data[2] + offs_y * rowstrides[2] + offs_x;
13153     src = (uint8_t *)pixel_data[2];
13154     for (i = 0; i < height; i++) {
13155       lives_memcpy(dst, src, width);
13156       dst += rowstrides[2];
13157       src += irowstrides[2];
13158     }
13159     break;
13160 
13161   case WEED_PALETTE_YUVA4444P:
13162     dst = (uint8_t *)new_pixel_data[0] + offs_y * rowstrides[0] + offs_x;
13163     src = (uint8_t *)pixel_data[0];
13164     for (i = 0; i < height; i++) {
13165       lives_memcpy(dst, src, width);
13166       dst += rowstrides[0];
13167       src += irowstrides[0];
13168     }
13169     dst = (uint8_t *)new_pixel_data[1] + offs_y * rowstrides[1] + offs_x;
13170     src = (uint8_t *)pixel_data[1];
13171     for (i = 0; i < height; i++) {
13172       lives_memcpy(dst, src, width);
13173       dst += rowstrides[1];
13174       src += irowstrides[1];
13175     }
13176     dst = (uint8_t *)new_pixel_data[2] + offs_y * rowstrides[2] + offs_x;
13177     src = (uint8_t *)pixel_data[2];
13178     for (i = 0; i < height; i++) {
13179       lives_memcpy(dst, src, width);
13180       dst += rowstrides[2];
13181       src += irowstrides[2];
13182     }
13183     dst = (uint8_t *)new_pixel_data[3] + offs_y * rowstrides[3] + offs_x;
13184     src = (uint8_t *)pixel_data[3];
13185     for (i = 0; i < height; i++) {
13186       lives_memcpy(dst, src, width);
13187       dst += rowstrides[3];
13188       src += irowstrides[3];
13189     }
13190     break;
13191 
13192   case WEED_PALETTE_YUV422P:
13193     dst = (uint8_t *)new_pixel_data[0] + offs_y * rowstrides[0] + offs_x;
13194     src = (uint8_t *)pixel_data[0];
13195     for (i = 0; i < height; i++) {
13196       lives_memcpy(dst, src, width);
13197       dst += rowstrides[0];
13198       src += irowstrides[0];
13199     }
13200     height >>= 1;
13201     offs_x >>= 1;
13202     dst = (uint8_t *)new_pixel_data[1] + offs_y * rowstrides[1] + offs_x;
13203     src = (uint8_t *)pixel_data[1];
13204     for (i = 0; i < height; i++) {
13205       lives_memcpy(dst, src, width);
13206       dst += rowstrides[1];
13207       src += irowstrides[1];
13208     }
13209     dst = (uint8_t *)new_pixel_data[2] + offs_y * rowstrides[2] + offs_x;
13210     src = (uint8_t *)pixel_data[2];
13211     for (i = 0; i < height; i++) {
13212       lives_memcpy(dst, src, width);
13213       dst += rowstrides[2];
13214       src += irowstrides[2];
13215     }
13216     break;
13217 
13218   case WEED_PALETTE_YUV420P:
13219   case WEED_PALETTE_YVU420P:
13220     dst = (uint8_t *)new_pixel_data[0] + offs_y * rowstrides[0] + offs_x;
13221     src = (uint8_t *)pixel_data[0];
13222     for (i = 0; i < height; i++) {
13223       lives_memcpy(dst, src, width);
13224       dst += rowstrides[0];
13225       src += irowstrides[0];
13226     }
13227     height >>= 1;
13228     offs_x >>= 1;
13229     width >>= 1;
13230     offs_y >>= 1;
13231     dst = (uint8_t *)new_pixel_data[1] + offs_y * rowstrides[1] + offs_x;
13232     src = (uint8_t *)pixel_data[1];
13233     for (i = 0; i < height; i++) {
13234       lives_memcpy(dst, src, width);
13235       dst += rowstrides[1];
13236       src += irowstrides[1];
13237     }
13238     dst = (uint8_t *)new_pixel_data[2] + offs_y * rowstrides[2] + offs_x;
13239     src = (uint8_t *)pixel_data[2];
13240     for (i = 0; i < height; i++) {
13241       lives_memcpy(dst, src, width);
13242       dst += rowstrides[2];
13243       src += irowstrides[2];
13244     }
13245     break;
13246 
13247   case WEED_PALETTE_RGBFLOAT:
13248     width *= 3 * sizeof(float);
13249     dst = (uint8_t *)new_pixel_data[0] + offs_y * rowstrides[0] + offs_x * 3 * sizeof(float);
13250     src = (uint8_t *)pixel_data[0];
13251     for (i = 0; i < height; i++) {
13252       lives_memcpy(dst, src, width);
13253       dst += rowstrides[0];
13254       src += irowstrides[0];
13255     }
13256     break;
13257 
13258   case WEED_PALETTE_RGBAFLOAT:
13259     width *= 4 * sizeof(float);
13260     dst = (uint8_t *)new_pixel_data[0] + offs_y * rowstrides[0] + offs_x * 4 * sizeof(float);
13261     src = (uint8_t *)pixel_data[0];
13262     for (i = 0; i < height; i++) {
13263       lives_memcpy(dst, src, width);
13264       dst += rowstrides[0];
13265       src += irowstrides[0];
13266     }
13267     break;
13268 
13269   case WEED_PALETTE_AFLOAT:
13270     width *= sizeof(float);
13271     dst = (uint8_t *)new_pixel_data[0] + offs_y * rowstrides[0] + offs_x * sizeof(float);
13272     src = (uint8_t *)pixel_data[0];
13273     for (i = 0; i < height; i++) {
13274       lives_memcpy(dst, src, width);
13275       dst += rowstrides[0];
13276       src += irowstrides[0];
13277     }
13278     break;
13279 
13280   case WEED_PALETTE_A8:
13281     dst = (uint8_t *)new_pixel_data[0] + offs_y * rowstrides[0] + offs_x;
13282     src = (uint8_t *)pixel_data[0];
13283     for (i = 0; i < height; i++) {
13284       lives_memcpy(dst, src, width);
13285       dst += rowstrides[0];
13286       src += irowstrides[0];
13287     }
13288     break;
13289 
13290   // assume offs_x and width is a multiple of 8
13291   case WEED_PALETTE_A1:
13292     width >>= 3;
13293     dst = (uint8_t *)new_pixel_data[0] + offs_y * rowstrides[0] + (offs_x >> 3);
13294     src = (uint8_t *)pixel_data[0];
13295     for (i = 0; i < height; i++) {
13296       lives_memcpy(dst, src, width);
13297       dst += rowstrides[0];
13298       src += irowstrides[0];
13299     }
13300     break;
13301   }
13302 
13303   if (mainw->frame_layer && weed_layer_get_pixel_data_packed(mainw->frame_layer) == pixel_data[0]) {
13304     /// retain pixel_data if it belongs to mainw->frame_layer
13305     weed_layer_nullify_pixel_data(old_layer);
13306   }
13307   /// otherwise do not nullify, as we want to free old pixel_data
13308   weed_layer_free(old_layer);
13309   lives_free(pixel_data);
13310   lives_free(new_pixel_data);
13311   lives_free(irowstrides);
13312   lives_free(rowstrides);
13313   return TRUE;
13314 
13315 memfail2:
13316   weed_layer_pixel_data_free(layer);
13317   weed_layer_copy(layer, old_layer);
13318   weed_layer_nullify_pixel_data(old_layer);
13319   weed_layer_free(old_layer);
13320   lives_free(pixel_data);
13321   lives_free(irowstrides);
13322   return FALSE;
13323 }
13324 
13325 
13326 /**
13327    @brief turn a (Gdk)Pixbuf into a Weed layer
13328 
13329    return TRUE if we can use the original pixbuf pixels; in this case the pixbuf pixels should only be freed via
13330    lives_layer_pixel_data_free() or lives_layer_free()
13331    see code example.
13332 
13333    code example:
13334 
13335    if (pixbuf) {
13336    if (!pixbuf_to_layer(layer, pixbuf)) lives_widget_object_unref(pixbuf);
13337    else do NOT unref the pixbuf !!!!
13338    }
13339 
13340    do something with layer...
13341 
13342    weed_layer_pixel_data_free(layer); unrefs the pixbuf
13343    or
13344    weed_layer_free(layer); also unrefs the pixbuf
13345 
13346 */
pixbuf_to_layer(weed_layer_t * layer,LiVESPixbuf * pixbuf)13347 boolean pixbuf_to_layer(weed_layer_t *layer, LiVESPixbuf * pixbuf) {
13348   size_t framesize;
13349   void *pixel_data;
13350   void *in_pixel_data;
13351   int rowstride;
13352   int width;
13353   int height;
13354   int nchannels, palette;
13355 
13356   if (!LIVES_IS_PIXBUF(pixbuf)) {
13357     weed_layer_set_size(layer, 0, 0);
13358     weed_set_int_value(layer, WEED_LEAF_ROWSTRIDES, 0);
13359     weed_layer_pixel_data_free(layer);
13360     return FALSE;
13361   }
13362 
13363   rowstride = lives_pixbuf_get_rowstride(pixbuf);
13364   width = lives_pixbuf_get_width(pixbuf);
13365   height = lives_pixbuf_get_height(pixbuf);
13366   nchannels = lives_pixbuf_get_n_channels(pixbuf);
13367 
13368   weed_layer_set_width(layer, width);
13369   weed_layer_set_height(layer, height);
13370   weed_set_int_value(layer, WEED_LEAF_ROWSTRIDES, rowstride);
13371 
13372   if (!weed_plant_has_leaf(layer, WEED_LEAF_CURRENT_PALETTE)) {
13373 #ifdef GUI_GTK
13374     if (nchannels == 4) weed_set_int_value(layer, WEED_LEAF_CURRENT_PALETTE, WEED_PALETTE_RGBA32);
13375     else weed_set_int_value(layer, WEED_LEAF_CURRENT_PALETTE, WEED_PALETTE_RGB24);
13376 #endif
13377   }
13378 
13379   if (rowstride == get_last_pixbuf_rowstride_value(width, nchannels)) {
13380     in_pixel_data = (void *)lives_pixbuf_get_pixels(pixbuf);
13381     weed_layer_pixel_data_free(layer);
13382     weed_set_voidptr_value(layer, WEED_LEAF_PIXEL_DATA, in_pixel_data);
13383     weed_set_voidptr_value(layer, WEED_LEAF_HOST_PIXBUF_SRC, pixbuf);
13384     palette = weed_get_int_value(layer, WEED_LEAF_CURRENT_PALETTE, NULL);
13385     if (weed_palette_is_rgb(palette)) weed_set_int_value(layer, WEED_LEAF_GAMMA_TYPE, WEED_GAMMA_SRGB);
13386     return TRUE;
13387   }
13388 
13389   framesize = ALIGN_CEIL(rowstride * height, 32);
13390   pixel_data = lives_calloc(framesize >> 5, 32);
13391 
13392   if (pixel_data) {
13393     in_pixel_data = (void *)lives_pixbuf_get_pixels_readonly(pixbuf);
13394     lives_memcpy(pixel_data, in_pixel_data, rowstride * (height - 1));
13395     // this part is needed because layers always have a memory size height*rowstride, whereas gdkpixbuf can have
13396     // a shorter last row
13397     lives_memcpy((uint8_t *)pixel_data + rowstride * (height - 1), (uint8_t *)in_pixel_data + rowstride * (height - 1),
13398                  get_last_pixbuf_rowstride_value(width, nchannels));
13399   }
13400 
13401   weed_layer_pixel_data_free(layer);
13402   weed_set_voidptr_value(layer, WEED_LEAF_PIXEL_DATA, pixel_data);
13403 
13404   palette = weed_get_int_value(layer, WEED_LEAF_CURRENT_PALETTE, NULL);
13405   if (weed_palette_is_rgb(palette)) weed_set_int_value(layer, WEED_LEAF_GAMMA_TYPE, WEED_GAMMA_SRGB);
13406 
13407   return FALSE;
13408 }
13409 
13410 
swap_red_blue(int pal)13411 LIVES_LOCAL_INLINE int swap_red_blue(int pal) {
13412   if (pal == WEED_PALETTE_RGB24) return WEED_PALETTE_BGR24;
13413   if (pal == WEED_PALETTE_RGBA32) return WEED_PALETTE_BGRA32;
13414   if (pal == WEED_PALETTE_BGR24) return WEED_PALETTE_RGB24;
13415   if (pal == WEED_PALETTE_BGRA32) return WEED_PALETTE_RGBA32;
13416   return WEED_PALETTE_END;
13417 }
13418 
13419 /**
13420    @brief look for shortcuts in palette conversions
13421    instead of converting e.g RGB -> BGRA, we may be able to pretend that the
13422    input palette is BGR and thus the conversion to BGRA is slightly cheaper
13423    we can do this provided the caller can take into account that the red / blue
13424    components are now swapped.
13425    If this is possible, then either inpal or outpal mau ne altered, and TRUE is returned
13426 */
consider_swapping(int * inpal,int * outpal)13427 boolean consider_swapping(int *inpal, int *outpal) {
13428   if (*inpal == *outpal) return FALSE;
13429   if (!weed_palette_is_rgb(*inpal) || !weed_palette_is_rgb(*outpal)) return FALSE;
13430   switch (*inpal) {
13431   case WEED_PALETTE_RGB24:
13432   case WEED_PALETTE_RGBA32:
13433     switch (*outpal) {
13434     case WEED_PALETTE_BGR24:
13435     case WEED_PALETTE_BGRA32:
13436       *inpal = swap_red_blue(*inpal);
13437       return TRUE;
13438     default: return FALSE;
13439     }
13440   case WEED_PALETTE_BGR24:
13441   case WEED_PALETTE_BGRA32:
13442     switch (*outpal) {
13443     case WEED_PALETTE_RGB24:
13444     case WEED_PALETTE_RGBA32:
13445     case WEED_PALETTE_ARGB32:
13446       *inpal = swap_red_blue(*inpal);
13447       return TRUE;
13448     default: return FALSE;
13449     }
13450   case WEED_PALETTE_ARGB32:
13451     /// since we dont have ABGR, we can switch the out palette instead
13452     switch (*outpal) {
13453     case WEED_PALETTE_BGR24:
13454     case WEED_PALETTE_BGRA32:
13455       *outpal = swap_red_blue(*outpal);
13456       return TRUE;
13457     default: return FALSE;
13458     }
13459   default:
13460     break;
13461   }
13462   return FALSE;
13463 }
13464 
13465 
13466 
13467 #ifdef GUI_GTK
13468 
13469 /**
13470    @brief convert a weed layer to lives_painter (a.k.a cairo)
13471 
13472    width, height and rowstrides of source layer may all change */
layer_to_lives_painter(weed_layer_t * layer)13473 lives_painter_t *layer_to_lives_painter(weed_layer_t *layer) {
13474   lives_painter_surface_t *surf;
13475   lives_painter_t *cairo;
13476   lives_painter_format_t cform;
13477   uint8_t *src, *dst, *pixel_data;
13478 
13479   int irowstride, orowstride;
13480   int width, widthx;
13481   int height, pal;
13482 
13483   register int i;
13484 
13485   if (weed_plant_has_leaf(layer, WEED_LEAF_HOST_SURFACE_SRC)) {
13486     surf = (lives_painter_surface_t *)weed_get_voidptr_value(layer, WEED_LEAF_HOST_SURFACE_SRC, NULL);
13487   } else {
13488     width = weed_layer_get_width(layer);
13489     pal = weed_layer_get_palette(layer);
13490     if (pal == WEED_PALETTE_A8) {
13491       cform = LIVES_PAINTER_FORMAT_A8;
13492       widthx = width;
13493     } else if (pal == WEED_PALETTE_A1) {
13494       cform = LIVES_PAINTER_FORMAT_A1;
13495       widthx = width >> 3;
13496     } else {
13497       cform = LIVES_PAINTER_COLOR_PALETTE(capable->byte_order);
13498       convert_layer_palette(layer, cform, 0);
13499       cform = LIVES_PAINTER_FORMAT_ARGB32;
13500       widthx = width << 2;
13501     }
13502 
13503     height = weed_layer_get_height(layer);
13504     irowstride = weed_layer_get_rowstride(layer);
13505     orowstride = lives_painter_format_stride_for_width(cform, width);
13506     src = (uint8_t *)weed_layer_get_pixel_data_packed(layer);
13507 
13508     if (irowstride == orowstride && !weed_plant_has_leaf(layer, WEED_LEAF_HOST_PIXBUF_SRC) &&
13509         !weed_plant_has_leaf(layer, WEED_LEAF_HOST_ORIG_PDATA)) {
13510       pixel_data = src;
13511     } else {
13512       dst = pixel_data = (uint8_t *)lives_calloc(1, height * orowstride);
13513       if (!pixel_data) return NULL;
13514       for (i = 0; i < height; i++) {
13515         lives_memcpy(dst, src, widthx);
13516         dst += orowstride;
13517         src += irowstride;
13518       }
13519       weed_layer_pixel_data_free(layer);
13520       weed_layer_set_pixel_data_packed(layer, pixel_data);
13521       weed_layer_set_rowstride(layer, orowstride);
13522     }
13523 
13524     if (weed_palette_has_alpha(pal)) {
13525       int flags = weed_get_int_value(layer, WEED_LEAF_FLAGS, NULL);
13526       if (!(flags & WEED_LAYER_ALPHA_PREMULT)) {
13527         // if we have post-multiplied alpha, pre multiply
13528         alpha_unpremult(layer, FALSE);
13529         flags |= WEED_LAYER_ALPHA_PREMULT;
13530         weed_set_int_value(layer, WEED_LEAF_FLAGS, flags);
13531       }
13532     }
13533     surf = lives_painter_image_surface_create_for_data(pixel_data, cform, width, height, orowstride);
13534   }
13535   if (!surf) return NULL;
13536 
13537   cairo = lives_painter_create_from_surface(surf); // surf is refcounted
13538 #ifdef DEBUG_CAIRO_SURFACE
13539   g_print("VALaa1 = %d %p\n", cairo_surface_get_reference_count(surf), surf);
13540 #endif
13541   weed_set_voidptr_value(layer, WEED_LEAF_PIXEL_DATA, lives_painter_image_surface_get_data(surf));
13542   weed_set_voidptr_value(layer, WEED_LEAF_HOST_SURFACE_SRC, surf);
13543   return cairo;
13544 }
13545 
13546 
13547 /**
13548    @brief convert a lives_painter_t (a.k.a) cairo_t to a weed layer */
lives_painter_to_layer(lives_painter_t * cr,weed_layer_t * layer)13549 boolean lives_painter_to_layer(lives_painter_t *cr, weed_layer_t *layer) {
13550   // updates a weed_layer from a cr
13551   void *src;
13552   lives_painter_surface_t *surface = lives_painter_get_target(cr), *xsurface = NULL;
13553   lives_painter_format_t  cform;
13554 
13555   int width, height, rowstride;
13556 
13557   /// flush to ensure all writing to the image surface was done
13558   lives_painter_surface_flush(surface);
13559 
13560   if (weed_plant_has_leaf(layer, WEED_LEAF_HOST_SURFACE_SRC)) {
13561     xsurface = (lives_painter_surface_t *)weed_get_voidptr_value(layer, WEED_LEAF_HOST_SURFACE_SRC, NULL);
13562   }
13563   if (xsurface != surface) weed_layer_pixel_data_free(layer);
13564 
13565   src = lives_painter_image_surface_get_data(surface);
13566 
13567   weed_layer_set_pixel_data_packed(layer, src);
13568   weed_set_voidptr_value(layer, WEED_LEAF_HOST_SURFACE_SRC, surface);
13569 
13570 #ifdef DEBUG_CAIRO_SURFACE
13571   g_print("VALaa2 = %d %p\n", cairo_surface_get_reference_count(surface), surface);
13572 #endif
13573   lives_painter_surface_reference(surface);
13574   lives_painter_destroy(cr);
13575 
13576   width = lives_painter_image_surface_get_width(surface);
13577   height = lives_painter_image_surface_get_height(surface);
13578   rowstride = lives_painter_image_surface_get_stride(surface);
13579 
13580   weed_layer_set_rowstride(layer, rowstride);
13581   weed_layer_set_size(layer, width, height);
13582 
13583   cform = lives_painter_image_surface_get_format(surface);
13584 
13585   switch (cform) {
13586   case LIVES_PAINTER_FORMAT_ARGB32:
13587     if (capable->byte_order == LIVES_BIG_ENDIAN) {
13588       weed_layer_set_palette(layer, WEED_PALETTE_ARGB32);
13589     } else {
13590       weed_layer_set_palette(layer, WEED_PALETTE_BGRA32);
13591     }
13592     weed_layer_set_gamma(layer, WEED_GAMMA_SRGB);
13593 
13594     if (prefs->alpha_post) {
13595       /// un-premultiply the alpha
13596       alpha_unpremult(layer, TRUE);
13597     } else {
13598       int flags = weed_layer_get_flags(layer);
13599       flags |= WEED_LAYER_ALPHA_PREMULT;
13600       weed_layer_set_flags(layer, flags);
13601     }
13602     break;
13603 
13604   case LIVES_PAINTER_FORMAT_A8:
13605     weed_layer_set_palette(layer, WEED_PALETTE_A8);
13606     break;
13607 
13608   case LIVES_PAINTER_FORMAT_A1:
13609     weed_layer_set_palette(layer, WEED_PALETTE_A1);
13610     break;
13611 
13612   default:
13613     break;
13614   }
13615 
13616   return TRUE;
13617 }
13618 
13619 #endif
13620 
13621 
resize_all(int fileno,int width,int height,lives_img_type_t imgtype,boolean do_back,int * nbad,int * nmiss)13622 int resize_all(int fileno, int width, int height, lives_img_type_t imgtype, boolean do_back, int *nbad, int *nmiss) {
13623   LiVESPixbuf *pixbuf;
13624   LiVESError *error = NULL;
13625   lives_clip_t *sfile;
13626   lives_img_type_t ximgtype;
13627   weed_layer_t *layer;
13628   char *fname;
13629   int miss = 0, bad = 0;
13630   int nimty = (int)N_IMG_TYPES;
13631   int j, nres = 0;
13632 
13633   mainw->cancelled = CANCEL_NONE;
13634   if (!IS_VALID_CLIP(fileno)) return 0;
13635   sfile = mainw->files[fileno];
13636   for (int i = 0; i < sfile->frames; i++) {
13637     threaded_dialog_spin((double)i / (double)sfile->frames);
13638     if (mainw->cancelled) return nres;
13639     if (sfile->frame_index && sfile->frame_index[i] != -1) continue;
13640     ximgtype = imgtype;
13641     fname = make_image_file_name(sfile, i + 1, get_image_ext_for_type(ximgtype));
13642     if (!lives_file_test(fname, LIVES_FILE_TEST_EXISTS)) {
13643       // check all img_types
13644       for (j = 1; j < nimty; j++) {
13645         ximgtype = (lives_img_type_t)j;
13646         if (ximgtype == imgtype) continue;
13647         lives_free(fname);
13648         fname = make_image_file_name(sfile, i + 1, get_image_ext_for_type(ximgtype));
13649         if (lives_file_test(fname, LIVES_FILE_TEST_EXISTS)) break;
13650       }
13651       if (j == nimty) {
13652         miss++;
13653         lives_free(fname);
13654         continue;
13655       } else bad++;
13656     }
13657     layer = weed_layer_new(WEED_LAYER_TYPE_VIDEO);
13658     weed_set_int_value(layer, WEED_LEAF_HOST_FLAGS, LIVES_LAYER_LOAD_IF_NEEDS_RESIZE);
13659     if (!weed_layer_create_from_file_progressive(layer, fname, width, height, WEED_PALETTE_END,
13660         get_image_ext_for_type(ximgtype))) {
13661       lives_free(fname);
13662       miss++;
13663       continue;
13664     }
13665 
13666     if (weed_layer_get_width(layer) == width
13667         && weed_layer_get_height(layer) == height) {
13668       weed_layer_free(layer);
13669       lives_free(fname);
13670       continue;
13671     }
13672 
13673     if (!resize_layer(layer, width, height, LIVES_INTERP_BEST, WEED_PALETTE_END,
13674                       WEED_YUV_CLAMPING_UNCLAMPED)) {
13675       weed_layer_free(layer);
13676       lives_free(fname);
13677       continue;
13678     }
13679     pixbuf = layer_to_pixbuf(layer, TRUE, FALSE);
13680     weed_layer_free(layer);
13681     if (pixbuf) {
13682       if (do_back) {
13683         char *fname_bak = make_image_file_name(sfile, i + 1, LIVES_FILE_EXT_BAK);
13684         if (lives_file_test(fname_bak, LIVES_FILE_TEST_EXISTS)) lives_rm(fname_bak);
13685         lives_mv(fname, fname_bak);
13686       }
13687       lives_pixbuf_save(pixbuf, fname, ximgtype, 100 - prefs->ocp, width, height, &error);
13688       lives_widget_object_unref(pixbuf);
13689       if (error) {
13690         lives_error_free(error);
13691         error = NULL;
13692         lives_free(fname);
13693         miss++;
13694         continue;
13695       }
13696       nres++;
13697     }
13698     lives_free(fname);
13699   }
13700   if (nbad) *nbad = bad;
13701   if (nmiss) *nmiss = miss;
13702   return nres;
13703 }
13704 
13705 
13706 /**
13707    @brief create a layer, setting the most important properties */
weed_layer_create(int width,int height,int * rowstrides,int palette)13708 weed_layer_t *weed_layer_create(int width, int height, int *rowstrides, int palette) {
13709   weed_layer_t *layer = weed_layer_new(WEED_LAYER_TYPE_VIDEO);
13710 
13711   weed_layer_set_width(layer, width);
13712   weed_layer_set_height(layer, height);
13713 
13714   if (palette != WEED_PALETTE_END) {
13715     weed_layer_set_palette(layer, palette);
13716     if (rowstrides) weed_layer_set_rowstrides(layer, rowstrides, weed_palette_get_nplanes(palette));
13717   }
13718   return layer;
13719 }
13720 
13721 
weed_layer_create_full(int width,int height,int * rowstrides,int palette,int YUV_clamping,int YUV_sampling,int YUV_subspace,int gamma_type)13722 weed_layer_t *weed_layer_create_full(int width, int height, int *rowstrides, int palette,
13723                                      int YUV_clamping, int YUV_sampling, int YUV_subspace, int gamma_type) {
13724   weed_layer_t *layer = weed_layer_create(width, height, rowstrides, palette);
13725   weed_layer_set_palette_yuv(layer, palette, YUV_clamping, YUV_sampling, YUV_subspace);
13726   weed_layer_set_gamma(layer, gamma_type);
13727   return layer;
13728 }
13729 
13730 
13731 /**
13732    @brief copy source layer slayer to dest layer dlayer
13733 
13734    if dlayer is NULL, we return a new layer, otherwise we return dlayer
13735    for a newly created layer, this is a deep copy, since the pixel_data array is also copied
13736    for an existing dlayer, we copy pixel_data by reference.
13737    all the other relevant attributes are also copied
13738 */
weed_layer_copy(weed_layer_t * dlayer,weed_layer_t * slayer)13739 weed_layer_t *weed_layer_copy(weed_layer_t *dlayer, weed_layer_t *slayer) {
13740   weed_layer_t *layer;
13741   void **pd_array = NULL;
13742 
13743   if (!slayer || (!WEED_IS_LAYER(slayer) && !WEED_PLANT_IS_CHANNEL(slayer))) return NULL;
13744 
13745   if (dlayer) {
13746     if (!WEED_IS_LAYER(dlayer) && !WEED_PLANT_IS_CHANNEL(dlayer)) return NULL;
13747     layer = dlayer;
13748   }
13749 
13750   pd_array = weed_layer_get_pixel_data(slayer, NULL);
13751 
13752   if (!dlayer) {
13753     /// deep copy
13754     int height = weed_layer_get_height(slayer);
13755     int width = weed_layer_get_width(slayer);
13756     int palette = weed_layer_get_palette(slayer);
13757     int *rowstrides = weed_layer_get_rowstrides(slayer, NULL);
13758     if (height <= 0 || width < 0 || !rowstrides || !weed_palette_is_valid(palette)) {
13759       if (pd_array) lives_free(pd_array);
13760       return NULL;
13761     } else {
13762       layer = weed_layer_create(width, height, rowstrides, palette);
13763       if (!pd_array) weed_layer_nullify_pixel_data(layer);
13764       else copy_pixel_data(layer, slayer, 0);
13765       lives_free(rowstrides);
13766     }
13767   } else {
13768     /// shallow copy
13769     weed_leaf_dup(layer, slayer, WEED_LEAF_ROWSTRIDES);
13770     weed_leaf_dup(layer, slayer, WEED_LEAF_PIXEL_DATA);
13771     weed_leaf_dup(layer, slayer, WEED_LEAF_NATURAL_SIZE);
13772     weed_leaf_copy_or_delete(layer, WEED_LEAF_HEIGHT, slayer);
13773     weed_leaf_copy_or_delete(layer, WEED_LEAF_WIDTH, slayer);
13774     weed_leaf_copy_or_delete(layer, WEED_LEAF_CURRENT_PALETTE, slayer);
13775     if (pd_array) {
13776       weed_leaf_copy_or_delete(layer, WEED_LEAF_HOST_PIXBUF_SRC, slayer);
13777       weed_leaf_copy_or_delete(layer, WEED_LEAF_HOST_ORIG_PDATA, slayer);
13778       weed_leaf_copy_or_delete(layer, WEED_LEAF_HOST_SURFACE_SRC, slayer);
13779       weed_leaf_copy_or_delete(layer, WEED_LEAF_HOST_PIXEL_DATA_CONTIGUOUS, slayer);
13780     }
13781     if (pd_array) {
13782       if (weed_leaf_set_flags(layer, WEED_LEAF_PIXEL_DATA,
13783                               weed_leaf_get_flags(slayer, WEED_LEAF_PIXEL_DATA)));
13784     }
13785   }
13786 
13787   weed_leaf_copy_or_delete(layer, WEED_LEAF_GAMMA_TYPE, slayer);
13788   weed_leaf_copy_or_delete(layer, WEED_LEAF_FLAGS, slayer);
13789   weed_leaf_copy_or_delete(layer, WEED_LEAF_YUV_CLAMPING, slayer);
13790   weed_leaf_copy_or_delete(layer, WEED_LEAF_YUV_SUBSPACE, slayer);
13791   weed_leaf_copy_or_delete(layer, WEED_LEAF_YUV_SAMPLING, slayer);
13792   weed_leaf_copy_or_delete(layer, WEED_LEAF_PIXEL_ASPECT_RATIO, slayer);
13793 
13794   if (pd_array) lives_free(pd_array);
13795   return layer;
13796 }
13797 
13798 
weed_layer_count_refs(weed_layer_t * layer)13799 LIVES_GLOBAL_INLINE int weed_layer_count_refs(weed_layer_t *layer) {
13800   int refs;
13801   if (!layer) return 0;
13802   refs = weed_get_int_value(layer, WEED_LEAF_HOST_REFS, NULL);
13803   return refs;
13804 }
13805 
13806 
13807 /**
13808    @brief free pixel_data from layer
13809 
13810    we do not free if WEED_LEAF_HOST_ORIG_PDATA is set (data is an alpha in which "belongs" to another out param)
13811 
13812    take care of WEED_LEAF_HOST_PIXEL_DATA_CONTIGUOUS
13813    take care of WEED_LEAF_HOST_PIXBUF_SRC
13814    take care of WEED_LEAF_HOST_SURFACE_SRC
13815 
13816    sets WEED_LEAF_PIXEL_DATA to NULL for the layer
13817 
13818    this function should always be used to free WEED_LEAF_PIXEL_DATA */
weed_layer_pixel_data_free(weed_layer_t * layer)13819 void weed_layer_pixel_data_free(weed_layer_t *layer) {
13820   void **pixel_data;
13821   int pd_elements;
13822 
13823   if (!layer) return;
13824 
13825   if (weed_leaf_get_flags(layer, WEED_LEAF_PIXEL_DATA) & LIVES_FLAG_MAINTAIN_VALUE)
13826     return;
13827 
13828   if (weed_get_boolean_value(layer, WEED_LEAF_HOST_ORIG_PDATA, NULL) == WEED_TRUE)
13829     return;
13830 
13831   if (weed_layer_count_refs(layer) > 1) {
13832     weed_layer_nullify_pixel_data(layer);
13833     return;
13834   }
13835 
13836   if ((pixel_data = weed_layer_get_pixel_data(layer, &pd_elements)) != NULL) {
13837     if (pd_elements > 0) {
13838       if (weed_plant_has_leaf(layer, WEED_LEAF_HOST_PIXBUF_SRC)) {
13839         LiVESPixbuf *pixbuf = (LiVESPixbuf *)weed_get_voidptr_value(layer, WEED_LEAF_HOST_PIXBUF_SRC, NULL);
13840         if (pixbuf) lives_widget_object_unref(pixbuf);
13841       } else {
13842         if (weed_plant_has_leaf(layer, WEED_LEAF_HOST_SURFACE_SRC)) {
13843           lives_painter_surface_t *surface = (lives_painter_surface_t *)weed_get_voidptr_value(layer,
13844                                              WEED_LEAF_HOST_SURFACE_SRC, NULL);
13845           if (surface) {
13846             // this is where most surfaces die, as we convert from BGRA -> RGB
13847             uint8_t *pdata = lives_painter_image_surface_get_data(surface);
13848 #ifdef DEBUG_CAIRO_SURFACE
13849             g_print("VALaa23rrr = %d %p\n", cairo_surface_get_reference_count(surface), surface);
13850 #endif
13851             // call twice to remove our extra ref.
13852             lives_painter_surface_destroy(surface);
13853             lives_painter_surface_destroy(surface);
13854             lives_free(pdata);
13855           }
13856         } else {
13857           if (weed_get_boolean_value(layer, WEED_LEAF_HOST_PIXEL_DATA_CONTIGUOUS, NULL) == WEED_TRUE) {
13858             pd_elements = 1;
13859           }
13860           for (int i = 0; i < pd_elements; i++) {
13861             if (pixel_data[i]) lives_free(pixel_data[i]);
13862           }
13863         }
13864       }
13865       lives_free(pixel_data);
13866       weed_layer_nullify_pixel_data(layer);
13867     }
13868   }
13869 
13870   weed_leaf_delete(layer, WEED_LEAF_HOST_PIXEL_DATA_CONTIGUOUS);
13871   weed_leaf_delete(layer, WEED_LEAF_HOST_PIXBUF_SRC);
13872   weed_leaf_delete(layer, WEED_LEAF_HOST_SURFACE_SRC);
13873 }
13874 
13875 
13876 /**
13877    @brief frees pixel_data for a layer, then the layer itself
13878 
13879    if plant is freed
13880    returns (void *)NULL for convenience
13881 */
13882 
weed_layer_free(weed_layer_t * layer)13883 LIVES_GLOBAL_INLINE weed_layer_t *weed_layer_free(weed_layer_t *layer) {
13884   if (weed_layer_unref(layer)) return layer;
13885   return NULL;
13886 }
13887 
weed_layer_unref(weed_layer_t * layer)13888 int weed_layer_unref(weed_layer_t *layer) {
13889   int refs;
13890   if (!layer) return 0;
13891   refs = weed_get_int_value(layer, WEED_LEAF_HOST_REFS, NULL) - 1;
13892   weed_set_int_value(layer, WEED_LEAF_HOST_REFS, refs);
13893   if (refs > 0) return refs;
13894   weed_layer_pixel_data_free(layer);
13895   weed_plant_free(layer);
13896   return 0;
13897 }
13898 
weed_layer_ref(weed_layer_t * layer)13899 LIVES_GLOBAL_INLINE int weed_layer_ref(weed_layer_t *layer) {
13900   int refs;
13901   if (!layer) return 0;
13902   refs = weed_get_int_value(layer, WEED_LEAF_HOST_REFS, NULL);
13903   weed_set_int_value(layer, WEED_LEAF_HOST_REFS, ++refs);
13904   return refs;
13905 }
13906 
13907 
weed_layer_get_pixel_data(weed_layer_t * layer,int * nplanes)13908 LIVES_GLOBAL_INLINE void **weed_layer_get_pixel_data(weed_layer_t *layer, int *nplanes) {
13909   if (nplanes) *nplanes = 0;
13910   if (!layer)  return NULL;
13911   return weed_get_voidptr_array_counted(layer, WEED_LEAF_PIXEL_DATA, nplanes);
13912 }
13913 
13914 
weed_layer_get_pixel_data_packed(weed_layer_t * layer)13915 LIVES_GLOBAL_INLINE uint8_t *weed_layer_get_pixel_data_packed(weed_layer_t *layer) {
13916   if (!layer)  return NULL;
13917   return (uint8_t *)weed_get_voidptr_value(layer, WEED_LEAF_PIXEL_DATA, NULL);
13918 }
13919 
13920 
weed_layer_get_audio_data(weed_layer_t * layer,int * naudchans)13921 LIVES_GLOBAL_INLINE float **weed_layer_get_audio_data(weed_layer_t *layer, int *naudchans) {
13922   if (naudchans) *naudchans = 0;
13923   if (!layer)  return NULL;
13924   return (float **)weed_get_voidptr_array_counted(layer, WEED_LEAF_AUDIO_DATA, naudchans);
13925 }
13926 
13927 
weed_layer_get_rowstrides(weed_layer_t * layer,int * nplanes)13928 LIVES_GLOBAL_INLINE int *weed_layer_get_rowstrides(weed_layer_t *layer, int *nplanes) {
13929   if (nplanes) *nplanes = 0;
13930   if (!layer)  return NULL;
13931   return weed_get_int_array_counted(layer, WEED_LEAF_ROWSTRIDES, nplanes);
13932 }
13933 
13934 
weed_layer_get_rowstride(weed_layer_t * layer)13935 LIVES_GLOBAL_INLINE int weed_layer_get_rowstride(weed_layer_t *layer) {
13936   if (!layer)  return 0;
13937   return weed_get_int_value(layer, WEED_LEAF_ROWSTRIDES, NULL);
13938 }
13939 
13940 
weed_layer_get_width(weed_layer_t * layer)13941 LIVES_GLOBAL_INLINE int weed_layer_get_width(weed_layer_t *layer) {
13942   if (!layer)  return -1;
13943   return weed_get_int_value(layer, WEED_LEAF_WIDTH, NULL);
13944 }
13945 
13946 
weed_layer_get_width_pixels(weed_layer_t * layer)13947 LIVES_GLOBAL_INLINE int weed_layer_get_width_pixels(weed_layer_t *layer) {
13948   if (!layer)  return -1;
13949   return  weed_layer_get_width(layer) * weed_palette_get_pixels_per_macropixel(weed_layer_get_palette(layer));
13950 }
13951 
13952 
weed_layer_get_height(weed_layer_t * layer)13953 LIVES_GLOBAL_INLINE int weed_layer_get_height(weed_layer_t *layer) {
13954   if (!layer)  return -1;
13955   return weed_get_int_value(layer, WEED_LEAF_HEIGHT, NULL);
13956 }
13957 
13958 
weed_layer_get_yuv_clamping(weed_layer_t * layer)13959 LIVES_GLOBAL_INLINE int weed_layer_get_yuv_clamping(weed_layer_t *layer) {
13960   if (!layer)  return 0;
13961   return weed_get_int_value(layer, WEED_LEAF_YUV_CLAMPING, NULL);
13962 }
13963 
13964 
weed_layer_get_yuv_sampling(weed_layer_t * layer)13965 LIVES_GLOBAL_INLINE int weed_layer_get_yuv_sampling(weed_layer_t *layer) {
13966   if (!layer)  return 0;
13967   return weed_get_int_value(layer, WEED_LEAF_YUV_SAMPLING, NULL);
13968 }
13969 
13970 
weed_layer_get_yuv_subspace(weed_layer_t * layer)13971 LIVES_GLOBAL_INLINE int weed_layer_get_yuv_subspace(weed_layer_t *layer) {
13972   if (!layer)  return 0;
13973   return weed_get_int_value(layer, WEED_LEAF_YUV_SUBSPACE, NULL);
13974 }
13975 
13976 
weed_layer_get_palette(weed_layer_t * layer)13977 LIVES_GLOBAL_INLINE int weed_layer_get_palette(weed_layer_t *layer) {
13978   if (!layer)  return WEED_PALETTE_END;
13979   return weed_get_int_value(layer, WEED_LEAF_CURRENT_PALETTE, NULL);
13980 }
13981 
13982 
weed_layer_get_palette_yuv(weed_layer_t * layer,int * clamping,int * sampling,int * subspace)13983 LIVES_GLOBAL_INLINE int weed_layer_get_palette_yuv(weed_layer_t *layer, int *clamping, int *sampling, int *subspace) {
13984   if (!layer)  return WEED_PALETTE_END;
13985   if (clamping) *clamping = weed_layer_get_yuv_clamping(layer);
13986   if (sampling) *sampling = weed_layer_get_yuv_sampling(layer);
13987   if (subspace) *subspace = weed_layer_get_yuv_subspace(layer);
13988   return weed_get_int_value(layer, WEED_LEAF_CURRENT_PALETTE, NULL);
13989 }
13990 
13991 
weed_layer_get_audio_rate(weed_layer_t * layer)13992 LIVES_GLOBAL_INLINE int weed_layer_get_audio_rate(weed_layer_t *layer) {
13993   if (!WEED_IS_LAYER(layer)) return 0;
13994   return weed_get_int_value(layer, WEED_LEAF_AUDIO_RATE, NULL);
13995 }
13996 
13997 
weed_layer_get_naudchans(weed_layer_t * layer)13998 LIVES_GLOBAL_INLINE int weed_layer_get_naudchans(weed_layer_t *layer) {
13999   if (!WEED_IS_LAYER(layer)) return 0;
14000   return weed_get_int_value(layer, WEED_LEAF_AUDIO_CHANNELS, NULL);
14001 }
14002 
14003 
weed_layer_get_audio_length(weed_layer_t * layer)14004 LIVES_GLOBAL_INLINE int weed_layer_get_audio_length(weed_layer_t *layer) {
14005   if (!WEED_IS_LAYER(layer)) return 0;
14006   return weed_get_int_value(layer, WEED_LEAF_AUDIO_DATA_LENGTH, NULL);
14007 }
14008 
14009