(* GRAPHIC.PAS -- graphic stuff
** Copyright (c) 1995,1996 Jochen Metzinger
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2, or (at your option)
** any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*)

UNIT graphic;

INTERFACE

USES Crt, Dos, Graph, global, errors;

CONST
 VGA_MAXX = 639;
 VGA_MAXY = 479;
  (* use VGA mode VGAHi *)
 BGI_PATH = 'C:\TP\BGI';
  (* default path to driver EGAVGA.BGI *)

TYPE
 ColorPal = (EGAcp, CBMcp);
  (* which color palette *)

PROCEDURE OpenGraphic(p: ColorPal);
(* Open graphic mode VGAHi *)

PROCEDURE CloseGraphic(wait_key: BOOLEAN);
(* CloseGraph() with optional waiting for a key pressed *)

PROCEDURE GetColorPalette(p: ColorPal; VAR pal);
(* Store RGBPalette of "p" in "pal" *)

PROCEDURE TextOut(s: STRING);
(* output "s" at bottom graphic line *)

PROCEDURE G_FATAL(msg: STRING);
(* for FATAL errors within graphic mode *)

PROCEDURE G_err_stop;
(* for FATAL errors within graphic mode *)

PROCEDURE ScrollUp(x1, y1, x2, y2: INTEGER; lines: WORD);
(* scroll up graphic window *)

IMPLEMENTATION

TYPE
 ColorPaletteType =
  RECORD
   text, black: BYTE;
   rgb: ARRAY [0..MaxColors] OF RECORD r, g, b: BYTE END;
  END;

CONST
 ColorPalette: ARRAY [ColorPal] OF ColorPaletteType =
  ((* EGAcp *)
   (text:  7; (* LightGray *)
    black: 0;
    rgb: ((r:$00;g:$00;b:$00), (r:$00;g:$00;b:$AA),
          (r:$00;g:$AA;b:$00), (r:$00;g:$AA;b:$AA),
          (r:$AA;g:$00;b:$00), (r:$AA;g:$00;b:$AA),
          (r:$AA;g:$55;b:$00), (r:$AA;g:$AA;b:$AA),
          (r:$55;g:$55;b:$55), (r:$55;g:$55;b:$FF),
          (r:$55;g:$FF;b:$55), (r:$55;g:$FF;b:$FF),
          (r:$FF;g:$55;b:$55), (r:$FF;g:$55;b:$FF),
          (r:$FF;g:$FF;b:$55), (r:$FF;g:$FF;b:$FF))),
   (* CBMcp *)
   (text: 15; (* lt gey *)
    black: 0;
    rgb: ((r:$00;g:$00;b:$00), (r:$FF;g:$FF;b:$FF),
          (r:$FF;g:$00;b:$00), (r:$00;g:$FF;b:$FF),
          (r:$FF;g:$00;b:$FF), (r:$00;g:$FF;b:$00),
          (r:$00;g:$00;b:$FF), (r:$FF;g:$FF;b:$00),
          (r:$FF;g:$66;b:$00), (r:$AA;g:$44;b:$00),
          (r:$FF;g:$77;b:$77), (r:$55;g:$55;b:$55),
          (r:$88;g:$88;b:$88), (r:$99;g:$FF;b:$99),
          (r:$99;g:$99;b:$FF), (r:$BB;g:$BB;b:$BB))));

VAR cp: ColorPal;
    graphic_on: BOOLEAN;

PROCEDURE InitGraphic;
 VAR GraphDriver, GraphMode, ErrorCode, i: INTEGER;
  path: PathStr; D: DirStr; N: NameStr; E: ExtStr;
BEGIN
 FOR i := 1 TO 4 DO
  BEGIN (* try 4 paths *)
   GraphDriver := VGA;
   GraphMode := VGAHi;
   CASE i OF
    1: path := GetEnv('BGI');
    2: path := BGI_PATH;
    3: BEGIN
        FSplit(ParamStr(0), D, N, E);
        path := D;
       END;
    4: path := '.';
   END; (* case *)
   InitGraph(GraphDriver,GraphMode, path);
   ErrorCode := GraphResult;
   IF ErrorCode = grOK THEN BEGIN
    graphic_on := TRUE;
    SetTextJustify(LeftText, BottomText);
    EXIT;
   END; (* if *)
   IF ErrorCode <> grFileNotFound  THEN
    FATAL(GraphErrorMsg(ErrorCode));
  END; (* for *)
 FATAL(GraphErrorMsg(ErrorCode));
END; (* InitGraphic *)

PROCEDURE OpenGraphic(p: ColorPal);
 VAR Palette: PaletteType; i: INTEGER;
BEGIN
 InitGraphic;
 GetPalette(Palette);
 cp := p;
 WITH Palette, ColorPalette[cp] DO
  FOR i := 0 TO Size-1 DO
   WITH rgb[i] DO
    SetRGBPalette(Colors[i], r SHR 2, g SHR 2, b SHR 2);
END; (* SetRGBColors *)

PROCEDURE GetColorPalette(p: ColorPal; VAR pal);
 VAR pl: ARRAY [0..47] OF BYTE ABSOLUTE pal;
  i: BYTE;
BEGIN
 FOR i := 0 TO 15 DO
  WITH ColorPalette[p].rgb[i] DO BEGIN
   pl[3*i+0] := r;
   pl[3*i+1] := g;
   pl[3*i+2] := b;
  END; (* with *)
END; (* GetColorPalette *)

PROCEDURE CloseGraphic(wait_key: BOOLEAN);
BEGIN
 IF wait_key THEN BEGIN
  TextOut('<<Press a key to continue>>');
  REPEAT UNTIL ReadKey <> #0;
 END; (* if *)
 Graph.CloseGraph;
 graphic_on := FALSE;
END; (* CloseGraphic *)

PROCEDURE G_FATAL(msg: STRING);
BEGIN
 IF graphic_on THEN BEGIN
  TextOut('ERROR: ' + msg);
  REPEAT UNTIL ReadKey <> #0;
  Graph.CloseGraph;
 END; (* if *)
 FATAL(msg);
END; (* G_FATAL *)

PROCEDURE G_err_stop;
 VAR err_msg: STRING;
BEGIN
 IF is_err THEN BEGIN
  IF graphic_on THEN BEGIN
   err_msg := get_error;
   TextOut(err_msg);
   REPEAT UNTIL ReadKey <> #0;
   Graph.CloseGraph;
  END; (* if *)
  WriteLn(err_msg);
  HALT(1);
 END; (* if *)
END; (* G_err_stop *)

PROCEDURE ScrollUp(x1, y1, x2, y2: INTEGER; lines: WORD);
 VAR h, y: INTEGER; size: WORD; p: POINTER;
BEGIN
 IF lines = 0 THEN EXIT;
 (* coordinates within graphic screen *)
 IF x1 < 0 THEN x1 := 0; IF x1 > VGA_MAXX THEN x1 := VGA_MAXX;
 IF y1 < 0 THEN y1 := 0; IF y1 > VGA_MAXY THEN y1 := VGA_MAXY;
 IF x2 < 0 THEN x2 := 0; IF x2 > VGA_MAXX THEN x2 := VGA_MAXX;
 IF y2 < 0 THEN y2 := 0; IF y2 > VGA_MAXY THEN y2 := VGA_MAXY;
 (* upper left corner z1 *)
 IF x1 > x2 THEN BEGIN h := x1; x1 := x2; x2 := h; END;
 IF y1 > y2 THEN BEGIN h := y1; y1 := y2; y2 := h; END;
 (* can only lines in window *)
 IF lines > y2 - y1 THEN lines := y2 - y1;
 (* get one line buffer *)
 GetMem(p, ImageSize(x1, 0, x2, 0));
 (* scroll up *)
 FOR y := y1 + lines TO y2 DO BEGIN
  GetImage(x1, y, x2, y, p^);
  PutImage(x1, y-lines, p^, NormalPut);
 END; (* for *)
 (* clear new lines *)
 FOR y := y2-lines TO y2 DO BEGIN
  GetImage(x1, y, x2, y, p^);
  PutImage(x1, y, p^, XorPut);
 END; (* for *)
END; (* ScrollUp *)

PROCEDURE TextOut(s: STRING);
BEGIN
 SetFillStyle(SolidFill, Black);
 Bar(0, VGA_MAXY-8, VGA_MAXX, VGA_MAXY);
 SetColor(ColorPalette[cp].text);
 SetTextJustify(LeftText, BottomText);
 OutTextXY(0,VGA_MAXY,s);
END; (* TextOut *)

PROCEDURE ExitProcedure;
(* called after before terminating program *)
BEGIN
 (* normal termination? *)
 IF ErrorAddr = NIL THEN
  HALT(ExitCode);
 (* fatal run-time error *)
 IF graphic_on THEN Graph.CloseGraph;
 IF source = '' THEN Write('FATAL: ') ELSE Write(source,': ');
 (* too few memory? *)
 IF ExitCode = 203 THEN BEGIN
  WriteLn('Heap overflow');
  HALT(4);
 END; (* if *)
 Write('INTERN [Run-time error ',ExitCode,' at ');
 WriteLn(HexStr(Seg(ErrorAddr^),4),':',HexStr(Ofs(ErrorAddr^),4),']');
 HALT(3);
END; (* ExitProcedure *)

BEGIN
 cp := EGAcp;
 graphic_on := FALSE;
 ExitProc := @ExitProcedure;
END. (* graphic *)
