PDImageReaderImpl.mesa
Last edited by Ken Pier, September 18, 1985 11:53:48 am PDT
DIRECTORY
Basics USING [bitsPerWord, bytesPerWord, LongNumber],
BiScrollers USING [ClientDataOfViewer],
ImagerPixelMap USING [Clear, Clip, Create, DeviceRectangle, Fill, Intersect, PixelMap, Reshape, Tile, Transfer, TransferTile, Window],
IO USING [EndOfStream, Error, GetIndex, SetIndex],
PDFileFormat USING [Command, Herald, Run, StartImage, StoreLoad],
PDFileReader USING [Close, ColorSamples, ColorTileFromLoad, ColorType, DeviceCommand, Error, Get, Handle, Keep, MaskRectangle, MaskRunGroup, MaskSamples, MaskTrapezoid, Rep, StateChange],
PDImageReader USING [Handle, ImageLink, ImageRep, PaintActionProc, Rep],
PreView USING [PDData, Rep],
ViewerClasses USING [Viewer];
PDImageReaderImpl: CEDAR PROGRAM
IMPORTS BiScrollers, PDFileReader, IO, ImagerPixelMap
EXPORTS PDImageReader = BEGIN
bitsPerWord: NAT = Basics.bitsPerWord;
bytesPerWord: NAT = Basics.bytesPerWord;
ImageLink: TYPE = PDImageReader.ImageLink;
ImageRep: TYPE = PDImageReader.ImageRep;
Leftover list types
LeftoverRef: TYPE = REF Leftover;
Leftover: TYPE = RECORD [
link: LeftoverRef ← NIL,
command: REF ANY,
colorType: PDFileReader.ColorType,
colorTileLoadAddress: INT ← -1,
priority: INT
];
InterpretImage: PUBLIC PROC [handle: PDFileReader.Handle, viewer: ViewerClasses.Viewer, paintAction: PDImageReader.PaintActionProc] RETURNS [lastImage: BOOLEANFALSE] = {
Interprets one image in a PDFile. Must be called ONLY AFTER the desired image has been selected by a call to ResetToImage.
May raise PDFileReader.Warning or PDFileReader.Error or PDReaderOutput.Error or PDReaderOutput.Abort
Returns TRUE if the interpreted image is the last image in the document.
ENABLE IO.EndOfStream, IO.Error => GOTO Quit; --Viewers closed the stream
imageHandle: PDImageReader.Handle←NIL;
pdData: PreView.PDData ← NARROW[BiScrollers.ClientDataOfViewer[viewer]];
readHead, writeHead, writeTail: LeftoverRef ← NIL; --heads and tail of leftover lists
currentColorType: PDFileReader.ColorType;
currentColorTileLoadAddress: INT ← -1;
currentPriority: INT ← -1;
leftovers: BOOLEAN; --can't be filled in until StartImage has happened
commandRef: REF ANY;
band: ImagerPixelMap.PixelMap;
bandMaxS: INT;
cachedColorTile: ImagerPixelMap.Tile ← [0,0,0,0,0,NIL];
cachedColorReference: INT ← -1;
GetColorTile: PROC = {
this routine was changed to recirculate the PixelMapRep in the cachedColorTile
IF currentColorTileLoadAddress # cachedColorReference THEN {
cachedColorTile ← PDFileReader.ColorTileFromLoad[handle: handle, colorTileLoadAddress: (cachedColorReference ← currentColorTileLoadAddress), scratch: cachedColorTile.refRep];
};
};
TransferRectangle: PROC [rectangle: ImagerPixelMap.DeviceRectangle] = {
SELECT currentColorType FROM
none => ERROR;
clear => ImagerPixelMap.Fill[band, rectangle, 0];
ink => ImagerPixelMap.Fill[band, rectangle, 1];
opaqueTile => {
GetColorTile[];
ImagerPixelMap.TransferTile[band.Clip[rectangle], cachedColorTile];
};
transparentTile => {
GetColorTile[];
ImagerPixelMap.TransferTile[band.Clip[rectangle], cachedColorTile, [or, null]];
};
ENDCASE => ERROR;
};
DoLine: PROCEDURE [sMin,fMin,fSize: CARDINAL] = {
SELECT currentColorType FROM
none => ERROR;
clear => ImagerPixelMap.Fill[band, [sMin: sMin, fMin: fMin, sSize: 1, fSize: fSize], 0];
opaqueTile, ink => ImagerPixelMap.Fill[band, [sMin: sMin, fMin: fMin, sSize: 1, fSize: fSize], 1];
transparentTile =>
ImagerPixelMap.TransferTile[band.Clip[[sMin: sMin, fMin: fMin, sSize: 1, fSize: fSize]], cachedColorTile, [or, null]];
ENDCASE => ERROR;
};
TempColor: PROC [rectangle: ImagerPixelMap.DeviceRectangle] = {
SELECT currentColorType FROM
none => ERROR;
clear => NULL;
ink => NULL;
opaqueTile => {
GetColorTile[];
ImagerPixelMap.TransferTile[band.Clip[rectangle], cachedColorTile, [xor, complement]];
};
transparentTile => GetColorTile[];
ENDCASE => ERROR;
};
AddLeftover: PROC = {
[]←PDFileReader.Keep[handle: handle, ref: commandRef];
IF writeHead=NIL THEN writeHead←writeTail←NEW[Leftover ←[link: NIL, command: commandRef, colorType: currentColorType, colorTileLoadAddress: currentColorTileLoadAddress, priority: currentPriority]]
ELSE {
writeTail.link←NEW[Leftover ←[link: NIL, command: commandRef, colorType: currentColorType, colorTileLoadAddress: currentColorTileLoadAddress, priority: currentPriority]];
writeTail←writeTail.link;
};
};
DO
ENABLE UNWIND => {readHead←writeHead←writeTail←NIL; handle.Close[];};
IF readHead#NIL AND readHead.priority<handle.priority THEN {
commandRef ← readHead.command;
currentColorTileLoadAddress←readHead.colorTileLoadAddress;
currentColorType←readHead.colorType;
currentPriority←readHead.priority;
now remove this record from readList
readHead←readHead.link;
}
ELSE {
commandRef ← PDFileReader.Get[handle: handle, scanning: FALSE];
currentColorTileLoadAddress←handle.colorTileLoadAddress;
currentColorType←handle.colorType;
currentPriority←handle.priority;
};
IF pdData.abort THEN {readHead←writeHead←writeTail←NIL; RETURN [TRUE];};
WITH commandRef SELECT FROM
stateChange: PDFileReader.StateChange => {
SELECT stateChange.whatChanged FROM
imageStart => {
pdData.image←imageHandle ← StartImage[fileHandle: handle, oldPixelMap: IF imageHandle#NIL THEN imageHandle.pixelMap ELSE [,,,,,,NIL] ];
band ← imageHandle.pixelMap;
bandMaxS�nd.sOrigin+band.sMin+band.sSize;
leftovers ← handle.image.leftOverMode;
};
imageEnd => {
paintAction[self: viewer, whatChanged: imageHandle];
readHead←writeHead←writeTail←NIL;
RETURN [FALSE];
};
priorityChange => NULL;
colorChange => NULL;
bandChange => {
IF pdData.abort THEN {readHead←writeHead←writeTail←NIL; RETURN [TRUE];};
paintAction[self: viewer, whatChanged: imageHandle];
update pixelMap in handle
imageHandle.pixelMap.sOrigin ← handle.herald.bandSSize*(handle.bandNumber+handle.image.passBands);
ImagerPixelMap.Clear[imageHandle.pixelMap];
band ← imageHandle.pixelMap;
bandMaxS�nd.sOrigin+band.sMin+band.sSize;
IF readHead#NIL THEN ERROR; --DEBUGGING CHECK !!
readHead←writeHead;
writeHead←NIL;
};
loadChange => NULL;
documentEnd => RETURN [TRUE];
ENDCASE => ERROR;
};
maskRectangle: PDFileReader.MaskRectangle => {
TransferRectangle[[maskRectangle.sMin, maskRectangle.fMin, maskRectangle.sSize, maskRectangle.fSize]];
IF leftovers AND maskRectangle.sMin+maskRectangle.sSize>bandMaxS THEN AddLeftover[];
};
maskTrapezoid: PDFileReader.MaskTrapezoid => {
EdgeBlock: TYPE = RECORD [fPos, fIncr: Basics.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: CARDINALMIN[maskTrapezoid.fMin, maskTrapezoid.fMinLast];
fMaxBox: CARDINALMAX[maskTrapezoid.fMin+maskTrapezoid.fSize, maskTrapezoid.fMinLast+maskTrapezoid.fSizeLast];
fSizeBox: CARDINAL𡤏MaxBox-fMinBox;
trapMaxS: CARDINAL ← maskTrapezoid.sMin+maskTrapezoid.sSize;
s: CARDINAL ← maskTrapezoid.sMin;
sMinBand: CARDINALMIN[band.sOrigin+band.sMin, maskTrapezoid.sMin+maskTrapezoid.sSize];
sMaxBand: CARDINALMIN[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: PDFileReader.MaskRunGroup => TRUSTED {
s: CARDINAL ← maskRunGroup.sMin;
sMinBand: CARDINALMIN[band.sOrigin+band.sMin, maskRunGroup.sMin+maskRunGroup.sSize];
sMaxBand: CARDINALMIN[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
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: PDFileReader.MaskSamples => {
rectangle: ImagerPixelMap.DeviceRectangle ← ImagerPixelMap.Window[maskSamples.samples];
TempColor[rectangle];
SELECT currentColorType FROM
none => ERROR;
clear => ImagerPixelMap.Transfer[band, maskSamples.samples, [and, complement]];
opaqueTile, ink => ImagerPixelMap.Transfer[band, maskSamples.samples, [or, null]];
transparentTile => {
bounds: ImagerPixelMap.DeviceRectangle ← ImagerPixelMap.Intersect[rectangle, ImagerPixelMap.Window[band]];
destDesc: ImagerPixelMap.PixelMap←ImagerPixelMap.Create[lgBitsPerPixel: 0, bounds: bounds];
GetColorTile[];
ImagerPixelMap.TransferTile[dest: destDesc, tile: cachedColorTile];
ImagerPixelMap.Transfer[dest: destDesc, source: maskSamples.samples, function: [and, null]];
ImagerPixelMap.Transfer[dest: band, source: destDesc, function: [or, null]];
};
ENDCASE => ERROR;
TempColor[rectangle];
IF leftovers AND maskSamples.loadAddress>=0 AND maskSamples.samples.sOrigin+maskSamples.samples.sMin+maskSamples.samples.sSize > bandMaxS THEN AddLeftover[];
};
colorSamples: PDFileReader.ColorSamples => {
ImagerPixelMap.Transfer[band, colorSamples.samples];
};
deviceCommand: PDFileReader.DeviceCommand => {
};
ENDCASE => NULL;
ENDLOOP;
EXITS
Quit => NULL;
};
StartImage: PROC [fileHandle: PDFileReader.Handle, oldPixelMap: ImagerPixelMap.PixelMap←[,,,,,,NIL] ] RETURNS [handle: PDImageReader.Handle] = TRUSTED {
handle ← NEW[PDImageReader.Rep];
IF oldPixelMap.refRep=NIL THEN handle.pixelMap ← ImagerPixelMap.Create[lgBitsPerPixel: 0, bounds: [sMin: 0, fMin: 0, sSize: fileHandle.herald.bandSSize, fSize: fileHandle.herald.imageFSize]]
ELSE handle.pixelMap ← ImagerPixelMap.Reshape[refRep: oldPixelMap.refRep, lgBitsPerPixel: 0, bounds: [sMin: 0, fMin: fileHandle.image.fMinPage, sSize: fileHandle.herald.bandSSize, fSize: fileHandle.image.fSizePage]];
handle.pixelMap.sOrigin ← fileHandle.herald.bandSSize*fileHandle.image.passBands;
ImagerPixelMap.Clear[handle.pixelMap];
};
GetPageStructure: PUBLIC PROC [handle: PDFileReader.Handle] RETURNS [list: ImageLink, imageCount: INT] = {
assumes filestream is available from Handle and Gets can be executed safely
images, imageTail: ImageLink ← NIL;
currentImageNumber: INT ← 0;
copyHandle: PDFileReader.Handle ← NEW[PDFileReader.Rep ← handle^];
streamInitialIndex: INT ← handle.stream.GetIndex[];
handle.stream.SetIndex[SIZE[PDFileFormat.Herald]*bytesPerWord];--start of file just past Herald
DO
WITH copyHandle.Get[scanning: TRUE] SELECT FROM
stateChange: PDFileReader.StateChange => {
SELECT stateChange.whatChanged FROM
imageStart => {
IF copyHandle.image.nBands>0 THEN {
newImage: ImageLink ← NEW[ImageRep ←[link: NIL, type: imageStart, imageNumber: (currentImageNumber𡤌urrentImageNumber+1), index: copyHandle.stream.GetIndex[]-(SIZE[PDFileFormat.StartImage]+SIZE[PDFileFormat.Command])*bytesPerWord]];
IF images=NIL THEN images←imageTail←newImage
ELSE {imageTail.link←newImage; imageTail←newImage};
};
};
imageEnd => NULL;
priorityChange => NULL;
colorChange => NULL;
bandChange => NULL;
loadChange => {
newImage: ImageLink←NEW[ImageRep ←[link: NIL, type: loadChange, imageNumber: -1, index: copyHandle.stream.GetIndex[]-(SIZE[PDFileFormat.StoreLoad]+SIZE[PDFileFormat.Command]+stateChange.loadChangeLength)*bytesPerWord]];
IF images=NIL THEN images←imageTail←newImage
ELSE {imageTail.link←newImage; imageTail←newImage; };
};
documentEnd => {
handle.stream.SetIndex[streamInitialIndex]; --restore PD stream
RETURN [images, currentImageNumber];
};
ENDCASE => PDFileReader.Error[handle: handle, code: unrecognisedControlCommand, wordIndex: -1, wordCount: -1, description: "Image structure Get error"];
};
ENDCASE => NULL;
ENDLOOP;
};
ResetToImage: PUBLIC PROC [data: REF ANY] RETURNS [ImageLink] = {
A routine to reconstruct the load for the image number in pdData.pageNumber. pdData MUST BE a Preview.PDData. It has type REF ANY to avoid circular recompilation dependencies between this module and PreView.mesa.
In this case, pdData.pageNumber contains an image number rather than a page number.
Requires that a page structure has already been built using GetPageStructure
and stored in pdData.pageStructure . Returns an ImageLink (single element) for the image that has been successfully reset to. Returns NIL if pageNumber is not in the images list.
rebuildLoad: BOOLEANTRUE;
pdData: PreView.PDData ← NARROW[data];
handle: PDFileReader.Handle ← pdData.pdhandle;
imageNumber: INT ← pdData.pageNumber;
nextImage: ImageLink ← pdData.pageStructure;
handle.priority𡤀 handle.colorType←none; --fake the reader into rebuilding the load
IF pdData.atImage#NIL AND pdData.atImage.imageNumber >= imageNumber THEN rebuildLoad←FALSE;
UNTIL nextImage=NIL DO
SELECT nextImage.type FROM
imageStart => IF nextImage.imageNumber=imageNumber THEN {
handle.stream.SetIndex[nextImage.index]; --set stream to point to StartImage command
pdData.atImage ← nextImage;
RETURN[nextImage];
};
loadChange => {
IF rebuildLoad THEN {
handle.stream.SetIndex[nextImage.index]; --set stream to point to storeLoad command
WITH handle.Get[scanning: FALSE] SELECT FROM --handle.Get[] has the side effect of building the load!!
sc: PDFileReader.StateChange =>
SELECT sc.whatChanged FROM
loadChange => NULL; --found the expected loadChange command
ENDCASE => PDFileReader.Error[handle: handle, code: unrecognisedControlCommand, wordIndex: -1, wordCount: -1, description: "Page structure index error"]; --found a StateChange which was not a loadChange
ENDCASE => PDFileReader.Error[handle: handle, code: unrecognisedControlCommand, wordIndex: -1, wordCount: -1, description: "Page structure index error"]; --tried to point to a loadChange but reader didn't find one
};
};
ENDCASE => PDFileReader.Error[handle: handle, code: unrecognisedControlCommand, wordIndex: -1, wordCount: -1, description: "Page structure type error"];
nextImage←nextImage.link;
ENDLOOP;
RETURN[NIL]; --never found the requested imageNumber
};
END.