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