
// gfxgraf.cpp
//
// Copyright (c) 1995 by Toshiaki Tsuji, all rights reserved.

#include "stdgfx.h"
#include "gfxgraf.h"
#include "lgfxdib.h"
#include <math.h>

POLYGON::POLYGON ()
  {
    NumPoints = 0;
    Points = NULL;  
  }; // End of Constructor for POLYGON

POLYGON::~POLYGON ()
  {
    DestroyPoints ();  
  }; // End of Destructor for POLYGON

VOID POLYGON::CreatePoints ( LONG Num )
  {
    DestroyPoints ();
    Points = new XYPOINT [Num];
    NumPoints = Num;  
  }; // End of CreatePoints for POLYGON

VOID POLYGON::DestroyPoints ()
  {
    if (Points!=NULL)
      delete Points;
    Points = NULL;
    NumPoints = 0;    
  }; // End of DestroyPoints for POLYGON

EDGETABLE::EDGETABLE ()
  {
  } // End of Constructor for EDGETABLE

EDGETABLE::~EDGETABLE ()
  {
  } // End of Destructor for EDGETABLE

//*************************************************
//
// Graphics Class
//
//*************************************************

POLYGON *Poly1 = NULL;
POLYGON *Poly2 = NULL;
XYPOINT *Left = NULL;
XYPOINT *Right = NULL;

GRAFIX Grafix;

GRAFIX::GRAFIX () : DCIClass ()
  {
    Init ();
    MagH = MagV = DivH = DivV = 1;
    Poly1->CreatePoints ( 50 );
    Poly2->CreatePoints ( 50 );
  } // End of Constructor for GRAFIX

GRAFIX::~GRAFIX ()
  {
    DeInit ();
  } // End of Destructor for GRAFIX

BOOLEAN GRAFIX::Init ()
  {
    DisplayDriver = NULL;
    Poly1 = new POLYGON ();
    Poly2 = new POLYGON ();
    Left = new XYPOINT [768];
    Right = new XYPOINT [768];

    return TRUE;
  } // End of Init for GRAFIX

VOID GRAFIX::DeInit ()
  {
    if (DisplayDriver!=NULL)
      delete DisplayDriver;
    DisplayDriver = NULL;

    if (Poly1!=NULL)
      delete Poly1;
    Poly1 = NULL;

    if (Poly2!=NULL)
      delete Poly2;
    Poly2 = NULL;

    if (Left!=NULL)
      delete Left;
    Left = NULL;

    if (Right!=NULL)
      delete Right;
    Right = NULL;
  } // End of DeInit for GRAFIX

BOOLEAN GRAFIX::SetDisplay ( DISPLAYDATA *Data )
  {
    BOOLEAN Result;
    if (Data==NULL)
      {
        return FAILURE;  
      } // End if
    if (DisplayDriver!=NULL)
      {
        delete DisplayDriver;
        DisplayDriver = NULL;  
      } // End if
      
    #if defined (__FORWINDOWS__)
      #if defined (__FORGDK__)
        if (Data->UseDirectDraw)
          DisplayDriver = new DDRAWDRIVER ();
        else  
          DisplayDriver = new GDIDRIVER ();
      #else    
        DisplayDriver = new GDIDRIVER ();        
      #endif
      
      if (DisplayDriver==NULL)
        {
          Error.SetError ( ERR_NOMEMORY );
          return FAILURE;
        } // End if
      Result = DisplayDriver->SetUp ( Data );
      return Result;
    #elif defined (__FOROS2__)
      DisplayDriver = new GPIDRIVER ();
      if (DisplayDriver==NULL)
        {
          Error.SetError ( ERR_NOMEMORY );
          return FAILURE;
        } // End if
      Result = DisplayDriver->SetUp ( Data );
      return Result;
    #elif defined (__FORDOS__)
      MODEINFO ModeInfo;

      ModeInfo = ModeTable[Data->Mode];
      if (ModeInfo.IsSVGA==TRUE)
        {
          DisplayDriver = new VESADRIVER ();
          if (DisplayDriver==NULL)
            {
              Error.SetError ( ERR_NOMEMORY );
              return FAILURE;
            } // End if
          Result = DisplayDriver->SetUp ( Data );
        } // End if
      else
        {
          DisplayDriver = new VGADRIVER ();
          if (DisplayDriver==NULL)
            {
              Error.SetError ( ERR_NOMEMORY );
              return FAILURE;
            } // End if
          Result = DisplayDriver->SetUp ( Data );
        } // End else
      return Result;
    #elif defined (__FORUNIX__)
      DisplayDriver = new XWINDRIVER ();
      if (DisplayDriver==NULL)
        {
          Error.SetError ( ERR_NOMEMORY );
          return FAILURE;
        } // End if
      Result = DisplayDriver->SetUp ( Data );
      return Result;
    #endif    
  } // End of SetDisplay for GRAFIX

VOID GRAFIX::ResetDisplay ()
  {
    if (DisplayDriver==NULL)
      return;

    DisplayDriver->Reset ();
  } // End of ResetDisplay for GRAFIX

VOID GRAFIX::SetScaleFactor ( LONG MH, LONG DH, LONG MV, LONG DV )
  {
    if (MH>=0)  
      MagH = MH;
    else
      MagH = 1;      
    if (DH>=0)  
      DivH = DH;
    else
      DivH = 1;

    if (MV>=0)  
      MagV = MV;
    else
      MagV = 1;      
    if (DV>=0)  
      DivV = DV;
    else
      DivV = 1;  
      
    if (DisplayDriver!=NULL)
      DisplayDriver->SetScaleFactor ( MagH, DivH, MagV, DivV );
  } // End of SetScaleFactor for GRAFIX
  
VOID GRAFIX::ClearDisplay ( HDISPLAY hDisplay, LONG Color )
  {
    if (DisplayDriver==NULL)
      return;

    DisplayDriver->Clear ( hDisplay, Color );
  } // End of ClearDisplay for GRAFIX

VOID GRAFIX::WaitForRetrace ( LONG Count )
  {      
    #if defined (__FORDOS__)
      WaitForRetraceMany ( Count );
    #else
      if (Count)
        {}
    #endif  
  } // End of WaitForRetrace for GRAFIX

VOID GRAFIX::WaitForRetrace ()
  {
    #if defined (__FORDOS__)
      WaitForRetraceOnce ();
    #endif  
  } // End of WaitForRetrace for GRAFIX

VOID GRAFIX::WaitForRetraceEnd ()
  {
    #if defined (__FORDOS__)
      WaitForRetraceTerminate ();
    #endif  
  } // End of WaitForRetrace for GRAFIX

VOID GRAFIX::CopyImage ( IMAGE *Src, LONG Sx, LONG Sy, LONG Wd, LONG Ht,
                         IMAGE *Dest, LONG Cx, LONG Cy )
  {
    if ((Src==NULL)||(Dest==NULL))
      return;

    if ((Wd==0)||(Ht==0))
      return;
    
    RECTANGLE ViewPort;
    ViewPort.x1 = 0;
    ViewPort.y1 = 0;
    ViewPort.x2 = Src->GetWidth()-1;
    ViewPort.y2 = Src->GetHeight()-1;

    if (ClipRect ( &ViewPort, &Sx, &Sy, &Wd, &Ht )==FALSE)
      return;

    ViewPort = Dest->GetViewPort ();
    if (ClipDest ( &ViewPort, &Sx, &Sy, &Wd, &Ht, &Cx, &Cy )==FALSE)
      return;

    INT i;

    BYTE *SrcBuffer;
    BYTE *DestBuffer;

    SrcBuffer = Src->SetOffset ( Sx, Sy );
    DestBuffer = Dest->SetOffset ( Cx, Cy );

    if (Src->Transparent<0)
      {
        for (i=0;i<Ht;i++)
          {
            memcpy ( DestBuffer, SrcBuffer, Wd );
            SrcBuffer = Src->GetNextRow ( ROW_DOWN );
            DestBuffer = Dest->GetNextRow ( ROW_DOWN );
          } // End for
      } // End if
    else
      {
        for (i=0;i<Ht;i++)
          {
            TransBlt256To256 ( DestBuffer, SrcBuffer, Wd, (BYTE)Src->Transparent );
            SrcBuffer = Src->GetNextRow ( ROW_DOWN );
            DestBuffer = Dest->GetNextRow ( ROW_DOWN );
          } // End for
      } // End else
  } // End of CopyImage for GRAFIX

VOID GRAFIX::ScaleImage ( IMAGE *Src, LONG Sx, LONG Sy, LONG Wd, LONG Ht,
                          IMAGE *Dest, LONG Cx, LONG Cy )
  {
    if ((Src==NULL)||(Dest==NULL))
      return;

    if ((Wd==0)||(Ht==0))
      return;
      
    RECTANGLE ViewPort;
    LONG DWd,DHt;

    ViewPort.x1 = 0;
    ViewPort.y1 = 0;
    ViewPort.x2 = Src->GetWidth()-1;
    ViewPort.y2 = Src->GetHeight()-1;

    if (ClipRect ( &ViewPort, &Sx, &Sy, &Wd, &Ht )==FALSE)
      return;

    DWd = (MagH*Wd)/DivH;
    DHt = (MagV*Ht)/DivV;
    
    ViewPort = Dest->GetViewPort ();
    if (ClipDest ( &ViewPort, &Sx, &Sy, &DWd, &DHt, &Cx, &Cy )==FALSE)
      return;

    INT i;

    BYTE *SrcBuffer;
    BYTE *DestBuffer;

    SrcBuffer = Src->SetOffset ( Sx, Sy );
    DestBuffer = Dest->SetOffset ( Cx, Cy );

    WORD Error = 0;
    DWORD AddError = (((LONG)DivH<<16)/MagH);

    if (Src->Transparent<0)
      {
        for (i=0;i<DHt;i++)
          {
            ScaleBlt256To256 ( DestBuffer, SrcBuffer, DWd, Error, AddError );
            SrcBuffer = Src->SetOffset ( Sx, Sy+(i*DivV)/MagV );
            DestBuffer = Dest->GetNextRow ( ROW_DOWN );
          } // End for
      } // End if
    else
      {
        for (i=0;i<DHt;i++)
          {
            TransBlt256To256 ( DestBuffer, SrcBuffer, Wd, (BYTE)Src->Transparent );
            SrcBuffer = Src->GetNextRow ( ROW_DOWN );
            DestBuffer = Dest->GetNextRow ( ROW_DOWN );
          } // End for
      } // End else
  } // End of ScaleImage for GRAFIX
                            
VOID GRAFIX::AdjustImageSize ( IMAGE *Image, LONG Wd, LONG Ht )
  {
    IMAGE *TempImage;

    if ((Image->GetWidth()==Wd)&&(Image->GetHeight()==Ht))
      return;
      
    SetScaleFactor ( 1, 1, 1, 1 );
    TempImage = new IMAGE ( Image->GetDirection () );
    if (TempImage->Create ( Image->GetFormat (), Image->GetWidth(), Image->GetHeight() )==FAILURE)
      {
        delete TempImage;  
        return;
      } // End if  

    CopyImage ( Image, 0, 0, Image->GetWidth(), Image->GetHeight(),
                TempImage, 0, 0 );

    if (Image->Create ( TempImage->GetFormat(), Wd, Ht )==FAILURE)
      {
        delete TempImage;
        return;  
      } // End if
      
    SetScaleFactor ( Wd, TempImage->GetWidth(), Ht, TempImage->GetHeight() );
    ScaleImage ( TempImage, 0, 0, TempImage->GetWidth(), TempImage->GetHeight(),
                 Image, 0, 0 ); 
    SetScaleFactor ( 1, 1, 1, 1 );
    delete TempImage;
  } // End of AdjustImageSize for GRAFIX

VOID GRAFIX::DisplayImage ( HDISPLAY hDisplay, IMAGE *Image, LONG Sx, LONG Sy,
                            LONG Wd, LONG Ht, LONG Cx, LONG Cy )
  {
    if (DisplayDriver==NULL)
      return;

    if ((Wd==0)||(Ht==0))
      return;
      
    RECTANGLE ViewPort;
    LONG DWd,DHt;

    ViewPort.x1 = 0;
    ViewPort.y1 = 0;
    ViewPort.x2 = Image->GetWidth()-1;
    ViewPort.y2 = Image->GetHeight()-1;
    if (ClipRect ( &ViewPort, &Sx, &Sy, &Wd, &Ht )==FALSE)
      return;

    DWd = (MagH*Wd)/DivH;
    DHt = (MagV*Ht)/DivV;

    ViewPort.x1 = 0;
    ViewPort.y1 = 0;
    ViewPort.x2 = DisplayDriver->GetWidth ( hDisplay );
    ViewPort.y2 = DisplayDriver->GetHeight ( hDisplay );

    if (ClipDest ( &ViewPort, &Sx, &Sy, &DWd, &DHt, &Cx, &Cy )==FALSE)
      return;

    DisplayDriver->DisplayImage ( Image, Sx, Sy, Wd, Ht, hDisplay, Cx, Cy, DWd, DHt );
  } // End of DisplayImage for GRAFIX

INT GRAFIX::GetImageFileType ( FILEHANDLE f )
  {
    CHAR Ch;
    CHAR ID[8];

    // Check for PCX
    File.Seek ( f, 0, FROM_BEGIN );
    Ch = (CHAR)File.GetCh ( f );
    if (Ch==0x0A)
      {
        return PCXFILE;
      } // End if

    // Check for BMP
    File.Seek ( f, 0, FROM_BEGIN );
    File.Read ( f, ID, 2 );
    if (strncmp(ID,"BM",2)==0)
      {
        return BMPFILE;
      } // End if

    // Check for GIF
    File.Seek ( f, 0, FROM_BEGIN );
    File.Read ( f, ID, 3 );
    if (strncmp(ID,"GIF",3)==0)
      {
        File.Read ( f, ID, 3 );
        if ((strncmp(ID,"87a",3)==0)||(strncmp(ID,"89a",3)==0))
          {
            return GIFFILE;
          } // End if
      } // End if

    return UNKNOWN;
  } // End of GetImageFileType for GRAFIX

BOOLEAN GRAFIX::ClipRect ( RECTANGLE *ViewPort, LONG *Sx, LONG *Sy, LONG *Wd, LONG *Ht )
  {
    LONG StartX,StartY,EndX,EndY;

    StartX = *Sx;
    StartY = *Sy;
    EndX = *Sx+*Wd-1;
    EndY = *Sy+*Ht-1;

    if (StartX<ViewPort->x1)
      StartX = ViewPort->x1;
    else if (StartX>ViewPort->x2)
      return FAILURE;

    if (StartY<ViewPort->y1)
      StartY = ViewPort->y1;
    else if (StartY>ViewPort->y2)
      return FAILURE;

    if (EndX<ViewPort->x1)
      return FAILURE;
    else if (EndX>ViewPort->x2)
      EndX = ViewPort->x2;

    if (EndY<ViewPort->y1)
      return FAILURE;
    else if (EndY>ViewPort->y2)
      EndY = ViewPort->y2;

    *Wd = EndX-StartX+1;
    *Ht = EndY-StartY+1;
    *Sx = StartX;
    *Sy = StartY;

    return SUCCESS;
  } // End of ClipRect for GRAFIX

BOOLEAN GRAFIX::ClipDest ( RECTANGLE *ViewPort, LONG *Sx, LONG *Sy, LONG *Wd, LONG *Ht,
                           LONG *Cx, LONG *Cy )
  {
    LONG StartX,StartY,EndX,EndY;

    StartX = *Cx;
    StartY = *Cy;
    EndX = *Cx+*Wd-1;
    EndY = *Cy+*Ht-1;

    if (StartX<ViewPort->x1)
      StartX = ViewPort->x1;
    else if (StartX>ViewPort->x2)
      return FAILURE;

    if (StartY<ViewPort->y1)
      StartY = ViewPort->y1;
    else if (StartY>ViewPort->y2)
      return FAILURE;

    if (EndX<ViewPort->x1)
      return FAILURE;
    else if (EndX>ViewPort->x2)
      EndX = ViewPort->x2;

    if (EndY<ViewPort->y1)
      return FAILURE;
    else if (EndY>ViewPort->y2)
      EndY = ViewPort->y2;

    *Wd = EndX-StartX+1;
    *Ht = EndY-StartY+1;
    *Sx += StartX-*Cx;
    *Sy += StartY-*Cy;
    *Cx = StartX;
    *Cy = StartY;

    return SUCCESS;
  } // End of ClipDest for GRAFIX

VOID GRAFIX::FindIntersect ( XYPOINT *In, XYPOINT *Out, XYPOINT *I,
                             RECTANGLE ViewPort, LONG Code )
  {
    double Ratio;
    switch (Code)
      {
        case 0 : // Top  
          Ratio = (double)(ViewPort.y1-Out->y)/(In->y-Out->y);
          I->x = (LONG)((In->x-Out->x)*Ratio + Out->x);
          I->y = ViewPort.y1;
          break;
        case 1 : // Left
          Ratio = (double)(ViewPort.x1-Out->x)/(In->x-Out->x);
          I->x = ViewPort.x1;
          I->y = (LONG)((In->y-Out->y)*Ratio + Out->y);
          break;
        case 2 : // Bottom  
          Ratio = (double)(ViewPort.y2-Out->y)/(In->y-Out->y);
          I->x = (LONG)((In->x-Out->x)*Ratio + Out->x);
          I->y = ViewPort.y2;
          break;
        case 3 : // Right
          Ratio = (double)(ViewPort.x2-Out->x)/(In->x-Out->x);
          I->x = ViewPort.x2;
          I->y = (LONG)((In->y-Out->y)*Ratio + Out->y);
          break;
      } // End if
    I->u = (LONG)((In->u-Out->u)*Ratio + Out->u);  
    I->v = (LONG)((In->v-Out->v)*Ratio + Out->v);  
  } // End of FindIntersect for GRAFIX
                               
VOID GRAFIX::ClipPolygon ( POLYGON *SrcPoly, POLYGON *ClipPoly,
                           RECTANGLE ViewPort )
  {
    LONG NumPoints;
    LONG Count;
    LONG i;
    XYPOINT P1,P2,I;    
    
    // Clip for Top
    NumPoints = SrcPoly->NumPoints;
    Count = 0;
    
    P1 = SrcPoly->Points[NumPoints-1];
    for (i=0;i<NumPoints;i++)
      {
        P2 = SrcPoly->Points[i];
        if (P1.y>=ViewPort.y1)
          {
            if (P2.y>=ViewPort.y1)
              {
                Poly1->Points[Count++] = P2;  
              } // End if
            else
              {
                FindIntersect ( &P1, &P2, &I, ViewPort, 0 );
                Poly1->Points[Count++] = I;  
              } // End else  
          } // End if
        else
          {
            if (P2.y>=ViewPort.y1)
              {
                FindIntersect ( &P2, &P1, &I, ViewPort, 0 );
                Poly1->Points[Count++] = I;  
                Poly1->Points[Count++] = P2;  
              } // End if
          } // End if
        P1 = P2;  
      } // End for
    
    // Clip for Left
    NumPoints = Count;
    Count = 0;
    
    P1 = Poly1->Points[NumPoints-1];
    for (i=0;i<NumPoints;i++)
      {
        P2 = Poly1->Points[i];
        if (P1.x>=ViewPort.x1)
          {
            if (P2.x>=ViewPort.x1)
              {
                Poly2->Points[Count++] = P2;  
              } // End if
            else
              {
                FindIntersect ( &P1, &P2, &I, ViewPort, 1 );
                Poly2->Points[Count++] = I;  
              } // End else  
          } // End if
        else
          {
            if (P2.x>=ViewPort.x1)
              {
                FindIntersect ( &P2, &P1, &I, ViewPort, 1 );
                Poly2->Points[Count++] = I;  
                Poly2->Points[Count++] = P2;  
              } // End if
          } // End if
        P1 = P2;  
      } // End for
      
    // Clip for Bottom
    NumPoints = Count;
    Count = 0;
    
    P1 = Poly2->Points[NumPoints-1];
    for (i=0;i<NumPoints;i++)
      {
        P2 = Poly2->Points[i];
        if (P1.y<=ViewPort.y2)
          {
            if (P2.y<=ViewPort.y2)
              {
                Poly1->Points[Count++] = P2;  
              } // End if
            else
              {
                FindIntersect ( &P1, &P2, &I, ViewPort, 2 );
                Poly1->Points[Count++] = I;  
              } // End else  
          } // End if
        else
          {
            if (P2.y<=ViewPort.y2)
              {
                FindIntersect ( &P2, &P1, &I, ViewPort, 2 );
                Poly1->Points[Count++] = I;  
                Poly1->Points[Count++] = P2;  
              } // End if
          } // End if
        P1 = P2;  
      } // End for
      
    // Clip for Right
    NumPoints = Count;
    Count = 0;
    
    P1 = Poly1->Points[NumPoints-1];
    for (i=0;i<NumPoints;i++)
      {
        P2 = Poly1->Points[i];
        if (P1.x<=ViewPort.x2)
          {
            if (P2.x<=ViewPort.x2)
              {
                Poly2->Points[Count++] = P2;  
              } // End if
            else
              {
                FindIntersect ( &P1, &P2, &I, ViewPort, 3 );
                Poly2->Points[Count++] = I;  
              } // End else  
          } // End if
        else
          {
            if (P2.x<=ViewPort.x2)
              {
                FindIntersect ( &P2, &P1, &I, ViewPort, 3 );
                Poly2->Points[Count++] = I;  
                Poly2->Points[Count++] = P2;  
              } // End if
          } // End if
        P1 = P2;  
      } // End for

    NumPoints = Count;
    
    ClipPoly->CreatePoints ( NumPoints );
    for (i=0;i<NumPoints;i++)
      {
        ClipPoly->Points[i] = Poly2->Points[i];  
      } // End for        
  } // End of ClipPolygon for GRAFIX

VOID GRAFIX::DrawPolygon ( IMAGE *Dest, POLYGON *Poly )
  {
    INT i;
    for (i=0;i<Poly->NumPoints-2;i++)
      {
        DrawLine ( Dest, Poly->Points[i].x, Poly->Points[i].y,
                   Poly->Points[i+1].x, Poly->Points[i+1].y );
      } // End for
      
    DrawLine ( Dest, Poly->Points[i].x, Poly->Points[i].y,
               Poly->Points[0].x, Poly->Points[0].y );
  } // End of DrawPolygon for GRAFIX

VOID GRAFIX::CreateEdgeTable ( POLYGON *Poly, EDGETABLE *EdgeTable )
  {
    INT i;
    LONG Hi,HiP,Lo,LoP;
    LONG NumPoints;
    XYPOINT *Points;
    
    Points = Poly->Points;

    HiP = LoP = 0;
    Hi = Lo = Points[0].y;
    
    NumPoints = Poly->NumPoints;
    
    for (i=1;i<NumPoints;i++)
      {
        if (Hi>Points[i].y)
          {
            Hi = Points[i].y;
            HiP = i;
          } // End if  
        else if (Lo<Points[i].y)
          {
            Lo = Points[i].y;
            LoP = i;
          } // End if  
      } // End for

    LONG CurP,NextP;
    XYPOINT *Edge1,*Edge2;

    EdgeTable->Top = Hi;
    EdgeTable->Height = Lo-Hi+1;
    Edge1 = Left;
    Edge2 = Right;

    LONG Sx,Ex;
    LONG Sy,Ey;
    LONG Su,Eu;
    LONG Sv,Ev;
    LONG Row;
    double CurX;
    double CurU,CurV;
    double Slope;
    double Du,Dv;
    
    // Get Counter Clockwise edges
    CurP = HiP;
    NextP = CurP-1;
    if (NextP<0)
      NextP = NumPoints-1;

    Row = 0;  
    while (Row<EdgeTable->Height)
      {
        Sx = Points[CurP].x;  
        Sy = Points[CurP].y;  
        Ex = Points[NextP].x;  
        Ey = Points[NextP].y;
        Su = Points[CurP].u;  
        Sv = Points[CurP].v;  
        Eu = Points[NextP].u;  
        Ev = Points[NextP].v;
        
        if ((Sx==Ex)&&(Sy==Ey))
          {
            CurP = NextP;
            if (CurP==LoP)
              break;
            NextP = CurP-1;
            if (NextP<0)
              NextP = NumPoints-1;          
            continue;
          } // End if

        double Ratio;
        Ratio = (double)1/(Ey-Sy+1);
        Slope = (double)(Ex-Sx)*Ratio;
        Du = (double)(Eu-Su)*Ratio;
        Dv = (double)(Ev-Sv)*Ratio;
        CurX = Sx;
        CurU = Su;
        CurV = Sv;
        for (i=0;i<(Ey-Sy+1);i++)
          {
            CurX += Slope;
            CurU += Du;
            CurV += Dv;
            Edge1[Row].x = (LONG)CurX;
            Edge1[Row].u = (LONG)CurU;
            Edge1[Row++].v = (LONG)CurV;
          } // End for
        CurP = NextP;
        NextP = CurP-1;
        if (NextP<0)
          NextP = NumPoints-1;          
      } // End while

    if (Row<EdgeTable->Height)
      EdgeTable->Height = Row;
    
    // Get Clockwise edges
    CurP = HiP;
    NextP = CurP+1;
    if (NextP>=NumPoints)
      NextP = 0;

    Row = 0;  
    while (Row<EdgeTable->Height)
      {
        Sx = Points[CurP].x;  
        Sy = Points[CurP].y;  
        Ex = Points[NextP].x;  
        Ey = Points[NextP].y;
        Su = Points[CurP].u;  
        Sv = Points[CurP].v;  
        Eu = Points[NextP].u;  
        Ev = Points[NextP].v;
        
        if ((Sx==Ex)&&(Sy==Ey))
          {
            CurP = NextP;
            if (CurP==LoP)
              break;
            NextP = CurP+1;
            if (NextP>=NumPoints)
              NextP = 0;          
            continue;
          } // End if

        double Ratio;
        Ratio = (double)1/(Ey-Sy+1);
        Slope = (double)(Ex-Sx)*Ratio;
        Du = (double)(Eu-Su)*Ratio;
        Dv = (double)(Ev-Sv)*Ratio;
        CurX = Sx;
        CurU = Su;
        CurV = Sv;
        
        for (i=0;i<(Ey-Sy+1);i++)
          {
            CurX += Slope;
            CurU += Du;
            CurV += Dv;
            Edge2[Row].x = (LONG)CurX;
            Edge2[Row].u = (LONG)CurU;
            Edge2[Row++].v = (LONG)CurV;
          } // End for
        CurP = NextP;
        NextP = CurP+1;
        if (NextP>=NumPoints)
          NextP = 0;          
      } // End while

    if (Row<EdgeTable->Height)
      EdgeTable->Height = Row;
    
    // Determine which one is right or left
    for (i=0;i<EdgeTable->Height;i++)
      {
        if (Edge1[i].x>Edge2[i].x)
          {
            SwapValue ( &Edge1, &Edge2 );
            break;  
          } // End if
        else if (Edge1[i].x<Edge2[i].x)
          break;
      } // End for

    EdgeTable->Left = Edge1;  
    EdgeTable->Right = Edge2;
  } // End of CreateEdgeTable for GRAFIX
  
VOID GRAFIX::FillPolygon ( IMAGE *Dest, POLYGON *Poly )
  {
    RECTANGLE ViewPort;
    POLYGON *ClipPoly;
    EDGETABLE *EdgeTable;

    EdgeTable = new EDGETABLE ();    
    ClipPoly = new POLYGON ();
    ViewPort = Dest->GetViewPort ();
    ClipPolygon ( Poly, ClipPoly, ViewPort );
    
    if (ClipPoly->NumPoints>2)
      {
        CreateEdgeTable ( ClipPoly, EdgeTable );
        LONG i;
        LONG x1,x2;
        LONG Wd;
        BYTE *DestOffset;
        DestOffset = Dest->SetOffset ( 0, EdgeTable->Top );
        for (i=0;i<EdgeTable->Height;i++)
          {
            x1 = EdgeTable->Left[i].x;  
            x2 = EdgeTable->Right[i].x;
            Wd = x2-x1+1;
            memset ( DestOffset+x1, BGColor, Wd );
            DestOffset = Dest->GetNextRow ( ROW_DOWN );
          } // End for
      } // End if
      
    delete EdgeTable;
    delete ClipPoly;
  } // End of FillPolygon for GRAFIX

VOID GRAFIX::RotateImage ( IMAGE *Src, LONG Sx, LONG Sy, LONG Wd, LONG Ht,
                           LONG CenterX, LONG CenterY, float Angle,
                           IMAGE *Dest, LONG Cx, LONG Cy )
  {
    if ((Src==NULL)||(Dest==NULL))
      return;

    if ((Wd==0)||(Ht==0))
      return;
      
    RECTANGLE ViewPort;

    ViewPort.x1 = 0;
    ViewPort.y1 = 0;
    ViewPort.x2 = Src->GetWidth()-1;
    ViewPort.y2 = Src->GetHeight()-1;

    if (ClipRect ( &ViewPort, &Sx, &Sy, &Wd, &Ht )==FALSE)
      return;

    POLYGON *Poly,*ClipPoly;
    
    Poly = new POLYGON ();
    ClipPoly = new POLYGON ();

    double Sz,Cz;
    Angle = (float)((Angle*6.281) / 360);
    Sz = sin ( Angle );
    Cz = cos ( Angle );

    double ScaleX,ScaleY;

    ScaleX = (double)MagH/DivH;
    ScaleY = (double)MagV/DivV;
        
    Poly->CreatePoints ( 4 );
    Poly->Points[0].x = Sx-CenterX; Poly->Points[0].y = Sy-CenterY;
    Poly->Points[1].x = Sx-CenterX; Poly->Points[1].y = Sy-CenterY+Ht;
    Poly->Points[2].x = Sx-CenterX+Wd; Poly->Points[2].y = Sy-CenterY+Ht;
    Poly->Points[3].x = Sx-CenterX+Wd; Poly->Points[3].y = Sy-CenterY;

    Poly->Points[0].u = Sx<<16; Poly->Points[0].v = Sy<<16;    
    Poly->Points[1].u = Sx<<16; Poly->Points[1].v = (Sy+Ht-1)<<16;    
    Poly->Points[2].u = (Sx+Wd-1)<<16; Poly->Points[2].v = (Sy+Ht-1)<<16;    
    Poly->Points[3].u = (Sx+Wd-1)<<16; Poly->Points[3].v = Sy<<16;

    LONG x,y;
    INT i;
    for (i=0;i<4;i++)
      {
        x = (LONG)(Poly->Points[i].x * ScaleX);  
        y = (LONG)(Poly->Points[i].y * ScaleY);
        Poly->Points[i].x = (LONG)(x*Cz - y*Sz + Cx);
        Poly->Points[i].y = (LONG)(x*Sz + y*Cz + Cy);
      } // End for

    ViewPort = Dest->GetViewPort ();
    
    ClipPolygon ( Poly, ClipPoly, ViewPort );

    EDGETABLE *EdgeTable;   
    EdgeTable = new EDGETABLE ();

    LONG SrcWd = Src->GetBytesPerRow ();
    BYTE *SrcBuffer = Src->GetBuffer ();
    
    if (ClipPoly->NumPoints>2)
      {
        CreateEdgeTable ( ClipPoly, EdgeTable );  
        LONG x1,x2;
        LONG U1,U2,Du;
        LONG V1,V2,Dv;
        LONG DWd;
        BYTE *DestBuffer;
        
        DestBuffer = Dest->SetOffset ( 0, EdgeTable->Top );
        for (i=0;i<EdgeTable->Height;i++)
          {
            x1 = EdgeTable->Left[i].x;  
            x2 = EdgeTable->Right[i].x;
            U1 = EdgeTable->Left[i].u;  
            U2 = EdgeTable->Right[i].u;
            V1 = EdgeTable->Left[i].v;  
            V2 = EdgeTable->Right[i].v;
            DWd = x2-x1+1;
            if (DWd<=0)
              {
                DestBuffer = Dest->GetNextRow ( ROW_DOWN );
                continue;
              } // End if

            Du = (U2-U1)/DWd;
            Dv = (V2-V1)/DWd;
            INT j;
            DestBuffer += x1;
            for (j=0;j<DWd;j++)
              {
                LONG SrcOffset;
                SrcOffset = (V1>>16)*SrcWd+(U1>>16);
                *DestBuffer = *(SrcBuffer+SrcOffset);
                DestBuffer++;
                U1 += Du;
                V1 += Dv;
              } // End for
            DestBuffer = Dest->GetNextRow ( ROW_DOWN );
          } // End for
      } // End if       
    
    delete EdgeTable;
    delete Poly;
    delete ClipPoly;
  } // End of RotateImage for GRAFIX
                           
VOID GRAFIX::ConvertImage ( IMAGE *Image, COLORTABLE *MatchTable )
  {
    LONG i,j;
    BYTE *Buffer;
    BYTE *Table;

    Table = MatchTable->GetTable ();
    if (Table==NULL)
      return;

    for (i=0;i<Image->GetHeight();i++)
      {
        Buffer = Image->SetOffset ( 0, i );
        for (j=0;j<Image->GetWidth();j++)
          {
            LONG Color;
            Color = Buffer[j];
            Buffer[j] = Table[Color];  
          } // End for  
      } // End for  
  } // End of ConvertImage for GRAFIX

BOOLEAN GRAFIX::LoadImage ( STRING FileName, IMAGE *Image, RGBPALETTE *Pal )
  {
    IMAGEFILETOOL *ImageTool;
    INT FileType;
    FILEHANDLE f;
    BOOLEAN Result;

    f = File.Open ( FileName, OPEN_READ | OPEN_BINARY );
    if (f==NULL)
      {
        Error.SetError ( ERR_FILEIO );
        return FAILURE;
      } // End if  

    FileType = GetImageFileType ( f );
    switch (FileType)
      {
        case PCXFILE :
          ImageTool = new PCXFILETOOL ( f );
          break;
        case BMPFILE :
          ImageTool = new BMPFILETOOL ( f );
          break;
        case GIFFILE :
          ImageTool = new GIFFILETOOL ( f );
          break;
        default :
          ImageTool = NULL;
          break;
      } // End switch

    if (ImageTool==NULL)
      {
        Error.SetError ( ERR_NOMEMORY );
        File.Close ( f );
        return FAILURE;
      } // End if

    Result = ImageTool->LoadImage ( Image, Pal );
    if (Result==FAILURE)
      {
        Error.SetError ( ERR_NOVALID );
      } // End if

    File.Close ( f );
    if (Result)
      {}
    return Result;
  } // End of LoadImage for GRAFIX

BOOLEAN GRAFIX::SaveImage ( STRING FileName, SHORT ImageType, IMAGE *Image,
                            LONG Sx, LONG Sy, LONG Wd, LONG Ht, RGBPALETTE *Pal )
  {
    IMAGEFILETOOL *ImageTool;
    FILEHANDLE f;

    f = File.Open ( FileName, OPEN_WRITE | OPEN_BINARY );
    if (f==NULL)
      return FAILURE;

    switch (ImageType)
      {
        case PCXFILE :
          ImageTool = new PCXFILETOOL ( f );
          break;
        case BMPFILE :
          ImageTool = new BMPFILETOOL ( f );
          break;
        case GIFFILE :
          ImageTool = new GIFFILETOOL ( f );
          break;
        default :
          ImageTool = NULL;
          break;
      } // End switch

    if (ImageTool==NULL)
      {
        File.Close ( f );
        return FAILURE;
      } // End if

    BOOLEAN Ok;
    RECTANGLE ViewPort;

    ViewPort = Image->GetViewPort ();

    Ok = ClipRect ( &ViewPort, &Sx, &Sy, &Wd, &Ht );
    if (!Ok)
      {
        File.Close ( f );
        return FAILURE;
      } // End if

    Ok = ImageTool->SaveImage ( Image, Sx, Sy, Wd, Ht, Pal );
    if (!Ok)
      {
        File.Close ( f );
        return FAILURE;
      } // End if
    File.Close ( f );
    return SUCCESS;
  } // End of SaveImage for GRAFIX

VOID GRAFIX::PlayFLIC ( FLICFILE *Flic, RECTANGLE Region, LONG Cx, LONG Cy,
                        LONG StartFrame, LONG EndFrame, LONG Flags, FLICCallback Callback )
  {
    if (DisplayDriver==NULL)
      return;

    if (Flic==NULL)
      return;

    LONG Sx,Sy,Wd,Ht;
    LONG NumFrames;
    BOOLEAN PalChange;
    IMAGE *Image;

    Sx = Region.x1;
    Sy = Region.y1;
    Wd = Region.x2-Region.x1+1;
    Ht = Region.y2-Region.y1+1;
    
    Image = Flic->GetImage ();
    HDISPLAY hDisplay = Flic->GetDisplay ();
    
    if (Flags&FLIC_NEXT)
      {
        Flic->PlayFrame ( TRUE );
        PalChange = Flic->IsPalChanged ();
        if (PalChange)
          {
            SetPalette ( hDisplay, Flic->GetPalette() );
            Flic->PalProcessed ();
          } // End if
        DisplayImage ( hDisplay, Image, Sx, Sy, Wd, Ht, Cx, Cy );
        return;  
      } // End if
      
    if (Flags&FLIC_NOADVANCE)
      {
        Flic->PlayFrame ( FALSE );
        PalChange = Flic->IsPalChanged ();
        if (PalChange)
          {
            SetPalette ( hDisplay, Flic->GetPalette() );
            Flic->PalProcessed ();
          } // End if
        DisplayImage ( hDisplay, Image, Sx, Sy, Wd, Ht, Cx, Cy );
        return;  
      } // End if
      
    NumFrames = Flic->GetNumFrames ();
    if (NumFrames==0)
      return;
     
    if (Flags&FLIC_ALL)
      {
        StartFrame = 0;
        EndFrame = NumFrames-1;  
      } // End if
    else
      {
        if (StartFrame>=NumFrames)
          return;  
        if (EndFrame>=NumFrames)
          EndFrame = EndFrame%NumFrames;
      } // End else  
      
    LONG LoopCount=0;
    LONG Speed;

    Speed = Flic->Speed;
    if (Speed==FLIC_SPEED_DEFAULT)
      {
        if (Flic->IsFLCFile()!=TRUE)
          Speed = Flic->GetDefaultSpeed ();
        else
          Speed = 1;  
      } // End if

         if (Speed)
           {} 
    BOOLEAN Done=FALSE;

    Flic->SetFrame ( StartFrame );
    
    while (!Done)
      {
        Flic->PlayFrame ( TRUE );
        PalChange = Flic->IsPalChanged ();
        if (PalChange)
          {
            SetPalette ( hDisplay, Flic->GetPalette() );
            Flic->PalProcessed ();
          } // End if
              
        #if defined (__FORDOS__)
          WaitForRetrace ( Speed );
        #endif
          
        DisplayImage ( hDisplay, Image, Sx, Sy, Wd, Ht, Cx, Cy );
        if (Callback!=NULL)
          Done = Callback ( Flic );
            
        if (Flic->GetFrame ()>EndFrame)
          {
            if (Flags&FLIC_LOOP)
              {  
                Flic->SetFrame ( StartFrame );
                LoopCount++;
                if (Flic->Loop>=0)
                  {
                    if (LoopCount>=Flic->Loop)
                      Done = TRUE;  
                  } // End if
              } // End if
            else
              Done = TRUE;      
          } // End if    
      } // End while
  } // End of PlayFLIC for GRAFIX

VOID GRAFIX::SetPalette ( HDISPLAY hDisplay, RGBPALETTE *Pal )
  {
    if (DisplayDriver==NULL)
      return;
    DisplayDriver->SetPalette ( hDisplay, Pal );
  } // End of SetPalette for GRAFIX

VOID GRAFIX::GetPalette ( HDISPLAY hDisplay, RGBPALETTE *Pal )
  {
    if (DisplayDriver==NULL)
      return;
    DisplayDriver->GetPalette ( hDisplay, Pal );
  } // End of GetPalette for GRAFIX

VOID GRAFIX::ClearPalette ( HDISPLAY hDisplay, BYTE R, BYTE G, BYTE B )
  {
    RGBPALETTE *Pal;
    RGBCOLOR *Entry;
    
    Pal = new RGBPALETTE ();
    if (Pal==NULL)
      return;
    Entry = Pal->GetEntry ();

    INT i;
    for (i=0;i<256;i++)
      {
        Entry[i].Red = R;  
        Entry[i].Green = G;  
        Entry[i].Blue = B;  
      } // End for
    SetPalette ( hDisplay, Pal );  
    delete Pal;  
  } // End of ClearPalette for GRAFIX

VOID GRAFIX::FadePalette ( HDISPLAY hDisplay, RGBPALETTE *Pal,
                           LONG Direction, LONG NumSteps )
  {
    RGBCOLOR OldEntry[256];
    RGBCOLOR *Entry;
    INT i,j;

    if (NumSteps==0)
      return;

    Entry = Pal->GetEntry ();
    for (i=0;i<256;i++)
      {
        OldEntry[i].Red = Entry[i].Red;
        OldEntry[i].Green = Entry[i].Green;
        OldEntry[i].Blue = Entry[i].Blue;
      } // End for

    double StepRatio;
    double Ratio;
    StepRatio = (double)1/NumSteps;

    if (Direction==PAL_FADE_IN)
      {
        for (i=0;i<NumSteps;i++)
          {
            Ratio = StepRatio*i;
            for (j=0;j<256;j++)
              {
                Entry[j].Red = (BYTE)(Ratio*OldEntry[j].Red);
                Entry[j].Green = (BYTE)(Ratio*OldEntry[j].Green);
                Entry[j].Blue = (BYTE)(Ratio*OldEntry[j].Blue);
              } // End for
            WaitForRetrace ();
            SetPalette ( hDisplay, Pal );
          } // End for
      } // End if
    else
      {
        for (i=NumSteps-1;i>=0;i--)
          {
            Ratio = StepRatio*i;
            for (j=0;j<256;j++)
              {
                Entry[j].Red = (BYTE)(Ratio*OldEntry[j].Red);
                Entry[j].Green = (BYTE)(Ratio*OldEntry[j].Green);
                Entry[j].Blue = (BYTE)(Ratio*OldEntry[j].Blue);
              } // End for
            WaitForRetrace ();
            SetPalette ( hDisplay, Pal );
          } // End for
      } // End else

    for (i=0;i<256;i++)
      {
        Entry[i].Red = OldEntry[i].Red;
        Entry[i].Green = OldEntry[i].Green;
        Entry[i].Blue = OldEntry[i].Blue;
      } // End for
  } // End of FadePalette for GRAFIX

BOOLEAN GRAFIX::SavePalette ( STRING FileName, RGBPALETTE *Pal, LONG PalFormat )
  {
    FILEHANDLE f;

    if (Pal==NULL)
      return FAILURE;

    if (PalFormat==PAL_MSWIN)
      {
        f = File.Open ( FileName, OPEN_WRITE | OPEN_BINARY );
        if (f==NULL)
          return FAILURE;

        CHAR Str[4];
        DWORD Size;

        strncpy ( Str, "RIFF", 4 );
        if (File.Write ( f, Str, 4 )==FALSE)
          {
            File.Close ( f );
            return FAILURE;
          } // End if

        Size = 1040;
        if (File.Write ( f, &Size, 4 )==FALSE)
          {
            File.Close ( f );
            return FAILURE;
          } // End if

        strncpy ( Str, "PAL ", 4 );
        if (File.Write ( f, Str, 4 )==FALSE)
          {
            File.Close ( f );
            return FAILURE;
          } // End if

        strncpy ( Str, "data", 4 );
        if (File.Write ( f, Str, 4 )==FALSE)
          {
            File.Close ( f );
            return FAILURE;
          } // End if

        Size = 4+4*256;
        if (File.Write ( f, &Size, 4 )==FALSE)
          {
            File.Close ( f );
            return FAILURE;
          } // End if

        SHORT Version;
        SHORT NumEntries;

        Version = 0x0300;
        if (File.Write ( f, &Version, 2 )==FALSE)
          {
            File.Close ( f );
            return FAILURE;
          } // End if

        NumEntries = 256;
        if (File.Write ( f, &NumEntries, 2 )==FALSE)
          {
            File.Close ( f );
            return FAILURE;
          } // End if

        RGBCOLOR *Entry;

        Entry = Pal->GetEntry ();

        INT i;
        BYTE Flags;
        for (i=0;i<256;i++)
          {
            File.Write ( f, &(Entry[i].Red), 1 );
            File.Write ( f, &(Entry[i].Green), 1 );
            File.Write ( f, &(Entry[i].Blue), 1 );
            File.Write ( f, &(Flags), 1 );
          } // End for

        File.Close ( f );
        return SUCCESS;
      } // End if
    else if (PalFormat==PAL_PSP)
      {
        f = File.Open ( FileName, OPEN_WRITE );
        if (f==NULL)
          return FAILURE;

        fprintf ( f, "JASC-PAL\n" );
        fprintf ( f, "0100\n" );
        fprintf ( f, "256\n" );

        RGBCOLOR *Entry;
        Entry = Pal->GetEntry ();

        INT i;
        for (i=0;i<256;i++)
          {
            fprintf ( f, "%d %d %d\n", Entry[i].Red,
                                       Entry[i].Green,
                                       Entry[i].Blue );
          } // End for

        File.Close ( f );
        return SUCCESS;
      } // End else if
    else     
      {
        return FAILURE;
      } // End else
  } // End of SavePalette for GRAFIX

BOOLEAN GRAFIX::LoadPalette ( STRING FileName, RGBPALETTE *Pal )
  {
    FILEHANDLE f;
    if (Pal==NULL)
      return FAILURE;

    f = File.Open ( FileName, OPEN_READ | OPEN_BINARY );
    if (f==NULL)
      return FAILURE;

    CHAR Str[4];
    DWORD Size;

    // Check format first
    CHAR ID[8];
    LONG Format;

    if (File.Read ( f, ID, 8 )==FALSE)
      {
        File.Close ( f );
        return FAILURE;
      } // End if
    if (strncmp (ID, "RIFF", 4)==0)
      {
        Format = PAL_MSWIN;
      } // End if
    else if (strncmp ( ID, "JASC-PAL", 8 )==0)
      {
        Format = PAL_PSP;
      } // End else if
    else
      {
        File.Close ( f );
        return FAILURE;
      } // End else

    // Go back to first byte
    File.Seek ( f, 0, FROM_BEGIN );

    if (Format==PAL_MSWIN)
      {
        if (File.Read ( f, Str, 4 )==FALSE)
          {
            File.Close ( f );
            return FAILURE;
          } // End if
        if (strncmp (Str, "RIFF", 4)!=0)
          {
            File.Close ( f );
            return FAILURE;
          } // End if

        if (File.Read ( f, &Size, 4 )==FALSE)
          {
            File.Close ( f );
            return FAILURE;
          } // End if

        if (File.Read ( f, Str, 4 )==FALSE)
          {
            File.Close ( f );
            return FAILURE;
          } // End if
        if (strncmp (Str, "PAL ", 4)!=0)
          {
            File.Close ( f );
            return FAILURE;
          } // End if

        if (File.Read ( f, Str, 4 )==FALSE)
          {
            File.Close ( f );
            return FAILURE;
          } // End if
        if (strncmp (Str, "data", 4)!=0)
          {
            File.Close ( f );
            return FAILURE;
          } // End if

        if (File.Read ( f, &Size, 4 )==FALSE)
          {
            File.Close ( f );
            return FAILURE;
          } // End if

        SHORT Version;
        SHORT NumEntries;

        if (File.Read ( f, &Version, 2 )==FALSE)
          {
            File.Close ( f );
            return FAILURE;
          } // End if

        if (File.Read ( f, &NumEntries, 2 )==FALSE)
          {
            File.Close ( f );
            return FAILURE;
          } // End if

        #if defined (__MSBFIRST__)
          SwapWord ( (WORD*)NumEntries );
        #endif 

        RGBCOLOR *Entry;

        Entry = Pal->GetEntry ();

        INT i;
        BYTE Flags;

        for (i=0;i<NumEntries;i++)
          {
            File.Read ( f, &(Entry[i].Red), 1 );
            File.Read ( f, &(Entry[i].Green), 1 );
            File.Read ( f, &(Entry[i].Blue), 1 );
            File.Read ( f, &(Flags), 1 );
          } // End for

        File.Close ( f );
        return SUCCESS;
      } // End if
    else if (Format==PAL_PSP)
      {
        CHAR VerStr[8];
        CHAR PalStr[8];
        INT Num;
        INT Red,Green,Blue;

        fscanf ( f, "%s\n", PalStr );
        fscanf ( f, "%s\n", VerStr );
        fscanf ( f, "%d\n", &Num );

        RGBCOLOR *Entry;
        Entry = Pal->GetEntry ();

        if (Entry==NULL)
          {
            File.Close ( f );
            return FAILURE;
          } // End if
          
        INT i;
        for (i=0;i<Num;i++)
          {
            fscanf ( f, "%d %d %d\n", &Red, &Green, &Blue );
            Entry[i].Red = (BYTE)Red;
            Entry[i].Green = (BYTE)Green;
            Entry[i].Blue = (BYTE)Blue;
          } // End for
        File.Close ( f );
        return SUCCESS;  
      } // End else if
    File.Close ( f );
    return FAILURE;
  } // End of LoadPalette for GRAFIX

LONG GRAFIX::GetWidth ( HDISPLAY hDisplay )
  {
    if (DisplayDriver==NULL)
      return 0;
    return DisplayDriver->GetWidth ( hDisplay );
  } // End of GetWidth for GRAFIX

LONG GRAFIX::GetHeight ( HDISPLAY hDisplay )
  {
    if (DisplayDriver==NULL)
      return 0;
    return DisplayDriver->GetHeight ( hDisplay );
  } // End of GetHeight for GRAFIX

LONG GRAFIX::GetCode ( RECTANGLE Rect, LONG x, LONG y )
  {
    // 4 bits are use.
    // From most significant bit Top, Bottom, Right, Left.

    INT Code;

    Code = 0;
    if (y<Rect.y1)
      Code |= (1<<3);
    else if (y>Rect.y2)
      Code |= (1<<2);

    if (x<Rect.x1)
      Code |= (1<<0);
    else if (x>Rect.x2)
      Code |= (1<<1);

    return Code;
  } // End of GetCode for GRAFIX

BOOLEAN GRAFIX::ClipLine ( RECTANGLE Rect, LONG *x1, LONG *y1, LONG *x2, LONG *y2 )
  {
    LONG Code1,Code2;

    LONG Code;

    BOOLEAN Done=FALSE;

    Code1 = GetCode ( Rect, *x1, *y1 );
    Code2 = GetCode ( Rect, *x2, *y2 );

    INT x, y;

    while (!Done)
      {
        if ((Code1|Code2)==0)
          return TRUE;
        if (Code1&Code2)
          return FALSE;

        if (Code1)
          Code = Code1;
        else
          Code = Code2;

        if (Code&(1<<3)) // Clip against top
          {
            x = *x1 + ((*x2-*x1)*(Rect.y1-*y1) / (*y2-*y1));
            y = Rect.y1;
          } // End if
        else if (Code&(1<<2)) // Clip against bottom
          {
            x = *x1 + ((*x2-*x1)*(Rect.y2-*y1) / (*y2-*y1));
            y = Rect.y2;
          } // End if
        else if (Code&(1<<1)) // Clip against right
          {
            y = *y1 + ((*y2-*y1)*(Rect.x2-*x1) / (*x2-*x1));
            x = Rect.x2;
          } // End if
        else if (Code&(1<<0)) // Clip against left
          {
            y = *y1 + ((*y2-*y1)*(Rect.x1-*x1) / (*x2-*x1));
            x = Rect.x1;
          } // End if

        if (Code==Code1)
          {
            *x1 = x;
            *y1 = y;
            Code1 = GetCode ( Rect, *x1, *y1 );
          } // End if
        else
          {
            *x2 = x;
            *y2 = y;
            Code2 = GetCode ( Rect, *x1, *y1 );
          } // End if
      } // End while
    return FALSE;
  } // End of ClipLine for GRAFIX

VOID GRAFIX::DrawPixel ( IMAGE *Dest, LONG x, LONG y )
  {
    RECTANGLE ViewPort;

    ViewPort = Dest->GetViewPort ();
    if ((x<ViewPort.x1)||(y<ViewPort.y1)||(x>ViewPort.x2)||(y>ViewPort.y2))
      return;

    BYTE *Buffer;
    Buffer = Dest->SetOffset ( x, y );
    *Buffer = (BYTE)FGColor;
  } // End of DrawPixel for GRAFIX

VOID GRAFIX::DrawPixel ( HDISPLAY hDisplay, LONG x, LONG y )
  {
    if (DisplayDriver==NULL)
      return;

    DisplayDriver->DrawPixel ( hDisplay, x, y, FGColor );
  } // End of DrawPixel for GRAFIX

VOID GRAFIX::DrawLine ( IMAGE *Dest, LONG x1, LONG y1, LONG x2, LONG y2 )
  {
    BOOLEAN Ok;
    RECTANGLE ViewPort;

    ViewPort = Dest->GetViewPort ();

    Ok = ClipLine ( ViewPort, &x1, &y1, &x2, &y2 );
    if (!Ok)
      return;

    if (y1>y2)
      {                   
        #ifndef __FORVISUAL__
          SwapValue ( &y1, &y2 );
          SwapValue ( &x1, &x2 );
        #endif  
      } // End if

    INT Dx, Dy;
    INT AddX;
    INT Len;

    if (x2<x1)
      {
        AddX = -1;
      } // End if
    else
      AddX = 1;

    Dx = abs(x2-x1);
    Dy = abs(y2-y1);

    Len = MaxValue ( Dx, Dy ) + 1;

    BYTE *Buffer;

    Buffer = Dest->SetOffset ( 0, y1 );

    COLOR Color = FGColor;
    INT i;
    DWORD Error=0;

    DWORD x;

    if (Dy==0)
      {
        x = MinValue ( x1, x2 );
        int i;
        Buffer+=x;
        for (i=0;i<Len;i++)
          Buffer[i] = (BYTE)Color;
        //memset ( Buffer+x, Color, Len );
      } // End if
    else
      {
        x = x1;
        if (Dx>Dy)
          {
            for (i=0;i<Len;i++)
              {
                Buffer[x] = (BYTE)Color;
                Error += (DWORD)Dy;
                if (Error>=(DWORD)Dx)
                  {
                    Buffer = Dest->GetNextRow ( ROW_DOWN );
                    Error -= (DWORD)Dx;
                  } // End Error
                x += AddX;
              } // End for
          } // End if
        else
          {
            for (i=0;i<Len;i++)
              {
                Buffer[x] = (BYTE)Color;
                Error += (DWORD)Dx;
                if (Error>=(DWORD)Dy)
                  {
                    x += AddX;
                    Error -= (DWORD)Dy;
                  } // End Error
                Buffer = Dest->GetNextRow ( ROW_DOWN );
              } // End for
          } // End else
      } // End else
  } // End of DrawLine for GRAGIX

VOID GRAFIX::DrawLine ( HDISPLAY hDisplay, LONG x1, LONG y1, LONG x2, LONG y2 )
  {
    if (DisplayDriver==NULL)
      return;

    RECTANGLE ViewPort;
    ViewPort.x1 = 0;
    ViewPort.y1 = 0;
    ViewPort.x2 = DisplayDriver->GetWidth( hDisplay )-1;
    ViewPort.y2 = DisplayDriver->GetHeight( hDisplay )-1;

    BOOLEAN Ok = ClipLine ( ViewPort, &x1, &y1, &x2, &y2 );
    if (!Ok)
      return;

    if (y1>y2)
      {
        SwapValue ( &y1, &y2 );
        SwapValue ( &x1, &x2 );
      } // End if

    COLOR Color = FGColor;
    DisplayDriver->DrawLine ( hDisplay, x1, y1, x2, y2, Color );
  } // End of DrawLine for GRAFIX

VOID GRAFIX::DrawRect ( IMAGE *Dest, LONG x1, LONG y1, LONG x2, LONG y2 )
  {
    DrawLine ( Dest, x1, y2, x2, y2 );
    DrawLine ( Dest, x1, y1, x2, y1 );
    DrawLine ( Dest, x1, y1, x1, y2 );
    DrawLine ( Dest, x2, y1, x2, y2 );
  } // End of DrawRect for GRAFIX

VOID GRAFIX::DrawRect ( HDISPLAY hDisplay, LONG x1, LONG y1, LONG x2, LONG y2 )
  {
    if (DisplayDriver==NULL)
      return;

    RECTANGLE ViewPort;
    ViewPort.x1 = 0;
    ViewPort.y1 = 0;
    ViewPort.x2 = DisplayDriver->GetWidth( hDisplay )-1;
    ViewPort.y2 = DisplayDriver->GetHeight( hDisplay )-1;

    if (y1>y2)
      {
        SwapValue ( &y1, &y2 );
        SwapValue ( &x1, &x2 );
      } // End if

    LONG Wd,Ht;
    Wd = x2-x1+1;
    Ht = y2-y1+1;

    BOOLEAN Ok = ClipRect ( &ViewPort, &x1, &y1, &Wd, &Ht );
    if (!Ok)
      return;

    x2 = x1+Wd-1;
    y2 = y1+Ht-1;

    DisplayDriver->DrawRect ( hDisplay, x1, y1, x2, y2, FGColor );
  } // End of DrawRect for GRAFIX

VOID GRAFIX::FillRect ( IMAGE *Dest, LONG x1, LONG y1, LONG x2, LONG y2 )
  {
    if (DisplayDriver==NULL)
      return;

    RECTANGLE ViewPort;
    ViewPort = Dest->GetViewPort ();

    if (y1>y2)
      {
        SwapValue ( &y1, &y2 );
        SwapValue ( &x1, &x2 );
      } // End if

    LONG Wd,Ht;
    Wd = x2-x1+1;
    Ht = y2-y1+1;

    BOOLEAN Ok = ClipRect ( &ViewPort, &x1, &y1, &Wd, &Ht );
    if (!Ok)
      return;

    x2 = x1+Wd-1;
    y2 = y1+Ht-1;

    INT i;
    BYTE *Buffer;

    Buffer = Dest->SetOffset ( x1, y1 );

    for (i=0;i<Ht;i++)
      {
        memset ( Buffer, BGColor, Wd );
        Buffer = Dest->GetNextRow ( ROW_DOWN );
      } // End for
  } // End of FillRect for GRAFIX

VOID GRAFIX::FillRect ( HDISPLAY hDisplay, LONG x1, LONG y1, LONG x2, LONG y2 )
  {
    if (DisplayDriver==NULL)
      return;

    RECTANGLE ViewPort;
    ViewPort.x1 = 0;
    ViewPort.y1 = 0;
    ViewPort.x2 = DisplayDriver->GetWidth( hDisplay )-1;
    ViewPort.y2 = DisplayDriver->GetHeight( hDisplay )-1;

    if (y1>y2)
      {
        SwapValue ( &y1, &y2 );
        SwapValue ( &x1, &x2 );
      } // End if

    LONG Wd,Ht;
    Wd = x2-x1+1;
    Ht = y2-y1+1;

    BOOLEAN Ok = ClipRect ( &ViewPort, &x1, &y1, &Wd, &Ht );
    if (!Ok)
      return;

    x2 = x1+Wd-1;
    y2 = y1+Ht-1;

    DisplayDriver->FillRect ( hDisplay, x1, y1, x2, y2, BGColor );
  } // End of FillRect for GRAFIX

VOID GRAFIX::DrawEllipsePoint ( IMAGE *Dest, LONG Cx, LONG Cy, LONG x, LONG y )
  {
    INT Dx,Dy;

    Dx = x - Cx;
    Dy = Cy - y;

    DrawPixel ( Dest, Cx+Dx, Cy-Dy );
    DrawPixel ( Dest, Cx+Dx, Cy+Dy );
    DrawPixel ( Dest, Cx-Dx, Cy-Dy );
    DrawPixel ( Dest, Cx-Dx, Cy+Dy );
  } // End of DrawEllipsePoints for GRAFIX

VOID GRAFIX::DrawEllipse ( IMAGE *Dest, LONG Cx, LONG Cy, LONG Rx, LONG Ry )
  {
    LONG RsqX,RsqY;
    LONG RsqXY;
    LONG X2,Y2;
    LONG x,y;
    LONG MidTest;

    if ((Rx==0)||(Ry==0))
      return;

    RsqX = Rx*Rx;
    RsqY = Ry*Ry;
    RsqXY = RsqX*RsqY;
    x = 0;
    y = Ry;

    // Midtest = a^2*x^2 + b^2*y^2 - a^2*b^2
    DrawEllipsePoint ( Dest, Cx, Cy, x+Cx,
                       Cy-y );

    Y2 = y*y;
    while ((x*RsqY)<=(y*RsqX))
      {
        x++;
        X2 = x*x;
        MidTest = RsqY*X2+RsqX*Y2 - RsqXY;
        if (MidTest>=0)
          {
            y--;
            Y2 = y*y;
          } // End if
        DrawEllipsePoint ( Dest, Cx, Cy, x+Cx,
                           Cy-y );
      } // End while

    while (y>=0)
      {
        y--;
        Y2 = y*y;
        MidTest = RsqY*X2+RsqX*Y2 - RsqXY;
        if (MidTest<0)
          {
            x++;
            X2 = x*x;
          } // End if
        DrawEllipsePoint ( Dest, Cx, Cy, x+Cx,
                           Cy-y );
      } // End while
  } // End of DrawEllipse for GRAFIX

VOID GRAFIX::DrawEllipse ( HDISPLAY hDisplay, LONG Cx, LONG Cy, LONG Rx, LONG Ry )
  {
    if (DisplayDriver==NULL)
      return;
    DisplayDriver->DrawEllipse ( hDisplay, Cx, Cy, Rx, Ry, FGColor );
  } // End of DrawEllipse for GRAFIX

VOID GRAFIX::DrawHorizLine ( IMAGE *Dest, LONG x1, LONG x2, LONG y )
  {
    RECTANGLE ViewPort;

    ViewPort = Dest->GetViewPort ();

    if (x1>x2)
      SwapValue ( &x1, &x2 );

    if (x1<ViewPort.x1)
      x1 = ViewPort.x1;
    else if (x1>ViewPort.x2)
      return;

    if (x2<ViewPort.x1)
      return;
    else if (x2>ViewPort.x2)
      x2 = ViewPort.x2;

    if (y<ViewPort.y1)
      y = ViewPort.y1;
    else if (y>ViewPort.y2)
      return;

    INT Wd;

    Wd = x2 - x1 + 1;

    BYTE *Buffer = Dest->SetOffset ( x1, y );

    memset ( Buffer, BGColor, Wd );
  } // End of DrawHorizLine for GRAFIX

VOID GRAFIX::FillEllipsePoint ( IMAGE *Dest, LONG Cx, LONG Cy, LONG x, LONG y )
  {
    INT Dx,Dy;

    Dx = x - Cx;
    Dy = Cy - y;

    DrawHorizLine ( Dest, Cx-Dx, Cx+Dx, Cy-Dy );
    DrawHorizLine ( Dest, Cx-Dx, Cx+Dx, Cy+Dy );
  } // End of FillEllipsePoints for GRAFIX

VOID GRAFIX::FillEllipse ( IMAGE *Dest, LONG Cx, LONG Cy, LONG Rx, LONG Ry )
  {
    LONG RsqX,RsqY;
    LONG RsqXY;
    LONG X2,Y2;
    LONG x,y;
    LONG MidTest;

    if ((Rx==0)||(Ry==0))
      return;

    RsqX = Rx*Rx;
    RsqY = Ry*Ry;
    RsqXY = RsqX*RsqY;
    x = 0;
    y = Ry;

    // Midtest = a^2*x^2 + b^2*y^2 - a^2*b^2
    FillEllipsePoint ( Dest, Cx, Cy, x+Cx,
                       Cy-y );

    Y2 = y*y;
    while ((x*RsqY)<=(y*RsqX))
      {
        x++;
        X2 = x*x;
        MidTest = RsqY*X2+RsqX*Y2 - RsqXY;
        if (MidTest>=0)
          {
            y--;
            Y2 = y*y;
          } // End if
        FillEllipsePoint ( Dest, Cx, Cy, x+Cx,
                           Cy-y );
      } // End while

    while (y>=0)
      {
        y--;
        Y2 = y*y;
        MidTest = RsqY*X2+RsqX*Y2 - RsqXY;
        if (MidTest<0)
          {
            x++;
            X2 = x*x;
          } // End if
        FillEllipsePoint ( Dest, Cx, Cy, x+Cx,
                           Cy-y );
      } // End while
  } // End of FillEllipse for GRAFIX

VOID GRAFIX::FillEllipse ( HDISPLAY hDisplay, LONG Cx, LONG Cy, LONG Rx, LONG Ry )
  {
    if (DisplayDriver==NULL)
      return;
    DisplayDriver->FillEllipse ( hDisplay, Cx, Cy, Rx, Ry, BGColor );
  } // End of FillEllipse for GRAFIX

VOID GRAFIX::DrawText ( HDISPLAY hDisplay, STRING Text, LONG x, LONG y )
  {
    if (DisplayDriver==NULL)
      return;
    DisplayDriver->DrawText ( hDisplay, Text, x, y, FGColor );
  } // End of DrawText for GRAFIX


