ColorTrixFileImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Plass and Bloomenthal, December 4, 1986 11:54:00 am PST
DIRECTORY AIS, ColorDisplay, ColorTrixBasics, ColorTrixFile, Commander, FileNames, FS, Imager, ImagerBackdoor, ImagerColorOperator, ImagerPixelArray, ImagerPixelMap, ImagerSample, ImagerTransformation, Interpress, IO, PixelMapOps, Process, ProcessProps, RefText, Rope, Terminal, Vector2;
ColorTrixFileImpl: CEDAR PROGRAM
IMPORTS AIS, ColorDisplay, ColorTrixBasics, FileNames, FS, Imager, ImagerBackdoor, ImagerColorOperator, ImagerPixelArray, ImagerPixelMap, ImagerSample, ImagerTransformation, Interpress, IO, PixelMapOps, Process, ProcessProps, RefText, Rope
EXPORTS ColorTrixFile
~ BEGIN
ROPE:     TYPE ~ Rope.ROPE;
Context:    TYPE ~ Imager.Context;
FileType:    TYPE = ColorTrixFile.FileType;
PixelMap:    TYPE ~ ColorTrixBasics.PixelMap;
PixelMapMisc:  TYPE ~ ColorTrixBasics.PixelMapMisc;
DeviceRectangle: TYPE ~ ImagerPixelMap.DeviceRectangle;
SampleBuffer:  TYPE ~ ImagerSample.SampleBuffer;
UnsafeSamples:  TYPE ~ ImagerSample.UnsafeSamples;
File Write Operations
WritePmMiscToAIS: PUBLIC PROC [pmMisc: ColorTrixBasics.PixelMapMisc, name: Rope.ROPE]
~ {
RedAndGrnFromRG: PROC [rg: PixelMap] RETURNS [red, grn: PixelMap] ~ {
bufferSize: INTEGER ← rg.fMin+rg.fSize;
redLine: SampleBuffer ~ ImagerSample.ObtainScratchBuffer[1, bufferSize];
grnLine: SampleBuffer ~ ImagerSample.ObtainScratchBuffer[1, bufferSize];
rgLine: SampleBuffer ~ ImagerSample.ObtainScratchBuffer[1, bufferSize];
w: DeviceRectangle ← ImagerPixelMap.BoundedWindow[rg];
red ← ImagerPixelMap.Create[3, w];
grn ← ImagerPixelMap.Create[3, w];
FOR y: NAT IN [w.sMin..w.sMin+w.sSize) DO
Process.CheckForAbort[];
PixelMapOps.GetF[rg, y, w.fMin, rgLine, 0, 0, w.fSize];
FOR x: NAT IN [0..w.fSize) DO
redLine[x] ← rgLine[x]/256;
grnLine[x] ← rgLine[x] MOD 256;
ENDLOOP;
PixelMapOps.PutF[red, y, w.fMin, redLine, 0, 0, w.fSize, null, null];
PixelMapOps.PutF[grn, y, w.fMin, grnLine, 0, 0, w.fSize, null, null];
ENDLOOP;
ImagerSample.ReleaseScratchBuffer[redLine];
ImagerSample.ReleaseScratchBuffer[grnLine];
ImagerSample.ReleaseScratchBuffer[rgLine];
};
SELECT pmMisc.bpp FROM
8 => PixelMapOps.StoreAIS[name, [pmMisc.bw, FALSE, NIL]];
24 => {
r, g: PixelMap;
b: PixelMap ← pmMisc.b;
[r, g] ← RedAndGrnFromRG[pmMisc.rg];
PixelMapOps.StoreAIS[Rope.Concat[name, "-red.ais"], [r, FALSE, NIL]];
PixelMapOps.StoreAIS[Rope.Concat[name, "-grn.ais"], [g, FALSE, NIL]];
PixelMapOps.StoreAIS[Rope.Concat[name, "-blu.ais"], [b, FALSE, NIL]];
};
ENDCASE => NULL;
};
File Read Operations:
ReadFile: PUBLIC PROC [
baseName: ROPE,
aisX, aisY: CARDINAL ← 0,
aisW, aisH: CARDINALLAST[CARDINAL]]
RETURNS [pmMisc: PixelMapMisc] ~ {
r, g, b: ROPE;
type: FileType;
[type, r, g, b] ← Parse[FileNames.ResolveRelativePath[baseName]];
SELECT type FROM
bw => pmMisc ← Read8BitFile[r, aisX, aisY, aisW, aisH];
color => pmMisc ← Read24BitFile[r, g, b, aisX, aisY, aisW, aisH]
ENDCASE;
};
Read8BitFile: PUBLIC PROC [
name: ROPE,
aisX, aisY: CARDINAL ← 0,
aisW, aisH: CARDINALLAST[CARDINAL]]
RETURNS [pmMisc: PixelMapMisc] ~ {
ais: AIS.FRef ← AIS.OpenFile[name];
IF (pmMisc.bpp ← AIS.ReadRaster[ais].bitsPerPixel) = 8 THEN {
wRef: AIS.WRef ← AIS.OpenWindow[ais, aisY, aisY+aisH-1, aisX, aisX+aisW-1];
w: INTEGER ← pmMisc.w ← wRef.lastPixel-wRef.firstPixel+1;
h: INTEGER ← pmMisc.h ← wRef.lastScan-wRef.firstScan+1;
linePm: PixelMap ← ImagerPixelMap.Create[3, [0, 0, 1, w]];
lineBuf: AIS.Buffer ← [linePm.refRep.words, linePm.refRep.pointer];
pmMisc.bw ← ImagerPixelMap.Create[3, [0, 0, h, w]];
FOR i: INT IN [0..h) DO
TRUSTED {AIS.UnsafeReadLine[wRef, lineBuf, i]};
ImagerPixelMap.Transfer[pmMisc.bw, ImagerPixelMap.ShiftMap[linePm, i, 0]];
Process.CheckForAbort[];
ENDLOOP;
AIS.CloseWindow[wRef];
};
AIS.CloseFile[ais];
};
Read24BitFile: PUBLIC PROC [
rName, gName, bName: ROPE,
aisX, aisY: CARDINAL ← 0,
aisW, aisH: CARDINALLAST[CARDINAL]]
RETURNS [pmMisc: PixelMapMisc] ~ {
rAis: AIS.FRef ← AIS.OpenFile[rName];
gAis: AIS.FRef ← AIS.OpenFile[gName];
raster: AIS.Raster ← AIS.ReadRaster[rAis];
temp: PixelMapMisc ← Read8BitFile[bName, aisX, aisY, aisW, aisH];
IF temp.bpp = 8 AND raster.bitsPerPixel = 8 AND raster^ = AIS.ReadRaster[gAis]^ THEN {
rWRef: AIS.WRef ← AIS.OpenWindow[rAis, aisY, aisY+aisH-1, aisX, aisX+aisW-1];
gWRef: AIS.WRef ← AIS.OpenWindow[gAis, aisY, aisY+aisH-1, aisX, aisX+aisW-1];
w: INTEGER ← pmMisc.w ← rWRef.lastPixel-rWRef.firstPixel+1;
h: INTEGER ← pmMisc.h ← rWRef.lastScan-rWRef.firstScan+1;
rSampleBuffer: SampleBuffer ← ImagerSample.ObtainScratchBuffer[1, w];
gSampleBuffer: SampleBuffer ← ImagerSample.ObtainScratchBuffer[1, w];
rgSampleBuffer: SampleBuffer ← ImagerSample.ObtainScratchBuffer[1, w];
rPm: PixelMap ← ImagerPixelMap.Create[3, [0, 0, 1, w]];
gPm: PixelMap ← ImagerPixelMap.Create[3, [0, 0, 1, w]];
rBuf: AIS.Buffer ← [rPm.refRep.words, rPm.refRep.pointer];
gBuf: AIS.Buffer ← [gPm.refRep.words, gPm.refRep.pointer];
rgPm: PixelMap ← ImagerPixelMap.Create[4, [0, 0, 1, w]];
pmMisc.bpp ← 24;
pmMisc.b ← temp.bw;
pmMisc.rg ← ImagerPixelMap.Create[4, [0, 0, h, w]];
FOR i: INT IN [0..h) DO
TRUSTED {AIS.UnsafeReadLine[rWRef, rBuf, i]};
TRUSTED {AIS.UnsafeReadLine[gWRef, gBuf, i]};
PixelMapOps.GetF[rPm, 0, 0, rSampleBuffer, 0, 0, w];
PixelMapOps.GetF[gPm, 0, 0, gSampleBuffer, 0, 0, w];
TRUSTED {
rPtr: UnsafeSamples ~ ImagerSample.GetPointer[rSampleBuffer, 0, 0, w];
gPtr: UnsafeSamples ~ ImagerSample.GetPointer[gSampleBuffer, 0, 0, w];
rgPtr: UnsafeSamples ~ ImagerSample.GetPointer[rgSampleBuffer, 0, 0, w];
FOR j: INT IN [0..w) DO rgPtr[j] ← 256*rPtr[j]+gPtr[j]; ENDLOOP;
};
PixelMapOps.PutF[rgPm, 0, 0, rgSampleBuffer, 0, 0, w];
ImagerPixelMap.Transfer[pmMisc.rg, ImagerPixelMap.ShiftMap[rgPm, i, 0]];
Process.CheckForAbort[];
ENDLOOP;
AIS.CloseWindow[rWRef];
AIS.CloseWindow[gWRef];
};
AIS.CloseFile[rAis];
AIS.CloseFile[gAis];
};
Display Operations:
ViewFile: PUBLIC PROC [
cd: PixelMapMisc,
baseName: ROPE,
center: BOOLFALSE,
aisX, aisY: CARDINAL ← 0,
aisW, aisH: CARDINALLAST[CARDINAL]]
RETURNS [error: ROPENIL] ~ {
r, g, b: ROPE;
type: FileType;
[type, r, g, b] ←
Parse[FileNames.StripVersionNumber[FileNames.ResolveRelativePath[baseName]]];
error ← SELECT type FROM
ip => ViewIpFile[r],
bw => IF cd.bpp = 24
THEN View24BitFile[cd, r, r, r, center, aisX, aisY, aisW, aisH]
ELSE View8BitFile[cd.bw, r, center, aisX, aisY, aisW, aisH],
color => IF cd.bpp = 24
THEN View24BitFile[cd, r, g, b, center, aisX, aisY, aisW, aisH]
ELSE ViewColorFiles[r, g, b],
ENDCASE => Rope.Cat["Bad name: ", baseName];
};
View8BitFile: PUBLIC PROC [
cd: PixelMap,
name: ROPE,
center: BOOLFALSE,
aisX, aisY: CARDINAL ← 0,
aisW, aisH: CARDINALLAST[CARDINAL]]
RETURNS [error: ROPENIL] ~ {
err: ROPENIL;
ais: AIS.FRef;
ais ← AIS.OpenFile[name ! FS.Error => {err ← error.explanation; CONTINUE}];
IF err # NIL THEN {error ← err; RETURN};
IF AIS.ReadRaster[ais].bitsPerPixel = 8
THEN {
wRef: AIS.WRef ← AIS.OpenWindow[ais, aisY, aisY+aisH-1, aisX, aisX+aisW-1];
w: INTEGER ← wRef.lastPixel-wRef.firstPixel+1;
h: INTEGER ← wRef.lastScan-wRef.firstScan+1;
linePm: PixelMap ← ImagerPixelMap.Create[3, [0, 0, 1, w]];
lineBuf: AIS.Buffer ← [linePm.refRep.words, linePm.refRep.pointer];
IF center THEN cd ← ImagerPixelMap.ShiftMap[cd, (h-cd.sSize)/2, (w-cd.fSize)/2];
FOR i: INT IN [0..h) DO
TRUSTED {AIS.UnsafeReadLine[wRef, lineBuf, i]};
ImagerPixelMap.Transfer[cd, ImagerPixelMap.ShiftMap[linePm, cd.sMin+i, cd.fMin]];
Process.CheckForAbort[];
ENDLOOP;
AIS.CloseWindow[wRef];
}
ELSE error ← "Error in viewing file.";
AIS.CloseFile[ais];
};
View24BitFile: PUBLIC PROC [
cd: PixelMapMisc,
rName, gName, bName: ROPE,
center: BOOLFALSE,
aisX, aisY: CARDINAL ← 0,
aisW, aisH: CARDINALLAST[CARDINAL]]
RETURNS [error: ROPENIL] ~ {
For 512 by 480 image, Preview and ViewColorFiles[] take ~ 23 secs; this takes ~ 12 secs.
Can save ~ 0.5 secs if call View8BitFile for blue: slightly reduces jerkiness, but it's 2 pass.
rAis: AIS.FRef ← AIS.OpenFile[rName];
gAis: AIS.FRef ← AIS.OpenFile[gName];
bAis: AIS.FRef ← AIS.OpenFile[bName];
r: AIS.Raster ← AIS.ReadRaster[rAis];
IF r.bitsPerPixel = 8 AND r^ = AIS.ReadRaster[gAis]^ AND r^ = AIS.ReadRaster[gAis]^
THEN {
rWRef: AIS.WRef ← AIS.OpenWindow[rAis, aisY, aisY+aisH-1, aisX, aisX+aisW-1];
gWRef: AIS.WRef ← AIS.OpenWindow[gAis, aisY, aisY+aisH-1, aisX, aisX+aisW-1];
bWRef: AIS.WRef ← AIS.OpenWindow[bAis, aisY, aisY+aisH-1, aisX, aisX+aisW-1];
w: INTEGER ← rWRef.lastPixel-rWRef.firstPixel+1;
h: INTEGER ← rWRef.lastScan-rWRef.firstScan+1;
rSampleBuffer: SampleBuffer ← ImagerSample.ObtainScratchBuffer[1, w];
gSampleBuffer: SampleBuffer ← ImagerSample.ObtainScratchBuffer[1, w];
bSampleBuffer: SampleBuffer ← ImagerSample.ObtainScratchBuffer[1, w];
rgSampleBuffer: SampleBuffer ← ImagerSample.ObtainScratchBuffer[1, w];
rPm: PixelMap ← ImagerPixelMap.Create[3, [0, 0, 1, w]];
gPm: PixelMap ← ImagerPixelMap.Create[3, [0, 0, 1, w]];
bPm: PixelMap ← ImagerPixelMap.Create[3, [0, 0, 1, w]];
rgPm: PixelMap ← ImagerPixelMap.Create[4, [0, 0, 1, w]];
rBuf: AIS.Buffer ← [rPm.refRep.words, rPm.refRep.pointer];
gBuf: AIS.Buffer ← [gPm.refRep.words, gPm.refRep.pointer];
bBuf: AIS.Buffer ← [bPm.refRep.words, bPm.refRep.pointer];
IF center THEN {
cd.rg ← ImagerPixelMap.ShiftMap[cd.rg, (h-cd.rg.sSize)/2, (w-cd.rg.fSize)/2];
cd.b ← ImagerPixelMap.ShiftMap[cd.b, (h-cd.b.sSize)/2, (w-cd.b.fSize)/2];
};
FOR i: INT IN [0..h) DO
TRUSTED {AIS.UnsafeReadLine[rWRef, rBuf, i]};
TRUSTED {AIS.UnsafeReadLine[gWRef, gBuf, i]};
TRUSTED {AIS.UnsafeReadLine[bWRef, bBuf, i]};
PixelMapOps.GetF[rPm, 0, 0, rSampleBuffer, 0, 0, w];
PixelMapOps.GetF[gPm, 0, 0, gSampleBuffer, 0, 0, w];
PixelMapOps.GetF[bPm, 0, 0, bSampleBuffer, 0, 0, w];
TRUSTED {
rPtr: UnsafeSamples ~ ImagerSample.GetPointer[rSampleBuffer, 0, 0, w];
gPtr: UnsafeSamples ~ ImagerSample.GetPointer[gSampleBuffer, 0, 0, w];
rgPtr: UnsafeSamples ~ ImagerSample.GetPointer[rgSampleBuffer, 0, 0, w];
FOR j: INT IN [0..w) DO rgPtr[j] ← 256*rPtr[j]+gPtr[j]; ENDLOOP;
};
PixelMapOps.PutF[rgPm, 0, 0, rgSampleBuffer, 0, 0, w];
ImagerPixelMap.Transfer[
cd.rg,ImagerPixelMap.ShiftMap[rgPm, cd.rg.sMin+i, cd.rg.fMin]];
ImagerPixelMap.Transfer[
cd.b, ImagerPixelMap.ShiftMap[bPm, cd.b.sMin+i, cd.b.fMin]];
Process.CheckForAbort[];
ENDLOOP;
AIS.CloseWindow[rWRef];
AIS.CloseWindow[gWRef];
AIS.CloseWindow[bWRef];
}
ELSE error ← "Error in viewing file.";
AIS.CloseFile[rAis];
AIS.CloseFile[gAis];
AIS.CloseFile[bAis];
};
Display: PUBLIC PROC [
cd: Context, pa: ImagerPixelArray.PixelArray, op: ImagerColorOperator.ColorOperator]
~ {
r1: ImagerTransformation.Rectangle ← ViewRectangle[pa];
r2: ImagerTransformation.Rectangle ← ImagerBackdoor.GetBounds[cd];
Inner: PROC ~ {
x: REAL ~ (r2.w-r1.w)/2;
y: REAL ~ (r2.h-r1.h)/2;
Imager.TranslateT[cd, [x, y]];
Imager.SetSampledColor[cd, pa, ViewPort[], op];
Imager.MaskRectangle[cd, ViewRectangle[pa]];       
};
Process.CheckForAbort[];
Imager.DoSave[cd, Inner];
};
ViewRectangle: PROC [pa: ImagerPixelArray.PixelArray]
RETURNS [rect: ImagerTransformation.Rectangle] ~ {
rect ← ImagerTransformation.TransformRectangle[pa.m, [0, 0, pa.sSize, pa.fSize]];
};
ViewPort: PROC RETURNS [ImagerTransformation.Transformation] ~ {
RETURN[ImagerTransformation.Scale[1]];
};
ViewDitherFile: PUBLIC PROC [fileName: ROPE, cd: Context ← NIL, center, cmapInit: BOOL]
RETURNS [error: ROPENIL] ~ {
pa: ImagerPixelArray.PixelArray;
pa ← ImagerPixelArray.FromAIS[fileName !
FS.Error, ImagerPixelArray.Error => {error ← "Couldn't make dither array"; CONTINUE}];
IF error # NIL THEN RETURN;
IF cd = NIL THEN cd ← ColorTrixBasics.InitCd[gray, TRUE, TRUE, cmapInit];
Display[cd, pa, ImagerColorOperator.GrayLinearColorModel[255, 0]];
};
ViewGrayFile: PUBLIC PROC [fileName: ROPE, cd: Context ← NIL, center, cmapInit: BOOL]
RETURNS [error: ROPENIL] ~ {
pa: ImagerPixelArray.PixelArray;
pa ← ImagerPixelArray.FromAIS[fileName !
FS.Error, ImagerPixelArray.Error => {error ← "Couldn't make gray array"; CONTINUE}];
IF error # NIL THEN RETURN;
IF cd = NIL THEN cd ← ColorTrixBasics.InitCd[gray, TRUE, TRUE, cmapInit];
Display[cd, pa, ImagerColorOperator.GrayLinearColorModel[255, 0]];
};
ViewColorFiles: PUBLIC PROC [r, g, b: ROPE, cmapInit: BOOLTRUE]
RETURNS [error: ROPENIL] ~ {
pa: ImagerPixelArray.PixelArray;
pa ← ImagerPixelArray.Join3AIS[r, g, b !
FS.Error, ImagerPixelArray.Error => {error ← "Couldn't make color array"; CONTINUE}];
IF error = NIL THEN Display[ColorTrixBasics.InitCd[color, TRUE, TRUE, cmapInit], pa, ImagerColorOperator.RGBLinearColorModel[255]];
};
Interpress Operations
IpOpenFailed: ERROR = CODE;
LogError: PUBLIC Interpress.LogProc ~ {
WITH ProcessProps.GetProp[$CommanderHandle] SELECT FROM
cmd: Commander.Handle =>
IO.PutRope[cmd.err, Rope.Concat["\n Interpress error: ", explanation]];
ENDCASE => NULL;
IF class = 0 THEN ERROR IpOpenFailed;
};
OpenInterpress: PUBLIC PROC [name: ROPE] RETURNS [master: Interpress.Master] ~ {
master ← Interpress.Open[name, LogError ! IpOpenFailed => {master ← NIL; CONTINUE}];
};
ViewIpFile: PUBLIC PROC [name: ROPE, cmapInit: BOOLTRUE] RETURNS [error: ROPENIL]
~ {
type: ColorTrixBasics.CdType ~ IF
ColorDisplay.GetColorDisplayStatus[].gray OR
ColorDisplay.GetColorDisplayStatus[].bpp = 24 THEN smooth ELSE color;
cd: Context ~ ColorTrixBasics.InitCd[type, FALSE, FALSE, cmapInit];
master: Interpress.Master ~ OpenInterpress[name];
IF master = NIL THEN error ← "Nil interpress master";
IF error = NIL THEN FOR page: INT IN [1..master.pages] DO
Action: PROC ~ {Interpress.DoPage[master, page, cd, LogError]};
Process.CheckForAbort[];
Imager.SetColor[cd, Imager.black];
Imager.DoSaveAll[cd, Action];
ENDLOOP;
};
Parsing Procedures:
Parse: PUBLIC PROC [name: ROPE]
RETURNS [type: ColorTrixFile.FileType ← bad, name1, name2, name3: ROPENIL] ~ {
ok: BOOL;
ext: ROPE ← GetSuffix[name];
Test: PROC [x: ROPE] RETURNS [BOOL] ~ {RETURN[Rope.Equal[ext, x, FALSE]]};
IF (Test["ip"] OR Test["interpress"]) AND TestIpName[name] THEN RETURN[ip, name];
IF Test["ais"] AND FileExists[name] THEN RETURN [bw, name];
[ok, name1, name2, name3] ← GetColorNames[name]; IF ok THEN {type ← color; RETURN};
IF TestIpName[name.Cat["ip"]] THEN RETURN[ip, name.Cat["ip"],,];
IF TestIpName[name.Cat["interpress"]] THEN RETURN[ip, name.Cat["interpress"]];
IF ext = NIL AND FileExists[name] THEN RETURN [bw, name];
};
GetSuffix: PUBLIC PROC [rope: ROPE] RETURNS [ROPE] ~ {
n, len: INT;
n ← len ← rope.Length[]-1;
WHILE n >= 0 AND rope.Fetch[n] # '. DO n ← n-1; ENDLOOP;
RETURN[IF n = len OR n = -1 THEN NIL ELSE rope.Substr[n+1, len]];
};
TestIpName: PROC [name: ROPE] RETURNS [ok: BOOLTRUE] ~ {
s: FS.STREAM;
ipText: REF TEXT ~ "Interpress/";
text: REF TEXTNEW[TEXT[ipText.length]];
IF NOT FileExists[name] THEN RETURN[FALSE];
s ← FS.StreamOpen[name ! FS.Error => { ok ← FALSE; CONTINUE}];
IF NOT ok THEN RETURN;
[] ← IO.GetBlock[s, text];
ok ← RefText.Equal[ipText, text, FALSE];
IO.Close[s];
};
FileExists: PROC [name: ROPE] RETURNS [ok: BOOLTRUE] ~ {
[] ← FS.FileInfo[name ! FS.Error => {ok ← FALSE; CONTINUE}];
};
AisSuffix: PROC [base, suffix: ROPE] RETURNS [name: ROPE] ~ {
name ← IO.PutFR["%g-%g.ais", IO.rope[base], IO.rope[suffix]];
};
FileChoice: PROC [r: ROPE, a: ROPENIL, b: ROPENIL, c: ROPENIL]
RETURNS [result: ROPE] ~ {
IF FileExists[result ← AisSuffix[r, a]] THEN RETURN;
IF b = NIL THEN RETURN[NIL];
IF FileExists[result ← AisSuffix[r, b]] THEN RETURN;
IF c = NIL THEN RETURN[NIL];
IF NOT FileExists[result ← AisSuffix[r, c]] THEN RETURN[NIL];
};
GetColorNames: PROC [name: ROPE] RETURNS [ok: BOOL, red, grn, blu: ROPE] ~ {
red ← FileChoice[name, "red", "r"];
IF red = NIL THEN {ok ← FALSE; RETURN};
grn ← FileChoice[name, "grn", "green", "g"];
IF grn = NIL THEN {ok ← FALSE; RETURN};
blu ← FileChoice[name, "blu", "blue", "b"];
ok ← blu # NIL;
};
END.