212 lines
12 KiB
Markdown
212 lines
12 KiB
Markdown
------------------------ --------------------------------- --------------------
|
||
[Previous](48-05.html) [Table of Contents](index.html) [Next](49-02.html)
|
||
------------------------ --------------------------------- --------------------
|
||
|
||
Chapter 49\
|
||
Mode X 256-Color Animation {#Heading1}
|
||
---------------------------
|
||
|
||
### How to Make the VGA Really Get up and Dance {#Heading2}
|
||
|
||
Okay—no amusing stories or informative anecdotes to kick off this
|
||
chapter; lotta ground to cover, gotta hurry—you’re impatient, I can
|
||
smell it. I won’t talk about the time a friend made the mistake of
|
||
loudly saying “\$100 bill” during an animated discussion while walking
|
||
among the bums on Market Street in San Francisco one night, thereby
|
||
graphically illustrating that context is everything. I can’t spare a
|
||
word about how my daughter thinks my 11-year-old floppy-disk-based CP/M
|
||
machine is more powerful than my 386 with its 100-MB hard disk because
|
||
the CP/M machine’s word processor loads and runs twice as fast as the
|
||
386’s Windows-based word processor, demonstrating that progress is not
|
||
the neat exponential curve we’d like to think it is, and that features
|
||
and performance are often conflicting notions. And, lord knows, I can’t
|
||
take the time to discuss the habits of small white dogs, notwithstanding
|
||
that such dogs seem to be relevant to just about every aspect of
|
||
computing, as Jeff Duntemann’s writings make manifest. No lighthearted
|
||
fluff for us; we have real work to do, for today we animate with 256
|
||
colors in Mode X.
|
||
|
||
### Masked Copying {#Heading3}
|
||
|
||
Over the past two chapters, we’ve put together most of the tools needed
|
||
to implement animation in the VGA’s undocumented 320x240 256-color Mode
|
||
X. We now have mode set code, solid and 4x4 pattern fills, system
|
||
memory-to-display memory block copies, and display memory-to-display
|
||
memory block copies. The final piece of the puzzle is the ability to
|
||
copy a nonrectangular image to display memory. I call this *masked
|
||
copying*.
|
||
|
||
Masked copying is sort of like drawing through a stencil, in that only
|
||
certain pixels within the destination rectangle are drawn. The objective
|
||
is to fit the image seamlessly into the background, without the
|
||
rectangular fringe that results when nonrectangular images are drawn by
|
||
block copying their bounding rectangle. This is accomplished by using a
|
||
second rectangular bitmap, separate from the image but corresponding to
|
||
it on a pixel-by-pixel basis, to control which destination pixels are
|
||
set from the source and which are left unchanged. With a masked copy,
|
||
only those pixels properly belonging to an image are drawn, and the
|
||
image fits perfectly into the background, with no rectangular border. In
|
||
fact, masked copying even makes it possible to have transparent areas
|
||
within images.
|
||
|
||
Note that another way to achieve this effect is to implement copying
|
||
code that supports a transparent color; that is, a color that doesn’t
|
||
get copied but rather leaves the destination unchanged. Transparent
|
||
copying makes for more compact images, because no separate mask is
|
||
needed, and is generally faster in a software-only implementation.
|
||
However, Mode X supports masked copying but not transparent copying in
|
||
hardware, so we’ll use masked copying in this chapter.
|
||
|
||
The system memory to display memory masked copy routine in Listing 49.1
|
||
implements masked copying in a straightforward fashion. In the main
|
||
drawing loop, the corresponding mask byte is consulted as each image
|
||
pixel is encountered, and the image pixel is copied only if the mask
|
||
byte is nonzero. As with most of the system-to-display code I’ve
|
||
presented, Listing 49.1 is not heavily optimized, because it’s
|
||
inherently slow; there’s a better way to go when performance matters,
|
||
and that’s to use the VGA’s hardware.
|
||
|
||
**LISTING 49.1 L49-1.ASM**
|
||
|
||
; Mode X (320x240, 256 colors) system memory-to-display memory masked copy
|
||
; routine. Not particularly fast; images for which performance is critical
|
||
; should be stored in off-screen memory and copied to screen via latches. Works
|
||
; on all VGAs. Copies up to but not including column at SourceEndX and row at
|
||
; SourceEndY. No clipping is performed. Mask and source image are both byte-
|
||
; per-pixel, and must be of same widths and reside at same coordinates in their
|
||
; respective bitmaps. Assembly code tested with TASM C near-callable as:
|
||
;
|
||
; void CopySystemToScreenMaskedX(int SourceStartX,
|
||
; int SourceStartY, int SourceEndX, int SourceEndY,
|
||
; int DestStartX, int DestStartY, char * SourcePtr,
|
||
; unsigned int DestPageBase, int SourceBitmapWidth,
|
||
; int DestBitmapWidth, char * MaskPtr);
|
||
|
||
SC_INDEX equ 03c4h ;Sequence Controller Index register port
|
||
MAP_MASK equ 02h ;index in SC of Map Mask register
|
||
SCREEN_SEG equ 0a000h ;segment of display memory in mode X
|
||
|
||
parms struc
|
||
dw 2 dup (?) ;pushed BP and return address
|
||
SourceStartX dw ? ;X coordinate of upper left corner of source
|
||
; (source is in system memory)
|
||
SourceStartY dw ? ;Y coordinate of upper left corner of source
|
||
SourceEndX dw ? ;X coordinate of lower right corner of source
|
||
; (the column at EndX is not copied)
|
||
SourceEndY dw ? ;Y coordinate of lower right corner of source
|
||
; (the row at EndY is not copied)
|
||
DestStartX dw ? ;X coordinate of upper left corner of dest
|
||
; (destination is in display memory)
|
||
DestStartY dw ? ;Y coordinate of upper left corner of dest
|
||
SourcePtr dw ? ;pointer in DS to start of bitmap which source resides
|
||
DestPageBase dw ? ;base offset in display memory of page in
|
||
; which dest resides
|
||
SourceBitmapWidth dw ? ;# of pixels across source bitmap (also must
|
||
; be width across the mask)
|
||
DestBitmapWidth dw ? ;# of pixels across dest bitmap (must be multiple of 4)
|
||
MaskPtr dw ? ;pointer in DS to start of bitmap in which mask
|
||
; resides (byte-per-pixel format, just like the source
|
||
; image; 0-bytes mean don’t copy corresponding source
|
||
; pixel, 1-bytes mean do copy)
|
||
parms ends
|
||
|
||
RectWidth equ -2 ;local storage for width of rectangle
|
||
RectHeight equ -4 ;local storage for height of rectangle
|
||
LeftMask equ -6 ;local storage for left rect edge plane mask
|
||
STACK_FRAME_SIZE equ 6
|
||
.model small
|
||
.code
|
||
public _CopySystemToScreenMaskedX
|
||
_CopySystemToScreenMaskedX proc near
|
||
push bp ;preserve caller’s stack frame
|
||
mov bp,sp ;point to local stack frame
|
||
sub sp,STACK_FRAME_SIZE ;allocate space for local vars
|
||
push si ;preserve caller’s register variables
|
||
push di
|
||
|
||
mov ax,SCREEN_SEG ;point ES to display memory
|
||
mov es,ax
|
||
mov ax,[bp+SourceBitmapWidth]
|
||
mul [bp+SourceStartY] ;top source rect scan line
|
||
add ax,[bp+SourceStartX]
|
||
mov bx,ax
|
||
add ax,[bp+SourcePtr] ;offset of first source rect pixel
|
||
mov si,ax ; in DS
|
||
add bx,[bp+MaskPtr] ;offset of first mask pixel in DS
|
||
|
||
mov ax,[bp+DestBitmapWidth]
|
||
shr ax,1 ;convert to width in addresses
|
||
shr ax,1
|
||
mov [bp+DestBitmapWidth],ax ;remember address width
|
||
mul [bp+DestStartY] ;top dest rect scan line
|
||
mov di,[bp+DestStartX]
|
||
mov cx,di
|
||
shr di,1 ;X/4 = offset of first dest rect pixel in
|
||
shr di,1 ; scan line
|
||
add di,ax ;offset of first dest rect pixel in page
|
||
add di,[bp+DestPageBase] ;offset of first dest rect pixel
|
||
; in display memory
|
||
and cl,011b ;CL = first dest pixel’s plane
|
||
mov al,11h ;upper nibble comes into play when plane wraps
|
||
; from 3 back to 0
|
||
shl al,cl ;set the bit for the first dest pixel’s plane
|
||
mov [bp+LeftMask],al ; in each nibble to 1
|
||
|
||
mov ax,[bp+SourceEndX] ;calculate # of pixels across
|
||
sub ax,[bp+SourceStartX] ; rect
|
||
jle CopyDone ;skip if 0 or negative width
|
||
mov [bp+RectWidth],ax
|
||
sub word ptr [bp+SourceBitmapWidth],ax
|
||
;distance from end of one source scan line
|
||
to start of next
|
||
mov ax,[bp+SourceEndY]
|
||
sub ax,[bp+SourceStartY] ;height of rectangle
|
||
jle CopyDone ;skip if 0 or negative height
|
||
mov [bp+RectHeight],ax
|
||
mov dx,SC_INDEX ;point to SC Index register
|
||
mov al,MAP_MASK
|
||
out dx,al ;point SC Index reg to the Map Mask
|
||
inc dx ;point DX to SC Data reg
|
||
CopyRowsLoop:
|
||
mov al,[bp+LeftMask]
|
||
mov cx,[bp+RectWidth]
|
||
push di ;remember the start offset in the dest
|
||
CopyScanLineLoop:
|
||
cmp byte ptr [bx],0 ;is this pixel mask-enabled?
|
||
jz MaskOff ;no, so don’t draw it
|
||
;yes, draw the pixel
|
||
out dx,al ;set the plane for this pixel
|
||
mov ah,[si] ;get the pixel from the source
|
||
mov es:[di],ah ;copy the pixel to the screen
|
||
MaskOff:
|
||
inc bx ;advance the mask pointer
|
||
inc si ;advance the source pointer
|
||
rol al,1 ;set mask for next pixel’s plane
|
||
adc di,0 ;advance destination address only when
|
||
;wrapping from plane 3 to plane 0
|
||
loop CopyScanLineLoop
|
||
pop di ;retrieve the dest start offset
|
||
add di,[bp+DestBitmapWidth];point to the start of the
|
||
; next scan line of the dest
|
||
add si,[bp+SourceBitmapWidth] ;point to the start of the
|
||
;next scan line of the source
|
||
add bx,[bp+SourceBitmapWidth] ;point to the start of the
|
||
;next scan line of the mask
|
||
dec word ptr [bp+RectHeight] ;count down scan lines
|
||
jnz CopyRowsLoop
|
||
CopyDone:
|
||
pop di ;restore caller’s register variables
|
||
pop si
|
||
mov sp,bp ;discard storage for local variables
|
||
pop bp ;restore caller’s stack frame
|
||
ret
|
||
_CopySystemToScreenMaskedX endp
|
||
end
|
||
|
||
------------------------ --------------------------------- --------------------
|
||
[Previous](48-05.html) [Table of Contents](index.html) [Next](49-02.html)
|
||
------------------------ --------------------------------- --------------------
|
||
|
||
* * * * *
|
||
|
||
Graphics Programming Black Book © 2001 Michael Abrash
|