ColorizeViewPointGraphicsCommonImpl.mesa
Copyright Ó 1989 by Xerox Corporation. All rights reserved.
Eric Nickell, May 30, 1989 10:02:38 am PDT
Bob Coleman, July 19, 1990 3:51:50 pm PDT
DIRECTORY
ColorizeViewPoint, ColorizeViewPointBackdoor, ColorizeViewPointGraphicsCommon, ColorizeViewPointSweep,
Convert, Ieee, IO, IPMaster, IPScan, PBasics, Profiles, Rope, SymTab, Vector2;
ColorizeViewPointGraphicsCommonImpl: CEDAR PROGRAM
IMPORTS ColorizeViewPoint, ColorizeViewPointBackdoor, ColorizeViewPointSweep, Convert, IO, IPMaster, IPScan, PBasics, Profiles, Rope, SymTab
EXPORTS ColorizeViewPointGraphicsCommon
~ BEGIN
OPEN ColorizeViewPointGraphicsCommon;
Colorization: TYPE ~ ColorizeViewPointBackdoor.Colorization;
Profile: TYPE ~ Profiles.Profile;
ROPE: TYPE ~ Rope.ROPE;
SampledColorIPFragments: TYPE ~ ColorizeViewPointBackdoor.SampledColorIPFragments;
SampledColorTransformSet: TYPE ~ ColorizeViewPointSweep.SampledColorTransformSet;
ASSERTION: TYPE ~ BOOL [TRUE..TRUE];
Graphics Common (Basic Graphics/Pro Illustrator/BitMapper)
ColorizeGraphics: PUBLIC PROC [ip: ROPE, subs: SubsInfo, opList: LIST OF Op, getColor: GetColorProc, seqList: LIST OF Seq ← NIL, customOnly: BOOLFALSE] RETURNS [colorizedIP: ROPENIL] ~ {
PerColorSpecified: IPScan.ScanProc = {
[min: INT, max: INT, op: IPMaster.Op ← nil, seq: IPScan.Seq ← nil, num: INTEGER ← 0, punt: BOOLFALSE]
color, addendum: ROPENIL; -- addendum follows replacementColor to store it correctly
replacementColor: REF;
[newMin: min, newMax: max, color: color, addendum: addendum] ← getColor[ip, min, max]; --client defines the piece of the ip to be replaced and what addendum to add after the replacement
IF color=NIL THEN RETURN; --client returns NIL if ip segment unrecognized; leave alone
replacementColor ← GetReplacementColor[color: color, subs: subs, customOnly: customOnly];
IF ~customOnly AND replacementColor=NIL AND color.Find["\017\317\224\240\347"]#-1 --YES color-- THEN {ColorizeViewPointBackdoor.SetProfileBoolean[profile: subs.palette, key: "AmbushAllYESColors", val: FALSE]; ColorizeViewPointBackdoor.SetProfileBoolean [profile: subs.palette, key: "IP2", val: FALSE]}; --replacementColor=NIL means a YES color isn't getting changed, so don't remove YES operators and don't use 2.0 header
IF ~punt AND replacementColor#NIL THEN WITH replacementColor SELECT FROM
replacementColor: ROPE => { --Constant color
colorizedIP ← colorizedIP.Cat[ip.Substr[start: flushFrom, len: min-flushFrom], replacementColor, addendum]; --replace MakeSampledBlack section w/ replacementColor
flushFrom ← max; --reset marker to after original color & ISET section
};
replacementColor: REF SampledColorIPFragments => { --Sampled color (sweep)
replacement: ROPENARROW[sampledColors.Fetch[key: color].val];
ts: SampledColorTransformSet;
pos: INT;
SELECT TRUE FROM
replacement#NIL => { --Have already spatially resolved this color in ENDCASE below
colorizedIP ← colorizedIP.Cat[ip.Substr[start: flushFrom, len: min-flushFrom], replacement, addendum]; --replace MakeSampledBlack section w/ replacement
flushFrom ← max; --reset marker to after original color & ISET section
};
(([transformSet: ts, finalPos: pos] ← BuildTransformSet[ip: ip, pos: max, remove: replacementColor.removeDefiningObject]).transformSet = nullTransformSet) => SIGNAL ColorizeViewPoint.Warning[$MalformedDefiningObject, "Malformed Sweep defining object"]; --Failure (but on non-failures this branch causes the transformation vector set to be built before going to ENDCASE)
ENDCASE => { --build the transform for the pixel array
replacement ← Rope.Cat[replacementColor.beforeTransform, ColorizeViewPointSweep.ConstructSweepTransform[ts: ts, sc: replacementColor], replacementColor.afterTransform];
[] ← SymTab.Store[x: sampledColors, key: color, val: replacement];
colorizedIP ← colorizedIP.Cat[ip.Substr[start: flushFrom, len: min-flushFrom], replacement, addendum]; --replace original color and MaskRectangle section with a MakeSampledColor section. MaskRectangle is removed (if "removeDefiningObject" is TRUE) because it is not part of the image; it is only used to define sweep size and angle parms
flushFrom ← pos; --throws out the defining rectangle or triangle if "removeDefiningObject" is TRUE, otherwise (default) keeps it.
};
};
ENDCASE => ERROR;
};
sampledColors: SymTab.Ref ~ SymTab.Create[];
flushFrom: INT ← 0;
IPScan.ScanRope[ip: ip, ops: opList, seqs: seqList, action: PerColorSpecified];
colorizedIP ← colorizedIP.Concat[ip.Substr[start: flushFrom, len: ip.Size]];
};
GetReplacementColor: PROC [color: ROPE, subs: SubsInfo, customOnly: BOOLFALSE] RETURNS [ipFrag: REFNIL] ~ {--customOnly => only look in Custom palette for color
lookupColor: REF ← SymTab.Fetch[x: subs.subs, key: color].val; --first time, should be a Color like [25%AC, $CVPPattern]. After that, an already resolved REF
IF lookupColor#NIL THEN WITH lookupColor SELECT FROM
getVal: Color => { --not previously resolved
value: LIST OF ROPE;
levelsExceeded: BOOL;
[value, levelsExceeded] ← ColorizeViewPointBackdoor.GetRecursiveValue[key: getVal.value, palette: subs.palette, subpaletteList: subs.subpaletteList, mapData: subs.mapData, customOnly: customOnly];
SELECT TRUE FROM
value=NIL => {
[] ← SymTab.Store[x: subs.subs, key: color, val: NIL];--saves time next time
RETURN;
};
levelsExceeded => {
[] ← SymTab.Store[x: subs.subs, key: color, val: NIL];--saves time next time
SIGNAL ColorizeViewPoint.Warning[class: $MalformedPaletteEntry, explanation: IO.PutFR[format: "%g is part of a recursive color definition beyond allowable levels; ignoring it.", v1: [rope[color]]]];
RETURN;
};
ENDCASE => {
ipFrag ← ColorizeViewPointBackdoor.IPFragmentForColorDefinition[value, subs.palette];
[] ← SymTab.Store[x: subs.subs, key: color, val: ipFrag];--so already done next time
};
};
ENDCASE --already resolved before-- => RETURN [lookupColor];
};
nullTransformSet: SampledColorTransformSet ~ ColorizeViewPointSweep.nullTransformSet;
BuildTransformSet: PROC [ip: ROPE, pos: INT, remove: BOOL] RETURNS [transformSet: SampledColorTransformSet, finalPos: INT] ~ {
This procedure attempts to find an interpress fragment in one of the three forms given below starting at position pos in ip. This will be the fragment used to define a sweep. Acceptable forms include:
I: n1 n2 n3 n4 MASKRECTANGLE (rectangle) NOTE rectangles can't be produced by MAKEOUTLINE on four lines
II: n1 n2 MOVETO n3 n4 LINETO n5 n6 LINETO 1 MAKEOUTLINE MASKFILL (triangle)
III: n1 n2 MOVETO n3 n4 LINETO n5 n6 LINETO n1 n2 LINETO 1 MAKEOUTLINE MASKFILL (triangle)
RETURNS a valid SampledColorTransformSet from one of the above forms and a position. The position is (default) the same as coming in, or if "remove" is TRUE, points to after the encoded interpress for the SampledColorTransformSet. If it gets lost, returns [nullTransformSet, pos].
NOTE1: The following code takes the execution order guarantees of Cedar very seriously w.r.t. AND and OR.
NOTE2: Every reference to Token[] returns a value of TYPE IPMaster.Token[] to the caller, but also has the side effect of placing that value in the variable token. This is especially important in conjunction with NOTE 1.
Token: PROC RETURNS [IPMaster.Token] ~ {
[token: token, next: pos] ← IPMaster.GetToken[encoding: ip, start: pos];
Interpress represents some large numbers as sequenceIntegers; for convenience change them into type num before returning:
IF token.seq=sequenceInteger THEN {
token.type ← num;
token.num ← IPMaster.IntFromSequenceData[text: Rope.ToRefText[ip.Substr[pos, token.len]]]; --not bothering to reset token.seq and token.len
pos ← pos + token.len; --move pointer beyond sequence; for seqs, pos is not moved by IPMaster.GetToken
};
RETURN [token];
};
token: IPMaster.Token;
t: SampledColorTransformSet;
transformSet ← nullTransformSet; finalPos ← pos; --Return values for error
The following assumes the BasicGraphics/ProIllustrator coord system of origin at upper left, x right and y down
IF Token[].type=num THEN t.offset.x ← token.num ELSE RETURN; --n1
IF Token[].type=num THEN t.offset.y ← token.num ELSE RETURN; --n2
IF Token[].type=op AND token.op=setxy THEN {--occasionally an n1 n2 SETXY is stuck in the way (eg, in Bitmapper graphics frames)
IF Token[].type=num THEN t.offset.x ← token.num ELSE RETURN; --n1 again
IF Token[].type=num THEN t.offset.y ← token.num ELSE RETURN; --n2 again
[] ← Token[]; --advance token to look at next
};
SELECT TRUE FROM
token.type=num => {   --Case I : n3 found
t.width.x ← token.num; t.width.y ← 0;
IF Token[].type#num THEN RETURN; --looking for n4 else error
t.height.x ← 0; t.height.y ← token.num;
IF t.width.x<0 AND t.height.y<0 --typical BasicGraphics pattern starts the rectangle farthest from origin and subtracts the length and width; relocate the startpoint closest to origin and add length and width to match ProIllustrator-- THEN {
t.offset.x ← t.offset.x+t.width.x;
t.offset.y ← t.offset.y+t.height.y;
t.width.x ← ABS[t.width.x]; -- height and width positive
t.height.y ← ABS[t.height.y];
};
IF Token[].op#maskrectangle THEN RETURN; --end Case1
};
token.type=op AND token.op=moveto => { --Cases II and III - triangles
temp: IPMaster.Token;
IF Token[].type=num THEN t.width.x ← token.num - t.offset.x ELSE RETURN; --width will be the first leg of the triangle drawn
IF Token[].type=num THEN t.width.y ← token.num - t.offset.y ELSE RETURN;
IF Token[]--.type#op OR token--.op#lineto THEN RETURN;
IF Token[].type=num THEN t.height.x ← token.num - (t.offset.x+t.width.x) ELSE RETURN; --height will be the second leg of the triangle drawn
IF Token[].type=num THEN t.height.y ← token.num - (t.offset.y+t.width.y) ELSE RETURN;
IF Token[]--.type#op OR token--.op#lineto THEN RETURN;
IF (temp ← Token[]).type#num THEN RETURN; --Could be 1 or n1
SELECT Token[].type FROM
op => {    --Case II
IF temp.num#1 OR token.op#makeoutline THEN RETURN;
};
num => {    --Case III
IF t.offset#[x: temp.num, y: token.num] OR Token[]--.type#op OR token--.op#lineto OR Token[]--.type#num OR token--.num#1 OR Token[]--.type#op OR token--.op#makeoutline THEN RETURN;
};
ENDCASE => RETURN;
IF Token[]--.type#op OR token--.op#maskfill THEN RETURN; --end Cases II and III
};
ENDCASE => RETURN;
The only successful exit
transformSet ← t;
IF remove THEN finalPos ← pos; --ELSE finalpos doesn't change
};
InvalidGfxKey: PUBLIC ERROR ~ CODE; --Never seen outside
GfxPaletteFromProfile: PUBLIC PROC [profile: Profiles.Profile, seqAction: SeqAction, gfxTexture: GfxTextureProc, gfxGray: GfxGrayProc, transparent, opaque: BOOLTRUE] ~ {--looks up each pattern in profile, uses callbacks to construct its ip frag
PerKey: Profiles.EnumProc = {
[key: ROPE] RETURNS [quit: BOOLFALSE]
ENABLE ColorizeViewPoint.Error => { --Map the error to a warning and proceed
SIGNAL ColorizeViewPoint.Warning[class: class, explanation: explanation];
GOTO AbandonKey;
};
Action: SeqAction ~ {
seqAction[key: key, makeSampledBlack: makeSampledBlack, real: real, textured: textured, transparent: transparent]
};
IF Rope.Match[pattern: "ForceTo*", object: key, case: FALSE] THEN RETURN; --ignores entries like "ForceTo25%ABD: c1, c15" for color mapping
GfxSeqFromKey[
key: key,
gfxTexture: gfxTexture,
gfxGray: gfxGray,
seqAction: Action,
transparent: transparent,
opaque: opaque
! InvalidGfxKey => GOTO AbandonKey
];
EXITS AbandonKey => NULL;
};
Profiles.EnumerateKeys[profile: profile, pattern: "*%*", proc: PerKey]; --patterns have "%" (eg, "0%AE")
};
GfxSeqFromKey: PROC [key: ROPE, gfxTexture: GfxTextureProc, gfxGray: GfxGrayProc, seqAction: SeqAction, transparent, opaque: BOOLTRUE] ~ {
transparent => permit the transparent form of the MAKESAMPLEDBLACK
opaque => permit the opaque form of the MAKESAMPLEDBLACK
pattern: Pattern ← ALL[0];
setGrayRope: ROPENIL;
f: REAL ← 0;
textured: BOOLFALSE;  --TRUE => a texture has been included
text: Rope.Text ~ Rope.NewText[size: 64];
msbFirstPart, msbPixelPattern, msbThirdPart: ROPE; --msb=makesampledblack
{ --Set up the pattern for msbPixelPattern
s: IO.STREAM ~ IO.RIS[rope: key];
char: CHAR;
DO
IncludePattern: PROC [new: Pattern] ~ TRUSTED {
FOR k: NAT IN [0..16) DO
pattern[k] ← PBasics.BITOR[pattern[k], new[k]];
ENDLOOP;
};
KeyBreak: IO.BreakProc = {
[char: CHAR] RETURNS [IO.CharClass]
RETURN [SELECT char FROM IN ['A..'A+GfxTexture.LAST], 'O, 'T => break, IN ['0..'9], '. => other, ENDCASE => sepr --includes '%--];
};
token: ROPEIO.GetTokenRope[stream: s, breakProc: KeyBreak ! IO.EndOfStream => EXIT].token;
IF token.Size=1 AND (char ← token.Fetch) IN ['A..'Z] THEN {
SELECT char FROM
IN ['A..'A+GfxTexture.LAST] => {
IncludePattern[gfxTexture[char-'A]];
textured ← TRUE;
};
'O => transparent ← FALSE;
'T => opaque ← FALSE;
ENDCASE => SIGNAL ColorizeViewPoint.Warning[$MalformedPaletteEntry, IO.PutFR[format: "Character \"%g\" illegal in profile entry", v1: [character[char]]]];
}
ELSE {
ENABLE Convert.Error => {
ERROR ColorizeViewPoint.Error[$MalformedPaletteEntry, IO.PutFR[format: "Illegal key: \"%g\"", v1: [rope[key]]]];
};
f ← (MIN[100.0, MAX[0.0, Convert.RealFromRope[r: token]]])/100.0; --0.0 to 1.0; for tokens betwn 0.0 & 100.0
f ← MIN[1.0, MAX[0.0, Convert.RealFromRope[r: token]]]; --0.0 to 1.0; for tokens betwn 0.0 & 1.0
IncludePattern[gfxGray[f]];
};
ENDLOOP;
};
{
tI: NAT ← 0; --Index into the REF TEXT
FOR i: NAT IN [0..16) DO
CharPair: TYPE = MACHINE DEPENDENT RECORD [high, low: CHAR];
[high: text[tI], low: text[tI+1]] ← LOOPHOLE[pattern[i], CharPair];
text[tI+2] ← text[tI+3] ← '\000;
tI ← tI+4;
ENDLOOP;
};
msbFirstPart ← "\017\260\017\260\017\241\017\241\017\241\304\004\000\001\000\020\240\244\017F\240\243\240\245\017\240\017\241\240\242\240\245"; -- 16 16 1 1 1 n SCALE -90 ROTATE CONCAT 0 1 TRANSLATE CONCAT
msbPixelPattern ← Rope.Concat["\311D\000\001\000\020" --sequencePackedPixelVector, 68 bytes long, 1 bit/sample, 16 samples/scan line--, text];
msbThirdPart ← "\241\302\304\004\360 \000\036\304\004\017\340\000\036\240\246\017\244\222\240\245"; --MAKEPIXELARRAY n n SCALE2 4 IGET CONCAT
IF transparent THEN seqAction[key: key, makeSampledBlack: Rope.Cat[msbFirstPart, msbPixelPattern, msbThirdPart, "\017\241\241\252" --1 MAKESAMPLEDBLACK--], real: f, textured: textured, transparent: TRUE];
IF opaque THEN seqAction[key: key, makeSampledBlack: Rope.Cat[msbFirstPart, msbPixelPattern, msbThirdPart, "\017\240\241\252" --0 MAKESAMPLEDBLACK--], real: f, textured: textured, transparent: FALSE];
};
END.