

#include "gfd_grifo.h"

#include "gfd_p_.h"


int gfd_grifo_fb_init(gfd_grifo_framebuffer_t* const g_fb, const gfd_framebuffer_t* const fb)
{
  *g_fb=*fb;

  g_fb->buffer=gfd_fb_get_pixel_address(fb, 8, 8);

  g_fb->dx=fb->dx-16;
  g_fb->dy=fb->dy-16;
  g_fb->ox=0;
  g_fb->oy=0;
  g_fb->ex=g_fb->dx;
  g_fb->ey=g_fb->dy;
  g_fb->size=(g_fb->pitch*g_fb->dx);
  return gfd_ok;
}

static void gfd_grifo_draw_hline_w2(gfd_grifo_framebuffer_t* const fb, int sx, int sy, int ex, int color, int occlusion)
{
  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-1;

  {
    gfd_pixel_t c=GFD_PIXEL(color);

    {
      int ocs=occlusion & 1 ? sx-1 : sx;
      int oce=occlusion & 2 ? ex+1 : ex;

      gfd_pixel_t* const p0=fb_get_row(fb, sy-1);
      gfd_pixel_t* const p1=fb_get_row(fb, sy+2);
      for(x=ocs; x<=oce; ++x)
      {
        fb_set_pixel_row_no_clip(fb, p0, x, 1);
        fb_set_pixel_row_no_clip(fb, p1, x, 1);
      }
    }

    {
      gfd_pixel_t* const p0=fb_get_row(fb, sy);
      gfd_pixel_t* const p1=fb_get_row(fb, sy+1);

      if (occlusion & 1)
      {
        fb_set_pixel_row_no_clip(fb, p0, sx-1, 1);
        fb_set_pixel_row_no_clip(fb, p1, sx-1, 1);
      }

      for(x=sx; x<=ex; ++x)
      {
        fb_set_pixel_row_no_clip(fb, p0, x, c);
        fb_set_pixel_row_no_clip(fb, p1, x, c);
      }
      if (occlusion & 2)
      {
        fb_set_pixel_row_no_clip(fb, p0, ex+1, 1);
        fb_set_pixel_row_no_clip(fb, p1, ex+1, 1);
      }

    }
  }
}

void gfd_grifo_draw_hline_w(gfd_grifo_framebuffer_t* const fb, int sx, int sy, int ex, int color, int width, int occlusion)
{
  int x;
  int y;

  if (width==2)
  {
    gfd_grifo_draw_hline_w2(fb, sx, sy, ex, color, occlusion);
    return;
  }

  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 (width<2)
    width=2;

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

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

  {
    gfd_pixel_t c=GFD_PIXEL(color);
    int w2=width/2;

    {
      int ocs=occlusion & 1 ? sx-1 : sx;
      int oce=occlusion & 2 ? ex+1 : ex;

      gfd_pixel_t* const p0=fb_get_row(fb, sy-w2-1);
      gfd_pixel_t* const p1=fb_get_row(fb, sy+w2);
      for(x=ocs; x<=oce; ++x)
      {
        fb_set_pixel_row_no_clip(fb, p0, x, 1);
        fb_set_pixel_row_no_clip(fb, p1, x, 1);
      }
    }

    for(y=sy-w2; y<sy+w2; ++y)
    {
      gfd_pixel_t* const p=fb_get_row(fb, y);
      if (occlusion & 1)
        fb_set_pixel_row_no_clip(fb, p, sx-1, 1);
      if (occlusion & 2)
        fb_set_pixel_row_no_clip(fb, p, ex+1, 1);

      for(x=sx; x<=ex; ++x)
        fb_set_pixel_row_no_clip(fb, p, x, c);
    }
  }
}

void gfd_grifo_draw_vline_w(gfd_grifo_framebuffer_t* const fb, int sx, int sy, int ey, int color, int width, int occlusion)
{
  int y;
  int x;

  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 (width<2)
    width=2;

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

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

  {
    gfd_pixel_t c=GFD_PIXEL(color);
    int w2=width/2;

    if (occlusion & 1)
    {
      gfd_pixel_t* p=fb_get_pixel_address(fb, sx-w2, sy-1);

      for(x=-1; x<width+1; ++x)
      {
        p[x]=1;
      }
    }
    if (occlusion & 2)
    {
      gfd_pixel_t* p=fb_get_pixel_address(fb, sx-w2, ey+1);

      for(x=-1; x<width+1; ++x)
      {
        p[x]=1;
      }
    }


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

void gfd_grifo_draw_line_w2(gfd_grifo_framebuffer_t* const fb, int x1, int y1, int x2, int y2, int color, int /*occlusion*/)
{
  long XDelta,YDelta,M,Y,X;

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

 {
   int x, y, a;
   gfd_pixel_t c=GFD_PIXEL(color);

   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;

            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, 1);
            fb_set_pixel_no_clip(fb, a, y-1, 1);
         }
      }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);
            fb_set_pixel_no_clip(fb, a, y+2, 1);
            fb_set_pixel_no_clip(fb, a, y-1, 1);

         }
      }
      return;
   } 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);
            fb_set_pixel_no_clip(fb, x+2, a, 1);
            fb_set_pixel_no_clip(fb, x-1, a, 1);

         }
      }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);
            fb_set_pixel_no_clip(fb, x+2, a, 1);
            fb_set_pixel_no_clip(fb, x-1, a, 1);

         }
      }
   }
  }

  return;

}

#define p_inside(r_, x_, y_) p_inside_((r_), (x_), (y_))
#define p_inside_(r_, x_, y_) (x_>=r_.p0.x && x_<=r_.p1.x && y_>=r_.p0.y && y_<=r_.p1.y)

#define arc_set_pixel_e(fb_, x_, y_, c_) \
  (fb_set_pixel_no_clip(fb_, x_, y_, c_),\
   fb_set_pixel_no_clip(fb_, x_-1, y_, c_),\
   fb_set_pixel_no_clip(fb_, x_-2, y_, 1),\
   fb_set_pixel_no_clip(fb_, x_+1, y_, 1))

#define arc_set_pixel_w(fb_, x_, y_, c_) \
  (fb_set_pixel_no_clip(fb_, x_, y_, c_),\
   fb_set_pixel_no_clip(fb_, x_+1, y_, c_),\
   fb_set_pixel_no_clip(fb_, x_+2, y_, 1),\
   fb_set_pixel_no_clip(fb_, x_-1, y_, 1))

#define arc_set_pixel_s(fb_, x_, y_, c_) \
  (fb_set_pixel_no_clip(fb_, x_, y_, c_),\
   fb_set_pixel_no_clip(fb_, x_, y_-1, c_),\
   fb_set_pixel_no_clip(fb_, x_, y_-2, 1),\
   fb_set_pixel_no_clip(fb_, x_, y_+1, 1))

#define arc_set_pixel_n(fb_, x_, y_, c_) \
  (fb_set_pixel_no_clip(fb_, x_, y_, c_),\
   fb_set_pixel_no_clip(fb_, x_, y_+1, c_),\
   fb_set_pixel_no_clip(fb_, x_, y_+2, 1),\
   fb_set_pixel_no_clip(fb_, x_, y_-1, 1))

#define circ_draw_pixel(orient_, fb_, bb_, x0_, x_, y0_, y_, c_) \
  (p_inside(bb_, (x_), (y_)) ? (arc_set_pixel_##orient_(fb_, (x0_+(x_)), (y0_+(y_)), c_)) : (void)(0))

static void grifo_draw_circle_bb_w2oc(gfd_framebuffer_t* const fb, const gfd_rect_t* const bb, int x0, int y0, int radius, int cc)
{
  int f = 1 - radius;
  int ddF_x = 1;
  int ddF_y = -2 * radius;
  int x = 0;
  int y = radius;
  gfd_rect_t rect=*bb;

  gfd_pixel_t color=GFD_PIXEL(cc);

  gfd_clip_rect(&rect, fb->ox-x0, fb->oy-y0, fb->ex-x0, fb->ey-y0);

  while(x <= y)
  {
    circ_draw_pixel(n, fb, rect, x0, - x, y0, + y, color);
    circ_draw_pixel(n, fb, rect, x0, + x, y0, + y, color);

    circ_draw_pixel(s, fb, rect, x0, - x, y0, - y, color);
    circ_draw_pixel(s, fb, rect, x0, + x, y0, - y, color);

    circ_draw_pixel(w, fb, rect, x0, + y, y0, + x, color);
    circ_draw_pixel(e, fb, rect, x0, - y, y0, + x, color);
    circ_draw_pixel(w, fb, rect, x0, + y, y0, - x, color);
    circ_draw_pixel(e, fb, rect, x0, - y, y0, - x, color);

    if(f >= 0)
    {
      y--;
      ddF_y += 2;
      f += ddF_y;
    }
    x++;
    ddF_x += 2;
    f += ddF_x;
  }
}

void gfd_grifo_draw_arc179_w2o(gfd_framebuffer_t* const fb, int x0, int y0, int radius, double angle_start, double angle_stop, int color)
{
  gfd_fb_draw_arc179_func(grifo_draw_circle_bb_w2oc, fb, x0, y0, radius, angle_start, angle_stop, color);
}


/* Draws a horizontal run of pixels, then advances the bitmap pointer to
the first pixel of the next run. */
static void DrawHorizontalRun(unsigned char **ScreenPtr, int XAdvance, int RunLength, int Color,int SCREEN_WIDTH)
{
  int i;
  unsigned char *WorkingScreenPtr = *ScreenPtr;
  for (i=0; i<RunLength; i++)
  {
    WorkingScreenPtr[-SCREEN_WIDTH]=1;
    *WorkingScreenPtr = Color;
    WorkingScreenPtr[SCREEN_WIDTH]=Color;
    WorkingScreenPtr[SCREEN_WIDTH<<1]=1;
    WorkingScreenPtr += XAdvance;
  }
  /* Advance to the next scan line */
  WorkingScreenPtr += SCREEN_WIDTH;
  *ScreenPtr = WorkingScreenPtr;
}
/* Draws a vertical run of pixels, then advances the bitmap pointer to
the first pixel of the next run. */
static void DrawFirstVerticalRun(unsigned char **ScreenPtr, int XAdvance, int RunLength, int Color, int SCREEN_WIDTH)
{
  int i;
  unsigned char *WorkingScreenPtr = *ScreenPtr;
  WorkingScreenPtr[-(SCREEN_WIDTH+1)]=1;
  WorkingScreenPtr[-SCREEN_WIDTH]=1;
  WorkingScreenPtr[-(SCREEN_WIDTH-1)]=1;
  WorkingScreenPtr[-(SCREEN_WIDTH-2)]=1;
  for (i=0; i<RunLength; i++)
  {
    WorkingScreenPtr[-1]=1;
    *WorkingScreenPtr = Color;
    WorkingScreenPtr[1]=Color;
    WorkingScreenPtr[2]=1;

    WorkingScreenPtr += SCREEN_WIDTH;
  }
  WorkingScreenPtr[3-SCREEN_WIDTH]=1;

  /* Advance to the next column */
  WorkingScreenPtr += XAdvance;
  *ScreenPtr = WorkingScreenPtr;
}

static void DrawLastVerticalRun(unsigned char **ScreenPtr, int XAdvance, int RunLength, int Color, int SCREEN_WIDTH)
{
  int i;
  unsigned char *WorkingScreenPtr = *ScreenPtr;
  WorkingScreenPtr[-2]=1;

  for (i=0; i<RunLength; i++)
  {
    WorkingScreenPtr[-1]=1;
    *WorkingScreenPtr = Color;
    WorkingScreenPtr[1]=Color;
    WorkingScreenPtr[2]=1;

    WorkingScreenPtr += SCREEN_WIDTH;
  }
  WorkingScreenPtr[-1]=1;
  WorkingScreenPtr[0]=1;
  WorkingScreenPtr[1]=1;
  WorkingScreenPtr[2]=1;

  /* Advance to the next column */
  WorkingScreenPtr += XAdvance;
  *ScreenPtr = WorkingScreenPtr;
}

static void DrawVerticalRun(unsigned char **ScreenPtr, int XAdvance, int RunLength, int Color, int SCREEN_WIDTH)
{
  int i;
  unsigned char *WorkingScreenPtr = *ScreenPtr;
  WorkingScreenPtr[-2]=1;
  for (i=0; i<RunLength; i++)
  {
    WorkingScreenPtr[-1]=1;
    *WorkingScreenPtr = Color;
    WorkingScreenPtr[1]=Color;
    WorkingScreenPtr[2]=1;

    WorkingScreenPtr += SCREEN_WIDTH;
  }
  WorkingScreenPtr[3-SCREEN_WIDTH]=1;

  /* Advance to the next column */
  WorkingScreenPtr += XAdvance;
  *ScreenPtr = WorkingScreenPtr;
}

/*LineDraw: rearranged and optimized from the original Run-Length Slice LineDraw Computer Science Graphics Programming Black Book*/
void LineDraw_W2O(gfd_grifo_framebuffer_t* const fb, int XStart, int YStart, int XEnd, int YEnd, int Color)
{
  int Temp, AdjUp, AdjDown, ErrorTerm, XAdvance, XDelta, YDelta;
  int WholeStep, InitialPixelCount, FinalPixelCount, i, RunLength;


  unsigned char* ScreenPtr=fb->buffer;
  int SCREEN_WIDTH=fb->pitch;

  /* We’ll always draw top to bottom, to reduce the number of cases we have to
handle, and to make lines between the same endpoints draw the same pixels */
  if (YStart > YEnd)
  {
    Temp = YStart;
    YStart = YEnd;
    YEnd = Temp;
    Temp = XStart;
    XStart = XEnd;
    XEnd = Temp;
  }
  if ((XDelta = XEnd - XStart) < 0)
  {
    XAdvance = -1;
    XDelta = -XDelta;
  }
  else
  {
    XAdvance = 1;
  }

  ScreenPtr=&fb->buffer[SCREEN_WIDTH*YStart+XStart];

  /* Figure out how far we’re going vertically */
  YDelta = YEnd - YStart;
  if (XDelta == 0)
  {
    /* Vertical line */
    for (i=0; i<=YDelta; i++)
    {
      ScreenPtr[-1]=1;
      ScreenPtr[0]=Color;
      ScreenPtr[1]=Color;
      ScreenPtr[2]=1;
      ScreenPtr += SCREEN_WIDTH;
    }
    return;
  }
  if (YDelta == 0)
  {
    /* Horizontal line */
    for (i=0; i<=XDelta; i++)
    {
      ScreenPtr[-XAdvance]=1;
      ScreenPtr[0] = Color;
      ScreenPtr[XAdvance] =Color;
      ScreenPtr[XAdvance*2] =1;
      ScreenPtr += XAdvance;
    }
    return;
  }
  if (XDelta == YDelta)
  {
    /* Diagonal line */
    for (i=0; i<=XDelta; i++)
    {
      ScreenPtr[0] = Color;
      ScreenPtr += XAdvance + SCREEN_WIDTH;
    }
    return;
  }
  /* Determine whether the line is X or Y major, and handle accordingly */
  if (XDelta >= YDelta)
  {
    /* X major line */
    /* Minimum # of pixels in a run in this line */
    WholeStep = XDelta / YDelta;
    /* Error term adjust each time Y steps by 1; used to tell when one
  extra pixel should be drawn as part of a run, to account for
  fractional steps along the X axis per 1-pixel steps along Y */
    AdjUp = (XDelta % YDelta) * 2;
    /* Error term adjust when the error term turns over, used to factor
  out the X step made at that time */
    AdjDown = YDelta * 2;
    /* Initial error term; reflects an initial step of 0.5 along the Y
  axis */
    ErrorTerm = (XDelta % YDelta) - (YDelta * 2);
    /* The initial and last runs are partial, because Y advances only 0.5
  for these runs, rather than 1. Divide one full run, plus the
  initial pixel, between the initial and last runs */
    InitialPixelCount = (WholeStep / 2) + 1;
    FinalPixelCount = InitialPixelCount;
    /* If the basic run length is even and there’s no fractional
  advance, we have one pixel that could go to either the initial
  or last partial run, which we’ll arbitrarily allocate to the
  last run */
    if ((AdjUp == 0) && ((WholeStep & 0x01) == 0))
    {
      InitialPixelCount--;
    }
    /* If there’re an odd number of pixels per run, we have 1 pixel that can’t
  be allocated to either the initial or last partial run, so we’ll add 0.5
  to error term so this pixel will be handled by the normal full-run loop */
    if ((WholeStep & 0x01) != 0)
    {
      ErrorTerm += YDelta;
    }
    /* Draw the first, partial run of pixels */
    DrawHorizontalRun(&ScreenPtr, XAdvance, InitialPixelCount, Color, SCREEN_WIDTH);
    /* Draw all full runs */
    for (i=0; i<(YDelta-1); i++)
    {
      RunLength = WholeStep; /* run is at least this long */
      /* Advance the error term and add an extra pixel if the error
  term so indicates */
      if ((ErrorTerm += AdjUp) > 0)
      {
        RunLength++;
        ErrorTerm -= AdjDown; /* reset the error term */
      }
      /* Draw this scan line’s run */
      DrawHorizontalRun(&ScreenPtr, XAdvance, RunLength, Color, SCREEN_WIDTH);
    }
    /* Draw the final run of pixels */
    DrawHorizontalRun(&ScreenPtr, XAdvance, FinalPixelCount, Color, SCREEN_WIDTH);
    return;
  }
  else
  {
    /* Y major line */
    /* Minimum # of pixels in a run in this line */
    WholeStep = YDelta / XDelta;
    /* Error term adjust each time X steps by 1; used to tell when 1 extra
  pixel should be drawn as part of a run, to account for
  fractional steps along the Y axis per 1-pixel steps along X */
    AdjUp = (YDelta % XDelta) * 2;
    /* Error term adjust when the error term turns over, used to factor
  out the Y step made at that time */
    AdjDown = XDelta * 2;
    /* Initial error term; reflects initial step of 0.5 along the X axis */
    ErrorTerm = (YDelta % XDelta) - (XDelta * 2);
    /* The initial and last runs are partial, because X advances only 0.5
  for these runs, rather than 1. Divide one full run, plus the
  initial pixel, between the initial and last runs */
    InitialPixelCount = (WholeStep / 2) + 1;
    FinalPixelCount = InitialPixelCount;
    /* If the basic run length is even and there’s no fractional advance, we
  have 1 pixel that could go to either the initial or last partial run,
  which we’ll arbitrarily allocate to the last run */
    if ((AdjUp == 0) && ((WholeStep & 0x01) == 0))
    {
      InitialPixelCount--;
    }
    /* If there are an odd number of pixels per run, we have one pixel
  that can’t be allocated to either the initial or last partial
  run, so we’ll add 0.5 to the error term so this pixel will be
  handled by the normal full-run loop */
    if ((WholeStep & 0x01) != 0)
    {
      ErrorTerm += XDelta;
    }
    /* Draw the first, partial run of pixels */
    DrawFirstVerticalRun(&ScreenPtr, XAdvance, InitialPixelCount, Color, SCREEN_WIDTH);
    /* Draw all full runs */
    for (i=0; i<(XDelta-1); i++)
    {
      RunLength = WholeStep; /* run is at least this long */
      /* Advance the error term and add an extra pixel if the error
  term so indicates */
      if ((ErrorTerm += AdjUp) > 0)
      {
        RunLength++;
        ErrorTerm -= AdjDown; /* reset the error term */
      }
      /* Draw this scan line’s run */
      DrawVerticalRun(&ScreenPtr, XAdvance, RunLength, Color, SCREEN_WIDTH);
    }
    /* Draw the final run of pixels */
    DrawLastVerticalRun(&ScreenPtr, XAdvance, FinalPixelCount, Color, SCREEN_WIDTH);
    return;
  }
}
