summaryrefslogblamecommitdiff
path: root/src/lib/evas/filters/evas_filter_blend.c
blob: 4c90a924669b24c247ae5e47fd5c2ee4e248b7c6 (plain) (tree)
1
2
3
4
5
6
7
8
9



                                


                                                                              
                                                                                                                                                                                                                     
                                                                                                                                                                                                           
 
                                




                 
                





                                                                       
 
                                                  

                             

                                     
                       
                 
 



























                                                                                     
                                                  

                             
                                     






                                                                                     
                                                                 
                                                    
 





                           
      


                                                                       
      
 








                                                        
                                       
 

                              

                                                   

                                                                


          

                          


                     

                           
 




                                                        


                                                                            


                                                                                         
      













                                                                         

      

                                                                            

                                                                    
 
 










                                                                             
                                     
                    









                                








                                                                                     
      


                                         
                                                                           
                      






                      
















                                                                                     
                                                                                                                                                                           

















                                                                       

















                                                                         



                                                
                                      
                 
 



                                                   

                                                               
 


                                                                         




                          



                           
 


                                                        



                                                                             

                          



                                                                        
                                                              

                                                                             
       
      

                                                                            
      
 


                                                                       

                                               
              

 








                                             


                                                


                                                                       

                                              
      
                                                                                  
                                                                              

                                                                      





                                             

      
                                                 
      


                                               



                                                

               
                                                       
      
                 



               
                                           
                 
                 

      
                                                 
      


                                              



                                                

               
                                                       
      
                 
               


       
                                           
                 
                 










                                       
                           


                              
                           
           
                                           
           
                           


                                         
                           


            
                       











                                                            
           







                                            
                                


                                    
                                
                
                                               
                
                                


                                               
                                


                 
                            











                                                                 
                


                                                              
                                          

                                                      
                                 



                                                   
           
      
              

 






                                                        
                                                                             
      






                                                  
            





                                                   


       


                                                        

      
#include "evas_filter.h"
#include "evas_filter_private.h"
#include "evas_blend_private.h"

// Use a better formula than R+G+B for rgba to alpha conversion (RGB to YCbCr)
#define RGBA2ALPHA_WEIGHTED 1

typedef Eina_Bool (*image_draw_func) (void *data, void *context, void *surface, void *image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int dst_w, int dst_h, int smooth, Eina_Bool do_async);
static Eina_Bool _mapped_blend(void *data, void *drawctx, void *in, void *out, Evas_Filter_Fill_Mode fillmode, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, image_draw_func image_draw);

struct Filter_Blend_Draw_Context
{
   int render_op;
   DATA32 color;
};

static Eina_Bool
_image_draw_cpu_alpha2alpha(void *data EINA_UNUSED, void *context,
                            void *surface, void *image,
                            int src_x, int src_y, int src_w, int src_h,
                            int dst_x, int dst_y, int dst_w, int dst_h,
                            int smooth EINA_UNUSED,
                            Eina_Bool do_async EINA_UNUSED)
{
   struct Filter_Blend_Draw_Context *dc = context;
   RGBA_Image *src = image;
   RGBA_Image *dst = surface;
   DATA8* srcdata = src->image.data8;
   DATA8* dstdata = dst->image.data8;
   Alpha_Gfx_Func func;
   int y, sw, dw;

   EINA_SAFETY_ON_FALSE_RETURN_VAL((src_w == dst_w) && (src_h == dst_h), EINA_FALSE);

   func = evas_common_alpha_func_get(dc->render_op);
   EINA_SAFETY_ON_NULL_RETURN_VAL(func, EINA_FALSE);

   sw = src->cache_entry.w;
   dw = dst->cache_entry.w;

   srcdata += src_y * sw;
   dstdata += dst_y * dw;
   for (y = src_h; y; y--)
     {
        func(srcdata + src_x, dstdata + dst_x, src_w);
        srcdata += sw;
        dstdata += dw;
     }

   return EINA_TRUE;
}

static Eina_Bool
_image_draw_cpu_alpha2rgba(void *data EINA_UNUSED, void *context,
                           void *surface, void *image,
                           int src_x, int src_y, int src_w, int src_h,
                           int dst_x, int dst_y, int dst_w, int dst_h,
                           int smooth EINA_UNUSED,
                           Eina_Bool do_async EINA_UNUSED)
{
   struct Filter_Blend_Draw_Context *dc = context;
   RGBA_Image *src = image;
   RGBA_Image *dst = surface;
   DATA8* srcdata = src->image.data8;
   DATA32* dstdata = dst->image.data;
   RGBA_Gfx_Func func;
   int y, sw, dw;

   EINA_SAFETY_ON_FALSE_RETURN_VAL((src_w == dst_w) && (src_h == dst_h), EINA_FALSE);

   func = evas_common_gfx_func_composite_mask_color_span_get
     (dc->color, dst->cache_entry.flags.alpha, 1, dc->render_op);
   EINA_SAFETY_ON_NULL_RETURN_VAL(func, EINA_FALSE);

   sw = src->cache_entry.w;
   dw = dst->cache_entry.w;

   srcdata += src_y * sw;
   dstdata += dst_y * dw;
   for (y = src_h; y; y--)
     {
        func(NULL, srcdata + src_x, dc->color, dstdata + dst_x, src_w);
        srcdata += sw;
        dstdata += dw;
     }

   return EINA_TRUE;
}

static Eina_Bool
_filter_blend_cpu_generic_do(Evas_Filter_Command *cmd,
                             image_draw_func image_draw)
{
   RGBA_Image *in, *out;
   int sw, sh, dx, dy, dw, dh, sx, sy;
   struct Filter_Blend_Draw_Context dc;

   in = cmd->input->backing;
   out = cmd->output->backing;
   EINA_SAFETY_ON_NULL_RETURN_VAL(in, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(out, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(in->image.data8, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(out->image.data8, EINA_FALSE);

   sx = 0;
   sy = 0;
   sw = in->cache_entry.w;
   sh = in->cache_entry.h;

   dx = cmd->draw.ox;
   dy = cmd->draw.oy;
   dw = out->cache_entry.w;
   dh = out->cache_entry.h;

   if ((dw <= 0) || (dh <= 0) || (sw <= 0) || (sh <= 0))
     return EINA_TRUE;

   // Stretch if necessary.

   /* NOTE: As of 2014/03/11, this will happen only with RGBA buffers, since
    * only proxy sources may be scaled. So, we don't need an alpha scaling
    * algorithm just now.
    */

   if ((sw != dw || sh != dh) && (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_XY))
     {
        Evas_Filter_Buffer *fb;

        if (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_X)
          sw = dw;
        if (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_Y)
          sh = dh;

        BUFFERS_LOCK();
        fb = evas_filter_buffer_scaled_get(cmd->ctx, cmd->input, sw, sh);
        BUFFERS_UNLOCK();

        EINA_SAFETY_ON_NULL_RETURN_VAL(fb, EINA_FALSE);
        fb->locked = EINA_FALSE;
        in = fb->backing;
     }

   dc.render_op = cmd->draw.render_op;
   dc.color = ARGB_JOIN(cmd->draw.A, cmd->draw.R, cmd->draw.G, cmd->draw.B);
   return _mapped_blend(cmd->ENDT, &dc, in, out, cmd->draw.fillmode,
                        sx, sy, sw, sh, dx, dy, dw, dh, image_draw);
}

static Eina_Bool
_image_draw_cpu_rgba2alpha(void *data EINA_UNUSED, void *context EINA_UNUSED,
                           void *surface, void *image,
                           int src_x, int src_y, int src_w, int src_h,
                           int dst_x, int dst_y, int dst_w, int dst_h,
                           int smooth EINA_UNUSED,
                           Eina_Bool do_async EINA_UNUSED)
{
   RGBA_Image *src = image;
   RGBA_Image *dst = surface;
   DATA32* srcdata = src->image.data;
   DATA8* dstdata = dst->image.data8;
   int x, y, sw, dw;
#if RGBA2ALPHA_WEIGHTED
   const int WR = 299;
   const int WG = 587;
   const int WB = 114;
#else
   const int WR = 1;
   const int WG = 1;
   const int WB = 1;
#endif
   DEFINE_DIVIDER(WR + WG + WB);

   EINA_SAFETY_ON_FALSE_RETURN_VAL((src_w == dst_w) && (src_h == dst_h), EINA_FALSE);

   sw = src->cache_entry.w;
   dw = dst->cache_entry.w;

   srcdata += src_y * sw;
   dstdata += dst_y * dw;
   for (y = src_h; y; y--)
     {
        DATA32 *s = srcdata + src_x;
        DATA8 *d = dstdata + dst_x;
        for (x = src_w; x; x--, d++, s++)
          *d = DIVIDE((R_VAL(s) * WR) + (G_VAL(s) * WG) + (B_VAL(s) * WB));
        srcdata += sw;
        dstdata += dw;
     }

   return EINA_TRUE;
}

static Eina_Bool
_image_draw_cpu_rgba2rgba(void *data EINA_UNUSED, void *context,
                          void *surface, void *image,
                          int src_x, int src_y, int src_w, int src_h,
                          int dst_x, int dst_y, int dst_w, int dst_h,
                          int smooth EINA_UNUSED,
                          Eina_Bool do_async EINA_UNUSED)
{
   struct Filter_Blend_Draw_Context *dc = context;
   RGBA_Image *src = image;
   RGBA_Image *dst = surface;
   DATA32* srcdata = src->image.data;
   DATA32* dstdata = dst->image.data;
   RGBA_Gfx_Func func;
   int y, sw, dw;

   EINA_SAFETY_ON_FALSE_RETURN_VAL((src_w == dst_w) && (src_h == dst_h), EINA_FALSE);

   func = evas_common_gfx_func_composite_pixel_span_get(src->cache_entry.flags.alpha, src->cache_entry.flags.alpha_sparse, dst->cache_entry.flags.alpha, 1, dc->render_op);
   EINA_SAFETY_ON_NULL_RETURN_VAL(func, EINA_FALSE);

   sw = src->cache_entry.w;
   dw = dst->cache_entry.w;

   srcdata += src_y * sw;
   dstdata += dst_y * dw;
   for (y = src_h; y; y--)
     {
        func(srcdata + src_x, NULL, dc->color, dstdata + dst_x, src_w);
        srcdata += sw;
        dstdata += dw;
     }

   return EINA_TRUE;
}

static Eina_Bool
_filter_blend_cpu_alpha(Evas_Filter_Command *cmd)
{
   return _filter_blend_cpu_generic_do(cmd, _image_draw_cpu_alpha2alpha);
}

static Eina_Bool
_filter_blend_cpu_mask_rgba(Evas_Filter_Command *cmd)
{
   return _filter_blend_cpu_generic_do(cmd, _image_draw_cpu_alpha2rgba);
}

static Eina_Bool
_filter_blend_cpu_rgba2alpha(Evas_Filter_Command *cmd)
{
   return _filter_blend_cpu_generic_do(cmd, _image_draw_cpu_rgba2alpha);
}

static Eina_Bool
_filter_blend_cpu_rgba(Evas_Filter_Command *cmd)
{
   RGBA_Image *in, *out;
   RGBA_Draw_Context *drawctx;
   int sw, sh, dx, dy, dw, dh, sx, sy;
   Eina_Bool ret;

   in = cmd->input->backing;
   out = cmd->output->backing;
   EINA_SAFETY_ON_NULL_RETURN_VAL(in, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(out, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(in->image.data, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(out->image.data, EINA_FALSE);

   if (cmd->ctx->gl_engine)
     return _filter_blend_cpu_generic_do(cmd, _image_draw_cpu_rgba2rgba);

   sx = 0;
   sy = 0;
   sw = in->cache_entry.w;
   sh = in->cache_entry.h;

   dx = cmd->draw.ox;
   dy = cmd->draw.oy;
   dw = out->cache_entry.w;
   dh = out->cache_entry.h;

   if ((dw <= 0) || (dh <= 0) || (sw <= 0) || (sh <= 0))
     return EINA_TRUE;

   drawctx = cmd->ENFN->context_new(cmd->ENDT);
   cmd->ENFN->context_color_set(cmd->ENDT, drawctx, cmd->draw.R, cmd->draw.G,
                                cmd->draw.B, cmd->draw.A);
   cmd->ENFN->context_render_op_set(cmd->ENDT, drawctx, cmd->draw.render_op);

   if (cmd->draw.clip_use)
     {
        cmd->ENFN->context_clip_set(cmd->ENDT, drawctx,
                                    cmd->draw.clip.x, cmd->draw.clip.y,
                                    cmd->draw.clip.w, cmd->draw.clip.h);
        cmd->ENFN->context_clip_clip(cmd->ENDT, drawctx, 0, 0,
                                     out->cache_entry.w, out->cache_entry.h);
     }
   else
     {
        cmd->ENFN->context_clip_set(cmd->ENDT, drawctx, 0, 0,
                                    out->cache_entry.w, out->cache_entry.h);
     }

   ret = _mapped_blend(cmd->ENDT, drawctx, in, out, cmd->draw.fillmode,
                       sx, sy, sw, sh, dx, dy, dw, dh,
                       cmd->ENFN->image_draw);

   cmd->ENFN->context_free(cmd->ENDT, drawctx);
   return ret;
}

static Eina_Bool
_mapped_blend(void *data, void *drawctx,
              void *in, void *out,
              Evas_Filter_Fill_Mode fillmode,
              int sx, int sy,
              int sw, int sh,
              int dx, int dy,
              int dw, int dh,
              image_draw_func image_draw)
{
   int right = 0, bottom = 0, left = 0, top = 0;
   int row, col, rows, cols;
   Eina_Bool ret = EINA_TRUE;

   EINA_SAFETY_ON_FALSE_RETURN_VAL((sx == 0) && (sy == 0), EINA_FALSE);

   if (fillmode == EVAS_FILTER_FILL_MODE_NONE)
     {
        _clip_to_target(&sx, &sy, sw, sh, dx, dy, dw, dh, &dx, &dy, &rows, &cols);
        DBG("blend: %d,%d,%d,%d --> %d,%d,%d,%d (from %dx%d to %dx%d +%d,%d)",
            0, 0, sw, sh, dx, dy, cols, rows, sw, sh, dw, dh, dx, dy);

        image_draw(data, drawctx, out, in,
                   sx, sy, cols, rows, // src
                   dx, dy, cols, rows, // dst
                   EINA_TRUE, // smooth
                   EINA_FALSE); // Not async
        return EINA_TRUE;
     }

   if (fillmode & EVAS_FILTER_FILL_MODE_REPEAT_X)
     {
        if (dx > 0) left = dx % sw;
        else if (dx < 0) left = sw + (dx % sw);
        cols = (dw  /*- left*/) / sw;
        if (left > 0)
          right = dw - (sw * (cols - 1)) - left;
        else
          right = dw - (sw * cols);
        dx = 0;
     }
   else if (fillmode & EVAS_FILTER_FILL_MODE_STRETCH_X)
     {
        cols = 0;
        dx = 0;
     }
   else
     {
        // FIXME: Probably wrong if dx != 0
        cols = 0;
        dw -= dx;
     }

   if (fillmode & EVAS_FILTER_FILL_MODE_REPEAT_Y)
     {
        if (dy > 0) top = dy % sh;
        else if (dy < 0) top = sh + (dy % sh);
        rows = (dh /*- top*/) / sh;
        if (top > 0)
          bottom = dh - (sh * (rows - 1)) - top;
        else
          bottom = dh - (sh * rows);
        dy = 0;
     }
   else if (fillmode & EVAS_FILTER_FILL_MODE_STRETCH_Y)
     {
        rows = 0;
        dy = 0;
     }
   else
     {
        // FIXME: Probably wrong if dy != 0
        rows = 0;
        dh -= dy;
     }

   if (top > 0) row = -1;
   else row = 0;
   for (; row <= rows; row++)
     {
        int src_x, src_y, src_w, src_h;
        int dst_x, dst_y, dst_w, dst_h;

        if (row == -1 && top > 0)
          {
             // repeat only
             src_h = top;
             src_y = sh - top;
             dst_y = dy;
             dst_h = src_h;
          }
        else if (row == rows && bottom > 0)
          {
             // repeat only
             src_h = bottom;
             src_y = 0;
             dst_y = top + dy + row * sh;
             dst_h = src_h;
          }
        else
          {
             src_y = 0;
             if (fillmode & EVAS_FILTER_FILL_MODE_STRETCH_Y)
               {
                  src_h = sh;
                  dst_h = dh;
                  dst_y = 0;
               }
             else
               {
                  dst_y = top + dy + row * sh;
                  src_h = MIN(dh - dst_y, sh);
                  dst_h = src_h;
               }
          }
        if (src_h <= 0 || dst_h <= 0) break;

        if (left > 0) col = -1;
        else col = 0;
        for (; col <= cols; col++)
          {
             if (col == -1 && left > 0)
               {
                  // repeat only
                  src_w = left;
                  src_x = sw - left;
                  dst_x = dx;
                  dst_w = src_w;
               }
             else if (col == cols && right > 0)
               {
                  // repeat only
                  src_w = right;
                  src_x = 0;
                  dst_x = left + dx + col * sw;
                  dst_w = src_w;
               }
             else
               {
                  src_x = 0;
                  if (fillmode & EVAS_FILTER_FILL_MODE_STRETCH_X)
                    {
                       src_w = sw;
                       dst_w = dw;
                       dst_x = 0;
                    }
                  else
                    {
                       dst_x = left + dx + col * sw;
                       src_w = MIN(dw - dst_x, sw);
                       dst_w = src_w;
                    }
               }
             if (src_w <= 0 || dst_w <= 0) break;

             DBG("blend: [%d,%d] %d,%d,%dx%d --> %d,%d,%dx%d "
                 "(src %dx%d, dst %dx%d)",
                 col, row, src_x, src_y, src_w, src_h,
                 dst_x, dst_y, dst_w, dst_h,
                 sw, sh, dw, dh);
             image_draw(data, drawctx, out, in,
                        src_x, src_y, src_w, src_h,
                        dst_x, dst_y, dst_w, dst_h,
                        EINA_TRUE, EINA_FALSE);
          }
     }
   return ret;
}

Evas_Filter_Apply_Func
evas_filter_blend_cpu_func_get(Evas_Filter_Command *cmd)
{
   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, NULL);
   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output, NULL);
   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input, NULL);

   if (!cmd->ctx->gl_engine || !cmd->output->glimage || cmd->output->backing)
     {
        if (cmd->input->alpha_only)
          {
             if (cmd->output->alpha_only)
               return _filter_blend_cpu_alpha;
             else
               return _filter_blend_cpu_mask_rgba;
          }
        else
          {
             if (cmd->output->alpha_only)
               return _filter_blend_cpu_rgba2alpha;
             else
               return _filter_blend_cpu_rgba;
          }
     }
   else
     {
        CRI("Can't render to GL image for the moment!");
        //return _filter_blend_opengl_generic;
        return NULL;
     }
}