CnC_Red_Alert/WIN32LIB/SRCDEBUG/DRAWSHP.ASM

1129 lines
46 KiB
NASM
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;
; Command & Conquer Red Alert(tm)
; Copyright 2025 Electronic Arts Inc.
;
; This program is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, either version 3 of the License, or
; (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program. If not, see <http://www.gnu.org/licenses/>.
;
;***************************************************************************
;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S **
;***************************************************************************
;* *
;* Project Name : WWLIB32 *
;* *
;* File Name : DRAWSHP.ASM *
;* *
;* Programmer : Phil W. Gorrow *
;* *
;* Start Date : April 13, 1992 *
;* *
;* Last Update : September 14, 1994 [IML] *
;* *
;*-------------------------------------------------------------------------*
;* Functions: *
;* Draw_Shape -- Draws a shape at given buffer coordinates and clips *
;* Not_Supported -- Replacement function for Draw_Shape routines not used*
;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *
;********************* Model & Processor Directives ************************
IDEAL
P386
MODEL USE32 FLAT
; this struct is here to remove the hardwire way of programing
; implemented in the funtion Draw_Shape in ian image of
STRUC VVPC_IMAGE
Off dd ?
Width dd ?
Height dd ?
Page dd ?
ENDS
STRUC GVPC_IMAGE
vvpc VVPC_IMAGE <>
Xpos dd ?
Ypos dd ?
GraphicBuff dd ?
ENDS
;******************************** Includes *********************************
INCLUDE "shape.inc"
;****************************** Declarations ********************************
GLOBAL Draw_Shape:NEAR
GLOBAL LCW_Uncompress:NEAR
GLOBAL _ShapeBuffer:DWORD
GLOBAL _ShapeBufferSize:DWORD
GLOBAL MaskPage : dword
GLOBAL BackGroundPage : dword
;********************************* Data ************************************
DATASEG
;---------------------------------------------------------------------------
; Shape buffer & its size, set by Set_Shape_Buffer()
;---------------------------------------------------------------------------
_ShapeBuffer DD 0
_ShapeBufferSize DD 0
;---------------------------------------------------------------------------
; Address of MaskPage & BackGroundPage, set by Init_Priority_System()
;---------------------------------------------------------------------------
MaskPage DD 0
BackGroundPage DD 0
;---------------------------------------------------------------------------
; Predator effect variables
;---------------------------------------------------------------------------
PredCount DD 0
PredTable DB 1, 3, 2, 5, 4, 3, 2, 1
PredValue DD 1
PartialPred DD 0 ; partially faded predator effect value
PartialCount DD 0
;---------------------------------------------------------------------------
; 32 bit versions of 16 bit stack variables
;---------------------------------------------------------------------------
Flags DD ? ; globally accessible copy of flags
viewport_ptr DD ? ; pointer to upper-left corner of viewport
viewport_width DD ? ; viewport width
viewport_height DD ? ; viewport height
viewport_yadd DD ? ; viewport y add
viewport_x DD ? ; viewport x-coord
viewport_y DD ? ; viewport y-coord
buff_ptr DD ? ; pointer to buffer containing viewport
;********************************* Code ************************************
CODESEG
;***************************************************************************
;* Draw_Shape -- Draws a shape at given buffer coordinates and clips *
;* *
;* INPUT: *
;* DWORD gvp_ptr ; pointer to graphic viewport info *
;* DWORD shape_ptr ; the shape pointer to draw *
;* DWORD draw_x ; x-coord of hotspot in viewport *
;* DWORD draw_y ; y-coord of hotspot in viewport *
;* DWORD flags ; the flags for drawing the shape *
;* *
;* Optional Arguments: If the following flags are used, the given args *
;* MUST be present. Note that, if more than one one set of args is used, *
;* they must appear in this order (alphabetical). *
;* SHAPE_COLOR: DWORD color_table (256 bytes) *
;* SHAPE_FADING: DWORD fade_table (256 bytes), DWORD fade_count *
;* SHAPE_GHOST: DWORD is_translucent tbl, DWORD translucent tbl *
;* SHAPE_PARTIAL: DWORD predator partial_value (0-255) *
;* SHAPE_PRIORITY: DWORD priority_level *
;* SHAPE_SCALING: DWORD x_scale, WORD y_scale *
;* SHAPE_SHADOW: DWORD shadowing_table (256 bytes) *
;* *
;* OUTPUT: *
;* none. *
;* *
;* WARNINGS: *
;* none. *
;* *
;*-------------------------------------------------------------------------*
;* *
;* File Organization: *
;* drawshp.asm : this file *
;* shape.inc : main shape header file; contains declarations for all *
;* globals, procedures and constants *
;* ds_table.asm: contains the procedure address tables for LSkipRout, *
;* RSkipRout, DrawRout, & PixelRout *
;* ds_l*.asm : left-skip routines *
;* ds_r*.asm : right-skip routines *
;* ds_d*.asm : drawing routines *
;* *
;*-------------------------------------------------------------------------*
;* *
;* Shape format: *
;* Header: *
;* UWORD SType (0=normal, 1=16-color, 2=uncompressed, 4=variable-color) *
;* UBYTE Height *
;* UWORD Width *
;* UBYTE unmodified height *
;* UWORD size of shape in memory, including this header *
;* UWORD uncompressed data size *
;* Normal Shape: *
;* UBYTE [compressed] Shape data *
;* 16-color shape: *
;* UBYTE 16-color table *
;* UBYTE [compressed] Shape data *
;* variable-color shape: *
;* UBYTE # colors *
;* UBYTE color table (variable-length) *
;* UBYTE [compressed] Shape data *
;* *
;*-------------------------------------------------------------------------*
;* *
;* Uncompressed shape data format: *
;* Data is stored as a bitmap, with 0's treated as a special case. Every *
;* 0 byte is followed by a repetition count byte. Every scan line is *
;* compressed separately. Thus, the following bitmap results in the *
;* following shape data: *
;* 0 0 0 5 6 7 0 0 0 0 *
;* 0 0 0 5 6 7 0 0 0 0 *
;* 0 0 0 5 6 7 0 0 0 0 *
;* *
;* 0 3 5 6 7 0 4 *
;* 0 3 5 6 7 0 4 *
;* 0 3 5 6 7 0 4 *
;* *
;*-------------------------------------------------------------------------*
;* *
;* How scaling is handled: *
;* Scaling is done by accumulating the x & y scale values. When the high *
;* byte of the accumulated value is set, the pixel (for x-scaling) or *
;* the line (for y-scaling) is drawn. The high byte is then cleared, *
;* and the low byte is left so roundoffs continue to accumulate. *
;* *
;*-------------------------------------------------------------------------*
;* *
;* Drawing Procedures: *
;* *
;*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*
;* *
;* RSkipRout: skips given # bytes of data on the right-hand side of a *
;* shape. Just has to handle changing the current byte offset in the *
;* shape data buffer, since the draw routine knows where the left-hand *
;* side of the drawable region is. The routine may skip more bytes than *
;* it was told if it encounters a run of 0's, but it's assumed that a *
;* run of 0's will never go past the right edge of a shape. *
;* Input: *
;* ECX - number of uncompressed bytes to skip *
;* ESI - shape buffer data address *
;* Output: *
;* ECX - negative # bytes overrun, or 0 *
;* ESI - updated to the current location in the shape data *
;* *
;*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*
;* *
;* LSkipRout: skips given # bytes of data on the left-hand side of a *
;* shape. This routine must update the shape data byte offset, and it *
;* must properly update the current drawing position due to scaling, so *
;* it's a little more involved than the RSkip routine. The routine may *
;* skip more bytes than it's told if it encounters a run of 0's. If this *
;* happens, the draw routine must take these extra bytes into *
;* consideration. *
;* Input: *
;* ECX = number of uncompressed bytes to skip *
;* ESI = shape (source) buffer data address *
;* EDI = viewport (destination) address *
;* [WidthCount] = shape's width in bytes *
;* Output: *
;* ECX - negative # pixels (not bytes) overrun, or 0 *
;* EDX - accumulated XTotal value at new pixel location *
;* ESI - updated to the current location in the shape data *
;* EDI - incr/decr by # pixels (not bytes) overrun *
;* [WidthCount] - decremented by # bytes skipped *
;* *
;*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*
;* *
;* DrawRout: draws one row of pixels, handles scaling, reversal and *
;* any per pixel effects like predator, shadow etc. *
;* EDX must be set up as follows: *
;* - No scaling: 0 *
;* - No left-clipping: 0 *
;* - Left clipping, but no overrun: set to computed initial value for *
;* that viewport coordinate *
;* - Left clipping, with overrun: set to XTotal value for that coordinate *
;* In any case, only the low byte of DL should contain data; the current *
;* byte in the shapebuffer should always be the first drawable pixel, *
;* even if it's partially clipped (in which case DL will contain data). *
;* Input: *
;* ECX = number of pixels (not bytes) to draw *
;* EDX = XTotal initializer value *
;* ESI = shape (source) buffer address *
;* EDI = viewport (destination) address *
;* [WidthCount] = remaining bytes on the line *
;* Output: *
;* ESI - updated to current location in the shape data *
;* EDI - incr/decr by # pixels (not bytes) drawn/skipped *
;* [WidthCount] - decremented by # bytes (not pixels) drawn *
;* *
;*-------------------------------------------------------------------------*
;* *
;* Algorithm: *
;* - Initialize globals *
;* - Pull optional arguments off the stack *
;* - Set up drawing procedure pointers based on drawing flags *
;* - Read the values from the shape header *
;* - Compute the shape's scaled width & height *
;* - Adjust the shape's drawing coordinates based on centering & *
;* viewport-relative flag settings *
;* - Compute the clipped areas of the shape *
;* - Compute the number of drawn pixels horizontally & vertically *
;* - Compute the starting drawing offset in the viewport *
;* - Draw the shape *
;* *
;*-------------------------------------------------------------------------*
;* *
;* HISTORY: *
;* 04/13/1992 PWG : Created. *
;* 08/19/1993 SKB : Split drawshp.asm into several modules. *
;* 05/26/1994 BR : Converted to 32-bit, restructured quite a bit. *
;* 08/09/1994 IML : Added C++ style interface. Various optimizations. *
;* 09/06/1994 IML : Ammendments for integration of p_* and ds_* routines.*
;* 09/14/1994 IML : Now handles LCW compression. *
;*=========================================================================*
PROC Draw_Shape C NEAR
USES eax,ebx,ecx,edx,edi,esi
;--------------------------------------------------------------------
; Arguments:
;--------------------------------------------------------------------
ARG gvp_ptr:DWORD ; pointer to graphic viewport info
ARG shape_ptr:DWORD ; the shape pointer to draw
ARG draw_x:DWORD ; the destination x pixel
ARG draw_y:DWORD ; the destination y pixel
ARG flags:DWORD ; the flags for drawing the shape
IF FALSE
;--------------------------------------------------------------------
; Define the local stack variables that Draw_Shape needs. These
; parameters are defined in shape.inc. They're included here
; just for reference.
;--------------------------------------------------------------------
;
;...................... proc addresses ..............................
;
LOCAL LSkipRout:DWORD ; pointer to the skip routine
LOCAL RSkipRout:DWORD ; pointer to the skip routine
LOCAL DrawRout:DWORD ; pointer to the draw routine
;
;.................... optional arguments ............................
;
LOCAL ColorTable:DWORD ; ptr to the shapes color table
LOCAL FadingTable:DWORD ; extracted fading table pointer
LOCAL FadingNum:DWORD ; get the number of times to fade
LOCAL IsTranslucent:DWORD ; ptr to "are we translucent?" tbl
LOCAL Translucent:DWORD ; ptr to "ok we are translucent!" tbl
LOCAL PriLevel:BYTE ; the priority level of the object
LOCAL ScaleX:DWORD ; the x increment to scale by
LOCAL ScaleY:DWORD ; the y increment to scale by
LOCAL ShadowingTable:DWORD ; ptr to the shadowing table
;
;.................... Shape header values ...........................
;
LOCAL ShapeType:DWORD ; shape type
LOCAL ShapeWidth:DWORD ; shape's unscaled width
LOCAL ShapeHeight:DWORD ; shape's unscaled height
LOCAL UncompDataLen:DWORD ; uncompressed data length
LOCAL ShapeData:DWORD ; pointer to [compressed] shape data
;
;.................. Scaled shape dimensions .........................
;
LOCAL ScaledWidth:DWORD ; shape's scaled width
LOCAL ScaledHeight:DWORD ; shape's scaled height
;
;.................. Pixel clipping variables ........................
;
LOCAL LeftClipPixels:DWORD ; # left-clipped pixels
LOCAL RightClipPixels:DWORD ; # right-clipped pixels
LOCAL TopClipPixels:DWORD ; # top-clipped pixels
LOCAL BotClipPixels:DWORD ; # bottom-clipped pixels
LOCAL PixelWidth:DWORD ; width of drawable area in pixels
LOCAL PixelHeight:DWORD ; height of drawable area in pixels
;
;..................... Drawing variables ............................
;
LOCAL NumColors:DWORD ; # colors for 16-color shapes
LOCAL StartDraw:DWORD ; ptr to starting draw position
LOCAL NextLine:DWORD ; offset of next drawing line
LOCAL LeftClipBytes:DWORD ; # left-clipped bytes
LOCAL XTotal:DWORD ; accumulated x-pixels for scaling
LOCAL XTotalInit:DWORD ; initial roundoff bits for XTotal
LOCAL YTotal:DWORD ; accumulated y-pixels for scaling
LOCAL HeightCount:DWORD ; height counter for drawing lines
LOCAL LineStart:DWORD ; address of start of line
LOCAL WidthCount:DWORD ; counts down # bytes skipped
LOCAL StashReg:DWORD ; temp variable for draw routines
LOCAL MaskAdjust:DWORD ; priority buffer offset
LOCAL BackAdjust:DWORD ; background buffer offset
LOCAL StashECX:DWORD ; temp variable for ECX register
LOCAL StashEDX:DWORD ; temp variable for EDX register
ENDIF
;====================================================================
; Initialization:
; - allocate space for globals
; - validate shape pointer
; - set SHAPE_COMPACT flag if needed
;====================================================================
;--------------------------------------------------------------------
; Allocate stack space for our local variables.
;--------------------------------------------------------------------
LOCAL Local_Stack:BYTE:Local_Size
;--------------------------------------------------------------------
; Make sure the shape pointer is not NULL
;--------------------------------------------------------------------
cmp [shape_ptr],0 ; compare shape ptr value to NULL
jnz ??valid_shp ; if non-zero, it's valid
jmp ??exit ; otherwise get the heck outta here
;--------------------------------------------------------------------
; Move gvp info into local variables
;--------------------------------------------------------------------
??valid_shp:
mov edi,[gvp_ptr] ; get pointer to graphic viewport info
mov esi, [(type GVPC_IMAGE ptr edi). vvpc . Off ] ; extract viewport pointer
mov [viewport_ptr],esi
mov ebx,[(type GVPC_IMAGE ptr edi) . vvpc . Width ] ; extract viewport width
mov [viewport_width],ebx
mov eax,[(type GVPC_IMAGE ptr edi) . vvpc . Height ] ; extract viewport height
mov [viewport_height],eax
mov ecx,[(type GVPC_IMAGE ptr edi) . vvpc . Page ] ; calculate y add value
add ecx,ebx
mov [viewport_yadd],ecx
mov eax,[(type GVPC_IMAGE ptr edi) . Xpos ] ; extract viewport x-coord
mov [viewport_x],eax
mov eax, [(type GVPC_IMAGE ptr edi) . Ypos ] ; extract viewport y-coord
mov [viewport_y],eax
mul ecx ; calculate buffer pointer
add eax,[viewport_x]
sub esi,eax
mov [buff_ptr],esi
;--------------------------------------------------------------------
; If this shape is a compact shape, set that bit in the flags arg
;--------------------------------------------------------------------
mov edi,[shape_ptr] ; check for compact shape flag
test [BYTE PTR edi],MAKESHAPE_COMPACT
jz ??do_args ; if not process flags as is
or [flags],SHAPE_COMPACT ; mark it as a compact shape
;====================================================================
; Pull off optional arguments:
; EDI is used as an offset from the 'flags' parameter, to point
; to the optional argument currently being processed.
;====================================================================
??do_args:
mov edi,4 ; optional params start past flags
;--------------------------------------------------------------------
; Initialize optional argument values:
;--------------------------------------------------------------------
mov [ColorTable],0 ; default = NULL
mov [FadingTable],0 ; default = NULL
mov [FadingNum],0 ; default = no fading
mov [IsTranslucent],0 ; default = NULL
mov [Translucent],0 ; default = NULL
mov [PriLevel],0 ; default = no priority
mov [ScaleX],100h ; default = unity X scaling
mov [ScaleY],100h ; default = unity Y scaling
mov [ShadowingTable],0 ; default = NULL
;--------------------------------------------------------------------
; SHAPE_COLOR: DWORD color_table[256]
;--------------------------------------------------------------------
??color:
test [flags],SHAPE_COLOR ; does it have a color table
jz ??fading ; if not skip to fading
or [flags],SHAPE_COMPACT ; mark it as a compact shape
; (for remapping purposes only)
mov eax,[flags + edi]
mov [ColorTable],eax ; save address of color table
add edi,4 ; point to next optional argument
;--------------------------------------------------------------------
; SHAPE_FADING: DWORD fade_table[256], DWORD fade_count
;--------------------------------------------------------------------
??fading:
test [flags],SHAPE_FADING ; are we fading this shape
jz ??ghost ; skip to ghosting check
mov eax,[flags + edi]
mov [FadingTable],eax ; save address of fading tbl
mov eax,[flags + edi + 4] ; get fade num
add edi,8 ; next argument
cmp eax,0 ; check if it's 0
jnz ??set_fading ; if not, store fade num
and [flags],NOT SHAPE_FADING ; otherwise, don't fade
??set_fading:
mov [FadingNum],eax
;--------------------------------------------------------------------
; SHAPE_GHOST: DWORD is_translucent tbl, DWORD translucent tbl
;--------------------------------------------------------------------
??ghost:
test [flags],SHAPE_GHOST ; are we ghosting this shape
jz ??init_predator ; skip to predator check
mov eax,[flags + edi]
mov [IsTranslucent],eax ; save ptr to is_trans. tbl
mov eax,[flags + edi + 4]
mov [Translucent],eax ; save ptr to translucent tbl
add edi,8 ; next argument
;--------------------------------------------------------------------
; SHAPE_PREDATOR: Initialize the predator effect variables
;--------------------------------------------------------------------
??init_predator:
test [flags],SHAPE_PREDATOR ; is predator effect on
jz ??partial ; if not skip to partial
inc [PredCount] ; the pred table is byte aligned
and [PredCount],PRED_MASK ; keep entries within bounds
mov eax,[PredCount]
mov al,[BYTE PTR PredTable + eax]
mov [PredValue],eax ; put the pred value cs
mov [PartialCount],0 ; clear the partial count
mov [PartialPred],100h ; init partial to off
;--------------------------------------------------------------------
; SHAPE_PARTIAL: DWORD partial_pred_value (0-255)
;--------------------------------------------------------------------
??partial:
test [flags],SHAPE_PARTIAL ; is this a partial pred?
jz ??priority ; if not check priority
mov eax,[flags + edi] ; pull the partial value
mov [PartialPred],eax ; store it off
add edi,4 ; next argument
;--------------------------------------------------------------------
; SHAPE_PRIORITY: DWORD priority_level
;--------------------------------------------------------------------
??priority:
test [flags],SHAPE_PRIORITY ; is this a priority draw
jz ??scale ; if not skip to scale
mov eax,[flags + edi]
mov [PriLevel],al ; store priority level
add edi,4 ; next argument
mov eax,[MaskPage] ; calculate priority buffer
sub eax,[buff_ptr] ; offset
mov [MaskAdjust],eax
mov eax,[BackGroundPage] ; calculate background buffer
sub eax,[buff_ptr] ; offset
mov [BackAdjust],eax
;--------------------------------------------------------------------
; SHAPE_SCALING: DWORD x_scale, WORD y_scale
;--------------------------------------------------------------------
??scale:
test [flags],SHAPE_SCALING ; are we scaling this shape.
jz ??shadow ; if not then skip scale y value
mov eax,[flags + edi]
mov [ScaleX],eax
mov eax,[flags + edi + 4]
mov [ScaleY],eax
add edi,8 ; next argument
;--------------------------------------------------------------------
; SHAPE_SHADOW: DWORD shadow_table[256]
;--------------------------------------------------------------------
??shadow:
test [flags],SHAPE_SHADOW ; are we ghosting this shape
jz short ??get_header ; if not then skip
mov eax,[flags + edi]
mov [ShadowingTable],eax ; save address of shadow table
add edi,4 ; next argument
??get_header:
;====================================================================
; Get Shape header values
;====================================================================
mov esi,[shape_ptr] ; prepare to read header
movzx eax,[WORD PTR esi]
mov [ShapeType],eax ; extract shape type
movzx eax,[BYTE PTR esi + 2]
mov [ShapeHeight],eax
movzx eax,[WORD PTR esi + 3] ; extract shape height
mov [ShapeWidth],eax
movzx eax,[WORD PTR esi + 8] ; extract uncompressed data length
mov [UncompDataLen],eax
add esi,10 ; reposition index
;--------------------------------------------------------------------
; Now get NumColors, ColorTable address, & data pointer:
; <16-color shape:
; shape.Colortable[0] = # colors
; shape data is after that many colors
; 16-color shape:
; shape.Colortable[] contains colors
; shape data is after those colors
; default 256-color shape:
; shape data starts at shape.Colortable[0]
; Note: ColorTable is set only if flags & SHAPE_COLOR is 0; otherwise,
; the color table was passed in & we already have a pointer to it
;--------------------------------------------------------------------
;
;....................... <16-color shape: ...........................
;
test [ShapeType],MAKESHAPE_VARIABLE
jz ??check_16
movzx eax,[BYTE PTR esi] ; read # colors
mov [NumColors],eax ; save # colors
inc esi
test [flags],SHAPE_COLOR ; don't set ColorTable if
jnz ??norm_get_data_addr ; it was passed in
mov [ColorTable],esi ; save color table pointer
??norm_get_data_addr:
add esi,[NumColors] ; skip past color data
mov [ShapeData],esi ; set data address
jmp ??setup_procs
;....................... 16-color shape: ............................
??check_16:
test [ShapeType],MAKESHAPE_COMPACT
jz ??256_get_data_addr
mov [NumColors],16 ; save # colors
test [flags],SHAPE_COLOR ; don't set ColorTable if
jnz ??16_get_data_addr ; it was passed in
mov [ColorTable],esi ; save color table pointer
??16_get_data_addr:
add esi,[NumColors] ; skip past color data
mov [ShapeData],esi ; set data address
jmp ??setup_procs
;
;....................... 256-color shape: ...........................
;
??256_get_data_addr:
mov [ShapeData],esi ; set data address
;====================================================================
; Set up the drawing procedure addresses
;====================================================================
;--------------------------------------------------------------------
; This code uses HORZ_REV, VERT_REV, & SCALING flags as an
; offset into the LSkipTable, RSkipTable, and DrawTable. These
; flags combined have values from 00h-07h, so each table must have
; at least 8 entries.
;--------------------------------------------------------------------
??setup_procs:
mov ebx,[flags] ; load flags value
and ebx,07h ; clip high bits
add ebx,ebx ; mult by 4 to get DWORD offset
add ebx,ebx
mov eax,[LSkipTable + ebx] ; get table value
mov [LSkipRout],eax ; store it in the function pointer
mov eax,[RSkipTable + ebx] ; get table value
mov [RSkipRout],eax ; store it in the function pointer
mov eax,[DrawTable + ebx] ; get table value
mov [DrawRout],eax ; store it in the function pointer
??compute_scalevals:
;====================================================================
; Now compute scaled width & height. If the shape scales down to 0
; either horizontally or vertically, exit.
;====================================================================
test [flags],SHAPE_SCALING ; skip if no scaling
jz ??no_scaling
;
;........................ scaled width: .............................
;
mov eax,[ShapeWidth] ; get byte width
mov ebx,[ScaleX] ; prepare for register mul
mul ebx ; EDX:EAX = result
shrd eax,edx,8 ; EAX = result rounded down
or eax,eax
jz ??exit ; exit if EAX is 0
mov [ScaledWidth],eax ; save the scaled width
;
;........................ scaled height: ............................
;
mov eax,[ShapeHeight] ; get byte height
mov ebx,[ScaleY] ; prepare for register mul
mul ebx ; EDX:EAX = result
shrd eax,edx,8 ; EAX = result rounded down
or eax,eax
jz ??exit ; exit if EAX is 0
mov [ScaledHeight],eax ; save the scaled height
jmp ??handle_centering
;
;......................... no scaling: ..............................
;
??no_scaling:
mov eax,[ShapeWidth]
mov [ScaledWidth],eax ; pixel width = byte width
mov eax,[ShapeHeight]
mov [ScaledHeight],eax ; pixel height = byte height
;====================================================================
; Allow for SHAPE_CENTER by adjusting the draw_x & draw_y arguments:
; draw_x -= ScaledWidth / 2
; draw_y -= ScaledHeight / 2
;====================================================================
??handle_centering:
;
;........................ adjust draw_x .............................
;
test [flags],SHAPE_CENTER ; skip if not centered
jz ??handle_vp_rel
mov eax,[draw_x] ; load in draw_x
mov edx,[ScaledWidth] ; load in ScaledWidth
shr edx,1 ; divide it by 2
sub eax,edx ; subract it from eax
mov [draw_x],eax ; store it back into draw_x
;
;........................ adjust draw_y .............................
;
mov eax,[draw_y] ; load in draw_y
mov edx,[ScaledHeight] ; load in ScaledHeight
shr edx,1 ; divide it by 2
sub eax,edx ; subract it from eax
mov [draw_y],eax ; store it back into draw_y
;====================================================================
; Allow for SHAPE_VIEWPORT_REL by adjusting draw_x & draw_y by the
; viewport's coordinates
;====================================================================
??handle_vp_rel:
test [flags],SHAPE_VIEWPORT_REL ; skip if not vp-relative
jz ??compute_horz_clip
mov eax,[viewport_x]
add [draw_x],eax ; draw_x += viewport_x
mov eax,[viewport_y]
add [draw_y],eax ; draw_y += viewport_y
;====================================================================
; Now that we have the scaled size and adjusted x & y drawing
; coordinates, we can compute the clipped areas of the shape:
; LeftClipPixels = viewport_x - draw_x
; - if negative, set to 0
; RightClipPixels = (draw_x + ScaledWidth) -
; (viewport_x + viewport_width)
; - if negative, set to 0
;
; TopClipPixels = viewport_y - draw_y
; - if negative, set to 0
; BotClipPixels = (draw_y + ScaledHeight) -
; (viewport_y + viewport_height)
; - if negative, set to 0
;====================================================================
??compute_horz_clip:
;
;...................... left-clipped pixels .........................
;
mov eax,[viewport_x]
sub eax,[draw_x] ; EAX = viewport_x - draw_x
jge ??set_left_clip
mov eax,0 ; if EAX<0, set to 0
??set_left_clip:
mov [LeftClipPixels],eax ; store # left-clipped pixels
;
;...................... right-clipped pixels ........................
;
mov eax,[draw_x]
add eax,[ScaledWidth] ; EAX = draw_x + ScaledWidth
mov edx,[viewport_x]
add edx,[viewport_width] ; EDX = viewport_x + viewport_width
sub eax,edx
jge ??set_right_clip
mov eax,0 ; if EAX<0, set to 0
??set_right_clip:
mov [RightClipPixels],eax ; store # right-clipped pixels
;
;...................... top-clipped pixels ..........................
;
??compute_vert_clip:
mov eax,[viewport_y]
sub eax,[draw_y] ; EAX = viewport_y - draw_y
jge ??set_top_clip
mov eax,0 ; if EAX<0, set to 0
??set_top_clip:
mov [TopClipPixels],eax ; store # top-clipped pixels
;
;.................... bottom-clipped pixels .........................
;
mov eax,[draw_y]
add eax,[ScaledHeight] ; EAX = draw_y + ScaledHeight
mov edx,[viewport_y]
add edx,[viewport_height] ; EDX = viewport_y + viewport_height
sub eax,edx
jge ??set_bottom_clip
mov eax,0 ; if EAX<0, set to 0
??set_bottom_clip:
mov [BotClipPixels],eax ; store # bottom-clipped pixels
;====================================================================
; Now compute the number of pixels actually drawn, horizontally and
; vertically; exit if either is <= 0
;====================================================================
??compute_drawn_pixels:
;
;.................... pixel width of drawn area .....................
;
mov eax,[ScaledWidth] ; get total width in pixels
sub eax,[LeftClipPixels] ; subtract off left-clipped pixels
sub eax,[RightClipPixels] ; subtract off right-clipped pixels
jle ??exit ; exit if no horizontal pixels drawn
mov [PixelWidth],eax ; store drawn pixel width
;
;.................... pixel height of drawn area ....................
;
mov eax,[ScaledHeight] ; get total height in pixels
sub eax,[TopClipPixels] ; subtract off top-clipped pixels
sub eax,[BotClipPixels] ; subtract off bottom-clipped pixels
jle ??exit ; exit if no horizontal pixels drawn
mov [PixelHeight],eax ; store drawn pixel height
;====================================================================
; So, we're actually going to draw something; if (ShapeType &
; MAKESHAPE_NOCOMP == 0) decompress the shape data into _ShapeBuffer:
; LCW_Uncompress(ShapeData, _ShapeBuffer, UncompDataLen);
; shape.DataLength
; &_ShapeBuffer
; &(shape's data)
; - otherwise the shape data is already uncompressed
;====================================================================
test [ShapeType],MAKESHAPE_NOCOMP
jnz ??uncompressed
mov eax,[UncompDataLen]
push eax ; push arg 3
mov eax,[_ShapeBuffer]
push eax ; push arg 2
mov eax,[ShapeData]
push eax ; push arg 1
call LCW_Uncompress ; call routine
add esp,12 ; restore stack
mov eax,[_ShapeBuffer]
mov [ShapeData],eax
jmp ??copy_flags
??uncompressed:
; mov eax,[ShapeData] ; set up pointer to shape data
; mov [_ShapeBuffer],eax
;--------------------------------------------------------------------
; Set the global Flags variable
;--------------------------------------------------------------------
??copy_flags:
mov eax,[flags]
mov [Flags],eax
;====================================================================
; Now compute the actual buffer offset where drawing (not skipping)
; will begin
;====================================================================
;--------------------------------------------------------------------
; First, compute the x & y offsets of the shape's clipped upper-left
; corner, relative to the viewport's upper-left corner:
; x-offset = draw_x + LeftClipPixels - viewport_x
; y-offset = draw_y + TopClipPixels - viewport_y
;--------------------------------------------------------------------
mov ebx,[draw_x]
add ebx,[LeftClipPixels]
sub ebx,[viewport_x] ; EBX = viewport x-offset
mov eax,[draw_y]
add eax,[TopClipPixels]
sub eax,[viewport_y] ; EAX = viewport y-offset
;--------------------------------------------------------------------
; Then, adjust the viewport offsets due to horizontal & vertical
; reversal:
; if HORZ_REV, x-offset += (PixelWidth - 1)
; if VERT_REV, y-offset += (PixelHeight - 1)
;--------------------------------------------------------------------
;
;................. Adjust for horizontal reversal ...................
;
test [flags],SHAPE_HORZ_REV
jz ??adjust_vert_offset
add ebx,[PixelWidth]
dec ebx ; EBX = true x-offset
;
;................ Swap LeftClip & RightClip pixels ..................
;
mov edx,[LeftClipPixels] ; exchange left & right-clipped pixels
xchg edx,[RightClipPixels]
mov [LeftClipPixels],edx
;
;.................. Adjust for vertical reversal ....................
;
??adjust_vert_offset:
test [flags],SHAPE_VERT_REV
jz ??adjust_pointer
add eax,[PixelHeight]
dec eax ; EAX = true y-offset
;
;.................. Swap TopClip & BotClip pixels ...................
;
mov edx,[TopClipPixels]
xchg edx,[BotClipPixels]
mov [TopClipPixels],edx
;--------------------------------------------------------------------
; Now, adjust the starting position pointer:
;--------------------------------------------------------------------
??adjust_pointer: ;!!!!!!! convert to register mul for speed !!!!!!!!
add ebx,[viewport_ptr] ; add initial ptr to x-offset
mul [viewport_yadd] ; convert y-offset (EAX) to bytes
add ebx,eax ; add those bytes in
mov [StartDraw],ebx ; store the starting pointer
;--------------------------------------------------------------------
; Finally, if VERT_REV, negate yadd to move up not down:
;--------------------------------------------------------------------
test [flags],SHAPE_VERT_REV
jz ??init_xtotal
neg [viewport_yadd] ; move up, not down
;====================================================================
; Initialize the horizontal scale accumulation value:
; If there are any left-clipped pixels, the scale accumulator will
; have to be initialized with the value it >would< have by stepping
; over that many pixels. This initial value can be computed by
; dividing the # of left-clipped pixels by the x-scale value itself,
; picking off the remainder from this division & negating it. This
; sets the low byte of the remainder to the correct accumulation
; value (the high bytes will be garbage).
; (The alternative to this approach would be to multiply the
; scale factor by the # clipped bytes, which is the result of the
; division; however, negating the remainder is much faster than
; the multiply would be.)
;====================================================================
??init_xtotal:
mov edx,0 ; prepare for divide
mov eax,[LeftClipPixels] ; get # left-clipped pixels
shl eax,8 ; multiply by 100h
mov ebx,[ScaleX] ; load ScaleX value
div bx ; 16-bit div: AX = rslt, DX = rem
mov [LeftClipBytes],eax ; save # left-clipped bytes
neg edx ; generate roundoff bits
and edx,0Fh ; only save low byte
mov [XTotalInit],edx ; save initial roundoff value
;====================================================================
; Initialize drawing variables:
;====================================================================
mov esi,[ShapeData] ; ESI = shape buffer starting point
mov edi,[StartDraw] ; EDI = drawing address
mov [YTotal],0 ; initialize accumulated scale
;====================================================================
; Clip the top-clipped lines. The object here is to set ESI to the
; first drawable line in the _ShapeBuffer, and YTotal to:
; high byte = # times to draw that line,
; low byte = roundoff bits
;
; - Initialize values (ESI, HeightCount, YTotal)
; - Skip loop if no top lines to clip
; - Loop:
; - save this line's byte position in _ShapeBuffer, in case we
; overrun
; - call RSkipRout with ECX set to # bytes to skip (one row)
; - accumulate ScaleY into YTotal
; - if high byte is non-zero, there are that many drawn lines:
; - decrement HeightCount by that many lines
; - clear the high byte in YTotal, but keep the roundoff bits
; - if HeightCount > 0, loop again to clip more lines
; - if HeightCount is 0, start drawing:
; - ESI points to first non-clipped line in _ShapeBuffer
; - YTotal contains 0 in high byte, roundoff bits in low byte
; - otherwise, we've clipped too many lines:
; - put ESI back to the line we just clipped
; - set high byte of YTotal to # lines overrun
; - subtract ScaleY from YTotal, to set it up for drawing loop
;====================================================================
;
;..................... skip if nothing to clip ......................
;
mov eax,[TopClipPixels]
cmp eax,0 ; see if any top-clipped pixels
jz ??draw_loop ; if not, skip this
mov [HeightCount],eax ; save off # lines to clip
??clip_y_loop:
;
;...................... skip this row of bytes ......................
;
mov [LineStart],esi ; save this line's byte position
mov ecx,[ShapeWidth] ; set up ECX for RSkipRout
call [RSkipRout] ; skip 'ShapeWidth' bytes
;
;............... see if this row would have been drawn ..............
;
mov eax,[ScaleY]
add [YTotal],eax ; accumulate scale factor
test [YTotal],0FF00h ; check to see if we draw the line
jz ??clip_y_loop ; if not loop again
;
;...................... decrement HeightCount .......................
;
mov eax,0 ; clear EAX
xchg al,[BYTE PTR YTotal+1] ; get # lines, clear it in YTotal
sub [HeightCount],eax ; subtract # drawn lines from HtCt
jg ??clip_y_loop ; if more lines remain, loop again
jns ??draw_loop ; is exactly 0; we're done clipping
;
;....................... adjust for overrun .........................
;
mov esi,[LineStart] ; point ESI back to this line
mov eax,[HeightCount]
neg eax ; EAX = # lines overrun
shl eax,8 ; multiply by 100h
add eax,[YTotal] ; add in roundoff bits
sub eax,[ScaleY] ; adjust down by y-scale
mov [YTotal],eax ; store in YTotal
;====================================================================
; The drawing loop (at long last!):
; - Accumulate YTotal; if high byte is 0, skip this row of bytes &
; loop again
; - Skip left-clipped pixels:
; - If we've skipped all the bytes on the line, just go to the
; next line
; - Draw middle pixels:
; - Add the shape's pixel width to ECX (which could be negative
; if we left-skipped into the drawable area)
; - If ECX is still 0, there are no pixels to draw
; - Otherwise, leave ECX as is & draw the pixels
; - Skip right-clipped pixels:
; - Add # right-clipped pixels to ECX (which could be negative if
; the draw routine uncompressed 0's into the right-clipped
; region)
; - if ECX > 0, skip the remaining bytes
; - Go to the next line:
; - point EDI to the start of the next line in the viewport
; - decrement the height counter, exit if it's 0
; - decrement YTotal's high byte
; - if it's 0, go to the loop top
; - otherwise, reset ESI to this line's start & redraw the line,
; starting at left-clipped pixels
; (NOTE: why not start drawing at middle pixels??????????)
;====================================================================
??draw_loop:
;
;................... accumulate YTotal & test it ....................
;
mov eax,[ScaleY] ; get y scaling factor
add [YTotal],eax ; accumulate YTotal
test [YTotal],0FF00h ; see if we need to draw anything
jnz ??draw_line ; draw this line
;
;......................... skip this line ...........................
;
mov ecx,[ShapeWidth] ; load shape's width in bytes
call [RSkipRout] ; skip this row & loop again
jmp ??draw_loop
;
;--------------------- start drawing this line ----------------------
;
??draw_line:
mov [LineStart],esi ; save current byte position
;....................................................................
; Skip left-clipped pixels:
; - initialize [WidthCount] to total shape width in bytes
; - set ECX to # >bytes< to clip
; When LSkipRout returns:
; - ECX will contain # >pixels< overrun
; - EDX will contain the XTotal init value
; - [WidthCount] will be decremented by total bytes skipped
;....................................................................
??draw_left:
mov eax,[ShapeWidth] ; load shape width
mov [WidthCount],eax ; set up for LSkipRout
mov ecx,[LeftClipBytes] ; bytes, not pixels!
call [LSkipRout] ; skip the bytes
cmp [WidthCount],0
jz ??next_line ; The whole line was 0's
;....................................................................
; Draw middle pixels:
; - add PixelWidth to ECX (which may be negative)
; - if ECX is 0, don't bother drawing
; When DrawRout returns:
; - ECX will contain # >pixels< overrun
; - [WidthCount] will be decremented by # bytes drawn
;....................................................................
??draw_middle:
add ecx,[PixelWidth] ; since ECX could overrun, add width
jle ??draw_right ; if ECX<=0, no middle pixels to draw
call [DrawRout] ; draw the pixels
;
;................... skip past right-clipped pixels .................
;
??draw_right:
mov ecx,[WidthCount] ; ECX = remaining # bytes
jecxz ??next_line ; don't bother if no bytes remain
call [RSkipRout] ; skip right-clipped bytes
;
;----------------------- go to the next line ------------------------
;
??next_line:
;
;................. adjust EDI to start of next line .................
;
mov eax,[viewport_yadd] ; get yadd
add [StartDraw],eax ; add it to this line's position
mov edi,[StartDraw] ; EDI = next line
;
;................. decrement our pixel row counter ..................
;
dec [PixelHeight] ; count down a line
jz ??exit ; we're done!
;
;.................. decrement YTotal's high byte ....................
;
dec [BYTE PTR YTotal + 1] ; decrement high byte
jz ??draw_loop ; draw next line if 0
;
;....................... re-draw this line ..........................
;
mov esi,[LineStart] ; reset to this line's start
jmp ??draw_left ; redraw this line
??exit:
ret
ENDP Draw_Shape
;***************************************************************************
;* Not_Supported -- Replacement function for Draw_Shape routines not used. *
;* *
;* INPUT: *
;* none. *
;* *
;* OUTPUT: *
;* none. *
;* *
;* WARNINGS: *
;* none. *
;* *
;* HISTORY: *
;* 08/24/1993 SKB : Created. *
;*=========================================================================*
PROC Not_Supported NOLANGUAGE NEAR
ret
ENDP Not_Supported
END
;************************** End of drawshp.asm *****************************