; SCAN.ASM provides a scan-converting mechanism for polygons specified
; by fixed-point coordinates.
;
; Copr. 1994 Matthias K”ppe

	; Publics

		PUBLIC  scanInsert, scanDrawLine, scanPutPixel, \
			scanSetupP, scanDrawLineP, scanOutBufP, \
			scanConvBufP, scanPutPixelP, scanDoLine

	; Externals

		EXTRN	PutPixel: FAR, Line: FAR

	; Metas

		LOCALS	@@
		JUMPS

	; Includes

		INCLUDE	fixedop.asm
		INCLUDE	scandef.asm

	; Code Segment

CODE		SEGMENT BYTE PUBLIC

		ASSUME CS: CODE

	.386

	; Insert an entry
	;
	; AX	x value
	; BX	y value				-- is kept
	; CX	Vertical direction +1 / -1	-- is kept
	; ES:SI ScanHeader plus ScanEntry stream

scanInsert	PROC NEAR
		cmp	bx, es:[si].TScanHeader.TopClip
		jge	SHORT @@0
		sub	bx, es:[si].TScanHeader.BottomClip
		jl	SHORT @@0
		cld
		add	ax, es:[si].TScanHeader.LeftAdd
		mov	di, cx
		and	di, 8000H
		or	ax, di
		mov	di, es:[si].TScanHeader.FreeEntry
		mov	es:[di].TScanHeader.XValue, ax		; store x value
		shl	bx, 2					; BX offset of head entry
		mov	ax, di
		xchg	ax, es:[bx][si+TYPE TScanHeader].TScanEntry.Offs	; get first offset
		mov	es:[di].TScanEntry.Offs, ax
		add	di, 4
		mov	es:[si].TScanHeader.FreeEntry, di
		shr	bx, 2
@@0:		retn
scanInsert	ENDP

	; Draw a line of fixed-coord points and update ScanEntry stream
	;
	; ES:SI	ScanHeader plus ScanEntry stream
	; EBX	x1.fixed
	; EAX	x2.fixed
	; EDX	y1.fixed
	; ECX	y2.fixed

; y: pixel 2 <==> line 2.5 ???

 ; vertikale Spitze....  ---- verursacht vermutlich die Restfehler

scanDrawLine	PROC NEAR
		LOCAL	deltaX: DWORD, deltaY: DWORD, Delta: DWORD, \
			Dir: WORD, CurX: DWORD, Count: WORD = LocalSize
		enter	LocalSize, 0

		call	SetupLine
		mov	eax, ebx		; EAX x1
		push	dx ecx		; +++
		call	NextLine
		mov	CurX, eax
		sar	edx, 16
		mov	Count, dx
		pop	edx		;  -- 	y2
		call	NextLine
		sar	edx, 16
		mov	bx, Count		; BX y start
		sub	dx, bx
		jz	SHORT @@1

	; normal case: line count > 0

		iabs	dx
		mov	Count, dx
		pop	ax		; -
		mov	eax, CurX
		call	AddRound
		mov	cx, Dir
@@2:
		mov	CurX, eax
;		roundf	eax
		sar	eax, 16
		push	bx		; +
		call	scanInsert
		pop	bx		; -
		mov	eax, CurX
		add	eax, Delta
		add	bx, cx			; BX := BX + Dir
		dec	Count
		jnz     @@2

@@0:		leave
		ret

	; special case: line count = 0

@@1:		pop	ax		; -
		or	ax, ax
		jnz	@@0			; not on scan line

	; on scan line

		cmp	DeltaX, 0
		mov	cx, -1			; on transition
		jl	SHORT @@3
		mov	cx, +1			; x2 < x1: off transition
@@3:
		mov	eax, CurX
		call	AddRound
		sar	eax, 16
;		roundf	eax
		call	scanInsert

		leave
		ret
scanDrawLine	ENDP

	; SetupLine calcs values on the stack frame
	;
	; IN 	ebx	x1.fixed	OUT     deltaX	x2 - x1 (signed)
	; 	edx	y1.fixed                Dir	Y increment (integer)
	; 	eax	x2.fixed
	; 	ecx	y2.fixed		deltaY	y2 - y1 (signed)
	;

SetupLine	PROC	NEAR

		cmp	ecx, edx
		mov	Dir, +1
		jge	SHORT @@3

		mov	Dir, -1
@@3:
		push	eax ecx
		sub	eax, ebx		; EAX (delta x).fixed
		mov	deltaX, eax
		sub	ecx, edx		; ECX (delta y).fixed
		mov	deltaY, ecx
		pop	ecx eax

		retn

SetupLine	ENDP

	; AddRound adds 7FFFH (on) or 8000H (off) to EAX
	;

AddRound        PROC NEAR

		cmp	Dir, +1			; on ?
		jnz	SHORT @@1
		add	eax, 7FFFh
		retn
@@1:
		add	eax, 8000h
		retn

AddRound	ENDP

	; CalcDelta calcs the line Delta = deltaX / |deltaY|
	;
	; no calc if |deltaY| < 1.0, CF set
	; OUT	ecx	|deltaY|
	;	eax	Delta on CF clear, saved on CF set
	;

CalcDelta	PROC NEAR
		mov	ecx, deltaY
		iabs	ecx
		cmp	ecx, 10000h
		jb	SHORT @@0

		mov	eax, deltaX
		divf	ecx
		mov	Delta, eax
		clc
@@0:
		retn
CalcDelta	ENDP

	; MultDelta mults eax,fixed * (dx / |dy|)
	;

MultDelta	PROC NEAR
		push	eax		; ++
		call	CalcDelta
		jc	SHORT @@1

	; Delta calculated: mult the former eax

		pop	ecx		; --
		mulf	ecx
		retn

	; Delta not calced: calc (eax * dx) / |dy|
@@1:
		pop	eax		; --
comment *
		or	ecx, ecx
		jz	SHORT @@2
		imul	deltaX
		idiv	ecx
		retn
*

	; dy = 0: return 0
@@2:
		xor	eax, eax
		retn
MultDelta	ENDP

	; NextLine skips to the next scan line, according to direction.
	; if on scan line, no move.
	;
	; IN	eax	X,fixed		OUT	eax	X,fixed
	; 	edx	Y,fixed		   	edx	Y,fixed

NextLine	PROC NEAR

		or	dx, dx			; frac(y) = 0 ?
		jz	SHORT @@0

		push	edx eax		; ++++
		xor	eax, eax
		mov	ax, dx			; eax frac(y)

		cmp	Dir, +1
		jz	SHORT @@1

	; down: add frac * Delta

		call	MultDelta
		pop	ecx		;   --
		add	eax, ecx
		pop	edx		; --
		xor	dx, dx
		retn

	; up: add (1-frac) * Delta

@@1:
		neg	eax
		add	eax, 10000H
		call	MultDelta
		pop	ecx		;   --
		add	eax, ecx
		pop	edx		; --
		add	edx, 10000H
		xor	dx, dx
		retn

	; no move: calc Delta

@@0:
		push	edx eax		; ++++
		call	CalcDelta
		pop	eax edx		; ----
		retn

NextLine	ENDP


; *************************************************************************


	; Find minimal entry. Remove this entry.
	;
	; In:	ES:SI	start of line
	; Out:  CF	set on EOL
	;	ZF	transition type (Z on, NZ off)
	;	CX	minimum co-ordinate
	;	Regs	destroyed

scanFindMin	PROC NEAR
		mov	cx, 7FFFh			; current minimum
		mov	bx, 0				; before min entry
		jmp	SHORT @@1
@@2:
		mov	ax, es:[si].TScanEntry.XValue
		push	ax cx
		and	ah, 7Fh
		and 	ch, 7Fh
		cmp	ax, cx
		pop	cx ax
		jg	SHORT @@1

		jl	SHORT @@3

		test	ah, 80h
		jz	@@1
@@3:
		mov	bx, dx
		mov	cx, ax
@@1:
		mov	dx, si				; before entry
	;	and	ch, 7Fh
		mov	si, es:[si].TScanEntry.Offs
		or	si, si
		jne	@@2
		or	bx, bx
		stc
		jz	SHORT @@0
		and	cx, 7FFFH
		mov	si, es:[bx].TScanEntry.Offs	; SI offset min entry
		test	es:[si].TScanEntry.XValue, 8000H
		mov	si, es:[si].TScanEntry.Offs	; SI offset next entry
		mov	es:[bx].TScanEntry.Offs, si
@@0:
		ret
scanFindMin	ENDP

	; scanPutPixel puts a pixel at (ax,cx), colored bx
	; es:si	Buffer (for coord trans)

scanPutPixel	PROC NEAR
		sub	cx, es:[si].TScanHeader.TopClip
		neg	cx
		add	cx, es:[si].TScanHeader.TopBearing
		add	ax, es:[si].TScanHeader.LeftAdd
		push	ax cx bx
		call	PutPixel
		ret
scanPutPixel	ENDP

	;
	; Pascal procedures
	;

scanSetupP	PROC NEAR
		ARG	aDoLine: WORD, aBitmap: DWORD, aTopBearing: WORD, \
			aLeftAdd: WORD, aBottomClip: WORD, aTopClip: WORD, \
			aBuffer: DWORD = ArgSize
		push	bp
		mov	bp, sp
		les	si, aBuffer

		mov	ax, aDoLine
		mov	es:[si].TScanHeader.DoLine, ax
		mov	eax, aBitmap
		mov	es:[si].TScanHeader.Bitmap, eax
		mov	ax, aTopBearing
		mov	es:[si].TScanHeader.TopBearing, ax

		mov	ax, aTopClip
		mov	bx, aBottomClip
		mov	cx, aLeftAdd

		mov	es:[si].TScanHeader.TopClip, ax
		mov	es:[si].TScanHeader.BottomClip, bx
		mov	es:[si].TScanHeader.LeftAdd, cx
		sub	ax, bx

	; init scan-line headers

		mov	cx, ax
		mov	di, si
		add	di, TYPE TScanHeader
		xor	eax, eax
		cld
		rep	stosd

	; set FreeEntry value

		mov	es:[si].TScanHeader.FreeEntry, di

		pop	bp
		ret	ArgSize
scanSetupP	ENDP

scanDrawLineP	PROC NEAR
		ARG     y2: DWORD, x2: DWORD, \
			y1: DWORD, x1: DWORD, \
			Buffer: DWORD = ArgSize
		push	bp
		mov	bp, sp
		les	si, Buffer
		mov	eax, x2
		mov	ebx, x1
		mov	ecx, y2
		mov	edx, y1
		call	scanDrawLine
		pop	bp
		ret	ArgSize
scanDrawLineP	ENDP

scanOutBufP	PROC NEAR
		ARG     Buffer: DWORD = ArgSize
		push	bp
		mov	bp, sp
		les	si, Buffer
		mov	ax, es:[si].TScanHeader.BottomClip
		mov	cx, es:[si].TScanHeader.TopClip
		sub	cx, ax				; count
		add	si, TYPE TScanHeader

@@1:		push	si cx

@@2:		push	es
		pusha
		mov	ax, es:[si].TScanEntry.XValue
		test	ah, 80H
		jz	SHORT @@3
		and	ah, 7Fh
		push	ax
		push	cx
		push	2
		jmp	SHORT @@4
@@3:		push	ax
		push	cx
		push	12
@@4:		call	PutPixel
		popa
		pop	es
		mov	si, es:[si].TScanEntry.Offs
		or	si, si
		jnz	@@2

		pop	cx si
		add	si, TYPE TScanEntry
		loop	@@1

		pop	bp
		ret
scanOutBufP	ENDP

AddZ		MACRO	Value
		LOCAL	@@0, @@1
		jz	SHORT @@1

		inc	Value			; off transition: -1
		jmp	SHORT @@0
@@1:
		dec	Value			; on transition: +1
@@0:
		ENDM

scanConvBufP	PROC NEAR
		ARG     Buffer: DWORD = ArgSize
		LOCAL	Winding: WORD, Current: WORD, ABitmap: DWORD, \
			LineProc: WORD, ATop: WORD = LocalSize
		enter	LocalSize, 0
		les	si, Buffer
		mov	eax, es:[si].TScanHeader.Bitmap
		mov	ABitmap, eax
		mov	ax, es:[si].TScanHeader.DoLine
		mov	LineProc, ax
		mov	ax, es:[si].TScanHeader.TopBearing
		mov	ATop, ax
		mov	ax, es:[si].TScanHeader.BottomClip
		mov	cx, es:[si].TScanHeader.TopClip
		sub	cx, ax				; count
		add	si, TYPE TScanHeader

@@1:
		push	si cx
		mov	Winding, 0

	; Winding is zero > read entry

@@6:
		call	scanFindMin
		jc	SHORT @@5

		addz	Winding
		mov	Current, cx

		pop	cx si
		push	si cx

	; Winding is nonzero > wait for zero

@@2:
		push	si
		call	scanFindMin
		pop	si
		jc	SHORT @@5

		addz	Winding
		jnz	@@2

		pop	ax si
		push	si ax
		dec	cx			; off position
		cmp	cx, Current
		jl	@@6

		push	es
		pusha

		add	ax, ATop
		push	ABitmap
		push	Current cx ax
		call	LineProc

		popa
		pop	es
		jmp	@@6
@@5:
		pop	cx si
		add	si, TYPE TScanEntry
		loop	@@1

		leave
		ret	ArgSize
scanConvBufP	ENDP

scanPutPixelP	PROC NEAR
		ARG	Color: WORD, y: WORD, x: WORD = ArgSize
		push	bp
		mov	bp, sp
		mov	ax, x
		mov	cx, y
		mov	bx, Color
		call	scanPutPixel
		pop	bp
		ret	ArgSize
scanPutPixelP	ENDP

scanDoLine	PROC NEAR
		ARG	y: WORD, x2: WORD, x1: WORD, ABitmap: DWORD = ArgSize
		push	bp
		mov	bp, sp
		push	x1 y x2 y
		call	Line
		pop	bp
		ret	ArgSize
scanDoLine	ENDP

CODE		ENDS

		END
