CtModImpl.mesa
Copyright Ó 1985, 1992 by Xerox Corporation. All rights reserved.
Bloomenthal, April 24, 1993 10:59 am PDT
Glassner, November 14, 1990 4:34 pm PST
DIRECTORY Basics, CtBasic, CtMap, CtMod, G2dContour, G2dBasic, G2dMatrix, G2dScan, G2dVector, ImagerColor, ImagerPixel, ImagerSample, ImagerTransformation, IO, MessageWindow, Process, Real, Rope, SF;
CtModImpl: CEDAR PROGRAM
IMPORTS Basics, CtBasic, CtMap, G2dContour, G2dMatrix, G2dScan, G2dVector, ImagerPixel, ImagerSample, ImagerTransformation, Process, Real, SF
EXPORTS CtMod
~ BEGIN
Types and Constants
IntegerPair:  TYPE ~ CtBasic.IntegerPair;
PixelArray:   TYPE ~ CtBasic.PixelArray;
SampleMaps:  TYPE ~ CtBasic.SampleMaps;
Cmap:    TYPE ~ CtMap.Cmap;
Table:    TYPE ~ CtMap.Table;
CoonsControl:  TYPE ~ CtMod.CoonsControl;
TransformProc: TYPE ~ CtMod.TransformProc;
Pair:     TYPE ~ G2dBasic.Pair;
PairSequence:  TYPE ~ G2dBasic.PairSequence;
Matrix:    TYPE ~ G2dMatrix.Matrix;
PixelMap:   TYPE ~ ImagerPixel.PixelMap;
Sample:    TYPE ~ ImagerSample.Sample;
SampleMap:   TYPE ~ ImagerSample.SampleMap;
SampleBuffer:  TYPE ~ ImagerSample.SampleBuffer;
Box:     TYPE ~ ImagerSample.Box;
Vec:     TYPE ~ ImagerSample.Vec;
Transformation: TYPE ~ ImagerTransformation.Transformation;
ROPE:    TYPE ~ Rope.ROPE;
maxBox:    Box ~ SF.maxBox;
Error:    PUBLIC SIGNAL [reason: ROPE] = CODE;
Raster Transformations
TransformMap: PUBLIC PROC [map: SampleMap, transformation: Transformation] ~ {
Action: ImagerPixel.ResampleAction ~ {ImagerSample.PutSamples[map, min,, pixels[0]]};
box: Box ¬ ImagerSample.GetBox[map];
pm: ImagerPixel.PixelMap ¬ ImagerPixel.MakePixelMap[ImagerSample.Copy[map,, box]];
boxes: SF.BoxGenerator ~ {boxAction[box]};
ImagerPixel.Resample[pm, transformation, TRUE, boxes, box, Action];
};
TransformMaps: PUBLIC PROC [maps: SampleMaps, transformation: Transformation] ~ {
FOR i: NAT IN [0..maps.nChannels) DO TransformMap[maps[i].map, transformation]; ENDLOOP;
};
ArbTransformMap: PUBLIC PROC [map: SampleMap, proc: TransformProc] ~ {
Value: CtBasic.ValueProc ~ {
pair: IntegerPair ¬ proc[[x, y]];
pair ¬ [MIN[xMax, MAX[pa.x, pair.x]], MIN[yMax, MAX[pa.y, pair.y]]];
value ¬ pa[pair.y][pair.x];
};
pa: PixelArray ¬ CtBasic.PixelArrayFromSampleMap[map];
xMax: INTEGER ¬ pa.x+pa.w-1;
yMax: INTEGER ¬ pa.y+pa.h-1;
CtBasic.PutBWFrame[map, Value];
};
ArbTransformMaps: PUBLIC PROC [maps: SampleMaps, proc: TransformProc] ~ {
FOR i: NAT IN [0..maps.nChannels) DO ArbTransformMap[maps[i].map, proc]; ENDLOOP;
};
Palette Procedures
Palette: PUBLIC PROC [maps: SampleMaps, nRows: NAT ¬ 5, smooth: BOOL ¬ FALSE]
RETURNS [affectedRegion: Box]
~ {
FillBox: PROC [x, y, w, h, val: INTEGER] ~ {
affectedRegion ¬ [
[MIN[affectedRegion.min.s, y], MIN[affectedRegion.min.f, x]],
[MAX[affectedRegion.max.s, y+h], MAX[affectedRegion.max.f, x+w]]];
CtBasic.PutBWBox[maps[0].map, x, y, x+w, y+h, val];
};
hSpace: NAT ~ 2; -- horizontal
vSpace: NAT ~ 2; -- vertical
i:   NAT ¬ 0;
vSize:  NAT ¬ 7;
vTot:  NAT ¬ vSize+vSpace;
nRows2: NAT ¬ MAX[1, MIN[60/vTot, nRows]];
nCols:  NAT ¬ IF 255 MOD nRows2 = 0 THEN 255/nRows2 ELSE 255/nRows2+1;
hSize:  NAT ¬ MIN[14, (maps.size.f-2*20)/nCols-hSpace];
hTot:  NAT ¬ hSize+hSpace;
x:   NAT ¬ (maps.size.f-nCols*hTot)/2;
bot:  NAT ¬ maps.y+2*maps.h/3;
affectedRegion ¬ [[10000, 10000], [0, 0]];
IF smooth
THEN FOR xx: NAT IN [x..x+hTot*nCols) DO
val: CARDINAL ¬ Real.Round[255.0*REAL[xx-x]/REAL[hTot*nCols]];
FillBox[xx, bot, 1, vTot*nRows2, val];
ENDLOOP
ELSE FOR row: NAT IN [0..nRows2) DO
FOR col: NAT IN [0..nCols) DO
xx: NAT ¬ x+col*hTot;
yy: NAT ¬ bot+row*vTot;
FillBox[xx, yy, hSize+1, vSize, i];
IF (i ¬ i+1) = 256 THEN EXIT;
ENDLOOP;
ENDLOOP;
};
Color Map Indirection
Indirect: PUBLIC PROC [map: SampleMap, table: Table, box: Box] ~ {
Action: PROC [samples: ImagerSample.SampleBuffer] ~ TRUSTED {
FOR y: INT IN [box.min.s..box.max.s) DO
Process.CheckForAbort[];
ImagerSample.GetSamples[map, [y, box.min.f],, samples, 0, width];
FOR x: NAT IN [0..width) DO samples[x] ¬ table[samples[x]]; ENDLOOP;
ImagerSample.PutSamples[map, [y, box.min.f],, samples, 0, width];
ENDLOOP;
};
width: INTEGER ¬ SF.SizeF[box];
IF ImagerSample.GetBitsPerSample[map] = 8
THEN ImagerSample.DoWithScratchSamples[width, Action];
};
Lum24: PUBLIC PROC [maps: SampleMaps, cmap: Cmap, coeff: ImagerColor.RGB ¬ [.30, .59, .11]]
~ {
IF maps.bpp = 24 THEN {
MakeTable: PROC [cmap: Cmap, index: [0..3), coeff: REAL] RETURNS [tab: Table] ~ {
tab ¬ NEW[CtMap.TableRep];
FOR v: NAT IN [0..256) DO tab[v] ¬ Real.Floor[cmap[index][v]*coeff]; ENDLOOP;
};
bufs: ARRAY [0..4) OF ImagerSample.SampleBuffer;
yMax: NAT ¬ maps.box.max.s-1;
sc: INT ¬ 64;
Multiply by sc for higher precision:
rtab: Table ¬ MakeTable[cmap, 0, coeff.R*sc];
gtab: Table ¬ MakeTable[cmap, 1, coeff.G*sc];
btab: Table ¬ MakeTable[cmap, 2, coeff.B*sc];
FOR i: INT IN [0..4) DO bufs[i] ¬ ImagerSample.ObtainScratchSamples[maps.size.f]; ENDLOOP;
FOR y: INT IN [maps.box.min.s..maps.box.max.s) DO
Process.CheckForAbort[];
IF y MOD 10 = 0 OR y = yMax THEN
MessageWindow.Append[IO.PutFR["\t\tLine %g/%g", IO.int[y], IO.int[yMax]], TRUE];
FOR i: INT IN [0..3) DO
ImagerSample.GetSamples[maps[0].map, [y, maps.box.min.f],, bufs[i], 0, maps.size.f];
ENDLOOP;
FOR x: INT IN [0..maps.size.f) DO
bufs[3][x] ¬ (rtab[bufs[0][x]]+gtab[bufs[1][x]]+btab[bufs[2][x]]+sc/2)/sc; -- normalize
ENDLOOP;
FOR i: INT IN [0..3) DO
ImagerSample.PutSamples[maps[i].map, [y, maps.box.min.f],, bufs[3], 0, maps.size.f];
ENDLOOP;
ENDLOOP;
FOR i: INT IN [0..4) DO ImagerSample.ReleaseScratchSamples[bufs[i]]; ENDLOOP;
};
};
Color Display Modification Procedures
Dif: PUBLIC PROC [map: SampleMap, x0, y0, x1, y1, x2, y2, w, h: NAT] ~ {
src0: SampleMap ~ ImagerSample.Clip[map, [[y0, x0], [y0+h, x0+w]]];
src1: SampleMap ~ ImagerSample.Clip[map, [[y1, x1], [y1+h, x1+w]]];
dif: SampleMap ~ ImagerSample.Clip[map, [[y2, x2], [y2+h, x2+w]]];
box0: Box ¬ ImagerSample.GetBox[src0];
box1: Box ¬ ImagerSample.GetBox[src1];
boxd: Box ¬ ImagerSample.GetBox[dif];
FOR y: INT IN [0..h) DO
FOR x: INT IN [0..w) DO
src0Value: CARDINAL ~ CtBasic.GetBWPixel[src0, x+box0.min.f, y+box0.min.s];
src1Value: CARDINAL ~ CtBasic.GetBWPixel[src1, x+box1.min.f, y+box1.min.s];
CtBasic.PutBWPixel[dif, x+boxd.min.f, y+boxd.min.s, ABS[src0Value-src1Value]];
ENDLOOP;
ENDLOOP;
};
Up: PUBLIC PROC [map: SampleMap, shift: INTEGER] ~ {
size: ImagerSample.Vec ¬ ImagerSample.GetSize[map];
tmpSize: INTEGER ¬ ABS[shift MOD size.s];
moveSize: INTEGER ¬ size.s-tmpSize;
IF shift > 0
THEN {
tmp: SampleMap ¬ ImagerSample.Copy[map, [0, 0], [[0, 0], [tmpSize, size.f]]];
ImagerSample.Move[map, [0, 0], [tmpSize, 0], [moveSize, size.f]];
ImagerSample.BasicTransfer[map, tmp, [moveSize, 0], [0, 0], [tmpSize, size.f]];
}
ELSE {
tmp: SampleMap ¬ ImagerSample.Copy[map, [-moveSize, 0], [[0, 0], [tmpSize, size.f]]];
ImagerSample.Move[map, [tmpSize, 0], [0, 0], [moveSize, size.f]];
ImagerSample.BasicTransfer[map, tmp, [0, 0], [0, 0], [tmpSize, size.f]];
};
};
Left: PUBLIC PROC [map: SampleMap, shift: INTEGER] ~ {
size: ImagerSample.Vec ¬ ImagerSample.GetSize[map];
tmpSize: INTEGER ¬ ABS[shift MOD size.f];
moveSize: INTEGER ¬ size.f-tmpSize;
IF shift > 0
THEN {
tmp: SampleMap ¬ ImagerSample.Copy[map, [0, 0], [[0, 0], [size.s, tmpSize]]];
ImagerSample.Move[map, [0, 0], [0, tmpSize], [size.s, moveSize]];
ImagerSample.BasicTransfer[map, tmp, [0, moveSize], [0, 0], [size.s, tmpSize]];
}
ELSE {
tmp: SampleMap ¬ ImagerSample.Copy[map, [0, -moveSize], [[0, 0], [size.s, tmpSize]]];
ImagerSample.Move[map, [0, tmpSize], [0, 0], [size.s, moveSize]];
ImagerSample.BasicTransfer[map, tmp, [0, 0], [0, 0], [size.s, tmpSize]];
};
};
Negate: PUBLIC PROC [map: SampleMap] ~ {
ImagerSample.Transfer[map, map, , [null, complement]];
};
ReflectH: PUBLIC PROC [map: SampleMap] ~ {
box: Box ¬ ImagerSample.GetBox[map];
size: Vec ¬ ImagerSample.GetSize[map];
line1: ImagerSample.SampleBuffer ~ ImagerSample.ObtainScratchSamples[size.f];
line2: ImagerSample.SampleBuffer ~ ImagerSample.ObtainScratchSamples[size.f];
FOR dy: NAT IN [0..size.s/2) DO
y: NAT ¬ box.min.s+dy;
yy: NAT ¬ box.max.s-1-dy;
Process.CheckForAbort[];
ImagerSample.GetSamples[map, [y, box.min.f],, line1, 0, size.f];
ImagerSample.GetSamples[map, [yy, box.min.f],, line2, 0, size.f];
ImagerSample.PutSamples[map, [y, box.min.f],, line2, 0, size.f];
ImagerSample.PutSamples[map, [yy, box.min.f],, line1, 0, size.f];
ENDLOOP;
ImagerSample.ReleaseScratchSamples[line1];
ImagerSample.ReleaseScratchSamples[line2];
};
ReflectV: PUBLIC PROC [map: SampleMap] ~ {
Action: PROC [samples: ImagerSample.SampleBuffer] ~ TRUSTED {
xRight: NAT ¬ size.f-1;
FOR y: INT IN [box.min.s..box.max.s) DO
Process.CheckForAbort[];
ImagerSample.GetSamples[map, [y, box.min.f],, samples, 0, size.f];
FOR x: NAT IN [0..size.f/2) DO
t: CARDINAL ¬ samples[x];
xx: CARDINAL ¬ xRight-x;
samples[x] ¬ samples[xx];
samples[xx] ¬ t;
ENDLOOP;
ImagerSample.PutSamples[map, [y, box.min.f],, samples, 0, size.f];
ENDLOOP;
};
box: Box ¬ ImagerSample.GetBox[map];
size: Vec ¬ ImagerSample.GetSize[map];
ImagerSample.DoWithScratchSamples[size.f, Action];
};
MirrorH: PUBLIC PROC [map: SampleMap, topToBottom: BOOL ¬ TRUE] ~ {
Action: PROC [samples: ImagerSample.SampleBuffer] ~ TRUSTED {
y1, y2: NAT;
FOR dy: NAT IN [0..size.s/2) DO
Process.CheckForAbort[];
IF topToBottom
THEN {y1 ¬ box.min.s+dy; y2 ¬ box.max.s-dy-1}
ELSE {y1 ¬ box.max.s-dy-1; y2 ¬ box.min.s+dy};
ImagerSample.GetSamples[map, [y1, box.min.f], , samples, 0, size.f];
ImagerSample.PutSamples[map, [y2, box.min.f], , samples, 0, size.f];
ENDLOOP;
};
box: Box ¬ ImagerSample.GetBox[map];
size: Vec ¬ ImagerSample.GetSize[map];
ImagerSample.DoWithScratchSamples[size.f, Action];
};
MirrorV: PUBLIC PROC [map: SampleMap, leftToRight: BOOL ¬ TRUE] ~ {
Action: PROC [samples: ImagerSample.SampleBuffer] ~ TRUSTED {
xRight: NAT ¬ size.f-1;
FOR y: INT IN [box.min.s..box.max.s) DO
Process.CheckForAbort[];
ImagerSample.GetSamples[map, [y, box.min.f],, samples, 0, size.f];
IF leftToRight
THEN FOR x: NAT IN [0..size.f/2) DO samples[xRight-x] ¬ samples[x]; ENDLOOP
ELSE FOR x: NAT IN [0..size.f/2) DO samples[x] ¬ samples[xRight-x]; ENDLOOP;
ImagerSample.PutSamples[map, [y, box.min.f],, samples, 0, size.f];
ENDLOOP;
};
box: Box ¬ ImagerSample.GetBox[map];
size: Vec ¬ ImagerSample.GetSize[map];
ImagerSample.DoWithScratchSamples[size.f, Action];
};
Transpose: PUBLIC PROC [map: SampleMap] ~ {
pa: PixelArray ¬ CtBasic.PixelArrayFromSampleMap[map];
FOR i: NAT IN [0..MIN[pa.w, pa.h]) DO
x: NAT ¬ pa.x+i;
Process.CheckForAbort[];
FOR y: NAT IN [pa.y..pa.y+i) DO
temp: WORD ¬ pa[y][x];
pa[y][x] ¬ pa[x][y];
pa[x][y] ¬ temp;
ENDLOOP;
ENDLOOP;
CtBasic.TransferPixelArrayToMap[pa, map];
};
Rotate: PUBLIC PROC [map: SampleMap, degrees: REAL] ~ {
box: Box ¬ ImagerSample.GetBox[map];
w: INTEGER ¬ SF.SizeF[box];
h: INTEGER ¬ SF.SizeS[box];
TransformMap[map, ImagerTransformation.Cat[
ImagerTransformation.Translate[[-w/2, -h/2]],
ImagerTransformation.Rotate[degrees],
ImagerTransformation.Translate[[w/2, h/2]]]];
};
Rotate: PUBLIC PROC [map: SampleMap, ccw: BOOL] ~ {
Rotate sample map by 90 degrees
pa: PixelArray ← CtBasic.PixelArrayFromSampleMap[map];
s: NATMIN[pa.w, pa.h];
d: NAT ← s-1; 
FOR i: NAT IN [0..s/2) DO     -- vertical loop
xi, xij:  NAT ← pa.x+i;
yi, yij: NAT ← pa.y+i;
xdi, xdij: NAT ← pa.x+d-i;
ydi, ydij: NAT ← pa.y+d-i;
Process.CheckForAbort[];
FOR j: NAT IN [0..d-i-i) DO     -- horizontal loop
IF ccw
THEN {
temp: WORD ← pa[yi][xij];
pa[yi][xij] ← pa[yij][xdi]; -- upper left ← upper right
pa[yij][xdi] ← pa[ydi][xdij]; -- upper right ← lower right
pa[ydi][xdij] ← pa[ydij][xi]; -- lower right ← lower left
pa[ydij][xi] ← temp;   -- lower left ← upper left
}
ELSE {
temp: WORD ← pa[ydij][xi];
pa[ydij][xi] ← pa[ydi][xdij]; -- lower right ← lower left
pa[ydi][xdij] ← pa[yij][xdi]; -- upper right ← lower right
pa[yij][xdi] ← pa[yi][xij]; -- upper left ← upper right
pa[yi][xij] ← temp;
};
xij  ← xij+1;
yij ← yij+1;
xdij ← xdij-1;
ydij ← ydij-1;
ENDLOOP;
ENDLOOP;
CtBasic.TransferPixelArrayToMap[pa, map];
};
Add: PUBLIC PROC [dst, map: SampleMap, fbAlpha, fileAlpha: REAL] ~ {
box: Box ¬ SF.Intersect[ImagerSample.GetBox[dst], ImagerSample.GetBox[map]];
width: NAT ¬ box.max.f-box.min.f;
dstBuf: SampleBuffer ¬ ImagerSample.ObtainScratchSamples[width];
mapBuf: SampleBuffer ¬ ImagerSample.ObtainScratchSamples[width];
SELECT ImagerSample.GetBitsPerSample[map] FROM
8 =>
FOR y: NAT IN [box.min.s..box.max.s) DO
Process.CheckForAbort[];
ImagerSample.GetSamples[map, [y, box.min.f],, mapBuf, 0, width];
ImagerSample.GetSamples[dst, [y, box.min.f],, dstBuf, 0, width];
FOR x: NAT IN [0..width) DO
v: REAL ¬ REAL[mapBuf[x]];
pv: INTEGER ¬ Real.Round[(v*fbAlpha)+(fileAlpha*dstBuf[x])];
dstBuf[x] ¬ MIN[255, MAX[0, pv]];
ENDLOOP;
ImagerSample.PutSamples[dst, [y, box.min.f],, dstBuf, 0, width];
ENDLOOP;
16 =>
FOR y: NAT IN [box.min.s..box.max.s) DO
Process.CheckForAbort[];
ImagerSample.GetSamples[map, [y, box.min.f],, mapBuf, 0, width];
ImagerSample.GetSamples[dst, [y, box.min.f],, dstBuf, 0, width];
FOR x: NAT IN [0..width) DO
rgDst: CARDINAL ¬ dstBuf[x];
rgMap: CARDINAL ¬ mapBuf[x];
rDst: REAL ¬ REAL[rgDst/256];
rMap: REAL ¬ REAL[rgMap/256];
gDst: REAL ¬ REAL[rgDst MOD 256];
gMap: REAL ¬ REAL[rgMap MOD 256];
rv: INTEGER ¬ Real.Round[(rMap*fbAlpha)+(fileAlpha*rDst)];
gv: INTEGER ¬ Real.Round[(gMap*fbAlpha)+(fileAlpha*gDst)];
r: CARDINAL¬ MIN[255, MAX[0, rv]];
g: CARDINAL ¬ MIN[255, MAX[0, gv]];
dstBuf[x] ¬ 256*r+g;
ENDLOOP;
ImagerSample.PutSamples[dst, [y, box.min.f],, dstBuf, 0, width];
ENDLOOP;
ENDCASE;
ImagerSample.ReleaseScratchSamples[dstBuf];
ImagerSample.ReleaseScratchSamples[mapBuf];
};
Lerp: PUBLIC PROC [dst, map: SampleMap, alpha: REAL] ~ {
box: Box ¬ SF.Intersect[ImagerSample.GetBox[dst], ImagerSample.GetBox[map]];
width: NAT ¬ box.max.f-box.min.f;
dstBuf: SampleBuffer ¬ ImagerSample.ObtainScratchSamples[width];
mapBuf: SampleBuffer ¬ ImagerSample.ObtainScratchSamples[width];
SELECT ImagerSample.GetBitsPerSample[map] FROM
8 =>
FOR y: NAT IN [box.min.s..box.max.s) DO
Process.CheckForAbort[];
ImagerSample.GetSamples[map, [y, box.min.f],, mapBuf, 0, width];
ImagerSample.GetSamples[dst, [y, box.min.f],, dstBuf, 0, width];
FOR x: NAT IN [0..width) DO
v: REAL ¬ REAL[mapBuf[x]];
dstBuf[x] ¬ Real.Round[v+alpha*(REAL[dstBuf[x]]-v)];
ENDLOOP;
ImagerSample.PutSamples[dst, [y, box.min.f],, dstBuf, 0, width];
ENDLOOP;
16 =>
FOR y: NAT IN [box.min.s..box.max.s) DO
Process.CheckForAbort[];
ImagerSample.GetSamples[map, [y, box.min.f],, mapBuf, 0, width];
ImagerSample.GetSamples[dst, [y, box.min.f],, dstBuf, 0, width];
FOR x: NAT IN [0..width) DO
rgDst: CARDINAL ¬ dstBuf[x];
rgMap: CARDINAL ¬ mapBuf[x];
rDst: REAL ¬ REAL[rgDst/256];
rMap: REAL ¬ REAL[rgMap/256];
gDst: REAL ¬ REAL[rgDst MOD 256];
gMap: REAL ¬ REAL[rgMap MOD 256];
r: CARDINAL ¬ Real.Round[rMap+alpha*(rDst-rMap)];
g: CARDINAL ¬ Real.Round[gMap+alpha*(gDst-gMap)];
dstBuf[x] ¬ 256*r+g;
ENDLOOP;
ImagerSample.PutSamples[dst, [y, box.min.f],, dstBuf, 0, width];
ENDLOOP;
ENDCASE;
ImagerSample.ReleaseScratchSamples[dstBuf];
ImagerSample.ReleaseScratchSamples[mapBuf];
};
PVal: PUBLIC PROC [map: SampleMap, new: NAT, old: LIST OF NAT] ~ {
table: Table ¬ NEW[CtMap.TableRep];
IF old = NIL THEN RETURN;
FOR i: NAT IN [0..256) DO table[i] ¬ i; ENDLOOP;
new ¬ MIN[255, MAX[0, INTEGER[new]]];
FOR o: LIST OF NAT ¬ old, o.rest WHILE o # NIL DO
table[MIN[255, MAX[0, INTEGER[o.first]]]] ¬ new;
ENDLOOP;
Indirect[map, table, ImagerSample.GetBox[map]];
};
Gammatize: PUBLIC PROC [maps: SampleMaps, gamma: REAL, x, y, w, h: NAT] ~ {
box: Box ¬ CtBasic.BoxFromXYWH[x, y, w, h];
table: Table ¬ CtMap.GetGammaTable[gamma];
FOR n: NAT IN [0..maps.nChannels) DO Indirect[maps[n].map, table, box]; ENDLOOP;
};
Warping
PerspWarp: PUBLIC PROC [
texture:  PixelArray ¬ NIL,
dest:   SampleMaps,
smooth:  BOOL ¬ FALSE,
points:  ARRAY [0..4) OF IntegerPair]
RETURNS [transform: Matrix]
~ {
PixelProc: G2dScan.PixelProc ~ {
t: G2dBasic.Triple ¬ G2dMatrix.Transform[[x, y, 1.0], transform];
tx: REAL ¬ t.x/t.z;
ty: REAL ¬ t.y/t.z;
ix: NAT ¬ Real.Floor[tx];
iy: NAT ¬ Real.Floor[ty];
IF ix IN [texture.x..xmax] AND iy IN [texture.y..ymax] THEN
CtBasic.PutBWPixel[dest[0].map, x, y, IF smooth
THEN BiLerp[texture, tx, ty]
ELSE texture[iy][ix]];
};
GetPair: PROC [ip: IntegerPair] RETURNS [Pair] ~ {RETURN[[REAL[ip.x], REAL[ip.y]]]};
xmax: INTEGER ¬ texture.x+texture.w-1;
ymax: INTEGER ¬ texture.y+texture.h-1;
transform ¬ G2dMatrix.QuadrilateralToRectangle[
GetPair[points[0]], GetPair[points[1]], GetPair[points[2]], GetPair[points[3]],
[texture.x, texture.y, texture.w, texture.h]];
G2dScan.ScanTriangle[points[0], points[1], points[2], PixelProc];
G2dScan.ScanTriangle[points[0], points[2], points[3], PixelProc];
};
CoonsWarp: PUBLIC PROC [
pa1, pa2, pa3: PixelArray,     -- source image (pa1 for bw, or pa1,2,3 for rgb)
curves: ARRAY [0..4) OF PairSequence, -- outline curves
dest: SampleMaps,       -- destination window
control: REF CoonsControl ¬ NIL]
~ {
By Paul Heckbert.
See nonoptimized, more readable code at end of file.
CoonsWarpInner: PROC ~ {
Highly optimized version (incremental and INT32)
640x480 timing tests:
b&w without pointers: 22 sec pointsamp, 80 sec filtered
b&w with pointers: 16 sec pointsamp, 77 sec filtered
rgb with pointers: 32 sec pointsamp, 304 sec filtered
rgb: BOOL ¬ pa2 # NIL AND pa3 # NIL;          -- rgb or b&w?
line1: SampleBuffer ¬ ImagerSample.ObtainScratchSamples[du+1];  -- bw or r
line2: SampleBuffer ¬ ImagerSample.ObtainScratchSamples[du+1];  -- g or unused
line3: SampleBuffer ¬ ImagerSample.ObtainScratchSamples[du+1];  -- b or unused
Resample the outline curves with equal spacing at dest resolution
qu0: PairSequence ¬ G2dContour.ResamplePairs[curves[0], du+1];
q1v: PairSequence ¬ G2dContour.ResamplePairs[curves[1], dv+1];
qu1: PairSequence ¬ G2dContour.ResamplePairs[curves[2], du+1];
q0v: PairSequence ¬ G2dContour.ResamplePairs[curves[3], dv+1];
pu0: Int32PairSequence ¬ NEW[Int32PairSequenceRep[du+1]];
pu1: Int32PairSequence ¬ NEW[Int32PairSequenceRep[du+1]];
p0v: Int32PairSequence ¬ NEW[Int32PairSequenceRep[dv+1]];
p1v: Int32PairSequence ¬ NEW[Int32PairSequenceRep[dv+1]];
buv: Int32PairSequence ¬ NEW[Int32PairSequenceRep[du+1]];
dbuv: Int32PairSequence ¬ NEW[Int32PairSequenceRep[du+1]];
c00, c10, c11, c01, c0v, c1v, dc0v, dc1v: Int32Pair;
shift: NAT ~ 20;          -- number of fractional bits
scale: REAL ~ LOOPHOLE[Basics.BITLSHIFT[LOOPHOLE[INT32[1]], shift], INT32];
recipscale: REAL ~ 1./scale;
Reverse the last two curves
[] ¬ G2dVector.ReverseSequence[qu1, qu1];
[] ¬ G2dVector.ReverseSequence[q0v, q0v];
Quantize the real curves to integer curves
FOR u: NAT IN [0..du] DO
pu0[u] ¬ Int32ize[qu0[u], scale];
pu1[u] ¬ Int32ize[qu1[u], scale];
ENDLOOP;
FOR v: NAT IN [0..dv] DO
p0v[v] ¬ Int32ize[q0v[v], scale];
p1v[v] ¬ Int32ize[q1v[v], scale];
ENDLOOP;
Infer the four corner points (somewhat arbitrary since curves don't necessarily join)
c00 ¬ Int32PairMidpoint[pu0[0], p0v[0]];
c10 ¬ Int32PairMidpoint[pu0[du], p1v[0]];
c11 ¬ Int32PairMidpoint[pu1[du], p1v[dv]];
c01 ¬ Int32PairMidpoint[pu1[0], p0v[dv]];
Setup for incremental inner loops
c0v ¬ c00; dc0v ¬ [(c01.x-c00.x)/dv, (c01.y-c00.y)/dv];
c1v ¬ c10; dc1v ¬ [(c11.x-c10.x)/dv, (c11.y-c10.y)/dv];
FOR u: NAT IN [0..du] DO
buv[u] ¬ pu0[u];
dbuv[u] ¬ [(pu1[u].x-pu0[u].x)/dv, (pu1[u].y-pu0[u].y)/dv];
ENDLOOP;
buv.length ¬ dbuv.length ¬ du+1;
Warp!
FOR v: NAT IN [0..dv] WHILE NOT control.abort DO
TRUSTED {
auv: Int32Pair ¬ [p0v[v].x-c0v.x, p0v[v].y-c0v.y];
dauv: Int32Pair ¬ [(p1v[v].x-c1v.x-auv.x)/du, (p1v[v].y-c1v.y-auv.y)/du];
buvp: LONG POINTER TO Int32Pair ¬ @buv[0];
dbuvp: LONG POINTER TO Int32Pair ¬ @dbuv[0];
lp1: LONG POINTER TO Sample ¬ @line1[0];
lp2: LONG POINTER TO Sample ¬ @line2[0];
lp3: LONG POINTER TO Sample ¬ @line3[0];
IF control.filter = point
THEN             -- point sample
FOR u: NAT IN [0..du] DO
x: INT32 ¬
LOOPHOLE[Basics.BITRSHIFT[LOOPHOLE[auv.x+buvp.x], shift]];
y: INT32 ¬
LOOPHOLE[Basics.BITRSHIFT[LOOPHOLE[auv.y+buvp.y], shift]];
IF x >= 0 AND x < dx AND y >= 0 AND y < dy
THEN {
lp1­ ¬ pa1[y][x];
IF rgb THEN {lp2­ ¬ pa2[y][x]; lp3­ ¬ pa3[y][x]};
}
ELSE {
lp1­ ¬ 0;
IF rgb THEN {lp2­ ¬ 0; lp3­ ¬ 0};
};
auv ¬ [auv.x+dauv.x, auv.y+dauv.y];
buvp­ ¬ [buvp.x+dbuvp.x, buvp.y+dbuvp.y];
buvp ¬ buvp+SIZE[Int32Pair];
dbuvp ¬ dbuvp+SIZE[Int32Pair];
lp1 ¬ lp1+SIZE[Sample];
IF rgb THEN {lp2 ¬ lp2+SIZE[Sample]; lp3 ¬ lp3+SIZE[Sample]};
ENDLOOP
ELSE             -- bilinear interp
FOR u: NAT IN [0..du] DO
x: REAL ¬ (auv.x+buvp.x)*recipscale;
y: REAL ¬ (auv.y+buvp.y)*recipscale;
IF x >= 0 AND x < dx AND y >= 0 AND y < dy
THEN {
lp1­ ¬ BiLerp[pa1, x, y];
IF rgb THEN {lp2­ ¬ BiLerp[pa2, x, y]; lp3­ ¬ BiLerp[pa3, x, y]};
}
ELSE {
lp1­ ¬ 0;
IF rgb THEN {lp2­ ¬ 0; lp3­ ¬ 0};
};
auv ¬ [auv.x+dauv.x, auv.y+dauv.y];
buvp­ ¬ [buvp.x+dbuvp.x, buvp.y+dbuvp.y];
buvp ¬ buvp+SIZE[Int32Pair];
dbuvp ¬ dbuvp+SIZE[Int32Pair];
lp1 ¬ lp1+SIZE[Sample];
IF rgb THEN {lp2 ¬ lp2+SIZE[Sample]; lp3 ¬ lp3+SIZE[Sample]};
ENDLOOP;
ImagerSample.PutSamples[dest[0].map, [v, u0],, line1, 0, du+1];
IF rgb THEN {
ImagerSample.PutSamples[dest[1].map, [v, u0],, line2, 0, du+1];
ImagerSample.PutSamples[dest[2].map, [v, u0],, line3, 0, du+1];
};
c0v ¬ [c0v.x+dc0v.x, c0v.y+dc0v.y];
c1v ¬ [c1v.x+dc1v.x, c1v.y+dc1v.y];
};
ENDLOOP;
ImagerSample.ReleaseScratchSamples[line1];
ImagerSample.ReleaseScratchSamples[line2];
};
dx: NAT ¬ pa1.w;    -- source window size
dy: NAT ¬ pa1.h;
du: NAT ¬ dest.size.f-1;  -- dest window size
dv: NAT ¬ dest.size.s-1;
u0: NAT ¬ dest.box.min.f;  -- noninclusive window: [u0..u1) x [v0..v1)
v0: NAT ¬ dest.box.min.s;
u1: NAT ¬ u0+du+1;
v1: NAT ¬ v0+dv+1;
IF control = NIL THEN control ¬ NEW[CoonsControl];
Exclude cursor from dest window while drawing
Terminal.ModifyColorFrame[Terminal.Current[], CoonsWarpInner, u0, v0, u1, v1];
};
Pixel Procedures
BiLerp: PUBLIC PROC [pa: PixelArray, x, y: REAL] RETURNS [CARDINAL] ~ {
ix0: INT16 ¬ Real.Floor[x];
iy0: INT16 ¬ Real.Floor[y];
ix1: INT16 ¬ ix0+1;
iy1: INT16 ¬ iy0+1;
dx: REAL ¬ x-ix0; 
t00, t10: CARD;
IF ix0 < pa.x THEN ix0 ¬ pa.x;
IF iy0 < pa.y THEN iy0 ¬ pa.y;
IF ix1 >= INT16[pa.x+pa.w] THEN ix1 ¬ pa.x+pa.w-1;
IF iy1 >= INT16[pa.y+pa.h] THEN iy1 ¬ pa.y+pa.h-1;
t00 ¬ pa[iy0][ix0];
t10 ¬ pa[iy1][ix0];
SELECT pa.bpp FROM
8 => RETURN[Real.Floor[BiLerpReal[pa, x, y]]];
16 => {
t01: CARD ¬ pa[iy0][ix1];
t11: CARD ¬ pa[iy1][ix1];
u00: INTEGER ¬ t00/256;       -- high byte (red)
u10: INTEGER ¬ t10/256;
u0x: REAL ¬ u00+dx*(t01/256-u00);
u1x: REAL ¬ u10+dx*(t11/256-u10);
tyxr: CARD ¬ Real.Round[u0x+(y-iy0)*(u1x-u0x)];
tyxg: CARD;
u00 ¬ t00 MOD 256;         -- low byte (green)
u10 ¬ t10 MOD 256;
u0x ¬ u00+dx*(t01 MOD 256-u00);
u1x ¬ u10+dx*(t11 MOD 256-u10);
tyxg ¬ Real.Round[u0x+(y-iy0)*(u1x-u0x)];
RETURN[tyxr*256+tyxg];
};
ENDCASE => RETURN[0];
};
BiLerpReal: PUBLIC PROC [pa: PixelArray, x, y: REAL] RETURNS [REAL] ~ {
ix0: INT16 ¬ Real.Floor[x];
iy0: INT16 ¬ Real.Floor[y];
ix1: INT16 ¬ ix0+1;
iy1: INT16 ¬ iy0+1;
dx: REAL ¬ x-ix0; 
t00, t10: CARD;
IF ix0 < pa.x THEN ix0 ¬ pa.x;
IF iy0 < pa.y THEN iy0 ¬ pa.y;
IF ix1 >= INT16[pa.x+pa.w] THEN ix1 ¬ pa.x+pa.w-1;
IF iy1 >= INT16[pa.y+pa.h] THEN iy1 ¬ pa.y+pa.h-1;
t00 ¬ pa[iy0][ix0];
t10 ¬ pa[iy1][ix0];
SELECT pa.bpp FROM
8 => {
t0x: REAL ¬ t00+dx*(REAL[pa[iy0][ix1]]-t00);
t1x: REAL ¬ t10+dx*(REAL[pa[iy1][ix1]]-t10);
RETURN[t0x+(y-iy0)*(t1x-t0x)];
};
ENDCASE => ERROR;
};
Support Code
Int16Pair:     TYPE ~ G2dBasic.IntPair;
Int16PairSequence:  TYPE ~ G2dBasic.IntPairSequence;
Int16PairMidpoint: PROC [v1, v2: Int16Pair] RETURNS [Int16Pair] ~ {
RETURN [[Basics.BITSHIFT[v1.x+v2.x, -1], Basics.BITSHIFT[v1.y+v2.y, -1]]];
};
Int16ize: PROC [p: Pair, scale: REAL] RETURNS [Int16Pair] ~ {
RETURN[[Real.Fix[scale*p.x], Real.Fix[scale*p.y]]];
};
Int32Pair:     TYPE ~ RECORD [x, y: INT ¬ 0];
Int32PairSequence:  TYPE ~ REF Int32PairSequenceRep;
Int32PairSequenceRep: TYPE ~ RECORD [
length:        CARDINAL ¬ 0,
element:        SEQUENCE maxLength: CARDINAL OF Int32Pair
];
Int32PairMidpoint: PROC [v1, v2: Int32Pair] RETURNS [Int32Pair] ~ {
RETURN[[(v1.x+v2.x)/2, (v1.y+v2.y)/2]];
};
Int32ize: PROC [p: Pair, scale: REAL] RETURNS [Int32Pair] ~ {
RETURN[[Real.Round[scale*p.x], Real.Round[scale*p.y]]];
};
END.
..
Old Code
BiLerp: PUBLIC PROC [pa: PixelArray, x, y: REAL] RETURNS [CARDINAL] ~ {
val: REAL;
ix: NAT ← Real.Floor[x];
iy: NAT ← Real.Floor[y];
IF ix = pa.x+pa.dx-1
THEN {
IF iy = pa.y+pa.dy-1
THEN RETURN[pa[iy][ix]]
ELSE {
t1: INTEGER ← pa[iy][ix];
t2: INTEGER ← pa[iy+1][ix];
IF t1 = t2 THEN RETURN[t1] ELSE val ← t1+(y-iy)*(t2-t1);
}
}
ELSE {
IF iy = pa.y+pa.dy-1
THEN {
t1: INTEGER ← pa[iy][ix];
t2: INTEGER ← pa[iy][ix+1];
IF t1 = t2 THEN RETURN[t1] ELSE val ← t1+(x-ix)*(t2-t1);
}
ELSE {
dx: REAL ← x-ix;
t1: INTEGER ← pa[iy][ix];
t2: INTEGER ← pa[iy+1][ix];
t3: INTEGER ← pa[iy][ix+1];
t4: INTEGER ← pa[iy+1][ix+1];
IF t1 = t2 AND t1 = t3 AND t1 = t4
THEN RETURN[t1]
ELSE {
val1: REAL ← t1+dx*(t3-t1);
val2: REAL ← t2+dx*(t4-t2);
val ← (val1)+(y-iy)*(val2-val1);
};
};
};
RETURN[Real.Floor[val]];
};
Reflect: PUBLIC PROC [p: SampleMapCD] ~ {
ImagerSampleMap.Transfer[p, ImagerSampleMap.ShiftMap[ImagerSampleMap.Reflect[p], p.sSize, 0]];
};
Lum: PUBLIC PROC [map: SampleMapCD] ~ {
buf: ARRAY [0..256) OF NAT;
cm: ColorMap.Cmap ← ColorMap.Read[];
By keeping buf outside of action, action runs ~30% faster because less stack mgt. necessary
action: PROC [line: ImagerSample.SampleBuffer] ~ TRUSTED {
sample: ImagerSample.UnsafeSamples ~ ImagerSample.GetPointer[line, 0, 0, map.fSize];
FOR y: INT IN [map.sMin..map.sMin+map.sSize) DO
Process.CheckForAbort[];
SampleMapOps.GetF[map, y, map.fMin, line, 0, 0, map.fSize];
FOR x: NAT IN [0..map.fSize) DO sample[x] ← buf[sample[x]]; ENDLOOP;
SampleMapOps.PutF[map, y, map.fMin, line, 0, 0, map.fSize, null, null];
ENDLOOP;
};
FOR i: INT IN [0..256) DO
buf[i] ← MIN[255, MAX[0, Real.RoundI[.30*cm[0][i] + .59*cm[1][i] + .11*cm[2][i]]]];
ENDLOOP;
ColorMap.Write[ColorMap.Mono[]];
ImagerSample.DoWithScratchBuffer[1, map.fSize, action];
};
PerLine: PUBLIC PROC [perLineProc: ColorDisplayCase.PerLineProc] ~ {
map: SampleMapCD;
map ← ImagerOps.SampleMapFromFrameBuffer [Terminal.GetColorFrameBufferA[InterminalBackdoor.terminal]];
action: PROC [sampleBuffer: ImagerSample.SampleBuffer] ~ TRUSTED {
bounds: ImagerSampleMap.DeviceRectangle ~ map.Window;
sample: ImagerSample.UnsafeSamples ~ GetPointer[sampleBuffer, 0, 0, bounds.fSize];
maxVal: ImagerSample.Sample ~ CARDINAL[Basics.BITSHIFT[1, Basics.BITSHIFT[1, map.refRep.lgBitsPerPixel]]-1];
FOR s: INT IN [bounds.sMin..bounds.sMin+bounds.sSize) DO
Process.CheckForAbort[];
SampleMapOps.GetF[map, s, bounds.fMin, sampleBuffer, 0, 0, bounds.fSize];
perLineProc[sample, bounds.fSize];
SampleMapOps.PutF[map, s, bounds.fMin, sampleBuffer, 0, 0, bounds.fSize, null, null];
ENDLOOP;
};
ImagerSample.DoWithScratchBuffer[1, map.fSize, action];
};
PerPixel: PUBLIC PROC [perPixelProc: ColorDisplayCase.PerPixelProc] ~ {
map: SampleMapCD;
map: SampleMapCD ← ImagerOps.SampleMapFromFrameBuffer [Terminal.GetColorFrameBufferA[InterminalBackdoor.terminal]];
action: PROC [sampleBuffer: ImagerSample.SampleBuffer] ~ TRUSTED {
bounds: ImagerSampleMap.DeviceRectangle ~ map.Window;
sample: ImagerSample.UnsafeSamples ~ GetPointer[sampleBuffer, 0, 0, bounds.fSize];
maxVal: ImagerSample.Sample ~ CARDINAL[Basics.BITSHIFT[1, Basics.BITSHIFT[1, map.refRep.lgBitsPerPixel]]-1];
FOR s: INT IN [bounds.sMin..bounds.sMin+bounds.sSize) DO
Process.CheckForAbort[];
SampleMapOps.GetF[map, s, bounds.fMin, sampleBuffer, 0, 0, bounds.fSize];
FOR j: NAT IN [0..bounds.fSize) DO
sample[j] ← MAX[MIN[INT[perPixelProc[sample[j]]], maxVal], 0];
ENDLOOP;
SampleMapOps.PutF[map, s, bounds.fMin, sampleBuffer, 0, 0, bounds.fSize, null, null];
ENDLOOP;
};
ImagerSample.DoWithScratchBuffer[1, map.fSize, action];
};
ExpandReals: TYPE ~ RECORD [length: NAT ← 0, e: SEQUENCE maxLength: NAT OF REAL];
expandFactors: ARRAY [0..9) OF REAL ← [0./8., 5./8., 2./8., 7./8., 4./8., 1./8., 6./8., 3./8, 8./8.];
expandArray: ARRAY [0..9) OF REF ExpandReals;
Expand: PUBLIC PROC [map: SampleMap, sourceBox: Box, report: BOOLTRUE] ~ {
CheckAndReport: PROC [y: NAT] ~ {
Process.CheckForAbort[];
IF report AND (y MOD 10 = 0 OR y > 700) THEN
MessageWindow.Append[IO.PutFR["\t\tAt line %g (of 767)", IO.int[y]], TRUE];
};
sourceMap: SampleMap ← ImagerSample.ReIndex[map, , sourceBox];
s: PixelArray ← CtBasic.PixelArrayFromSampleMap[sourceMap];
d: PixelArray ← CtBasic.AllocatePixelArray[[[0, 0], [768, 1024]]];
IF sourceBox = [[0, 0], [480, 640]]
THEN { -- optimized for the common case
DoLine: PROC [reals0, reals1: REF ExpandReals] ~ {
DoPixel: PROC [fX0Y0, fX1Y0, fX0Y1, fX1Y1: REAL] ~ INLINE {
vX0Y0: NAT ← sLine0[sX0];
vX1Y0: NAT ← sLine0[sX1];
vX0Y1: NAT ← sLine1[sX0];
vX1Y1: NAT ← sLine1[sX1];
dLine[dX] ← IF vX0Y0=vX1Y0 AND vX0Y0=vX0Y1 AND vX0Y0=vX1Y1 THEN vX0Y0 ELSE Real.Round[fX0Y0*vX0Y0+fX1Y0*vX1Y0+fX0Y1*vX0Y1+fX1Y1*vX1Y1];
dX ← dX+1;
};
dX, sX0, sX1: NAT ← 0;
CheckAndReport[dY];
FOR n: NAT IN [0..128) DO
sX0 ← sX1; sX1 ← sX1+1;
DoPixel[reals0[8], reals0[0], reals1[8], reals1[0]];
DoPixel[reals0[7], reals0[1], reals1[7], reals1[1]];
sX0 ← sX1; sX1 ← sX1+1;
DoPixel[reals0[6], reals0[2], reals1[6], reals1[2]];
DoPixel[reals0[5], reals0[3], reals1[5], reals1[3]];
sX0 ← sX1; sX1 ← sX1+1;
DoPixel[reals0[4], reals0[4], reals1[4], reals1[4]];
sX0 ← sX1; sX1 ← sX1+1;
DoPixel[reals0[3], reals0[5], reals1[3], reals1[5]];
DoPixel[reals0[2], reals0[6], reals1[2], reals1[6]];
sX0 ← sX1; IF sX1 < 639 THEN sX1 ← sX1+1;
DoPixel[reals0[1], reals0[7], reals1[1], reals1[7]];
ENDLOOP;
IF dY < 767 THEN dLine ← d[dY ← dY+1];
};
dY, sY1: NAT ← 0;
dLine: SampleBuffer ← d[0];
sLine0: SampleBuffer ← s[0];
sLine1: SampleBuffer ← s[1];
FOR n: NAT IN [0..96) DO
sLine0 ← sLine1; sLine1 ← s[sY1 ← sY1+1];
DoLine[expandArray[8], expandArray[0]];
DoLine[expandArray[7], expandArray[1]];
sLine0 ← sLine1; sLine1 ← s[sY1 ← sY1+1];
DoLine[expandArray[6], expandArray[2]];
DoLine[expandArray[5], expandArray[3]];
sLine0 ← sLine1; sLine1 ← s[sY1 ← sY1+1];
DoLine[expandArray[4], expandArray[4]];
sLine0 ← sLine1; sLine1 ← s[sY1 ← sY1+1];
DoLine[expandArray[3], expandArray[5]];
DoLine[expandArray[2], expandArray[6]];
sLine0 ← sLine1; IF sY1 < 479 THEN sLine1 ← s[sY1 ← sY1+1];
DoLine[expandArray[1], expandArray[7]];
ENDLOOP;
}
ELSE {
maxX: NAT ← sourceBox.max.f-1;
maxY: NAT ← sourceBox.max.s-1;
xScale: REALREAL[sourceBox.max.f-sourceBox.min.f]/1024.0;
yScale: REALREAL[sourceBox.max.s-sourceBox.min.s]/768.0;
FOR y: NAT IN [0..768) DO
yy: REALREAL[y]*yScale;
y0: NAT ← sourceBox.min.s+Real.Fix[yy];
y1: NATIF y0 < maxY THEN y0+1 ELSE y0;
dy: REAL ← yy-y0;
my: REAL ← 1.0-dy;
CheckAndReport[y];
FOR x: NAT IN [0..1024) DO
xx: REALREAL[x]*xScale;
x0: NAT ← sourceBox.min.f+Real.Fix[xx];
x1: NATIF x0 < maxX THEN x0+1 ELSE x0;
dx: REAL ← xx-x0;
mx: REAL ← 1.0-dx;
vx0y0: CARDINAL ← s[y0][x0];
vx0y1: CARDINAL ← s[y1][x0];
vx1y0: CARDINAL ← s[y0][x1];
vx1y1: CARDINAL ← s[y1][x1];
d[y][x] ← IF vx0y0=vx0y1 AND vx0y0=vx1y0 AND vx0y0=vx1y1 THEN vx0y0 ELSE Real.Round[mx*my*vx0y0+dx*my*vx1y0+mx*dy*vx0y1+dx*dy*vx1y1];
ENDLOOP;
ENDLOOP;
};
ImagerSample.Transfer[map, CtBasic.SampleMapFromPixelArray[d]];
};
ExpandInit: PROC ~ {
FOR n: NAT IN [0..9) DO
v: REF ExpandReals ← expandArray[n] ← NEW[ExpandReals[9]];
f: REAL ← expandFactors[n];
FOR nn: NAT IN [0..9) DO
v[nn] ← f*expandFactors[nn];
ENDLOOP;
ENDLOOP;
};
CoonsWarpInner1: PROC ~ {
Nonoptimized (nonincremental) version, 130 sec for full screen:
c00, c10, c11, c01: Pair;
line: SampleBuffer ← ImagerSample.ObtainScratchSamples[du+1];
pu0: PairSequence ← G2dContour.ResamplePairs[curves[0], du+1];
p1v: PairSequence ← G2dContour.ResamplePairs[curves[1], dv+1];
pu1: PairSequence ← G2dContour.ResamplePairs[curves[2], du+1];
p0v: PairSequence ← G2dContour.ResamplePairs[curves[3], dv+1];
[] ← G2dContour.ReversePairs[pu1, pu1];
[] ← G2dContour.ReversePairs[p0v, p0v];
c00 ← G2dVector.Midpoint[pu0[0], p0v[0]];
c10 ← G2dVector.Midpoint[pu0[du], p1v[0]];
c11 ← G2dVector.Midpoint[pu1[du], p1v[dv]];
c01 ← G2dVector.Midpoint[pu1[0], p0v[dv]];
Given function F(u, v), with u, v IN [0..1]:
FOR v: NAT IN [v0..v1) DO
vf: REALREAL[v-v0]/dv;
FOR u: NAT IN [u0..u1) DO
uf: REALREAL[u-u0]/du;
x: INT16 ← Real.Fix[
(1-vf)*pu0[u].x+
vf*pu1[u].x+
(1-uf)*p0v[v].x+
uf*p1v[v].x-
(1-uf)*(1-vf)*c00.x-
uf*(1-vf)*c10.x-
(1-uf)*vf*c01.x-
uf*vf*c11.x];
y: INT16 ← Real.Fix[
(1-vf)*pu0[u].y+
vf*pu1[u].y+
(1-uf)*p0v[v].y+
uf*p1v[v].y-
(1-uf)*(1-vf)*c00.y-
uf*(1-vf)*c10.y-
(1-uf)*vf*c01.y-
uf*vf*c11.y];
line[u] ← IF x >= 0 AND x < dx AND y >= 0 AND y < dy THEN pa1[y][x] ELSE 0;
ENDLOOP;
ImagerSample.PutSamples[dest.bw, [v, u0], , line, 0, du+1];
ENDLOOP;
ImagerSample.ReleaseScratchSamples[line];
};
CoonsWarpInner2: PROC ~ {
Moderately optimized version (mostly incremental yet REAL), 30 sec for full screen:
c00, c10, c11, c01, c0v, c1v, dc0v, dc1v: Pair;
line: SampleBuffer ← ImagerSample.ObtainScratchSamples[du+1];
pu0: PairSequence ← G2dContour.ResamplePairs[curves[0], du+1];
p1v: PairSequence ← G2dContour.ResamplePairs[curves[1], dv+1];
pu1: PairSequence ← G2dContour.ResamplePairs[curves[2], du+1];
p0v: PairSequence ← G2dContour.ResamplePairs[curves[3], dv+1];
[] ← G2dContour.ReversePairs[pu1, pu1];
[] ← G2dContour.ReversePairs[p0v, p0v];
c00 ← G2dVector.Midpoint[pu0[0], p0v[0]];
c10 ← G2dVector.Midpoint[pu0[du], p1v[0]];
c11 ← G2dVector.Midpoint[pu1[du], p1v[dv]];
c01 ← G2dVector.Midpoint[pu1[0], p0v[dv]];
c0v ← c00; dc0v ← [(c01.x-c00.x)/dv, (c01.y-c00.y)/dv];
c1v ← c10; dc1v ← [(c11.x-c10.x)/dv, (c11.y-c10.y)/dv];
FOR v: NAT IN [v0..v1) DO
vf: REALREAL[v-v0]/dv;
puv: Pair ← [p0v[v].x-c0v.x, p0v[v].y-c0v.y];
dpuv: Pair ← [(p1v[v].x-c1v.x-puv.x)/du, (p1v[v].y-c1v.y-puv.y)/du];
FOR u: NAT IN [u0..u1) DO
x: INT16 ← Real.Fix[puv.x + pu0[u].x+vf*(pu1[u].x-pu0[u].x)];
y: INT16 ← Real.Fix[puv.y + pu0[u].y+vf*(pu1[u].y-pu0[u].y)];
line[u] ← IF x >= 0 AND x < dx AND y >= 0 AND y < dy THEN pa1[y][x] ELSE 0;
puv ← [puv.x+dpuv.x, puv.y+dpuv.y];
ENDLOOP;
ImagerSample.PutSamples[dest.bw, [v, u0], , line, 0, du+1];
c0v ← [c0v.x+dc0v.x, c0v.y+dc0v.y];
c1v ← [c1v.x+dc1v.x, c1v.y+dc1v.y];
ENDLOOP;
ImagerSample.ReleaseScratchSamples[line];
};
ExpandInit[];