ImageTransform:
PROC[dst, src: Pixels.SubMap, dstPos: IntPair, theta, scale:
REAL] ~ {
i: NAT ← 0; -- loop index
dstEdge, srcEdge, dstStart, dstBias, srcStart: EdgeDesc;
srcBytesPerLine, dstBytesPerLine: CARDINAL;
ebt: EdgeBltTable;
scaledWidth: NAT ← Real.RoundC[scale * src.subMap.size.f];
scaledHeight: NAT ← Real.RoundC[scale * src.subMap.size.s];
srcAddr: INT ← LOOPHOLE[src.subMap.sampleMap.base.word, INT]*2
+ src.subMap.sampleMap.base.bit/8;
dstAddr: INT ← LOOPHOLE[dst.subMap.sampleMap.base.word, INT]*2
+ dst.subMap.sampleMap.base.bit/8;
dstEdgeLength: CARDINAL ← Real.RoundC[RealFns.CosDeg[theta] * scaledWidth];
dstEdgeHiccups: CARDINAL ← Real.RoundC[RealFns.SinDeg[theta] * scaledWidth];
CheckLimits[]; -- ensure transformed image fits in target buffer, 0<theta<pi/4, .5<scale<1.5
dstBytesPerLine ← dst.subMap.sampleMap.bitsPerLine / 8;
dstAddr ← dstAddr + dst.subMap.start.s * dstBytesPerLine + dst.subMap.start.f * dst.df;
dstAddr ← dstAddr + dstPos.y * dstBytesPerLine + dstPos.x * dst.df;
dstStart ← [
-- generates addresses walking down left edge of destination image
val: dstAddr, -- start position for destination image
length: Real.RoundC[RealFns.CosDeg[theta] * scaledHeight], -- left edge height, dst image
hiccups: Real.RoundC[RealFns.SinDeg[theta] * scaledHeight], -- hiccups in left edge
lngthIncr: dst.subMap.sampleMap.bitsPerLine / 8, -- vertical step to next scanline
hicIncr: dst.df, -- horizontal step to next pixel
bias: Real.RoundC[RealFns.CosDeg[theta] * scaledHeight] -- supply bias in lieu of EdgeBlt
];
dstBias ← [
-- generates bias to correctly place first hiccup in image scanline-to-edge blt
val: 0, -- initial bias is zero
length: dstStart.length, -- height of left edge of dst image
hiccups: dstStart.hiccups, -- hiccups in left edge
lngthIncr: 0, -- no change until hiccup
hicIncr: -2 * dstEdgeHiccups, -- increment to bias
bias: dstStart.bias - 2 * dstStart.hiccups -- same as dstStart, off by one
];
srcBytesPerLine ← src.subMap.sampleMap.bitsPerLine / 8;
srcStart ← [
-- walks down source image scanlines, skipping or replicating lines as needed
val: srcAddr + src.subMap.start.s * INT[srcBytesPerLine] + src.subMap.start.f * src.df,
length: src.subMap.size.s, -- height of source image
hiccups: ABS[src.subMap.size.s - INTEGER[scaledHeight]], -- pixels to drop/add to scale down/up
lngthIncr: srcBytesPerLine, -- vertical step to next scanline
hicIncr: IF scale > 1.0 THEN -srcBytesPerLine ELSE srcBytesPerLine, -- hiccup step
bias: src.subMap.size.s -- supply bias since not using EdgeBlt
];
dstEdge ← [
-- walks across destinatation image along slanted edge
val: dstStart.val,
length: dstEdgeLength, -- horizontal dimension of edge
hiccups: dstEdgeHiccups, -- vertical dimension of edge
lngthIncr: dst.df, -- horizontal step to next pixel
hicIncr: -dst.subMap.sampleMap.bitsPerLine / 8, -- vertical step to next pixel
bias: dstBias.val, -- bias computed by dstBias
indirect: TRUE -- incrementing addresses
];
srcEdge ← [
-- walks along scanline of source image, skipping or copying pixels as needed
val: srcStart.val,
length: src.subMap.size.f, -- width of source image
hiccups: ABS[INTEGER[src.subMap.size.f] - scaledWidth], -- pixels to drop/add to scale down/up
lngthIncr: src.df, -- horizontal step to next pixel
hicIncr: IF scale > 1.0 THEN -src.df ELSE src.df, -- hiccup step to next pixel
bias: 0, -- will be supplied by EdgeBlt
indirect: TRUE -- incrementing addresses
];
ebt ← [dstEdge, srcEdge, [TRUE, FALSE] ];
WHILE i
< dstStart.length
DO
EdgeBlt[ebt];
IF dstBias.val <= - dstEdgeLength
-- equals 0 after EdgeBlt adds initial bias
THEN {
-- should be integrated better
dstBias.val ← dstBias.val + 2 * dstEdgeLength; -- reset bias
dstStart.val ← dstStart.val - dstStart.lngthIncr; -- move start position up one line
}
ELSE i ← i+1;
ebt.dst.bias ← dstBias.val; -- set up bias for distance to first hiccup
dstBias ← EdgeIncr[dstBias];
dstStart ← EdgeIncr[dstStart]; ebt.dst.val ← dstStart.val;
srcStart ← EdgeIncr[srcStart]; ebt.src.val ← srcStart.val;
ENDLOOP;
};