$NOLIST

NAME  WindowProcs

$INCLUDE (WinCnsts~Inc~)

CGROUP GROUP CODE

; This module contains routines that affect the display in some way visually.

; NOTE: These multi-pixel frames are drawn outside of the rectangle

PUBLIC WinDrawRectangleFrame, WinEraseRectangleFrame
PUBLIC WinInvertRectangleFrame, WinPatternRectangleFrame

PUBLIC WinTestPixel
PUBLIC WinDrawPixel, WinErasePixel, WinInvertPixel, WinPatternPixel
PUBLIC WinDrawLine, WinEraseLine, WinInvertLine, WinPatternLine
PUBLIC WinDrawRectangle, WinEraseRectangle, WinInvertRectangle, WinPatternRectangle


; These routines are used internally by MiniGRiD, but are not exported
; beyond that because all you can do is draw very small circles.

PUBLIC WinDrawCircle, WinEraseCircle, WinInvertCircle, WinPatternCircle

; NOTE: These single pixel frames are drawn inside of the rectangle

PUBLIC WinDrawCircleFrame, WinEraseCircleFrame
PUBLIC WinInvertCircleFrame, WinPatternCircleFrame


EXTRN  drawWindow: WORD
EXTRN  currOrientation: BYTE, currPtnXferMode: BYTE, currPattern: BYTE
EXTRN  cursorOffCount: WORD

EXTRN  Encode: NEAR, ClipLine: NEAR, WinClipRectangle: NEAR
EXTRN  CsrMaybeTurnCursorOff: NEAR, CsrTurnCursorOn: NEAR

EXTRN  GfxTestPixel: FAR, GfxLineRoutine: FAR, GfxHLineRoutine: FAR


CODE SEGMENT  PUBLIC 'CODE'
	ASSUME CS:CGROUP

maxCircleDiam EQU 38

circleOffsets LABEL BYTE
	DB   5,   5,   4,   9,   9,   3,   8,   8,   2,  11
	DB   7,  15,  15,   0,  20,  20,  26,  33,  40,  48
	DB  56,  64,  73,  82,  92, 102, 113, 124, 135, 147
	DB 159, 159, 172, 186, 200, 214, 229, 244, 244

circleData LABEL BYTE
	DB 4, 3, 2, 1, 0, -1
	DB 4, 3, 2, 1, -1
	DB 3, 1, 1, -1
	DB 4, 2, 1, 1, -1
	DB 5, 3, 2, 1, 1, -1
	DB 6, 4, 2, 2, 1, 1, -1
	DB 6, 4, 3, 2, 1, 1, -1
	DB 6, 4, 3, 2, 1, 1, 0, -1
	DB 7, 5, 3, 2, 2, 1, 1, -1
	DB 7, 5, 4, 3, 2, 1, 1, -1
	DB 8, 5, 4, 3, 2, 1, 1, 1, -1
	DB 8, 6, 4, 3, 2, 2, 1, 1, -1
	DB 9, 6, 5, 4, 3, 2, 1, 1, 1, -1
	DB 9, 7, 5, 4, 3, 2, 2, 1, 1, -1
	DB 9, 7, 5, 4, 3, 2, 2, 1, 1, 0, -1
	DB 10, 7, 6, 4, 3, 3, 2, 1, 1, 1, -1
	DB 10, 8, 6, 5, 4, 3, 2, 2, 1, 1, -1
	DB 11, 8, 6, 5, 4, 3, 2, 2, 1, 1, 1, -1
	DB 11, 8, 7, 5, 4, 3, 3, 2, 1, 1, 1, -1
	DB 12, 9, 7, 6, 5, 4, 3, 2, 2, 1, 1, 1, -1
	DB 12, 10, 8, 6, 5, 4, 3, 3, 2, 2, 1, 1, 0, -1
	DB 13, 10, 8, 7, 5, 4, 4, 3, 2, 2, 1, 1, 1, -1
	DB 13, 10, 8, 7, 6, 5, 4, 3, 2, 2, 1, 1, 1, -1
	DB 14, 11, 9, 7, 6, 5, 4, 3, 3, 2, 2, 1, 1, 1, -1
	DB 14, 11, 9, 8, 6, 5, 4, 4, 3, 2, 2, 1, 1, 1, -1
	DB 15, 12, 10, 8, 7, 6, 5, 4, 3, 3, 2, 2, 1, 1, 1, -1
$EJECT

; PROCEDURE Win***RectangleFrame
;  (frameType: Word; VAR rect: Rectangle; cornerDiam: Integer);

frameType    EQU  WORD PTR [BP+12]
pRect        EQU DWORD PTR [BP+8]
cornerDiam   EQU  WORD PTR [BP+6]

xferMode     EQU  BYTE PTR [BP-2]
saveXferMode EQU  BYTE PTR [BP-3]
rExtentY     EQU  WORD PTR [BP-4]
rExtentX     EQU  WORD PTR [BP-6]
rTopLftY     EQU  WORD PTR [BP-8]
rTopLftX     EQU  WORD PTR [BP-10]
frameRtn     EQU  WORD PTR [BP-12]
bottomEdge   EQU  WORD PTR [BP-14]
rightEdge    EQU  WORD PTR [BP-16]
topEdge      EQU  WORD PTR [BP-18]
leftEdge     EQU  WORD PTR [BP-20]

WinDrawRectangleFrame PROC NEAR
	MOV	AL, drawVerb
	JMP	SHORT WinRectangleFrameRoutine

WinEraseRectangleFrame LABEL NEAR
	MOV	AL, eraseVerb
	JMP	SHORT WinRectangleFrameRoutine

WinInvertRectangleFrame LABEL NEAR
	MOV	AL, invertVerb
	JMP	SHORT WinRectangleFrameRoutine

WinPatternRectangleFrame LABEL NEAR
	MOV	AL, CS:currPtnXferMode
	OR	AL, ptnVerbsBit

WinRectangleFrameRoutine:
	PUSH	DS
	PUSH	BP
	MOV	BP, SP
	SUB	SP, 20	; Leave room for local variables

	MOV	xferMode, AL	; Save xfer mode as local variable

	LDS	SI, pRect
	PUSH	SS
	POP	ES
	LEA	DI, rTopLftX
	CLD
	MOVSW
	MOVSW
	MOVSW
	MOVSW	; Copy the rectangle to a local on the stack

	MOV	AX, OFFSET FrameSquaredRect
	MOV	BX, OFFSET FrameRoundedRect
	CMP	cornerDiam, 0
	JE	StoreFrameCallRoutine

	XCHG	AX, BX	; Make FrameRoundedRect be the call routine

StoreFrameCallRoutine:
	MOV	frameRtn, AX

InnerFrame:
	MOV	CX, frameType
	AND	CX, 00000011b
	JZ	SpacingFrame

InnerFrameLoop:
	MOV	AX, 1	; Outset amount is 1
	SUB	rTopLftX, AX	; rect.topLeft.x := rect.topLeft.x - 1
	SUB	rTopLftY, AX	; rect.topLeft.y := rect.topLeft.y - 1
	INC	AX	; AX now is 2
	ADD	rExtentX, AX 	; rect.extent.x := rect.extent.x + 2
	ADD	rExtentY, AX	; rect.extent.y := rect.extent.y + 2

	MOV	DS, CX	; Save count in DS
	CALL	frameRtn
	MOV	CX, DS	; Restore count from DS
	LOOP	InnerFrameLoop

SpacingFrame:
	MOV	CX, frameType
	AND	CX, 00001100b
	JZ	OuterFrame

	MOV	AL, xferMode
	MOV	saveXferMode, AL
	MOV	xferMode, eraseVerb	; Always erase the spaces between frames

SpacingFrameLoop:
	MOV	AX, 1	; Outset amount is 1
	SUB	rTopLftX, AX	; rect.topLeft.x := rect.topLeft.x - 1
	SUB	rTopLftY, AX	; rect.topLeft.y := rect.topLeft.y - 1
	INC	AX	; AX now is 2
	ADD	rExtentX, AX 	; rect.extent.x := rect.extent.x + 2
	ADD	rExtentY, AX	; rect.extent.y := rect.extent.y + 2

	MOV	DS, CX	; Save count in DS
	CALL	frameRtn
	MOV	CX, DS	; Restore count from DS
	SUB	CX, 100b
	JNZ	SpacingFrameLoop

	MOV	AL, saveXferMode
	MOV	xferMode, AL	; Restore mode for outer frame and shadow

OuterFrame:
	MOV	CX, frameType
	AND	CX, 00110000b
	JZ	Extra3DFrame

OuterFrameLoop:
	MOV	AX, 1	; Outset amount is 1
	SUB	rTopLftX, AX	; rect.topLeft.x := rect.topLeft.x - 1
	SUB	rTopLftY, AX	; rect.topLeft.y := rect.topLeft.y - 1
	INC	AX	; AX now is 2
	ADD	rExtentX, AX 	; rect.extent.x := rect.extent.x + 2
	ADD	rExtentY, AX	; rect.extent.y := rect.extent.y + 2

	MOV	DS, CX	; Save count in DS
	CALL	frameRtn
	MOV	CX, DS	; Restore count from DS
	SUB	CX, 10000b
	JNZ	OuterFrameLoop

Extra3DFrame:
	MOV	CX, frameType
	MOV	BL, CH	; Save shadow corner in BL
	SHL	CX, 1
	SHL	CX, 1
	XCHG	CH, CL
	AND	CX, 3	; If there is no shadow on this frame
	JZ	WinRectangleFrameRet	; then return

	MOV	DS, rExtentY	; Save rectangles extent y in DS for now
	MOV	rExtentY, CX	; Make the extent y be the shadow height

	MOV	AX, CX	; Move shadow right by shadow width for east
	TEST	BL, 01b	; If eastern shadow
	JZ	SetTopShadowX	; then value is ready

	NEG	AX	; Move shadow to left for western shadow

SetTopShadowX:
	ADD	rTopLftX, AX	; Move shadow topLeft east/west (left/right)

	MOV	AX, CX	; Move shadow up by shadow width
	NEG	AX	; if northern shadow
	TEST	BL, 10b	; If northern shadow
	JZ	SetTopShadowY	; then value is ready

	MOV	AX, DS	; Move shadow down rectangle height for south

SetTopShadowY:
	ADD	rTopLftY, AX	; Move shadow topLeft north/south (up/down)

	PUSH	CX
	PUSH	BX

	LEA	AX, rTopLftX
	PUSH	SS
	PUSH	AX
	XOR	AX, AX	; Rectangle corner is 0 (squared rectangle)
	PUSH	AX
	MOV	AL, xferMode
	CALL	WinRectRtnCursorMayOverlap

	POP	BX
	POP	CX

	MOV	AX, DS	; origin rect.extent.y
	SUB	AX, CX	; don't redraw overlapping part
	MOV	rExtentY, AX
	MOV	DS, rExtentX	; Now DS is saving original rExtentX
	MOV	rExtentX, CX

	TEST	BL, 01b	; If western shadow
	JNZ	CheckSideShadowY	; then topLeft x value is okay

	MOV	AX, DS	; original extent x
	SUB	AX, CX	; less width of shadow
	ADD	rTopLftX, AX	; Move shadow topLeft east/west (left/right)

CheckSideShadowY:
	MOV	AX, CX	; Move shadow down by shadow width if northern
	TEST	BL, 10b	; If northern shadow
	JZ	SetSideShadowY	; then value is ready

	MOV	AX, rExtentY	; Move shadow down rectangle height for south
	NEG	AX

SetSideShadowY:
	ADD	rTopLftY, AX	; Move shadow topLeft north/south (up/down)

	LEA	AX, rTopLftX
	PUSH	SS
	PUSH	AX
	XOR	AX, AX	; Rectangle corner is 0 (squared rectangle)
	PUSH	AX
	MOV	AL, xferMode
	CALL	WinRectRtnCursorMayOverlap

WinRectangleFrameRet:
	MOV	SP, BP
	POP	BP
	POP	DS
	RET	8
WinDrawRectangleFrame ENDP
$EJECT

; Entry
;   BP => local variables of Win***RectangleFrame

FrameSquaredRect PROC NEAR
	MOV	BX, rExtentX
	MOV	CX, rExtentY
	OR	BX, BX
	JLE	FrameSquaredRectRet
	OR	CX, CX
	JLE	FrameSquaredRectRet

	MOV	AX, rTopLftX
	MOV	leftEdge, AX	; left  := r.topleft.x;
	ADD	AX, BX
	DEC	AX
	MOV	rightEdge, AX	; right := r.topleft.x + r.extent.x - 1;

	MOV	AX, rTopLftY
	MOV	topEdge, AX	; top    := r.topleft.y;
	ADD	AX, CX
	DEC	AX
	MOV	bottomEdge, AX	; bottom := r.topleft.y + r.extent.y - 1;

	PUSH	CX	; save extent y
	MOV	AL, xferMode	; Win...Line
	PUSH	leftEdge	;  (leftEdge,
	PUSH	topEdge	;   topEdge.
	PUSH	rightEdge	;   right,
	PUSH	topEdge	;   topEdge);
	CALL	WinLineRtnCursorMayOverlap
	POP	CX	; restore extent y

	DEC	CX	; If y extent of rectangle is only one
	JZ	FrameSquaredRectRet	; then don't draw "bottom" line

	PUSH	CX	; save extent y
	MOV	AL, xferMode	; Win...Line
	PUSH	leftEdge	;  (leftEdge,
	PUSH	bottomEdge	;   bottom,
	PUSH	rightEdge	;   right,
	PUSH	bottomEdge	;   bottom);
	CALL	WinLineRtnCursorMayOverlap
	POP	CX	; restore extent y

	DEC	CX	; if y extent of rectangle was only two
	JZ	FrameSquaredRectRet	; then don't draw "sides" of the frame

	INC	topEdge	; topEdge := topEdge + 1
	DEC	bottomEdge	; bottom := bottom - 1

	MOV	AL, xferMode	; Win...Line
	PUSH	leftEdge	;  (leftEdge,
	PUSH	topEdge	;   topEdge + 1
	PUSH	leftEdge	;   leftEdge,
	PUSH	bottomEdge	;   bottom-1);
	CALL	WinLineRtnCursorMayOverlap

	MOV	AL, xferMode	; Win...Line
	PUSH	rightEdge	;  (right,
	PUSH	topEdge	;   topEdge+1
	PUSH	rightEdge	;   right,
	PUSH	bottomEdge	;   bottom-1);
	CALL	WinLineRtnCursorMayOverlap

FrameSquaredRectRet:
	RET
FrameSquaredRect ENDP
$EJECT

; Entry
;   BP => local variables of Win***RectangleFrame

FrameRoundedRect PROC NEAR
	PUSH	rTopLftX	; topLeftX of rounded rectangle
	PUSH	rTopLftY	; topLeftY of rounded rectangle
	MOV	CX, cornerDiam
	INC	CX
	INC	CX	; diameter increases by 2 when rect outsets 1
	MOV	cornerDiam, CX
	PUSH	CX	; diameter of curvature of rect corner
	MOV	DX, rExtentX
	SUB	DX, CX
	PUSH	DX	; extraX = extentX - diameter
	MOV	DX, rExtentY
	SUB	DX, CX
	PUSH	DX	; extraY = extentY - diameter
	MOV	CL, 0
	PUSH	CX	; fill = FALSE
	MOV	AL, xferMode
	PUSH	AX	; mode
	CALL	CircleRoutine	; (topleft.x, topleft.y, diameter, 0, 0);
	RET
FrameRoundedRect ENDP

PURGE frameType, pRect, cornerDiam, xferMode, saveXferMode
PURGE rTopLftX, rTopLftY, rExtentX, rExtentY, frameRtn
PURGE leftEdge, topEdge, rightEdge, bottomEdge
$EJECT

;***************************************************
;*                                                 *
;* PROCEDURE WinTestPixel(x, y: Integer): Boolean; *
;*                                                 *
;***************************************************

xPixel EQU WORD PTR [BP+08]
yPixel EQU WORD PTR [BP+06]

WinTestPixel PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP

	MOV	DS, CS:drawWindow

	MOV	CX, xPixel
	MOV	DX, yPixel
	CALL	Encode
	OR	AX, AX
	MOV	AL, 0	; Return FALSE if outside of window
	JNZ	WinTestPixelRet

	MOV	SI, CX	; Right = Left for a point
	MOV	DI, DX	; Bottom = Top for a point
	CALL	CsrMaybeTurnCursorOff	; Should only be turned off if it overlaps
	PUSH	AX

	PUSH	DS:wiDisplayAddr	; display location
	PUSH	DS:wiDisplayWidth	; display width
	PUSH	DS:wiDisplayHeight	; display height
	MOV	CX, xPixel
	ADD	CX, DS:wiBoundsTopLeftX
	PUSH	CX
	MOV	DX, yPixel
	ADD	DX, DS:wiBoundsTopLeftY
	PUSH	DX
	CALL	GfxTestPixel	; Returns "state" of pixel - on or off

WinTestPixelExit:
	POP	BX	; If the cursor did not have to be turned
	OR	BL, BL	; off to look at the pixel location
	JZ	WinTestPixelRet	; then don't have to call TurnCursorOn

	PUSH	AX	; Save state of pixel location
	CALL	CsrTurnCursorOn	; Only turn back on if it was turned off
	POP	AX	; Restore state of pixel location

WinTestPixelRet:
	POP	BP
	POP	DS
	RET	4
WinTestPixel ENDP

PURGE xPixel, yPixel
$EJECT

;*****************************************
;*                                       *
;* PROCEDURE Win___Pixel(x, y: Integer); *
;*                                       *
;*****************************************

WinDrawPixel PROC NEAR
	MOV	AL, drawVerb
	JMP	SHORT Win___Pixel

WinErasePixel LABEL NEAR
	MOV	AL, eraseVerb
	JMP	SHORT Win___Pixel

WinInvertPixel LABEL NEAR
	MOV	AL, invertVerb
	JMP	SHORT Win___Pixel

WinPatternPixel LABEL NEAR
	MOV	AL, CS:currPtnXferMode
	OR	AL, ptnVerbsBit

Win___Pixel:
	POP	SI	; Return IP
	POP	BX	; y pixel
	POP	DX	; x pixel
	PUSH	DX	; x1 line
	PUSH	BX	; y1 line
	PUSH	DX	; x2 line
	PUSH	BX	; y2 line
	PUSH	SI	; Return IP
	JMP	SHORT WinLineRtnCursorMayOverlap
WinDrawPixel  ENDP
$EJECT

;***************************************************
;*                                                 *
;* PROCEDURE Win___Line (x1, y1, x2, y2: Integer); *
;*                                                 *
;***************************************************

lineX1       EQU WORD PTR [BP+12]
lineY1       EQU WORD PTR [BP+10]
lineX2       EQU WORD PTR [BP+08]
lineY2       EQU WORD PTR [BP+06]

grafVerb     EQU WORD PTR [BP-2]
crsrOverlaps EQU BYTE PTR [BP-4]


; NOTE: If these become FAR routines then the 'try a little trick' section
; below needs to change the ADD BP & SUB BP from 14 to 16 !!

WinDrawLine PROC NEAR
	MOV	AL, drawVerb
	JMP	SHORT WinLineRtnCursorMayOverlap

WinEraseLine LABEL NEAR
	MOV	AL, eraseVerb
	JMP	SHORT WinLineRtnCursorMayOverlap

WinInvertLine LABEL NEAR
	MOV	AL, invertVerb
	JMP	SHORT WinLineRtnCursorMayOverlap

WinPatternLine LABEL NEAR
	MOV	AL, CS:currPtnXferMode
	OR	AL, ptnVerbsBit
	JMP	SHORT WinLineRtnCursorMayOverlap

WinLineRtnCursorNotInWay:
	XOR	CL, CL	; Caller guarantees that cursor is "off"
	JMP	SHORT WinLineRoutine

WinLineRtnCursorMayOverlap:
	MOV	CL, 1	; Cursor may be in the way, while drawing

WinLineRoutine:
	PUSH	DS
	PUSH	BP
	MOV	BP, SP

	CBW
	PUSH	AX	; graphics verb is saved as local variable

	PUSH	CX	; state of cursor ("off" / maybe on)

	MOV	DS, CS:drawWindow

; Try a little trick

	ADD	BP, 14	; now at TOP of parameters
	CALL	ClipLine
	SUB	BP, 14	; back at bottom of parameters
	RCR	AL, 1
	JC	WinLineCheckForCursorInWay
	JMP	WinLineRoutineRet

WinLineCheckForCursorInWay:
	MOV	AL, crsrOverlaps	; Cursor overlaps flag
	OR	AL, AL	; If caller guarantees cursor to not overlap
	JZ	WinLineCursorWontOverlap	; then don't check again

WinLineLeftRight:
	MOV	CX, lineX1	; Left (?)
	MOV	SI, lineX2	; Right (?)
	CMP	CX, SI
	JLE	WinLineTopBottom

	XCHG	CX, SI	; Adjust so lesser is to the left

WinLineTopBottom:
	MOV	DX, lineY1	; Top (?)
	MOV	DI, lineY2	; Bottom (?)
	CMP	DX, DI
	JLE	WinLineCheckCursor

	XCHG	DX, DI	; Adjust so lesser is to the top

WinLineCheckCursor:
	CALL	CsrMaybeTurnCursorOff	; Should only be turned off if it overlaps

WinLineCursorWontOverlap:
	PUSH	AX

	PUSH	DS:wiDisplayAddr	; display location
	PUSH	DS:wiDisplayWidth	; display width
	PUSH	DS:wiDisplayHeight	; display height

	MOV	AX, lineX1	; x1
	MOV	CX, lineY1	; y1
	MOV	BX, lineX2	; x2
	MOV	DX, lineY2	; y2

	CMP	CX, DX	; If a horizontal line then its faster
	JE	MakeX1BeLeftmost	; to use the HLine routines

	CMP	AX, BX	; If a vertical line then its better to use
	JE	MakeY1BeTopmost	; the HLine routines (for portrait mode)

; Use slower line routine if not a vertical or horizontal line

UseLineRtns:
	ADD	AX, DS:wiBoundsTopLeftX
	PUSH	AX	; push topLeft.x + x1
	ADD	CX, DS:wiBoundsTopLeftY
	PUSH	CX	; push topLeft.y + y1
	ADD	BX, DS:wiBoundsTopLeftX
	PUSH	BX	; push topLeft.x + x2
	ADD	DX, DS:wiBoundsTopLeftY
	PUSH	DX	; push topLeft.y + y2

	SUB	SP, 8	; push phantom pattern for draw,erase,invert
	MOV	AX, grafVerb
	TEST	AL, ptnVerbsBit
	JZ	VerbForLineRtn

	ADD	SP, 8
	PUSH	WORD PTR CS:currPattern+6
	PUSH	WORD PTR CS:currPattern+4
	PUSH	WORD PTR CS:currPattern+2
	PUSH	WORD PTR CS:currPattern+0

VerbForLineRtn:
	PUSH	AX

	CALL	GfxLineRoutine
	JMP SHORT WinLineRoutineExit

; Use faster line routines for vertical & horizontal lines
; This is also the best way to do it to support portrait mode
; so sideways vertical lines are as fast as regular horizontal lines

MakeY1BeTopmost:	; DX (y2) >= CX (y1) after this check
	CMP	DX, CX
	JGE	UseHLineRtns

	XCHG	CX, DX
	JMP	SHORT UseHLineRtns

MakeX1BeLeftmost:	; BX (x2) >= AX (x1) after this check
	CMP	BX, AX
	JGE	UseHLineRtns

	XCHG	AX, BX

UseHLineRtns:
	MOV	DI, CX	; save y1
	ADD	CX, DS:wiBoundsTopLeftY
	PUSH	CX	; topLeft.y + y1
	MOV	SI, AX	; save x1
	ADD	AX, DS:wiBoundsTopLeftX
	PUSH	AX	; topLeft.x + x1
	SUB	BX, SI
	INC	BX
	PUSH	BX	; x2 - x1 + 1
	SUB	DX, DI
	INC	DX
	PUSH	DX	; y2 - y1 + 1

	SUB	SP, 8	; push phantom pattern for draw,erase,invert
	MOV	AX, grafVerb
	TEST	AL, ptnVerbsBit
	JZ	VerbForHLineRtn

	ADD	SP, 8
	PUSH	WORD PTR CS:currPattern+6
	PUSH	WORD PTR CS:currPattern+4
	PUSH	WORD PTR CS:currPattern+2
	PUSH	WORD PTR CS:currPattern+0

VerbForHLineRtn:
	PUSH	AX

	CALL	GfxHLineRoutine

WinLineRoutineExit:
	POP	AX	; If the cursor did not have to
	OR	AL, AL	; be turned off to draw the line
	JZ	WinLineRoutineRet	; then don't have to call TurnCursorOn

	CALL	CsrTurnCursorOn	; Only turn back on if it was turned off

WinLineRoutineRet:
	MOV	SP, BP	; Restore correct stack
	POP	BP
	POP	DS
	RET	8
WinDrawLine ENDP

PURGE lineX1, lineY1, lineX2, lineY2, grafVerb, crsrOverlaps
$EJECT

;*****************************************************************
;*                                                               *
;* PROCEDURE Win___Rect (VAR r: Rectangle; cornerDiam: Integer); *
;*                                                               *
;*****************************************************************

pRect        EQU DWORD PTR [BP+8]
cornerDiam   EQU  WORD PTR [BP+6]

grafVerb     EQU  WORD PTR [BP-2]
crsrOverlaps EQU  BYTE PTR [BP-4]
locExtY	    EQU  WORD PTR [BP-6]
locExtX	    EQU  WORD PTR [BP-8]
locTLY       EQU  WORD PTR [BP-10]
locTLX       EQU  WORD PTR [BP-12]

WinDrawRectangle PROC NEAR
	MOV	AL, drawVerb
	JMP	SHORT WinRectRtnCursorMayOverlap

WinEraseRectangle LABEL NEAR
	MOV	AL, eraseVerb
	JMP	SHORT WinRectRtnCursorMayOverlap

WinInvertRectangle LABEL NEAR
	MOV	AL, invertVerb
	JMP	SHORT WinRectRtnCursorMayOverlap

WinPatternRectangle LABEL NEAR
	MOV	AL, CS:currPtnXferMode
	OR	AL, ptnVerbsBit
	JMP	SHORT WinRectRtnCursorMayOverlap

WinRectRtnCursorNotInWay LABEL NEAR
	XOR	CL, CL	; Caller guarantees cursor doesn't overlap
	JMP	SHORT WinRectangleRoutine

WinRectRtnCursorMayOverlap LABEL NEAR
	MOV	CL, 1	; Cursor may be in the way, while drawing

WinRectangleRoutine:
	PUSH	DS
	PUSH	BP
	MOV	BP, SP

	LDS	SI, pRect
	MOV	DX, cornerDiam	; Don't use AX or CX for this variable !!
	OR	DX, DX	; If corner diameter is zero then
	JZ	WinSquaredRectangle	; don't draw any rounded corners on rectangle
	
WinRoundedRectangle:
	PUSH	DS:[SI].rectTopLeftX	; topLeftX of rounded rectangle
	PUSH	DS:[SI].rectTopLeftY	; topLeftY of rounded rectangle
	PUSH	DX	; diameter of curvature of rect corner
	MOV	CX, DS:[SI].rectExtentX
	SUB	CX, DX
	PUSH	CX	; extraX = extentX - diameter
	MOV	CX, DS:[SI].rectExtentY
	SUB	CX, DX
	PUSH	CX	; extraY = extentY - diameter
	MOV	CL, 1
	PUSH	CX	; fill = TRUE
	PUSH	AX	; mode
	CALL	CircleRoutine	; (topleft.x, topleft.y, diameter, 0, 0);
	JMP	WinRectangleRoutineRet

WinSquaredRectangle:
	CBW
	PUSH	AX	; graf verb is saved as local on the stack
	PUSH	CX	; save state of cursor

	PUSH	SS
	POP	ES
	MOV	CX, 8H	; rect size
	SUB	SP, CX	; make room for local rect
	MOV	DI, SP
	CLD
	SHR	CX, 1
	REP	MOVSW	; move the VAR parm to a local on the stack

	MOV	DS, CS:drawWindow

  ; clip the local rect

	LEA	AX, locTLX
	PUSH	SS
	PUSH	AX
	CALL	WinClipRectangle

	MOV	SI, locExtX	; If there aren't
	OR	SI, SI	; any lines to work on
	JLE	WinDontRectangle	; then exit

	MOV	DI, locExtY	; If there are
	OR	DI, DI	; some lines to work on
	JG	WinDoRectangle	; then draw,etc them

WinDontRectangle:
	JMP	WinRectangleRoutineRet

WinDoRectangle:
	MOV	AL, crsrOverlaps	; Cursor overlaps state
	OR	AL, AL	; If caller guarantees cursor to not overlap
	JZ	WinRectCursorWontOverlap	; then don't check again

	MOV	CX, locTLX	; Left
	MOV	DX, locTLY	; Top
	ADD	SI, CX
	DEC	SI	; Right
	ADD	DI, DX
	DEC	DI	; Bottom
	CALL	CsrMaybeTurnCursorOff	; Should only be turned off if it overlaps

WinRectCursorWontOverlap:
	PUSH	AX

	PUSH	DS:wiDisplayAddr	; display location
	PUSH	DS:wiDisplayWidth	; display width
	PUSH	DS:wiDisplayHeight      	; display height

	MOV	AX, locTLY
	ADD	AX, DS:wiBoundsTopLeftY
	PUSH	AX	; y of line
	MOV	AX, locTLX
	ADD	AX, DS:wiBoundsTopLeftX
	PUSH	AX	; left of line
	PUSH	locExtX	; length of line
	PUSH	locExtY	; number of lines

	SUB	SP, 8	; push phantom pattern for draw,erase,invert
	MOV	AX, grafVerb
	TEST	AL, ptnVerbsBit
	JZ	VerbForHLineRectRtn

	ADD	SP, 8
	PUSH	WORD PTR CS:currPattern+6
	PUSH	WORD PTR CS:currPattern+4
	PUSH	WORD PTR CS:currPattern+2
	PUSH	WORD PTR CS:currPattern+0

VerbForHLineRectRtn:
	PUSH	AX	; draw, erase, invert, patterns
	CALL	GfxHLineRoutine

WinRectangleRoutineExit:
	POP	AX	; If the cursor did not have to
	OR	AL, AL	; be turned off to draw the line
	JZ	WinRectangleRoutineRet	; then don't have to call TurnCursorOn

	CALL	CsrTurnCursorOn	; Only turn back on if it was turned off

WinRectangleRoutineRet:
	MOV	SP, BP
	POP	BP
	POP	DS
	RET	6
WinDrawRectangle ENDP

PURGE pRect, cornerDiam
PURGE grafVerb, crsrOverlaps, locTLX, locTLY, locExtX, locExtY
$EJECT

;****************************************************************
;*                                                              *
;* PROCEDURE Win___RoundedRectFrame (pRect, diameter: Integer); *
;*                                                              *
;****************************************************************

fillIt     EQU 256	; Sets bit 0 in AH to 1 (for filling)

pRect      EQU DWORD PTR [BP+8]
diameter   EQU  WORD PTR [BP+6]

WinDrawRoundedRectFrame PROC NEAR
	MOV	AX, drawVerb
	JMP	SHORT WinRoundedRectangleRoutine

WinEraseRoundedRectFrame LABEL NEAR
	MOV	AX, eraseVerb
	JMP	SHORT WinRoundedRectangleRoutine

WinInvertRoundedRectFrame LABEL NEAR
	MOV	AX, invertVerb
	JMP	SHORT WinRoundedRectangleRoutine

WinPatternRoundedRectFrame LABEL NEAR
	MOV	AL, CS:currPtnXferMode
	OR	AL, ptnVerbsBit
	XOR	AH, AH

WinRoundedRectangleRoutine:
	PUSH	DS
	PUSH	BP
	MOV	BP, SP

	LDS	SI, pRect

	PUSH	DS:[SI].rectTopLeftX	; topLeftX of rounded rectangle
	PUSH	DS:[SI].rectTopLeftY	; topLeftY of rounded rectangle
	MOV	CX, diameter
	PUSH	CX	; diameter of curvature of rect corner
	MOV	DX, DS:[SI].rectExtentX
	SUB	DX, CX
	PUSH	DX	; extraX = extentX - diameter
	MOV	DX, DS:[SI].rectExtentY
	SUB	DX, CX
	PUSH	DX	; extraY = extentY - diameter
	MOV	CL, AH
	PUSH	CX	; fill = TRUE/FALSE
	PUSH	AX	; mode
	CALL	CircleRoutine	; (topleft.x, topleft.y, diameter, 0, 0);

	POP	BP
	POP	DS
	RET	6
WinDrawRoundedRectFrame ENDP

PURGE fillIt, pRect, diameter
$EJECT

;***********************************************************************
;*                                                                     *
;* PROCEDURE Win___Circle (topLeftX, topLeftY, diameter: Integer);     *
;* PROCEDURE Win___CircleFrame (topLeftX, topLeftY, diameter: Integer);*
;*                                                                     *
;***********************************************************************

fillIt     EQU 256	; Sets bit 0 in AH

topLeftX   EQU  WORD PTR [BP+10]
topLeftY   EQU  WORD PTR [BP+8]
diameter   EQU  WORD PTR [BP+6]

WinDrawCircle PROC NEAR
	MOV	AX, drawVerb + fillIt
	JMP	SHORT WinCircleRoutine

WinEraseCircle LABEL NEAR
	MOV	AX, eraseVerb + fillIt
	JMP	SHORT WinCircleRoutine

WinInvertCircle LABEL NEAR
	MOV	AX, invertVerb + fillIt
	JMP	SHORT WinCircleRoutine

WinPatternCircle LABEL NEAR
	MOV	AL, CS:currPtnXferMode
	OR	AL, ptnVerbsBit
	MOV	AH, 1
	JMP	SHORT WinCircleRoutine

WinDrawCircleFrame LABEL NEAR
	MOV	AX, drawVerb
	JMP	SHORT WinCircleRoutine

WinEraseCircleFrame LABEL NEAR
	MOV	AX, eraseVerb
	JMP	SHORT WinCircleRoutine

WinInvertCircleFrame LABEL NEAR
	MOV	AX, invertVerb
	JMP	SHORT WinCircleRoutine

WinPatternCircleFrame LABEL NEAR
	MOV	AL, CS:currPtnXferMode
	OR	AL, ptnVerbsBit
	XOR	AH, AH

WinCircleRoutine:
	PUSH	DS
	PUSH	BP
	MOV	BP, SP

	PUSH	topLeftX	; topLeftX of circle rectangle
	PUSH	topLeftY	; topLeftY of circle rectangle
	PUSH	diameter	; extentX = extentY = diameter
	XOR	CX, CX
	PUSH	CX	; extraX = 0
	PUSH	CX	; extraY = 0
	MOV	CL, AH
	PUSH	CX	; fill = TRUE/FALSE
	PUSH	AX	; mode
	CALL	CircleRoutine	; (topleft.x, topleft.y, diameter, 0, 0);

	POP	BP
	POP	DS
	RET	6
WinDrawCircle ENDP

PURGE fillIt, topLeftX, topLeftY, diameter
$EJECT

; CircleRoutine: PROCEDURE (leftX, topY, diameter, extraX, extraY, fill, mode);

; This routine will allow one to draw,erase,invert,etc a circle or rounded
; rectangle or a frame of a circle or a frame of a rounded rectangle,
; depending on the values of extraX, extraY, fill and mode.  There is some
; special logic here to make the drawing of east/west circles faster.

leftX    EQU WORD PTR [BP+18]
topY     EQU WORD PTR [BP+16]
diameter EQU WORD PTR [BP+14]
extraX   EQU WORD PTR [BP+12]
extraY   EQU WORD PTR [BP+10]
fill     EQU BYTE PTR [BP+8]
mode     EQU BYTE PTR [BP+6]

rightX   EQU WORD PTR [BP-2]
bottomY  EQU WORD PTR [BP-4]
rExtentY EQU WORD PTR [BP-6]
rExtentX EQU WORD PTR [BP-8]
rTopLftY EQU WORD PTR [BP-10]
rTopLftX EQU WORD PTR [BP-12]

CircleRoutine PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP
	SUB	SP, 12

	MOV	AX, diameter
	OR	AX, AX	; IF (diameter = 0) THEN
	JZ	JumpToCircleRoutineRet	; RETURN

	CMP	AX, maxCircleDiam	; IF (diameter > maxCircleDiam) THEN
	JLE	CircleIsInRange	; RETURN

JumpToCircleRoutineRet:
	JMP	CircleRoutineRet

CircleIsInRange:
	MOV	CX, leftX
	MOV	SI, CX
	ADD	SI, extraX
	ADD	SI, AX
	DEC	SI
	MOV	rightX, SI	; rightX = leftX + extraX + diameter - 1

	MOV	DX, topY
	MOV	DI, DX
	ADD	DI, extraY
	ADD	DI, AX
	DEC	DI
	MOV	bottomY, DI	; botY = topY + extraY + diameter - 1

	MOV	DS, CS:drawWindow	; CX,DX,SI,DI set above
	CALL	CsrMaybeTurnCursorOff	; Should only be turned off if it overlaps
	PUSH	AX

	MOV	BX, diameter
	DEC	BX
	MOV	AL, CS:circleOffsets[BX]
	XOR	AH, AH
	XCHG	SI, AX	; pOuterPts = @circleData
	LEA	SI, CS:circleData[SI]	;  (circleOffsets (diameter - 1))

	DEC	BX
	DEC	BX
	MOV	AL, CS:circleOffsets[BX]
	XOR	AH, AH
	DEC	AX	; Don't use inside data first time around
	XCHG	DI, AX	; pInnerPts = @circleData
	LEA	DI, CS:circleData[DI]	;  (circleOffsets (diameter - 3) - 1)

	XOR	CX, CX
	JMP	CircleLoopEntry	; Get pts first

CircleLoopTop:	; AL = offset1 here
	PUSH	CX
	PUSH	DI
	PUSH	SI

; For the following, assign AX,BX,CX,DX,DI,SI as:
;  x1,x2,x3,x4,y1,y2 (for N/S) or y1,y2,y3,y4,x1,x2 (for E/W)

	XCHG	SI, AX	; SI = delta to get x1,x2 or y1,y2
	MOV	AL, CS:[DI]	; offset2 = insidePts (index)
	XOR	AH, AH
	XCHG	DI, AX	; DI = delta to get x3,x4 or y3,y4

	OR	CX, CX	; Test CX before changing it

	MOV	AX, leftX
	MOV	BX, rightX
	MOV	CX, topY
	MOV	DX, bottomY

	JZ	CircleLines	; IF (index = 0) THEN Draw Lines Across
	TEST	fill, 1	; IF NOT filling the circle THEN
	JZ	CircleEndPoints	; Draw End Pts of lines only

CircleLines:
	TEST	CS:currOrientation, 1
	JZ	CircleLinesNorthSouth

CircleLinesEastWest:
	ADD	CX, SI	; y1 (in CX) = topY + offset1
	SUB	DX, SI	; y2 (in DX) = bottomY - offset1
	
	PUSH	AX	; Push parms for second call first
	PUSH	CX
	PUSH	AX
	PUSH	DX	; (leftX, y1, leftX, y2)

	PUSH	BX	; Push parms for first call second
	PUSH	CX
	PUSH	BX
	PUSH	DX	; (rightX, y1, rightX, y2)
	JMP	SHORT CircleLinesMakeCalls

CircleLinesNorthSouth:
	ADD	AX, SI	; x1 (in AX) = leftX + offset1
	SUB	BX, SI	; x2 (in BX) = rightX - offset1
	
	PUSH	AX	; Push parms for second call first
	PUSH	DX
	PUSH	BX
	PUSH	DX	; (x1, bottomY, x2, bottomY)

	PUSH	AX	; Push parms for first call second
	PUSH	CX
	PUSH	BX
	PUSH	CX	; (x1, topY, x2, topY)

CircleLinesMakeCalls:
	MOV	AL, mode
	CALL	WinLineRtnCursorNotInWay	; CALL *Line (x1, topY, x2, topY, mode)
	MOV	AL, mode
	CALL	WinLineRtnCursorNotInWay	; CALL *Line (x1, bottomY, x2, bottomY, mode)
	JMP	CircleLoopBottom

CircleEndPoints:
	TEST	CS:currOrientation, 1
	JZ	CircleEndPtsNorthSouth

CircleEndPtsEastWest:
	MOV	AX, CX
	MOV	BX, DX
	ADD	AX, SI	; y1 (in AX) = topY + offset1
	SUB	BX, SI	; y2 (in BX) = bottomY - offset1
	ADD	CX, DI	; y3 (in CX) = topY + offset2
	SUB	DX, DI	; y4 (in DX) = bottomY - offset2
	MOV	DI, leftX
	MOV	SI, rightX

	PUSH	SI	; Push parms for fourth call first
	PUSH	DX
	PUSH	SI
	PUSH	BX	; (rightX, y4, rightX, y2)

	PUSH	SI	; Push parms for third call second
	PUSH	AX
	PUSH	SI
	PUSH	CX	; (rightX, y1, rightX, y3)

	PUSH	DI	; Push parms for second call third
	PUSH	DX
	PUSH	DI
	PUSH	BX	; (leftX, y4, leftX, y2)

	PUSH	DI	; Push parms for first call last
	PUSH	AX
	PUSH	DI
	PUSH	CX	; (leftX, y1, leftX, y3)
	JMP	SHORT CircleEndPtsMakeCalls

CircleEndPtsNorthSouth:
	MOV	CX, AX
	MOV	DX, BX
	ADD	AX, SI	; x1 (in AX) = leftX + offset1
	SUB	BX, SI	; x2 (in BX) = rightX - offset1
	ADD	CX, DI	; x3 (in CX) = leftX + offset2
	SUB	DX, DI	; x4 (in DX) = rightX - offset2
	MOV	DI, topY
	MOV	SI, bottomY

	PUSH	DX	; Push parms for fourth call first
	PUSH	SI
	PUSH	BX
	PUSH	SI	; (x4, bottomY, x2, bottomY)

	PUSH	AX	; Push parms for third call second
	PUSH	SI
	PUSH	CX
	PUSH	SI	; (x1, bottomY, x3, bottomY)

	PUSH	DX	; Push parms for second call third
	PUSH	DI
	PUSH	BX
	PUSH	DI	; (x4, topY, x2, topY)

	PUSH	AX	; Push parms for first call last
	PUSH	DI
	PUSH	CX
	PUSH	DI	; (x1, topY, x3, topY)

CircleEndPtsMakeCalls:
	MOV	AL, mode
	CALL	WinLineRtnCursorNotInWay	; CALL *Line (x1, topY, x3, topY, mode)
	MOV	AL, mode
	CALL	WinLineRtnCursorNotInWay	; CALL *Line (x4, topY, x2, topY, mode)
	MOV	AL, mode
	CALL	WinLineRtnCursorNotInWay	; CALL *Line (x1, bottomY, x3, bottomY, mode)
	MOV	AL, mode
	CALL	WinLineRtnCursorNotInWay	; CALL *Line (x4, bottomY, x2, bottomY, mode)

CircleLoopBottom:
	TEST	CS:currOrientation, 1
	JZ	CircleLoopBottomNorthSouth

CircleLoopBottomEastWest:
	INC	leftX
	DEC	rightX
	JMP	SHORT CircleLoopRestoreIndices

CircleLoopBottomNorthSouth:
	INC	topY
	DEC	bottomY

CircleLoopRestoreIndices:
	POP	SI
	POP	DI
	POP	CX

	INC	CX
	INC	DI
	INC	SI

CircleLoopEntry:
	MOV	AL, CS:[SI]	; offset1 = outsidePts (index)
	XOR	AH, AH
	CMP	AL, -1	; If offset1 = -1 THEN
	JE	CircleLastPart	; finish the last part of the circle
	JMP	CircleLoopTop	; else keep on going

CircleLastPart:
	MOV	AX, diameter	; If diameter
	CMP	AX, 3	; is less than 3 (special cases)
	JLE	CircleLastLinesAcross	; then finish with solid lines

	TEST	fill, 1	; IF NOT filling the circle THEN
	JZ	CircleLastEndPtsAcross	; Draw End Pts of last lines only

CircleLastLinesAcross:
	MOV	AX, leftX
	MOV	BX, rightX
	SUB	BX, AX
	INC	BX
	MOV	CX, topY
	MOV	DX, bottomY
	SUB	DX, CX
	INC	DX
	MOV	rTopLftX, AX
	MOV	rTopLftY, CX
	MOV	rExtentX, BX
	MOV	rExtentY, DX
	LEA	AX, rTopLftX
	PUSH	SS
	PUSH	AX
	XOR	AX, AX
	PUSH	AX	; Diameter of zero for square corner rect
	MOV	AL, mode
	CALL	WinRectRtnCursorNotInWay
	JMP	SHORT CircleRoutineExit

CircleLastEndPtsAcross:
	MOV	AX, leftX
	MOV	BX, rightX
	MOV	CX, topY
	MOV	DX, bottomY

	TEST	CS:currOrientation, 1
	JZ	CircleLastEndPtsNorthSouth

CircleLastEndPtsEastWest:
	PUSH	AX	; Push parms for second call first
	PUSH	DX
	PUSH	BX
	PUSH	DX	; (leftX, bottomY, rightX, bottomY)

	PUSH	AX	; Push parms for first call second
	PUSH	CX
	PUSH	BX
	PUSH	CX	; (leftX, topY, rightX, topY)
	JMP	SHORT CircleLastEndPtsMakeCalls

CircleLastEndPtsNorthSouth:
	PUSH	BX	; Push parms for second call first
	PUSH	CX
	PUSH	BX
	PUSH	DX	; (rightX, topY, rightX, botY)

	PUSH	AX	; Push parms for first call second
	PUSH	CX
	PUSH	AX
	PUSH	DX	; (leftX, topY, leftX, botY)

CircleLastEndPtsMakeCalls:
	MOV	AL, mode
	CALL WinLineRtnCursorNotInWay	; CALL *Line (leftX, topY, leftX, botY, mode)
	MOV	AL, mode
	CALL WinLineRtnCursorNotInWay	; CALL *Line (rightX, topY, rightX, botY, mode)

CircleRoutineExit:
	POP	AX	; If the cursor did not have to be turned
	OR	AL, AL	; off to draw the circle/rounded rectangle
	JZ	CircleRoutineRet	; then don't have to call TurnCursorOn

	CALL	CsrTurnCursorOn	; Only turn back on if it was turned off

CircleRoutineRet:
	MOV	SP, BP
	POP	BP
	POP	DS
	RET	14
CircleRoutine ENDP

PURGE leftX, topY, diameter, extraX, extraY, fill, mode
PURGE rightX, bottomY, rTopLftX, rTopLftY, rExtentX, rExtentY


CODE ENDS

END
