1 /* WEED is free software; you can redistribute it and/or
2    modify it under the terms of the GNU Lesser General Public
3    License as published by the Free Software Foundation; either
4    version 3 of the License, or (at your option) any later version.
5 
6    Weed is distributed in the hope that it will be useful,
7    but WITHOUT ANY WARRANTY; without even the implied warranty of
8    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
9    Lesser General Public License for more details.
10 
11    You should have received a copy of the GNU Lesser General Public
12    License along with this source code; if not, write to the Free Software
13    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
14 
15    Weed is developed by:
16    Gabriel "Salsaman" Finch - http://lives-video.com
17 
18    partly based on LiViDO, which is developed by:
19    Niels Elburg - http://veejay.sf.net
20    Denis "Jaromil" Rojo - http://freej.dyne.org
21    Tom Schouten - http://zwizwa.fartit.com
22    Andraz Tori - http://cvs.cinelerra.org
23 
24    reviewed with suggestions and contributions from:
25    Silvano "Kysucix" Galliani - http://freej.dyne.org
26    Kentaro Fukuchi - http://megaui.net/fukuchi
27    Jun Iio - http://www.malib.net
28    Carlo Prelz - http://www2.fluido.as:8080/
29 */
30 
31 /* (C) G. Finch, 2005 - 2020u */
32 
33 #ifdef __WEED_HOST__
34 #error This file is intended only for Weed plugins
35 #endif
36 
37 #ifndef __HAVE_WEED_PLUGIN_UTILS__
38 #define __HAVE_WEED_PLUGIN_UTILS__
39 
40 #ifdef __WEED_PLUGIN__
41 
42 #ifndef NEED_LOCAL_WEED_PLUGIN
43 #include <weed/weed.h>
44 #include <weed/weed-effects.h>
45 #include <weed/weed-palettes.h>
46 #include <weed/weed-plugin-utils.h>
47 #else
48 #include "../../libweed/weed.h"
49 #include "../../libweed/weed-effects.h"
50 #include "../../libweed/weed-palettes.h"
51 #include "../../libweed/weed-plugin-utils.h"
52 #endif
53 #endif
54 
55 #include <string.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 ///////////////////////////////////////////////////////////
59 // this 90% of the API right here
60 static int wtrue = WEED_TRUE;
61 #define wlne(p,k) weed_leaf_num_elements((p),(k))
62 #define wlg(p,w,i,v) weed_leaf_get((p),(w),(i),(v))
63 #define wls(p,w,t,n,v) weed_leaf_set((p),(w),(t),(n),(v))
64 #define _leaf_has_value(p,k) ((wlne(p,k)>0)?1:0)
65 #define gg(p,w,i,v) (p?((wlg((p),(w),(i),(v))==WEED_SUCCESS)?v:0):0)
66 #define __WPFC__ WEED_PLANT_FILTER_CLASS
67 #define __WPPT__ WEED_PLANT_PARAMETER_TEMPLATE
68 #define __WPCT__ WEED_PLANT_CHANNEL_TEMPLATE
69 #define __WPP__ WEED_PLANT_PARAMETER
70 #define __WPFI__ WEED_PLANT_FILTER_INSTANCE
gg_i(weed_plant_t * p,const char * w)71 static inline int gg_i(weed_plant_t *p, const char *w)
72 {int v, *vp = (int *)gg(p, w, 0, &v); return vp ? v : 0;}
gg_p(weed_plant_t * p,const char * w,int i)73 static inline weed_plant_t *gg_p(weed_plant_t *p, const char *w, int i)
74 {weed_plant_t *v, **vp = (weed_plant_t **)gg(p, w, i, &v); return vp ? v : 0;}
gg_i64(weed_plant_t * p,const char * w)75 static inline int64_t gg_i64(weed_plant_t *p, const char *w)
76 {int64_t v, *vp = (int64_t *)gg(p, w, 0, &v); return vp ? v : 0;}
weed_plant_get_type(weed_plant_t * p)77 static inline int weed_plant_get_type(weed_plant_t *p) {return gg_i(p, WEED_LEAF_TYPE);}
_weed_get_gui(weed_plant_t * p)78 static inline weed_plant_t *_weed_get_gui(weed_plant_t *p) {
79   weed_plant_t *g = NULL; int t = weed_plant_get_type(p);
80   if (t != __WPFC__ && t != __WPPT__ && t != __WPP__ && t != __WPFI__) return NULL;
81   gg(p, WEED_LEAF_GUI, 0, (void *)&g);
82   if (!g) {
83     g = weed_plant_new(WEED_PLANT_GUI);
84     wls(p, WEED_LEAF_GUI, WEED_SEED_PLANTPTR, 1, &g);
85   } return g;
86 }
gg_dbl(weed_plant_t * p,const char * w)87 static inline double gg_dbl(weed_plant_t *p, const char *w)
88 {double v, *vp = (double *)gg(p, w, 0, &v); return vp ? v : 0;}
weed_get_api_version(weed_plant_t * pi)89 static inline int weed_get_api_version(weed_plant_t *pi)
90 {weed_plant_t *h; wlg(pi, WEED_LEAF_HOST_INFO, 0, &h); return gg_i(h, WEED_LEAF_FILTER_API_VERSION);}
_weed_plant_set_flags(weed_plant_t * p,int f)91 static inline void _weed_plant_set_flags(weed_plant_t *p, int f) {
92   int t = weed_plant_get_type(p);
93   if (t == __WPFC__ || t == __WPPT__ || t == __WPCT__ || t == WEED_PLANT_GUI)
94     wls(p, WEED_LEAF_FLAGS, WEED_SEED_INT, 1, &f);
95 }
weed_filter_set_flags(weed_plant_t * f,int fl)96 static inline void weed_filter_set_flags(weed_plant_t *f, int fl) {_weed_plant_set_flags(f, fl);}
weed_chantmpl_set_flags(weed_plant_t * c,int f)97 static inline void weed_chantmpl_set_flags(weed_plant_t *c, int f) {_weed_plant_set_flags(c, f);}
weed_paramtmpl_set_flags(weed_plant_t * p,int f)98 static inline void weed_paramtmpl_set_flags(weed_plant_t *p, int f) {_weed_plant_set_flags(p, f);}
weed_gui_set_flags(weed_plant_t * g,int f)99 static inline void weed_gui_set_flags(weed_plant_t *g, int f) {_weed_plant_set_flags(g, f);}
_weed_plant_set_name(weed_plant_t * p,const char * n)100 static inline void _weed_plant_set_name(weed_plant_t *p, const char *n) {
101   int t = weed_plant_get_type(p);
102   if (t == __WPFC__ || t == __WPPT__ || t == __WPCT__) wls(p, WEED_LEAF_NAME, WEED_SEED_STRING, 1, &n);
103 }
weed_filter_set_name(weed_plant_t * f,const char * n)104 static inline void weed_filter_set_name(weed_plant_t *f, const char *n) {_weed_plant_set_name(f, n);}
weed_chantmpl_set_name(weed_plant_t * c,const char * n)105 static inline void weed_chantmpl_set_name(weed_plant_t *c, const char *n) {_weed_plant_set_name(c, n);}
weed_paramtmpl_set_name(weed_plant_t * p,const char * n)106 static inline void weed_paramtmpl_set_name(weed_plant_t *p, const char *n) {_weed_plant_set_name(p, n);}
weed_paramtmpl_declare_transition(weed_plant_t * pt)107 static inline void weed_paramtmpl_declare_transition(weed_plant_t *pt)
108 {wls(pt, WEED_LEAF_IS_TRANSITION, WEED_SEED_BOOLEAN, 1, &wtrue);}
weed_plugin_set_package_version(weed_plant_t * pi,int v)109 static inline void weed_plugin_set_package_version(weed_plant_t *pi, int v)
110 {wls(pi, WEED_LEAF_VERSION, WEED_SEED_INT, 1, &v);}
weed_filter_get_gui(weed_plant_t * f)111 static inline weed_plant_t *weed_filter_get_gui(weed_plant_t *f) {return _weed_get_gui(f);}
weed_param_get_gui(weed_plant_t * p)112 static inline weed_plant_t *weed_param_get_gui(weed_plant_t *p) {return _weed_get_gui(p);}
weed_paramtmpl_get_gui(weed_plant_t * pt)113 static inline weed_plant_t *weed_paramtmpl_get_gui(weed_plant_t *pt) {return _weed_get_gui(pt);}
weed_instance_get_gui(weed_plant_t * i)114 static inline weed_plant_t *weed_instance_get_gui(weed_plant_t *i) {return _weed_get_gui(i);}
weed_get_host_info(weed_plant_t * pi)115 static inline weed_plant_t *weed_get_host_info(weed_plant_t *pi)
116 {weed_plant_t *hi; return *((weed_plant_t **)(gg(pi, WEED_LEAF_HOST_INFO, 0, (void *)&hi)));}
weed_get_host_verbosity(weed_plant_t * hi)117 static inline int weed_get_host_verbosity(weed_plant_t *hi) {return gg_i(hi, WEED_LEAF_VERBOSITY);}
_weed_plant_get_flags(weed_plant_t * p)118 static inline int _weed_plant_get_flags(weed_plant_t *p) {return gg_i(p, WEED_LEAF_FLAGS);}
weed_host_get_flags(weed_plant_t * h)119 static inline int weed_host_get_flags(weed_plant_t *h) {return _weed_plant_get_flags(h);}
weed_filter_get_flags(weed_plant_t * f)120 static inline int weed_filter_get_flags(weed_plant_t *f) {return _weed_plant_get_flags(f);}
weed_filter_get_version(weed_plant_t * f)121 static inline int weed_filter_get_version(weed_plant_t *f) {return gg_i(f, WEED_LEAF_VERSION);}
weed_chantmpl_get_flags(weed_plant_t * c)122 static inline int weed_chantmpl_get_flags(weed_plant_t *c) {return _weed_plant_get_flags(c);}
weed_paramtmpl_get_flags(weed_plant_t * p)123 static inline int weed_paramtmpl_get_flags(weed_plant_t *p) {return _weed_plant_get_flags(p);}
weed_instance_get_flags(weed_plant_t * i)124 static inline int weed_instance_get_flags(weed_plant_t *i) {return _weed_plant_get_flags(i);}
weed_host_supports_linear_gamma(weed_plant_t * h)125 static inline int weed_host_supports_linear_gamma(weed_plant_t *h)
126 {return (weed_host_get_flags(h) & WEED_HOST_SUPPORTS_LINEAR_GAMMA) ? 1 : 0;}
weed_host_supports_premultiplied_alpha(weed_plant_t * h)127 static inline int weed_host_supports_premultiplied_alpha(weed_plant_t *h)
128 {return (weed_host_get_flags(h) & WEED_HOST_SUPPORTS_PREMULTIPLIED_ALPHA) ? 1 : 0;}
weed_instance_get_filter(weed_plant_t * i)129 static inline weed_plant_t *weed_instance_get_filter(weed_plant_t *i)
130 {weed_plant_t *f; return *((weed_plant_t **)gg(i, WEED_LEAF_FILTER_CLASS, 0, (void *)&f));}
weed_get_in_channel(weed_plant_t * i,int x)131 static inline weed_plant_t *weed_get_in_channel(weed_plant_t *i, int x)
132 {return gg_p(i, WEED_LEAF_IN_CHANNELS, x);}
weed_get_out_channel(weed_plant_t * i,int x)133 static inline weed_plant_t *weed_get_out_channel(weed_plant_t *i, int x)
134 {return gg_p(i, WEED_LEAF_OUT_CHANNELS, x);}
weed_get_in_param(weed_plant_t * i,int x)135 static inline weed_plant_t *weed_get_in_param(weed_plant_t *i, int x)
136 {return gg_p(i, WEED_LEAF_IN_PARAMETERS, x);}
weed_get_out_param(weed_plant_t * i,int x)137 static inline weed_plant_t *weed_get_out_param(weed_plant_t *i, int x)
138 {return gg_p(i, WEED_LEAF_OUT_PARAMETERS, x);}
weed_channel_get_pixel_data(weed_plant_t * c)139 static inline void *weed_channel_get_pixel_data(weed_plant_t *c)
140 {void *pd; return *((void **)(gg(c, WEED_LEAF_PIXEL_DATA, 0, (void *)&pd)));}
weed_channel_get_width(weed_plant_t * c)141 static inline int weed_channel_get_width(weed_plant_t *c) {return gg_i(c, WEED_LEAF_WIDTH);}
weed_channel_get_height(weed_plant_t * c)142 static inline int weed_channel_get_height(weed_plant_t *c) {return gg_i(c, WEED_LEAF_HEIGHT);}
weed_channel_get_palette(weed_plant_t * c)143 static inline int weed_channel_get_palette(weed_plant_t *c) {return gg_i(c, WEED_LEAF_CURRENT_PALETTE);}
weed_channel_get_yuv_clamping(weed_plant_t * c)144 static inline int weed_channel_get_yuv_clamping(weed_plant_t *c) {return gg_i(c, WEED_LEAF_YUV_CLAMPING);}
weed_channel_get_stride(weed_plant_t * c)145 static inline int weed_channel_get_stride(weed_plant_t *c) {return gg_i(c, WEED_LEAF_ROWSTRIDES);}
weed_channel_get_offset(weed_plant_t * c)146 static inline int weed_channel_get_offset(weed_plant_t *c) {return gg_i(c, WEED_LEAF_OFFSET);}
weed_channel_get_real_height(weed_plant_t * c)147 static inline int weed_channel_get_real_height(weed_plant_t *c)
148 {int h; return *((int *)(gg(c, WEED_LEAF_HEIGHT, wlne(c, WEED_LEAF_HEIGHT) - 1, &h)));}
weed_channel_is_disabled(weed_plant_t * c)149 static inline int weed_channel_is_disabled(weed_plant_t *c) {return gg_i(c, WEED_LEAF_DISABLED);}
weed_param_get_template(weed_plant_t * p)150 static inline weed_plant_t *weed_param_get_template(weed_plant_t *p)
151 {weed_plant_t *pt; return *((weed_plant_t **)(gg(p, WEED_LEAF_TEMPLATE, 0, (void *)&pt)));}
152 ////////////////////////////////////////////////////////////////////////////////////////////////////
153 
weed_plugin_info_init(weed_bootstrap_f weed_boot,int32_t weed_api_min_version,int32_t weed_api_max_version,int32_t weed_filter_api_min_version,int32_t weed_filter_api_max_version)154 static weed_plant_t *weed_plugin_info_init(weed_bootstrap_f weed_boot, int32_t weed_api_min_version,
155     int32_t weed_api_max_version,
156     int32_t weed_filter_api_min_version,
157     int32_t weed_filter_api_max_version) {
158   /////////////////////////////////////////////////////////
159   // get our bootstrap values
160 
161   // every plugin should call this at the beginning of its weed_setup(), and return NULL if this function returns NULL
162   // otherwise it may add its filter classes to the returned plugin_info, and then return the plugin_info to the host
163 
164   // if using the standard headers, then the plugin can use the macro WEED_SETUP_START(int weed_api_version, int filter_api_version)
165   // and this function will be called and the return if NULL will be handled
166 
167   weed_default_getter_f weed_default_getp;
168 
169   weed_plant_t *host_info = (*weed_boot)(&weed_default_getp, weed_api_min_version, weed_api_max_version,
170                                          weed_filter_api_min_version, weed_filter_api_max_version);
171 
172   weed_plant_t *plugin_info = NULL;
173   int32_t weed_abi_version = WEED_ABI_VERSION;
174   int32_t filter_api_version = WEED_API_VERSION;
175   weed_error_t err;
176 
177   if (!host_info) return NULL; // matching version was not found
178 
179   // we must use the default getter to bootstrap our actual API functions
180 
181   //////////// get weed api version /////////
182   if ((*weed_default_getp)(host_info, WEED_LEAF_WEED_API_VERSION,
183                            (weed_funcptr_t *)&weed_abi_version) != WEED_SUCCESS) return NULL;
184   if ((*weed_default_getp)(host_info, WEED_LEAF_GET_FUNC, (weed_funcptr_t *)&weed_leaf_get) != WEED_SUCCESS) return NULL;
185   if ((*weed_default_getp)(host_info, WEED_LEAF_MALLOC_FUNC, (weed_funcptr_t *)&weed_malloc) != WEED_SUCCESS) return NULL;
186   if ((*weed_default_getp)(host_info, WEED_LEAF_FREE_FUNC, (weed_funcptr_t *)&weed_free) != WEED_SUCCESS) return NULL;
187   if ((*weed_default_getp)(host_info, WEED_LEAF_MEMSET_FUNC, (weed_funcptr_t *)&weed_memset) != WEED_SUCCESS) return NULL;
188   if ((*weed_default_getp)(host_info, WEED_LEAF_MEMCPY_FUNC, (weed_funcptr_t *)&weed_memcpy) != WEED_SUCCESS) return NULL;
189 
190   // now we can use the normal get function (weed_leaf_get)
191 
192   // get any additional functions for higher API versions ////////////
193   weed_realloc = NULL;
194   weed_plant_free = NULL;
195 
196   // 2.0
197   if (weed_abi_version >= 200) {
198     if (weed_leaf_get(host_info, WEED_LEAF_REALLOC_FUNC, 0, &weed_realloc) != WEED_SUCCESS) return NULL;
199     if (weed_leaf_get(host_info, WEED_LEAF_CALLOC_FUNC, 0, &weed_calloc) != WEED_SUCCESS) return NULL;
200     if (weed_leaf_get(host_info, WEED_LEAF_MEMMOVE_FUNC, 0, &weed_memmove) != WEED_SUCCESS) return NULL;
201   }
202 
203   // base functions 1.0
204   if (weed_leaf_get(host_info, WEED_LEAF_SET_FUNC, 0, &weed_leaf_set) != WEED_SUCCESS) return NULL;
205   if (weed_leaf_get(host_info, WEED_PLANT_NEW_FUNC, 0, &weed_plant_new) != WEED_SUCCESS) return NULL;
206   if (weed_leaf_get(host_info, WEED_PLANT_LIST_LEAVES_FUNC, 0, &weed_plant_list_leaves) != WEED_SUCCESS) return NULL;
207   if (weed_leaf_get(host_info, WEED_LEAF_NUM_ELEMENTS_FUNC, 0, &weed_leaf_num_elements) != WEED_SUCCESS) return NULL;
208   if (weed_leaf_get(host_info, WEED_LEAF_ELEMENT_SIZE_FUNC, 0, &weed_leaf_element_size) != WEED_SUCCESS) return NULL;
209   if (weed_leaf_get(host_info, WEED_LEAF_SEED_TYPE_FUNC, 0, &weed_leaf_seed_type) != WEED_SUCCESS) return NULL;
210   if (weed_leaf_get(host_info, WEED_LEAF_GET_FLAGS_FUNC, 0, &weed_leaf_get_flags) != WEED_SUCCESS) return NULL;
211 
212   weed_leaf_get(host_info, WEED_LEAF_FILTER_API_VERSION, 0, &filter_api_version);
213 
214   // base functions 2.0
215   if (filter_api_version >= 200) {
216     // added weed_plant_free for plugins
217     if (weed_leaf_get(host_info, WEED_PLANT_FREE_FUNC, 0, &weed_plant_free) != WEED_SUCCESS) return NULL;
218     // added weed_leaf_delete for plugins
219     if (weed_leaf_get(host_info, WEED_LEAF_DELETE_FUNC, 0, &weed_leaf_delete) != WEED_SUCCESS) return NULL;
220   }
221 
222   //////////////////////////////////////////////////////////////////////
223 
224   if (_leaf_has_value(host_info, WEED_LEAF_PLUGIN_INFO)) {
225     if ((err = weed_leaf_get(host_info, WEED_LEAF_PLUGIN_INFO, 0, &plugin_info)) == WEED_SUCCESS) {
226       int32_t type;
227       weed_leaf_get(plugin_info, WEED_LEAF_TYPE, 0, &type);
228       if (err != WEED_SUCCESS) return NULL;
229       if (type != WEED_PLANT_PLUGIN_INFO) plugin_info = NULL;
230     } else return NULL;
231   }
232 
233   if (!plugin_info) if (!(plugin_info = weed_plant_new(WEED_PLANT_PLUGIN_INFO))) return NULL;
234   weed_leaf_set(plugin_info, WEED_LEAF_HOST_INFO, WEED_SEED_PLANTPTR, 1, &host_info);
235   return plugin_info;
236 }
237 
weed_channel_template_init(const char * name,int flags)238 static weed_plant_t *weed_channel_template_init(const char *name, int flags) {
239   weed_plant_t *chantmpl = weed_plant_new(WEED_PLANT_CHANNEL_TEMPLATE);
240   if (!chantmpl || !name) return NULL;
241   weed_chantmpl_set_name(chantmpl, name); weed_chantmpl_set_flags(chantmpl, flags);
242   return chantmpl;
243 }
244 
weed_filter_class_init(const char * name,const char * author,int version,int flags,int * palettes,weed_init_f init_func,weed_process_f process_func,weed_deinit_f deinit_func,weed_plant_t ** in_chantmpls,weed_plant_t ** out_chantmpls,weed_plant_t ** in_paramtmpls,weed_plant_t ** out_paramtmpls)245 static weed_plant_t *weed_filter_class_init(const char *name, const char *author, int version, int flags, int *palettes,
246     weed_init_f init_func, weed_process_f process_func, weed_deinit_f deinit_func,
247     weed_plant_t **in_chantmpls, weed_plant_t **out_chantmpls,
248     weed_plant_t **in_paramtmpls, weed_plant_t **out_paramtmpls) {
249   int i;
250   weed_plant_t *filter_class = NULL;
251   if (name) filter_class = weed_plant_new(WEED_PLANT_FILTER_CLASS);
252   if (!filter_class) return NULL;
253   weed_filter_set_name(filter_class, name);
254   weed_leaf_set(filter_class, WEED_LEAF_AUTHOR, WEED_SEED_STRING, 1, &author);
255   weed_leaf_set(filter_class, WEED_LEAF_VERSION, WEED_SEED_INT, 1, &version);
256   weed_filter_set_flags(filter_class, flags);
257 
258   if (init_func) weed_leaf_set(filter_class, WEED_LEAF_INIT_FUNC, WEED_SEED_FUNCPTR, 1, &init_func);
259 
260   if (process_func) weed_leaf_set(filter_class, WEED_LEAF_PROCESS_FUNC, WEED_SEED_FUNCPTR, 1, &process_func);
261 
262   if (deinit_func) weed_leaf_set(filter_class, WEED_LEAF_DEINIT_FUNC, WEED_SEED_FUNCPTR, 1, &deinit_func);
263 
264   if (!in_chantmpls || !in_chantmpls[0])
265     weed_leaf_set(filter_class, WEED_LEAF_IN_CHANNEL_TEMPLATES, WEED_SEED_PLANTPTR, 0, NULL);
266   else {
267     for (i = 0; in_chantmpls[i] != NULL; i++);
268     weed_leaf_set(filter_class, WEED_LEAF_IN_CHANNEL_TEMPLATES, WEED_SEED_PLANTPTR, i, in_chantmpls);
269   }
270   if (!out_chantmpls || !out_chantmpls[0])
271     weed_leaf_set(filter_class, WEED_LEAF_OUT_CHANNEL_TEMPLATES, WEED_SEED_PLANTPTR, 0, NULL);
272   else {
273     for (i = 0; out_chantmpls[i] != NULL; i++);
274     weed_leaf_set(filter_class, WEED_LEAF_OUT_CHANNEL_TEMPLATES, WEED_SEED_PLANTPTR, i, out_chantmpls);
275   }
276   if (!in_paramtmpls || !in_paramtmpls[0])
277     weed_leaf_set(filter_class, WEED_LEAF_IN_PARAMETER_TEMPLATES, WEED_SEED_PLANTPTR, 0, NULL);
278   else {
279     for (i = 0; in_paramtmpls[i] != NULL; i++);
280     weed_leaf_set(filter_class, WEED_LEAF_IN_PARAMETER_TEMPLATES, WEED_SEED_PLANTPTR, i, in_paramtmpls);
281   }
282   if (!out_paramtmpls || !out_paramtmpls[0])
283     weed_leaf_set(filter_class, WEED_LEAF_OUT_PARAMETER_TEMPLATES, WEED_SEED_PLANTPTR, 0, NULL);
284   else {
285     for (i = 0; out_paramtmpls[i] != NULL; i++);
286     weed_leaf_set(filter_class, WEED_LEAF_OUT_PARAMETER_TEMPLATES, WEED_SEED_PLANTPTR, i, out_paramtmpls);
287   }
288   if (palettes) {
289     for (i = 0; palettes[i] != WEED_PALETTE_END; i++);
290     if (i == 0) weed_leaf_set(filter_class, WEED_LEAF_PALETTE_LIST, WEED_SEED_INT, 0, NULL);
291     weed_leaf_set(filter_class, WEED_LEAF_PALETTE_LIST, WEED_SEED_INT, i, palettes);
292   }
293   return filter_class;
294 }
295 
weed_plugin_info_add_filter_class(weed_plant_t * plugin_info,weed_plant_t * filter_class)296 static void weed_plugin_info_add_filter_class(weed_plant_t *plugin_info, weed_plant_t *filter_class) {
297   int num_filters = 0, i;
298   weed_plant_t **filters;
299   if (_leaf_has_value(plugin_info, WEED_LEAF_FILTERS)) num_filters = weed_leaf_num_elements(plugin_info, WEED_LEAF_FILTERS);
300   filters = (weed_plant_t **)weed_malloc((num_filters + 1) * sizeof(weed_plant_t *));
301   if (!filters) return;
302   for (i = 0; i < num_filters; i++) weed_leaf_get(plugin_info, WEED_LEAF_FILTERS, i, &filters[i]);
303   filters[i] = filter_class;
304   weed_leaf_set(plugin_info, WEED_LEAF_FILTERS, WEED_SEED_PLANTPTR, i + 1, filters);
305   weed_leaf_set(filter_class, WEED_LEAF_PLUGIN_INFO, WEED_SEED_PLANTPTR, 1, &plugin_info);
306   weed_free(filters);
307 }
308 
309 //////////////////////////////////////////////////////////////////////////////////////////////
310 
weed_integer_init(const char * name,const char * label,int def,int min,int max)311 static weed_plant_t *weed_integer_init(const char *name, const char *label, int def, int min, int max) {
312   weed_plant_t *paramt = weed_plant_new(WEED_PLANT_PARAMETER_TEMPLATE);
313   int ptype = WEED_PARAM_INTEGER;
314   weed_plant_t *gui;
315   weed_paramtmpl_set_name(paramt, name);
316   weed_leaf_set(paramt, WEED_LEAF_PARAM_TYPE, WEED_SEED_INT, 1, &ptype);
317   weed_leaf_set(paramt, WEED_LEAF_DEFAULT, WEED_SEED_INT, 1, &def);
318   weed_leaf_set(paramt, WEED_LEAF_MIN, WEED_SEED_INT, 1, &min);
319   weed_leaf_set(paramt, WEED_LEAF_MAX, WEED_SEED_INT, 1, &max);
320   gui = weed_paramtmpl_get_gui(paramt);
321   weed_leaf_set(gui, WEED_LEAF_LABEL, WEED_SEED_STRING, 1, &label);
322   weed_leaf_set(gui, WEED_LEAF_USE_MNEMONIC, WEED_SEED_BOOLEAN, 1, &wtrue);
323   return paramt;
324 }
325 
weed_string_list_init(const char * name,const char * label,int def,const char ** const list)326 static weed_plant_t *weed_string_list_init(const char *name, const char *label, int def, const char **const list) {
327   int i = 0;
328   weed_plant_t *paramt, *gui;
329   int min = 0;
330   while (list[i] != NULL) i++;
331   i--;
332   if (def <= -1) min = def = -1;
333   paramt = weed_integer_init(name, label, def, min, i);
334   gui = weed_paramtmpl_get_gui(paramt);
335   weed_leaf_set(gui, WEED_LEAF_CHOICES, WEED_SEED_STRING, i + 1, list);
336   return paramt;
337 }
338 
weed_switch_init(const char * name,const char * label,int def)339 static weed_plant_t *weed_switch_init(const char *name, const char *label, int def) {
340   weed_plant_t *paramt = weed_plant_new(WEED_PLANT_PARAMETER_TEMPLATE);
341   int ptype = WEED_PARAM_SWITCH;
342   weed_plant_t *gui;
343   weed_paramtmpl_set_name(paramt, name);
344   weed_leaf_set(paramt, WEED_LEAF_PARAM_TYPE, WEED_SEED_INT, 1, &ptype);
345   weed_leaf_set(paramt, WEED_LEAF_DEFAULT, WEED_SEED_BOOLEAN, 1, &def);
346   gui = weed_paramtmpl_get_gui(paramt);
347   weed_leaf_set(gui, WEED_LEAF_LABEL, WEED_SEED_STRING, 1, &label);
348   weed_leaf_set(gui, WEED_LEAF_USE_MNEMONIC, WEED_SEED_BOOLEAN, 1, &wtrue);
349   return paramt;
350 }
351 
weed_radio_init(const char * name,const char * label,int def,int group)352 static weed_plant_t *weed_radio_init(const char *name, const char *label, int def, int group) {
353   weed_plant_t *paramt = weed_switch_init(name, label, def);
354   weed_leaf_set(paramt, WEED_LEAF_GROUP, WEED_SEED_INT, 1, &group);
355   return paramt;
356 }
357 
weed_float_init(const char * name,const char * label,double def,double min,double max)358 static weed_plant_t *weed_float_init(const char *name, const char *label, double def, double min, double max) {
359   weed_plant_t *paramt = weed_plant_new(WEED_PLANT_PARAMETER_TEMPLATE);
360   int ptype = WEED_PARAM_FLOAT;
361   weed_plant_t *gui;
362   weed_paramtmpl_set_name(paramt, name);
363   weed_leaf_set(paramt, WEED_LEAF_PARAM_TYPE, WEED_SEED_INT, 1, &ptype);
364   weed_leaf_set(paramt, WEED_LEAF_DEFAULT, WEED_SEED_DOUBLE, 1, &def);
365   weed_leaf_set(paramt, WEED_LEAF_MIN, WEED_SEED_DOUBLE, 1, &min);
366   weed_leaf_set(paramt, WEED_LEAF_MAX, WEED_SEED_DOUBLE, 1, &max);
367   gui = weed_paramtmpl_get_gui(paramt);
368   weed_leaf_set(gui, WEED_LEAF_LABEL, WEED_SEED_STRING, 1, &label);
369   weed_leaf_set(gui, WEED_LEAF_USE_MNEMONIC, WEED_SEED_BOOLEAN, 1, &wtrue);
370   return paramt;
371 }
372 
weed_text_init(const char * name,const char * label,const char * def)373 static weed_plant_t *weed_text_init(const char *name, const char *label, const char *def) {
374   weed_plant_t *paramt = weed_plant_new(WEED_PLANT_PARAMETER_TEMPLATE);
375   int ptype = WEED_PARAM_TEXT;
376   weed_plant_t *gui;
377   weed_paramtmpl_set_name(paramt, name);
378   weed_leaf_set(paramt, WEED_LEAF_PARAM_TYPE, WEED_SEED_INT, 1, &ptype);
379   weed_leaf_set(paramt, WEED_LEAF_DEFAULT, WEED_SEED_STRING, 1, &def);
380   gui = weed_paramtmpl_get_gui(paramt);
381   weed_leaf_set(gui, WEED_LEAF_LABEL, WEED_SEED_STRING, 1, &label);
382   weed_leaf_set(gui, WEED_LEAF_USE_MNEMONIC, WEED_SEED_BOOLEAN, 1, &wtrue);
383   return paramt;
384 }
385 
weed_colRGBi_init(const char * name,const char * label,int red,int green,int blue)386 static weed_plant_t *weed_colRGBi_init(const char *name, const char *label, int red, int green, int blue) {
387   weed_plant_t *paramt = weed_plant_new(WEED_PLANT_PARAMETER_TEMPLATE);
388   int ptype = WEED_PARAM_COLOR;
389   int cspace = WEED_COLORSPACE_RGB;
390   int def[3] = {red, green, blue}, min = 0, max = 255;
391   weed_plant_t *gui;
392   weed_paramtmpl_set_name(paramt, name);
393   weed_leaf_set(paramt, WEED_LEAF_PARAM_TYPE, WEED_SEED_INT, 1, &ptype);
394   weed_leaf_set(paramt, WEED_LEAF_COLORSPACE, WEED_SEED_INT, 1, &cspace);
395   weed_leaf_set(paramt, WEED_LEAF_DEFAULT, WEED_SEED_INT, 3, def);
396   weed_leaf_set(paramt, WEED_LEAF_MIN, WEED_SEED_INT, 1, &min);
397   weed_leaf_set(paramt, WEED_LEAF_MAX, WEED_SEED_INT, 1, &max);
398   gui = weed_paramtmpl_get_gui(paramt);
399   weed_leaf_set(gui, WEED_LEAF_LABEL, WEED_SEED_STRING, 1, &label);
400   weed_leaf_set(gui, WEED_LEAF_USE_MNEMONIC, WEED_SEED_BOOLEAN, 1, &wtrue);
401   return paramt;
402 }
403 
weed_colRGBd_init(const char * name,const char * label,double red,double green,double blue)404 static weed_plant_t *weed_colRGBd_init(const char *name, const char *label, double red, double green, double blue) {
405   weed_plant_t *paramt = weed_plant_new(WEED_PLANT_PARAMETER_TEMPLATE);
406   int ptype = WEED_PARAM_COLOR;
407   int cspace = WEED_COLORSPACE_RGB;
408   double def[3] = {red, green, blue}, min = 0., max = 1.;
409   weed_plant_t *gui;
410   weed_paramtmpl_set_name(paramt, name);
411   weed_leaf_set(paramt, WEED_LEAF_PARAM_TYPE, WEED_SEED_INT, 1, &ptype);
412   weed_leaf_set(paramt, WEED_LEAF_COLORSPACE, WEED_SEED_INT, 1, &cspace);
413   weed_leaf_set(paramt, WEED_LEAF_DEFAULT, WEED_SEED_DOUBLE, 3, def);
414   weed_leaf_set(paramt, WEED_LEAF_MIN, WEED_SEED_DOUBLE, 1, &min);
415   weed_leaf_set(paramt, WEED_LEAF_MAX, WEED_SEED_DOUBLE, 1, &max);
416   gui = weed_paramtmpl_get_gui(paramt);
417   weed_leaf_set(gui, WEED_LEAF_LABEL, WEED_SEED_STRING, 1, &label);
418   weed_leaf_set(gui, WEED_LEAF_USE_MNEMONIC, WEED_SEED_BOOLEAN, 1, &wtrue);
419   return paramt;
420 }
421 
weed_out_param_integer_init(const char * name,int def,int min,int max)422 static weed_plant_t *weed_out_param_integer_init(const char *name, int def, int min, int max) {
423   weed_plant_t *paramt = weed_plant_new(WEED_PLANT_PARAMETER_TEMPLATE);
424   int ptype = WEED_PARAM_INTEGER;
425   weed_paramtmpl_set_name(paramt, name);
426   weed_leaf_set(paramt, WEED_LEAF_PARAM_TYPE, WEED_SEED_INT, 1, &ptype);
427   weed_leaf_set(paramt, WEED_LEAF_DEFAULT, WEED_SEED_INT, 1, &def);
428   weed_leaf_set(paramt, WEED_LEAF_MIN, WEED_SEED_INT, 1, &min);
429   weed_leaf_set(paramt, WEED_LEAF_MAX, WEED_SEED_INT, 1, &max);
430   return paramt;
431 }
432 
weed_out_param_integer_init_nominmax(const char * name,int def)433 static weed_plant_t *weed_out_param_integer_init_nominmax(const char *name, int def) {
434   weed_plant_t *paramt = weed_plant_new(WEED_PLANT_PARAMETER_TEMPLATE);
435   int ptype = WEED_PARAM_INTEGER;
436   weed_paramtmpl_set_name(paramt, name);
437   weed_leaf_set(paramt, WEED_LEAF_PARAM_TYPE, WEED_SEED_INT, 1, &ptype);
438   weed_leaf_set(paramt, WEED_LEAF_DEFAULT, WEED_SEED_INT, 1, &def);
439   return paramt;
440 }
441 
weed_out_param_switch_init(const char * name,int def)442 static weed_plant_t *weed_out_param_switch_init(const char *name, int def) {
443   weed_plant_t *paramt = weed_plant_new(WEED_PLANT_PARAMETER_TEMPLATE);
444   int ptype = WEED_PARAM_SWITCH;
445   weed_paramtmpl_set_name(paramt, name);
446   weed_leaf_set(paramt, WEED_LEAF_PARAM_TYPE, WEED_SEED_INT, 1, &ptype);
447   weed_leaf_set(paramt, WEED_LEAF_DEFAULT, WEED_SEED_BOOLEAN, 1, &def);
448   return paramt;
449 }
450 
weed_out_param_float_init(const char * name,double def,double min,double max)451 static weed_plant_t *weed_out_param_float_init(const char *name, double def, double min, double max) {
452   weed_plant_t *paramt = weed_plant_new(WEED_PLANT_PARAMETER_TEMPLATE);
453   int ptype = WEED_PARAM_FLOAT;
454   weed_paramtmpl_set_name(paramt, name);
455   weed_leaf_set(paramt, WEED_LEAF_PARAM_TYPE, WEED_SEED_INT, 1, &ptype);
456   weed_leaf_set(paramt, WEED_LEAF_DEFAULT, WEED_SEED_DOUBLE, 1, &def);
457   weed_leaf_set(paramt, WEED_LEAF_MIN, WEED_SEED_DOUBLE, 1, &min);
458   weed_leaf_set(paramt, WEED_LEAF_MAX, WEED_SEED_DOUBLE, 1, &max);
459   return paramt;
460 }
461 
weed_out_param_float_init_nominmax(const char * name,double def)462 static weed_plant_t *weed_out_param_float_init_nominmax(const char *name, double def) {
463   weed_plant_t *paramt = weed_plant_new(WEED_PLANT_PARAMETER_TEMPLATE);
464   int ptype = WEED_PARAM_FLOAT;
465   weed_paramtmpl_set_name(paramt, name);
466   weed_leaf_set(paramt, WEED_LEAF_PARAM_TYPE, WEED_SEED_INT, 1, &ptype);
467   weed_leaf_set(paramt, WEED_LEAF_DEFAULT, WEED_SEED_DOUBLE, 1, &def);
468   return paramt;
469 }
470 
weed_out_param_text_init(const char * name,const char * def)471 static weed_plant_t *weed_out_param_text_init(const char *name, const char *def) {
472   weed_plant_t *paramt = weed_plant_new(WEED_PLANT_PARAMETER_TEMPLATE);
473   int ptype = WEED_PARAM_TEXT;
474   weed_paramtmpl_set_name(paramt, name);
475   weed_leaf_set(paramt, WEED_LEAF_PARAM_TYPE, WEED_SEED_INT, 1, &ptype);
476   weed_leaf_set(paramt, WEED_LEAF_DEFAULT, WEED_SEED_STRING, 1, &def);
477   return paramt;
478 }
479 
weed_out_param_colRGBi_init(const char * name,int red,int green,int blue)480 static weed_plant_t *weed_out_param_colRGBi_init(const char *name, int red, int green, int blue) {
481   weed_plant_t *paramt = weed_plant_new(WEED_PLANT_PARAMETER_TEMPLATE);
482   int ptype = WEED_PARAM_COLOR;
483   int cspace = WEED_COLORSPACE_RGB;
484   int def[3] = {red, green, blue}, min = 0, max = 255;
485   weed_paramtmpl_set_name(paramt, name);
486   weed_leaf_set(paramt, WEED_LEAF_PARAM_TYPE, WEED_SEED_INT, 1, &ptype);
487   weed_leaf_set(paramt, WEED_LEAF_COLORSPACE, WEED_SEED_INT, 1, &cspace);
488   weed_leaf_set(paramt, WEED_LEAF_DEFAULT, WEED_SEED_INT, 3, def);
489   weed_leaf_set(paramt, WEED_LEAF_MIN, WEED_SEED_INT, 1, &min);
490   weed_leaf_set(paramt, WEED_LEAF_MAX, WEED_SEED_INT, 1, &max);
491   return paramt;
492 }
493 
weed_out_param_colRGBd_init(const char * name,double red,double green,double blue)494 static weed_plant_t *weed_out_param_colRGBd_init(const char *name, double red, double green, double blue) {
495   weed_plant_t *paramt = weed_plant_new(WEED_PLANT_PARAMETER_TEMPLATE);
496   int ptype = WEED_PARAM_COLOR;
497   int cspace = WEED_COLORSPACE_RGB;
498   double def[3] = {red, green, blue}, min = 0, max = 1.;
499   weed_paramtmpl_set_name(paramt, name);
500   weed_leaf_set(paramt, WEED_LEAF_PARAM_TYPE, WEED_SEED_INT, 1, &ptype);
501   weed_leaf_set(paramt, WEED_LEAF_COLORSPACE, WEED_SEED_INT, 1, &cspace);
502   weed_leaf_set(paramt, WEED_LEAF_DEFAULT, WEED_SEED_DOUBLE, 3, def);
503   weed_leaf_set(paramt, WEED_LEAF_MIN, WEED_SEED_DOUBLE, 1, &min);
504   weed_leaf_set(paramt, WEED_LEAF_MAX, WEED_SEED_DOUBLE, 1, &max);
505   return paramt;
506 }
507 
508 ///////////////////////////////////////////////////////////////////////
509 
510 #ifdef NEED_AUDIO
weed_channel_get_audio_rate(weed_plant_t * channel)511 static inline int weed_channel_get_audio_rate(weed_plant_t *channel) {return gg_i(channel, WEED_LEAF_AUDIO_RATE);}
weed_channel_get_naudchans(weed_plant_t * channel)512 static inline int weed_channel_get_naudchans(weed_plant_t *channel) {return gg_i(channel, WEED_LEAF_AUDIO_CHANNELS);}
weed_channel_get_audio_length(weed_plant_t * channel)513 static inline int weed_channel_get_audio_length(weed_plant_t *channel) {return gg_i(channel, WEED_LEAF_AUDIO_DATA_LENGTH);}
weed_paramtmpl_declare_volume_mastern(weed_plant_t * pt)514 static inline void weed_paramtmpl_declare_volume_mastern(weed_plant_t *pt) {
515   weed_leaf_set(pt, WEED_LEAF_IS_VOLUME_MASTER, WEED_SEED_BOOLEAN, 1, &wtrue);
516 }
517 static weed_plant_t *weed_audio_channel_template_init(const char *name, int flags);
518 #ifdef __WEED_UTILS_H__
weed_channel_get_audio_data(weed_plant_t * channel,int * naudchans)519 static inline  float **weed_channel_get_audio_data(weed_plant_t *channel, int *naudchans) {
520   if (naudchans) *naudchans = 0;
521   return (float **)weed_get_voidptr_array_counted(channel, WEED_LEAF_AUDIO_DATA, naudchans);
522 }
523 #endif
weed_audio_channel_template_init(const char * name,int flags)524 static weed_plant_t *weed_audio_channel_template_init(const char *name, int flags) {
525   weed_plant_t *chantmpl = weed_channel_template_init(name, flags);
526   if (chantmpl) weed_leaf_set(chantmpl, WEED_LEAF_IS_AUDIO, WEED_SEED_BOOLEAN, 1, &wtrue);
527   return chantmpl;
528 }
529 #endif
530 
weed_is_threading(weed_plant_t * inst)531 static inline int weed_is_threading(weed_plant_t *inst) {
532   if (inst) {
533     weed_plant_t *ochan = weed_get_out_channel(inst, 0);
534     return (ochan && _leaf_has_value(ochan, WEED_LEAF_OFFSET)) ? WEED_TRUE : WEED_FALSE;
535   } return WEED_FALSE;
536 }
537 
538 #ifdef __WEED_UTILS_H__
weed_param_get_array_int(weed_plant_t * param,int * nvalues)539 static inline int *weed_param_get_array_int(weed_plant_t *param, int *nvalues) {
540   return (int *)(weed_get_int_array_counted(param, WEED_LEAF_VALUE, nvalues));
541 }
weed_param_get_array_boolean(weed_plant_t * param,int * nvalues)542 static inline int *weed_param_get_array_boolean(weed_plant_t *param, int *nvalues) {
543   return weed_get_boolean_array_counted(param, WEED_LEAF_VALUE, nvalues);
544 }
weed_param_get_array_double(weed_plant_t * param,int * nvalues)545 static inline double *weed_param_get_array_double(weed_plant_t *param, int *nvalues) {
546   return weed_get_double_array_counted(param, WEED_LEAF_VALUE, nvalues);
547 }
weed_param_get_array_int64(weed_plant_t * param,int * nvalues)548 static inline int64_t *weed_param_get_array_int64(weed_plant_t *param, int *nvalues) {
549   return weed_get_int64_array_counted(param, WEED_LEAF_VALUE, nvalues);
550 }
weed_param_get_array_string(weed_plant_t * param,int * nvalues)551 static inline char **weed_param_get_array_string(weed_plant_t *param, int *nvalues) {
552   return weed_get_string_array_counted(param, WEED_LEAF_VALUE, nvalues);
553 }
weed_get_in_channels(weed_plant_t * instance,int * nchans)554 static inline weed_plant_t **weed_get_in_channels(weed_plant_t *instance, int *nchans) {
555   return weed_get_plantptr_array_counted(instance, WEED_LEAF_IN_CHANNELS, nchans);
556 }
weed_get_out_channels(weed_plant_t * instance,int * nchans)557 static inline weed_plant_t **weed_get_out_channels(weed_plant_t *instance, int *nchans) {
558   return weed_get_plantptr_array_counted(instance, WEED_LEAF_OUT_CHANNELS, nchans);
559 }
weed_get_in_params(weed_plant_t * instance,int * nparams)560 static inline weed_plant_t **weed_get_in_params(weed_plant_t *instance, int *nparams) {
561   return weed_get_plantptr_array_counted(instance, WEED_LEAF_IN_PARAMETERS, nparams);
562 }
weed_get_out_params(weed_plant_t * instance,int * nparams)563 static inline weed_plant_t **weed_get_out_params(weed_plant_t *instance, int *nparams) {
564   return weed_get_plantptr_array_counted(instance, WEED_LEAF_OUT_PARAMETERS, nparams);
565 }
weed_channel_get_rowstrides(weed_plant_t * channel,int * nplanes)566 static inline int *weed_channel_get_rowstrides(weed_plant_t *channel, int *nplanes) {
567   return weed_get_int_array_counted(channel, WEED_LEAF_ROWSTRIDES, nplanes);
568 }
weed_channel_get_pixel_data_planar(weed_plant_t * channel,int * nplanes)569 static inline void **weed_channel_get_pixel_data_planar(weed_plant_t *channel, int *nplanes) {
570   return weed_get_voidptr_array_counted(channel, WEED_LEAF_PIXEL_DATA, nplanes);
571 }
572 #endif
573 
weed_param_get_value_int(weed_plant_t * param)574 static inline int weed_param_get_value_int(weed_plant_t *param) {return gg_i(param, WEED_LEAF_VALUE);}
weed_param_get_value_boolean(weed_plant_t * param)575 static inline int weed_param_get_value_boolean(weed_plant_t *param) {return weed_param_get_value_int(param);}
weed_param_get_value_double(weed_plant_t * param)576 static inline double weed_param_get_value_double(weed_plant_t *param) {return gg_dbl(param, WEED_LEAF_VALUE);}
weed_param_get_value_int64(weed_plant_t * param)577 static inline int64_t weed_param_get_value_int64(weed_plant_t *param) {return gg_i64(param, WEED_LEAF_VALUE);}
weed_param_get_value_string(weed_plant_t * param)578 static inline char *weed_param_get_value_string(weed_plant_t *param) {
579   char *value;
580   return (!weed_leaf_num_elements(param, WEED_LEAF_VALUE)
581           || !(value = (char *)weed_malloc(weed_leaf_element_size(param, WEED_LEAF_VALUE, 0) + 1))) ? NULL
582          : *((char **)gg(param, WEED_LEAF_VALUE, 0, (void *)&value));
583 }
584 
585 ///////////////////////////////////////////////////////////////////////////////////////////////
586 
_weed_clone_leaf(weed_plant_t * from,const char * key,weed_plant_t * to)587 static void _weed_clone_leaf(weed_plant_t *from, const char *key, weed_plant_t *to) {
588   int i, *datai;
589   double *datad;
590   char **datac;
591   int64_t *datai6;
592   void **datav;
593   weed_funcptr_t *dataf;
594   weed_plant_t **datap;
595   int32_t seed_type = weed_leaf_seed_type(from, key);
596   weed_size_t stlen, num = wlne(from, key);
597   if (num == 0) weed_leaf_set(to, key, seed_type, 0, NULL);
598   else {
599     switch (seed_type) {
600     case WEED_SEED_BOOLEAN:
601     case WEED_SEED_INT:
602       datai = (int *)weed_malloc(num * sizeof(int));
603       for (i = 0; (weed_size_t)i < num; i++) weed_leaf_get(from, key, i, &datai[i]);
604       weed_leaf_set(to, key, seed_type, num, datai);
605       weed_free(datai);
606       break;
607     case WEED_SEED_INT64:
608       datai6 = (int64_t *)weed_malloc(num * sizeof(int64_t));
609       for (i = 0; (weed_size_t)i < num; i++) weed_leaf_get(from, key, i, &datai6[i]);
610       weed_leaf_set(to, key, WEED_SEED_INT64, num, datai6);
611       weed_free(datai6);
612       break;
613     case WEED_SEED_DOUBLE:
614       datad = (double *)weed_malloc(num * sizeof(double));
615       for (i = 0; (weed_size_t)i < num; i++) weed_leaf_get(from, key, i, &datad[i]);
616       weed_leaf_set(to, key, WEED_SEED_DOUBLE, num, datad);
617       weed_free(datad);
618       break;
619     case WEED_SEED_FUNCPTR:
620       dataf = (weed_funcptr_t *)weed_malloc(num * sizeof(weed_funcptr_t));
621       for (i = 0; (weed_size_t)i < num; i++) weed_leaf_get(from, key, i, &dataf[i]);
622       weed_leaf_set(to, key, WEED_SEED_FUNCPTR, num, dataf);
623       weed_free(dataf);
624       break;
625     case WEED_SEED_VOIDPTR:
626       datav = (void **)weed_malloc(num * sizeof(void *));
627       for (i = 0; (weed_size_t)i < num; i++) weed_leaf_get(from, key, i, &datav[i]);
628       weed_leaf_set(to, key, WEED_SEED_VOIDPTR, num, datav);
629       weed_free(datav);
630       break;
631     case WEED_SEED_PLANTPTR:
632       datap = (weed_plant_t **)weed_malloc(num * sizeof(weed_plant_t *));
633       for (i = 0; (weed_size_t)i < num; i++) weed_leaf_get(from, key, i, &datap[i]);
634       weed_leaf_set(to, key, WEED_SEED_PLANTPTR, num, datap);
635       weed_free(datap);
636       break;
637     case WEED_SEED_STRING:
638       datac = (char **)weed_malloc(num * sizeof(char *));
639       for (i = 0; (weed_size_t)i < num; i++) {
640         stlen = weed_leaf_element_size(from, key, i);
641         datac[i] = (char *)weed_malloc(stlen + 1);
642         weed_leaf_get(from, key, i, &datac[i]);
643       }
644       weed_leaf_set(to, key, WEED_SEED_STRING, num, datac);
645       for (i = 0; (weed_size_t)i < num; i++) weed_free(datac[i]);
646       weed_free(datac);
647       break;
648     }
649   }
650 }
651 
weed_clone_plants(weed_plant_t ** plants)652 static weed_plant_t **weed_clone_plants(weed_plant_t **plants) {
653   //plants must be a NULL terminated array
654   int i, j, k, type, num_plants;
655   weed_plant_t **ret, *gui, *gui2;
656   char **leaves, **leaves2;
657   for (i = 0; plants[i]; i++);
658   num_plants = i;
659   ret = (weed_plant_t **)weed_malloc((num_plants + 1) * sizeof(weed_plant_t *));
660   if (!ret) return NULL;
661 
662   for (i = 0; i < num_plants; i++) {
663     weed_leaf_get(plants[i], WEED_LEAF_TYPE, 0, &type);
664     ret[i] = weed_plant_new(type);
665     if (!ret[i]) return NULL;
666 
667     leaves = weed_plant_list_leaves(plants[i], NULL);
668     for (j = 0; leaves[j]; j++) {
669       if (!strcmp(leaves[j], WEED_LEAF_GUI)) {
670         weed_leaf_get(plants[i], WEED_LEAF_GUI, 0, &gui);
671         gui2 = weed_plant_new(WEED_PLANT_GUI);
672         weed_leaf_set(ret[i], WEED_LEAF_GUI, WEED_SEED_PLANTPTR, 1, &gui2);
673         leaves2 = weed_plant_list_leaves(gui, NULL);
674         for (k = 0; leaves2[k] != NULL; k++) {
675           _weed_clone_leaf(gui, leaves2[k], gui2);
676           free(leaves2[k]);
677         }
678         free(leaves2);
679       } else _weed_clone_leaf(plants[i], leaves[j], ret[i]);
680       free(leaves[j]);
681     }
682     free(leaves);
683   }
684   ret[i] = NULL;
685   return ret;
686 }
687 
688 #if defined (NEED_ALPHA_SORT) // for wrappers, use this to sort filters alphabetically
689 typedef struct dlink_list dlink_list_t;
690 struct dlink_list {
691   weed_plant_t *filter;
692   const char *name;
693   dlink_list_t  *next;
694 };
695 
add_to_list_sorted(dlink_list_t * list,weed_plant_t * filter,const char * name)696 static dlink_list_t *add_to_list_sorted(dlink_list_t *list, weed_plant_t *filter, const char *name) {
697   dlink_list_t *entry = (dlink_list_t *)weed_malloc(sizeof(dlink_list_t));
698   if (!entry) return list;
699   entry->filter = filter;
700   entry->name = strdup(name);
701   entry->next = list;
702   return entry;
703 }
704 
dlink_list_cmp(const void * p1,const void * p2)705 static int dlink_list_cmp(const void *p1, const void *p2) {
706   dlink_list_t *l1 = *((dlink_list_t **)p1);
707   dlink_list_t *l2 = *((dlink_list_t **)p2);
708   return strcmp(l1->name, l2->name);
709 }
710 
add_filters_from_list(weed_plant_t * plugin_info,dlink_list_t * list)711 static int add_filters_from_list(weed_plant_t *plugin_info, dlink_list_t *list) {
712   int count = 0, ocount;
713   dlink_list_t **dll, *xlist;
714   // map list to flat array so we can qsort it. The original list was prepended, so adding in reverse restores original order.
715   for (xlist = list; xlist; xlist = xlist->next) count++;
716   dll = (dlink_list_t **)weed_malloc((ocount = count) * sizeof(dlink_list_t *));
717   for (xlist = list; xlist; xlist = xlist->next) {
718     dll[--count] = xlist;
719   }
720   qsort(dll, ocount, sizeof(dlink_list_t *), dlink_list_cmp);
721   for (count = 0; count < ocount; count++) {
722     weed_plugin_info_add_filter_class(plugin_info, dll[count]->filter);
723     free((void *)dll[count]->name); weed_free(dll[count]);
724   }
725   weed_free(dll);
726   return count;
727 }
728 #endif
729 
730 #if defined(NEED_RANDOM)
731 
732 #if defined _WIN32 || defined __CYGWIN__ || defined IS_MINGW
733 #ifndef _CRT_RAND_S
734 #define _CRT_RAND_S
735 #endif
736 #else
737 #include <sys/time.h>
738 #endif
739 #include <stdlib.h>
740 
drand(double max)741 static inline double drand(double max)
742 #if defined _WIN32 || defined __CYGWIN__ || defined IS_MINGW
743 {uint32_t rval; rand_s(&rval); return ((double)rval / (double)UINT_MAX * max);}
744 #else
745 {return (double)(drand48() * max);}
746 #endif
747 
xorshift(uint64_t x)748 static inline uint64_t xorshift(uint64_t x) {x ^= x << 13; x ^= x >> 7; return x ^ (x << 17);}
749 
fastrand(uint64_t oldval)750 static inline uint64_t fastrand(uint64_t oldval) {
751   // pseudo-random number generator
752   static uint64_t val = 0;
753   if (!val) {
754 #if defined _WIN32 || defined __CYGWIN__ || defined IS_MINGW
755     uint32_t rval, rval2; val++; rand_s(&rval); rand_s(&rval2); val = fastrand(((uint64_t)rval << 32) | (uint64_t)rval2) + 1;
756 #else
757     struct timeval t; gettimeofday(&t, NULL); srand48(t.tv_sec & 0xFFFFFFFFFFFF);
758     val = ((uint64_t)(lrand48() << 32) ^ (uint64_t)(lrand48())) + 1;
759 #endif
760   }
761   return (val = xorshift(val));
762 }
763 
fastrand_dbl(double range)764 static inline double fastrand_dbl(double range) {
765   static const double divd = (double)(0xFFFFFFFF);
766   double val = (double)fastrand(1) / divd;
767   return val / divd * range;
768 }
769 
fastrand_int(uint32_t range)770 static inline uint32_t fastrand_int(uint32_t range) {
771   return (uint32_t)(fastrand_dbl((double)(++range)));
772 }
773 
774 #endif
775 
776 //utilities
is_big_endian(void)777 static inline int is_big_endian(void) {
778   union memtest {int32_t num; char chr[4];} mm;
779   mm.num = 0x12345678; return (mm.chr[0] == 0x78) ? WEED_FALSE : WEED_TRUE;
780 }
781 
782 #if defined(NEED_PALETTE_UTILS)
weed_palette_is_alpha(int pal)783 static inline int weed_palette_is_alpha(int pal) {return (pal >= 1024 && pal < 2048) ? WEED_TRUE : WEED_FALSE;}
weed_palette_is_rgb(int pal)784 static inline int weed_palette_is_rgb(int pal) {return (pal < 512) ? WEED_TRUE : WEED_FALSE;}
weed_palette_is_yuv(int pal)785 static inline int weed_palette_is_yuv(int pal) {return (pal >= 512 && pal < 1024) ? WEED_TRUE : WEED_FALSE;}
weed_palette_get_nplanes(int pal)786 static inline int weed_palette_get_nplanes(int pal) {
787   if (pal == WEED_PALETTE_RGB24 || pal == WEED_PALETTE_BGR24 || pal == WEED_PALETTE_RGBA32
788       || pal == WEED_PALETTE_BGRA32 ||
789       pal == WEED_PALETTE_ARGB32 || pal == WEED_PALETTE_UYVY8888 || pal == WEED_PALETTE_YUYV8888
790       || pal == WEED_PALETTE_YUV411 ||
791       pal == WEED_PALETTE_YUV888 || pal == WEED_PALETTE_YUVA8888 || pal == WEED_PALETTE_AFLOAT
792       || pal == WEED_PALETTE_A8 ||
793       pal == WEED_PALETTE_A1 || pal == WEED_PALETTE_RGBFLOAT || pal == WEED_PALETTE_RGBAFLOAT) return 1;
794   if (pal == WEED_PALETTE_YUV420P || pal == WEED_PALETTE_YVU420P || pal == WEED_PALETTE_YUV422P ||
795       pal == WEED_PALETTE_YUV444P) return 3;
796   if (pal == WEED_PALETTE_YUVA4444P) return 4;
797   return 0;
798 }
799 
weed_palette_is_valid(int pal)800 static inline int weed_palette_is_valid(int pal) {return weed_palette_get_nplanes(pal) ? WEED_TRUE : WEED_FALSE;}
801 
weed_palette_is_float_palette(int pal)802 static inline int weed_palette_is_float_palette(int pal) {
803   return (pal == WEED_PALETTE_RGBAFLOAT || pal == WEED_PALETTE_AFLOAT
804           || pal == WEED_PALETTE_RGBFLOAT) ? WEED_TRUE : WEED_FALSE;
805 }
806 
weed_palette_has_alpha_channel(int pal)807 static inline int weed_palette_has_alpha_channel(int pal) {
808   return (pal == WEED_PALETTE_RGBA32 || pal == WEED_PALETTE_BGRA32 || pal == WEED_PALETTE_ARGB32 ||
809           pal == WEED_PALETTE_YUVA4444P || pal == WEED_PALETTE_YUVA8888 || pal == WEED_PALETTE_RGBAFLOAT ||
810           weed_palette_is_alpha(pal)) ? WEED_TRUE : WEED_FALSE;
811 }
812 
weed_palette_get_plane_ratio_horizontal(int pal,int plane)813 static inline double weed_palette_get_plane_ratio_horizontal(int pal, int plane) {
814   // return ratio of plane[n] width/plane[0] width;
815   if (plane == 0) return 1.0;
816   if (plane == 1 || plane == 2) {
817     if (pal == WEED_PALETTE_YUV444P || pal == WEED_PALETTE_YUVA4444P) return 1.0;
818     if (pal == WEED_PALETTE_YUV422P || pal == WEED_PALETTE_YUV420P || pal == WEED_PALETTE_YVU420P) return 0.5;
819   }
820   if (plane == 3) if (pal == WEED_PALETTE_YUVA4444P) return 1.0;
821   return 0.0;
822 }
823 
weed_palette_get_plane_ratio_vertical(int pal,int plane)824 static inline double weed_palette_get_plane_ratio_vertical(int pal, int plane) {
825   // return ratio of plane[n] height/plane[n] height
826   if (plane == 0) return 1.0;
827   if (plane == 1 || plane == 2) {
828     if (pal == WEED_PALETTE_YUV444P || pal == WEED_PALETTE_YUVA4444P || pal == WEED_PALETTE_YUV422P) return 1.0;
829     if (pal == WEED_PALETTE_YUV420P || pal == WEED_PALETTE_YVU420P) return 0.5;
830   }
831   if (plane == 3) if (pal == WEED_PALETTE_YUVA4444P) return 1.0;
832   return 0.0;
833 }
834 
blank_pixel(uint8_t * dst,int pal,int yuv_clamping,uint8_t * src)835 static size_t blank_pixel(uint8_t *dst, int pal, int yuv_clamping, uint8_t *src) {
836   // set src to non-null to preserve the alpha channel (if applicable)
837   // yuv_clamping
838   // only valid for non-planar (packed) palettes
839   unsigned char *idst = dst;
840   uint8_t y_black = yuv_clamping == WEED_YUV_CLAMPING_UNCLAMPED ? 0 : 16;
841   switch (pal) {
842   case WEED_PALETTE_RGB24: case WEED_PALETTE_BGR24: dst[0] = dst[1] = dst[2] = 0; dst += 3; break;
843   case WEED_PALETTE_RGBA32: case WEED_PALETTE_BGRA32:
844     dst[0] = dst[1] = dst[2] = 0; dst[3] = src == NULL ? 255 : src[3]; dst += 4; break;
845   case WEED_PALETTE_ARGB32: dst[1] = dst[2] = dst[3] = 0; dst[0] = src == NULL ? 255 : src[0]; dst += 4; break;
846   case WEED_PALETTE_UYVY8888: dst[1] = dst[3] = y_black; dst[0] = dst[2] = 128; dst += 4; break;
847   case WEED_PALETTE_YUYV8888: dst[0] = dst[2] = y_black; dst[1] = dst[3] = 128; dst += 4; break;
848   case WEED_PALETTE_YUV888: dst[0] = y_black; dst[1] = dst[2] = 128; dst += 3; break;
849   case WEED_PALETTE_YUVA8888: dst[0] = y_black; dst[1] = dst[2] = 128; dst[3] = src == NULL ? 255 : src[3]; dst += 4; break;
850   case WEED_PALETTE_YUV411: dst[0] = dst[3] = 128; dst[1] = dst[2] = dst[4] = dst[5] = y_black; dst += 6; break;
851   default: break;
852   } return dst - idst;
853 }
854 
blank_row(uint8_t ** pdst,int width,int pal,int yuv_clamping,int uvcopy,uint8_t ** psrc)855 static void blank_row(uint8_t **pdst, int width, int pal, int yuv_clamping, int uvcopy, uint8_t **psrc) {
856   // for YUV420 and YVU420, only set uvcopy for even rows, and increment pdst[1], pdst[2] on the odd rows
857   int nplanes, p, mpsize;
858   uint8_t *dst = *pdst, *src = NULL, black[3];
859   if (pal == WEED_PALETTE_RGB24 || pal == WEED_PALETTE_BGR24) {weed_memset(dst, 0, width * 3); return;}
860   if (!uvcopy && (pal == WEED_PALETTE_YUV420P || pal == WEED_PALETTE_YVU420P)) nplanes = 1;
861   else nplanes = weed_palette_get_nplanes(pal);
862   black[0] = yuv_clamping == WEED_YUV_CLAMPING_UNCLAMPED ? 0 : 16; black[1] = black[2] = 128;
863   for (p = 0; p < nplanes; p++) {
864     dst = pdst[p];
865     if (psrc != NULL) src = psrc[p];
866     if (p == 3) {if (!src) weed_memset(dst, 255, width); else weed_memcpy(dst, src, width); break;}
867     if (pal == WEED_PALETTE_YUV420P || pal == WEED_PALETTE_YVU420P || pal == WEED_PALETTE_YUV422P
868         || pal == WEED_PALETTE_YUV444P || pal == WEED_PALETTE_YUVA4444P) weed_memset(dst, black[p], width);
869     else {
870       // RGBA, BGRA, ARGB, YUV888, YUVA8888, UYVY, YUYV, YUV411
871       for (int i = 0; i < width; i++) {mpsize = blank_pixel(dst, pal, yuv_clamping, src); dst += mpsize; if (src != NULL) src += mpsize;}
872       break;
873     } if (p == 0 && (pal == WEED_PALETTE_YUV420P || pal == WEED_PALETTE_YVU420P || pal == WEED_PALETTE_YUV422P))
874       width >>= 1;
875   }
876 }
877 
blank_frame(void ** pdata,int width,int height,int * rs,int pal,int yuv_clamping)878 static void blank_frame(void **pdata, int width, int height, int *rs, int pal, int yuv_clamping) {
879   uint8_t *pd2[4];
880   int nplanes = weed_palette_get_nplanes(pal), odd, is_420 = (pal == WEED_PALETTE_YUV420P || pal == WEED_PALETTE_YVU420P);
881   int i, j;
882   for (j = 0; j < nplanes; j++) pd2[j] = (uint8_t *)pdata[j];
883   for (i = 0; i < height; i++) {
884     blank_row(pd2, width, pal, yuv_clamping, !(odd = i ^ 1), NULL);
885     for (j = 0; j < nplanes; j++) {pd2[j] += rs[j]; if (is_420 && !odd) break;}
886   }
887 }
888 #endif
889 
890 #if defined(NEED_PALETTE_CONVERSIONS)
891 
892 #ifndef myround
893 #define myround(n) ((n >= 0.) ? (int)(n + 0.5) : (int)(n - 0.5))
894 #endif
895 
896 static int alpha_inited = 0;
897 static int unal[256][256], al[256][256];
898 
init_unal(void)899 static inline void init_unal(void) {
900   // premult to postmult and vice-versa
901   for (int i = 0; i < 256; i++)
902     for (int j = 0; j < 256; j++) {
903       unal[i][j] = (float)j * 255. / (float)i; al[i][j] = (float)j * (float)i / 255.;
904     }
905 }
906 
alpha_premult(unsigned char * ptr,int width,int height,int rowstride,int pal,int un)907 static void alpha_premult(unsigned char *ptr, int width, int height, int rowstride, int pal, int un) {
908   int aoffs = 3, coffs = 0, psizel = 3, alpha, psize = 4;
909   int i, j, p;
910   switch (pal) {
911   case WEED_PALETTE_BGRA32: break;
912   case WEED_PALETTE_ARGB32: psizel = 4; coffs = 1; aoffs = 0; break;
913   default: return;
914   }
915   if (!alpha_inited) init_unal();
916   if (un == WEED_TRUE) {
917     for (i = 0; i < height; i++) {
918       for (j = 0; j < width; j += psize)
919       {alpha = ptr[j + aoffs]; for (p = coffs; p < psizel; p++) ptr[j + p] = unal[alpha][ptr[j + p]];}
920       ptr += rowstride;
921     }
922   } else {
923     for (i = 0; i < height; i++) for (j = 0; j < width; j += psize) {
924         alpha = ptr[j + aoffs];
925         for (p = coffs; p < psizel; p++) ptr[j + p] = al[alpha][ptr[j + p]];
926       }
927     ptr += rowstride;
928   }
929 }
930 
931 /* precomputed tables */
932 #define FP_BITS 16
933 static int Y_Ru[256], Y_Gu[256], Y_Bu[256];
934 static int Cb_Ru[256], Cb_Gu[256], Cb_Bu[256];
935 static int Cr_Ru[256], Cr_Gu[256], Cr_Bu[256];
936 static int YCL_YUCL[256], UVCL_UVUCL[256], YUCL_YCL[256];
937 static int yuv_rgb_inited = 0, y_y_inited = 0;
938 
init_RGB_to_YCbCr_tables(void)939 static void init_RGB_to_YCbCr_tables(void) {
940   for (int i = 0; i < 256; i++) {
941     Y_Ru[i] = myround(0.299 * (double)i * (1 << FP_BITS)); Y_Gu[i] = myround(0.587 * (double)i * (1 << FP_BITS));
942     Y_Bu[i] = myround(0.114 * (double)i * (1 << FP_BITS)); Cb_Bu[i] = myround(-0.168736 * (double)i * (1 << FP_BITS));
943     Cb_Gu[i] = myround(-0.331264 * (double)i * (1 << FP_BITS)); Cb_Ru[i] = myround((0.500 * (double)i + 128.) * (1 << FP_BITS));
944     Cr_Bu[i] = myround(0.500 * (double)i * (1 << FP_BITS)); Cr_Gu[i] = myround(-0.418688 * (double)i * (1 << FP_BITS));
945     Cr_Ru[i] = myround((-0.081312 * (double)i + 128.) * (1 << FP_BITS));
946   } yuv_rgb_inited = 1;
947 }
948 
949 // unclamped values
950 #define RGB_2_YIQ(a, b, c) do {short x; for (int i = 0; i < NUM_PIXELS_SQUARED; i++) {	\
951       Unit Y, I, Q;							\
952       if ((x = ((Y_Ru[(int)a[i]] + Y_Gu[(int)b[i]] + Y_Bu[(int)(int)c[i]]) >> FP_BITS)) > 255) x = 255; \
953       Y = x < 0 ? 0 : x;							\
954       if ((x = ((Cr_Ru[(int)a[i]] + Cr_Gu[(int)b[i]] + Cr_Bu[(int)c[i]]) >> FP_BITS)) > 255) x = 255; \
955       I = x < 0 ? 0 : x;							\
956       if ((x = ((Cb_Ru[(int)a[i]] + Cb_Gu[(int)b[i]] + Cb_Bu[(int)c[i]] ) >> FP_BITS)) > 255) x = 255; \
957       Q = x < 0 ? 0 : x; a[i] = Y; b[i] = I; c[i] = Q;					\
958     }} while(0)
959 
init_Y_to_Y_tables(void)960 static void init_Y_to_Y_tables(void) {
961   int i;
962   for (i = 0; i < 17; i++) {UVCL_UVUCL[i] = YCL_YUCL[i] = 0; YUCL_YCL[i] = (uint8_t)((float)i / 255. * 219. + .5)  + 16;}
963   for (; i < 235; i++) {
964     YCL_YUCL[i] = (int)((float)(i - 16.) / 219. * 255. + .5); UVCL_UVUCL[i] = (int)((float)(i - 16.) / 224. * 255. + .5);
965     YUCL_YCL[i] = (uint8_t)((float)i / 255. * 219. + .5)  + 16;
966   }
967   for (; i < 256; i++) {UVCL_UVUCL[i] = YCL_YUCL[i] = 255; YUCL_YCL[i] = (uint8_t)((float)i / 255. * 219. + .5)  + 16;}
968   y_y_inited = 1;
969 }
970 
y_unclamped_to_clamped(uint8_t y)971 static inline uint8_t y_unclamped_to_clamped(uint8_t y) {if (!y_y_inited) init_Y_to_Y_tables(); return YUCL_YCL[y];}
y_clamped_to_unclamped(uint8_t y)972 static inline uint8_t y_clamped_to_unclamped(uint8_t y) {if (!y_y_inited) init_Y_to_Y_tables(); return YCL_YUCL[y];}
uv_clamped_to_unclamped(uint8_t u)973 static inline uint8_t uv_clamped_to_unclamped(uint8_t u) {if (!y_y_inited) init_Y_to_Y_tables(); return UVCL_UVUCL[u];}
974 
calc_luma(uint8_t * pixel,int palette,int yuv_clamping)975 static uint8_t calc_luma(uint8_t *pixel, int palette, int yuv_clamping) {
976   if (!yuv_rgb_inited) init_RGB_to_YCbCr_tables();
977   switch (palette) {
978   case WEED_PALETTE_RGB24: case WEED_PALETTE_RGBA32: return (Y_Ru[pixel[0]] + Y_Gu[pixel[1]] + Y_Bu[pixel[2]]) >> FP_BITS;
979   case WEED_PALETTE_BGR24: case WEED_PALETTE_BGRA32: return (Y_Ru[pixel[2]] + Y_Gu[pixel[1]] + Y_Bu[pixel[0]]) >> FP_BITS;
980   case WEED_PALETTE_ARGB32: return (Y_Ru[pixel[1]] + Y_Gu[pixel[2]] + Y_Bu[pixel[3]]) >> FP_BITS;
981   default:
982     if (yuv_clamping == WEED_YUV_CLAMPING_UNCLAMPED) return pixel[0];
983     else {if (!y_y_inited) init_Y_to_Y_tables(); return YCL_YUCL[pixel[0]];}
984   } return 0;
985 }
986 
987 #endif
988 
989 #ifdef NEED_FONT_UTILS
990 #include <wchar.h>
991 static const wchar_t *fstretches[] = {L"UltraCondensed", L"ExtraCondensed", L"Condensed", L"SemiCondensed",
992                                       L"SemiExpanded", L"Expanded", L"ExtraExpanded", L"UltraExpanded", NULL
993                                      };
994 
995 static const wchar_t *fweights[] = {L"Thin", L"UltraLight", L"ExtraLight", L"Light", L"SemiLight", L"DemiLight",
996                                     L"Book", L"Regular", L"Medium",
997                                     L"DemiBold", L"SemiBold", L"Bold", L"ExtraBold", L"UltraBold", L"Black", L"Heavy",
998                                     L"UltraHeavy", L"UltraBlack", L"ExtraBlack", NULL
999                                    };
1000 
1001 static const wchar_t *fstyles[] = {L"Roman", L"Italic", L"Oblique", NULL};
1002 
weed_parse_font_string(const char * fontstr,char ** family,char ** fstretch,char ** fweight,char ** fstyle,int * size)1003 void weed_parse_font_string(const char *fontstr, char **family, char **fstretch, char **fweight,
1004                             char **fstyle, int *size) {
1005   wchar_t *token, *state, *next_token;
1006   wchar_t *xfamily = L"\0";
1007   wchar_t *xfstretch = L"\0";
1008   wchar_t *xfweight = L"\0";
1009   wchar_t *xfstyle = L"\0";
1010   wchar_t *xfontstr = NULL;;
1011   size_t wcs_len;
1012   int i;
1013 
1014   if (size) *size = 0;
1015   if (!fontstr) goto fontdone;
1016 
1017   wcs_len = mbstowcs(NULL, fontstr, 0) + 1;
1018   xfontstr = weed_calloc(wcs_len, sizeof(wchar_t));
1019   mbstowcs(xfontstr, fontstr, wcs_len);
1020 
1021   for (token = wcstok(xfontstr, L" ", &state); token; token = next_token) {
1022     if (*token == L'\0') {
1023       next_token = wcstok(NULL, L" ", &state);
1024       continue;
1025     }
1026     do {
1027       next_token = wcstok(NULL, L" ", &state);
1028     } while (next_token && *next_token == L'\0');
1029     if (!next_token) {
1030       wchar_t *endptr;
1031       uintmax_t umax = wcstoumax(token, &endptr, 10);
1032       if (umax > 0 && *endptr == L'\0') {
1033         if (size) *size = (int)(umax & 0x80FFFFFF);
1034         goto fontdone;
1035       }
1036     }
1037     if (*xfstretch == L'\0') {
1038       for (i = 0; fstretches[i]; i++) {
1039         if (!wcscasecmp(token, fstretches[i])) {
1040           xfstretch = (wchar_t *)fstretches[i];
1041           break;
1042         }
1043       }
1044       if (*xfstyle != L'\0') continue;
1045     }
1046     if (*xfweight == L'\0') {
1047       for (i = 0; fweights[i]; i++) {
1048         if (!wcscasecmp(token, fweights[i])) {
1049           xfweight = (wchar_t *)fweights[i];
1050           break;
1051         }
1052       }
1053       if (*xfweight != L'\0') continue;
1054     }
1055     if (*xfstyle == L'\0') {
1056       for (i = 0; fstyles[i]; i++) {
1057         if (!wcscasecmp(token, fstyles[i])) {
1058           xfstyle = (wchar_t *)fstyles[i];
1059           break;
1060         }
1061       }
1062       if (*xfstyle != L'\0') continue;
1063     }
1064     if (*xfstretch == L'\0' && *xfweight == L'\0' && *xfstyle == L'\0') {
1065       if (*xfamily != L'\0') {
1066         wchar_t tmp[256];
1067         swprintf(tmp, 256, L"%ls %ls", xfamily, token);
1068         free(xfamily);
1069         xfamily = wcsdup(tmp);
1070       } else xfamily = wcsdup(token);
1071     }
1072   }
1073 fontdone:
1074   if (family) {
1075     wcs_len = wcstombs(NULL, xfamily, 0) + 1;
1076     *family = weed_calloc(wcs_len, 1);
1077     wcstombs(*family, xfamily, wcs_len);
1078   }
1079   if (fstretch) {
1080     wcs_len = wcstombs(NULL, xfstretch, 0) + 1;
1081     *fstretch = weed_calloc(wcs_len, 1);
1082     wcstombs(*fstretch, xfstretch, wcs_len);
1083   }
1084   if (fweight) {
1085     wcs_len = wcstombs(NULL, xfweight, 0) + 1;
1086     *fweight = weed_calloc(wcs_len, 1);
1087     wcstombs(*fweight, xfweight, wcs_len);
1088   }
1089   if (fstyle) {
1090     wcs_len = wcstombs(NULL, xfstyle, 0) + 1;
1091     *fstyle = weed_calloc(wcs_len, 1);
1092     wcstombs(*fstyle, xfstyle, wcs_len);
1093   }
1094 
1095   if (*xfamily != L'\0') weed_free(xfamily);
1096   if (xfontstr) weed_free(xfontstr);
1097 }
1098 #endif
1099 
1100 #endif // __HAVE_WEED_PLUGIN_UTILS__
1101