--MUST BE COMPILED WITH /L SWITCH
DIRECTORY
AltoMicrocodeDefs: FROM "AltoMicrocodeDefs",
D0MicrocodeDefs: FROM "D0MicrocodeDefs" USING [JumpRamArgRslt],
HalftoneDefs: FROM "HalftoneDefs",
InlineDefs: FROM "InlineDefs" USING [BITSHIFT,BITOR,BITAND,LongMult,LongDiv,HighHalf,LowHalf],
GraphicsDefs: FROM "GraphicsDefs" USING [Bitmap,GetDefaultBitmapHandle],
LongDefs: FROM "LongDefs",
MiscDefs: FROM "MiscDefs" USING [SetBlock],
SegmentDefs: FROM "SegmentDefs",
SystemDefs: FROM "SystemDefs";

HalftoneImpl: PROGRAM IMPORTS AltoMicrocodeDefs, D0MicrocodeDefs, GraphicsDefs, InlineDefs, LongDefs, SegmentDefs, MiscDefs, SystemDefs EXPORTS HalftoneDefs =
BEGIN OPEN HalftoneDefs, GraphicsDefs, MiscDefs;

htMicrocodeStart: CARDINAL = 7400B;

machine: SegmentDefs.MachineType ← SegmentDefs.GetMemoryConfig[].AltoType;
UseMicrocode: BOOLEAN ← machine IN [AltoIIXM..D0];
MulDiv: PROCEDURE [a,b,c:CARDINAL] RETURNS [CARDINAL] = INLINE
BEGIN
al: LONG CARDINAL ← InlineDefs.LongMult[a,b];
RETURN[InlineDefs.LongDiv[al,c]];
END;

HalftoneParams: TYPE = MACHINE DEPENDENT RECORD
[ nInputPixels: CARDINAL,
black: INTEGER,
range: INTEGER,
nOutputDots: CARDINAL,

errorVec: LONG POINTER,
bitmap: LONG POINTER,

bitOffset: CARDINAL,
mode: [0..17B],
xOdd: BOOLEAN,
bitmapWidth: [0..3777B],
pixelArray: LONG POINTER,

wordAlignPad: ARRAY [0..3) OF UNSPECIFIED
];
errorVec: POINTER TO ARRAY [0..0) OF INTEGER ← NIL;
hp: HalftoneParams;
off: CARDINAL ← SELECT InlineDefs.BITAND[@hp,3] FROM 0=>0,1=>3,2=>2,ENDCASE=>1;
halftoneParams: POINTER TO HalftoneParams ← LOOPHOLE[@hp+off];
yctr: INTEGER;
lineCount: INTEGER;
yInc,yDec: CARDINAL;
yLimit: INTEGER;
xMin: CARDINAL;
--for soft version, we also need:
BitTable: ARRAY [0..20B) OF CARDINAL ←
[100000B,40000B,20000B,10000B,4000B,2000B,
1000B,400B,200B,100B,40B,20B,10B,4,2,1
];
outputPixelArray,errorArray: POINTER TO ARRAY [0..256) OF CARDINAL ← NIL;
nBitsPerPixel: CARDINAL ← 0;--comes from bitmap.nBitsPerPixel
outputBitsPerPixel: CARDINAL ← 1;
colorShift: CARDINAL ← 0;
fieldLen: CARDINAL ← 1;
UseError: BOOLEAN ← TRUE;
DistributeError: BOOLEAN ← TRUE;

SetHalftoneResolution: PUBLIC PROCEDURE [nOutputBitsPerPixel,fieldLength,nOffsetBits: CARDINAL,useError: BOOLEAN ← TRUE,distributeError: BOOLEAN ← TRUE] =
BEGIN
IF nBitsPerPixel = 0 THEN nBitsPerPixel ←
GetDefaultBitmapHandle[].nBitsPerPixel;
outputBitsPerPixel ← nOutputBitsPerPixel;
colorShift ← (16-nBitsPerPixel)+nOffsetBits;
fieldLen ← fieldLength;
UseError ← useError;
DistributeError ← useError AND distributeError;
END;

MakeLongPointer: PUBLIC PROCEDURE [ptr: POINTER,bank: UNSPECIFIED] RETURNS [LONG POINTER] = MACHINE CODE BEGIN END;

InitHalftone: PUBLIC PROCEDURE [
x,y: CARDINAL,--top left corner in bitmap
nInputPixels,nOutputDots: CARDINAL,--8 bit pixels, packed
black: INTEGER ← 0,white: INTEGER ← 255,
bitmap: POINTER TO Bitmap ← NIL,
nInputLines: CARDINAL ← 0,nOutputLines: CARDINAL ← 0,
mode: CARDINAL ← 3] =
BEGIN
bMode: CARDINAL ← mode/4; --bit direction mode
sMode: CARDINAL ← mode MOD 4; --scan direction mode
dotMax,xLimit: CARDINAL;
initVal: INTEGER ← ((black-white)*3-4)/8;
IF initVal > 0 THEN initVal ← -MulDiv[white-black,3,8]; --overflow

IF bitmap = NIL THEN bitmap ← GetDefaultBitmapHandle[];
nBitsPerPixel ← bitmap.nBitsPerPixel;
IF nBitsPerPixel > 1 THEN initVal ← 0;

SELECT bMode FROM
0,1 =>
BEGIN
dotMax ← x+nOutputDots-1;
xLimit ← bitmap.nBits-1;
END;
ENDCASE =>
BEGIN
dotMax ← y+nOutputDots-1;
xLimit ← bitmap.nLines-1;
END;

IF errorVec # NIL THEN SystemDefs.FreeSegment[errorVec];
errorVec ← SystemDefs.AllocateSegment[x+nOutputDots];

xMin ← 0;
IF nInputLines = 0 THEN
BEGIN
IF bMode IN [2..3] THEN ERROR; --can’t default
nInputLines ← nInputPixels;
nOutputLines ← nOutputDots;
END;
IF INTEGER[x] < 0 THEN ERROR;
IF dotMax > xLimit THEN
BEGIN
nP: CARDINAL ← MulDiv[nInputPixels,nOutputDots-(dotMax-xLimit),nOutputDots];
nOutputDots ← MulDiv[nOutputDots,nP,nInputPixels];
IF bMode IN [1..2] THEN xMin ← nInputPixels-nP;
nInputPixels ← nP;
END;

halftoneParams↑ ←
[
nInputPixels: nInputPixels,black: black,range: white-black,
nOutputDots: nOutputDots,

errorVec: MakeLongPointer[errorVec,0],
bitmap: MakeLongPointer[bitmap.bits,bitmap.bank]+LONG[y]*bitmap.nWords +
(SELECT sMode FROM
0 => LONG[x/16],
1 => LONG[(x+nOutputLines-1)/16],
2=> LONG[nOutputLines-1]*bitmap.nWords,
ENDCASE => 0) +
(SELECT bMode FROM
2 => LONG[nOutputDots-1]*bitmap.nWords,
ENDCASE =>0),

bitOffset: x + (SELECT TRUE FROM
bMode=1 => nOutputDots-1,
sMode=1 => nOutputLines-1,
ENDCASE => 0),
mode: mode,xOdd: (xMin MOD 2) = 1,bitmapWidth: bitmap.nWords,
pixelArray: ,wordAlignPad:
];

IF machine = AltoIIXM THEN
BEGIN
UseMicrocode←TRUE;
AltoMicrocodeDefs.LoadRam[]; --nop after first call
--check for illegal long pointers
IF (InlineDefs.HighHalf[halftoneParams.errorVec] # 0) THEN ERROR;
IF (halftoneParams.mode # 3) THEN UseMicrocode←FALSE;
END;
lineCount ← SELECT sMode FROM
2=>y+nOutputLines-1,0=>x,1=>x+nOutputLines-1,ENDCASE=>y;

yLimit ← SELECT bMode FROM 2,3=>bitmap.nBits-1,ENDCASE=>bitmap.nLines-1;
yInc ← nInputLines;
yDec ← nOutputLines;
yctr ← -nOutputLines;
MiscDefs.SetBlock[errorVec,initVal,x+nOutputDots];
IF nBitsPerPixel > 1 THEN
BEGIN
maxVal: CARDINAL = SELECT outputBitsPerPixel FROM
1=>1,2=>3,3=>7,4=>15,5=>31,6=>63,7=>127,ENDCASE=>255;
maxVal2: CARDINAL = maxVal*2;
fieldMultiplier: CARDINAL = SELECT fieldLen FROM
1=>1,2=>3,3=>7,4=>15,5=>31,6=>63,7=>127,ENDCASE=>255;
bucket: CARDINAL ← 0;
i: CARDINAL;

IF outputPixelArray = NIL THEN
BEGIN
outputPixelArray ← SystemDefs.AllocateSegment[256];
errorArray ← SystemDefs.AllocateSegment[256];
END;
IF outputBitsPerPixel = 8 THEN
FOR i IN [0..256) DO
outputPixelArray[i] ← i;
errorArray[i] ← 0;
ENDLOOP
ELSE
UNTIL bucket = maxVal2 DO
FOR i IN [(256*bucket)/maxVal2..(256*(bucket+1))/maxVal2) DO
outputPixelArray[i] ← ((bucket/2)*fieldMultiplier)/maxVal;
errorArray[i] ← i-(256*bucket)/maxVal2;
ENDLOOP;
bucket ← bucket+1;
FOR i IN [(256*bucket)/maxVal2..(256*(bucket+1))/maxVal2) DO
outputPixelArray[i] ← (((bucket+1)/2)*fieldMultiplier)/maxVal;
errorArray[i] ← i-(256*(bucket+1))/maxVal2;
ENDLOOP;
bucket ← bucket+1;
ENDLOOP;
END;
END;

PrintHalftoneLine: PUBLIC PROCEDURE [p: LONG POINTER TO ARRAY [0..0) OF UNSPECIFIED] RETURNS [nLinesPrinted: CARDINAL] =
BEGIN
firstLineCount: INTEGER ← lineCount;
bMode: CARDINAL ← halftoneParams.mode/4; --bit direction mode
sMode: CARDINAL ← halftoneParams.mode MOD 4; --scan direction mode

IF outputBitsPerPixel = 0 THEN RETURN;
halftoneParams.pixelArray ← p+xMin/2;
IF machine NOT IN [D0..Dorado] THEN
IF InlineDefs.HighHalf[p] # 0 THEN ERROR;

WHILE yctr < 0 DO
IF lineCount IN [0..yLimit] THEN
IF nBitsPerPixel = 1 THEN
IF UseMicrocode THEN
IF machine = D0 THEN
[] ← D0MicrocodeDefs.JumpRamArgRslt[htMicrocodeStart,halftoneParams]
ELSE
AltoMicrocodeDefs.PrintHalftoneLine[halftoneParams]
ELSE SoftPrintLine1[p]
ELSE SoftPrintLineN[p];

yctr ← yctr + yInc;
halftoneParams.bitOffset ← halftoneParams.bitOffset +
(SELECT sMode FROM
0 => 1,
1 => -1,
ENDCASE => 0);
halftoneParams.bitmap ← halftoneParams.bitmap +
(SELECT sMode FROM
0 => IF halftoneParams.bitOffset MOD 16 = 0 THEN 1 ELSE 0,
1 => IF halftoneParams.bitOffset MOD 16 = 15 THEN -1 ELSE 0,
2 => -halftoneParams.bitmapWidth,
ENDCASE => halftoneParams.bitmapWidth);

lineCount ← lineCount + (SELECT sMode FROM
1,2 => -1,
ENDCASE => 1);

ENDLOOP; -- while yctr < 0
yctr ← yctr - yDec;
RETURN[ABS[lineCount-firstLineCount]];
END;

SoftPrintLine1: PROCEDURE [p: LONG POINTER TO ARRAY [0..0) OF UNSPECIFIED] =
BEGIN OPEN InlineDefs;
x: CARDINAL;
val: INTEGER;
error,error1: INTEGER;

bMode: CARDINAL ← halftoneParams.mode/4;
pixelArray: LONG POINTER TO PACKED ARRAY OF [0..377B] ← LOOPHOLE[p];
screenWord: LONG POINTER TO ARRAY [0..0) OF CARDINAL ← halftoneParams.bitmap;
bitmapWidth: CARDINAL ← halftoneParams.bitmapWidth;
bitCount: CARDINAL ← SELECT bMode FROM
2,3 => 0,ENDCASE => halftoneParams.bitOffset;
bit: CARDINAL ← BitTable[halftoneParams.bitOffset MOD 16];
CascadeDiag: INTEGER ← 0;
CascadeRight: INTEGER ← errorVec[bitCount]*(IF bMode<2 THEN 4 ELSE 2)-1;
jctr: INTEGER ← -halftoneParams.nOutputDots;
s: CARDINAL;

IF machine IN [D0..Dorado] THEN
FOR x IN [xMin..xMin+halftoneParams.nInputPixels) DO
val ← MIN[MAX[0,pixelArray[x]-halftoneParams.black],halftoneParams.range];
WHILE jctr < 0 DO
error ← CascadeRight+val;
IF error < 0 THEN --print black
BEGIN
SELECT bMode FROM
2,3 => --Bottom to Top or Bottom to Top
screenWord[0] ←
InlineDefs.BITOR[screenWord[0],bit];
ENDCASE => --Left to Right or Right to Left
screenWord[bitCount/16] ←
InlineDefs.BITOR[screenWord[bitCount/16],BitTable[bitCount MOD 16]];
END
ELSE error ← error-halftoneParams.range;
s←BITAND[error,100000B];
SELECT InlineDefs.BITAND[error,3] FROM
0=>BEGIN error ← BITSHIFT[error,-1]+s;
error1 ← BITSHIFT[error,-1]+s;END;
1=>BEGIN error ← BITSHIFT[error,-1]+s;
error1 ← BITSHIFT[error,-1]+s;error ← error+1;END;
2=>BEGIN error ← BITSHIFT[error,-1]+s;
error1 ← BITSHIFT[error,-1]+s;error ← error+1;END;
ENDCASE=>BEGIN error ← BITSHIFT[error,-1]+s;
error1 ← BITSHIFT[error,-1]+s+1;END;
IF bMode < 2 THEN errorVec[bitCount] ← CascadeDiag+error1
ELSE errorVec[bitCount] ← CascadeDiag+error;
CascadeDiag←error1;
bitCount ← bitCount+1; --right 3/4 of the time
SELECT bMode FROM
2 => --Bottom to Top
screenWord ← screenWord-halftoneParams.bitmapWidth;
1 => --Right to Left
bitCount ← bitCount-2;
3 => --Top to Bottom
screenWord ← screenWord+halftoneParams.bitmapWidth;
ENDCASE; --normal=Left to Right, Top to Bottom
CascadeRight ← errorVec[bitCount]+(IF bMode < 2 THEN error ELSE error1);
jctr ← jctr + halftoneParams.nInputPixels;
ENDLOOP; --while jctr < 0
jctr ← jctr - halftoneParams.nOutputDots;
ENDLOOP --FOR x
ELSE --Alto, long pointers die
BEGIN
p: POINTER TO PACKED ARRAY OF [0..377B] ← InlineDefs.LowHalf[pixelArray];
sw: POINTER TO ARRAY [0..0) OF CARDINAL ← InlineDefs.LowHalf[screenWord];
FOR x IN [xMin..xMin+halftoneParams.nInputPixels) DO
val ← MIN[MAX[0,p[x]-halftoneParams.black],halftoneParams.range];
WHILE jctr < 0 DO
error ← CascadeRight+val;
IF error < 0 THEN --print black
BEGIN OPEN LongDefs;
SELECT bMode FROM
2,3 => --Bottom to Top or Bottom to Top
AltoWrite[addr: screenWord,
val: InlineDefs.BITOR[AltoRead[screenWord],bit]];
ENDCASE => --Left to Right or Right to Left
AltoWrite[addr: screenWord+bitCount/16,
val: InlineDefs.BITOR[AltoRead[screenWord+bitCount/16],
BitTable[bitCount MOD 16]]];
END
ELSE error ← error-halftoneParams.range;
s←BITAND[error,100000B];
SELECT InlineDefs.BITAND[error,3] FROM
0=>BEGIN error ← BITSHIFT[error,-1]+s;
error1 ← BITSHIFT[error,-1]+s;END;
1=>BEGIN error ← BITSHIFT[error,-1]+s;
error1 ← BITSHIFT[error,-1]+s;error ← error+1;END;
2=>BEGIN error ← BITSHIFT[error,-1]+s;
error1 ← BITSHIFT[error,-1]+s;error ← error+1;END;
ENDCASE=>BEGIN error ← BITSHIFT[error,-1]+s;
error1 ← BITSHIFT[error,-1]+s+1;END;
IF bMode < 2 THEN errorVec[bitCount] ← CascadeDiag+error1
ELSE errorVec[bitCount] ← CascadeDiag+error;
CascadeDiag←error1;
bitCount ← bitCount+1; --right 3/4 of the time
SELECT bMode FROM
2 => --Bottom to Top
screenWord ← screenWord-halftoneParams.bitmapWidth;
1 => --Right to Left
bitCount ← bitCount-2;
3 => --Top to Bottom
screenWord ← screenWord+halftoneParams.bitmapWidth;
ENDCASE; --normal=Left to Right, Top to Bottom
CascadeRight ← errorVec[bitCount]+(IF bMode < 2 THEN error ELSE error1);
jctr ← jctr + halftoneParams.nInputPixels;
ENDLOOP; --while jctr < 0
jctr ← jctr - halftoneParams.nOutputDots;
ENDLOOP; --FOR x
END;
END;

SoftPrintLineN: PROCEDURE [p: LONG POINTER TO ARRAY [0..0) OF UNSPECIFIED] =
BEGIN OPEN InlineDefs;
x: CARDINAL;
val,error,pixelIndex,pixelVal: INTEGER;
prevError4: INTEGER ← 0;
bMode: CARDINAL ← halftoneParams.mode/4;
pixelArray: LONG POINTER TO PACKED ARRAY OF [0..377B] ← LOOPHOLE[p];
screenWord: LONG POINTER TO ARRAY [0..0) OF CARDINAL ← halftoneParams.bitmap;
bitmapWidth: CARDINAL ← halftoneParams.bitmapWidth;
bitCount: CARDINAL ← SELECT bMode FROM
2,3 => 0,ENDCASE => (halftoneParams.bitOffset)*nBitsPerPixel;
jctr: INTEGER ← -halftoneParams.nOutputDots;
firstBit: CARDINAL ← (halftoneParams.bitOffset*nBitsPerPixel) MOD 16;
shift: CARDINAL ← colorShift-firstBit;
CascadeDiag: INTEGER ← 0;

error ← 0;
FOR x IN [xMin..xMin+halftoneParams.nInputPixels) DO
val ← MIN[MAX[0,pixelArray[x]-halftoneParams.black],halftoneParams.range];
WHILE jctr < 0 DO
pixelIndex ← val+error;
SELECT pixelIndex FROM
<0 =>
BEGIN
pixelVal ← outputPixelArray[0];
IF UseError THEN error ← pixelIndex;
END;
IN [0..256) =>
BEGIN
pixelVal ← outputPixelArray[pixelIndex];
IF UseError THEN error ← errorArray[pixelIndex];
END;
ENDCASE =>
BEGIN
pixelVal ← outputPixelArray[255];
IF UseError THEN error ← pixelIndex-halftoneParams.range;
END;
SELECT bMode FROM
2,3 => --Bottom to Top or Bottom to Top
screenWord[0] ←
BITOR[screenWord[0],BITSHIFT[pixelVal,shift]];
ENDCASE => --Left to Right or Right to Left
screenWord[bitCount/16] ←
BITOR[screenWord[bitCount/16],
BITSHIFT[pixelVal,colorShift-(bitCount MOD 16)]];
IF DistributeError THEN
BEGIN
s: CARDINAL ← BITAND[error,100000B];
currentError4: INTEGER;
SELECT InlineDefs.BITAND[error,3] FROM
0=>BEGIN error ← BITSHIFT[error,-1]+s;
currentError4 ← BITSHIFT[error,-1]+s;END;
1=>BEGIN error ← BITSHIFT[error,-1]+s;
currentError4 ← BITSHIFT[error,-1]+s;error ← error+1;END;
2=>BEGIN error ← BITSHIFT[error,-1]+s;
currentError4 ← BITSHIFT[error,-1]+s;error ← error+1;END;
ENDCASE=>BEGIN error ← BITSHIFT[error,-1]+s;
currentError4 ← BITSHIFT[error,-1]+s+1;END;
errorVec[bitCount/nBitsPerPixel] ← prevError4+currentError4;
prevError4 ← currentError4;
END;
bitCount ← bitCount+nBitsPerPixel; --right 3/4 of the time
SELECT bMode FROM
2 => --Bottom to Top
screenWord ← screenWord-halftoneParams.bitmapWidth;
1 => --Right to Left
bitCount ← bitCount-2*nBitsPerPixel;
3 => --Top to Bottom
screenWord ← screenWord+halftoneParams.bitmapWidth;
ENDCASE; --normal=Left to Right, Top to Bottom
IF DistributeError THEN error ← errorVec[bitCount/nBitsPerPixel]+error;
jctr ← jctr + halftoneParams.nInputPixels;
ENDLOOP; --while jctr < 0

jctr ← jctr - halftoneParams.nOutputDots;
ENDLOOP; --FOR x
END;

END.