/* Utility functions for the filters.  */

#include "evas_filter_private.h"

Evas_Filter_Buffer *
evas_filter_buffer_scaled_get(Evas_Filter_Context *ctx,
                              Evas_Filter_Buffer *src,
                              unsigned w, unsigned h)
{
   unsigned int src_len = 0, src_stride, dst_len = 0, dst_stride;
   uint8_t *src_map = NULL, *dst_map = NULL;
   Evas_Filter_Buffer *dst;
   RGBA_Image dstim, srcim;
   RGBA_Draw_Context dc;
   Eina_Bool ok;

   DBG("Scaling buffer from %dx%d to %dx%d, this is a slow operation!",
       src->w, src->h, w, h);
   DEBUG_TIME_BEGIN();

   // Get destination buffer
   dst = evas_filter_temporary_buffer_get(ctx, w, h, src->alpha_only, 0);
   EINA_SAFETY_ON_NULL_RETURN_VAL(dst, NULL);

   // Map input and output
   src_map = _buffer_map_all(src->buffer, &src_len, E_READ, E_ARGB, &src_stride);
   dst_map = _buffer_map_all(dst->buffer, &dst_len, E_WRITE, E_ARGB, &dst_stride);
   EINA_SAFETY_ON_FALSE_GOTO(src_map && dst_map, end);
   EINA_SAFETY_ON_FALSE_GOTO((src_stride == ((unsigned) src->w * 4)) &&
                             (dst_stride == (w * 4)), end);

   // Wrap as basic RGBA_Images
   memset(&dstim, 0, sizeof(dstim));
   dstim.cache_entry.w = w;
   dstim.cache_entry.h = h;
   dstim.cache_entry.flags.alpha = 1;
   dstim.cache_entry.flags.alpha_sparse = 0;
   dstim.cache_entry.space = EVAS_COLORSPACE_ARGB8888;
   dstim.image.data8 = dst_map;

   memset(&srcim, 0, sizeof(srcim));
   srcim.cache_entry.w = src->w;
   srcim.cache_entry.h = src->h;
   srcim.cache_entry.flags.alpha = 1;
   srcim.cache_entry.flags.alpha_sparse = 0;
   srcim.cache_entry.space = EVAS_COLORSPACE_ARGB8888;
   srcim.image.data8 = src_map;

   // Basic draw context
   memset(&dc, 0, sizeof(dc));
   dc.render_op = EVAS_RENDER_COPY;

   // Do the scale
   ok = evas_common_scale_rgba_in_to_out_clip_smooth
         (&srcim, &dstim, &dc, 0, 0, src->w, src->h, 0, 0, w, h);

   if (!ok)
     {
       ERR("RGBA Image scaling failed.");
       dst = NULL;
     }

end:
   if (src_map) ector_buffer_unmap(src->buffer, src_map, src_len);
   if ((dst) && (dst_map))
     ector_buffer_unmap(dst->buffer, dst_map, dst_len);
   DEBUG_TIME_END();
   return dst;
}

static Eina_Bool
_interpolate_none(DATA8 *output, int *points)
{
   DATA8 val = 0;
   int j;
   for (j = 0; j < 256; j++)
     {
        if (points[j] == -1)
          output[j] = val;
        else
          val = output[j] = (DATA8) points[j];
     }
   return EINA_TRUE;
}

static Eina_Bool
_interpolate_linear(DATA8 *output, int *points)
{
   DATA8 val = 0;
   int j, k, last_idx = 0;
   for (j = 0; j < 256; j++)
     {
        if (points[j] != -1)
          {
             output[j] = (DATA8) points[j];
             for (k = last_idx + 1; k < j; k++)
               output[k] = (DATA8) (points[j] + ((k - last_idx) * (points[j] - points[last_idx]) / (j - last_idx)));
             last_idx = j;
          }
     }
   val = (DATA8) points[last_idx];
   for (j = last_idx + 1; j < 256; j++)
     output[j] = val;
   return EINA_TRUE;
}

Eina_Bool
evas_filter_interpolate(DATA8 *output, int *points,
                        Evas_Filter_Interpolation_Mode mode)
{
   switch (mode)
     {
      case EVAS_FILTER_INTERPOLATION_MODE_NONE:
        return _interpolate_none(output, points);
      case EVAS_FILTER_INTERPOLATION_MODE_LINEAR:
      default:
        return _interpolate_linear(output, points);
     }
}

int
evas_filter_smallest_pow2_larger_than(int val)
{
   int n;

   for (n = 0; n < 32; n++)
     if (val <= (1 << n)) return n;

   ERR("Value %d is too damn high!", val);
   return 32;
}
