Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

508 lines
18 KiB

page ,132
;------------------------------Module-Header-------------------------------;
; Module Name: xline.asm ;
; ;
; Contains the line intersection routine. ;
; ;
; Created: 26-Apr-1991 10:49:53 ;
; Author: Charles Whitmer [chuckwh] ;
; ;
; Copyright (c) 1991-1999 Microsoft Corporation ;
;--------------------------------------------------------------------------;
.386
.model small,c
assume cs:FLAT,ds:FLAT,es:FLAT,ss:FLAT
assume fs:nothing,gs:nothing
.xlist
include stdcall.inc
include gdii386.inc
.list
.code
QUAD struc
lo dd 0
hi dd 0
QUAD ends
extrn cmp_table_1:dword
extrn cmp_table_2:dword
extrn cmp_table_3:dword
extrn cmp_table_4:dword
;------------------------------Public-Routine------------------------------;
; LONG fxYIntersect (pptlAB,pptlCD) ;
; POINTL *pptlAB; // First line segment. ;
; POINTL *pptlCD; // Second line segment. ;
; ;
; Computes the y coordinate of the intersection of the two line segments. ;
; The returned value is the ceiling of the exact geometric intersection. ;
; ;
; The intersection is computed by: ;
; ;
; (Cx - Ax)(Cy - Dy) + (Cy - Ay)(Dx - Cx) ;
; lamda = --------------------------------------- ;
; (Bx - Ax)(Cy - Dy) + (By - Ay)(Dx - Cx) ;
; ;
; If (lamda < 0) or (lamda > 1) then there is no intersection, and we ;
; return 0x80000000. ;
; ;
; Y = Ay + lamda * (By - Ay) ;
; ;
; We assume on entry to this routine that all coordinates are limited to ;
; 31 bits of significance. Because of this we know that quantities like ;
; (Cx - Ax) will fit in 32 bits. Also, products like (Cx - Ax)(Cy - Dy) ;
; will fit in 63 bits, and sums as in the numerator of lamda will fit in ;
; 64 bits. ;
; ;
; Note that if you call this routine with POINTFX's instead of POINTL's ;
; you'll get answers that are correct down to 1/16th pel. If you only ;
; want the integer part, just shift the answer right by 4 bits. ;
; ;
; History: ;
; Wed 25-Sep-1991 16:52:20 -by- Wendy Wu [wendywu] ;
; 1) Changed to return the ceiling of the geometric intersection rather ;
; than the floor. ;
; 2) When the given two line segments are actually the same line, we used ;
; to return 0x80000000, now return the y at which they last "intersect".;
; 3) If Y is bigger or smaller than both Cy and Dy, return 0x80000000. ;
; ;
; Fri 26-Apr-1991 10:52:28 -by- Charles Whitmer [chuckwh] ;
; Wrote it. ;
;--------------------------------------------------------------------------;
SEGAB struc
AB_xA dd 0
AB_yA dd 0
AB_xB dd 0
AB_yB dd 0
SEGAB ends
SEGCD struc
CD_xC dd 0
CD_yC dd 0
CD_xD dd 0
CD_yD dd 0
SEGCD ends
cProc fxYIntersect,8,< \
uses ebx esi edi, \
pptlAB: ptr SEGAB, \
pptlCD: ptr SEGCD >
local xA: dword
local yA: dword
local yByA: dword
local qDenom: qword
; Load AB into registers.
mov esi,pptlAB
mov ebx,[esi].AB_yA
mov edx,[esi].AB_yB
mov eax,[esi].AB_xA
mov ecx,[esi].AB_xB
; Reorder AB so that By >= Ay. This will guarantee that (By - Ay)*lamda is a
; positive number so that it's easier to compute the ceiling. (This lets us
; complete the division earlier than a normal extended precision one.)
cmp edx,ebx
jg @F
xchg eax,ecx
xchg ebx,edx
@@:
; Save A locally.
mov xA,eax
mov yA,ebx
; Compute qDenom = (Bx - Ax)(Cy - Dy) + (By - Ay)(Dx - Cx).
sub edx,ebx ; EDX = (By - Ay)
mov esi,pptlCD
sub ecx,eax ; ECX = (Bx - Ax)
mov eax,[esi].CD_xD
mov yByA,edx
sub eax,[esi].CD_xC ; EAX = (Dx - Cx)
imul edx ; EDX:EAX = (By - Ay)(Dx - Cx)
xchg ecx,edx ; EDX = (Bx - Ax)
mov ebx,eax ; ECX:EBX = (By - Ay)(Dx - Cx)
mov eax,[esi].CD_yC
sub eax,[esi].CD_yD ; EAX = (Cy - Dy)
imul edx ; EDX:EAX = (Bx - Ax)(Cy - Dy)
add ebx,eax
jz qDenom_maybe_zero
adc ecx,edx ; ECX:EBX = qDenom
qDenom_not_zero:
mov qDenom.lo,ebx
mov qDenom.hi,ecx
; Compute qNum = (Cx - Ax)(Cy - Dy) + (Cy - Ay)(Dx - Cx).
mov edx,[esi].CD_yC
sub edx,yA ; EDX = (Cy - Ay)
mov eax,[esi].CD_xD
sub eax,[esi].CD_xC ; EAX = (Dx - Cx)
imul edx ; EDX:EAX = (Cy - Ay)(Dx - Cx)
mov ecx,edx
mov ebx,eax ; ECX:EBX = (Cy - Ay)(Dx - Cx)
mov edx,[esi].CD_xC
sub edx,xA ; EDX = (Cx - Ax)
mov eax,[esi].CD_yC
sub eax,[esi].CD_yD ; EAX = (Cy - Dy)
imul edx ; EDX:EAX = (Cx - Ax)(Cy - Dy)
add eax,ebx
adc edx,ecx ; EDX:EAX = qNum
; Force the denominator to be positive.
mov ebx,qDenom.lo
mov ecx,qDenom.hi ; ECX:EBX = qDenom
or ecx,ecx
jns @F
neg ebx ; Negate qDenom.
adc ecx,0
neg ecx
neg eax ; Negate qNum.
adc edx,0
neg edx
js no_intersection ; (qNum < 0) => (lamda < 0)
@@:
; See if (lamda > 1).
cmp edx,ecx
ja no_intersection ; (uqNum > uqDenom) => (lamda > 1)
jb no_problem
cmp eax,ebx
jbe no_problem
no_intersection:
mov eax,80000000h
cRet fxYIntersect
no_problem:
; See if we can do a short form calculation.
; (99% of all cases get handled here.)
or ecx,ecx
jnz long_form
mul yByA ; EDX:EAX = (By - Ay) uqNum
div ebx ; EAX = (By - Ay) uqNum / uqDenom
; Get the ceiling of the intersection by incrementing the quotient
; by 1 if the remainder is bigger than zero.
cmp ecx,edx ; CF = 1 if (edx > 0)
adc eax,0
add eax,yA ; EAX = CEILING((By - Ay) uqNum /
; uqDenom) + Ay
; Compare the intersection y with Cy and Dy.
; Return 0x80000000 if the intersection y is not between Cy and Dy.
cmp_with_cy_dy:
cmp eax,[esi].CD_yC
jg @F
jz done
cmp eax,[esi].CD_yD
jl no_intersection
cRet fxYIntersect
@@:
cmp eax,[esi].CD_yD
jg no_intersection
done:
cRet fxYIntersect
long_form:
; Do the extended precision calculation.
; Count the bits needed to normalize the denominator.
mov esi,ecx
mov edi,ebx ; ESI:EDI = uqDenom.
xor ecx,ecx
cmp esi,10000h ; This binary search scheme is a lot
adc ecx,ecx ; faster than BSR.
cmp esi,cmp_table_1[4*ecx]
adc ecx,ecx
cmp esi,cmp_table_2[4*ecx]
adc ecx,ecx
cmp esi,cmp_table_3[4*ecx]
adc ecx,ecx
cmp esi,cmp_table_4[4*ecx]
adc ecx,ecx ; CL = number of bits to shift left!
; Normalize the denominator and numerator.
shld esi,edi,cl
shl edi,cl
shld edx,eax,cl ; EDX:EAX = uqNum.
shl eax,cl
; Compute uqNum * (By - Ay), a 95 bit result.
mov ebx,edx
mul yByA
xchg eax,ebx
mov ecx,edx
mul yByA
add eax,ecx
adc edx,0 ; EDX:EAX:EBX = (By - Ay) * uqNum
; Divide by the 64 bit uqDenom, we're only interested in the integer part!
; (We would have to worry about overflow in the general case, but we know the
; answer cannot be 0FFFFFFFFh in this case, since (By - Ay) is bounded below
; that.)
div esi
mov ecx,edx ; ECX:EBX = remainder, so far.
; We want to increment the quotient if the remainder is positive. In this
; case, we want the carry flag set to avoid jumps. In order to do this,
; instead of calculating the remainder, we calculate the minus remainder.
mov esi,eax ; Hold quotient temporarily.
mul edi
sub eax,ebx
sbb edx,ecx ; EDX:EAX = -remainder
; We don't have enough bits here, since the remainder is unsigned. We
; therefore use the carry bit instead of a sign bit. If the carry
; is set, we have a positive remainder. We'll increment the quotient
; in this case.
; (We have bounded (By - Ay) to be less than 2^31, so we also know that
; the quotient is less than 2^31. This guarantees that we only have to
; adjust for the quotient once.)
adc esi,0
mov eax,esi
; Add Ay to the result then compare it with Cy and Dy.
add eax,yA
mov esi,pptlCD
jmp cmp_with_cy_dy
qDenom_maybe_zero:
adc ecx,edx ; ECX:EBX = qDenom
jnz qDenom_not_zero
; These two lines are parallel since qDenom is zero, we have to find out if
; they are actually the same line.
; !!! We may want to change the interface for ulPtOnWhichSide so we call
; !!! it in a more efficient way. However, this is not a critical path
; !!! so lets wait until performance tuning time. [wendywu]
mov esi,pptlAB
mov edi,pptlCD
cCall ulPtOnWhichSide,<edi,esi> ; See if C is on SEGAB
cmp eax,POINT_ON_LINE
jnz no_intersection
; They are indeed the same line, return the last point they meet.
; MIN(MAX(AB_yA, AB_yB), MAX(CD_yC, CD_yD))
mov eax,[esi].AB_yA
cmp eax,[esi].AB_yB
jg @F
mov eax,[esi].AB_yB ; EAX = MAX((AB_yA, AB_yB)
@@:
mov ebx,[edi].CD_yC
cmp ebx,[edi].CD_yD
jg @F
mov ebx,[edi].CD_yD ; EBX = MAX(CD_yC, CD_yD)
@@:
cmp eax,ebx
jl @F
mov eax,ebx
@@:
cRet fxYIntersect
endProc fxYIntersect
;------------------------------Public-Routine------------------------------;
; ULONG ulPtOnWhichSide(pptl,pptlAB) ;
; POINTL *pptl; // Point in question. ;
; POINTL *pptlAB; // Line segment. ;
; ;
; Compute on which side of the line a given point lies. This is ;
; determined by the sign of the z component of the cross product of ;
; the two vectors (pptlB - pptlA) x (pptl - pptlA). i.e. the sign of ;
; ;
; (Bx - Ax) * (y - Ay) - (By - Ay) * (x - Ax) ;
; ;
; We make sure By >= Ay so the first vector is in the 1st or 2nd quadrant. ;
; If the above product is ;
; 1) positive -> the given point is on the left side of the line ;
; 2) negative -> the given point is on the right side of the line ;
; 3) zero -> the given point is on the given line ;
; ;
; History: ;
; Wed 25-Sep-1991 16:52:20 -by- Wendy Wu [wendywu] ;
; Wrote it. ;
;--------------------------------------------------------------------------;
cProc ulPtOnWhichSide,8,< \
uses ebx esi, \
pptl: ptr POINTL, \
pptlAB: ptr SEGAB >
local yByA: dword
local xBxA: dword
; Load AB into registers.
mov esi,pptlAB
mov ebx,[esi].AB_yA
mov edx,[esi].AB_yB
mov ecx,[esi].AB_xA
mov eax,[esi].AB_xB
; Reorder AB so that By >= Ay.
cmp edx,ebx
jg @F
xchg eax,ecx
xchg ebx,edx
@@:
sub eax,ecx ; Compute (Bx - Ax)
mov xBxA,eax
sub edx,ebx ; Compute (By - Ay)
mov yByA,edx
mov esi,pptl
mov eax,[esi].ptl_y ; Compute (y - Ay)
sub eax,ebx
imul xBxA
mov ebx,eax
mov eax,[esi].ptl_x ; Compute (x - Ax)
sub eax,ecx
mov ecx,edx ; ECX:EBX = (Bx - Ax) * (y - Ay)
imul yByA ; EDX:EAX = (By - Ay) * (x - Ax)
sub ebx,eax
sbb ecx,edx ; ECX:EBX = (Bx - Ax) * (y - Ay) -
jns @F ; (By - Ay) * (x - Ax)
mov eax,POINT_ON_RIGHT_SIDE ; on the right if negative
cRet ulPtOnWhichSide
@@:
jnz @F ; on the line if zero
or ebx,ebx
jnz @F
mov eax,POINT_ON_LINE
cRet ulPtOnWhichSide
@@:
mov eax,POINT_ON_LEFT_SIDE ; on the left if positive
cRet ulPtOnWhichSide
endProc ulPtOnWhichSide
;------------------------------Public-Routine------------------------------;
; ULONG yGetLtoR/yGetRtoL
;
; Compute the ceiling of the y coordinate in integer of a line given the
; x coordinate in FIX. yB must be bigger than yA.
; plnfx points to a line which must satisfy ptfxLo.y < ptfxHi.y.
; Lines that start from left and end at right, i.e. ptfxLo.x <= ptfxHi.x,
; should use yGetLtoR.
; Lines that start from right and end at left, i.e. ptfxLo.x > ptfxHi.x,
; should use yGetRtoL.
;
; History:
; 02-Dec-1992 -by- Wendy Wu [wendywu]
; Wrote it.
;--------------------------------------------------------------------------;
cProc yGetLtoR,8,< \
uses ebx, \
plnfx: ptr SEGAB, \
x: dword >
; Calculate DN = yB - yA
mov ebx,plnfx
mov eax,[ebx].AB_yB
sub eax,[ebx].AB_yA ; EAX = yB - yA
; Calculate x - xA
mov edx,x
mov ecx,[ebx].AB_xA
sub edx,ecx ; EDX = x - xA
mul edx ; EDX:EAX = (yB - yA) * (x - xA)
; Calculate DM = xB - xA
sub ecx,[ebx].AB_xB
neg ecx ; ECX = xB - xA
; FXTOLONGCEILING(DN * (x - xA) / DM + yA)
div ecx
xor ecx,ecx ; increment by 1 if remainder not 0
cmp ecx,edx
adc eax,[ebx].AB_yA
add eax,15
sar eax,4
cRet yGetLtoR
endProc yGetLtoR
cProc yGetRtoL,8,< \
uses ebx, \
plnfx: ptr SEGAB, \
x: dword >
; Calculate DN = yB - yA
mov ebx,plnfx
mov eax,[ebx].AB_yB
sub eax,[ebx].AB_yA ; EAX = yB - yA
; Calculate xA - x
mov ecx,[ebx].AB_xA
mov edx,ecx
sub edx,x ; EDX = xA - x
mul edx ; EDX:EAX = (yB - yA) * (xA - x)
; Calculate DM = xA - xB
sub ecx,[ebx].AB_xB ; ECX = xA - xB
; FXTOLONGCEILING(DN * (xA - x) / DM + yA)
div ecx
xor ecx,ecx ; increment by 1 if remainder not 0
cmp ecx,edx
adc eax,[ebx].AB_yA
add eax,15
sar eax,4
cRet yGetRtoL
endProc yGetRtoL
end