
#include "gfd_framebuffer.h"
#include "gfd_font.h"

#include "gfd.h"

#include "gfd_fixedsize_memory.h"
#include "bsk_xdoformat.h"

#include "gfd_p_.h"

int gfd_fb_init(gfd_framebuffer_t* const fb, int width, int height, int pitch, gfd_pixel_t* const buffer)
{
  if (!fb)
    return gfd_err_null_fb;

  if (width<0)
    return gfd_err_inv_width;

  if (height<0)
    return gfd_err_inv_height;

  if (pitch<width)
    return gfd_err_inv_pitch;

  if (!buffer)
    return gfd_err_inv_buffer;

  fb->ox=0;
  fb->oy=0;
  fb->ex=width-1;
  fb->ey=height-1;
  fb->dx=width;
  fb->dy=height;
  fb->pitch=pitch;
  fb->size=(pitch*height);
  fb->buffer=buffer;

  return gfd_ok;
}

int gfd_fb_subframe(gfd_framebuffer_t* const fb, const gfd_framebuffer_t* const src, int ox, int oy, int width, int height)
{
  fb->buffer=&GFD_PIXEL_AT_(src, ox, oy);
  fb->pitch=src->pitch;
  fb->size=fb->pitch*height;
  /*
  fb->ox=-ox;
  fb->oy=-oy;
  fb->ex=fb->ox+width;
  fb->ey=fb->ox+height;
  */
  fb->ox=0;
  fb->oy=0;
  fb->ex=width-1;
  fb->ey=height-1;

  fb->dx=width;
  fb->dy=height;

  return gfd_ok;
}

int gfd_fb_subbbox(gfd_framebuffer_t* const fb, const gfd_framebuffer_t* const src, int ox, int oy, int width, int height)
{
  *fb = *src;

  fb->ox=ox;
  fb->oy=oy;
  fb->ex=ox+width-1;
  fb->ey=oy+height-1;
  fb->dx=width;
  fb->dy=height;

  return gfd_ok;
}

void gfd_fb_clear(gfd_framebuffer_t* const fb)
{
	int y;
	for(y = fb->oy; y < fb->dy; y++)
	{
		void* p = fb_get_pixel_address(fb, 0, y);
		gfd_memzero32(p, fb->dx);
	}

	//bsk_memzero32(fb->buffer, fb->size);
}

void gfd_fb_clear_phy(gfd_framebuffer_t* const fb)
{
	gfd_memzero32(fb->buffer, fb->size);
}

void gfd_fb_fill(gfd_framebuffer_t* const fb, int c)
{
	int y;
	for(y = fb->oy; y < fb->dy; y++)
	{
		void* p = fb_get_pixel_address(fb, 0, y);
		gfd_memset32(p, c, fb->dx);
	}
}

void gfd_fb_set_pixel_null(const gfd_framebuffer_t* const fb, int x, int y, int c)
{
  (void)fb;
  (void)x;
  (void)y;
  (void)c;
  return;
}

void gfd_fb_set_pixel_cs(const gfd_framebuffer_t* const fb, int x, int y, int c)
{
  if (c<2)
    return;
  if (!GFD_CHECK_X(fb, x))
    return;
  if (!GFD_CHECK_Y(fb, y))
    return;

  {
    gfd_pixel_t* p=&GFD_PIXEL_AT_(fb, x, y);
    gfd_pixel_t v=*p;
    *p=v+GFD_PIXEL(c);
  }
}

void gfd_fb_set_pixel(const gfd_framebuffer_t* const fb, int x, int y, int c)
{
  if (!GFD_CHECK_X(fb, x))
    return;
  if (!GFD_CHECK_Y(fb, y))
    return;

  GFD_PIXEL_AT_(fb, x, y)=GFD_PIXEL(c);
}

void gfd_fb_set_pixel2(const gfd_framebuffer_t* const fb, int x, int y, int c)
{
  if (!GFD_CHECK_X2(fb, x))
    return;
  if (!GFD_CHECK_Y2(fb, y))
    return;

  GFD_PIXEL_AT_(fb, x, y)=GFD_PIXEL(c);
}

int gfd_fb_get_pixel(const gfd_framebuffer_t* const fb, int x, int y)
{
  if (!GFD_CHECK_X(fb, x))
    return 0;
  if (!GFD_CHECK_Y(fb, y))
    return 0;

  return GFD_PIXEL_AT_(fb, x, y);
}

int gfd_fb_get_pixel2(const gfd_framebuffer_t* const fb, int x, int y)
{
  if (!GFD_CHECK_X2(fb, x))
    return 0;
  if (!GFD_CHECK_Y2(fb, y))
    return 0;

  return GFD_PIXEL_AT_(fb, x, y);
}

gfd_pixel_t* gfd_fb_get_pixel_address(const gfd_framebuffer_t* const fb, int x, int y)
{
  if (!GFD_CHECK_X(fb, x))
    return 0;
  if (!GFD_CHECK_Y(fb, y))
    return 0;

  return &GFD_PIXEL_AT_(fb, x, y);
}

int gfd_fb_fill_rect(gfd_framebuffer_t* const fb, int sx, int sy, int w, int h, int c)
{
  int ex;
  int ey;
  int y;
  gfd_pixel_t v;

  if (sx>fb->ex)
    return gfd_ok;
  if (sy>fb->ey)
    return gfd_ok;

  if (sx<fb->ox)
  {
    w-=fb->ox-sx;
    sx=fb->ox;
  }

  if (sy<fb->oy)
  {
    h-=fb->oy-sy;
    sy=fb->oy;
  }

  ex=(sx+w)-1;
  if (ex>fb->ex)
    ex=fb->ex;

  ey=(sy+h)-1;
  if (ey>fb->ey)
    ey=fb->ey;

  v=GFD_PIXEL(c);

  for(y=sy; y<=ey; ++y)
  {
    gfd_pixel_t* const p=fb_get_row(fb, y);
    int x;
    for(x=sx; x<=ex; ++x)
      fb_set_pixel_row_no_clip(fb, p, x, v);
  }

  return gfd_ok;
}

int gfd_fb_fill_rect_bordered(gfd_framebuffer_t* const fb, int sx, int sy, int w, int h, int c, int border_c)
{
  int ex;
  int ey;
  int y;
  gfd_pixel_t v;
  gfd_pixel_t vb;
  int sx_=sx;
  int sy_=sy;
  int ex_=(sx_+w)-1;
  int ey_=(sy_+h)-1;

  if (sx>=fb->ex)
    return gfd_ok;
  if (sy>=fb->ey)
    return gfd_ok;

  if (sx<fb->ox)
  {
    w-=fb->ox-sx;
    sx=fb->ox;
  }
  if (sy<fb->oy)
  {
    h-=fb->oy-sy;
    sy=fb->oy;
  }

  ex=(sx+w)-1;
  if (ex>fb->ex)
    ex=fb->ex;

  ey=(sy+h)-1;
  if (ey>fb->ey)
    ey=fb->ey;

  v=GFD_PIXEL(c);
  vb=GFD_PIXEL(border_c);

  for(y=sy; y<=ey; ++y)
  {
    int x;
    gfd_pixel_t* const p=fb_get_row(fb, y);
    if (y==sy_ || y==ey_)
    {
      for(x=sx; x<=ex; ++x)
        fb_set_pixel_row_no_clip(fb, p, x, vb); /*p[x]=vb;*/
    }
    else
    {
      for(x=sx; x<=ex; ++x)
        fb_set_pixel_row_no_clip(fb, p, x, v); /*p[x]=v;*/

      if (sx==sx_)
        fb_set_pixel_row_no_clip(fb, p, sx_, vb); /*p[sx_]=vb;*/
      if (ex>=ex_)
        fb_set_pixel_row_no_clip(fb, p, ex, vb); /*p[ex_]=vb;*/
    }
  }

  return gfd_ok;
}

int gfd_fb_fill_rect_cs(gfd_framebuffer_t* const fb, int sx, int sy, int w, int h, int c)
{
  int ex;
  int ey;
  int y;
  gfd_pixel_t v;

  if (sx>=fb->ex)
    return gfd_ok;
  if (sy>=fb->ey)
    return gfd_ok;

  if (sx<fb->ox)
  {
    w-=fb->ox-sx;
    sx=fb->ox;
  }
  if (sy<fb->oy)
  {
    h-=fb->oy-sy;
    sy=fb->oy;
  }

  ex=sx+w;
  if (ex>fb->ex)
    ex=fb->ex;

  ey=sy+h;
  if (ey>fb->ey)
    ey=fb->ey;

  v=GFD_PIXEL(c);

  for(y=sy; y<ey; ++y)
  {
    gfd_pixel_t* const p=fb_get_row(fb, y);
    int x;
    for(x=sx; x<ex; ++x)
      fb_set_pixel_row_no_clip(fb, p, x, fb_get_pixel_row_no_clip(fb, p, x)+v); /*p[x]=p[x]+v;*/
  }

  return gfd_ok;
}

//Font


void gfd_fb_draw_hline(gfd_framebuffer_t* const fb, int sx, int sy, int ex, int color)
{
  int x;
  if (sy>fb->ey)
    return;

  if (sy<fb->oy)
    return;

  if (sx>ex)
    gfd_swap(sx, ex);

  if (ex<fb->ox)
    return;

  if (sx>=fb->ex)
    return;
  if (sx<fb->ox)
    sx=fb->ox;

  if (ex>fb->ex)
    ex=fb->ex;

  {
    gfd_pixel_t* const p=fb_get_row(fb, sy);
    gfd_pixel_t c=GFD_PIXEL(color);
    for(x=sx; x<=ex; ++x)
      fb_set_pixel_row_no_clip(fb, p, x, c); /*p[x]=c;*/
  }
}

void gfd_fb_draw_vline(gfd_framebuffer_t* const fb, int sx, int sy, int ey, int color)
{
  int y;
  if (sx>fb->ex)
    return;

  if (sx<fb->ox)
    return;

  if (sy>ey)
    gfd_swap(sy, ey);

  if (ey<fb->oy)
    return;

  if (sy>fb->ey)
    return;

  if (sy<fb->oy)
    sy=fb->oy;

  if (ey>fb->ey)
    ey=fb->ey;

  {
    gfd_pixel_t* p=fb_get_pixel_address(fb, sx, sy);
    gfd_pixel_t c=GFD_PIXEL(color);
    for(y=sy; y<=ey; ++y)
    {
      //fb_set_pixel_row_no_clip(fb, p, x, c); /*p[x]=c;*/
      *p=c;
      p+=fb->pitch;
    }
  }
}

void gfd_fb_draw_hline_lim(gfd_framebuffer_t* const fb, int sx, int sy, int ex, int color)
{
  if (sy>fb->ey)
    sy=fb->ey;

  if (sy<fb->oy)
    sy=fb->oy;

  gfd_fb_draw_hline(fb, sx, sy, ex, color);
}

void gfd_fb_draw_vline_lim(gfd_framebuffer_t* const fb, int sx, int sy, int ey, int color)
{
  if (sx>fb->ex)
    sx=fb->ex;

  if (sx<fb->ox)
    sx=fb->ox;

  gfd_fb_draw_vline(fb, sx, sy, ey, color);
}

void gfd_fb_blit(gfd_framebuffer_t* const fb, int x, int y, const gfd_framebuffer_t* const src, int w, int h, int src_x, int src_y)
{
  int dh=fb->dy;
  int dw=fb->dx;
  int sh=src->dy;
  int sw=src->dx;

  dh=dh-y;
  dw=dw-x;
  sh=sh-src_y;
  sw=sw-src_x;

  if (h==0)
    h=dh;
  if (w==0)
    w=dw;

  h=gfd_min(h, gfd_min(dh, sh));
  w=gfd_min(w, gfd_min(dw, sw));

  {
    int dy=src_y-y;
    int yh=y+h;
    int y1;
    for(y1=y; y1<yh; ++y1)
      gfd_memcpy_opt32(fb_get_pixel_address(fb, x, y1), fb_get_pixel_address(src, src_x, y1+dy), w);
  }
}

void gfd_fb_draw_sprite(gfd_framebuffer_t* const fb, int x, int y, gfd_framebuffer_t* const src, int color, int drawOcclusion)
{
  signed int dh=fb->dy;
  signed int dw=fb->dx;
  signed int sh=src->dy;
  signed int sw=src->dx;
  gfd_pixel_t c=GFD_PIXEL(color);
  int fix_x=0;
  int fix_y=0;

  if (x>=fb->ex)
    return;
  if (y>=fb->ey)
    return;

  if (y<fb->oy)
  {
    fix_y=fb->oy-y;
    sh=sh-fix_y;
    y=fb->oy;
  }
  if (x<fb->ox)
  {
    fix_x=fb->ox-x;
    sw=sw-fix_x;
    x=fb->ox;
  }

  dh=dh-y;
  dw=dw-x;

  if (sh>dh)
    sh=dh;
  if (sw>dw)
    sw=dw;

  {
    int sx;
    int sy;

    if(drawOcclusion == 1)
    {
		for(sy=0; sy<sh; ++sy)
		{
		  gfd_pixel_t* d=fb_get_pixel_address(fb, x, y+sy);
		  gfd_pixel_t* s=fb_get_pixel_address(src, fix_x, fix_y+sy);

		  for(sx=0; sx<sw; ++sx)
		  {
			if (s[sx]==1)
			  d[sx]=1;
			else if (s[sx]>1)
			  d[sx]=c;
		  }
		}
    }
    else
    {
    	for(sy=0; sy<sh; ++sy)
		{
		  gfd_pixel_t* d=fb_get_pixel_address(fb, x, y+sy);
		  gfd_pixel_t* s=fb_get_pixel_address(src, fix_x, fix_y+sy);

		  for(sx=0; sx<sw; ++sx)
		  {
			if (s[sx] > 1)
			  d[sx]=c;
		  }
		}
    }
  }
}

void gfd_fb_draw_sprite_occ_ex(gfd_framebuffer_t* const fb, int x, int y, gfd_framebuffer_t* const src, int color, int drawOcclusion)
{
  signed int dh=fb->dy;
  signed int dw=fb->dx;
  signed int sh=src->dy;
  signed int sw=src->dx;
  gfd_pixel_t c=GFD_PIXEL(color);
  int fix_x=0;
  int fix_y=0;

  if (x>=fb->ex)
    return;
  if (y>=fb->ey)
    return;

  if (y<fb->oy)
  {
    fix_y=fb->oy-y;
    sh=sh-fix_y;
    y=fb->oy;
  }
  if (x<fb->ox)
  {
    fix_x=fb->ox-x;
    sw=sw-fix_x;
    x=fb->ox;
  }

  dh=dh-y;
  dw=dw-x;

  if (sh>dh)
    sh=dh;
  if (sw>dw)
    sw=dw;

  {
    int sx;
    int sy;

    if(drawOcclusion == 1)
    {
        for(sy=0; sy<sh; ++sy)
        {
          gfd_pixel_t* d=fb_get_pixel_address(fb, x, y+sy);
          gfd_pixel_t* s=fb_get_pixel_address(src, fix_x, fix_y+sy);

          for(sx=0; sx<sw; ++sx)
          {
            if (s[sx]==1)
              d[sx]=1;
          }
        }
    }
    else
    {
        for(sy=0; sy<sh; ++sy)
        {
          gfd_pixel_t* d=fb_get_pixel_address(fb, x, y+sy);
          gfd_pixel_t* s=fb_get_pixel_address(src, fix_x, fix_y+sy);

          for(sx=0; sx<sw; ++sx)
          {
            if (s[sx] > 1)
              d[sx]=c;
          }
        }
    }
  }
}

void gfd_fb_draw_sprite_cs(gfd_framebuffer_t* const fb, int x, int y, gfd_framebuffer_t* const src, int color)
{
  signed int dh=fb->dy;
  signed int dw=fb->dx;
  signed int sh=src->dy;
  signed int sw=src->dx;
  gfd_pixel_t c=GFD_PIXEL(color);
  int fix_x=0;
  int fix_y=0;

  if (x>=fb->ex)
    return;
  if (y>=fb->ey)
    return;

  if (y<fb->oy)
  {
    fix_y=fb->oy-y;
    sh=sh-fix_y;
    y=fb->oy;
  }
  if (x<fb->ox)
  {
    fix_x=fb->ox-x;
    sw=sw-fix_x;
    x=fb->ox;
  }

  dh=dh-y;
  dw=dw-x;

  if (sh>dh)
    sh=dh;
  if (sw>dw)
    sw=dw;

  {
    int sx;
    int sy;

    for(sy=0; sy<sh; ++sy)
    {
      gfd_pixel_t* d=fb_get_pixel_address(fb, x, y+sy);
      gfd_pixel_t* s=fb_get_pixel_address(src, fix_x, fix_y+sy);

      for(sx=0; sx<sw; ++sx)
      {
    	  if ((d[sx]==0) || (d[sx]>31))
    	  {
    	      if (s[sx]==1)
    	          d[sx]=1;
    	      else if (s[sx]>1)
    			  d[sx]=c;
    	  }
#if 0
        if (s[sx]==1)
          d[sx]=1;

        if (s[sx]>1)
        {
          d[sx]+=GFD_PIXEL(c);
        }
#endif
      }
    }
  }
}

int gfd_fb_make_occlusion(gfd_framebuffer_t* const fb, int color)
{
  int x;
  int y;

  for(y=0; y<fb->dy; ++y)
    for(x=0; x<fb->dx; ++x)
    {
      if (!gfd_fb_get_pixel(fb, x, y))
      {
        int u=gfd_fb_get_pixel(fb, x, y-1)>color;
        int b=gfd_fb_get_pixel(fb, x, y+1)>color;

        int l=gfd_fb_get_pixel(fb, x-1, y)>color;
        int r=gfd_fb_get_pixel(fb, x+1, y)>color;

        int ul=gfd_fb_get_pixel(fb, x-1, y-1)>color;
        int ur=gfd_fb_get_pixel(fb, x+1, y-1)>color;

        int bl=gfd_fb_get_pixel(fb, x-1, y+1)>color;
        int br=gfd_fb_get_pixel(fb, x+1, y+1)>color;

        if (u || b || l || r || ul || ur || bl || br)
          gfd_fb_set_pixel(fb, x, y, color);
      }
    }

  return gfd_ok;
}

int gfd_fb_make_occlusion_mask(gfd_framebuffer_t* const fb)
{
  int x;
  int y;
  int c;

  for(y=0; y<fb->dy; ++y)
    for(x=0; x<fb->dx; ++x)
    {
      c = gfd_fb_get_pixel2(fb, x, y);
      if (c==0)
      {
        int u=gfd_fb_get_pixel2(fb, x, y-1)==2;
        int b=gfd_fb_get_pixel2(fb, x, y+1)==2;

        int l=gfd_fb_get_pixel2(fb, x-1, y)==2;
        int r=gfd_fb_get_pixel2(fb, x+1, y)==2;

        int ul=gfd_fb_get_pixel2(fb, x-1, y-1)==2;
        int ur=gfd_fb_get_pixel2(fb, x+1, y-1)==2;

        int bl=gfd_fb_get_pixel2(fb, x-1, y+1)==2;
        int br=gfd_fb_get_pixel2(fb, x+1, y+1)==2;

        if (u || b || l || r || ul || ur || bl || br)
          gfd_fb_set_pixel2(fb, x, y, 1);
      }
      else if(c==3)
      {
    	gfd_fb_set_pixel2(fb, x, y, 0);
      }
    }

  return gfd_ok;
}


int gfd_fb_despackle_mask(gfd_framebuffer_t* const fb)
{
  int x;
  int y;

  for(y=0; y<fb->dy; ++y)
    for(x=0; x<fb->dx; ++x)
    {
      int u=gfd_fb_get_pixel2(fb, x, y-1)==2;
      int b=gfd_fb_get_pixel2(fb, x, y+1)==2;

      int l=gfd_fb_get_pixel2(fb, x-1, y)==2;
      int r=gfd_fb_get_pixel2(fb, x+1, y)==2;

      if (u && b && l && r )
        gfd_fb_set_pixel2(fb, x, y, 2);
    }

  return gfd_ok;
}

void gfd_fb_draw_line(gfd_framebuffer_t* const fb, int x1, int y1, int x2, int y2, int color)
{
  long XDelta,YDelta,M,Y,X;

  if (x1==x2)
  {
    gfd_fb_draw_vline(fb, x1, y1, y2, color);
    return;
  }
  if (y1==y2)
  {
    gfd_fb_draw_hline(fb, x1, y1, x2, color);
  }

  {
   int x, y, a;

   XDelta=((x2-x1)<<8);
   YDelta=((y2-y1)<<8);

   if(!(XDelta | YDelta))
           return;

   if(gfd_abs(XDelta)>=gfd_abs(YDelta)) {
      M=(YDelta<<8)/XDelta;
      Y=(y1<<8);
      if(XDelta>=0) {
         for(a=x1;a<=x2;a++,Y+=M) {
            y=Y>>8;

            /*
            if (do_clip)
              {
                if (a<fb->ox)
                  continue;
                if (a>=fb->ex)
                  break;
                if (y<fb->oy)
                  continue;
                if (y>>fb->ey)
                  continue;
              }
            */
            gfd_fb_set_pixel(fb, a, y, color);
         }
      }else {
         for(a=x1;a>=x2;a--,Y-=M) {
            y=Y>>8;

            /*
            if (do_clip)
              {
                if (a<clip_bsx)
                  break;
                if (a>clip_bex)
                  continue;
                if (y<clip_bsy)
                  continue;
                if (y>clip_bey)
                  continue;
              }

            RawPutPixelSrc_(this, a, y, color, rop);
            */
            gfd_fb_set_pixel(fb, a, y, color);

         }
      }
      return;
   } else {
      M=(XDelta<<8)/YDelta;
      X=(x1<<8);
      if(YDelta>=0) {
         for(a=y1;a<=y2;a++, X+=M) {
            x=X>>8;

            /*
            if (do_clip)
              {
                if (x<clip_bsx)
                  continue;
                if (x>clip_bex)
                  continue;
                if (a<clip_bsy)
                  continue;
                if (a>clip_bey)
                  break;
              }

            RawPutPixelSrc_(this, x, a, color, rop);
            */
            gfd_fb_set_pixel(fb, x, a, color);
         }
      }else {
         for(a=y1;a>=y2;a--,X-=M) {
            x=X>>8;

            /*
            if (do_clip)
              {
                if (x<clip_bsx)
                  continue;
                if (x>clip_bex)
                  continue;
                if (a<clip_bsy)
                  break;
                if (a>clip_bey)
                  continue;
              }

            RawPutPixelSrc_(this, x, a, color, rop);
            */
            gfd_fb_set_pixel(fb, x, a, color);

         }
      }
   }
  }

  return;

}

void gfd_fb_draw_line_cs(gfd_framebuffer_t* const fb, int x1, int y1, int x2, int y2, int color)
{
  long XDelta,YDelta,M,Y,X;

  /*
  if (x1==x2)
  {
    gfd_fb_draw_vline(fb, x1, y1, y2, color);
    return;
  }
  if (y1==y2)
  {
    gfd_fb_draw_hline(fb, x1, y1, x2, color);
  }
  */

  {
   int x, y, a;

   XDelta=((x2-x1)<<8);
   YDelta=((y2-y1)<<8);

   if(!(XDelta | YDelta))
           return;

   if(gfd_abs(XDelta)>=gfd_abs(YDelta)) {
      M=(YDelta<<8)/XDelta;
      Y=(y1<<8);
      if(XDelta>=0) {
         for(a=x1;a<=x2;a++,Y+=M) {
            y=Y>>8;

            gfd_fb_set_pixel_cs(fb, a, y, color);
         }
      }else {
         for(a=x1;a>=x2;a--,Y-=M) {
            y=Y>>8;

            gfd_fb_set_pixel_cs(fb, a, y, color);

         }
      }
      return;
   } else {
      M=(XDelta<<8)/YDelta;
      X=(x1<<8);
      if(YDelta>=0) {
         for(a=y1;a<=y2;a++, X+=M) {
            x=X>>8;

            gfd_fb_set_pixel_cs(fb, x, a, color);
         }
      }else {
         for(a=y1;a>=y2;a--,X-=M) {
            x=X>>8;

            gfd_fb_set_pixel_cs(fb, x, a, color);

         }
      }
   }
  }

  return;
}

void gfd_fb_draw_line_cs_2cols(gfd_framebuffer_t* const fb, int x1, int y1, int x2, int y2, int color1, int color2)
{
  long XDelta,YDelta,M,Y,X;
  int x, y, a;

  XDelta=((x2-x1)<<8);
  YDelta=((y2-y1)<<8);

  if(!(XDelta | YDelta))
    return;

  if(gfd_abs(XDelta)>=gfd_abs(YDelta))
  {
    M=(YDelta<<8)/XDelta;
    Y=(y1<<8);
    if(XDelta>=0)
    {
      for(a=x1;a<=x2;a++,Y+=M)
      {
          y=Y>>8;
          gfd_fb_set_pixel(fb, a, y, a&1?color1:color2);
      }
    }
    else
    {
      for(a=x1;a>=x2;a--,Y-=M)
      {
        y=Y>>8;
        gfd_fb_set_pixel(fb, a, y, a&1?color1:color2);
      }
    }
  }
  else
  {
    M=(XDelta<<8)/YDelta;
    X=(x1<<8);
    if(YDelta>=0)
    {
       for(a=y1;a<=y2;a++, X+=M)
       {
          x=X>>8;
          gfd_fb_set_pixel(fb, x, a, a&1?color1:color2);
       }
    }
    else
    {
      for(a=y1;a>=y2;a--,X-=M)
      {
        x=X>>8;
        gfd_fb_set_pixel(fb, x, a, a&1?color1:color2);
      }
    }
  }
}

void gfd_fb_draw_line_w2(gfd_framebuffer_t* const fb, int x1, int y1, int x2, int y2, int color, int occolor, bool occlusion)
{
	long XDelta, YDelta, M, Y, X;

	int show = gfd_line_clip(fb, &x1, &y1, &x2, &y2);
	if (!show)
		return;

	{
		int x=0;
		int y=0;
		int a;
		gfd_pixel_t c = GFD_PIXEL(color);
		gfd_pixel_t oc = GFD_PIXEL(occolor);

		XDelta = ((x2 - x1) << 8);
		YDelta = ((y2 - y1) << 8);

		if(!(XDelta | YDelta))
			return;

		if(occlusion)
		{
          if(gfd_abs(XDelta) >= gfd_abs(YDelta))
          {

              M = ((YDelta) << 8) / XDelta;
              Y = (y1 << 8);

              if(XDelta >= 0)
              {
                  fb_set_pixel_no_clip(fb, x1 - 1, y1,     oc);
                  fb_set_pixel_no_clip(fb, x1 - 1, y1 + 1, oc);
                  for(a = x1; a <= x2; a++,Y += M)
                  {
                      y = Y >> 8;

                      fb_set_pixel_no_clip(fb, a, y,     c);
                      fb_set_pixel_no_clip(fb, a, y + 1, c);
                      fb_set_pixel_no_clip(fb, a, y + 2, oc);
                      fb_set_pixel_no_clip(fb, a, y - 1, oc);
                  }
              }
              else
              {
                  fb_set_pixel_no_clip(fb, x1 + 1, y1,     oc);
                  fb_set_pixel_no_clip(fb, x1 + 1, y1 + 1, oc);
                  for(a = x1; a >= x2; a--, Y -= M)
                  {
                      y = Y >> 8;

                      fb_set_pixel_no_clip(fb, a, y,     c);
                      fb_set_pixel_no_clip(fb, a, y + 1, c);
                      fb_set_pixel_no_clip(fb, a, y + 2, oc);
                      fb_set_pixel_no_clip(fb, a, y - 1, oc);
                  }
              }
              fb_set_pixel_no_clip(fb, a, y,     oc);
              fb_set_pixel_no_clip(fb, a, y + 1, oc);
          }
          else
          {

              M = ((XDelta) << 8) / YDelta;
              X = (x1 << 8);

              if(YDelta >= 0)
              {
                  fb_set_pixel_no_clip(fb, x1,     y1 - 1, oc);
                  fb_set_pixel_no_clip(fb, x1 + 1, y1 - 1, oc);
                  for(a = y1; a <= y2; a++, X += M)
                  {
                      x = X >> 8;

                      fb_set_pixel_no_clip(fb, x,     a, c);
                      fb_set_pixel_no_clip(fb, x + 1, a, c);
                      fb_set_pixel_no_clip(fb, x + 2, a, oc);
                      fb_set_pixel_no_clip(fb, x - 1, a, oc);
                  }
              }
              else
              {
                  fb_set_pixel_no_clip(fb, x1,     y1 + 1, oc);
                  fb_set_pixel_no_clip(fb, x1 + 1, y1 + 1, oc);
                  for(a = y1; a >= y2; a--, X -= M)
                  {
                      x = X >> 8;

                      fb_set_pixel_no_clip(fb, x,     a, c);
                      fb_set_pixel_no_clip(fb, x + 1, a, c);
                      fb_set_pixel_no_clip(fb, x + 2, a, oc);
                      fb_set_pixel_no_clip(fb, x - 1, a, oc);
                  }
              }
              fb_set_pixel_no_clip(fb, x,     a, oc);
              fb_set_pixel_no_clip(fb, x + 1, a, oc);
          }
		}
		else
        {
          if(gfd_abs(XDelta) >= gfd_abs(YDelta))
          {

              M = ((YDelta) << 8) / XDelta;
              Y = (y1 << 8);

              if(XDelta >= 0)
              {
                  for(a = x1; a <= x2; a++,Y += M)
                  {
                      y = Y >> 8;

                      fb_set_pixel_no_clip(fb, a, y,     c);
                      fb_set_pixel_no_clip(fb, a, y + 1, c);
                  }
              }
              else
              {
                  for(a = x1; a >= x2; a--, Y -= M)
                  {
                      y = Y >> 8;

                      fb_set_pixel_no_clip(fb, a, y,     c);
                      fb_set_pixel_no_clip(fb, a, y + 1, c);
                  }
              }
          }
          else
          {

              M = ((XDelta) << 8) / YDelta;
              X = (x1 << 8);

              if(YDelta >= 0)
              {
                  for(a = y1; a <= y2; a++, X += M)
                  {
                      x = X >> 8;

                      fb_set_pixel_no_clip(fb, x,     a, c);
                      fb_set_pixel_no_clip(fb, x + 1, a, c);
                  }
              }
              else
              {
                  for(a = y1; a >= y2; a--, X -= M)
                  {
                      x = X >> 8;

                      fb_set_pixel_no_clip(fb, x,     a, c);
                      fb_set_pixel_no_clip(fb, x + 1, a, c);
                  }
              }
          }
        }
	}

	return;
}

void gfd_fb_draw_line_w1(gfd_framebuffer_t* const fb, int x1, int y1, int x2, int y2, int color, int occolor, bool occlusion)
{
    long XDelta, YDelta, M, Y, X;

    int show = gfd_line_clip(fb, &x1, &y1, &x2, &y2);
    if (!show)
        return;

    {
        int x=0;
        int y=0;
        int a=0;

        gfd_pixel_t c = GFD_PIXEL(color);
        gfd_pixel_t oc = GFD_PIXEL(occolor);

        XDelta = ((x2 - x1) << 8);
        YDelta = ((y2 - y1) << 8);

        if(!(XDelta | YDelta))
            return;

        if(occlusion)
        {
          if(gfd_abs(XDelta) >= gfd_abs(YDelta))
          {

              if(YDelta >= 0)
                M = ((YDelta + 127) << 8) / XDelta;
              else
                M = ((YDelta - 127) << 8) / XDelta;
              if(y1 >= 0)
                Y = (y1 << 8) + 127;
              else
                Y = (y1 << 8) - 127;

              if(XDelta >= 0)
              {
                  fb_set_pixel_no_clip(fb, x1 - 1, y1,     oc);
                  for(a = x1; a <= x2; a++,Y += M)
                  {
                      y = Y >> 8;

                      fb_set_pixel_no_clip(fb, a, y,     c);
                      fb_set_pixel_no_clip(fb, a, y + 1, oc);
                      fb_set_pixel_no_clip(fb, a, y - 1, oc);
                  }
              }
              else
              {
                  fb_set_pixel_no_clip(fb, x1 + 1, y1,     oc);
                  for(a = x1; a >= x2; a--, Y -= M)
                  {
                      y = Y >> 8;

                      fb_set_pixel_no_clip(fb, a, y,     c);
                      fb_set_pixel_no_clip(fb, a, y + 1, oc);
                      fb_set_pixel_no_clip(fb, a, y - 1, oc);
                  }
              }
              fb_set_pixel_no_clip(fb, a, y,     oc);
          }
          else
          {
              if(XDelta >= 0)
                M = ((XDelta + 127) << 8) / YDelta;
              else
                M = ((XDelta - 127) << 8) / YDelta;
              if(x1 >= 0)
                X = (x1 << 8) + 127;
              else
                X = (x1 << 8) - 127;

              if(YDelta >= 0)
              {
                  fb_set_pixel_no_clip(fb, x1,     y1 - 1, oc);
                  for(a = y1; a <= y2; a++, X += M)
                  {
                      x = X >> 8;

                      fb_set_pixel_no_clip(fb, x,     a, c);
                      fb_set_pixel_no_clip(fb, x + 1, a, oc);
                      fb_set_pixel_no_clip(fb, x - 1, a, oc);
                  }
              }
              else
              {
                  fb_set_pixel_no_clip(fb, x1,     y1 + 1, oc);
                  for(a = y1; a >= y2; a--, X -= M)
                  {
                      x = X >> 8;

                      fb_set_pixel_no_clip(fb, x,     a, c);
                      fb_set_pixel_no_clip(fb, x + 1, a, oc);
                      fb_set_pixel_no_clip(fb, x - 1, a, oc);
                  }
              }
              fb_set_pixel_no_clip(fb, x,     a, oc);
          }
        }
        else
        {
          if(gfd_abs(XDelta) >= gfd_abs(YDelta))
          {
              if(YDelta >= 0)
                M = ((YDelta + 127) << 8) / XDelta;
              else
                M = ((YDelta - 127) << 8) / XDelta;
              if(y1 >= 0)
                Y = (y1 << 8) + 127;
              else
                Y = (y1 << 8) - 127;

              if(XDelta >= 0)
              {
                  for(a = x1; a <= x2; a++,Y += M)
                  {
                      y = Y >> 8;

                      fb_set_pixel_no_clip(fb, a, y,     c);
                  }
              }
              else
              {
                  for(a = x1; a >= x2; a--, Y -= M)
                  {
                      y = Y >> 8;

                      fb_set_pixel_no_clip(fb, a, y,     c);
                  }
              }
          }
          else
          {
              if(XDelta >= 0)
                M = ((XDelta + 127) << 8) / YDelta;
              else
                M = ((XDelta - 127) << 8) / YDelta;
              if(x1 >= 0)
                X = (x1 << 8) + 127;
              else
                X = (x1 << 8) - 127;

              if(YDelta >= 0)
              {
                  for(a = y1; a <= y2; a++, X += M)
                  {
                      x = X >> 8;

                      fb_set_pixel_no_clip(fb, x,     a, c);
                  }
              }
              else
              {
                  for(a = y1; a >= y2; a--, X -= M)
                  {
                      x = X >> 8;

                      fb_set_pixel_no_clip(fb, x,     a, c);
                  }
              }
          }
        }
    }

    return;
}

static __inline int to_int(double x)
{
  return x<0 ? (int)(x-0.5) : (int)(x+0.5);
}

static void math_line_set_pixel(gfd_framebuffer_t* const fb, int x, int y, int w, int color)
{
  int i;
  int j;

  for(i=0; i<w; ++i)
    for(j=0; j<w; ++j)
    {
      gfd_fb_set_pixel(fb, x-j, y-i, color);
    }
}

void gfd_fb_draw_math_line(gfd_framebuffer_t* const fb, int x1, int y1, int x2, int y2, int width, int color)
{
  if (0) //TODO: gfd_line_clip(fb, &x1, &y1, &x2, &y2))
  {
    double dx=x1-x2;
    double dy=y1-y2;
    double abs_dx=dx<0 ? -dx : dx;
    double abs_dy=dy<0 ? -dy : dy;
    double m;

    if (abs_dx>=abs_dy)
    {
      int x;
      double y=y1;

      if ((x1-x2)==0)
        return;
      m=dy/dx;
      if (x1<x2)
      {
        for(x=x1; x<=x2; ++x)
        {
          math_line_set_pixel(fb, x, to_int(y), width, color);
          y+=m;
        }
      }
      else
      {
        for(x=x1; x>=x2; --x)
        {
          math_line_set_pixel(fb, x, to_int(y), width, color);
          y-=m;
        }
      }
    }
    else
    {
      int y;
      double x=x1;

      if ((y1-y2)==0)
        return;
      m=dx/dy;
      if (y1<y2)
      {
        for(y=y1; y<=y2; ++y)
        {
          math_line_set_pixel(fb,to_int(x), y, width, color);
          x+=m;
        }
      }
      else
      {
        for(y=y1; y>=y2; --y)
        {
          math_line_set_pixel(fb, to_int(x), y, width, color);
          x-=m;
        }
      }
    }
  }
}

void gfd_fb_flood_triangle(gfd_framebuffer_t* const fb, int sx, int sy, int color)
{
  int x;
  int y;
  int k=1;
  int kx=sx;

  for(y=sy; (y<fb->dy) && k; ++y)
  {
    int nkx=kx;
    k=0;

    for(x=kx; x<fb->dx; ++x)
    {
      if (gfd_fb_get_pixel(fb, x, y))
      {
        break;
      }
      ++k;
      gfd_fb_set_pixel(fb, x, y, color);
      if (!gfd_fb_get_pixel(fb, x, y+1))
        nkx=x;
    }
    for(x=kx-1; x>=fb->ox; --x)
    {
      if (gfd_fb_get_pixel(fb, x, y))
      {
        break;
      }
      ++k;
      gfd_fb_set_pixel(fb, x, y, color);
      if (!gfd_fb_get_pixel(fb, x, y+1))
        nkx=x;
    }
    kx=nkx;
  }
  k=1;
  kx=sx;
  for(y=sy-1; (y>=fb->oy) && k; --y)
  {
    int nkx=kx;
    k=0;
    for(x=kx; x<fb->dx; ++x)
    {
      if (gfd_fb_get_pixel(fb, x, y))
      {
        break;
      }
      ++k;
      gfd_fb_set_pixel(fb, x, y, color);
      if (!gfd_fb_get_pixel(fb, x, y-1))
        nkx=x;
    }
    for(x=kx-1; x>=fb->ox; --x)
    {
      if (gfd_fb_get_pixel(fb, x, y))
      {
        break;
      }
      ++k;
      gfd_fb_set_pixel(fb, x, y, color);
      if (!gfd_fb_get_pixel(fb, x, y-1))
        nkx=x;
    }
    kx=nkx;
  }
}

void gfd_fb_draw_rect(gfd_framebuffer_t* const fb, int sx, int sy, int ex, int ey, int color)
{
	gfd_fb_draw_hline(fb, sx, sy, ex, color);
	gfd_fb_draw_hline(fb, sx, ey, ex, color);
	gfd_fb_draw_vline(fb, sx, sy, ey, color);
	gfd_fb_draw_vline(fb, ex, sy, ey, color);
}

void gfd_fb_draw_rect_limH(gfd_framebuffer_t* const fb, int sx, int sy, int ex, int ey, int color)
{
  if(!((sy >= (fb->ey-1)) ||
          (ey <= (fb->oy+1))))
  {
    gfd_fb_draw_hline_lim(fb, sx, sy, ex, color);
    gfd_fb_draw_hline_lim(fb, sx, ey, ex, color);
    gfd_fb_draw_vline(fb, sx, sy, ey, color);
    gfd_fb_draw_vline(fb, ex, sy, ey, color);
  }
}

void gfd_fb_draw_rect_limV(gfd_framebuffer_t* const fb, int sx, int sy, int ex, int ey, int color)
{
  if(!((sx >= (fb->ex-1)) ||
        (ex <= (fb->ox+1))))
  {
    gfd_fb_draw_hline(fb, sx, sy, ex, color);
    gfd_fb_draw_hline(fb, sx, ey, ex, color);
    gfd_fb_draw_vline_lim(fb, sx, sy, ey, color);
    gfd_fb_draw_vline_lim(fb, ex, sy, ey, color);
  }
}

void gfd_fb_draw_rect_cs(gfd_framebuffer_t* const fb, int sx, int sy, int ex, int ey, int color)
{
	gfd_fb_draw_line_cs(fb, sx, sy, ex, sy, color);
	gfd_fb_draw_line_cs(fb, sx, ey, ex, ey, color);
	gfd_fb_draw_line_cs(fb, sx, sy, sx, ey, color);
	gfd_fb_draw_line_cs(fb, ex, sy, ex, ey, color);
}

#include <stdarg.h>
#include <bsk_xvprintf.h>

struct fb_printf_cx_t
{
  gfd_framebuffer_t* fb;
  const gfd_font256_t* font;
  int x;
  int y;
  int color;
  int dx;
  int fntx;
  int prevCh;
  int dy;
  int out_pixels;

  int measure_only;

  int ox;
  int oy;

};

static int fb_outch(char c, void* const cookie) /*0= ok, 1=interrupt*/
{
  struct fb_printf_cx_t* cx=(struct fb_printf_cx_t*)cookie;
  gfd_framebuffer_t fb;
  unsigned char x=(unsigned char)c;
  gfd_pixel_t* p=(gfd_pixel_t*)cx->font->map[(unsigned int)x].glyphs;
  int dx=cx->dx;
  int fntx=cx->fntx;
  if (p)
  {
    if (c=='.' && cx->font->special_dot_width)
    {
      /*
      int w=cx->font->special_dot_width;
      gfd_fb_init(&fb, w, cx->dy, cx->dx, &p[cx->font->special_dot_shift]);
      gfd_fb_draw_sprite(cx->fb, cx->x, cx->y, &fb, cx->color);
      cx->x+=w; //f.map[i].w;
      cx->out_pixels+=w;
      */
      p=&p[cx->font->special_dot_shift];
      dx=cx->font->special_dot_width;
      //fntx = dx-cx->font->backx;
      fntx = dx-1;
      if(cx->prevCh != -1 && cx->prevCh !='.')
      {
    	  cx->x-=cx->font->backx;
    	  cx->out_pixels-=cx->font->backx;
      }
    }
    /*
    else
    {
      gfd_fb_init(&fb, cx->dx, cx->dy, cx->dx, p);
      gfd_fb_draw_sprite(cx->fb, cx->x, cx->y, &fb, cx->color);
      cx->x+=cx->dx; //f.map[i].w;
      cx->out_pixels+=cx->dx;
    }
    */
    gfd_fb_init(&fb, dx, cx->dy, cx->dx, p);
    //cx->out_pixels+=dx;
    cx->out_pixels+=fntx;

    if (cx->measure_only==0)
    {
      gfd_fb_draw_sprite(cx->fb, cx->x, cx->y, &fb, cx->color, 1);
    }
    else if (cx->measure_only==2)
    {
      gfd_fb_draw_sprite_cs(cx->fb, cx->x, cx->y, &fb, cx->color);
    }
    //cx->x+=dx; //f.map[i].w;
    cx->x+=fntx;

  }
  else if (c==' ')
  {
    //cx->out_pixels+=dx; //cx->font->word_spacing;
    //cx->x+=dx; //cx->font->word_spacing;
    cx->out_pixels+=fntx;
    cx->x+=fntx;
  }
  else if (c=='|' && cx->font->special_dot_width)
  {
	//fntx = cx->font->special_dot_width-cx->font->backx;
	fntx = cx->font->special_dot_width-1;
	if(cx->prevCh != -1 && cx->prevCh !='.')
	{
	  cx->x-=cx->font->backx;
	  cx->out_pixels-=cx->font->backx;
	}
	cx->out_pixels+=fntx;
	cx->x+=fntx;
  }
  if ((c=='\n') || (c=='\r'))
  {
	  cx->x=cx->ox;
	  cx->y+=dx;
  }

  cx->prevCh = c;
  return 0;
}

int gfd_fb_text_vout_(gfd_framebuffer_t* const fb, int mode, const gfd_font256_t* const font, int x, int y, int color, const char* fmt, va_list args)
{
  struct fb_printf_cx_t info;
  info.fb=fb;
  info.x=x-font->ofsx;
  info.y=y-font->ofsy;
  info.font=font;
  info.color=color;
  info.dx=font->glyphs_witdh;
  info.fntx=font->char_spacing;
  info.dy=font->glyphs_height;
  info.out_pixels=0;
  info.measure_only=mode;
  info.prevCh = -1;
  info.ox=info.x;
  info.oy=info.y;

  (void)bsk_xvprintf(fb_outch, &info, fmt, args);
  if(info.prevCh=='.' || info.prevCh=='|')
  {
    info.out_pixels-=(((font->char_spacing - font->width)+1)/2);
  }
  else
  {
    info.out_pixels-=((font->char_spacing - font->width)+1);
  }
  return info.out_pixels;
}

int gfd_fb_printf(gfd_framebuffer_t* const fb, const gfd_font256_t* const font, int x, int y, int color, const char* fmt, ...)
{
  va_list args;
  va_start(args, fmt);
  int n=gfd_fb_vprintf(fb, font, x, y, color, fmt, args);
  va_end(args);
  return n;
}

int gfd_fb_vprintf(gfd_framebuffer_t* const fb, const gfd_font256_t* const font, int x, int y, int color, const char* fmt, va_list args)
{
  /*
  struct fb_printf_cx_t info;
  info.fb=fb;
  info.x=x;
  info.y=y;
  info.font=font;
  info.color=color;
  info.dx=font->glyphs_witdh;
  info.dy=font->glyphs_height;
  info.out_pixels=0;

  (void)bsk_xvdoformat(fb_outch, &info, fmt, args);
  return info.out_pixels;
  */
  return gfd_fb_text_vout_(fb, 0, font, x, y, color, fmt, args);
}

int gfd_fb_printf_cs(gfd_framebuffer_t* const fb, const gfd_font256_t* const font, int x, int y, int color, const char* fmt, ...)
{
  va_list args;
  va_start(args, fmt);
  int n=gfd_fb_vprintf_cs(fb, font, x, y, color, fmt, args);
  va_end(args);
  return n;
}

int gfd_fb_vprintf_cs(gfd_framebuffer_t* const fb, const gfd_font256_t* const font, int x, int y, int color, const char* fmt, va_list args)
{
  return gfd_fb_text_vout_(fb, 2, font, x, y, color, fmt, args);
}

int gfd_textLen(const char* text, int maxCnt)
{
  return gfd_strnlen(text, maxCnt);
}

char* gfd_textCpy(char *dest, const char *source, int count)
{
  return gfd_strncpy(dest, source, count);
}

int gfd_fb_text_measure(gfd_framebuffer_t* const fb, const gfd_font256_t* const font, int x, int y, int color, const char* fmt, ...)
{
  va_list args;
  va_start(args, fmt);
  int n=gfd_fb_text_vmeasure(fb, font, x, y, color, fmt, args);
  va_end(args);
  return n;
}

int gfd_fb_text_vmeasure(gfd_framebuffer_t* const fb, const gfd_font256_t* const font, int x, int y, int color, const char* fmt, va_list args)
{
  return gfd_fb_text_vout_(fb, 1, font, x, y, color, fmt, args);
}

int gfd_font_height(const gfd_font256_t* const font)
{
  return font->height;
}

int gfd_font_occl_height(const gfd_font256_t* const font)
{
  return font->occlusion_height;
}
