-- ShowAis.mesa
-- Last changed by Doug Wyatt, 14-Jul-81 14:27:59

DIRECTORY
  CGAis USING [APH, Header, password, RasterPart, UCA, UCACodingType],
  JaMFnsDefs USING [GetReal, PopBoolean, PopString, Register],
  ColorDisplay,
  Directory USING [Error, Lookup],
  File USING [Capability, PageCount, PageNumber],
  Inline USING [LongMult],
  Real USING [RoundC],
  RealFns USING [Power],
  Space USING [Handle, nullHandle, WindowOrigin, wordsPerPage, virtualMemory,
    Create, CreateUniformSwapUnits, Map, Activate, Unmap, Delete, LongPointer];

ShowAis: PROGRAM
IMPORTS JaMFnsDefs, ColorDisplay, Directory, Inline, Real, RealFns, Space = {
OPEN Ais: CGAis;

Color: TYPE = [0..256);

wordsPerPage: CARDINAL = Space.wordsPerPage;
bufferPages: CARDINAL = 32; -- number of pages in buffer
bufferWords: CARDINAL = bufferPages*wordsPerPage;

file: File.Capability; -- the AIS file
rasterBase: File.PageNumber; -- where the raster begins
rasterPages: File.PageCount; -- number of pages in the raster
bufferSpace: Space.Handle; -- space for the buffer
bufferAddress: LONG POINTER; -- buffer starting address
scanCount: CARDINAL; -- number of scan lines in raster
scanLength: CARDINAL; -- pixels per scan line
wordsPerLine: CARDINAL; -- words per scan line

space: Space.Handle ← Space.nullHandle;
header: LONG POINTER TO Ais.Header ← NIL;
part: LONG POINTER TO Ais.APH ← NIL;
rp: LONG POINTER TO Ais.RasterPart ← NIL;
up: LONG POINTER TO Ais.UCA ← NIL;
lines,pixels,wpl,bps: CARDINAL;
rbase,apages,rwords,rpages: CARDINAL;

RGB: TYPE = {r,g,b};

ShowFile: PROC[name: LONG STRING, color: RGB] = {
  lp: LONG POINTER;

  file ← Directory.Lookup[fileName: name ! Directory.Error => GOTO Punt];
    -- may raise Directory.Error

  space ← Space.Create[size: bufferPages, parent: Space.virtualMemory];
  Space.CreateUniformSwapUnits[size: 4, parent: space];

  Space.Map[space: space, window: [file: file, base: 1]];
  header ← lp ← Space.LongPointer[space];
  IF header.password=Ais.password THEN
    part ← LOOPHOLE[header+SIZE[Ais.Header]]
  ELSE ERROR; -- Wrong password
  apages ← header.attributeLength/wordsPerPage;
  rbase ← 1+apages;
  IF part.type=raster THEN {
    rp ← LOOPHOLE[part]; part ← part+part.length }
  ELSE ERROR; -- Raster part missing
  IF rp.samplesPerPixel=1 THEN NULL
  ELSE ERROR; -- Can't handle multiple samples per pixel
  IF rp.codingType=Ais.UCACodingType THEN
    up ← LOOPHOLE[rp+SIZE[Ais.RasterPart]]
  ELSE ERROR; -- Coding type not UCA
  IF up.scanLinesPerBlock=LAST[CARDINAL] THEN NULL
  ELSE ERROR; -- Can't handle blocked encoding yet
--  IF rp.scanDirection#3 THEN ERROR; // Unexpected scanDirection

  lines ← rp.scanCount;
  pixels ← rp.scanLength;
  wpl ← up.wordsPerScanLine;
  bps ← up.bitsPerSample;
  rwords ← lines*wpl;
  rpages ← rwords/wordsPerPage;
  
  rasterBase ← rbase; rasterPages ← rpages;
  bufferSpace ← space; bufferAddress ← lp;
  scanCount ← lines; scanLength ← pixels; wordsPerLine ← wpl;
  Space.Unmap[space];
  Generate[color];
  Space.Delete[space];
  EXITS Punt => NULL
  };

half: BOOLEAN ← FALSE;

Generate: PROC[color: RGB] = {
  h: CARDINAL = ColorDisplay.width;
  w: CARDINAL = ColorDisplay.height;
  wpl: CARDINAL ← wordsPerLine;
  min,max,bot,top: CARDINAL ← 0;
  xmin,xmax,ymin,ymax,ybase: CARDINAL;
  count: CARDINAL = scanCount; -- lines in image
  remain: CARDINAL ← MIN[count,IF half THEN 2*h ELSE h]; -- lines remaining
  space: Space.Handle ← bufferSpace;
  buffer: LONG POINTER ← bufferAddress;
  window: Space.WindowOrigin ← [file: file, base: rasterBase];
  offset: [0..wordsPerPage) ← 0;
  xbase: LONG POINTER;

  xmin ← 0;
  xmax ← MIN[scanLength,IF half THEN 2*w ELSE w];
  ybase ← 0;
  WHILE remain>0 DO -- map successive chunks of the image into the buffer
    lines,height,page,word: CARDINAL;
    canfit: CARDINAL = (bufferWords-offset)/wpl;
    lines ← MIN[canfit,remain];
    height ← lines;
    ymin ← 0; ymax ← height;
    xbase ← buffer+offset;
    Space.Map[space,window];
    Space.Activate[space];
    IF half THEN {
    FOR y: CARDINAL ← ymin, y+2 WHILE y<ymax DO
      line: LONG POINTER TO ColorDisplay.ByteSeq ← xbase + Inline.LongMult[wpl,y];
      yy: CARDINAL ← (h-1)-((ybase+y)/2);
      FOR x: CARDINAL ← xmin, x+2 WHILE x<xmax DO
	xx: CARDINAL ← x/2;
        val: [0..256) ← line[x];
        SELECT color FROM
	  r => ColorDisplay.SetRed[yy,xx,val];
	  g => ColorDisplay.SetGreen[yy,xx,val];
	  b => ColorDisplay.SetBlue[yy,xx,val];
	  ENDCASE;
        ENDLOOP;
      ENDLOOP;
    }
    ELSE {
    FOR y: CARDINAL IN[ymin..ymax) DO
      line: LONG POINTER TO ColorDisplay.ByteSeq ← xbase + Inline.LongMult[wpl,y];
      yy: CARDINAL ← (h-1)-(ybase+y);
      FOR x: CARDINAL IN[xmin..xmax) DO
        val: [0..256) ← line[x];
        SELECT color FROM
	  r => ColorDisplay.SetRed[yy,x,val];
	  g => ColorDisplay.SetGreen[yy,x,val];
	  b => ColorDisplay.SetBlue[yy,x,val];
	  ENDCASE;
        ENDLOOP;
      ENDLOOP;
    };
    Space.Unmap[space];
    -- prepare for next buffer load
    word ← offset+height*wpl;
    page ← word/wordsPerPage;
    offset ← word MOD wordsPerPage;
    window.base ← window.base+page;
    remain ← remain-height;
    ybase ← ybase + height;
    ENDLOOP;
  };

gamma: REAL ← 1;

Comp: PROC[val: Color] RETURNS[Color] = {
  IF gamma>1 THEN {
    r: REAL ← val;
    r ← r/LAST[Color];
    r ← RealFns.Power[r,1.0/gamma];
    r ← r*LAST[Color];
    val ← Real.RoundC[r];
    };
  RETURN[val];
  };

SetGamma: PROC = {
  gamma ← JaMFnsDefs.GetReal[];
  SetMaps[];
  };

SetMaps: PROC = {
  FOR i: Color IN Color DO
    val: Color ← Comp[i];
    ColorDisplay.SetRedMap[i,val];
    ColorDisplay.SetGreenMap[i,val];
    ColorDisplay.SetBlueMap[i,val];
    ENDLOOP;
  };

Neg: PROC = {
  FOR i: Color IN [0..128) DO
    j: Color ← 255-i;
    a,b: Color;
    a ← ColorDisplay.GetRedMap[i]; b ← ColorDisplay.GetRedMap[j];
    ColorDisplay.SetRedMap[i,b]; ColorDisplay.SetRedMap[j,a];
    a ← ColorDisplay.GetGreenMap[i]; b ← ColorDisplay.GetGreenMap[j];
    ColorDisplay.SetGreenMap[i,b]; ColorDisplay.SetGreenMap[j,a];
    a ← ColorDisplay.GetBlueMap[i]; b ← ColorDisplay.GetBlueMap[j];
    ColorDisplay.SetBlueMap[i,b]; ColorDisplay.SetBlueMap[j,a];
    ENDLOOP;
  };

ShowR: PROC = {
  name: STRING ← [100];
  JaMFnsDefs.PopString[name];
  ShowFile[name,r];
  };

ShowG: PROC = {
  name: STRING ← [100];
  JaMFnsDefs.PopString[name];
  ShowFile[name,g];
  };

ShowB: PROC = {
  name: STRING ← [100];
  JaMFnsDefs.PopString[name];
  ShowFile[name,b];
  };

Wipe: PROC = {
  FOR y: CARDINAL IN[0..ColorDisplay.height) DO
    FOR x: CARDINAL IN[0..ColorDisplay.width) DO
      ColorDisplay.SetPixel[x,y,0,0,0];
      ENDLOOP;
    ENDLOOP;
  };

SetHalf: PROC = {
  half ← JaMFnsDefs.PopBoolean[];
  };

-- Initialization...

IF NOT ColorDisplay.SetMode[[full: TRUE, bitsPerPixelA: 0, bitsPerPixelB: 0]] THEN ERROR;
Wipe[];
SetMaps[];
ColorDisplay.TurnOn[];

JaMFnsDefs.Register[".showr",ShowR];
JaMFnsDefs.Register[".showg",ShowG];
JaMFnsDefs.Register[".showb",ShowB];
JaMFnsDefs.Register[".wipe",Wipe];
JaMFnsDefs.Register[".setgamma",SetGamma];
JaMFnsDefs.Register[".sethalf",SetHalf];
JaMFnsDefs.Register[".neg",Neg];

}.