; Copyright 1994 by Matthias K”ppe

IDEAL
%TITLE	"TrueType glyph parser"
P386

MASM
LOCALS	@@

pushq	MACRO	Source
	push	DWORD PTR (Source+4)
	push	DWORD PTR Source
ENDM

popq	MACRO	Dest
	pop	DWORD PTR Dest
	pop	DWORD PTR (Dest+4)
ENDM

		INCLUDE	fixedop.asm

		EXTRN	PutPixel: FAR

DATA		SEGMENT USE16 BYTE PUBLIC

		EXTRN	SplineDeltaU: DWORD
		EXTRN	SplineDense: WORD

TTypeScanBuf	DD	?
TTypeLastPoint	DQ	?

DATA		ENDS

CODE		SEGMENT USE16 BYTE PUBLIC

		EXTRN	Spline: NEAR
		PUBLIC	LineTo

		ASSUME	CS:CODE, DS:DATA

	; Publics

		PUBLIC	OutPolygon, OutPolygonPts

	; Externals

		EXTRN	scanDrawLine: NEAR, scanPutPixel: NEAR

; FixedIntRec returns 1 / AX.int in DX.AX

FixedIntRec:	mov	dx, 1
		cmp	ax, dx
		mov	cx, 0
		xchg	ax, cx
		jz	@@1
		div	cx
		mov	dx, 0
@@1:		retn

	; Co-ordinate structure

xFract		EQU 	(WORD PTR 0)
xValue		EQU	(WORD PTR 2)
xCoord		EQU	(DWORD PTR 0)
yFract		EQU	(WORD PTR 4)
yValue		EQU	(WORD PTR 6)
yCoord		EQU	(DWORD PTR 4)

	; Delta to same co-ordinate of next point

CoordDelta	EQU	8

	; Constants

tt_Polygon_Type	EQU	24
tt_Prim_Line	EQU	1
tt_Prim_QSPLine	EQU	2

; DeAlign macro de-aligns a fixed number, adding 1/65536 to integer values.
;

DeAlign		MACRO	Fract
		LOCAL	@@1
		or	Fract, Fract
		jnz	SHORT @@1
		add	Fract, 32
@@1:
		ENDM


; OutPolygon draws the t'type polygons. Dense should be chosen appropriately
; to the glyph size.

		; Draws the polygon.

OutPolygon	PROC NEAR
		ARG	ScanBuf: DWORD, Dense: WORD, \
			SizeS: WORD, Struct: DWORD = ArgSize
		LOCAL	StructEnd: WORD, PolyEnd: WORD, \
			du: DWORD, temp: WORD, PointCount: WORD, \
			FirstPoint: QWORD = LocalSize

		enter	LocalSize, 0

		mov	ax, dense
		mov	SplineDense, ax
		call	FixedIntRec		; 1/dense
		mov	WORD PTR du, ax
		mov     WORD PTR du + 2, dx
		mov	WORD PTR SplineDeltaU, ax
		mov	WORD PTR SplineDeltaU + 2, dx

		mov	eax, ScanBuf
		mov	TTypeScanBuf, eax

		les	di, Struct

		mov	ax, SizeS		; buffer size
		add	ax, di
		mov	StructEnd, ax		; store ofs struct end

@@1:		mov	ax, es:[di]		; polygon size
		add	ax, di
		mov	PolyEnd, ax		; store ofs polygon end

		add	di, 8
		mov	edx, es:[di].xCoord
		mov	ecx, es:[di].yCoord
		DeAlign	cx
		mov     TTypeLastPoint.xCoord, edx
		mov	TTypeLastPoint.yCoord, ecx
		mov	FirstPoint.xCoord, edx
		mov	FirstPoint.yCoord, ecx
		add	di, 8

@@2:		mov	ax, es:[di]
		cmp	ax, tt_prim_line
		jz	SHORT @@4
		cmp	ax, tt_prim_qspline
		jz	@@3
		mov	di, PolyEnd		; unknown -- skip polygon
@@2a:
		push	es di
		mov	eax, FirstPoint.xCoord
		mov	ecx, FirstPoint.yCoord
		call	LineTo
		pop	di es
		cmp	di, StructEnd
		jnz	@@1			; next polygon
		jmp	@@6			; glyph drawn

	; line

@@4:		mov	cx, es:[di+2]		; point count
		add	di, 4

@@4a:		push	cx
		call	DrawLine
		pop	cx
		add	di, 8
		loop	@@4a

@@5:		cmp	di, PolyEnd
		jz	@@2a
		jmp	@@2

	; b-spline

@@3:		mov	cx, es:[di+2]		; coeff point count
		add	di, 4
		or	cx, cx
		jz	SHORT @@5		; zero: no curve
		cmp	cx, 1
		jz	@@4a			; straight line

	; curve (de Casteljau)

		pushq	TTypeLastPoint		; QWORD Last
		push	es di			; DWORD CoordPtr
		push	cx			; WORD  Count
		call	Spline
		pop	di ax			; DWORD CoordPtr
		add	sp, 8
	;	popq	TTypeLastPoint

	;

		jmp	@@5

@@6:		leave
		ret	ArgSize
OutPolygon	ENDP

OutPolygonPts	PROC NEAR
		ARG	ScanBuf: DWORD, Dense: WORD, \
			SizeS: WORD, Struct: DWORD = ArgSize
		LOCAL	StructEnd: WORD, PolyEnd: WORD = LocalSize

		enter	LocalSize, 0

		les	di, Struct
		mov	ax, SizeS		; buffer size
		add	ax, di
		mov	StructEnd, ax		; store ofs struct end

@@1:		mov	ax, es:[di]		; polygon size
		add	ax, di
		mov	PolyEnd, ax		; store ofs polygon end

		add	di, 8
		mov	bx, 0
		call	DrawPoint
		add	di, 8

@@2:		mov	ax, es:[di]
		cmp	ax, tt_prim_line
		jz	SHORT @@4
		cmp	ax, tt_prim_qspline
		jz	SHORT @@4
		mov	di, PolyEnd		; unknown -- skip polygon
@@2a:
		cmp	di, StructEnd
		jnz	@@1			; next polygon
		jmp	@@6			; glyph drawn

@@4:		mov	cx, es:[di+2]		; point count
		add	di, 4

@@4a:		push	cx
		mov	bx, 0
		call	DrawPoint
		pop	cx
		add	di, 8
		loop	@@4a

@@5:		cmp	di, PolyEnd
		jz	@@2a
		jmp	@@2

@@6:		leave
		ret	ArgSize


OutPolygonPts	ENDP

	; LineTo draws a line to (eax, ecx).
	;

LineTo		PROC NEAR
		les     si, TTypeScanBuf		; ES:SI scan buffer
		DeAlign	cx
		mov	edx, ecx
		mov	ebx, eax
		xchg	edx, TTypeLastPoint.yCoord	; EDX y1.fixed
		xchg	ebx, TTypeLastPoint.xCoord	; EBX x1.fixed
		jmp	ScanDrawLine
LineTo		ENDP

	; DrawSegment draws one B-spline segment
	;

DrawSegment	PROC NEAR
		ARG	deBoorProc: WORD = ArgSize \
			RETURNS count: WORD, du: DWORD
		LOCAL	ru: DWORD, temp: DWORD = LocalSize

		enter	LocalSize, 0

		push	count
		mov	eax, du
		mov	ru, eax
		dec	count
		jz	SHORT @@2
@@1:		push	ru

		call	deBoorProc
		mov	temp, eax
		add	di, 4			; -> y
		call	deBoorProc
		sub	di, 4			; -> x
		push	es di
		mov	ecx, eax		; ECX y2.fixed
		mov	eax, temp		; EAX x2.fixed
		push	bp
		mov	bp, [bp]
		call	LineTo
		pop	bp di es

		add	sp, 4
		mov	eax, du
		add	ru, eax
		dec	count
		jnz	@@1

@@2:		pop	count
		leave
		ret	ArgSize
DrawSegment	ENDP

	; DrawLine draws a straight line to the point at es:di

DrawLine	PROC NEAR
		push	es di
		mov	eax, es:[di].xCoord
		mov	ecx, es:[di].yCoord
		call	LineTo
		pop	di es
		ret
DrawLine	ENDP

	; DrawAt sets a pixel at the point at es:di, colored bx.
	;

DrawPoint	PROC NEAR
		push	es di
		mov	eax, es:[di].xCoord
		mov	ecx, es:[di].yCoord
		roundf	eax
		roundf	ecx
		les	si, ScanBuf
		call	scanPutPixel
		pop	di es
		ret
DrawPoint	ENDP

CODE		ENDS

		END
