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.
 
 
 
 
 
 

1331 lines
43 KiB

;---------------------------Module-Header------------------------------;
; Module Name: fastline.asm
;
; This module draws solid lines using the line hardware of the S3.
; It handles entirely unclipped lines, or lines clipped to a single
; rectangle by using the S3's hardware clipping.
;
; All lines that have integer end-points are drawn directly using the
; line hardware, and any lines having fractional end-points and are
; less than 256 pels long are also drawn directly by the line hardware.
; (Fractional end-point lines require 4 more bits of precision from the
; DDA hardware than do integer end-point lines, and the S3 has but 13
; bits to use. Lines longer than 255 pels are punted to the general
; purpose strip routines, which won't overflow the hardware).
;
; There are a number of ways unclipped lines can be sped up on the S3:
;
; 1) Optimize NT's GDI. This isn't an option for everybody.
;
; 2) Use memory-mapped I/O.
;
; 3) If you can't use memory-mapped I/O, you can at least minimize
; outs and ins because they are so painfully expensive. One
; way to do this is to amortize the cost of the in used for checking
; the FIFO: do one in to make sure a large number of entries on
; the FIFO are free (perhaps 6), then do the next 6 outs without
; having to check the FIFO.
;
; This will be a win on figures not requiring many outs (such as when
; radial lines are drawn as with a rectangle), assuming the hardware
; isn't always being overdriven.
;
; 4) In conjunction with 3) above, since short integer lines occur so
; frequently with CAD programs, it would be beneficial to use short-
; stroke vectors to do those lines, as they could reduce the number
; of outs required. Perhaps any line 10 pels or shorter could be
; output by using an array lookup to determine the short stroke vectors
; needed to represent that line.
;
; 5) Numerous other things I haven't thought of.
;
; Other improvements that should be done to make lines faster in general:
;
; 1) Simple clipping should be improved. Currently, we simply set the
; clip rectangle and let the hardware do everything. We should at least
; perform a trivial clip test to see if there's no chance the line
; intersects the clip rectangle.
;
; Also, for single lines it's expensive to do the 4 outs required to
; set the S3's clip rectangle, draw the line, then do 4 outs to reset
; the clip rectangle again. In those cases, it would be faster to do the
; clipping entirely in software.
;
; 2) This code can be enhanced to also handle styled lines, and to do
; complex clipping more efficiently than the strips routines.
;
; 3) It is possible to derive the mathematical algorithm for drawing any
; GIQ line in NT's 32 bit space using the limited bit precision of the
; S3's hardware. What this means is that absolutely any GIQ line can
; be drawn precisely with at most two drawing commands to the line
; hardware (and so the strips line drawing routine could be eliminated
; entirely). Just be careful of your math...
;
; 4) Numerous other things I haven't thought of.
;
; Copyright (c) 1992-1994 Microsoft Corporation
;-----------------------------------------------------------------------;
.386
.model small,c
assume cs:FLAT,ds:FLAT,es:FLAT,ss:FLAT
assume fs:nothing,gs:nothing
.xlist
include stdcall.inc ;calling convention cmacros
include i386\strucs.inc
include i386\hw.inc
include i386\lines.inc
.list
; Line coordinates are given in 28.4 fixed point format:
F equ 16
FLOG2 equ 4
; The S3's hardware can have 13 bits of significance for the error and
; step terms:
NUM_DDA_BITS equ 13
; GIQ lines have to dedicate 4 bits to the fractional term, so the largest
; delta we can handle for GIQ lines is calculated as follows (scaled by F
; so we can do the test in GIQ coordinates), and remembering that one bit
; has to be used as a sign bit:
MAX_GIQ_DELTA equ (((1 shl (NUM_DDA_BITS - 5)) - 1) * F)
; The following values must match those in winddi.h!
PD_BEGINSUBPATH equ 00000001h
PD_ENDSUBPATH equ 00000002h
PD_RESETSTYLE equ 00000004h
PD_CLOSEFIGURE equ 00000008h
PD_BEZIERS equ 00000010h
PATHDATA struc
pd_flags dd ?
pd_count dd ?
pd_pptfx dd ?
PATHDATA ends
;-------------------------------------------------------------------------;
; I felt a compelling need to use 'ebp' as a 7th general register, and we
; have no nifty macros for dereferencing frame variables off 'esp'. So
; with this structure I am rolling my own stack frame:
STATE_MEM_SIZE equ 4 ;4 dwords
PROC_MEM_SIZE equ 9 ;9 dwords
STACK_FRAME struc
; State variables (don't add/delete fields without modifying STATE_MEM_SIZE!)
sf_ulOurEbp dd ?
sf_ulOriginalEbx dd ?
sf_ulOriginalEdi dd ?
sf_ulOriginalEsi dd ?
; Frame variables (feel free to add/delete fields):
sf_y0 dd ? ;GIQ variables
sf_y1 dd ?
sf_x1 dd ?
sf_ptlOrg db (size POINTL) dup (?)
;our origin for normalizing the line
sf_ptfxLast db (size POINTL) dup (?)
;the most recent point
sf_ptfxStartFigure db (size POINTL) dup (?)
;the figure's 1st point
sf_bMore dd ? ;more path records to get?
sf_pptfxEnd dd ? ;points to last point in record
sf_pptfx dd ? ;points to current point
sf_pd db (size PATHDATA) dup (?)
;pathdata structure
sf_ptfxStart db (size POINTL) dup (?)
;temporary spot for saving start point
sf_ptfxEnd db (size POINTL) dup (?)
;temporary spot for saving end point
sf_ulCmd dd ? ;S3 draw command
sf_cPels dd ? ;length of line in pels
sf_bSetCP dd ? ;1 if first line in figure, 0 otherwise
sf_ulLastLength dd ? ;last value set as LINE_MAX
sf_ulErrorTerm dd ? ;error term for line
sf_xOffset dd ? ;stack copy of surface offset
sf_yOffset dd ?
sf_ioGp_stat_cmd dd ? ;local copy of gp_stat address
sf_pjMmBase dd ? ;local copy of the memory-mapped I/O
; base address
; Procedure variables (don't add/delete fields without modifying
; PROC_MEM_SIZE!)
sf_ulOriginalEbp dd ?
sf_ulOriginalReturn dd ?
sf_ppdev dd ?
sf_ppo dd ?
sf_prclClip dd ?
sf_apfn dd ?
sf_flags dd ?
sf_color dd ?
sf_ulHwMix dd ?
STACK_FRAME ends
.code
EXTRNP PATHOBJ_bEnum,8
EXTRNP bLines,36
ROUND_X_DOWN equ 01h
ROUND_Y_DOWN equ 02h
ROUND_SLOPE_ONE equ 04h
ROUND_X_AND_Y_DOWN equ (ROUND_X_DOWN + ROUND_Y_DOWN)
ROUND_X_DOWN_SLOPE_ONE equ (ROUND_X_DOWN + ROUND_SLOPE_ONE)
ROUND_Y_DOWN_SLOPE_ONE equ (ROUND_Y_DOWN + ROUND_SLOPE_ONE)
;--------------------------------Macro----------------------------------;
; GIQ flags
;
; This macros computes the start pixel, the number of pixels to
; be lit, and the initial error term given a GIQ line. The line must
; have already been normalized such that dM >= dN, dN >= 0.
;
; Input: ebx - M0
; ecx - N0
; esi - dM
; edi - dN
; Trashes:
; eax, edx
; [esp].sf_ptlOrg.ptl_x, [esp].sf_ptlOrg.ptl_y
; Output:
; [esp].sf_x1 - x-coordinate of last pixel (exclusive)
; ebx - x-coordinate of first pixel
; ecx - error term
; esi - dM
; edi - dN
; ebp - y-coordinate of first pixel
;-----------------------------------------------------------------------;
GIQ macro flags
local compute_x1, compute_error_term
; We normalize our coordinate system so that if the start point is
; (M0/F, N0/F), the origin is at (floor(M0/F), (N0/F)):
mov eax,ebx
mov ebp,ecx
sar eax,FLOG2
sar ebp,FLOG2
mov [esp].sf_ptlOrg.ptl_x,eax
;ptlOrg.x = floor(M0 / F)
mov [esp].sf_ptlOrg.ptl_y,ebp
;ptlOrg.y = floor(N0 / F)
; Calculate the correct [esp].sf_x1:
lea ebp,[ecx + edi] ;ebp = N1
and ebp,F - 1
if (flags AND ROUND_X_DOWN)
if (flags AND ROUND_SLOPE_ONE)
cmp ebp,8
sbb ebp,-1
endif
cmp ebp,1
sbb ebp,8 ;N1 -= 8
else
sub ebp,8 ;N1 -= 8
endif
sbb eax,eax
xor ebp,eax
sub ebp,eax ;N1 = ABS(N1)
lea edx,[ebx + esi]
mov eax,edx
sar edx,FLOG2
and eax,F - 1
jz short @f ;special case for M1 == 0
cmp eax,ebp ;cmp M1, N1
sbb edx,-1 ;edx is now one pixel past the actual
@@: ; end coordinate (note that it hasn't
; been affected by the origin shift)
compute_error_term:
; ebx = M0
; ecx = N0
; edx = x1
; esi = dM
; edi = dN
and ecx,F - 1
mov [esp].sf_x1,edx ;save x1
; Calculate our error term for x = 0.
;
; NOTE: Since this routine is used only for lines that are unclipped, we
; are guaranteed by our screen size that the values will be far less
; than 32 bits in significance, and so we don't worry about overflow.
; If this is used for clipped lines, these multiplies will have to
; be converted to give 64 bit results, because we can have 36 bits of
; significance!
lea ebp,[ecx + 8] ;ebp = N0 + 8
mov eax,esi
imul eax,ebp ;eax = dM * (N0 + 8)
mov ebp,edi
; We have to special case when M0 is 0 -- we know x0 will be zero.
; So we jump ahead a bit to a place where 'ebx' is assumed to contain
; x0 -- and it just so happens 'ebx' is zero in this case:
and ebx,F - 1
jz short @f
imul ebp,ebx ;ebp = dN * M0
sub eax,ebp
; Calculate the x-coordinate of the first pixel:
if (flags AND ROUND_X_DOWN)
if (flags AND ROUND_SLOPE_ONE)
cmp ecx,8
sbb ecx,-1
endif
cmp ecx,1
sbb ecx,8 ;N0 -= 8
else
sub ecx,8 ;N0 -= 8
endif
sbb ebp,ebp
xor ecx,ebp
sub ecx,ebp ;N0 = ABS(N0)
cmp ebx,ecx
sbb ebx,ebx
not ebx ;ebx = -x0
; Now adjust the error term accordingly:
@@:
if (flags AND ROUND_Y_DOWN)
dec eax
endif
sar eax,FLOG2 ;eax = floor((N0 + 8) dM - M0 dN] / 16)
mov ecx,[esp].sf_ptlOrg.ptl_x
mov ebp,[esp].sf_ptlOrg.ptl_y
sub ecx,ebx ;ecx = ptlOrg.ptl_x + x0
and ebx,edi
add ebx,eax
sub ebx,esi ;ebx = dN * x0 + initial error - dM
jl short @f ;if the error term >= 0, we have to
sub ebx,esi ; add 1 to the y position and subtract
inc ebp ; dM off again
@@:
xchg ebx,ecx
endm
;--------------------------------Macro----------------------------------;
; GIQR flags
;
; Same as above, except it handles flips about the line x = y.
;
; Input: ebx - M0
; ecx - N0
; esi - dM
; edi - dN
; Trashes:
; eax, edx
; [esp].sf_ptlOrg.ptl_x, [esp].sf_ptlOrg.ptl_y
; Output:
; [esp].sf_y1 - y-coordinate of last pixel (exclusive)
; ebx - x-coordinate of first pixel
; ecx - error term
; esi - dM
; edi - dN
; ebp - y-coordinate of first pixel
;-----------------------------------------------------------------------;
GIQR macro flags
; We normalize our coordinate system so that if the start point is
; (M0/F, N0/F), the origin is at (floor(M0/F), (N0/F)):
mov eax,ebx
mov ebp,ecx
sar eax,FLOG2
sar ebp,FLOG2
mov [esp].sf_ptlOrg.ptl_x,eax
;ptlOrg.x = floor(M0 / F)
mov [esp].sf_ptlOrg.ptl_y,ebp
;ptlOrg.y = floor(N0 / F)
; Calculate the correct [esp].sf_y1:
lea ebp,[ebx + esi] ;ebp = M1
and ebp,F - 1
if (flags AND ROUND_Y_DOWN)
cmp ebp,1
sbb ebp,8 ;M1 -= 8
else
sub ebp,8 ;M1 -= 8
endif
sbb eax,eax
xor ebp,eax
sub ebp,eax ;M1 = ABS(M1)
lea edx,[ecx + edi]
mov eax,edx
sar edx,FLOG2
and eax,F - 1
jz short @f ;special case for N1 == 0
cmp eax,ebp ;cmp N1, M1
sbb edx,-1 ;edx is now one pixel past the actual
@@: ; end coordinate (note that it hasn't
; been affected by the origin shift)
and ebx,F - 1
mov [esp].sf_y1,edx
; Calculate our error term for y = 0.
;
; NOTE: Since this routine is used only for lines that are unclipped, we
; are guaranteed by our screen size that the values will be far less
; than 32 bits in significance, and so we don't worry about overflow.
; If this is used for clipped lines, these multiplies will have to
; be converted to give 64 bit results, because we can have 36 bits of
; significance!
lea ebp,[ebx + 8] ;ebp = M0 + 8
mov eax,edi
imul eax,ebp ;eax = dN * (M0 + 8)
mov ebp,esi
; We have to special case when N0 is 0 -- we know y0 will be zero.
; So we jump ahead a bit to a place where 'ecx' is assumed to contain
; y0 -- and it just so happens 'ecx' is zero in this case:
and ecx,F - 1
jz short @f
imul ebp,ecx ;ebp = dM * N0
sub eax,ebp
; Calculate the x-coordinate of the first pixel:
if (flags AND ROUND_Y_DOWN)
cmp ebx,1
sbb ebx,8 ;M0 -= 8
else
sub ebx,8 ;M0 -= 8
endif
sbb ebp,ebp
xor ebx,ebp
sub ebx,ebp ;M0 = ABS(M0)
cmp ecx,ebx
sbb ecx,ecx
not ecx ;ecx = -y0
; Now adjust the error term accordingly:
@@:
if (flags AND ROUND_X_DOWN)
dec eax
endif
sar eax,FLOG2 ;eax = floor((M0 + 8) dN - N0 dM] / 16)
mov ebx,[esp].sf_ptlOrg.ptl_x
mov ebp,[esp].sf_ptlOrg.ptl_y
sub ebp,ecx ;ebp = ptlOrg.ptl_y + y0
and ecx,esi
add ecx,eax
sub ecx,edi ;ecx = dM * x0 + initial error - dN
jl short @f ;if the error term >= 0, we have to
sub ecx,edi ; add 1 to the x position and subtract
inc ebx ; dN off again
@@:
endm
;---------------------------Public-Routine------------------------------;
; vIoFastLine(ppdev, ppo, prclClip, apfn, flags, color, ulHwMix)
;
; Draws fast lines. Or at least attempts to. Uses normal I/O.
;
; Input:
;
; ppdev - PDEV pointer
; ppo - path to be drawn
; prclClip - pointer to rectangle array for passing to 'bLines'
; apfn - pointer to strip routines for passing to 'bLines'
; flags - status flag for passing to 'bLines'
; color - color
; ulHwMix - mix
;
;-----------------------------------------------------------------------;
; NOTE: Don't go changing parameters without also changing STACK_FRAME!
cProc vIoFastLine,28,< \
uses esi edi ebx, \
ebp_ppdev: ptr, \
ebp_ppo: ptr, \
ebp_prclClip: ptr, \
ebp_apfn: ptr, \
ebp_flags: dword,\
ebp_color: dword,\
ebp_ulHwMix: dword>
; Leave room for our stack frame.
;
; NOTE: Don't add local variables here -- you can't reference them with
; ebp anyway! Add them to the STACK_FRAME structure.
local aj[(size STACK_FRAME) - 4 * (STATE_MEM_SIZE + PROC_MEM_SIZE)]: byte
; We save 'ebp' on the stack (note that STACK_FRAME accounts for this push):
push ebp
mov esi,[esp].sf_ppdev
mov [esp].sf_ulLastLength,-1;make sure first line in path resets
; LINE_MAX value
; Warm up the hardware:
mov edx,[esi].pdev_ioGp_stat_cmd
@@: in ax,dx
and eax,FIFO_3_EMPTY
jnz short @b ;IO_FIFO_WAIT(ppdev, 3)
mov edx,[esi].pdev_ioFrgd_color
mov eax,[esp].sf_color
out dx,ax ;IO_FRGD_COLOR(ppdev, color)
mov edx,[esi].pdev_ioFrgd_mix
mov eax,[esp].sf_ulHwMix
or eax,FOREGROUND_COLOR
out dx,ax ;IO_FRGD_MIX(ppdev, FOREGROUND_COLOR |
; ulHwMix)
mov edx,[esi].pdev_ioMulti_function
mov eax,DATA_EXTENSION
out dx,ax ;IO_PIX_CNTL(ppdev, ALL_ONES)
; Now get some path stuff:
io_next_record:
mov eax,[esp].sf_ppo
lea ebx,[esp].sf_pd
cCall PATHOBJ_bEnum,<eax,ebx>
mov [esp].sf_bMore,eax ;save away return code for later
mov ebx,[esp].sf_pd.pd_count;if 0 points in record, get outta here
or ebx,ebx
jz io_check_for_closefigure
lea ebp,[8 * ebx - 8]
add ebp,[esp].sf_pd.pd_pptfx
mov [esp].sf_pptfxEnd,ebp ;points to last point in record
mov ecx,[esp].sf_pd.pd_flags
test ecx,PD_BEGINSUBPATH
jz short io_continue_subpath
; Handle a new sub-path:
mov eax,[esp].sf_pd.pd_pptfx
add eax,8
mov [esp].sf_pptfx,eax
mov esi,[ebp].ptl_x ;remember last point in case we have
mov edi,[ebp].ptl_y ; to continue to another record
mov [esp].sf_ptfxLast.ptl_x,esi
mov [esp].sf_ptfxLast.ptl_y,edi
mov ebx,[eax - 8].ptl_x ;load up current start and end point
mov ecx,[eax - 8].ptl_y
mov esi,[eax].ptl_x
mov edi,[eax].ptl_y
mov [esp].sf_ptfxStartFigure.ptl_x,ebx
mov [esp].sf_ptfxStartFigure.ptl_y,ecx
mov [esp].sf_bSetCP,1 ;this line is first in figure
cmp eax,[esp].sf_pptfxEnd ;we have to be careful when the only
; point in the record is the start-
; figure point (pretty rare)
jbe io_new_line
jmp short io_next_record
io_continue_subpath:
; This record continues the path:
mov eax,[esp].sf_pd.pd_pptfx
mov ebx,[esp].sf_ptfxLast.ptl_x ;load up current start point
mov ecx,[esp].sf_ptfxLast.ptl_y
mov esi,[ebp].ptl_x ;remember last point in case we have
mov edi,[ebp].ptl_y ; to continue to another record
mov [esp].sf_ptfxLast.ptl_x,esi
mov [esp].sf_ptfxLast.ptl_y,edi
mov esi,[eax].ptl_x ;load up current end point
mov edi,[eax].ptl_y
mov [esp].sf_pptfx,eax
jmp io_new_line
;/////////////////////////////////////////////////////////////////////
;// Next Line Stuff
;/////////////////////////////////////////////////////////////////////
io_handle_closefigure:
mov [esp].sf_pd.pd_flags,0
mov ebx,[esp].sf_ptfxLast.ptl_x
mov ecx,[esp].sf_ptfxLast.ptl_y
mov esi,[esp].sf_ptfxStartFigure.ptl_x
mov edi,[esp].sf_ptfxStartFigure.ptl_y
jmp io_new_line
; Before getting the next path record, see if we have to do a closefigure:
io_check_for_closefigure:
test [esp].sf_pd.pd_flags,PD_CLOSEFIGURE
jnz io_handle_closefigure
mov esi,[esp].sf_bMore
or esi,esi
jnz io_next_record
io_all_done:
pop ebp
cRet vIoFastLine
;/////////////////////////////////////////////////////////////////////
;// Output Integer Line
;/////////////////////////////////////////////////////////////////////
; ebx = x
; ecx = y
; esi = dx
; edi = dy
; [esp].sf_ulErrorTerm = incomplete error term (actual value is 'dx - bias')
; [esp].sf_ulCmd = draw command
; NOTE: The port values retrieved from the PDEV are word values, and word
; moves have a one cycle penalty. For this reason, the values should
; actually be defined as dwords in the PDEV...
public io_int_output_line
io_int_output_line::
mov ebp,[esp].sf_ppdev ;ebp = ppdev
cmp [esp].sf_bSetCP,0
je short io_int_current_position_already_set
mov edx,[ebp].pdev_ioGp_stat_cmd
@@: in ax,dx
and eax,FIFO_7_EMPTY ;wait for 7 entries
jnz short @b
mov edx,[ebp].pdev_ioCur_x
mov eax,ebx
add eax,[ebp].pdev_xOffset
out dx,ax
mov edx,[ebp].pdev_ioCur_y
mov eax,ecx
add eax,[ebp].pdev_yOffset
out dx,ax
mov [esp].sf_bSetCP,0 ;the next integer line in this figure
; will have the CP set correctly
jmp short io_int_output_common
io_int_current_position_already_set:
mov edx,[ebp].pdev_ioGp_stat_cmd
@@: in ax,dx
and eax,FIFO_5_EMPTY ;wait for 5 entries
jnz short @b
public io_int_output_common
io_int_output_common::
cmp esi,[esp].sf_ulLastLength
je short @f
mov edx,[ebp].pdev_ioMaj_axis_pcnt
mov eax,esi
out dx,ax
mov [esp].sf_ulLastLength,eax
@@:
mov edx,[ebp].pdev_ioDesty_axstp
mov eax,edi
out dx,ax ;axial = dy
mov edx,[ebp].pdev_ioDestx_diastp
sub eax,esi
out dx,ax ;diag = dy - dx
mov edx,[esp].sf_ulErrorTerm
sar edx,1
add eax,edx
mov edx,[ebp].pdev_ioErr_term
out dx,ax ;err = dy - dx + floor((dx - bias) / 2)
mov edx,[ebp].pdev_ioGp_stat_cmd
mov eax,[esp].sf_ulCmd
out dx,ax ;draw that puppy
;/////////////////////////////////////////////////////////////////////
;// Main Loop
;/////////////////////////////////////////////////////////////////////
public io_next_line
io_next_line::
mov eax,[esp].sf_pptfx
cmp eax,[esp].sf_pptfxEnd
jae io_check_for_closefigure
mov ebx,[eax].ptl_x
mov ecx,[eax].ptl_y
mov esi,[eax+8].ptl_x
mov edi,[eax+8].ptl_y
add eax,8
mov [esp].sf_pptfx,eax
public io_new_line
io_new_line::
; Octants are numbered as follows:
;
; \ 5 | 6 /
; \ | /
; 4 \ | / 7
; \ /
; -----+-----
; /|\
; 3 / | \ 0
; / | \
; / 2 | 1 \
;
; ebx = M0
; ecx = N0
; esi = M1 (dM)
; edi = N1 (dN)
mov [esp].sf_ptfxStart.ptl_x,ebx
mov [esp].sf_ptfxStart.ptl_y,ecx
mov [esp].sf_ptfxEnd.ptl_x,esi
mov [esp].sf_ptfxEnd.ptl_y,edi
;save points in case we have to punt
mov eax,ebx
or eax,ecx
or eax,esi
or eax,edi
and eax,F-1
jnz io_non_integer
;/////////////////////////////////////////////////////////////////////
;// Integer Lines
;/////////////////////////////////////////////////////////////////////
sar ebx,FLOG2 ;x0
sar ecx,FLOG2 ;y0
sar esi,FLOG2 ;x1 (soon to be dx)
sar edi,FLOG2 ;y1 (soon to be dy)
sub esi,ebx
jl io_int_2_3_4_5
jz io_int_radial_90_270
sub edi,ecx
jl io_int_6_7
jz io_int_radial_0
cmp esi,edi
jl io_int_1
je io_int_radial_315
io_int_0:
lea ebp,[esi - 1]
mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + PLUS_X
mov [esp].sf_ulErrorTerm,ebp
jmp io_int_output_line
public io_int_1
io_int_1::
xchg esi,edi
lea ebp,[esi - 1]
mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + PLUS_X + MAJOR_Y
mov [esp].sf_ulErrorTerm,ebp
jmp io_int_output_line
public io_int_2_3_4_5
io_int_2_3_4_5::
neg esi
sub edi,ecx
jl io_int_4_5
jz io_int_radial_180
cmp esi,edi
jl io_int_2
je io_int_radial_225
public io_int_3
io_int_3::
lea ebp,[esi - 1]
mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y
mov [esp].sf_ulErrorTerm,ebp
jmp io_int_output_line
public io_int_2
io_int_2::
xchg esi,edi
mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + MAJOR_Y
mov [esp].sf_ulErrorTerm,esi
jmp io_int_output_line
public io_int_4_5
io_int_4_5::
neg edi
cmp esi,edi
jl io_int_5
je io_int_radial_135
io_int_4:
mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD
mov [esp].sf_ulErrorTerm,esi
jmp io_int_output_line
public io_int_5
io_int_5::
xchg esi,edi
mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + MAJOR_Y
mov [esp].sf_ulErrorTerm,esi
jmp io_int_output_line
public io_int_6_7
io_int_6_7::
neg edi
cmp esi,edi
jg io_int_7
je io_int_radial_45
io_int_6:
xchg esi,edi
lea ebp,[esi - 1]
mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + MAJOR_Y + PLUS_X
mov [esp].sf_ulErrorTerm,ebp
jmp io_int_output_line
public io_int_7
io_int_7::
mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_X
mov [esp].sf_ulErrorTerm,esi
jmp io_int_output_line
;/////////////////////////////////////////////////////////////////////
;// Lines In The 8 Cardinal Directions
;/////////////////////////////////////////////////////////////////////
public io_int_radial_45
io_int_radial_45::
mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_45
jmp short io_int_output_radial
public io_int_radial_135
io_int_radial_135::
mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_135
jmp short io_int_output_radial
public io_int_radial_225
io_int_radial_225::
mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_225
jmp short io_int_output_radial
public io_int_radial_315
io_int_radial_315::
mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_315
jmp short io_int_output_radial
public io_int_radial_90_270
io_int_radial_90_270::
mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_270
mov esi,edi
sub esi,ecx
jg short io_int_output_radial ;if top-to-bottom vertical line
jz io_next_line ;if zero length line
neg esi
mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_90
jmp short io_int_output_radial
public io_int_radial_180
io_int_radial_180::
mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_180
jmp short io_int_output_radial
io_int_radial_0:
mov ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_0
; ebx = x
; ecx = y
; esi = dx (length of radial line since it's normalized)
; ebp = drawing command
public io_int_output_radial
io_int_output_radial::
mov edi,[esp].sf_ppdev
cmp [esp].sf_bSetCP,0
je short io_int_radial_continue_figure
mov edx,[edi].pdev_ioGp_stat_cmd
@@: in ax,dx
and eax,FIFO_4_EMPTY
jnz short @b
mov edx,[edi].pdev_ioCur_x
mov eax,ebx
add eax,[edi].pdev_xOffset
out dx,ax
mov edx,[edi].pdev_ioCur_y
mov eax,ecx
add eax,[edi].pdev_yOffset
out dx,ax
mov [esp].sf_bSetCP,0 ;the next integer line in this figure
; will have the CP set correctly
cmp esi,[esp].sf_ulLastLength
je short @f
mov edx,[edi].pdev_ioMaj_axis_pcnt
mov eax,esi
out dx,ax
mov [esp].sf_ulLastLength,eax
@@:
mov edx,[edi].pdev_ioGp_stat_cmd
mov eax,ebp
out dx,ax
jmp io_next_line
; Jump to here if we don't have to update the current position first:
public io_int_radial_continue_figure
io_int_radial_continue_figure::
cmp esi,[esp].sf_ulLastLength
je short io_int_radial_skip_length
mov edx,[edi].pdev_ioGp_stat_cmd
@@: in ax,dx
and eax,FIFO_2_EMPTY
jnz short @b
mov edx,[edi].pdev_ioMaj_axis_pcnt
mov eax,esi
out dx,ax
mov [esp].sf_ulLastLength,eax
mov edx,[edi].pdev_ioGp_stat_cmd
mov eax,ebp
out dx,ax
jmp io_next_line
; Jump to here if we don't have to update the current position or the
; line length variable:
public io_int_radial_skip_length
io_int_radial_skip_length::
mov edx,[edi].pdev_ioGp_stat_cmd
@@: in ax,dx
and eax,FIFO_1_EMPTY
jnz short @b
mov edx,[edi].pdev_ioGp_stat_cmd
mov eax,ebp
out dx,ax
jmp io_next_line
;/////////////////////////////////////////////////////////////////////
;// Non-Integer Lines
;/////////////////////////////////////////////////////////////////////
public io_non_integer
io_non_integer::
sub esi,ebx
jl io_not_int_2_3_4_5
sub edi,ecx
jl io_not_int_6_7
cmp esi,edi
jl io_not_int_1
je io_not_int_0_slope_one
io_not_int_0:
cmp esi,MAX_GIQ_DELTA
jg io_punt_line
GIQ ROUND_X_AND_Y_DOWN
io_not_int_0_common:
add ecx,edi ;err += dN
mov [esp].sf_ulErrorTerm,ecx
mov eax,[esp].sf_x1
sub eax,ebx
jle io_next_line
mov [esp].sf_cPels,eax
mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + PLUS_X
public io_not_int_x_major
io_not_int_x_major::
mov ecx,[esp].sf_ppdev
mov edx,[ecx].pdev_ioGp_stat_cmd
@@: in ax,dx
and eax,FIFO_7_EMPTY ;wait for 7 entries
jnz short @b
; Fractional end-point lines aren't usually going to have the current
; position already set correctly, so we explicitly set the CP each time:
mov edx,[ecx].pdev_ioCur_x
mov eax,ebx
add eax,[ecx].pdev_xOffset
out dx,ax ;x0
mov edx,[ecx].pdev_ioCur_y
mov eax,ebp
add eax,[ecx].pdev_yOffset
out dx,ax ;y0
mov [esp].sf_bSetCP,1 ;the next integer line in this figure
; will not necessarily have the CP set
; correctly
mov eax,[esp].sf_cPels
cmp eax,[esp].sf_ulLastLength
je short @f
mov edx,[ecx].pdev_ioMaj_axis_pcnt
out dx,ax ;length = cPels
mov [esp].sf_ulLastLength,eax
@@:
mov edx,[ecx].pdev_ioDesty_axstp
mov eax,edi
out dx,ax ;axial = dN
mov edx,[ecx].pdev_ioDestx_diastp
sub eax,esi
out dx,ax ;diag = dN - dM
mov edx,[ecx].pdev_ioErr_term
mov eax,[esp].sf_ulErrorTerm
out dx,ax ;error term
mov edx,[ecx].pdev_ioGp_stat_cmd
mov eax,[esp].sf_ulCmd
out dx,ax ;output it
jmp io_next_line
; Lines of slope one have a special rounding rule: when the line
; runs exactly half way between two pixels, the upper or right pel
; is illuminated. This translates into x=1/2 rounding up in value,
; and y=1/2 rounding down:
public io_not_int_0_slope_one
io_not_int_0_slope_one::
or esi,esi
jz io_next_line ;quick check for a zero length GIQ line
cmp esi,MAX_GIQ_DELTA
jg io_punt_line
GIQ ROUND_Y_DOWN_SLOPE_ONE
jmp io_not_int_0_common
public io_not_int_1
io_not_int_1::
cmp edi,MAX_GIQ_DELTA
jg io_punt_line
GIQR ROUND_X_AND_Y_DOWN
add ecx,esi ;err += dM
mov [esp].sf_ulErrorTerm,ecx
mov eax,[esp].sf_y1
sub eax,ebp
jle io_next_line
mov [esp].sf_cPels,eax
mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + PLUS_X + MAJOR_Y
public io_not_int_y_major
io_not_int_y_major::
mov ecx,[esp].sf_ppdev
mov edx,[ecx].pdev_ioGp_stat_cmd
@@: in ax,dx
and eax,FIFO_7_EMPTY ;wait for 7 entries
jnz short @b
; Fractional end-point lines aren't usually going to have the current
; position already set correctly, so we explicitly set the CP each time:
mov edx,[ecx].pdev_ioCur_x
mov eax,ebx
add eax,[ecx].pdev_xOffset
out dx,ax ;x0
mov edx,[ecx].pdev_ioCur_y
mov eax,ebp
add eax,[ecx].pdev_yOffset
out dx,ax ;y0
mov [esp].sf_bSetCP,1 ;the next integer line in this figure
; will not necessarily have the CP set
; correctly
mov eax,[esp].sf_cPels
cmp eax,[esp].sf_ulLastLength
je short @f
mov edx,[ecx].pdev_ioMaj_axis_pcnt
out dx,ax ;length = cPels
mov [esp].sf_ulLastLength,eax
@@:
mov edx,[ecx].pdev_ioDesty_axstp
mov eax,esi
out dx,ax ;axial = dM
mov edx,[ecx].pdev_ioDestx_diastp
sub eax,edi
out dx,ax ;diag = dM - dN
mov edx,[ecx].pdev_ioErr_term
mov eax,[esp].sf_ulErrorTerm
out dx,ax ;error term
mov edx,[ecx].pdev_ioGp_stat_cmd
mov eax,[esp].sf_ulCmd
out dx,ax ;output it
jmp io_next_line
public io_not_int_2_3_4_5
io_not_int_2_3_4_5::
neg esi ;dM = -dM (now positive)
neg ebx ;M0 = -M0
sub edi,ecx
jl io_not_int_4_5
cmp esi,edi
jl io_not_int_2
io_not_int_3:
cmp esi,MAX_GIQ_DELTA
jg io_punt_line
GIQ ROUND_Y_DOWN
add ecx,edi ;err += dN
mov [esp].sf_ulErrorTerm,ecx
neg ebx ;untransform x0
mov eax,[esp].sf_x1
add eax,ebx
jle io_next_line
mov [esp].sf_cPels,eax
mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y
jmp io_not_int_x_major
public io_not_int_2
io_not_int_2::
cmp edi,MAX_GIQ_DELTA
jg io_punt_line
GIQR ROUND_Y_DOWN
add ecx,esi ;err += dM
mov [esp].sf_ulErrorTerm,ecx
neg ebx ;untransform x0
mov eax,[esp].sf_y1
sub eax,ebp
jle io_next_line
mov [esp].sf_cPels,eax
mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + MAJOR_Y
jmp io_not_int_y_major
public io_not_int_4_5
io_not_int_4_5::
neg edi ;dN = -dN (now positive)
neg ecx ;N0 = -N0
cmp esi,edi
jl io_not_int_5
je io_not_int_4_slope_one
public io_not_int_4
io_not_int_4::
cmp esi,MAX_GIQ_DELTA
jg io_punt_line
GIQ 0
io_not_int_4_common:
add ecx,edi ;err += dN
mov [esp].sf_ulErrorTerm,ecx
neg ebx ;untransform x0
neg ebp ;untransform y0
mov eax,[esp].sf_x1
add eax,ebx
jle io_next_line
mov [esp].sf_cPels,eax
mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD
jmp io_not_int_x_major
; Lines of slope one have a special rounding rule.
public io_not_int_4_slope_one
io_not_int_4_slope_one::
cmp esi,MAX_GIQ_DELTA
jg io_punt_line
GIQ ROUND_X_DOWN_SLOPE_ONE
jmp io_not_int_4_common
public io_not_int_5
io_not_int_5::
cmp edi,MAX_GIQ_DELTA
jg io_punt_line
GIQR 0
add ecx,esi ;err += dM
mov [esp].sf_ulErrorTerm,ecx
neg ebx ;untransform x0
neg ebp ;untransform y0
mov eax,[esp].sf_y1
add eax,ebp
jle io_next_line
mov [esp].sf_cPels,eax
mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + MAJOR_Y
jmp io_not_int_y_major
public io_not_int_6_7
io_not_int_6_7::
neg edi ;dN = -dN (now positive)
neg ecx ;M0 = -M0
cmp esi,edi
je io_not_int_7_slope_one
jg io_not_int_7
public io_not_int_6
io_not_int_6::
cmp edi,MAX_GIQ_DELTA
jg io_punt_line
GIQR ROUND_X_DOWN
add ecx,esi ;err += dM
mov [esp].sf_ulErrorTerm,ecx
neg ebp ;untransform y0
mov eax,[esp].sf_y1
add eax,ebp
jle io_next_line
mov [esp].sf_cPels,eax
mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + MAJOR_Y + PLUS_X
jmp io_not_int_y_major
public io_not_int_7
io_not_int_7::
cmp esi,MAX_GIQ_DELTA
jg io_punt_line
GIQ ROUND_X_DOWN
io_not_int_7_common:
add ecx,edi ;err += dN
mov [esp].sf_ulErrorTerm,ecx
neg ebp ;untransform y0
mov eax,[esp].sf_x1
sub eax,ebx
jle io_next_line
mov [esp].sf_cPels,eax
mov [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_X
jmp io_not_int_x_major
public io_not_int_7_slope_one
io_not_int_7_slope_one::
cmp esi,MAX_GIQ_DELTA
jg io_punt_line
GIQ ROUND_X_DOWN_SLOPE_ONE
jmp io_not_int_7_common
;/////////////////////////////////////////////////////////////////////
;// Punt Line
;/////////////////////////////////////////////////////////////////////
; If the line is too long, we punt to our strip drawing routine.
public io_punt_line
io_punt_line::
mov esi,esp
lea eax,[esp].sf_ptfxStart
lea ebx,[esp].sf_ptfxEnd
cCall bLines,<[esi].sf_ppdev, eax, ebx, 0, 1, 0, \
[esi].sf_prclClip, [esi].sf_apfn, [esi].sf_flags>
mov [esp].sf_bSetCP,1 ;Always reset CP after punting
mov [esp].sf_ulLastLength,-1;Always reset line length after punting
jmp io_next_line
endProc vIoFastLine
end