// ConvertMask.bcpl -- Scan-conversion mask interpretation

//  errors 600

// This module deals with mask entries in the PD file.
// When an "imaging command" is read from a PD file, the procedure
// MaskNew is called. This procedure decodes the command and builds
// a corresponding "leftover" structure for the object.  Processing
// proceeds as if the leftover object had been read from a leftover
// list.

get "PDInternals.d"
get "PDConvert.d"

// outgoing procedures
external
	[
	MaskInit
	MaskFinish
	MaskNew
	]

// incoming procedures
external
	[
//ConvertPut
	PutRun
	PutBits
// ConvertUtils
	LoadRead
	UMax
	UMin
//WINDOW
	WindowRead
	WindowReadBlock
	WindowGetPosition
	WindowSetPosition
//PDPRINT
	FSGetX
	FSPutZ
	PDError
//PDML
	MulDiv
	MulFull
	DoubleAdd
//OS
	SetBlock
	]

// incoming statics
external
	[
	PDWindow
	SMin
	SMax
	LOSizes
	LOProcs
	]

// internal statics
static
	[
	MSizes
	MLO
	]

// File-wide structure and manifest declarations.

// Procedures

let MaskInit() be
[
	MSizes=FSGetX(imagingMax+1)
	MLO=FSGetX(imagingMax+1)

	MSizes!maskSamplesRef= size MaskSamplesRef/16
	MSizes!maskRunGroupRef=size MaskRunGroupRef/16
	MSizes!maskRectangle=size MaskRectangle/16
	MSizes!maskTrapezoid=size MaskTrapezoid/16
	MSizes!maskRunGroup=size MaskRunGroup/16
	MSizes!maskSamples=size MaskSamples/16
	MSizes!colorSamples=size ColorSamples/16

	MLO!maskSamplesRef=loSamples
	MLO!maskRunGroupRef=loRunGroup
	MLO!maskRectangle=loRectangle
	MLO!maskTrapezoid=loTrapezoid
	MLO!maskRunGroup=loRunGroup
	MLO!maskSamples=loSamples
	MLO!colorSamples=loColorSamples

	LOProcs!loRectangle=Rectangle
	LOProcs!loTrapezoid=Trapezoid
	LOProcs!loRunGroup=RunGroup
	LOProcs!loSamples=Samples
	LOProcs!loColorSamples=Samples

	LOSizes!loRectangle=size LORectangle/16
	LOSizes!loTrapezoid=size LOTrapezoid/16
	LOSizes!loRunGroup=size LORunGroup/16
	LOSizes!loSamples=size LOSamples/16
]

and MaskFinish() be
[
	FSPutZ(lv MSizes)
	FSPutZ(lv MLO)
]

// MaskNew -- called with fresh entry from PD file
//	command = first word of PD file entry
//	w = window on PD file
//	v = vector in which to build leftover description

and MaskNew(command, w, v) = valof
[
	let m=v+(size Command/16)		//PD structure: bypass command word
	let com=command<<Command.com
	if com ugr imagingMax then PDError(600)
	WindowReadBlock(w, m, MSizes!com)
	v!0=MLO!com		//Leftover type

	switchon com into
[
	case maskRectangle:
	case maskTrapezoid:
		endcase
	case maskRunGroup:
		v>>LORunGroup.addr=-1
		v>>LORunGroup.fOffset=0
		endcase
	case maskSamples:
	case colorSamples:
		v>>LOSamples.addr=-1
		if v>>LOSamples.sSize eq 0 then resultis false	//Nothing to do
		endcase
	case maskSamplesRef:
		[
		let fMin=m>>MaskSamplesRef.fMin
		let sMin=m>>MaskSamplesRef.sMin
		if command<<Command.rest ne 0 then PDError(602)
		let addr=m>>MaskSamplesRef.addr
		v>>LOSamples.fMin=fMin
		v>>LOSamples.sMin=sMin
		compileif offset SampleArray.sSize ne 0 then [ foo=nil ]
		compileif offset SampleArray.fSize ne 16 then [ foo=nil ]
		v>>LOSamples.sSize=LoadRead(addr)
		v>>LOSamples.fSize=LoadRead(addr+1)
		v>>LOSamples.addr=addr+2
		if v>>LOSamples.sSize eq 0 then resultis false	//Nothing to do
		]
		endcase
	case maskRunGroupRef:
		[
		let fMin=m>>MaskRunGroupRef.fMin
		let sMin=m>>MaskRunGroupRef.sMin
		if command<<Command.rest ne 0 then PDError(602)
		let addr=m>>MaskRunGroupRef.addr
		v>>LORunGroup.fOffset=fMin
		v>>LORunGroup.sMin=sMin
		compileif offset RunGroup.sSize ne 0 then [ foo=nil ]
		v>>LORunGroup.sSize=LoadRead(addr)
		v>>LORunGroup.addr=addr+1
		if v>>LORunGroup.sSize eq 0 then resultis false	//Nothing to do
		]
		endcase
]
	let loNeeded=(LOProcs!(v!0))(v)
	if loNeeded then
		[
		if com eq maskRunGroup % com eq maskSamples %
		   com eq colorSamples then PDError(601)
		]
	resultis loNeeded
]

and Rectangle(v) = valof
[
	let sFirst,sLast=nil,nil
	let c=BandClip(v>>LORectangle.sMin, v>>LORectangle.sSize, lv sFirst, lv sLast)
	PutRun(sFirst, sLast, v>>LORectangle.fMin, v>>LORectangle.fSize)
	resultis c
]

and Trapezoid(v) = valof
[
	let sFirst,sLast=nil,nil
	let c=BandClip(v>>LOTrapezoid.sMin, v>>LOTrapezoid.sSize, lv sFirst, lv sLast)
	let denom=v>>LOTrapezoid.sSize
	for s=sFirst to sLast do
		[
		let num=s+SMin-v>>LOTrapezoid.sMin
		let difMin=v>>LOTrapezoid.fMinLast-v>>LOTrapezoid.fMin
		let fm=nil
		test difMin ge 0 then
			fm=MulDiv(difMin, num, denom)
		or fm=-MulDiv(-difMin, num, denom)
		let difTop=difMin+v>>LOTrapezoid.fSizeLast-v>>LOTrapezoid.fSize
		let ft=nil
		test difTop ge 0 then
			ft=MulDiv(difTop, num, denom)
		or ft=-MulDiv(-difTop, num, denom)
		let fs=ft-fm+v>>LOTrapezoid.fSize
		if fs ne 0 then PutRun(s, s, fm+v>>LOTrapezoid.fMin, fs)
		]
	resultis c
]

and RunGroup(v) = valof
[
	compileif size Run ne 32 then [ foo=nil ] //This code depends on it
	let sFirst,sLast=nil,nil
	let c=BandClip(v>>LORunGroup.sMin, v>>LORunGroup.sSize, lv sFirst, lv sLast)
	let pos=lv v>>LORunGroup.addr
	let fOffset=v>>LORunGroup.fOffset
	let r=vec 1

	if sFirst+SMin ugr v>>LORunGroup.sMin then
		[
		let nLines=sFirst+SMin-v>>LORunGroup.sMin
		for i=0 to nLines do
			[ r!0=PosRead(pos); r!1=PosRead(pos) ] repeatuntil r>>Run.lastRun ne 0
		]

	for s=sFirst to sLast do
		[
		r!0=PosRead(pos); r!1=PosRead(pos)
		if r>>Run.fSize ne 0 then
			PutRun(s, s, r>>Run.fMin+fOffset, r>>Run.fSize)
		] repeatuntil r>>Run.lastRun ne 0

	if c then
		[
		v>>LORunGroup.sSize=v>>LORunGroup.sMin+v>>LORunGroup.sSize-1-SMax
		v>>LORunGroup.sMin=SMax+1	//next band
		]
	resultis c
]

and Samples(v) = valof
[
	let sFirst,sLast=nil,nil
	let c=BandClip(v>>LOSamples.sMin, v>>LOSamples.sSize, lv sFirst, lv sLast)
	let pos=lv v>>LOSamples.addr

	if sFirst+SMin ugr v>>LOSamples.sMin then
		[
		let nLines=sFirst+SMin-v>>LOSamples.sMin
		let wc=(v>>LOSamples.fSize+15) rshift 4
		let d=vec 1
		MulFull(nLines, wc, d)
		PosMove(pos, d)
		]

	PutBits(sFirst, sLast, v>>LOSamples.fMin, v>>LOSamples.fSize, pos, (v!0 eq loColorSamples))
	if c then
		[
		v>>LOSamples.sSize=v>>LOSamples.sMin+v>>LOSamples.sSize-1-SMax
		v>>LOSamples.sMin=SMax+1	//next band
		]
	resultis c
]

and MaskError(v) = valof
[
	PDError(603)
]

//BandClip(sMin, sSize, lv sFirst, lv sLast)
// Determine whether [sMin,sSize] lies within current band.
//	if to left of band, error
//	if terminates in band, return false (no leftovers)
//	if overlaps into next band, return true (leftovers)
// Returns in [sFirst..sLast] the scan-lines to be affected, in a
// coordinate system relative to beginning of band.

and BandClip(sMin, sSize, lvSFirst, lvSLast) = valof
[
	let sMax=sMin+sSize-1
// Check for error conditions
	if sMax uls sMin then PDError(604)	//add overflow?
	if sMax uls SMin then PDError(605)
	if sMin ugr SMax then PDError(606)
// Compute band-relative starting and finishing
	@lvSFirst=UMax(sMin,SMin)-SMin
	@lvSLast=UMin(sMax,SMax)-SMin
// Decide which condition exists
	if sMax ule SMax then resultis false
	resultis true
]

// Read a word from pos:
//		@pos=-1 means locn is in PD file
//		otherwise, it's a load address

and PosRead(pos) = valof
[
	if @pos eq -1 then resultis WindowRead(PDWindow)
	let t=LoadRead(@pos)
	@pos=@pos+1
	resultis t
]

// Increment position by double-precision count in c

and PosMove(pos, c) be
[
	test @pos eq -1 then
	[
		let a=vec 2
		WindowGetPosition(PDWindow, a)
		DoubleAdd(a, c)
		WindowSetPosition(PDWindow, a)
	] or @pos=@pos+c!1
]