-- PDInterpPageImpl.mesa
-- Copyright (C) 1984, 1986 Xerox Corporation.  All rights reserved.
-- Michael Plass, October 31, 1984 10:35:36 am PST
-- Last Edited by: Pier, November 30, 1983 6:37 pm
-- Tim Diebert, 5-Sep-86 13:52:52 
   
DIRECTORY Environment, Inline, PDFileFormat, PDInterpReader, PDInterpOutput,
  PDInterpPage, PDInterpBitmap, PDInterpSysCalls, Heap, PDQueue, Process;
   
PDInterpPageImpl: PROGRAM
   IMPORTS Inline, PDInterpReader, PDInterpOutput, PDInterpBitmap,
     PDInterpSysCalls, Heap, Process, PDQueue
   EXPORTS PDInterpPage
= BEGIN
   
CommandBuffer: TYPE = PDInterpReader.CommandBuffer;
bitsPerWord: NAT = Environment.bitsPerWord;
heapZone: UNCOUNTED ZONE=Heap.systemZone;
totalAllocates, totalFrees: INT ← 0;
availCount: NAT; --count of the number of leftover records on the free list
   
-- Leftover list types
LeftoverRef: TYPE = LONG POINTER TO Leftover;
Leftover: TYPE = RECORD [
   link: LeftoverRef ← NIL,
   commandBuffer: CommandBuffer,
   colorType: PDInterpReader.ColorType,
   colorTileLoadAddress:  INT ← -1,
   priority: INT
   ];
   
InterpretPage: PUBLIC PROC [handle: PDInterpReader.Handle, request: PDQueue.Request, abort: LONG POINTER TO BOOLEAN] RETURNS [ok: BOOLEAN] = {
   readHead, writeHead, availHead, writeTail: LeftoverRef ← NIL; --heads and tail of leftover lists
   currentColorType: PDInterpReader.ColorType;
   currentColorTileLoadAddress: INT ← -1;
   currentPriority: INT ← -1;
   leftovers: BOOLEAN; --can't be filled in until StartImage has happened
   commandBuffer: CommandBuffer;
   band: PDInterpBitmap.BitmapDesc;
   bandMaxS: INT;
   cachedColorTile: PDInterpBitmap.Tile;
   cachedColorReference: INT ← -1;
   GetColorTile: PROC = {
      IF currentColorTileLoadAddress # cachedColorReference THEN {
         cachedColorTile ← PDInterpReader.ColorTileFromLoad[handle, cachedColorReference ← currentColorTileLoadAddress, scratchPointer, scratchWords];
         };
      };
   TransferRectangle: PROC [rectangle: PDInterpBitmap.Rectangle] = {
      SELECT currentColorType FROM
         none => ERROR;
         clear => PDInterpBitmap.Fill[band, rectangle, 0];
         ink => PDInterpBitmap.Fill[band, rectangle, 1];
         opaqueTile => {
            GetColorTile[];
            PDInterpBitmap.TransferTile[band.Clip[rectangle], cachedColorTile];
            };
         transparentTile => {
            GetColorTile[];
            PDInterpBitmap.TransferTile[band.Clip[rectangle], cachedColorTile, [or, null]];
            };
         ENDCASE => ERROR;
      };
   DoLine: PROCEDURE [sMin,fMin,fSize: CARDINAL] = {
      SELECT currentColorType FROM
         none => ERROR;
         clear => PDInterpBitmap.Fill[band, [sMin: sMin, fMin: fMin, sSize: 1, fSize: fSize], 0];
         opaqueTile, ink => PDInterpBitmap.Fill[band, [sMin: sMin, fMin: fMin, sSize: 1, fSize: fSize], 1];
         transparentTile => 
            PDInterpBitmap.TransferTile[band.Clip[[sMin: sMin, fMin: fMin, sSize: 1, fSize: fSize]], cachedColorTile, [or, null]];
         ENDCASE => ERROR;
      };
   TempColor: PROC [rectangle: PDInterpBitmap.Rectangle] = {
      SELECT currentColorType FROM
         none => ERROR;
         clear => NULL;
         ink => NULL;
         opaqueTile => {
            GetColorTile[];
            PDInterpBitmap.TransferTile[band.Clip[rectangle], cachedColorTile, [xor, complement]];
            };
         transparentTile => GetColorTile[];
         ENDCASE => ERROR;
      };
   AddLeftover: PROC = {
      IF availCount=0 THEN {
         totalAllocates←totalAllocates+1;
         IF writeHead=NIL THEN writeHead←writeTail←heapZone.NEW[Leftover ←[link: NIL, commandBuffer: commandBuffer, colorType: currentColorType, colorTileLoadAddress: currentColorTileLoadAddress, priority: currentPriority]]
         ELSE {
            writeTail.link←heapZone.NEW[Leftover ←[link: NIL, commandBuffer: commandBuffer, colorType: currentColorType, colorTileLoadAddress: currentColorTileLoadAddress, priority: currentPriority]];
            writeTail←writeTail.link;
            };
         }
      ELSE {
         availCount←availCount-1; --move a node from availList to writeList
         IF writeHead=NIL THEN {writeHead←writeTail←availHead; availHead←availHead.link;}
         ELSE {
            writeTail.link←availHead; writeTail←availHead; availHead←availHead.link;
            };
         writeTail↑←[link: NIL, commandBuffer: commandBuffer, colorType: currentColorType, colorTileLoadAddress: currentColorTileLoadAddress, priority: currentPriority];
         };
      };
   FreeList: PROC[head: LeftoverRef] = { --this proc out here so the UNWIND can call it
      tempHead: LeftoverRef;
      UNTIL head=NIL DO
         tempHead←head↑.link;
         heapZone.FREE[@head];
         totalFrees←totalFrees+1;
         head←tempHead;
         ENDLOOP;
      };
   CheckAbort: PROC = {IF abort↑ THEN ERROR ABORTED};
   availCount←0;
   Process.SetPriority[Process.priorityBackground];
   DO
      ENABLE UNWIND => {
         FreeList[readHead];
         FreeList[writeHead];
         FreeList[availHead]; 
         availCount←0;
         Process.SetPriority[Process.priorityNormal];
         };
      IF readHead#NIL AND readHead.priority<handle.priority THEN {
         tempHead: LeftoverRef ← readHead;
         commandBuffer ←  readHead.commandBuffer;
         currentColorTileLoadAddress←readHead.colorTileLoadAddress;
         currentColorType←readHead.colorType;
         currentPriority←readHead.priority;
         -- now move this record from readList to availList
         readHead←readHead.link;
         tempHead.link←availHead;
         availHead←tempHead;
         availCount←availCount+1;
         }
      ELSE {
         commandBuffer ←  PDInterpReader.Get[handle];
         currentColorTileLoadAddress←handle.colorTileLoadAddress;
         currentColorType←handle.colorType;
         currentPriority←handle.priority;
         };
      WITH commandBuffer SELECT FROM
         stateChange: CommandBuffer.stateChange => {
            SELECT stateChange.whatChanged FROM
               imageStart => {
                  band ← PDInterpOutput.StartImage[handle.herald, handle.image, request];
                  bandMaxS ← band.sOrigin+band.sMin+band.sSize;
                  leftovers ← handle.image.leftOverMode;
                  IF handle.image.nBands = 0 THEN {PDInterpOutput.EndImage[request]};
                  };
               imageEnd => {
                  [] ← PDInterpOutput.EndBand[];
                  IF readHead#NIL THEN ERROR; --DEBUGGING CHECK !!
                  readHead←writeHead;
                  writeHead←NIL;
                  FreeList[readHead]; --from this image
                  FreeList[writeHead]; --from this image
                  FreeList[availHead]; 
                  availCount←0;
                  Process.SetPriority[Process.priorityForeground];
                  PDQueue.DeferSaving[];
                  PDInterpOutput.EndImage[request];
                  Process.SetPriority[Process.priorityNormal];
                  PDQueue.DoDeferredSave[];
                  RETURN [TRUE];
                  };
               priorityChange => NULL;
               colorChange => NULL;
               bandChange => {
                  CheckAbort[];
                  band ← PDInterpOutput.EndBand[];
                  bandMaxS←band.sOrigin+band.sMin+band.sSize;
                  IF readHead#NIL THEN ERROR; --DEBUGGING CHECK !!
                  readHead←writeHead;
                  writeHead←NIL;
                  };
               loadChange => NULL;
               documentEnd => {Process.SetPriority[Process.priorityNormal]; RETURN [FALSE]};
               ENDCASE => ERROR;
            };
         maskRectangle: CommandBuffer.maskRectangle => {
            TransferRectangle[[maskRectangle.sMin, maskRectangle.fMin, maskRectangle.sSize, maskRectangle.fSize]];
            IF leftovers AND maskRectangle.sMin+maskRectangle.sSize>bandMaxS THEN AddLeftover[];
            };
         maskTrapezoid: CommandBuffer.maskTrapezoid => {
            EdgeBlock: TYPE = RECORD [fPos, fIncr: Environment.LongNumber];
            MakeEdge: PROC[fMinLast, fMin, sSize: CARDINAL] RETURNS [edge: EdgeBlock] = {
               edge.fPos.highbits ← fMin;
               edge.fPos.lowbits ← 0;
               edge.fIncr.highbits ← fMinLast - fMin;
               edge.fIncr.lowbits  ← 0;
               IF sSize > 1 THEN edge.fIncr.li ← edge.fIncr.li/sSize; 
               }; 
            fMinBox: CARDINAL←MIN[maskTrapezoid.fMin, maskTrapezoid.fMinLast];
            fMaxBox: CARDINAL←MAX[maskTrapezoid.fMin+maskTrapezoid.fSize, maskTrapezoid.fMinLast+maskTrapezoid.fSizeLast];
            fSizeBox: CARDINAL←fMaxBox-fMinBox;
            trapMaxS: CARDINAL ← maskTrapezoid.sMin+maskTrapezoid.sSize;
            s: CARDINAL ← maskTrapezoid.sMin;
            sMinBand: CARDINAL ← MIN[band.sOrigin+band.sMin, maskTrapezoid.sMin+maskTrapezoid.sSize];
            sMaxBand: CARDINAL ← MIN[bandMaxS, trapMaxS];
            -- calculate initial edges:
            minEdge: EdgeBlock ← MakeEdge[fMinLast: maskTrapezoid.fMinLast, fMin: maskTrapezoid.fMin, sSize: maskTrapezoid.sSize];
            maxEdge: EdgeBlock ← MakeEdge[fMinLast: maskTrapezoid.fMinLast+maskTrapezoid.fSizeLast, fMin: maskTrapezoid.fMin+maskTrapezoid.fSize, sSize: maskTrapezoid.sSize];
            TempColor[[sMin: maskTrapezoid.sMin, fMin: fMinBox, sSize: maskTrapezoid.sSize, fSize: fSizeBox]];
            WHILE s < sMinBand DO
               minEdge.fPos.lc ← minEdge.fPos.lc + minEdge.fIncr.li;
               maxEdge.fPos.lc ← maxEdge.fPos.lc + maxEdge.fIncr.li;
               s ← s + 1;
               ENDLOOP;
            WHILE s < sMaxBand DO
               DoLine[sMin: s, fMin: minEdge.fPos.highbits, fSize: maxEdge.fPos.highbits-minEdge.fPos.highbits];
               minEdge.fPos.lc ← minEdge.fPos.lc + minEdge.fIncr.li;
               maxEdge.fPos.lc ← maxEdge.fPos.lc + maxEdge.fIncr.li;
               s ← s + 1;
               ENDLOOP;
            TempColor[[sMin: maskTrapezoid.sMin, fMin: fMinBox, sSize: maskTrapezoid.sSize, fSize: fSizeBox]];
            IF leftovers AND trapMaxS>bandMaxS THEN AddLeftover[];
            };
         maskRunGroup: CommandBuffer.maskRunGroup => {
            s: CARDINAL ← maskRunGroup.sMin;
            sMinBand: CARDINAL ← MIN[band.sOrigin+band.sMin, maskRunGroup.sMin+maskRunGroup.sSize];
            sMaxBand: CARDINAL ← MIN[band.sOrigin+band.sMin+band.sSize, maskRunGroup.sMin+maskRunGroup.sSize];
            run: LONG POINTER TO PDFileFormat.Run ← maskRunGroup.pointer;
            TempColor[[maskRunGroup.sMin, maskRunGroup.fMin, maskRunGroup.sSize, maskRunGroup.fSize]];
            WHILE s < sMinBand DO
               IF run.lastRun THEN s ← s + 1;
               run ← run + SIZE[PDFileFormat.Run];
               ENDLOOP;
            WHILE s < sMaxBand DO
               IF run.fMin+run.fSize > maskRunGroup.fSize THEN ERROR;
               DoLine[sMin: s, fMin: run.fMin+maskRunGroup.fOffset, fSize: run.fSize];
               IF run.lastRun THEN s ← s + 1;
               run ← run + SIZE[PDFileFormat.Run];
               ENDLOOP;
            TempColor[[maskRunGroup.sMin, maskRunGroup.fMin, maskRunGroup.sSize, maskRunGroup.fSize]];
            IF leftovers AND maskRunGroup.loadAddress>=0 AND maskRunGroup.sMin+maskRunGroup.sSize>bandMaxS THEN {
               maskRunGroup.sSize←maskRunGroup.sSize-(s-maskRunGroup.sMin);
               maskRunGroup.sMin←s;
               maskRunGroup.runCount ← -1; --unused, eventually eliminate from DEF
               maskRunGroup.pointer←run;
               AddLeftover[];
               };
            };
         maskSamples: CommandBuffer.maskSamples => {
            rectangle: PDInterpBitmap.Rectangle ← PDInterpBitmap.Window[maskSamples.samples];
            TempColor[rectangle];
            SELECT currentColorType FROM
               none => ERROR;
               clear => PDInterpBitmap.Transfer[band, maskSamples.samples, [and, complement]];
               opaqueTile, ink => PDInterpBitmap.Transfer[band, maskSamples.samples, [or, null]];
               transparentTile => {
                  bounds: PDInterpBitmap.Rectangle ← PDInterpBitmap.Intersect[rectangle, PDInterpBitmap.Window[band]];
                  scratchWords: INT ← Inline.LongMult[bounds.sSize, (bounds.fSize+bitsPerWord-1)/bitsPerWord];
                  buffer: LONG POINTER ←PDInterpSysCalls.AllocateSpace[scratchWords];
                  destDesc:PDInterpBitmap.BitmapDesc←PDInterpBitmap.Reshape[pointer: buffer, words: scratchWords, bounds: bounds];
                  GetColorTile[];
                  PDInterpBitmap.TransferTile[dest: destDesc, tile: cachedColorTile];
                  PDInterpBitmap.Transfer[destDesc, maskSamples.samples, [and, null]];
                  PDInterpBitmap.Transfer[band, destDesc, [or, null]];
                  PDInterpSysCalls.FreeSpace[buffer];
                  };
               ENDCASE => ERROR;
            TempColor[rectangle];
            IF leftovers AND maskSamples.loadAddress>=0 AND maskSamples.samples.sOrigin+maskSamples.samples.sMin+maskSamples.samples.sSize > bandMaxS THEN AddLeftover[];
            };
         colorSamples: CommandBuffer.colorSamples => {
            PDInterpBitmap.Transfer[band, colorSamples.samples];
            };
         deviceCommand: CommandBuffer.deviceCommand => {
            };
         ENDCASE => NULL;
      ENDLOOP;
   };
   
scratchPages: INT = 4;
scratchWords: INT ← scratchPages*Environment.wordsPerPage;
scratchPointer: LONG POINTER ← PDInterpSysCalls.AllocateSpace[scratchWords];
   
END.