CtFileCommandsImpl.mesa
Copyright Ó 1990, 1992 by Xerox Corporation. All rights reserved.
Bloomenthal, December 7, 1992 4:34 pm PST
Andrew Glassner July 9, 1991 1:21 pm PDT
DIRECTORY AISFileFormat, AISIO, Args, Basics, CtBasic, CtDispatch, CtFile, CtMap, CtMod, FileNames, FS, Imager, ImagerBox, ImagerColor, ImagerPixel, ImagerPixelArray, ImagerSample, IO, Process, Rope, SF, ViewerClasses;
CtFileCommandsImpl:
CEDAR
PROGRAM
IMPORTS AISIO, Args, Basics, CtBasic, CtDispatch, CtFile, CtMap, CtMod, FileNames, FS, Imager, ImagerColor, ImagerPixel, ImagerPixelArray, ImagerSample, IO, Process, Rope, SF
~ BEGIN
Types
AISInfo: TYPE ~ AISIO.Info;
SampleMaps: TYPE ~ CtBasic.SampleMaps;
SampleMap: TYPE ~ ImagerSample.SampleMap;
ROPE: TYPE ~ Rope.ROPE;
Write: Display to File
ctSaveUsage:
ROPE ~
"Ct Save <name> [-w <x y w h>] [-wi] [-wp]
Save the entire color display image to disk.
If -wi option, interactively select area to save.
If -wp option, then use last specified (-w or -wi) box.
If 24 bits, '-red.ais', '-grn.ais', '-blu.ais' are
appended to name for the appropriate file.";
CtSave: CtDispatch.CtProc ~ {
ENABLE AISIO.Error => {error ¬ reason; GOTO Bad};
placement:
AISIO.Placement ¬
NEW[AISFileFormat.PlacementPart ¬ [
Basics.HFromCard16[maps.x], Basics.HFromCard16[maps.y],
Basics.HFromCard16[maps.w], Basics.HFromCard16[maps.h]]];
name: ROPE ¬ Args.GetRope[cmd];
affect ¬ [[0, 0], [0, 0]];
IF name = NIL THEN RETURN[error: "no name"];
SELECT maps.nChannels
FROM
1 => {
IO.PutFL[cmd.out, "saving [%g, %g] to [%g, %g] (xywh: %g %g %g %g) %g . . . ", LIST[IO.int[maps.x], IO.int[maps.y], IO.int[maps.box.max.f], IO.int[maps.box.max.s], IO.int[maps.x], IO.int[maps.y], IO.int[maps.w], IO.int[maps.h], IO.rope[name]]];
AISIO.Write[FileNames.ResolveRelativePath[name], maps[0].map, placement];
};
3 => {
names: ARRAY [0..3) OF ROPE ¬ CtFile.GetColorAISNames[name];
IO.PutFL[cmd.out, "saving [%g, %g] to [%g, %g] (xywh: %g %g %g %g)",
LIST[IO.int[maps.x], IO.int[maps.y], IO.int[maps.x], IO.int[maps.y], IO.int[maps.w], IO.int[maps.h], IO.int[maps.box.max.f], IO.int[maps.box.max.s]]];
FOR n:
NAT
IN [0..3)
DO
IO.PutF1[cmd.out, " %g . . . ", IO.rope[names[n]]];
AISIO.Write[FileNames.ResolveRelativePath[names[n]], maps[n].map, placement];
ENDLOOP;
};
ENDCASE;
IO.PutRope[cmd.out, "done\n"];
EXITS Bad => NULL;
};
Read: File to Display
ctViewUsage: ROPE ~
"Ct View <AIS file> [option]
Display the named image file in a viewer.
The complete file name should be given unless the image
is 24 bits, in which case only the base name should be given.
Images are un-scaled, and clipped to a viewer when first displayed.
Options:
-clear clear the color display first
-center center the image on the color display
-noPlace otherwise, place where originally saved
-cmap <file> load the specified color map
-w <x y w h> display image in this box
-wi display image in selected box
-a <x y w h> if ais file, read data from this box.";
ctViewUsage:
ROPE ~
"Ct View <AIS file> [option]
Display the named, un-scaled image file in a viewer.
if too large, the image is clipped to a viewer when first displayed.
Options:
-clear clear the color display first
-center center the image within the viewer
-noPlace otherwise, place image at original location
-cmap <file> load the specified color map
-w <x y w h> display image in this window
-wi display image in selected box
-a <x y w h> if AIS file, read data from this box.";
CtView: CtDispatch.CtProc ~ {
name, clear, cmap, center, noPlace, x, y, w, h: Args.Arg;
[name, clear, cmap, center, noPlace, x, y, w, h] ¬
Args.ArgsGet[cmd, "%s-clear%b-cmap%s-center%b-noPlace%b-a%iiii"
! Args.Error => {error ¬ reason; CONTINUE}];
affect ¬ [[0, 0], [0, 0]];
IF error #
NIL
THEN RETURN
ELSE {
EachName:
PROC [fullFName:
ROPE]
RETURNS [continue:
BOOL ¬
TRUE] ~ {
ENABLE AISIO.Error => {IO.PutF[cmd.out, reason]; GOTO Bad};
GetInfoAndMap:
PROC
[name:
ROPE]
RETURNS [info: AISInfo, map: SampleMap] ~ {
map ¬ AISIO.Read[name, info ¬ AISIO.ReadInfo[name]];
IF x.ok
AND y.ok
AND w.ok
AND h.ok
THEN
map ¬ ImagerSample.ZeroOrigin[ImagerSample.Clip[map, [[y.int, x.int], [y.int+h.int, x.int+w.int]]]];
};
GetMinAndSize:
PROC [r, g, b, bw: SampleMap, placement:
AISIO.Placement]
RETURNS [size, srcMin, dstMin: SF.Vec ¬ [0, 0]]
~ {
IF r # NIL THEN size ¬ SF.Max[size, ImagerSample.GetSize[r]];
IF g # NIL THEN size ¬ SF.Max[size, ImagerSample.GetSize[g]];
IF b # NIL THEN size ¬ SF.Max[size, ImagerSample.GetSize[b]];
IF bw # NIL THEN size ¬ SF.Max[size, ImagerSample.GetSize[bw]];
dstMin ¬ maps.box.min;
SELECT
TRUE
FROM
center.ok
=>
{
IF maps.h > size.s
THEN dstMin.s ¬ (maps.h-size.s)/2
ELSE srcMin.s ¬ (size.s-maps.h)/2;
IF maps.w > size.f
THEN dstMin.f ¬ (maps.w-size.f)/2
ELSE srcMin.f ¬ (size.f-maps.w)/2;
};
placement #
NIL
AND
NOT noPlace.bool => {
dstMin ¬ [
Basics.Card16FromH[placement.yBottom],
Basics.Card16FromH[placement.xLeft]];
IF NOT SF.In[dstMin, maps.box] THEN dstMin ¬ maps.box.min;
};
ENDCASE;
size ¬ [MIN[size.s, maps.h-dstMin.s], MIN[size.f, maps.w-dstMin.f]];
};
r, g, b, bw: SampleMap ¬ NIL;
size, srcMin, dstMin: SF.Vec;
rInfo, gInfo, bInfo, bwInfo: AISInfo;
fileInfo: CtFile.FileInfo ¬ CtFile.Parse[fullFName];
Process.CheckForAbort[];
SELECT fileInfo.type
FROM
ip => IO.PutRope[cmd.out, "Interpress files not supported\n"];
bw => {
[bwInfo, bw] ¬ GetInfoAndMap[fullFName];
[size, srcMin, dstMin] ¬ GetMinAndSize[NIL, NIL, NIL, bw, bwInfo.placement];
ImagerSample.BasicTransfer[maps[0].map, bw, dstMin, srcMin, size];
affect.max ¬ SF.Max[affect.max, size];
};
color => {
[rInfo, r] ¬ GetInfoAndMap[fileInfo.names[0]];
[gInfo, g] ¬ GetInfoAndMap[fileInfo.names[1]];
[bInfo, b] ¬ GetInfoAndMap[fileInfo.names[2]];
SELECT maps.bpp
FROM
8 => {
pm: ImagerPixel.PixelMap ¬ ImagerPixel.MakePixelMap[r, g, b];
pa: ImagerPixelArray.PixelArray ¬ ImagerPixelArray.FromPixelMap[
pm, pm.box, [down, right]];
op: ImagerColor.ColorOperator ¬ ImagerColor.NewColorOperatorRGB[255];
delta: Imager.
VEC ¬
IF
center.ok
THEN [(maps.h-pa.sSize)/2, (maps.w-pa.fSize)/2]
ELSE [0, maps.h-pa.sSize];
CtMap.Dither[];
Note: even though CtView, as a DispatchProc, is supplied with a context,
we perform the following to ensure that the current context is associated
with the current colormap, which may have changed since the creation
of the context:
context ¬ CtBasic.ContextFromSampleMaps[maps];
Imager.DrawSampledColor[context, pa, op,, delta];
affect.max ¬ SF.Max[affect.max, [pa.sSize, pa.fSize]];
};
24 => {
[size, srcMin, dstMin] ¬ GetMinAndSize[r, b, b, NIL, rInfo.placement];
ImagerSample.BasicTransfer[maps[0].map, r,,, size];
ImagerSample.BasicTransfer[maps[1].map, g,,, size];
ImagerSample.BasicTransfer[maps[2].map, b,,, size];
affect.max ¬ SF.Max[affect.max, size];
};
ENDCASE;
};
ENDCASE => {
IO.PutF1[cmd.out, "bad file type: %g\n", IO.rope[fullFName]];
GOTO Bad;
};
msg ← CtFile.ViewFile[maps, fullFName, center.bool, noPlace.bool, x, y, w, h];
EXITS Bad => error ¬ " ";
};
IF cmap.ok
AND
NOT CtMap.Load[cmap.rope]
THEN RETURN[error: Rope.Concat["Can't load ", cmap.rope]];
IF clear.bool THEN CtBasic.FillMaps[maps, 0, 0, 0, 0];
name.rope ¬
FS.ExpandName[
IF Rope.Find[name.rope, "/"] = -1
AND Rope.Find[name.rope, ">"] = -1
THEN Rope.Concat[wDir, name.rope]
ELSE FileNames.ResolveRelativePath[name.rope]].fullFName;
IF Rope.Find[name.rope, "*"] = -1
THEN [] ¬ EachName[name.rope]
ELSE FS.EnumerateForNames[name.rope, EachName, wDir];
};
};
Dump File Conversion
ctDumpToAISUsage:
ROPE ~
"Ct DumpToAIS <dump file> <ais-file-root>
[-size <width height>] [-header] [-separate] [-ascii] [-gbr] [-mono]
Decode the named dump file into new AIS file(s) (one file if -mono, else three).
The dump file is assumed 8 (mono) or 24 (rgb) bits per pixel, with no header
If -separate, pixels are expected as all rrr.... then ggg.... then bbb....
else, pixels are expected as rgbrgbrgb....
If -header, then a header is expected, containing the number of channels
(typically 1 or 3), the width, and the height.
If -head is not given, then width and height must be specified.
If -ascii, the data is interpreted as ascii integers, rather than binary
If -gbr file contains green, then blue, then red order";
CtDumpToAIS: CtDispatch.CtProc ~ {
dump, aisName, w, h, hdr, ascii, sep, gbr, mono: Args.Arg;
[dump, aisName, w, h, hdr, ascii, sep, gbr, mono] ¬
Args.ArgsGet[cmd, "%ss-size%ii-header%b-ascii%b-separate%b-gbr%b-mono%b"
! Args.Error => {error ¬ reason; CONTINUE}];
IF hdr.ok AND (w.ok OR h.ok) THEN RETURN;
IF NOT hdr.ok AND (NOT w.ok OR NOT h.ok) THEN RETURN;
IF error #
NIL
THEN RETURN
ELSE {
ENABLE CtFile.Error => {error ¬ reason; GOTO Bad};
maps: SampleMaps;
IF hdr.ok
THEN maps ¬ CtFile.ReadDumpFile[dump.rope, NIL, ascii.ok, 0, 0, 0, NOT sep.ok]
ELSE {
nChans: INT ¬ IF mono.ok THEN 1 ELSE 3;
maps ¬ CtBasic.CreateMaps[8*nChans, 0, 0, w.int, h.int];
maps ¬ CtFile.ReadDumpFile[dump.rope, maps, ascii.ok, nChans, w.int, h.int, ~sep.ok];
};
IF gbr.ok
AND maps.bpp = 24
THEN {
r: SampleMap ¬ maps[2].map;
g: SampleMap ¬ maps[0].map;
b: SampleMap ¬ maps[1].map;
maps[0].map ¬ r;
maps[1].map ¬ g;
maps[2].map ¬ b;
};
CtFile.WriteMapsToAIS[maps, aisName.rope];
EXITS Bad => IO.PutRope[cmd.out, error];
};
};
Pseudo to 24bpp Conversion
ctPseudoTo24Usage:
ROPE ~
"Ct PseudoTo24 <AIS root name>
Convert the current 8 bpp display to 24 bpp AIS file triplet.";
CtPseudoTo24: CtDispatch.CtProc ~ {
ENABLE AISIO.Error => {error ¬ reason; GOTO Bad};
IF maps.bpp # 8
THEN RETURN[error: "this option only for 8 bpp displays"]
ELSE {
m: SampleMap ¬ ImagerSample.NewSampleMap[[[maps.y, maps.x], [maps.h, maps.w]], 8];
cmap: CtMap.Cmap ¬ CtMap.Read[];
exts: ARRAY [0..3) OF ROPE ¬ ["-red.ais", "-grn.ais", "-blu.ais"];
IO.PutFL[cmd.out, "saving [%g, %g] to [%g, %g] (xywh: %g %g %g %g)",
LIST[IO.int[maps.x], IO.int[maps.y], IO.int[maps.x], IO.int[maps.y], IO.int[maps.w], IO.int[maps.h], IO.int[maps.box.max.f], IO.int[maps.box.max.s]]];
FOR n:
NAT
IN [0..3)
DO
name: ROPE ¬ Rope.Concat[Args.GetRope[cmd], exts[n]];
ImagerSample.Transfer[m, maps[0].map];
CtMod.Indirect[m, CtMap.GetTable[cmap, n], maps.box];
IO.PutF1[cmd.out, " %g . . . ", IO.rope[name]];
AISIO.Write[FileNames.ResolveRelativePath[name], m];
ENDLOOP;
IO.PutRope[cmd.out, "done\n"];
};
EXITS Bad => RETURN;
};
Start Code
CtDispatch.RegisterCtOp["File IO:", NIL, NIL];
CtDispatch.RegisterCtOp["Save", CtSave, ctSaveUsage];
CtDispatch.RegisterCtOp["View", CtView, ctViewUsage];
CtDispatch.RegisterCtOp["DumpToAIS", CtDumpToAIS, ctDumpToAISUsage, FALSE];
CtDispatch.RegisterCtOp["PseudoTo24", CtPseudoTo24, ctPseudoTo24Usage];
END.
CtDispatch.RegisterCtOp["Dump", CtDump, ctDumpUsage];
ctDumpUsage:
ROPE ~
"Ct Dump <dump file> [-withHeader] [width] [height]
Display the named, dump file in a viewer.
If with header, the file must start with the following 16 bit numbers:
a magic number
# channels (1 or 3)
width
height
then followed by pixel data (for 24 bpp, r, g, b . . .)
If no header, file presumed 8 bpp and width, height needed.";
CtDump: CtDispatch.CtProc ~ {
widthA, heightA, nameA, withHeaderA: Args.Arg;
[widthA, heightA, nameA, withHeaderA] ¬ Args.ArgsGet[cmd, "[ii%s-withHeader%b"
! Args.Error => {msg ¬ reason; CONTINUE}];
IF msg # NIL THEN RETURN[$Failure, msg];
IF withHeaderA.bool
THEN [] ¬ CtFile.ReadDumpFile[nameA.rope, maps]
ELSE {
Action:
PROC [line: ImagerSample.SampleBuffer] ~
TRUSTED {
box: ImagerSample.Box ¬ ImagerSample.GetBox[maps[0].map];
w: INT ¬ box.max.f-box.min.f;
FOR y:
NAT
IN [0..heightA.int)
DO
Process.CheckForAbort[];
FOR x: NAT IN [0..widthA.int) DO line.samples[x] ¬ IO.GetByte[s]; ENDLOOP;
IF y
IN [box.min.s..box.max.s]
THEN
ImagerSample.PutSamples[maps[0].map, [y, box.min.f],, line, box.min.f, w];
ENDLOOP;
};
s: IO.STREAM ¬ FS.StreamOpen[nameA.rope ! FS.Error => GOTO Bad];
IF s = NIL THEN RETURN[$Failure, "Can't open file."];
ImagerSample.DoWithScratchSamples[widthA.int, Action];
EXITS Bad => RETURN[$Failure, "Can't open file."];