#include "evas_common_soft16.h"
#include "evas_soft16_point_blend.c"

static void
_soft16_image_draw_scaled_solid_solid(Soft16_Image *src,
				      Soft16_Image *dst,
				      RGBA_Draw_Context *dc __UNUSED__,
				      int dst_offset, int w, int h,
				      int *offset_x, int *offset_y)
{
   DATA16 *dst_itr;
   int y, w_align;

   w_align = w & ~7;

   dst_itr = dst->pixels + dst_offset;
   for (y = 0; y < h; y++, dst_itr += dst->stride)
     {
	DATA16 *d, *s;
	int x;

	s = src->pixels + offset_y[y];
	pld(s, 0);
	pld(offset_x, 0);

	d = dst_itr;
	x = 0;
	while (x < w_align)
	  {
	     pld(s, 32);
	     pld(offset_x + x, 32);

	     UNROLL8({
		_soft16_pt_blend_solid_solid(d, s[offset_x[x]]);
		x++;
		d++;
	     });
	  }

	for (; x < w; x++, d++)
	  _soft16_pt_blend_solid_solid(d, s[offset_x[x]]);
     }
}
static void
_soft16_image_draw_scaled_transp_solid(Soft16_Image *src,
				       Soft16_Image *dst,
				       RGBA_Draw_Context *dc __UNUSED__,
				       int dst_offset, int w, int h,
				       int *offset_x, int *offset_y)
{
   DATA16 *dst_itr;
   int y, w_align;

   w_align = w & ~7;

   dst_itr = dst->pixels + dst_offset;
   for (y = 0; y < h; y++, dst_itr += dst->stride)
     {
	DATA16 *d, *s;
	DATA8 *a;
	int x;

	s = src->pixels + offset_y[y];
	a = src->alpha + offset_y[y];
	pld(s, 0);
	pld(a, 0);
	pld(offset_x, 0);

	d = dst_itr;
	x = 0;
	while (x < w_align)
	  {
	     pld(s, 32);
	     pld(a, 8);
	     pld(offset_x + x, 32);

	     UNROLL8({
		int off_x = offset_x[x];
		_soft16_pt_blend_transp_solid(d, s[off_x], a[off_x]);
		x++;
		d++;
	     });
	  }

	for (; x < w; x++, d++)
	  _soft16_pt_blend_transp_solid(d, s[offset_x[x]], a[offset_x[x]]);
     }
}

static inline void
_soft16_image_draw_scaled_no_mul(Soft16_Image *src, Soft16_Image *dst,
				 RGBA_Draw_Context *dc,
				 int dst_offset, int w, int h,
				 int *offset_x, int *offset_y)
{
   if ((src->cache_entry.flags.alpha && src->alpha) && 
       (!dst->cache_entry.flags.alpha))
      _soft16_image_draw_scaled_transp_solid
	(src, dst, dc, dst_offset, w, h, offset_x, offset_y);
   else if (!dst->cache_entry.flags.alpha)
      _soft16_image_draw_scaled_solid_solid
	(src, dst, dc, dst_offset, w, h, offset_x, offset_y);
   else
      ERR("Unsupported draw of scaled images src->cache_entry.flags.alpha=%d, "
              "dst->cache_entry.flags.alpha=%d, WITHOUT COLOR MUL",
              src->cache_entry.flags.alpha, dst->cache_entry.flags.alpha);
}

static void
_soft16_image_draw_scaled_solid_solid_mul_alpha(Soft16_Image *src,
						Soft16_Image *dst,
						RGBA_Draw_Context *dc __UNUSED__,
						int dst_offset, int w, int h,
						int *offset_x, int *offset_y,
						DATA8 alpha)
{
   DATA16 *dst_itr;
   int y, w_align;

   w_align = w & ~7;

   dst_itr = dst->pixels + dst_offset;
   for (y = 0; y < h; y++, dst_itr += dst->stride)
     {
	DATA16 *d, *s;
	int x;

	s = src->pixels + offset_y[y];
	pld(s, 0);
	pld(offset_x, 0);

	d = dst_itr;
	x = 0;
	while (x < w_align)
	  {
	     pld(s, 32);
	     pld(offset_x + x, 32);

	     UNROLL8({
		_soft16_pt_blend_solid_solid_mul_alpha
		  (d, s[offset_x[x]], alpha);
		x++;
		d++;
	     });
	  }

	for (; x < w; x++, d++)
	  _soft16_pt_blend_solid_solid_mul_alpha
	    (d, s[offset_x[x]], alpha);
     }
}

static void
_soft16_image_draw_scaled_transp_solid_mul_alpha(Soft16_Image *src,
						 Soft16_Image *dst,
						 RGBA_Draw_Context *dc __UNUSED__,
						 int dst_offset, int w, int h,
						 int *offset_x, int *offset_y,
						 DATA8 alpha)
{
   DATA16 *dst_itr;
   int y, w_align;

   w_align = w & ~7;

   dst_itr = dst->pixels + dst_offset;
   for (y = 0; y < h; y++, dst_itr += dst->stride)
     {
	DATA16 *d, *s;
	DATA8 *a;
	int x;

	s = src->pixels + offset_y[y];
	a = src->alpha + offset_y[y];
	pld(s, 0);
	pld(a, 0);
	pld(offset_x, 0);

	d = dst_itr;
	x = 0;
	while (x < w_align)
	  {
	     pld(s, 32);
	     pld(a, 8);
	     pld(offset_x + x, 32);

	     UNROLL8({
		int off_x = offset_x[x];
		_soft16_pt_blend_transp_solid_mul_alpha
		  (d, s[off_x], a[off_x], alpha);
		x++;
		d++;
	     });
	  }

	for (; x < w; x++, d++)
	  _soft16_pt_blend_transp_solid_mul_alpha
	    (d, s[offset_x[x]], a[offset_x[x]], alpha);
     }
}

static inline void
_soft16_image_draw_scaled_mul_alpha(Soft16_Image *src, Soft16_Image *dst,
				    RGBA_Draw_Context *dc,
				    int dst_offset, int w, int h,
				    int *offset_x, int *offset_y, DATA8 a)
{
   if ((src->cache_entry.flags.alpha && src->alpha) && 
       (!dst->cache_entry.flags.alpha))
      _soft16_image_draw_scaled_transp_solid_mul_alpha
         (src, dst, dc, dst_offset, w, h, offset_x, offset_y, a);
   else if (!dst->cache_entry.flags.alpha)
      _soft16_image_draw_scaled_solid_solid_mul_alpha
         (src, dst, dc, dst_offset, w, h, offset_x, offset_y, a);
   else
     ERR("Unsupported draw of scaled images src->cache_entry.flags.alpha=%d, "
	   "dst->cache_entry.flags.alpha=%d, WITH ALPHA MUL %d",
	   src->cache_entry.flags.alpha, dst->cache_entry.flags.alpha, A_VAL(&dc->mul.col));
}

static void
_soft16_image_draw_scaled_solid_solid_mul_color(Soft16_Image *src,
						Soft16_Image *dst,
						RGBA_Draw_Context *dc __UNUSED__,
						int dst_offset, int w, int h,
						int *offset_x, int *offset_y,
						DATA8 r, DATA8 g, DATA8 b,
						DATA8 alpha)
{
   DATA16 *dst_itr;
   int y, w_align;

   w_align = w & ~7;

   dst_itr = dst->pixels + dst_offset;

   if (alpha == 31)
     for (y = 0; y < h; y++, dst_itr += dst->stride)
       {
	  DATA16 *d, *s;
	  int x;

	  s = src->pixels + offset_y[y];
	  pld(s, 0);
	  pld(offset_x, 0);

	  d = dst_itr;
	  x = 0;
	  while (x < w_align)
	    {
	       pld(s, 32);
	       pld(offset_x + x, 32);

	       UNROLL8({
		  _soft16_pt_blend_solid_solid_mul_color_solid
		    (d, s[offset_x[x]], r, g, b);
		  x++;
		  d++;
	       });
	    }

	  for (; x < w; x++, d++)
	    _soft16_pt_blend_solid_solid_mul_color_solid
	      (d, s[offset_x[x]], r, g, b);
       }
   else
     for (y = 0; y < h; y++, dst_itr += dst->stride)
       {
	  DATA16 *d, *s;
	  int x;

	  s = src->pixels + offset_y[y];
	  pld(s, 0);
	  pld(offset_x, 0);

	  d = dst_itr;
	  x = 0;
	  while (x < w_align)
	    {
	       pld(s, 32);
	       pld(offset_x + x, 32);

	       UNROLL8({
		  _soft16_pt_blend_solid_solid_mul_color_transp
		    (d, s[offset_x[x]], alpha, r, g, b);
		  x++;
		  d++;
	       });
	    }

	  for (; x < w; x++, d++)
	    _soft16_pt_blend_solid_solid_mul_color_transp
	      (d, s[offset_x[x]], alpha, r, g, b);
       }
}

static void
_soft16_image_draw_scaled_transp_solid_mul_color(Soft16_Image *src,
						 Soft16_Image *dst,
						 RGBA_Draw_Context *dc __UNUSED__,
						 int dst_offset, int w, int h,
						 int *offset_x, int *offset_y,
						 DATA8 r, DATA8 g, DATA8 b,
						 DATA8 alpha)
{
   DATA16 *dst_itr;
   int y, w_align;

   w_align = w & ~7;

   dst_itr = dst->pixels + dst_offset;

   if (alpha == 31)
     for (y = 0; y < h; y++, dst_itr += dst->stride)
       {
	  DATA16 *d, *s;
	  DATA8 *a;
	  int x;

	  s = src->pixels + offset_y[y];
	  a = src->alpha + offset_y[y];
	  pld(s, 0);
	  pld(a, 0);
	  pld(offset_x, 0);

	  d = dst_itr;
	  x = 0;
	  while (x < w_align)
	    {
	       pld(s, 32);
	       pld(a, 8);
	       pld(offset_x + x, 32);

	       UNROLL8({
		  int off_x = offset_x[x];
		  _soft16_pt_blend_transp_solid_mul_color_solid
		    (d, s[off_x], a[off_x], r, g, b);
		  x++;
		  d++;
	       });
	    }

	  for (; x < w; x++, d++)
	    _soft16_pt_blend_transp_solid_mul_color_solid
	      (d, s[offset_x[x]], a[offset_x[x]], r, g, b);
       }
   else
     for (y = 0; y < h; y++, dst_itr += dst->stride)
       {
	  DATA16 *d, *s;
	  DATA8 *a;
	  int x;

	  s = src->pixels + offset_y[y];
	  a = src->alpha + offset_y[y];
	  pld(s, 0);
	  pld(a, 0);
	  pld(offset_x, 0);

	  d = dst_itr;
	  x = 0;
	  while (x < w_align)
	    {
	       pld(s, 32);
	       pld(a, 8);
	       pld(offset_x + x, 32);

	       UNROLL8({
		  int off_x = offset_x[x];
		  _soft16_pt_blend_transp_solid_mul_color_transp
		    (d, s[off_x], a[off_x], alpha, r, g, b);
		  x++;
		  d++;
	       });
	    }

	  for (; x < w; x++, d++)
	    _soft16_pt_blend_transp_solid_mul_color_transp
	      (d, s[offset_x[x]], a[offset_x[x]], alpha, r, g, b);
       }
}

static inline void
_soft16_image_draw_scaled_mul_color(Soft16_Image *src, Soft16_Image *dst,
				    RGBA_Draw_Context *dc,
				    int dst_offset, int w, int h,
				    int *offset_x, int *offset_y,
				    DATA8 r, DATA8 g, DATA8 b, DATA8 a)
{
   if ((src->cache_entry.flags.alpha && src->alpha) && 
       (!dst->cache_entry.flags.alpha))
      _soft16_image_draw_scaled_transp_solid_mul_color
         (src, dst, dc, dst_offset, w, h, offset_x, offset_y, r, g, b, a);
   else if (!dst->cache_entry.flags.alpha)
      _soft16_image_draw_scaled_solid_solid_mul_color
         (src, dst, dc, dst_offset, w, h, offset_x, offset_y, r, g, b, a);
   else
      ERR("Unsupported draw of scaled images src->cache_entry.flags.alpha=%d, "
              "dst->cache_entry.flags.alpha=%d, WITH COLOR MUL 0x%08x",
              src->cache_entry.flags.alpha, dst->cache_entry.flags.alpha, dc->mul.col);
}

static inline void
_soft16_image_draw_scaled_mul(Soft16_Image *src, Soft16_Image *dst,
			      RGBA_Draw_Context *dc,
			      int dst_offset, int w, int h,
			      int *offset_x, int *offset_y, DATA8 r, DATA8 g,
			      DATA8 b, DATA8 a)
{
   if ((a == r) && (a == (g >> 1)) && (a == b))
      _soft16_image_draw_scaled_mul_alpha
	(src, dst, dc, dst_offset, w, h, offset_x, offset_y, a);
   else
      _soft16_image_draw_scaled_mul_color
	(src, dst, dc, dst_offset, w, h, offset_x, offset_y, r, g, b, a);
}

void
evas_common_soft16_image_draw_scaled_sampled(Soft16_Image *src, Soft16_Image *dst,
				 RGBA_Draw_Context *dc,
				 const Eina_Rectangle sr,
				 const Eina_Rectangle dr,
				 const Eina_Rectangle cr)
{
   int x, y, dst_offset, *offset_x, *offset_y;
   DATA16 mul_rgb565;
   DATA8 r, g, b, a;

   if (!dc->mul.use)
     {
	r = b = a = 31;
	g = 63;
	mul_rgb565 = 0xffff;
     }
   else
     {
	a = A_VAL(&dc->mul.col) >> 3;
	if (a == 0)
	  return;

	r = R_VAL(&dc->mul.col) >> 3;
	g = G_VAL(&dc->mul.col) >> 2;
	b = B_VAL(&dc->mul.col) >> 3;

	if (r > a) r = a;
	if (g > (a << 1)) g = (a << 1);
	if (b > a) b = a;

	mul_rgb565 = (r << 11) || (g << 5) | b;
     }

   /* pre-calculated scale tables */
   offset_x = alloca(cr.w * sizeof(*offset_x));
   for (x = 0; x < cr.w; x++)
     offset_x[x] = (((x + cr.x - dr.x) * sr.w) / dr.w) + sr.x;

   offset_y = alloca(cr.h * sizeof(*offset_y));
   for (y = 0; y < cr.h; y++)
     offset_y[y] = (((((y + cr.y - dr.y) * sr.h) / dr.h) + sr.y)
		    * src->stride);

   dst_offset = cr.x + (cr.y * dst->stride);


   if (mul_rgb565 == 0xffff)
     _soft16_image_draw_scaled_no_mul
       (src, dst, dc, dst_offset, cr.w, cr.h, offset_x, offset_y);
   else
     _soft16_image_draw_scaled_mul
       (src, dst, dc, dst_offset, cr.w, cr.h, offset_x, offset_y, r, g, b, a);
}
