/*******************************************************************************
+
+  LEDA 3.5
+
+  _x_basic.c
+
+  This file is part of the LEDA research version (LEDA-R) that can be 
+  used free of charge in academic research and teaching. Any commercial
+  use of this software requires a license which is distributed by the
+  LEDA Software GmbH, Postfach 151101, 66041 Saarbruecken, FRG
+  (fax +49 681 31104).
+
+  Copyright (c) 1991-1997  by  Max-Planck-Institut fuer Informatik
+  Im Stadtwald, 66123 Saarbruecken, Germany     
+  All rights reserved.
+ 
*******************************************************************************/

//------------------------------------------------------------------------------
//  basic graphics functions for libWx (declared in <LEDA/impl/x_basic.h>) 
//  implemented by svgalib (for linux)
//------------------------------------------------------------------------------


#include <LEDA/impl/x_basic.h>
#include "windef.h"
#include "vga.h"

/*
#include "fonts/font8.16"
#include "fonts/font7.13"
*/

#include "fonts/font8.13"
#include "fonts/fontb8.12"

#include <stdlib.h>


#if !defined(M_PI)
#define	M_PI   3.14159265358979323846
#endif

inline void SWAP(int& a, int& b)
{ int t = a;
  a = b;
  b = t;
 }


static unsigned char* FONT = FONT_0813;
static int FONT_WIDTH = 8;
static int FONT_HEIGHT = 13;
static int FONT_SCALE = 1;


int  x_display_width() 
{ x_open_display();
  return vga_width(); }

int  x_display_height()
{ x_open_display();
  return vga_height(); }

int  x_display_depth() 
{ x_open_display();
  return vga_depth(); }

int  x_display_bits_saved() { return 1; } 


// not implemented 

void x_flush_display() {}

int  x_create_buffer(Window) { return 1; }
void x_delete_buffer(Window) {}
void x_start_buffering(Window) { vga_set_buffering(1); }
void x_stop_buffering(Window)  { vga_set_buffering(0); }
char* x_get_buffer_pixrect(Window) { return 0; }

void x_set_icon_pixmap(Window,char*)
{ /* not implemented */ }


void x_flush_buffer(Window w,int x0,int y0,int x1,int y1, int dx, int dy)
{ VgaWindow win = set_draw_window(w);
  if (y0 > y1) SWAP(y0,y1);
  if (x0 > x1) SWAP(x0,x1);
  x0 += win->xpos;
  y0 += win->ypos;
  x1 += win->xpos;
  y1 += win->ypos;
  vga_flush(x0,y0,x1,y1,1);
}


void x_flush_buffer(Window w ,int x0, int y0, int x1, int y1) 
{ x_flush_buffer(w,x0,y0,x1,y1,0,0); }


int  x_new_color(const char*) { return 1; }
int  x_new_color(int,int,int) { return 1; }

int  x_load_text_font(const char* fn){ return 0; }
int  x_load_bold_font(const char*)   { return 0;}
int  x_load_fixed_font(const char*)  { return 0;}

int  x_set_font(Window, const char*) { return 0;}


void x_set_text_font(Window)
{ FONT = FONT_0813;
  FONT_WIDTH = 8;
  FONT_HEIGHT = 13;
}

void x_set_bold_font(Window) 
{ FONT = FONT_B_0812;
  FONT_WIDTH = 8;
  FONT_HEIGHT = 12;
}

void x_set_fixed_font(Window)
{ FONT = FONT_0813;
  FONT_WIDTH = 8;
  FONT_HEIGHT = 13;
}


void x_grab_pointer(Window) {}
void x_ungrab_pointer() {}
void x_set_border_width(Window,int) {}
int  x_set_join_style(Window,int) { return 0; }

void x_start_timer(Window,int) {}
void x_stop_timer(Window) {}




drawing_mode x_get_mode(Window w)       { return win_list[w]->MODE; }
text_mode    x_get_text_mode(Window w)  { return win_list[w]->TEXTMODE; }
line_style   x_get_line_style(Window w) { return win_list[w]->LINESTYLE; }
int          x_get_line_width(Window w) { return win_list[w]->LINEWIDTH; }
int          x_get_color(Window w)      { return win_list[w]->COLOR; }


int x_set_line_width(Window w, int width)
{ VgaWindow win = win_list[w];
  int save = win->LINEWIDTH;
  win->LINEWIDTH = width;
  return save;
}

text_mode x_set_text_mode(Window w, text_mode mode)
{ VgaWindow win = win_list[w];
  text_mode save = win->TEXTMODE;
  win->TEXTMODE = mode;
  return save;
}

line_style x_set_line_style(Window w, line_style style)
{ VgaWindow win = win_list[w];
  line_style save = win->LINESTYLE;
  win->LINESTYLE = style;
  return save;
}

int x_set_color(Window w, int color)
{ VgaWindow win = win_list[w];
  int save = win->COLOR;
  win->COLOR = color;
  return save;
 }

drawing_mode x_set_mode(Window w, drawing_mode mode)
{ VgaWindow win = win_list[w];
  drawing_mode save = win->MODE;
  win->MODE = mode;
  return save;
}

int x_text_height(Window, const char*) 
{ return FONT_SCALE*FONT_HEIGHT-1; }

int x_text_width(Window, const char* s) 
{ return FONT_SCALE*FONT_WIDTH*strlen(s); }

void x_set_palette(int index, int red, int green, int blue)
{ vga_setpal(index,red,green,blue); }

void x_get_palette(int index, int* red, int* green, int* blue)
{ vga_getpal(index,red,green,blue); }




static unsigned char* video_buf = 0;
static int video_width;
static int video_height;

static void setpix(VgaWindow win, int x, int y, int flush=0)
{ 
  // clip to window
  if (x < win->clip_x || x > win->clip_x1 || 
      y < win->clip_y || y > win->clip_y1) return;

  x += win->xpos;
  y += win->ypos;

  if (video_buf != 0)   /* write into (monochrome) buffer */
     video_buf[y*video_width+x/8] |= (0x80 >> (x%8));
  else
     vga_pixel(x,y,flush);
}



static void vline(VgaWindow win,int x, int y0, int y1)
{ 
  if (y0 > y1) SWAP(y0,y1);

  // clip into window
  if (x < win->clip_x || x > win->clip_x1 || 
      y1 < win->clip_y || y0 > win->clip_y1) return;

  if (y0 < win->clip_y)  y0 = win->clip_y;
  if (y1 > win->clip_y1) y1 = win->clip_y1;

  x  += win->xpos;
  y0 += win->ypos;
  y1 += win->ypos;

  vga_vline(x,y0,y1);
  vga_flush(x,y0,x,y1);
}



static void hline(VgaWindow win, int x0, int x1, int y)
{ 

  if (x0 > x1) SWAP(x0,x1);

  // clip into window
  if (y < win->clip_y || y > win->clip_y1 || 
      x1 < win->clip_x || x0 > win->clip_x1) return;

  if (x0 < win->clip_x) x0 = win->clip_x;
  if (x1 > win->clip_x1) x1 = win->clip_x1;

  x0 += win->xpos;
  x1 += win->xpos;
  y  += win->ypos;

  vga_hline(x0,x1,y);
  vga_flush(x0,y,x1,y);
}


static void adjust_line(int s, int& x1, int& y1, int& x2, int& y2)
{ int dx = x2 - x1;
  int dy = y2 - y1;
  if (dx == 0 && dy == 0) return;

  int xoff = s;
  int yoff = s;

  if (dx < 0) { dx = -dx; xoff = -s; }
  if (dy < 0) { dy = -dy; yoff = -s; }

  if ( dx >= dy) x2 += xoff;
  if ( dx <= dy) y2 += yoff;
 }



void x_line(Window w, int x1, int y1, int x2, int y2)
{ 
  VgaWindow win = set_draw_window(w);

  int xmin,xmax,ymin,ymax;

  if (x1 < x2) 
    { xmin = x1; xmax = x2; }
  else 
    { xmin = x2; xmax = x1; }

  if (y1 < y2) 
    { ymin = y1; ymax = y2; }
  else 
    { ymin = y2; ymax = y1; }



  int lw2 = win->LINEWIDTH/2;

  adjust_line(-(lw2+1),x1,y1,x2,y2);

  if (x1 == x2 && video_buf == 0)
  { vline(win,x1,y1,y2);
    for (int i=1; i<=lw2; i++)
    { vline(win,x1-i,y1,y2);
      vline(win,x1+i,y1,y2);
     }
    return;
   }

  if (y1 == y2 && video_buf == 0)
  { hline(win,x1,x2,y1);
    for (int i=1; i<=lw2; i++)
    { hline(win,x1,x2,y1-i);
      hline(win,x1,x2,y1+i);
     }
    return;
   }


  // draw line

  int sy = 1;
  int dx = x2 - x1;
  int dy = y2 - y1;

  if (dx < 0)
  { dx = -dx;
    SWAP(x1,x2);
    dy = -dy;
    SWAP(y1,y2);
   }

  if (dy < 0)
  { dy = -dy;
    sy = -1;
   }


  // clip line into window

 // if (dx > dy)
    { if (x1 < win->clip_x)
      { int d = win->clip_x - x1;
        x1 += d;
        y1 += (d*sy*dy)/dx;
       }
      if (x2 > win->clip_x1)
      { int d = x2 - win->clip_x1;
        x2 -= d;
        y2 -= (d*sy*dy)/dx;
       }
     }
 // else  // dy > dx
    {
      if (y1 < win->clip_y)
      { int d = win->clip_y - y1;
        y1 += d;
        x1 += (d*dx)/(sy*dy);
       }

      if (y2 < win->clip_y)
      { int d = win->clip_y - y2;
        y2 += d;
        x2 += (d*dx)/(sy*dy);
       }

      if (y1 > win->clip_y1)
      { int d = y1 - win->clip_y1;
        y1 -= d;
        x1 -= (d*dx)/(sy*dy);
       }

      if (y2 > win->clip_y1)
      { int d = y2 - win->clip_y1;
        y2 -= d;
        x2 -= (d*dx)/(sy*dy);
       }
    
     }

  dx = x2 - x1;
  dy = sy*(y2 - y1);

  if (dx < 0 || dy < 0) return;

  int fl = (dy > dx/3);

  if (dx > dy)
  { int c = dx / 2;
    setpix(win,x1,y1,fl);
    for (int i=1; i<=lw2; i++)
    { setpix(win,x1,y1+i,fl);
      setpix(win,x1,y1-i,fl);
     }

    while(x1 != x2)
    { x1++;
      if ((c+=dy) > dx)
      { c  -= dx;
        y1 += sy;
       }
      setpix(win,x1,y1,fl);
      for (int i=1; i<=lw2; i++)
      { setpix(win,x1,y1+i,fl);
        setpix(win,x1,y1-i,fl);
       }
    }
  }
  else  // dy > dx
  { 
    int c = dy / 2;
    setpix(win,x1,y1,fl);
    for (int i=1; i<=lw2; i++)
    { setpix(win,x1+i,y1,fl);
      setpix(win,x1-i,y1,fl);
     }

    while(y1 != y2)
    { y1 += sy;
      if ((c+=dx) >= dy)
      { c -= dy;
        x1++;
       }
      setpix(win,x1,y1,fl);
      for (int i=1; i<=lw2; i++)
      { setpix(win,x1+i,y1,fl);
        setpix(win,x1-i,y1,fl);
       }
    }
  }

  if (fl == 0)
    vga_flush(xmin+win->xpos, ymin+win->ypos, xmax+win->xpos, ymax+win->ypos);

}

void x_lines(Window w, int n, int* x1, int* y1, int* x2, int* y2) {}



static void clip_bitmap(VgaWindow win, int x, int y, unsigned char* data,
                                                int width, int height)
{ int y1 = y + height - 1;
  int x1 = x + 8*width - 1;

  int left_margin = -1;
  int top_margin  = -1;
  int right_margin  = width;
  int bottom_margin = height;

  unsigned char mask1 = 0xFF;
  unsigned char mask2 = 0xFF;

  if (y  < win->clip_y)  top_margin    += win->clip_y - y;
  if (y1 > win->clip_y1) bottom_margin -= y1 - win->clip_y1;

  if (x  < win->clip_x)  
  { left_margin  = win->clip_x - x;
    mask1 = (0xFF >> (left_margin%8));
    left_margin /= 8;
   }

  if (x1 > win->clip_x1) 
  { right_margin = (x1 - win->clip_x1);
    mask2 = ~(0xFF >> (right_margin%8));
    right_margin = width - right_margin/8 - 1;
   }

  
  for (int i = 0; i < height; i++)
  { unsigned char* p = data + i*width;
    if (i < top_margin || i > bottom_margin)
       { unsigned char* stop = p + width;
         while (p < stop) *p++ = 0;
        }
    else
      for(int j=0; j < width; j++)
      { if (j < left_margin || j > right_margin) *p=0;
        if (j == left_margin) *p &= mask1;
        if (j == right_margin) *p &= mask2;
        p++;
       }
   }
}


void x_polygon(Window w, int n, int* xcoord, int* ycoord)
{ int x = xcoord[n-1];
  int y = ycoord[n-1];
  for(int i = 0; i<n; i++)
  { int x1 = xcoord[i];
    int y1 = ycoord[i];
    x_line(w,x,y,x1,y1);
    x = x1;
    y = y1;
   }
}
  


#define FILLPUT(pos,byte)\
{ p = pos;\
  if ((*p & (byte)) == 0)\
  { *p |= (byte);\
    POS[top] = p; \
    BYTE[top] = byte; \
    top = (top+1) % 512; } else;\
}

static  unsigned char* POS[512];
static  unsigned char  BYTE[512];

static void fill_bits(unsigned char* pos, unsigned char byte)
{ int bot = 0;
  int top = 0;
  unsigned char* p;

  FILLPUT(pos,byte)

  while (top != bot)
  { pos  = POS[bot];
    byte = BYTE[bot];

    bot = (bot+1) % 512;

    if (byte == 128)
       FILLPUT(pos-1,1)
    else
       FILLPUT(pos,byte<<1)

    if (byte == 1)
       FILLPUT(pos+1,128)
    else
       FILLPUT(pos,byte>>1)

    FILLPUT(pos-video_width,byte)
    FILLPUT(pos+video_width,byte)
  }
}


void x_fill_polygon(Window w, int n, int* xcoord, int* ycoord)
{
  int i;

  // compute bounding box (minx,miny,maxx,maxy)

  int minxi = 0;
  int maxxi = 0;
  int minyi = 0;
  int maxyi = 0;


  for(i=1;i<n;i++)
  { minxi = (xcoord[i] < xcoord[minxi]) ? i : minxi;
    minyi = (ycoord[i] < ycoord[minyi]) ? i : minyi;
    maxxi = (xcoord[i] > xcoord[maxxi]) ? i : maxxi;
    maxyi = (ycoord[i] > ycoord[maxyi]) ? i : maxyi;
   }

  i = minxi;
  while (xcoord[i] == xcoord[minxi]) if (++i == n) i = 0;
  if (--i < 0) i = n-1;
  minxi = i;

  int minx = xcoord[minxi]-8;
  int maxx = xcoord[maxxi]+8;
  int miny = ycoord[minyi]-1;
  int maxy = ycoord[maxyi]+1;

  int m1 =  (minxi == 0)   ?  n-1 : minxi-1;
  while (xcoord[m1] == xcoord[minxi]) if (--m1 < 0) m1 = n-1;

  int m2 =  (minxi == n-1) ?  0   : minxi+1;
  while (xcoord[m2] == xcoord[minxi]) if (++m1 == n) m1 = 0;

  video_width  = (maxx - minx)/8 + 1;
  video_height = maxy - miny + 1;

  if (video_width % 2) video_width++;

  int sz = video_width * video_height;
  video_buf = new unsigned char[sz];

  unsigned char* video_stop   = video_buf + sz;

  for(unsigned char* p=video_buf; p < video_stop; p++) *p = 0;

  // move bounding box to origin

  for(i=0; i<n; i++)
  { xcoord[i] -= minx;
    ycoord[i] -= miny;
   }


  // draw outline of polygon into video buffer

  // bounding box
  int xm = 8*video_width-1;
  int ym = video_height-1;
  x_line(0,0,0,0,ym);
  x_line(0,0,ym,xm,ym);
  x_line(0,xm,ym,xm,0);
  x_line(0,xm,0,0,0);

  // polygon
  for(i=0; i<n-1; i++)
      x_line(0,xcoord[i],ycoord[i],xcoord[i+1],ycoord[i+1]);

  x_line(0,xcoord[0],ycoord[0],xcoord[n-1],ycoord[n-1]);

  // compute an interior point

  int x = (xcoord[m1] + xcoord[m2] + xcoord[minxi])/3;
  int y = (ycoord[m1] + ycoord[m2] + ycoord[minxi])/3;

  // fill

  fill_bits(video_buf+y*video_width+x/8,128>>(x%8));

  // transfer box back to video memory

  unsigned char* q  = video_buf;
  for(i=0; i<video_width;i++) *q++ = 0; 
  for(i=0; i<video_height-2;i++) 
  { q[0] = 0;
    q[video_width-1] = 0;
    q += video_width;
   }
  for(i=0; i<video_width;i++) *q++ = 0; 

  VgaWindow win = set_draw_window(w);
  clip_bitmap(win,minx,miny,video_buf,video_width,video_height);
  minx += win->xpos;
  miny += win->ypos;
  maxx += win->xpos;
  maxy += win->ypos;
  vga_bitmap(minx,miny,video_buf,video_width,video_height);

  delete[] video_buf;
  video_buf = 0;

  vga_flush(minx,miny,maxx,maxy);
}


void x_box(Window w, int x0, int y0, int x1, int y1)
{ VgaWindow win = set_draw_window(w);

  if (x0 > x1) SWAP(x0,x1);
  if (y0 > y1) SWAP(y0,y1);

  // clip into window 
  if (x0 < win->clip_x)  x0 = win->clip_x;
  if (x1 > win->clip_x1) x1 = win->clip_x1;
  if (y0 < win->clip_y)  y0 = win->clip_y;
  if (y1 > win->clip_y1) y1 = win->clip_y1;

  if (x0 <= x1 && y0 <= y1) 
  { x0 += win->xpos;
    y0 += win->ypos;
    x1 += win->xpos;
    y1 += win->ypos;
    vga_box(x0,y0,x1,y1);
    vga_flush(x0,y0,x1,y1);
   }
}


void  x_rect(Window w, int x0, int y0, int x1, int y1)
{ if (x0 > x1) SWAP(x0,x1);
  if (y0 > y1) SWAP(y0,y1);
  x_line(w,x0,y0,x1,y0);
  x_line(w,x1,y0,x1,y1);
  x_line(w,x1,y1,x0,y1);
  x_line(w,x0,y1,x0,y0);
/*
  VgaWindow win = set_draw_window(w);
  x0 += win->xpos;
  y0 += win->ypos;
  x1 += win->xpos;
  y1 += win->ypos;
  vga_hline(x0,x1,y0);
  vga_hline(x0,x1,y1);
  vga_vline(x0,y0,y1);
  vga_vline(x1,y0,y1);
  vga_flush(x0,y0,x1,y1);
*/
}


void x_circle(Window w, int x0,int y0,int r0)
{ VgaWindow win = set_draw_window(w);
  int r;

  for (r = r0-win->LINEWIDTH/2; r <= r0+win->LINEWIDTH/2; r++)
  { int y = r;
    int x = 0;
    int e = 3-2*y;

    setpix(win,x0,y0+r);
    setpix(win,x0,y0-r);
    setpix(win,x0+r,y0);
    setpix(win,x0-r,y0);

    for (x=1;x<y;)
      { setpix(win,x0+x,y0+y);
        setpix(win,x0+x,y0-y);
        setpix(win,x0-x,y0+y);
        setpix(win,x0-x,y0-y);
        setpix(win,x0+y,y0+x);
        setpix(win,x0+y,y0-x);
        setpix(win,x0-y,y0+x);
        setpix(win,x0-y,y0-x);
        x++;
        if (e>=0) { y--; e = e - 4*y; }
        e = e + 4*x + 2;
       }

    if (x == y)
    { setpix(win,x0+x,y0+y);
      setpix(win,x0+x,y0-y);
      setpix(win,x0-x,y0+y);
      setpix(win,x0-x,y0-y);
     }
  }

  x0+= win->xpos;
  y0+= win->ypos;
  vga_flush(x0-r,y0-r,x0+r,y0+r);

}



#define setarcpix(win,x,y)\
{ if (orient >= 0)\
   { if (ax*y >= ay*x && x*by >= y*bx) setpix(win,x0+x,y0+y); }\
  else  if (ax*y >= ay*x || x*by >= y*bx) setpix(win,x0+x,y0+y);\
 }
 

void x_arc(Window w, int x0, int y0, int r0, int r1, double start, double angle)
{ VgaWindow win = set_draw_window(w);

  double ax =  cos(start);
  double ay = -sin(start);
  double bx =  cos(start+angle);
  double by = -sin(start+angle);
  double orient = ax*by - ay*bx;

  for (int r = r0-win->LINEWIDTH/2; r <= r0+win->LINEWIDTH/2; r++)
  { int y = r;
    int x = 0;
    int e = 3-2*y;

    setarcpix(win, 0, r);
    setarcpix(win, 0,-r);
    setarcpix(win, r, 0);
    setarcpix(win,-r, 0);

    for (x=1;x<y;)
      { setarcpix(win, x, y);
        setarcpix(win, x,-y);
        setarcpix(win,-x, y);
        setarcpix(win,-x,-y);
        setarcpix(win, y, x);
        setarcpix(win, y,-x);
        setarcpix(win,-y, x);
        setarcpix(win,-y,-x);
        x++;
        if (e>=0) { y--; e = e - 4*y; }
        e = e + 4*x + 2;
       }

    if (x == y)
    { setarcpix(win, x, y);
      setarcpix(win, x,-y);
      setarcpix(win,-x, y);
      setarcpix(win,-x,-y);
     }
  }

  x0+= win->xpos;
  y0+= win->ypos;
  if (r0 < r1) r0 = r1;
  vga_flush(x0-r0,y0-r0,x0+r0,y0+r0);
}




void x_fill_circle(Window w, int x0, int y0, int r)
{ VgaWindow win = set_draw_window(w);
  int y = 1;
  int x = r;
  int e = 3-2*r;

  hline(win,x0-x,x0+x,y0);

  while (y<=x)
  { hline(win,x0-x,x0+x,y0+y);
    hline(win,x0-x,x0+x,y0-y);

    if (y<x && e>=0)
    { hline(win,x0-y,x0+y,y0+x);
      hline(win,x0-y,x0+y,y0-x);
      x--;
      e = e - 4*x;
     }
    y++;
    e = e + 4*y + 2;
   }
}


void x_pixel(Window w, int x, int y) 
{ VgaWindow win =set_draw_window(w);
  setpix(win,x,y);
  x += win->xpos;
  y += win->ypos;
  vga_flush(x,y,x,y);
}


int x_get_pixel(Window w, int, int) 
{ VgaWindow win =set_draw_window(w);
  return 0;
}

void x_pixels(Window w, int n, int* x, int* y)
{ if (n <=0 ) return;
  VgaWindow win =set_draw_window(w);
  int xmin = *x;
  int xmax = *x;
  int ymin = *y;
  int ymax = *y;
  while(n--) 
  { if (xmin > *x) xmin = *x;
    if (xmax < *x) xmax = *x;
    if (ymin > *y) ymin = *y;
    if (ymax < *y) ymax = *y;
    setpix(win,*x++,*y++); 
   }
  xmin += win->xpos;
  ymin += win->ypos;
  xmax += win->xpos;
  ymax += win->ypos;
  vga_flush(xmin,ymin,xmax,ymax);
}


void x_point(Window w, int x, int y) 
{ VgaWindow win = set_draw_window(w);
  setpix(win,x,y);
  setpix(win,x-2,y-2);
  setpix(win,x-1,y-1);
  setpix(win,x+1,y+1);
  setpix(win,x+2,y+2);
  setpix(win,x-2,y+2);
  setpix(win,x-1,y+1);
  setpix(win,x+1,y-1);
  setpix(win,x+2,y-2);
  x += win->xpos;
  y += win->ypos;
  vga_flush(x-2,y-2,x+2,y+2);
}



void x_ellipse(Window w, int x0, int y0, int a, int b)
{ 
  /* Foley, van Dam, Feiner, Huges: Computer Graphics, page 90 */

  VgaWindow win = set_draw_window(w);

  double d1,d2;
  int x,y;
  int a_2 = a*a;
  int b_2 = b*b;

  setpix(win,x0,y0-b);
  setpix(win,x0,y0+b);
  setpix(win,x0-a,y0);
  setpix(win,x0+a,y0);

  x = 0;
  y = b;

  d1 = b*b + a*a*(0.25 - b); 
   
  while (a_2*(y - 0.5) > b_2*(x+1))
  { if (d1 < 0)
      d1 += b_2*(2*x + 3);
    else
      { d1 += b_2*(2*x + 3) + a_2*(2 - 2*y);
        y--;
       }
    x++;
    setpix(win,x0+x,y0+y);
    setpix(win,x0-x,y0+y);
    setpix(win,x0+x,y0-y);
    setpix(win,x0-x,y0-y);
  }

  d2 = b_2*(x+0.5)*(x+0.5) + a_2*(y - 1)*(y - 1) - a_2*b_2;

  while (y > 1)
  { if (d2 < 0)
      { d2 += b_2*(2*x+2)+a_2*(3-2*y);
        x++;
       }
    else
      d2 += a*a*(3-2*y);
    y--;
    setpix(win,x0+x,y0+y);
    setpix(win,x0-x,y0+y);
    setpix(win,x0+x,y0-y);
    setpix(win,x0-x,y0-y);
   }
  x0 += win->xpos;
  y0 += win->ypos;
  if (a < b) a = b; 
  vga_flush(x0-a,y0-a,x0+a,y0+a);
}


void x_fill_ellipse(Window w, int x0, int y0, int a, int b)
{ VgaWindow win = set_draw_window(w);
  double d1,d2;
  int x,y;
  int a_2 = a*a;
  int b_2 = b*b;

  x = 0;
  y = b;

  d1 = b*b + a*a*(0.25 - b); 
   
  while (a_2*(y - 0.5) > b_2*(x+1))
  { if (d1 < 0)
      d1 += b_2*(2*x + 3);
    else
    { d1 += b_2*(2*x + 3) + a_2*(2 - 2*y);
      hline(win,x0-x,x0+x,y0+y);
      hline(win,x0-x,x0+x,y0-y);
      y--;
     }
    x++;
  }
  hline(win,x0-x,x0+x,y0+y);
  hline(win,x0-x,x0+x,y0-y);

  d2 = b_2*(x+0.5)*(x+0.5) + a_2*(y - 1)*(y - 1) - a_2*b_2;

  while (y > 1)
  { if (d2 < 0)
     { d2 += b_2*(2*x+2)+a_2*(3-2*y);
       x++;
      }
    else
       d2 += a*a*(3-2*y);

    y--;

    hline(win,x0-x,x0+x,y0+y);
    hline(win,x0-x,x0+x,y0-y);
   }

  hline(win,x0-x,x0+x,y0);

}


void x_fill_arc(Window,int,int,int,int,double,double)
{ }




//------------------------------------------------------------------------------
// bitmaps & pixrects
//------------------------------------------------------------------------------

struct x_image {
 int w;
 int h;
 unsigned char* buf;
};


static unsigned char rev_byte(unsigned char c)
{ unsigned char c1 = 0x00;
   for(int i=0; i<8; i++)
   { c1 <<= 1;
     if (c&1) c1 |= 1;
     c >>= 1;
    }
  return c1;
 }



char* x_create_pixrect(Window w, int wi, int he, char* data, int fg, int bg)
{ VgaWindow win = set_draw_window(w);
  char* p = new char[wi*he];
  return p;
 }


char* x_create_pixrect(Window w, int x0, int y0, int x1, int y1)
{ VgaWindow win = set_draw_window(w);
  x_image* ip = new x_image;
  if (x0 > x1) SWAP(x0,x1);
  if (y0 > y1) SWAP(y0,y1);
  ip->w = x1-x0+1;
  ip->h = y1-y0+1;
  ip->buf = vga_getimage(x0+win->xpos,y0+win->ypos,ip->w,ip->h);
  return (char*)ip;
 }


void x_insert_pixrect(Window w, char* p)
{ VgaWindow win = set_draw_window(w);
  int x = win->xpos; 
  int y = win->ypos; 
  x_image* ip = (x_image*)p;
  vga_putimage(x,y,ip->w,ip->h,ip->buf); 
  vga_flush(x,y,x+ip->w,y+ip->h);
 }

void x_insert_pixrect(Window w, int x, int y, char* p)
{ VgaWindow win = set_draw_window(w);
  x += win->xpos; 
  y += win->ypos; 
  x_image* ip = (x_image*)p;
  vga_putimage(x,y-ip->h+1,ip->w,ip->h,ip->buf); 
  vga_flush(x,y-ip->h+1,x+ip->w,y);
 }


void x_insert_pixrect(Window w, int x, int y, char* p, int x0, int y0, 
                                                       int x1, int y1)
{  /* not implemented */  }


void x_delete_pixrect(char* p) 
{ x_image* ip = (x_image*)p;
  vga_delimage(ip->buf); 
  delete ip;
}

/*
void x_copy_pixrect(Window w,int x0,int y0,int x1,int y1,int x,int y)
{ VgaWindow win = set_draw_window(w);
  int wi = x1 - x0 + 1;
  int he = y1 - y0 + 1;
  x0 += win->xpos; 
  y0 += win->ypos; 
  x  += win->xpos; 
  y  += win->ypos; 
  vga_copyimage(x0,y0,wi,he,x,y);
}
*/


void x_copy_pixrect(Window w, int x1, int y1, int x2, int y2, int x, int y)
{ char* im = x_create_pixrect(w,x1,y1,x2,y2); 
  x_insert_pixrect(w,x,y,im);
  x_delete_pixrect(im);
 }




char* x_create_bitmap(Window w, int width, int height, char* p)
{ VgaWindow win = set_draw_window(w);

  x_image* im = new x_image;

  int bytes = (width+7)/8;

  int sz = height * bytes;

  char* q = new char[sz];
  im->w = width;
  im->h = height;
  im->buf = (unsigned char*)q;

  char* stop = q + sz; 
  while (q < stop) *q++ = rev_byte(*p++);

  return (char*)im;
}



void x_insert_bitmap(Window w, int x, int y, char* bm)
{ VgaWindow win = set_draw_window(w);
  
  x_image* ip = (x_image*)bm;

  int bytes  = (ip->w + 7)/8;

  int width  = ip->w;
  int height = ip->h;

  y -= (height-1);

  clip_bitmap(win,x,y,ip->buf,bytes,height);
  x += win->xpos;
  y += win->ypos;
  vga_bitmap(x,y,ip->buf,bytes,height);
  vga_flush(x,y,x+width,y+height);
}


void x_delete_bitmap(char* bm) { x_delete_pixrect(bm); }


void x_insert_bitmap(Window w, int x, int y, int width, int height, 
                                                        unsigned char* data)
{ VgaWindow win = set_draw_window(w);

  int len =  width/8;
  if (width % 8) len++;

  unsigned char* buf = new unsigned char[len*height];
  unsigned char* q = buf;
  unsigned char* stop = q + len*height;

  while (q < stop) *q++ = rev_byte(*data++);

  vga_set_mode(0);
  clip_bitmap(win,x,y,buf,len,height);
  vga_bitmap(x+win->xpos,y+win->ypos,buf,len,height);

  delete[] buf;
}




void x_text(Window w, int x, int y, const char *text)
{ VgaWindow win = set_draw_window(w);

  if (y < win->clip_y || y+FONT_WIDTH > win->clip_y1 ) return;

  int bw = (FONT_WIDTH + 7)/8;

  int x1 = x + strlen(text)*FONT_WIDTH - 1;
  int y1 = y + FONT_HEIGHT - 1;

  if (win->TEXTMODE == opaque)  // clear background
  { int save_color = x_set_color(w,win->bg_col);
    drawing_mode save_mode = x_set_mode(w,src_mode);
    x_box(w,x,y,x1,y1);
    x_set_mode(w,save_mode);
    x_set_color(w,save_color);
   }

  x  += win->xpos;
  y  += win->ypos;
  x1 += win->xpos;
  y1 += win->ypos;

  int xmin = win->clip_x  + win->xpos;
  int xmax = win->clip_x1 + win->xpos - FONT_WIDTH;

  int xx = x;
  for (const char* q = text; *q; q++) 
  { unsigned char* p = FONT + (*q & 127)*FONT_HEIGHT*bw;
    if (xx >= xmin && xx <= xmax)
       vga_bitmap(xx,y,p,bw,FONT_HEIGHT);
    xx += FONT_WIDTH;
   }

  vga_flush(x,y,x1,y1);
}


void x_text(Window w, int x, int y, const char *text, int l)
{ char* str = new char[strlen(text)+1];
  strcpy(str,text);
  str[l] = '\0';
  x_text(w,x,y,str);
  delete[] str;
}

void x_ctext(Window w, int x, int y, const char* str)
{ x_text(w,x-(x_text_width(w,str)-1)/2, y-(x_text_height(w,str)-1)/2, str); }


void x_set_clip_rect(Window w, int x ,int y, int width,int height) 
{ VgaWindow win = win_list[w];
  win->clip_x = x;
  win->clip_y = y;
  if (x+width > win->width) width -= (x+width - win->width);
  if (y+height > win->height) height -= (y+height - win->height);
  win->clip_x1 = x + width - 1;
  win->clip_y1 = y + height - 1;
}



static drawing_mode save_mode;
static int save_lw;
static line_style save_ls;


void x_set_read_gc(Window w)
{ save_mode = x_set_mode(w,xor_mode);
  save_ls   = x_set_line_style(w,solid);
  save_lw   = x_set_line_width(w,1);
  x_set_color(w,black);
 }

void x_reset_gc(Window w)
{ x_set_mode(w,save_mode);
  x_set_line_style(w,save_ls);
  x_set_line_width(w,save_lw);
 }


