-- CGPressDeviceImpl.mesa
-- Last changed by Doug Wyatt, April 21, 1982 3:59 pm
-- Last changed by Warnock June 4, 1982 1:38 pm
-- Last changed by Plass and Stone October 1, 1982 10:19 am

DIRECTORY
  GraphicsBasic,
  CGArea USING [Empty, Ref, Remove],
  CGPressDevice,
  CGDevice USING [Ref, Rep],
  CGMatrix USING [Inv, InvRel, Make, Ref],
  CGSource USING [Mode, Ref, Type],
  CGStorage USING [qZone],
  SirPress,
  Inline,
  GraphicsColor USING [Color, ColorToHSV],
  Real USING [Fix, FixC, RoundLI];

CGPressDeviceImpl: MONITOR
IMPORTS
  CGArea, CGMatrix, CGStorage,
  Inline, Real,SirPress, GraphicsColor
EXPORTS GraphicsBasic, CGPressDevice = {

DeviceObject:PUBLIC TYPE = CGDevice.Rep;

dataZone: ZONE = CGStorage.qZone;
repZone: ZONE = CGStorage.qZone;

Error: SIGNAL = CODE;

Data: TYPE = REF DataRep;
DataRep: TYPE = RECORD [
  ph:SirPress.PressHandle,
  unit: INT,
  fatness: REAL,
  halftoneSubsamplingFactor: REAL,
  matrix: CGMatrix.Ref -- base transformation matrix
  ];

New: PUBLIC PROC [
    pressHandle: SirPress.PressHandle,
    resolution: REAL, -- in bits per inch
    halftoneResolution: REAL, -- maximum resolution for halftone images, in pixels per inch
    fatness: REAL -- in points (72 points per inch)
    ] RETURNS [CGDevice.Ref] = {
  s: REAL ← resolution/72.0;
  data: Data ← dataZone.NEW[DataRep ← [
    ph: pressHandle,
      unit: Real.RoundLI[SirPress.pt/s],
      fatness: fatness,
      halftoneSubsamplingFactor: resolution/halftoneResolution, 
      matrix: CGMatrix.Make[[s,0,0,s,0,0]]]];
  SirPress.SetColor[pressHandle,0,0,0];
  RETURN[repZone.NEW[CGDevice.Rep ← [
    GetMatrix: GetMatrix, GetBounds: GetBounds, Show: Show,
    GetRaster: GetRaster, data: data]]];
  };

GetMatrix: SAFE PROC[self: CGDevice.Ref] RETURNS [CGMatrix.Ref] = CHECKED {
  data: Data ← NARROW[self.data];
  RETURN[data.matrix];
  };

GetBounds: SAFE PROC[self: CGDevice.Ref] RETURNS [GraphicsBasic.Box] = CHECKED {
  data: Data ← NARROW[self.data];
  e: REAL = 0.1; -- small fudge factor
  RETURN[[xmin: e, ymin: e, xmax: 17*SirPress.in/(2*data.unit)-e, ymax: 11*SirPress.in/data.unit-e]];
  };

GetRaster: SAFE PROC[self: CGDevice.Ref] RETURNS [LONG POINTER,CARDINAL] = CHECKED {
  RETURN[NIL,0];
  };

Show: SAFE PROC[self: CGDevice.Ref, area: CGArea.Ref, src: CGSource.Ref, map: CGMatrix.Ref] = TRUSTED {
    data: Data ← NARROW[self.data];
    SetColor[data.ph,src.color];
    UNTIL CGArea.Empty[area] DO
        trap: GraphicsBasic.Trap ← CGArea.Remove[area];
        ShowTrap[data,trap,src,map];
        ENDLOOP;
    };

SetColor: PROC[pressHandle: SirPress.PressHandle, color: GraphicsColor.Color] = {
    h,s,v: REAL;
      [h,s,v] ← GraphicsColor.ColorToHSV[color];
    SirPress.SetColor[pressHandle,Real.RoundLI[h*240],Real.RoundLI[s*255],Real.RoundLI[v*255]];
    };

Store: PROC [scanLine: REF SirPress.ScanLine, index: NAT, sample: CARDINAL] = {
    WITH scanLine: scanLine SELECT FROM
        bitMap => scanLine[index] ← sample;
        bitSampled => scanLine[index] ← sample;
        bitBitSampled => scanLine[index] ← sample;
        nybbleSampled => scanLine[index] ← sample;
        byteSampled => scanLine[index] ← sample;
        ENDCASE => ERROR;
    };

Bot: PROC[r: REAL] RETURNS [CARDINAL] = INLINE { RETURN[Real.FixC[r]] };
Top: PROC[r: REAL] RETURNS [CARDINAL] = INLINE { RETURN[Real.FixC[r]+1] };
Fix: PROC[r: REAL] RETURNS [CARDINAL] = INLINE { RETURN[Real.FixC[r]] };
Rnd: PROC[r: REAL] RETURNS [CARDINAL] = INLINE { RETURN[Real.FixC[r+.5]] };
RndI: PROC[r: REAL] RETURNS [INTEGER] = INLINE {
  RETURN[Real.FixC[r+(LAST [INTEGER]+.5)]-LAST[INTEGER]] };

RndUp: PROC[c, m: CARDINAL] RETURNS [CARDINAL] = INLINE {RETURN[(c+(m-1))/m*m]};

White: PROC[bitsPerSample: [0..16]] RETURNS [intensity: CARDINAL] = {
    intensity ← Inline.BITSHIFT[1, bitsPerSample];
    intensity ← intensity-1;
    }; 

Line1: TYPE = RECORD[PACKED SEQUENCE COMPUTED CARDINAL OF [0..1B]];
Line2: TYPE = RECORD[PACKED SEQUENCE COMPUTED CARDINAL OF [0..3B]];
Line4: TYPE = RECORD[PACKED SEQUENCE COMPUTED CARDINAL OF [0..17B]];
Line8: TYPE = RECORD[PACKED SEQUENCE COMPUTED CARDINAL OF [0..377B]];

AGet: PROC[src: CGSource.Ref, x,y: CARDINAL] RETURNS[CARDINAL] = INLINE {
  xline: LONG POINTER ← src.xbase + Inline.LongMult[y,src.xrast];
  SELECT src.bps FROM
    0,1 => RETURN[LOOPHOLE[xline, LONG POINTER TO Line1][x]];
    2 => RETURN[LOOPHOLE[xline, LONG POINTER TO Line2][x]];
    4 => RETURN[LOOPHOLE[xline, LONG POINTER TO Line4][x]];
    8 => RETURN[LOOPHOLE[xline, LONG POINTER TO Line8][x]];
    ENDCASE => ERROR;
  };

Frac: TYPE = Inline.LongNumber;
fscl: Frac = [num[lowbits: 0, highbits: 1]];

FixF: PROC[r: REAL] RETURNS[Frac] = INLINE {
  RETURN[[li[Real.Fix[r*fscl.li]]]] };

FltF: PROC[f: Frac] RETURNS[REAL] = INLINE {
  r: REAL ← f.li; RETURN[r/fscl.li] };

AddF: PROC[a,b: Frac] RETURNS[Frac] = INLINE {
  RETURN[[li[a.li+b.li]]] };

IntF: PROC[f: Frac] RETURNS[INTEGER] = INLINE {
  RETURN[LOOPHOLE[f.highbits, INTEGER]] };

ShowTrap: ENTRY PROC[data: Data, trap: GraphicsBasic.Trap, src: CGSource.Ref, map: CGMatrix.Ref] = {
  type: CGSource.Type ← src.type;
  easy: BOOLEAN ← (SELECT type FROM
    const => TRUE, -- always easy
    array, tile => FALSE, -- Not  easy
    proc => FALSE, -- never easy
    ENDCASE => ERROR);
  fat: BOOLEAN ← src.fat;
  yb, yt, yl, xl, xr: CARDINAL ← 0;
  rdxl, rdxr: REAL ← 0; -- left and right x increments
  rsdx, rsdy: REAL ← 0; -- increments for source position
  fsdx, fsdy: Frac;
  mode: CGSource.Mode ← src.mode;
  white: CARDINAL ← White[src.bps];
  PutTrap: PROC[xbotL, xtopL, xtopR, xbotR, ybot, ytop: INT]  =  {
      IF xbotL=xtopL AND xbotR=xtopR THEN
          SirPress.PutRectangle[data.ph, xbotL, ybot, xbotR-xbotL, ytop-ybot, data.unit]
    ELSE {
        SirPress.StartOutline[data.ph];
        SirPress.PutMoveTo[data.ph, xbotL, ybot, data.unit];
        SirPress.PutDrawTo[data.ph, xtopL, ytop, data.unit];
        SirPress.PutDrawTo[data.ph, xtopR, ytop, data.unit];
        SirPress.PutDrawTo[data.ph, xbotR, ybot, data.unit];
        SirPress.EndOutline[data.ph];
        }
    };
    -- main code for PTrap starts here
  -- compute yb (bottom of first scanline) and yt (top of last scanline)
  IF fat THEN { yb←Bot[trap.ybot]; yt←Top[trap.ytop] }
  ELSE { yb←Rnd[trap.ybot]; yt←Rnd[trap.ytop] };
  IF yb<yt THEN yl←yt-1 -- yl is last line
  ELSE { IF yb>yt OR fat THEN SIGNAL Error; RETURN };
  IF easy THEN {  --constant color trapezoid
      IF fat THEN
          PutTrap[
              xbotL: Bot[trap.xbotL],
              xtopL: Bot[trap.xtopL],
              xtopR: Top[trap.xtopR],
              xbotR: Top[trap.xbotR],
              ybot: yb,
              ytop: yt
              ]
      ELSE PutTrap[
              xbotL: Rnd[trap.xbotL],
              xtopL: Rnd[trap.xtopL],
              xtopR: Rnd[trap.xtopR],
              xbotR: Rnd[trap.xbotR],
              ybot: yb,
              ytop: yt
              ]
    }
  ELSE {  --going to put out a showbits command with carefully placed 0's
    scanLine: REF SirPress.ScanLine;
    -- allocate storage for a scan line.
    scanLineOrigin: CARDINAL ← Bot[MIN[trap.xbotL, trap.xtopL]];
    scanLineEnd: CARDINAL ← Top[MAX[trap.xbotR, trap.xtopR]];
    scanLineLength: CARDINAL ← SELECT src.bps FROM
        0 => RndUp[scanLineEnd - scanLineOrigin, 16],
        1 => RndUp[scanLineEnd - scanLineOrigin, 16],
        2 => RndUp[scanLineEnd - scanLineOrigin, 8],
        4 => RndUp[scanLineEnd - scanLineOrigin, 4],
        8 => RndUp[scanLineEnd - scanLineOrigin, 2],
        ENDCASE => ERROR;
    DoLine: PROC[xmin, xmax, y: CARDINAL] = {
    	rsx, rsy: REAL; -- current source position
    	fsx, fsy: Frac;
    	sx, sy: CARDINAL; -- truncated source position
    	s: CARDINAL; -- sample value from source
    	FOR x: CARDINAL IN [scanLineOrigin..xmin) DO
        	Store[scanLine, x-scanLineOrigin, white]
        	ENDLOOP;
    	FOR x: CARDINAL IN [xmin..xmax) DO    --put in the rest of the samples
        	IF x=xmin THEN {
            	[[rsx,rsy]] ← CGMatrix.Inv[map,[x+0.5,y+0.5]];
            	fsx ← FixF[rsx]; fsy ← FixF[rsy];
            	}
        	ELSE { fsx ← AddF[fsx,fsdx]; fsy ← AddF[fsy,fsdy]};
        	-- Get a sample from the source
        	IF type=proc THEN s ← src.Get[FltF[fsx], FltF[fsy]]
        	ELSE { sx ← IntF[fsx]; sy ← IntF[fsy]; s ← AGet[src, sx, sy] };
        	Store[scanLine, x-scanLineOrigin, s]
        	ENDLOOP;
    	FOR x: CARDINAL IN [xmax..scanLineEnd) DO
        	Store[scanLine, x-scanLineOrigin, white]
        	ENDLOOP;
    	SirPress.ShowLine[data.ph, scanLine];
    	};
    SELECT src.bps FROM
        0 => scanLine ← NEW[SirPress.ScanLine[bitMap][scanLineLength]];
        1 => scanLine ← NEW[SirPress.ScanLine[bitSampled][scanLineLength]];
        2 => scanLine ← NEW[SirPress.ScanLine[bitBitSampled][scanLineLength]];
        4 => scanLine ← NEW[SirPress.ScanLine[nybbleSampled][scanLineLength]];
        8 => scanLine ← NEW[SirPress.ScanLine[byteSampled][scanLineLength]];
        ENDCASE => ERROR;
    FOR i: NAT IN [scanLineEnd-scanLineOrigin..scanLineLength) DO Store[scanLine, i, white] ENDLOOP;
    --if opaque, then blot out the trapezoidal region
      IF src.mode=opaque THEN {
        SirPress.SetColor[data.ph, 0, 0, 255];
        PutTrap[
              xbotL: Rnd[trap.xbotL],
              xtopL: Rnd[trap.xtopL],
              xtopR: Rnd[trap.xtopR],
              xbotR: Rnd[trap.xbotR],
              ybot: yb,
              ytop: yt
              ];
        SetColor[data.ph, src.color];
          };
    SirPress.BeginScannedRectangle[
        p: data.ph,
        x: scanLineOrigin,
        y: yb,
        dotsPerLine: scanLineLength,
        numberOfLines: yt-yb,
        width: scanLineLength,
        height: yt-yb,
        unit: data.unit,
        nextLineDirection: up,
        nextDotDirection: right,
        coding: (WITH scanLine SELECT FROM bitMap => bitMap, packedMap => packedMap, bitSampled => bitSampled, bitBitSampled => bitBitSampled, nybbleSampled => nybbleSampled, byteSampled => byteSampled, ENDCASE => ERROR)];
    --set up for getting samples out of a sampled source
    [[rsdx, rsdy]] ← CGMatrix.InvRel[map,[1,0]];
    rsdx ← rsdx;
    rsdy ← rsdy;
    fsdx ← FixF[rsdx]; fsdy ← FixF[rsdy];
    --set up for the scan converter
    rdxl←(trap.xtopL-trap.xbotL); -- delta x, left
    rdxr←(trap.xtopR-trap.xbotR); -- delta x, right
    IF NOT(fat AND yb=yl) THEN {
      rdy: REAL←trap.ytop-trap.ybot; -- delta y
      rdxl←rdxl/rdy; rdxr←rdxr/rdy; -- dx/dy, left right
      };
    IF fat THEN {
      rxlb,rxrb,rxlt,rxrt: REAL ← 0; -- x at bottom and top of current line
      ltop: BOOLEAN ← rdxl<0; -- TRUE means leftmost x is at top of line
      rtop: BOOLEAN ← rdxr>0; -- TRUE means rightmost x is at top of line
      FOR y: CARDINAL IN[yb..yl] DO -- for each scan line
        IF y=yb THEN { rxlb←trap.xbotL; rxrb←trap.xbotR } -- first line
        ELSE { rxlb←rxlt; rxrb←rxrt }; -- successive lines
        IF y=yl THEN { rxlt←trap.xtopL; rxrt←trap.xtopR } -- last line
        ELSE IF y=yb THEN { -- first line, if yl>yb
          d: REAL←(yb+1)-trap.ybot; -- distance to top of line
          rxlt←rxlb+d*rdxl; rxrt←rxrb+d*rdxr }
        ELSE { rxlt←rxlb+rdxl; rxrt←rxrb+rdxr }; -- middle lines
        xl←Bot[IF ltop THEN rxlt ELSE rxlb];
        xr←Top[IF rtop THEN rxrt ELSE rxrb];
        IF xl<xr THEN DoLine[xmin: xl, xmax: xr, y: y];
        ENDLOOP;
      }
    ELSE {
      rxl,rxr: REAL ← 0; -- left and right x at middle of current line
      FOR y: CARDINAL IN[yb..yl] DO -- for each scan line
        IF y=yb THEN { -- first line
          d: REAL←(yb+0.5)-trap.ybot; -- distance to middle of line
          rxl←trap.xbotL+d*rdxl; rxr←trap.xbotR+d*rdxr }
        ELSE { rxl←rxl+rdxl; rxr←rxr+rdxr }; -- successive lines
        xl←Rnd[rxl]; xr←Rnd[rxr];
        IF xl<xr THEN DoLine[xl,xr,y];
        ENDLOOP;
      };
    SirPress.EndScannedRectangle[data.ph];
    };
  };
}.