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



                                
                                                                                
                                                                        

                                                                  

                          

                        




                                              
                                                    
 

                                                               



                                                                         
                               


                                
                                                              
                
 
                 





                                                                    
                 




                                                                    
 
                         
                                 
                       


                              
                                               
                      




                                                                                         
                                                                       
                     
                









                                                       




           
                                                                               
                                                                         

                                                                 

                          

                        
                                   




                                              
                                                    
 

                                                               

                                                                         
                            

                               
                        


                                
                                                              
                
 






                                                                    
                 





                                                                    
                 




                                                                    
 
                         
                                 
                                                                 
                              
                                          
                 




                                                                     
                                                              




















                                                                            
                           



                           
                                              
                
 







                                                 
                 
                          

           


                                                     





                                      
                      



                                                    
                                     

                     
                                    








                                                                    
                                                           
                                                               
                                                            



                                                         




                                                                    
                                                      
 


















                                                                                             
                                                               
                                                                              







                                     
                      



                                                   
                                     
                                 
                                    









                                                                    
                                                               




                                                           




                                                                    
                                                      
 


















                                                                                             

                                                                             










                                                           
                                                                 


                                                         
                                     



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

static void
_filter_displace_cpu_alpha_do(int w, int h, int map_w, int map_h, int intensity,
                              DATA8 *src, DATA8 *dst, DATA32 *map_start,
                              Eina_Bool stretch, Eina_Bool smooth,
                              Eina_Bool blend)
{
   int x, y, map_x, map_y;
   const int dx = RED;
   const int dy = GREEN;
   DATA8 *map;

   for (y = 0, map_y = 0; y < h; y++, map_y++)
     {
        if (map_y >= map_h) map_y = 0;
        map = (DATA8 *) (map_start + map_y * map_w);

        for (x = 0, map_x = 0; x < w;
             x++, dst++, src++, map_x++, map += sizeof(DATA32))
          {
             int offx = 0, offy = 0, offx_dec = 0, offy_dec = 0, val = 0;
             Eina_Bool out = 0;

             // wrap around (x)
             if (map_x >= map_w)
               {
                  map_x = 0;
                  map = (DATA8 *) (map_start + map_y * map_w);
               }

             // x
             val = ((int) map[dx] - 128) * intensity;
             offx = val >> 7;
             offx_dec = val & 0x7f;
             if ((x + offx) < 0) { offx = -x; out = 1; }
             if ((x + offx + 1) >= w) { offx = w - x - 2; out = 1; }

             // y
             val = ((int) map[dy] - 128) * intensity;
             offy = val >> 7;
             offy_dec = val & 0x7f;
             if ((y + offy) < 0) { offy = -y; out = 1; }
             if ((y + offy + 1) >= h) { offy = h - y - 2; out = 1; }

             // get value
             if (out && !stretch)
               val = 0;
             else
               {
                  if (!smooth)
                    val = src[offx + offy * w];
                  else
                    {
                       val  = src[offx + offy * w] * (128 - offx_dec) * (128 - offy_dec);
                       val += src[offx + 1 + offy * w] * offx_dec * (128 - offy_dec);
                       val += src[offx + (offy + 1) * w] * (128 - offx_dec) * offy_dec;
                       val += src[offx + 1 + (offy + 1) * w] * offx_dec * offy_dec;
                       val = val >> 14; // <=> *dst = val / (128 * 128)
                    }
               }

             // apply alpha
             if (map[ALPHA] != 255)
               val = (val * map[ALPHA]) / 255;

             // write to dest
             if (blend)
               *dst = (*dst * (255 - val)) / 255 + val;
             else
               *dst = val;
          }
     }
}

static void
_filter_displace_cpu_rgba_do(int w, int h, int map_w, int map_h, int intensity,
                             DATA32 *src, DATA32 *dst, DATA32 *map_start,
                             Eina_Bool stretch, Eina_Bool smooth,
                             Eina_Bool blend)
{
   int x, y, map_x, map_y;
   const int dx = RED;
   const int dy = GREEN;
   Eina_Bool unpremul = EINA_FALSE;
   DATA8 *map;

   for (y = 0, map_y = 0; y < h; y++, map_y++)
     {
        if (map_y >= map_h) map_y = 0;
        map = (DATA8 *) (map_start + map_y * map_w);

        for (x = 0, map_x = 0; x < w;
             x++, dst++, src++, map_x++, map += sizeof(DATA32))
          {
             int offx = 0, offy = 0, offx_dec = 0, offy_dec = 0, val = 0;
             DATA32 col = 0;
             Eina_Bool out = 0;

             // wrap (x)
             if (map_x >= map_w)
               {
                  map_x = 0;
                  map = (DATA8 *) (map_start + map_y * map_w);
               }

             if (!map[ALPHA]) continue;
             if (!unpremul && map[ALPHA] != 0xFF)
               {
                  unpremul = EINA_TRUE;
                  evas_data_argb_unpremul(map_start, map_w * map_h);
               }

             // x
             val = ((int) map[dx] - 128) * intensity;
             offx = val >> 7;
             offx_dec = val & 0x7f;
             if ((x + offx) < 0) { offx = -x; out = 1; }
             if ((x + offx + 1) >= w) { offx = w - x - 2; out = 1; }

             // y
             val = ((int) map[dy] - 128) * intensity;
             offy = val >> 7;
             offy_dec = val & 0x7f;
             if ((y + offy) < 0) { offy = -y; out = 1; }
             if ((y + offy + 1) >= h) { offy = h - y - 2; out = 1; }

             // get value
             if (out && !stretch)
               col = A_VAL(src + offx + offy * w) << (ALPHA * 8);
             else if (!smooth)
               col = src[offx + offy * w];
             else
               {
                  int R, G, B, A;
                  DATA32 s00, s01, s10, s11; // indexes represent x,y
                  int mul00, mul01, mul10, mul11;

                  mul00 = (128 - offx_dec) * (128 - offy_dec);
                  mul01 = (128 - offx_dec) * offy_dec;
                  mul10 = offx_dec * (128 - offy_dec);
                  mul11 = offx_dec * offy_dec;

                  s00 = src[offx + offy * w];
                  s01 = src[offx + (offy + 1) * w];
                  s10 = src[offx + 1 + offy * w];
                  s11 = src[offx + 1 + (offy + 1) * w];

                  A = (ALPHA_OF(s00) * mul00) + (ALPHA_OF(s10) * mul10)
                        + (ALPHA_OF(s01) * mul01) + (ALPHA_OF(s11) * mul11);

                  R = (RED_OF(s00) * mul00) + (RED_OF(s10) * mul10)
                        + (RED_OF(s01) * mul01) + (RED_OF(s11) * mul11);

                  G = (GREEN_OF(s00) * mul00) + (GREEN_OF(s10) * mul10)
                        + (GREEN_OF(s01) * mul01) + (GREEN_OF(s11) * mul11);

                  B = (BLUE_OF(s00) * mul00) + (BLUE_OF(s10) * mul10)
                        + (BLUE_OF(s01) * mul01) + (BLUE_OF(s11) * mul11);

                  A >>= 14;
                  R >>= 14;
                  G >>= 14;
                  B >>= 14;

                  col = ARGB_JOIN(A, R, G, B);
               }

             if (map[ALPHA] != 0xFF)
               col = MUL_256(map[ALPHA], col);

             if (blend)
               {
                  DATA32 a = 256 - ALPHA_OF(col);
                  *dst = col + MUL_256(a, *dst);
               }
             else
               *dst = col;
          }
     }

   if (unpremul)
     evas_data_argb_premul(map_start, map_w * map_h);
}

/**
 * Apply distortion map on alpha image
 * input:  alpha
 * output: alpha
 * map:    rg+a (rgba)
 */
static Eina_Bool
_filter_displace_cpu_alpha(Evas_Filter_Command *cmd)
{
   int w, h, map_w, map_h, intensity;
   DATA8 *dst, *src;
   DATA32 *map_start;
   Eina_Bool stretch, smooth, blend;

   w = cmd->input->w;
   h = cmd->input->h;
   EINA_SAFETY_ON_FALSE_RETURN_VAL(w == cmd->output->w, EINA_FALSE);
   EINA_SAFETY_ON_FALSE_RETURN_VAL(h == cmd->output->h, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input->backing, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->mask->backing, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output->backing, EINA_FALSE);

   src = ((RGBA_Image *) cmd->input->backing)->image.data8;
   map_start = ((RGBA_Image *) cmd->mask->backing)->image.data;
   dst = ((RGBA_Image *) cmd->output->backing)->image.data8;
   EINA_SAFETY_ON_NULL_RETURN_VAL(src, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(map_start, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(dst, EINA_FALSE);

   stretch = cmd->displacement.flags & EVAS_FILTER_DISPLACE_STRETCH;
   smooth = cmd->displacement.flags & EVAS_FILTER_DISPLACE_LINEAR;
   map_w = cmd->mask->w;
   map_h = cmd->mask->h;
   intensity = cmd->displacement.intensity;
   blend = (cmd->draw.render_op == EVAS_RENDER_BLEND);

   // Stretch if necessary.
   if ((map_w != w || map_h != h) && (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_XY))
     {
        Evas_Filter_Buffer *fb;

        if (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_X)
          map_w = w;
        if (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_Y)
          map_h = h;

        BUFFERS_LOCK();
        fb = evas_filter_buffer_scaled_get(cmd->ctx, cmd->mask, map_w, map_h);
        BUFFERS_UNLOCK();

        EINA_SAFETY_ON_NULL_RETURN_VAL(fb, EINA_FALSE);
        fb->locked = EINA_FALSE;
        map_start = ((RGBA_Image *) fb->backing)->image.data;
     }

   _filter_displace_cpu_alpha_do(w, h, map_w, map_h, intensity,
                                 src, dst, map_start, stretch, smooth, blend);

   return EINA_TRUE;
}

/**
 * Apply distortion map on rgba image
 * input:  rgba
 * output: rgba
 * map:    rg+a (rgba)
 */
static Eina_Bool
_filter_displace_cpu_rgba(Evas_Filter_Command *cmd)
{
   int w, h, map_w, map_h, intensity;
   DATA32 *dst, *src, *map_start;
   Eina_Bool stretch, smooth, blend;

   w = cmd->input->w;
   h = cmd->input->h;
   EINA_SAFETY_ON_FALSE_RETURN_VAL(w == cmd->output->w, EINA_FALSE);
   EINA_SAFETY_ON_FALSE_RETURN_VAL(h == cmd->output->h, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input->backing, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->mask->backing, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output->backing, EINA_FALSE);

   src = ((RGBA_Image *) cmd->input->backing)->image.data;
   map_start = ((RGBA_Image *) cmd->mask->backing)->image.data;
   dst = ((RGBA_Image *) cmd->output->backing)->image.data;
   EINA_SAFETY_ON_NULL_RETURN_VAL(src, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(map_start, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(dst, EINA_FALSE);

   stretch = cmd->displacement.flags & EVAS_FILTER_DISPLACE_STRETCH;
   smooth = cmd->displacement.flags & EVAS_FILTER_DISPLACE_LINEAR;
   map_w = cmd->mask->w;
   map_h = cmd->mask->h;
   intensity = cmd->displacement.intensity;
   blend = (cmd->draw.render_op == EVAS_RENDER_BLEND);

   // Stretch if necessary.
   if ((map_w != w || map_h != h) && (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_XY))
     {
        Evas_Filter_Buffer *fb;

        if (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_X)
          map_w = w;
        if (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_Y)
          map_h = h;

        BUFFERS_LOCK();
        fb = evas_filter_buffer_scaled_get(cmd->ctx, cmd->mask, map_w, map_h);
        BUFFERS_UNLOCK();

        EINA_SAFETY_ON_NULL_RETURN_VAL(fb, EINA_FALSE);
        fb->locked = EINA_FALSE;
        map_start = ((RGBA_Image *) fb->backing)->image.data;
     }

   _filter_displace_cpu_rgba_do(w, h, map_w, map_h, intensity,
                                src, dst, map_start, stretch, smooth, blend);

   return EINA_TRUE;
}

Evas_Filter_Apply_Func
evas_filter_displace_cpu_func_get(Evas_Filter_Command *cmd)
{
   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, NULL);
   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input, NULL);
   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output, NULL);
   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->mask, NULL);
   EINA_SAFETY_ON_FALSE_RETURN_VAL(!cmd->mask->alpha_only, NULL);

   if (cmd->input->alpha_only != cmd->output->alpha_only)
     {
        CRI("Invalid color formats");
        return NULL;
     }

   if (cmd->input->alpha_only)
     return _filter_displace_cpu_alpha;
   else
     return _filter_displace_cpu_rgba;
}