1 // mask_overlay.c
2 // weed plugin - resize using gdk
3 // (c) G. Finch (salsaman) 2011
4 //
5 // released under the GNU GPL 3 or later
6 // see file COPYING or www.gnu.org for details
7 
8 ///////////////////////////////////////////////////////////////////
9 
10 static int package_version = 1; // version of this package
11 
12 //////////////////////////////////////////////////////////////////
13 
14 #define NEED_PALETTE_UTILS
15 
16 #ifndef NEED_LOCAL_WEED_PLUGIN
17 #include <weed/weed-plugin.h>
18 #include <weed/weed-utils.h> // optional
19 #include <weed/weed-plugin-utils.h> // optional
20 #else
21 #include "../../../libweed/weed-plugin.h"
22 #include "../../../libweed/weed-utils.h"
23 #include "../../../libweed/weed-plugin-utils.h" // optional
24 #endif
25 
26 #include "../weed-plugin-utils.c" // optional
27 
28 /////////////////////////////////////////////////////////////
29 
30 typedef struct _sdata {
31   int *xmap; // x offset in src0 to map to point (or -1 to map src1)
32   int *ymap; // x offset in src0 to map to point (or -1 to map src1)
33 } sdata;
34 
35 #include <gdk/gdk.h>
36 
37 
make_mask(GdkPixbuf * pbuf,int mode,int owidth,int oheight,int * xmap,int * ymap)38 static void make_mask(GdkPixbuf *pbuf, int mode, int owidth, int oheight, int *xmap, int *ymap) {
39   int iwidth = gdk_pixbuf_get_width(pbuf);
40   int iheight = gdk_pixbuf_get_height(pbuf);
41   int stride = gdk_pixbuf_get_rowstride(pbuf);
42   guchar *pdata = gdk_pixbuf_get_pixels(pbuf);
43   gboolean has_alpha = gdk_pixbuf_get_has_alpha(pbuf);
44 
45   double xscale = (double)iwidth / (double)owidth;
46   double yscale = (double)iheight / (double)oheight;
47 
48   double xscale2 = xscale, yscale2 = yscale;
49 
50   int psize = 3;
51 
52   int top = -1, bot = -1, left = -1, right = -1, tline = 0, xwidth = 0;
53   double xpos = 0., ypos = 0.;
54 
55   register int i, j;
56 
57   if (has_alpha) psize = 4;
58 
59   if (mode == 1) {
60     // get bounds
61 
62     for (i = 0; i < oheight; i++) {
63       for (j = 0; j < owidth; j++) {
64         if (*(pdata + (int)(i * yscale) * stride + (int)(j * xscale) * psize + 1) == 0) {
65           if (top == -1) top = i;
66           if (j < left || left == -1) left = j;
67           if (j > right) right = j;
68           if (i > bot) bot = i;
69         }
70       }
71     }
72 
73     // get width (ignoring non-black)
74 
75     tline = (top + bot) >> 1;
76 
77     for (j = 0; j < owidth; j++) {
78       if (*(pdata + (int)(tline * yscale) * stride + (int)(j * xscale) * psize + 1) == 0) xwidth++;
79     }
80 
81     yscale2 = (double)oheight / (double)(bot - top);
82     xscale2 = (double)owidth / (double)xwidth;
83 
84     // map center row as template for other rows
85     for (j = 0; j < owidth; j++) {
86       if (*(pdata + (int)(tline * yscale) * stride + (int)(j * xscale) * psize + 1) == 0) {
87         // map front frame
88         xmap[tline * owidth + j] = (int)xpos;
89         xpos += xscale2;
90       } else {
91         xmap[tline * owidth + j] = -1;
92       }
93     }
94   }
95 
96   for (i = 0; i < oheight; i++) {
97     for (j = 0; j < owidth; j++) {
98       if (*(pdata + (int)(i * yscale) * stride + (int)(j * xscale) * psize + 1) == 0) {
99         // map front frame
100 
101         if (mode == 0) {
102           // no re-mapping of front frame
103           xmap[i * owidth + j] = j;
104           ymap[i * owidth + j] = i;
105         } else {
106           xmap[i * owidth + j] = xmap[tline * owidth + j];
107           ymap[i * owidth + j] = (int)ypos;
108         }
109 
110       } else {
111         // map back frame
112         xmap[i * owidth + j] = ymap[i * owidth + j] = -1;
113       }
114     }
115     if (i >= top) ypos += yscale2;
116   }
117 }
118 
119 
masko_init(weed_plant_t * inst)120 static weed_error_t masko_init(weed_plant_t *inst) {
121   struct _sdata *sdata;
122   int video_height, video_width, video_area;
123   int mode;
124   weed_plant_t *in_channel, **in_params;
125   GdkPixbuf *pbuf;
126   GError *gerr = NULL;
127   char *mfile;
128 
129   in_channel = weed_get_plantptr_value(inst, WEED_LEAF_IN_CHANNELS, NULL);
130 
131   sdata = weed_malloc(sizeof(struct _sdata));
132 
133   if (sdata == NULL) return WEED_ERROR_MEMORY_ALLOCATION;
134 
135   video_height = weed_get_int_value(in_channel, WEED_LEAF_HEIGHT, NULL);
136   video_width = weed_get_int_value(in_channel, WEED_LEAF_WIDTH, NULL);
137   video_area = video_width * video_height;
138 
139   sdata->xmap = (int *)weed_malloc(video_area * sizeof(int));
140 
141   if (sdata->xmap == NULL) {
142     weed_free(sdata);
143     return WEED_ERROR_MEMORY_ALLOCATION;
144   }
145 
146   sdata->ymap = (int *)weed_malloc(video_area * sizeof(int));
147 
148   if (sdata->ymap == NULL) {
149     weed_free(sdata->xmap);
150     weed_free(sdata);
151     return WEED_ERROR_MEMORY_ALLOCATION;
152   }
153 
154   // load image, then get luma values and scale
155   in_params = weed_get_plantptr_array(inst, WEED_LEAF_IN_PARAMETERS, NULL);
156   mfile = weed_get_string_value(in_params[0], WEED_LEAF_VALUE, NULL);
157   mode = weed_get_int_value(in_params[1], WEED_LEAF_VALUE, NULL);
158 
159   pbuf = gdk_pixbuf_new_from_file(mfile, &gerr);
160 
161   if (gerr != NULL) {
162     weed_free(sdata->xmap);
163     weed_free(sdata->ymap);
164     //g_object_unref(gerr);
165     sdata->xmap = sdata->ymap = NULL;
166   } else {
167     make_mask(pbuf, mode, video_width, video_height, sdata->xmap, sdata->ymap);
168     g_object_unref(pbuf);
169   }
170 
171   weed_free(mfile);
172   weed_free(in_params);
173 
174   weed_set_voidptr_value(inst, "plugin_internal", sdata);
175 
176   return WEED_SUCCESS;
177 }
178 
179 
masko_deinit(weed_plant_t * inst)180 static weed_error_t masko_deinit(weed_plant_t *inst) {
181   struct _sdata *sdata = weed_get_voidptr_value(inst, "plugin_internal", NULL);
182   if (sdata) {
183     if (sdata->xmap != NULL) weed_free(sdata->xmap);
184     if (sdata->ymap != NULL) weed_free(sdata->ymap);
185     weed_free(sdata);
186   }
187   weed_set_voidptr_value(inst, "plugin_internal", NULL);
188   return WEED_SUCCESS;
189 }
190 
191 
masko_process(weed_plant_t * inst,weed_timecode_t timestamp)192 static weed_error_t masko_process(weed_plant_t *inst, weed_timecode_t timestamp) {
193   weed_plant_t **in_channels = weed_get_plantptr_array(inst, WEED_LEAF_IN_CHANNELS, NULL),
194                  *out_channel = weed_get_plantptr_value(inst, WEED_LEAF_OUT_CHANNELS, NULL);
195 
196   int palette = weed_get_int_value(out_channel, WEED_LEAF_CURRENT_PALETTE, NULL);
197   int width = weed_get_int_value(out_channel, WEED_LEAF_WIDTH, NULL);
198   int height = weed_get_int_value(out_channel, WEED_LEAF_HEIGHT, NULL);
199   int offset = 0;
200 
201   register int i, j, pos;
202   struct _sdata *sdata;
203 
204   int psize = 3;
205 
206   unsigned char *dst, *src0, *src1;
207   int orow, irow0, irow1;
208 
209   if (palette == WEED_PALETTE_RGBA32 || palette == WEED_PALETTE_BGRA32 || palette == WEED_PALETTE_ARGB32 ||
210       palette == WEED_PALETTE_YUVA8888) psize = 4;
211 
212   sdata = weed_get_voidptr_value(inst, "plugin_internal", NULL);
213 
214   if (sdata->xmap == NULL || sdata->ymap == NULL) return WEED_SUCCESS;
215 
216   dst = weed_get_voidptr_value(out_channel, WEED_LEAF_PIXEL_DATA, NULL);
217   src0 = weed_get_voidptr_value(in_channels[0], WEED_LEAF_PIXEL_DATA, NULL);
218   src1 = weed_get_voidptr_value(in_channels[1], WEED_LEAF_PIXEL_DATA, NULL);
219 
220   orow = weed_get_int_value(out_channel, WEED_LEAF_ROWSTRIDES, NULL);
221   irow0 = weed_get_int_value(in_channels[0], WEED_LEAF_ROWSTRIDES, NULL);
222   irow1 = weed_get_int_value(in_channels[1], WEED_LEAF_ROWSTRIDES, NULL);
223 
224   // new threading arch
225   if (weed_plant_has_leaf(out_channel, WEED_LEAF_OFFSET)) {
226     offset = weed_get_int_value(out_channel, WEED_LEAF_OFFSET, NULL);
227     int dheight = weed_get_int_value(out_channel, WEED_LEAF_HEIGHT, NULL);
228     height = offset + dheight;
229     dst += offset * orow;
230     src1 += offset * irow1;
231   }
232 
233   pos = offset * width;
234   orow -= width * psize;
235   irow1 -= width * psize;
236 
237   for (i = offset; i < height; i++) {
238     for (j = 0; j < width; j++) {
239       if (sdata->xmap[pos] == -1 || sdata->ymap[pos] == -1) {
240         // map bg pixel to dst
241         weed_memcpy(dst, src1, psize);
242       } else {
243         // remap fg pixel
244         weed_memcpy(dst, src0 + sdata->ymap[pos]*irow0 + sdata->xmap[pos]*psize, psize);
245       }
246       dst += psize;
247       src1 += psize;
248       pos++;
249     }
250     dst += orow;
251     src1 += irow1;
252   }
253 
254   weed_free(in_channels);
255 
256   return WEED_SUCCESS;
257 }
258 
259 
260 WEED_SETUP_START(200, 200) {
261   int palette_list[] = ALL_PACKED_PALETTES;
262 
263   weed_plant_t *in_chantmpls[] = {weed_channel_template_init("in channel 0", 0),
264                                   weed_channel_template_init("in channel 1", 0), NULL
265                                  };
266 
267   weed_plant_t *out_chantmpls[] = {weed_channel_template_init("out channel 0", WEED_CHANNEL_REINIT_ON_SIZE_CHANGE), NULL};
268   weed_plant_t *filter_class;
269   weed_plant_t *in_params[3], *gui;
270   char *rfx_strings[] = {"special|fileread|0|"};
271   const char *modes[] = {"normal", "stretch", NULL};
272 
273   char *defmaskfile = g_build_filename(g_get_home_dir(), "mask.png", NULL);
274   int flags;
275 
276   in_params[0] = weed_text_init("maskfile", "_Mask file (.png or .jpg)", defmaskfile);
277   gui = weed_paramtmpl_get_gui(in_params[0]);
278   weed_set_int_value(gui, WEED_LEAF_MAXCHARS, 80); // for display only - fileread will override this
279   flags = 0;
280   if (weed_plant_has_leaf(in_params[0], WEED_LEAF_FLAGS))
281     flags = weed_get_int_value(in_params[0], WEED_LEAF_FLAGS, NULL);
282   flags |= WEED_PARAMETER_REINIT_ON_VALUE_CHANGE;
283   weed_set_int_value(in_params[0], WEED_LEAF_FLAGS, flags);
284 
285   in_params[1] = weed_string_list_init("mode", "Effect _mode", 0, modes);
286   flags = 0;
287   if (weed_plant_has_leaf(in_params[1], WEED_LEAF_FLAGS))
288     flags = weed_get_int_value(in_params[1], WEED_LEAF_FLAGS, NULL);
289   flags |= WEED_PARAMETER_REINIT_ON_VALUE_CHANGE;
290   weed_set_int_value(in_params[1], WEED_LEAF_FLAGS, flags);
291   in_params[2] = NULL;
292 
293   weed_free(defmaskfile);
294 
295   filter_class = weed_filter_class_init("mask_overlay", "salsaman", 1, WEED_FILTER_HINT_MAY_THREAD, palette_list,
296                                         masko_init, masko_process, masko_deinit, in_chantmpls, out_chantmpls, in_params, NULL);
297 
298   gui = weed_filter_get_gui(filter_class);
299   weed_set_string_value(gui, WEED_LEAF_LAYOUT_SCHEME, "RFX");
300   weed_set_string_value(gui, "layout_rfx_delim", "|");
301   weed_set_string_array(gui, "layout_rfx_strings", 1, rfx_strings);
302 
303   weed_plugin_info_add_filter_class(plugin_info, filter_class);
304   weed_set_int_value(plugin_info, WEED_LEAF_VERSION, package_version);
305 }
306 WEED_SETUP_END;
307 
308 
309