1 /* Implementation of some masking functions for the software engine */
2
3 #include "evas_engine_filter.h"
4
5 // Naming convention: _func_engine_incolor_maskcolor_outcolor()
6 static Eina_Bool _mask_cpu_alpha_alpha_alpha(Evas_Filter_Command *cmd);
7 static Eina_Bool _mask_cpu_alpha_rgba_rgba(Evas_Filter_Command *cmd);
8 static Eina_Bool _mask_cpu_alpha_alpha_rgba(Evas_Filter_Command *cmd);
9 static Eina_Bool _mask_cpu_rgba_alpha_rgba(Evas_Filter_Command *cmd);
10 static Eina_Bool _mask_cpu_rgba_rgba_rgba(Evas_Filter_Command *cmd);
11
12 Software_Filter_Func
eng_filter_mask_func_get(Evas_Filter_Command * cmd)13 eng_filter_mask_func_get(Evas_Filter_Command *cmd)
14 {
15 EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, NULL);
16 EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input, NULL);
17 EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output, NULL);
18 EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->mask, NULL);
19 EINA_SAFETY_ON_FALSE_RETURN_VAL((cmd->input->w > 0) && (cmd->input->h > 0), NULL);
20 EINA_SAFETY_ON_FALSE_RETURN_VAL((cmd->mask->w > 0) && (cmd->mask->h > 0), NULL);
21 EINA_SAFETY_ON_FALSE_RETURN_VAL(cmd->input->w == cmd->output->w, NULL);
22 EINA_SAFETY_ON_FALSE_RETURN_VAL(cmd->input->h == cmd->output->h, NULL);
23
24 if (cmd->input->alpha_only)
25 {
26 if (cmd->output->alpha_only)
27 {
28 if (!cmd->mask->alpha_only)
29 {
30 DBG("Input and output are Alpha but mask is RGBA. This is not "
31 "optimal (implicit conversion and loss of color).");
32 }
33 return _mask_cpu_alpha_alpha_alpha;
34 }
35 else if (!cmd->mask->alpha_only)
36 return _mask_cpu_alpha_rgba_rgba;
37 else
38 return _mask_cpu_alpha_alpha_rgba;
39 }
40 else
41 {
42 if (!cmd->output->alpha_only)
43 {
44 // rgba -> rgba
45 if (cmd->mask->alpha_only)
46 return _mask_cpu_rgba_alpha_rgba;
47 else
48 return _mask_cpu_rgba_rgba_rgba;
49 }
50 else
51 {
52 // rgba -> alpha
53 DBG("Input is RGBA but output is Alpha, losing colors.");
54 return _mask_cpu_alpha_alpha_alpha;
55 }
56 }
57 }
58
59 static Eina_Bool
_mask_cpu_alpha_alpha_alpha(Evas_Filter_Command * cmd)60 _mask_cpu_alpha_alpha_alpha(Evas_Filter_Command *cmd)
61 {
62 unsigned int src_len = 0, src_stride, msk_len = 0, msk_stride, dst_len = 0, dst_stride;
63 Efl_Gfx_Render_Op render_op = cmd->draw.rop;
64 Evas_Filter_Buffer *msk_fb;
65 Draw_Func_Alpha func;
66 uint8_t *src_map = NULL, *dst, *dst_map = NULL, *msk, *msk_map = NULL;
67 int w, h, mw, mh, x, y, my;
68 int stepsize, stepcount, step;
69 Eina_Bool ret = EINA_FALSE;
70
71 /* Mechanism:
72 * 1. Stretch mask as requested in fillmode
73 * 2. Copy source to destination
74 * 3. Render mask into destination using alpha function
75 *
76 * FIXME: Could probably be optimized into a single op :)
77 */
78
79 w = cmd->input->w;
80 h = cmd->input->h;
81 mw = cmd->mask->w;
82 mh = cmd->mask->h;
83 stepsize = MIN(mw, w);
84 stepcount = w / stepsize;
85
86 // Stretch if necessary.
87 if ((mw != w || mh != h) && (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_XY))
88 {
89 EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->ctx->buffer_scaled_get, EINA_FALSE);
90
91 if (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_X)
92 mw = w;
93 if (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_Y)
94 mh = h;
95
96 BUFFERS_LOCK();
97 msk_fb = cmd->ctx->buffer_scaled_get(cmd->ctx, cmd->mask, mw, mh);
98 BUFFERS_UNLOCK();
99
100 EINA_SAFETY_ON_NULL_RETURN_VAL(msk_fb, EINA_FALSE);
101 msk_fb->locked = EINA_FALSE;
102 }
103 else msk_fb = cmd->mask;
104
105 msk_map = msk = _buffer_map_all(msk_fb->buffer, &msk_len, E_READ, E_ALPHA, &msk_stride);
106 dst_map = dst = _buffer_map_all(cmd->output->buffer, &dst_len, E_WRITE, E_ALPHA, &dst_stride);
107 EINA_SAFETY_ON_FALSE_GOTO(dst_map && msk_map, end);
108
109 // First pass: copy to dest
110 if (cmd->input->buffer != cmd->output->buffer)
111 {
112 src_map = _buffer_map_all(cmd->input->buffer, &src_len, E_READ, E_ALPHA, &src_stride);
113 EINA_SAFETY_ON_FALSE_GOTO(src_map, end);
114 if (dst_stride == src_stride)
115 memcpy(dst_map, src_map, dst_stride * h * sizeof(uint8_t));
116 else
117 {
118 for (y = 0; y < h; y++)
119 memcpy(dst_map + (y * dst_stride), src_map + (y * src_stride),
120 MIN(dst_stride, src_stride) * h * sizeof(uint8_t));
121 }
122 }
123
124 // Second pass: apply render op
125 func = efl_draw_alpha_func_get(render_op, EINA_FALSE);
126 for (y = 0, my = 0; y < h; y++, my++)
127 {
128 if (my >= mh) my = 0;
129
130 msk = msk_map + (my * msk_stride);
131 dst = dst_map + (y * dst_stride);
132
133 for (step = 0; step < stepcount; step++, dst += stepsize)
134 func(dst, msk, stepsize);
135
136 x = stepsize * stepcount;
137 if (x < w)
138 {
139 func(dst, msk, w - x);
140 }
141 }
142
143 ret = EINA_TRUE;
144
145 end:
146 ector_buffer_unmap(cmd->input->buffer, src_map, src_len);
147 ector_buffer_unmap(msk_fb->buffer, msk_map, msk_len);
148 ector_buffer_unmap(cmd->output->buffer, dst_map, dst_len);
149 return ret;
150 }
151
152 static Eina_Bool
_mask_cpu_rgba_alpha_rgba(Evas_Filter_Command * cmd)153 _mask_cpu_rgba_alpha_rgba(Evas_Filter_Command *cmd)
154 {
155 Evas_Filter_Buffer *fb;
156 Eina_Bool ok;
157
158 /* Mechanism:
159 * 1. Swap input and mask
160 * 2. Apply mask operation for alpha+rgba+rgba
161 * 3. Swap input and mask
162 */
163
164 fb = cmd->input;
165 cmd->input = cmd->mask;
166 cmd->mask = fb;
167
168 ok = _mask_cpu_alpha_rgba_rgba(cmd);
169
170 fb = cmd->input;
171 cmd->input = cmd->mask;
172 cmd->mask = fb;
173
174 return ok;
175 }
176
177 static Eina_Bool
_mask_cpu_alpha_rgba_rgba(Evas_Filter_Command * cmd)178 _mask_cpu_alpha_rgba_rgba(Evas_Filter_Command *cmd)
179 {
180 unsigned int src_len, src_stride, msk_len, msk_stride, dst_len, dst_stride;
181 Efl_Gfx_Render_Op op = cmd->draw.rop;
182 Evas_Filter_Buffer *msk_fb;
183 RGBA_Comp_Func_Mask func1;
184 Draw_Func_ARGB_Mix3 func2;
185 uint8_t *src, *src_map = NULL, *msk_map = NULL, *dst_map = NULL;
186 uint32_t *dst, *msk, *span;
187 int w, h, mw, mh, x, y, my;
188 int stepsize, stepcount, step;
189 Eina_Bool ret = EINA_FALSE;
190 uint32_t color;
191
192 /* Mechanism:
193 * 1. Stretch mask as requested in fillmode
194 * 2. Render input to span using (using "mask" function)
195 * 3. Mix 3 RGBA images together into dest: span, mask, dest
196 *
197 * FIXME: Could probably be optimized into a single op :)
198 */
199
200 w = cmd->input->w;
201 h = cmd->input->h;
202 mw = cmd->mask->w;
203 mh = cmd->mask->h;
204 color = ARGB_JOIN(cmd->draw.A, cmd->draw.R, cmd->draw.G, cmd->draw.B);
205 stepsize = MIN(mw, w);
206 stepcount = w / stepsize;
207 span = alloca(stepsize * sizeof(uint32_t));
208
209 // Stretch if necessary.
210 if ((mw != w || mh != h) && (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_XY))
211 {
212 EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->ctx->buffer_scaled_get, EINA_FALSE);
213
214 if (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_X)
215 mw = w;
216 if (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_Y)
217 mh = h;
218
219 BUFFERS_LOCK();
220 msk_fb = cmd->ctx->buffer_scaled_get(cmd->ctx, cmd->mask, mw, mh);
221 BUFFERS_UNLOCK();
222
223 EINA_SAFETY_ON_NULL_RETURN_VAL(msk_fb, EINA_FALSE);
224 msk_fb->locked = EINA_FALSE;
225 }
226 else msk_fb = cmd->mask;
227
228 src_map = _buffer_map_all(cmd->input->buffer, &src_len, E_READ, E_ALPHA, &src_stride);
229 msk_map = _buffer_map_all(msk_fb->buffer, &msk_len, E_READ, E_ARGB, &msk_stride);
230 dst_map = _buffer_map_all(cmd->output->buffer, &dst_len, E_WRITE, E_ARGB, &dst_stride);
231 EINA_SAFETY_ON_FALSE_GOTO(src_map && dst_map && msk_map, end);
232
233 func1 = efl_draw_func_mask_span_get(op, 0xFFFFFFFF);
234 func2 = efl_draw_func_argb_mix3_get(op, color);
235 EINA_SAFETY_ON_FALSE_GOTO(func1 && func2, end);
236
237 // Apply mask using Gfx functions
238 for (y = 0, my = 0; y < h; y++, my++)
239 {
240 int len;
241
242 if (my >= mh) my = 0;
243
244 len = stepsize;
245 src = src_map + (y * src_stride);
246 msk = (uint32_t *) (msk_map + (my * msk_stride));
247 dst = (uint32_t *) (dst_map + (y * dst_stride));
248
249 for (step = 0; step < stepcount; step++, dst += stepsize, src += stepsize)
250 {
251 memset(span, 0, len * sizeof(uint32_t));
252 func1(span, src, len, 0xFFFFFFFF);
253 func2(dst, span, msk, len, color);
254 }
255
256 x = stepsize * stepcount;
257 if (x < w)
258 {
259 len = w - x;
260 memset(span, 0, len * sizeof(uint32_t));
261 func1(span, src, len, 0xFFFFFFFF);
262 func2(dst, span, msk, len, color);
263 }
264 }
265
266 ret = EINA_TRUE;
267
268 end:
269 ector_buffer_unmap(cmd->input->buffer, src_map, src_len);
270 ector_buffer_unmap(msk_fb->buffer, msk_map, msk_len);
271 ector_buffer_unmap(cmd->output->buffer, dst_map, dst_len);
272 return ret;
273 }
274
275 static Eina_Bool
_mask_cpu_alpha_alpha_rgba(Evas_Filter_Command * cmd)276 _mask_cpu_alpha_alpha_rgba(Evas_Filter_Command *cmd)
277 {
278 unsigned int src_len, src_stride, msk_len, msk_stride, dst_len, dst_stride;
279 uint8_t *src, *msk, *span, *src_map = NULL, *msk_map = NULL, *dst_map = NULL;
280 Evas_Filter_Buffer *msk_fb;
281 Draw_Func_Alpha func1;
282 RGBA_Comp_Func_Mask func2;
283 uint32_t *dst;
284 uint32_t color;
285 Efl_Gfx_Render_Op op = cmd->draw.rop;
286 int w, h, mw, mh, x, y, my;
287 int stepsize, stepcount, step;
288 Eina_Bool ret = EINA_FALSE;
289
290 /* Mechanism:
291 * 1. Copy mask to span buffer (1 line)
292 * 2. Multiply source by span (so that: span = mask * source)
293 * 3. Render span to destination using color (blend)
294 *
295 * FIXME: Could probably be optimized into a single op :)
296 */
297
298 w = cmd->input->w;
299 h = cmd->input->h;
300 mw = cmd->mask->w;
301 mh = cmd->mask->h;
302 color = ARGB_JOIN(cmd->draw.A, cmd->draw.R, cmd->draw.G, cmd->draw.B);
303 stepsize = MIN(mw, w);
304 stepcount = w / stepsize;
305 span = alloca(stepsize * sizeof(uint32_t));
306
307 // Stretch if necessary.
308 if ((mw != w || mh != h) && (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_XY))
309 {
310 EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->ctx->buffer_scaled_get, EINA_FALSE);
311
312 if (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_X)
313 mw = w;
314 if (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_Y)
315 mh = h;
316
317 BUFFERS_LOCK();
318 msk_fb = cmd->ctx->buffer_scaled_get(cmd->ctx, cmd->mask, mw, mh);
319 BUFFERS_UNLOCK();
320
321 EINA_SAFETY_ON_NULL_RETURN_VAL(msk_fb, EINA_FALSE);
322 msk_fb->locked = EINA_FALSE;
323 }
324 else msk_fb = cmd->mask;
325
326 src_map = _buffer_map_all(cmd->input->buffer, &src_len, E_READ, E_ALPHA, &src_stride);
327 msk_map = _buffer_map_all(msk_fb->buffer, &msk_len, E_READ, E_ALPHA, &msk_stride);
328 dst_map = _buffer_map_all(cmd->output->buffer, &dst_len, E_WRITE, E_ARGB, &dst_stride);
329 EINA_SAFETY_ON_FALSE_GOTO(src_map && dst_map && msk_map, end);
330
331 func1 = efl_draw_alpha_func_get(op, EINA_TRUE);
332 func2 = efl_draw_func_mask_span_get(op, color);
333 EINA_SAFETY_ON_FALSE_GOTO(func1 && func2, end);
334
335 for (y = 0, my = 0; y < h; y++, my++)
336 {
337 int len;
338
339 if (my >= mh) my = 0;
340
341 len = stepsize;
342 src = src_map + (y * src_stride);
343 msk = msk_map + (my * msk_stride);
344 dst = (uint32_t *) (dst_map + (y * dst_stride));
345
346 for (step = 0; step < stepcount; step++, dst += stepsize, src += stepsize)
347 {
348 memcpy(span, msk, len * sizeof(uint8_t));
349 func1(span, src, len);
350 func2(dst, span, len, color);
351 }
352
353 x = stepsize * stepcount;
354 if (x < w)
355 {
356 len = w - x;
357 memcpy(span, msk, len * sizeof(uint8_t));
358 func1(span, src, len);
359 func2(dst, span, len, color);
360 }
361 }
362
363 ret = EINA_TRUE;
364
365 end:
366 ector_buffer_unmap(cmd->input->buffer, src_map, src_len);
367 ector_buffer_unmap(msk_fb->buffer, msk_map, msk_len);
368 ector_buffer_unmap(cmd->output->buffer, dst_map, dst_len);
369 return ret;
370 }
371
372 static Eina_Bool
_mask_cpu_rgba_rgba_rgba(Evas_Filter_Command * cmd)373 _mask_cpu_rgba_rgba_rgba(Evas_Filter_Command *cmd)
374 {
375 unsigned int src_len, src_stride, msk_len, msk_stride, dst_len, dst_stride;
376 uint8_t *src_map = NULL, *msk_map = NULL, *dst_map = NULL;
377 Draw_Func_ARGB_Mix3 func;
378 Evas_Filter_Buffer *msk_fb;
379 uint32_t *dst, *msk, *src;
380 int w, h, mw, mh, x, y, my;
381 int stepsize, stepcount, step;
382 Eina_Bool ret = EINA_FALSE;
383 uint32_t color;
384
385 /* Mechanism:
386 * 1. Stretch mask as requested in fillmode
387 * 2. Mix 3 colors
388 */
389
390 w = cmd->input->w;
391 h = cmd->input->h;
392 mw = cmd->mask->w;
393 mh = cmd->mask->h;
394 color = ARGB_JOIN(cmd->draw.A, cmd->draw.R, cmd->draw.G, cmd->draw.B);
395 stepsize = MIN(mw, w);
396 stepcount = w / stepsize;
397
398 // Stretch if necessary.
399 if ((mw != w || mh != h) && (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_XY))
400 {
401 EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->ctx->buffer_scaled_get, EINA_FALSE);
402
403 if (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_X)
404 mw = w;
405 if (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_Y)
406 mh = h;
407
408 BUFFERS_LOCK();
409 msk_fb = cmd->ctx->buffer_scaled_get(cmd->ctx, cmd->mask, mw, mh);
410 BUFFERS_UNLOCK();
411
412 EINA_SAFETY_ON_NULL_RETURN_VAL(msk_fb, EINA_FALSE);
413 msk_fb->locked = EINA_FALSE;
414 }
415 else msk_fb = cmd->mask;
416
417 src_map = _buffer_map_all(cmd->input->buffer, &src_len, E_READ, E_ARGB, &src_stride);
418 msk_map = _buffer_map_all(msk_fb->buffer, &msk_len, E_READ, E_ARGB, &msk_stride);
419 dst_map = _buffer_map_all(cmd->output->buffer, &dst_len, E_WRITE, E_ARGB, &dst_stride);
420 EINA_SAFETY_ON_FALSE_GOTO(src_map && dst_map && msk_map, end);
421
422 func = efl_draw_func_argb_mix3_get(cmd->draw.rop, color);
423
424 // Apply mask using Gfx functions
425 for (y = 0, my = 0; y < h; y++, my++)
426 {
427 if (my >= mh) my = 0;
428
429 src = (uint32_t *) (src_map + (y * src_stride));
430 msk = (uint32_t *) (msk_map + (my * msk_stride));
431 dst = (uint32_t *) (dst_map + (y * dst_stride));
432
433 for (step = 0; step < stepcount; step++, dst += stepsize, src += stepsize)
434 func(dst, src, msk, stepsize, color);
435
436 x = stepsize * stepcount;
437 if (x < w)
438 {
439 func(dst, src, msk, w - x, color);
440 }
441 }
442
443 ret = EINA_TRUE;
444
445 end:
446 ector_buffer_unmap(cmd->input->buffer, src_map, src_len);
447 ector_buffer_unmap(msk_fb->buffer, msk_map, msk_len);
448 ector_buffer_unmap(cmd->output->buffer, dst_map, dst_len);
449 return ret;
450 }
451