#include "evas_common_private.h"
#include "evas_blend_private.h"

#include "Ecore.h"

static Eina_Bool scale_rgba_in_to_out_clip_sample_internal(RGBA_Image *src, RGBA_Image *dst, RGBA_Draw_Context *dc, int src_region_x, int src_region_y, int src_region_w, int src_region_h, int dst_region_x, int dst_region_y, int dst_region_w, int dst_region_h);

typedef struct _Evas_Scale_Thread Evas_Scale_Thread;
typedef struct _Evas_Scale_Msg Evas_Scale_Msg;

struct _Evas_Scale_Msg
{
   Eina_Thread_Queue_Msg head;
   Evas_Scale_Thread *task;
};

struct _Evas_Scale_Thread
{
   RGBA_Image *mask8;
   DATA32 **row_ptr;
   DATA32 *dptr;
   int *lin_ptr;

   RGBA_Gfx_Func func;
   RGBA_Gfx_Func func2;

   int dst_clip_x;
   int dst_clip_y;
   int dst_clip_h;
   int dst_clip_w;
   int dst_w;

   int mask_x;
   int mask_y;

   unsigned int mul_col;
};

static Eina_Bool use_thread = EINA_FALSE;
static Eina_Thread scaling_thread;
static Eina_Thread_Queue *thread_queue = NULL;
static Eina_Thread_Queue *main_queue = NULL;

EVAS_API Eina_Bool
evas_common_scale_rgba_in_to_out_clip_sample(RGBA_Image *src, RGBA_Image *dst,
                                             RGBA_Draw_Context *dc,
                                             int src_region_x, int src_region_y,
                                             int src_region_w, int src_region_h,
                                             int dst_region_x, int dst_region_y,
                                             int dst_region_w, int dst_region_h)
{
   return evas_common_scale_rgba_in_to_out_clip_cb
     (src, dst, dc,
      src_region_x, src_region_y, src_region_w, src_region_h,
      dst_region_x, dst_region_y, dst_region_w, dst_region_h,
      scale_rgba_in_to_out_clip_sample_internal);
}

EVAS_API void
evas_common_scale_rgba_in_to_out_clip_sample_do(const Cutout_Rects *reuse,
                                                const Eina_Rectangle *clip,
                                                RGBA_Image *src, RGBA_Image *dst,
                                                RGBA_Draw_Context *dc,
                                                int src_region_x, int src_region_y,
                                                int src_region_w, int src_region_h,
                                                int dst_region_x, int dst_region_y,
                                                int dst_region_w, int dst_region_h)
{
   Eina_Rectangle area;
   Cutout_Rect *r;
   int i;

   if (!reuse)
     {
        evas_common_draw_context_clip_clip(dc, clip->x, clip->y, clip->w, clip->h);
        scale_rgba_in_to_out_clip_sample_internal(src, dst, dc,
                                                  src_region_x, src_region_y,
                                                  src_region_w, src_region_h,
                                                  dst_region_x, dst_region_y,
                                                  dst_region_w, dst_region_h);
        return;
     }

   for (i = 0; i < reuse->active; ++i)
     {
        r = reuse->rects + i;

        EINA_RECTANGLE_SET(&area, r->x, r->y, r->w, r->h);
        if (!eina_rectangle_intersection(&area, clip)) continue ;
        evas_common_draw_context_set_clip(dc, area.x, area.y, area.w, area.h);
        scale_rgba_in_to_out_clip_sample_internal(src, dst, dc,
                                                  src_region_x, src_region_y,
                                                  src_region_w, src_region_h,
                                                  dst_region_x, dst_region_y,
                                                  dst_region_w, dst_region_h);
     }
}

static void
_evas_common_scale_rgba_sample_scale_nomask(int y,
                                            int dst_clip_w, int dst_clip_h, int dst_w,
                                            DATA32 **row_ptr, int *lin_ptr,
                                            DATA32 *dptr, RGBA_Gfx_Func func, unsigned int mul_col,
                                            DATA32 *srcptr, int src_w)
{
   int x;
   dptr = dptr + dst_w * y;

   if (srcptr)
     {
        for (; y < dst_clip_h; y++)
          {
             /* * blend here [clip_w *] buf -> dptr * */
             func(srcptr, NULL, mul_col, dptr, dst_clip_w);
             dptr += dst_w;
             srcptr += src_w;
          }
     }
   else
     {
        DATA32 *buf = alloca(dst_clip_w * sizeof(DATA32));
        for (; y < dst_clip_h; y++)
          {
             DATA32 *dst_ptr = buf;
             for (x = 0; x < dst_clip_w; x++)
               {
                  DATA32 *ptr = row_ptr[y] + lin_ptr[x];
                  *dst_ptr = *ptr;
                  dst_ptr++;
               }
             /* * blend here [clip_w *] buf -> dptr * */
             func(buf, NULL, mul_col, dptr, dst_clip_w);
             dptr += dst_w;
          }
     }
}

static void
_evas_common_scale_rgba_sample_scale_mask(int y,
                                          int dst_clip_x, int dst_clip_y,
                                          int dst_clip_w, int dst_clip_h, int dst_w,
                                          int mask_x, int mask_y,
                                          DATA32 **row_ptr, int *lin_ptr, RGBA_Image *mask_ie,
                                          DATA32 *dptr, RGBA_Gfx_Func func, RGBA_Gfx_Func func2,
                                          unsigned int mul_col,
                                          DATA32 *srcptr, int src_w)
{
   DATA32 *buf;
   int x;

   /* clamp/map to mask geometry */
   if (EINA_UNLIKELY(dst_clip_x < mask_x))
     dst_clip_x = mask_x;
   if (EINA_UNLIKELY(dst_clip_y < mask_y))
     dst_clip_y = mask_y;
   if (EINA_UNLIKELY(dst_clip_x + dst_clip_w > mask_x + (int)mask_ie->cache_entry.w))
     dst_clip_w = mask_x + mask_ie->cache_entry.w - dst_clip_x;
   if (EINA_UNLIKELY(dst_clip_y + dst_clip_h > mask_y + (int)mask_ie->cache_entry.h))
     dst_clip_h = mask_y + mask_ie->cache_entry.h - dst_clip_y;

   /* a scanline buffer */
   buf = alloca(dst_clip_w * sizeof(DATA32));

   dptr = dptr + dst_w * y;
   for (; y < dst_clip_h; y++)
     {
        DATA8 *mask;

        mask = mask_ie->image.data8
          + ((dst_clip_y - mask_y + y) * mask_ie->cache_entry.w)
          + (dst_clip_x - mask_x);

        if (!srcptr)
          {
             DATA32 *dst_ptr = buf;
             for (x = 0; x < dst_clip_w; x++)
               {
                  DATA32 *ptr;

                  ptr = row_ptr[y] + lin_ptr[x];
                  *dst_ptr = *ptr;
                  dst_ptr++;
               }
          }

        /* * blend here [clip_w *] buf -> dptr * */
        if (mul_col != 0xFFFFFFFF)
          {
             func2(srcptr ?: buf, NULL, mul_col, buf, dst_clip_w);
             func(buf, mask, 0, dptr, dst_clip_w);
          }
        else
          func(srcptr ?: buf, mask, 0, dptr, dst_clip_w);

        dptr += dst_w;
        if (srcptr) srcptr += src_w;
     }
}

EVAS_API void
evas_common_scale_rgba_sample_draw(RGBA_Image *src, RGBA_Image *dst, int dst_clip_x, int dst_clip_y, int dst_clip_w, int dst_clip_h, DATA32 mul_col, int render_op, int src_region_x, int src_region_y, int src_region_w, int src_region_h, int dst_region_x, int dst_region_y, int dst_region_w, int dst_region_h, RGBA_Image *mask_ie, int mask_x, int mask_y)
{
   int      x, y;
   int     *lin_ptr;
   DATA32 **row_ptr;
   DATA32  *ptr, *dst_ptr, *src_data, *dst_data;
   int      src_w, src_h, dst_w, dst_h;
   RGBA_Gfx_Func func, func2 = NULL;

   if ((!src->image.data) || (!dst->image.data)) return;
   if (!(RECTS_INTERSECT(dst_region_x, dst_region_y, dst_region_w, dst_region_h,
                         0, 0, dst->cache_entry.w, dst->cache_entry.h))) return;
   if (!(RECTS_INTERSECT(src_region_x, src_region_y, src_region_w, src_region_h,
                         0, 0, src->cache_entry.w, src->cache_entry.h))) return;

   if ((src_region_w <= 0) || (src_region_h <= 0) ||
       (dst_region_w <= 0) || (dst_region_h <= 0)) return;

   src_w = src->cache_entry.w;
   if (src_region_x >= src_w) return;

   src_h = src->cache_entry.h;
   if (src_region_y >= src_h) return;

   dst_w = dst->cache_entry.w;
   dst_h = dst->cache_entry.h;

   src_data = src->image.data;
   dst_data = dst->image.data;

   /* sanitise clip x */
   if (dst_clip_x < 0)
     {
        dst_clip_w += dst_clip_x;
        dst_clip_x = 0;
     }

   if ((dst_clip_x + dst_clip_w) > dst_w)
     dst_clip_w = dst_w - dst_clip_x;

   if (dst_clip_x < dst_region_x)
     {
        dst_clip_w += dst_clip_x - dst_region_x;
        dst_clip_x = dst_region_x;
     }

   if (dst_clip_x >= dst_w) return;

   if ((dst_clip_x + dst_clip_w) > (dst_region_x + dst_region_w))
     dst_clip_w = dst_region_x + dst_region_w - dst_clip_x;

   if (dst_clip_w <= 0) return;

   /* sanitise clip y */
   if (dst_clip_y < 0)
     {
        dst_clip_h += dst_clip_y;
        dst_clip_y = 0;
     }

   if ((dst_clip_y + dst_clip_h) > dst_h)
     dst_clip_h = dst_h - dst_clip_y;

   if (dst_clip_y < dst_region_y)
     {
        dst_clip_h += dst_clip_y - dst_region_y;
        dst_clip_y = dst_region_y;
     }

   if (dst_clip_y >= dst_h) return;

   if ((dst_clip_y + dst_clip_h) > (dst_region_y + dst_region_h))
     dst_clip_h = dst_region_y + dst_region_h - dst_clip_y;

   if (dst_clip_h <= 0) return;

   /* sanitise region x */
   if (src_region_x < 0)
     {
        dst_region_x -= (src_region_x * dst_region_w) / src_region_w;
        dst_region_w += (src_region_x * dst_region_w) / src_region_w;
        src_region_w += src_region_x;
        src_region_x = 0;

        if (dst_clip_x < dst_region_x)
          {
             dst_clip_w += (dst_clip_x - dst_region_x);
             dst_clip_x = dst_region_x;
          }
     }

   if ((dst_clip_x + dst_clip_w) > dst_w)
     dst_clip_w = dst_w - dst_clip_x;

   if (dst_clip_w <= 0) return;

   if ((src_region_x + src_region_w) > src_w)
     {
        dst_region_w = (dst_region_w * (src_w - src_region_x)) / (src_region_w);
        src_region_w = src_w - src_region_x;
     }

   if ((dst_region_w <= 0) || (src_region_w <= 0)) return;

   /* sanitise region y */
   if (src_region_y < 0)
     {
        dst_region_y -= (src_region_y * dst_region_h) / src_region_h;
        dst_region_h += (src_region_y * dst_region_h) / src_region_h;
        src_region_h += src_region_y;
        src_region_y = 0;

        if (dst_clip_y < dst_region_y)
          {
             dst_clip_h += (dst_clip_y - dst_region_y);
             dst_clip_y = dst_region_y;
          }
     }

   if ((dst_clip_y + dst_clip_h) > dst_h)
     dst_clip_h = dst_h - dst_clip_y;

   if (dst_clip_h <= 0) return;

   if ((src_region_y + src_region_h) > src_h)
     {
        dst_region_h = (dst_region_h * (src_h - src_region_y)) / (src_region_h);
        src_region_h = src_h - src_region_y;
     }

   if ((dst_region_h <= 0) || (src_region_h <= 0)) return;

   /* figure out dst jump */
   //dst_jump = dst_w - dst_clip_w;

   /* figure out dest start ptr */
   dst_ptr = dst_data + dst_clip_x + (dst_clip_y * dst_w);

   if (!mask_ie)
     {
         if (mul_col != 0xffffffff)
           func = evas_common_gfx_func_composite_pixel_color_span_get(src->cache_entry.flags.alpha, src->cache_entry.flags.alpha_sparse, mul_col, dst->cache_entry.flags.alpha, dst_clip_w, render_op);
         else
           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, dst_clip_w, render_op);
     }
   else
     {
        if (mul_col != 0xffffffff)
          {
             func = evas_common_gfx_func_composite_pixel_mask_span_get(src->cache_entry.flags.alpha, src->cache_entry.flags.alpha_sparse, dst->cache_entry.flags.alpha, dst_clip_w, render_op);
             func2 = evas_common_gfx_func_composite_pixel_color_span_get(src->cache_entry.flags.alpha, src->cache_entry.flags.alpha_sparse, mul_col, dst->cache_entry.flags.alpha, dst_clip_w, EVAS_RENDER_COPY);
          }
        else
          func = evas_common_gfx_func_composite_pixel_mask_span_get(src->cache_entry.flags.alpha, src->cache_entry.flags.alpha_sparse, dst->cache_entry.flags.alpha, dst_clip_w, render_op);
     }

   if ((dst_region_w == src_region_w) && (dst_region_h == src_region_h))
     {
        ptr = src_data + (((dst_clip_y - dst_region_y) + src_region_y) * src_w) + ((dst_clip_x - dst_region_x) + src_region_x);


        if (mask_ie)
          _evas_common_scale_rgba_sample_scale_mask(0,
            dst_clip_x, dst_clip_y, dst_clip_w, dst_clip_h,
            dst_w, mask_x, mask_y,
            NULL, NULL,
            mask_ie, dst_ptr,
            func, func2, mul_col,
            ptr, src_w);
        else
          _evas_common_scale_rgba_sample_scale_nomask(0,
            dst_clip_w, dst_clip_h, dst_w,
            NULL, NULL,
            dst_ptr,
            func, mul_col,
            ptr, src_w);
     }
   else
     {
        /* allocate scale lookup tables */
        lin_ptr = alloca(dst_clip_w * sizeof(int));
        row_ptr = alloca(dst_clip_h * sizeof(DATA32 *));

        /* fill scale tables */
        for (x = 0; x < dst_clip_w; x++)
          lin_ptr[x] = (((x + dst_clip_x - dst_region_x) * src_region_w) / dst_region_w) + src_region_x;
        for (y = 0; y < dst_clip_h; y++)
          row_ptr[y] = src_data + (((((y + dst_clip_y - dst_region_y) * src_region_h) / dst_region_h)
                                    + src_region_y) * src_w);

        if (mask_ie)
          _evas_common_scale_rgba_sample_scale_mask(0,
            dst_clip_x, dst_clip_y, dst_clip_w, dst_clip_h,
            dst_w, mask_x, mask_y,
            row_ptr, lin_ptr,
            mask_ie, dst_ptr,
            func, func2, mul_col,
            NULL, 0);
        else
          _evas_common_scale_rgba_sample_scale_nomask(0,
            dst_clip_w, dst_clip_h, dst_w,
            row_ptr, lin_ptr,
            dst_ptr,
            func, mul_col,
            NULL, 0);
     }
}

static Eina_Bool
scale_rgba_in_to_out_clip_sample_internal(RGBA_Image *src, RGBA_Image *dst,
                                         RGBA_Draw_Context *dc,
                                         int src_region_x, int src_region_y,
                                         int src_region_w, int src_region_h,
                                         int dst_region_x, int dst_region_y,
                                         int dst_region_w, int dst_region_h)
{
   int      x, y;
   int     *lin_ptr;
   DATA32 *dptr;
   DATA32 **row_ptr;
   DATA32  *ptr, *dst_ptr, *src_data, *dst_data;
   int      dst_clip_x, dst_clip_y, dst_clip_w, dst_clip_h;
   int      src_w, src_h, dst_w, dst_h, mask_x, mask_y;
   RGBA_Gfx_Func func, func2 = NULL;
   RGBA_Image *mask_ie = dc->clip.mask;

   if (!(RECTS_INTERSECT(dst_region_x, dst_region_y, dst_region_w, dst_region_h, 0, 0, dst->cache_entry.w, dst->cache_entry.h)))
     return EINA_FALSE;
   if (!(RECTS_INTERSECT(src_region_x, src_region_y, src_region_w, src_region_h, 0, 0, src->cache_entry.w, src->cache_entry.h)))
     return EINA_FALSE;

   src_w = src->cache_entry.w;
   src_h = src->cache_entry.h;
   dst_w = dst->cache_entry.w;
   dst_h = dst->cache_entry.h;

   src_data = src->image.data;
   dst_data = dst->image.data;

   mask_x = dc->clip.mask_x;
   mask_y = dc->clip.mask_y;

   if (dc->clip.use)
     {
        dst_clip_x = dc->clip.x;
        dst_clip_y = dc->clip.y;
        dst_clip_w = dc->clip.w;
        dst_clip_h = dc->clip.h;
        if (dst_clip_x < 0)
          {
             dst_clip_w += dst_clip_x;
             dst_clip_x = 0;
          }
        if (dst_clip_y < 0)
          {
             dst_clip_h += dst_clip_y;
             dst_clip_y = 0;
          }
        if ((dst_clip_x + dst_clip_w) > dst_w)
          dst_clip_w = dst_w - dst_clip_x;
        if ((dst_clip_y + dst_clip_h) > dst_h)
          dst_clip_h = dst_h - dst_clip_y;
     }
   else
     {
        dst_clip_x = 0;
        dst_clip_y = 0;
        dst_clip_w = dst_w;
        dst_clip_h = dst_h;
     }

   if (dst_clip_x < dst_region_x)
     {
        dst_clip_w += dst_clip_x - dst_region_x;
        dst_clip_x = dst_region_x;
     }
   if ((dst_clip_x + dst_clip_w) > (dst_region_x + dst_region_w))
     dst_clip_w = dst_region_x + dst_region_w - dst_clip_x;
   if (dst_clip_y < dst_region_y)
     {
        dst_clip_h += dst_clip_y - dst_region_y;
        dst_clip_y = dst_region_y;
     }
   if ((dst_clip_y + dst_clip_h) > (dst_region_y + dst_region_h))
     dst_clip_h = dst_region_y + dst_region_h - dst_clip_y;

   if ((src_region_w <= 0) || (src_region_h <= 0) ||
       (dst_region_w <= 0) || (dst_region_h <= 0) ||
       (dst_clip_w <= 0) || (dst_clip_h <= 0))
     return EINA_FALSE;

   /* sanitise x */
   if (src_region_x < 0)
     {
        dst_region_x -= (src_region_x * dst_region_w) / src_region_w;
        dst_region_w += (src_region_x * dst_region_w) / src_region_w;
        src_region_w += src_region_x;
        src_region_x = 0;
     }
   if (src_region_x >= src_w) return EINA_FALSE;
   if ((src_region_x + src_region_w) > src_w)
     {
        dst_region_w = (dst_region_w * (src_w - src_region_x)) / (src_region_w);
        src_region_w = src_w - src_region_x;
     }
   if (dst_region_w <= 0) return EINA_FALSE;
   if (src_region_w <= 0) return EINA_FALSE;
   if (dst_clip_x >= dst_w) return EINA_FALSE;
   if (dst_clip_x < dst_region_x)
     {
        dst_clip_w += (dst_clip_x - dst_region_x);
        dst_clip_x = dst_region_x;
     }
   if ((dst_clip_x + dst_clip_w) > dst_w)
     {
        dst_clip_w = dst_w - dst_clip_x;
     }
   if (dst_clip_w <= 0) return EINA_FALSE;

   /* sanitise y */
   if (src_region_y < 0)
     {
        dst_region_y -= (src_region_y * dst_region_h) / src_region_h;
        dst_region_h += (src_region_y * dst_region_h) / src_region_h;
        src_region_h += src_region_y;
        src_region_y = 0;
     }
   if (src_region_y >= src_h) return EINA_FALSE;
   if ((src_region_y + src_region_h) > src_h)
     {
        dst_region_h = (dst_region_h * (src_h - src_region_y)) / (src_region_h);
        src_region_h = src_h - src_region_y;
     }
   if (dst_region_h <= 0) return EINA_FALSE;
   if (src_region_h <= 0) return EINA_FALSE;
   if (dst_clip_y >= dst_h) return EINA_FALSE;
   if (dst_clip_y < dst_region_y)
     {
        dst_clip_h += (dst_clip_y - dst_region_y);
        dst_clip_y = dst_region_y;
     }
   if ((dst_clip_y + dst_clip_h) > dst_h)
     {
        dst_clip_h = dst_h - dst_clip_y;
     }
   if (dst_clip_h <= 0) return EINA_FALSE;

   /* figure out dst jump */
   //dst_jump = dst_w - dst_clip_w;

   /* figure out dest start ptr */
   dst_ptr = dst_data + dst_clip_x + (dst_clip_y * dst_w);

   if (!mask_ie)
     {
        if (dc->mul.use)
          func = evas_common_gfx_func_composite_pixel_color_span_get(src->cache_entry.flags.alpha, src->cache_entry.flags.alpha_sparse, dc->mul.col, dst->cache_entry.flags.alpha, dst_clip_w, dc->render_op);
        else
          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, dst_clip_w, dc->render_op);
     }
   else
     {
        func = evas_common_gfx_func_composite_pixel_mask_span_get(src->cache_entry.flags.alpha, src->cache_entry.flags.alpha_sparse, dst->cache_entry.flags.alpha, dst_clip_w, dc->render_op);
        if (dc->mul.use)
          func2 = evas_common_gfx_func_composite_pixel_color_span_get(src->cache_entry.flags.alpha, src->cache_entry.flags.alpha_sparse, dc->mul.col, dst->cache_entry.flags.alpha, dst_clip_w, EVAS_RENDER_COPY);
     }

   if ((dst_region_w == src_region_w) && (dst_region_h == src_region_h))
     {
#ifdef HAVE_PIXMAN
# ifdef PIXMAN_IMAGE_SCALE_SAMPLE
        if ((src->pixman.im) && (dst->pixman.im) && (!dc->clip.mask) &&
            ((!dc->mul.use) || ((dc->mul.use) && (dc->mul.col == 0xffffffff))) &&
            ((dc->render_op == _EVAS_RENDER_COPY) ||
             (dc->render_op == _EVAS_RENDER_BLEND)))
          {
             pixman_op_t op = PIXMAN_OP_SRC; // _EVAS_RENDER_COPY
             if (dc->render_op == _EVAS_RENDER_BLEND)
               op = PIXMAN_OP_OVER;

             pixman_image_composite(op,
                                    src->pixman.im, NULL,
                                    dst->pixman.im,
                                    (dst_clip_x - dst_region_x) + src_region_x,
                                    (dst_clip_y - dst_region_y) + src_region_y,
                                    0, 0,
                                    dst_clip_x, dst_clip_y,
                                    dst_clip_w, dst_clip_h);
          }
        else
# endif
#endif
          {
             int mul_col = dc->mul.use ? dc->mul.col : 0xffffffff;
             ptr = src_data + ((dst_clip_y - dst_region_y + src_region_y) * src_w) + (dst_clip_x - dst_region_x) + src_region_x;

             if (mask_ie)
               _evas_common_scale_rgba_sample_scale_mask(0,
                 dst_clip_x, dst_clip_y, dst_clip_w, dst_clip_h,
                 dst_w, mask_x, mask_y,
                 NULL, NULL,
                 mask_ie, dst_ptr,
                 func, func2, mul_col,
                 ptr, src_w);
             else
               _evas_common_scale_rgba_sample_scale_nomask(0,
                 dst_clip_w, dst_clip_h, dst_w,
                 NULL, NULL,
                 dst_ptr,
                 func, mul_col,
                 ptr, src_w);
          }
     }
   else
     {
        /* allocate scale lookup tables */
        lin_ptr = alloca(dst_clip_w * sizeof(int));
        row_ptr = alloca(dst_clip_h * sizeof(DATA32 *));

        /* fill scale tables */
        for (x = 0; x < dst_clip_w; x++)
          lin_ptr[x] = (((x + dst_clip_x - dst_region_x) * src_region_w) / dst_region_w) + src_region_x;
        for (y = 0; y < dst_clip_h; y++)
          row_ptr[y] = src_data + (((((y + dst_clip_y - dst_region_y) * src_region_h) / dst_region_h)
                                    + src_region_y) * src_w);
        /* scale to dst */
        dptr = dst_ptr;
#ifdef DIRECT_SCALE
        if ((!src->cache_entry.flags.alpha) &&
            (!dst->cache_entry.flags.alpha) &&
            (!dc->mul.use) &&
            (!dc->clip.mask))
          {
             for (y = 0; y < dst_clip_h; y++)
               {

                  dst_ptr = dptr;
                  for (x = 0; x < dst_clip_w; x++)
                    {
                       ptr = row_ptr[y] + lin_ptr[x];
                       *dst_ptr = *ptr;
                       dst_ptr++;
                    }

                  dptr += dst_w;
               }
          }
        else
#endif
          {
             unsigned int mul_col;

             mul_col = dc->mul.use ? dc->mul.col : 0xFFFFFFFF;

             /* do we have enough data to start some additional thread ? */
             if (use_thread && dst_clip_h > 32 && dst_clip_w * dst_clip_h > 4096)
               {
                  /* Yes, we do ! */
                  Evas_Scale_Msg *msg;
                  void *ref;
                  Evas_Scale_Thread local;

                  local.mask8 = dc->clip.mask;
                  local.row_ptr = row_ptr;
                  local.dptr = dptr;
                  local.lin_ptr = lin_ptr;
                  local.func = func;
                  local.func2 = func2;
                  local.dst_clip_x = dst_clip_x;
                  local.dst_clip_y = dst_clip_y;
                  local.dst_clip_h = dst_clip_h;
                  local.dst_clip_w = dst_clip_w;
                  local.dst_w = dst_w;
                  local.mask_x = mask_x;
                  local.mask_y = mask_y;
                  local.mul_col = mul_col;

                  msg = eina_thread_queue_send(thread_queue, sizeof (Evas_Scale_Msg), &ref);
                  msg->task = &local;
                  eina_thread_queue_send_done(thread_queue, ref);

                  /* image masking */
                  if (dc->clip.mask)
                    {
                       _evas_common_scale_rgba_sample_scale_mask(0,
                                                                 dst_clip_x, dst_clip_y,
                                                                 dst_clip_w, dst_clip_h >> 1, dst_w,
                                                                 dc->clip.mask_x, dc->clip.mask_y,
                                                                 row_ptr, lin_ptr, dc->clip.mask,
                                                                 dptr, func, func2, mul_col,
                                                                 NULL, 0);

                    }
                  else
                    {
                       _evas_common_scale_rgba_sample_scale_nomask(0,
                                                                   dst_clip_w, dst_clip_h >> 1, dst_w,
                                                                   row_ptr, lin_ptr,
                                                                   dptr, func, mul_col,
                                                                   NULL, 0);
                    }

                  msg = eina_thread_queue_wait(main_queue, &ref);
                  if (msg) eina_thread_queue_wait_done(main_queue, ref);
               }
             else
               {
                  /* No we don't ! */

                  /* image masking */
                  if (dc->clip.mask)
                    {
                       _evas_common_scale_rgba_sample_scale_mask(0,
                                                                 dst_clip_x, dst_clip_y,
                                                                 dst_clip_w, dst_clip_h, dst_w,
                                                                 dc->clip.mask_x, dc->clip.mask_y,
                                                                 row_ptr, lin_ptr, dc->clip.mask,
                                                                 dptr, func, func2, mul_col,
                                                                 NULL, 0);

                    }
                  else
                    {
                       _evas_common_scale_rgba_sample_scale_nomask(0,
                                                                   dst_clip_w, dst_clip_h, dst_w,
                                                                   row_ptr, lin_ptr,
                                                                   dptr, func, mul_col,
                                                                   NULL, 0);
                    }
               }
          }
     }

   return EINA_TRUE;
}

static void *
_evas_common_scale_sample_thread(void *data EINA_UNUSED,
                                 Eina_Thread t EINA_UNUSED)
{
   Evas_Scale_Msg *msg;
   Evas_Scale_Thread *todo = NULL;

   eina_thread_name_set(eina_thread_self(), "Evas-scale-sam");
   do
     {
        void *ref;

        todo = NULL;

        msg = eina_thread_queue_wait(thread_queue, &ref);
        if (msg)
          {
             int h;

             todo = msg->task;
             eina_thread_queue_wait_done(thread_queue, ref);

             if (!todo) goto end;

             h = todo->dst_clip_h >> 1;

             if (todo->mask8)
               _evas_common_scale_rgba_sample_scale_mask(h,
                                                         todo->dst_clip_x, todo->dst_clip_y,
                                                         todo->dst_clip_w, todo->dst_clip_h,
                                                         todo->dst_w,
                                                         todo->mask_x, todo->mask_y,
                                                         todo->row_ptr, todo->lin_ptr, todo->mask8,
                                                         todo->dptr, todo->func, todo->func2,
                                                         todo->mul_col,
                                                         NULL, 0);
             else
               _evas_common_scale_rgba_sample_scale_nomask(h,
                                                           todo->dst_clip_w, todo->dst_clip_h,
                                                           todo->dst_w,
                                                           todo->row_ptr, todo->lin_ptr,
                                                           todo->dptr, todo->func, todo->mul_col,
                                                           NULL, 0);
          }

     end:
        msg = eina_thread_queue_send(main_queue, sizeof (Evas_Scale_Msg), &ref);
        msg->task = NULL;
        eina_thread_queue_send_done(main_queue, ref);
     }
   while (todo);

   return NULL;
}

static void
evas_common_scale_sample_fork_reset(void *data EINA_UNUSED)
{
   eina_thread_queue_free(thread_queue);
   eina_thread_queue_free(main_queue);

   thread_queue = eina_thread_queue_new();
   main_queue = eina_thread_queue_new();

   if (!eina_thread_create(&scaling_thread, EINA_THREAD_NORMAL, -1,
                           _evas_common_scale_sample_thread, NULL))
     {
        CRI("We failed to recreate the upscaling thread.");
        use_thread = EINA_FALSE;
     }
}

EVAS_API void
evas_common_scale_sample_init(void)
{
   if (eina_cpu_count() <= 2) return ;

//Eina_Thread_Queue doesn't work on WIN32.
#ifdef _WIN32
   return;
#endif

   ecore_fork_reset_callback_add(evas_common_scale_sample_fork_reset, NULL);

   thread_queue = eina_thread_queue_new();
   if (EINA_UNLIKELY(!thread_queue))
     {
        ERR("Failed to create thread queue");
        goto cleanup;
     }
   main_queue = eina_thread_queue_new();
   if (EINA_UNLIKELY(!thread_queue))
     {
        ERR("Failed to create thread queue");
        goto cleanup;
     }

   if (!eina_thread_create(&scaling_thread, EINA_THREAD_NORMAL, -1,
                           _evas_common_scale_sample_thread, NULL))
     {
        CRI("We failed to create the upscaling thread.");
        goto cleanup;
     }

   use_thread = EINA_TRUE;
   return;

cleanup:
   if (thread_queue) eina_thread_queue_free(thread_queue);
   if (main_queue) eina_thread_queue_free(main_queue);
}

EVAS_API void
evas_common_scale_sample_shutdown(void)
{
   Evas_Scale_Msg *msg;
   void *ref;

   if (!use_thread) return ;

   ecore_fork_reset_callback_del(evas_common_scale_sample_fork_reset, NULL);

   msg = eina_thread_queue_send(thread_queue, sizeof (Evas_Scale_Msg), &ref);
   msg->task = NULL;
   eina_thread_queue_send_done(thread_queue, ref);

   /* Here is the thread commiting succide*/

   msg = eina_thread_queue_wait(main_queue, &ref);
   if (msg) eina_thread_queue_wait_done(main_queue, ref);

   eina_thread_join(scaling_thread);

   eina_thread_queue_free(thread_queue);
   eina_thread_queue_free(main_queue);
}
