;========================================================================= ; XSCALE1.ASM by John A. Slagel, jas37876@uxa.cso.uiuc.edu ; This is some code to do bitmap scaling in VGA Mode X. It can scale a ; bitmap of any size down to 2 pixels wide, or up to thousands of pixels ; wide. It performs complete clipping, with only a small constant amount ; of time to clip, no matter how huge the image is. It draws column by ; column to reduce the number of plane switches, which are slow. The inner ; column loop has been optimized for no memory accesses, except to read or ; write a pixel. This uses MASM 5.1 features, and can be compiled in any ; memory model by changing the .MODEL line, but make sure that you always ; pass a far pointer to the bitmap data, regardless of memory model. ; C-callable as: ; void XSCALE1( int X, int Y, int DW, int DY, ; int SW, int SH, void far * Bitmap ); ; X,Y are the upper left-hand coordinates of where to draw the bitmap. ; DW,DH are the width and height of the SCALEed bitmap ; SW,SH are the width and height of the source bitmap. ; Bitmap is a pointer to the bitmap bits. ; ; Routine has been modified for 32 bit protected mode by John McCarthy. ; John McCarthy thanks John A. Slagel for providing this code, and hopes ; John A. Slagel is not offended by the changes. ; ;========================================================================== public draw_scale public tdraw_scale public xscale1 public xscale2 public repeat_bitmap ; uses edi esi, destx:word, desty:word, ; destwidth:word, destheight:word, ; sourcewidth:word, sourceheight:word, ; bitmap:far ptr draw_scale: mov bitmap,esi mov destwidth,ax mov destheight,bx mov destx,cx mov desty,dx xscale1: cmp destwidth, 2 ; if destination width is less than 2 jl done ; then don't draw it. cmp destheight, 2 ; if destination height is less than 2 jl done ; then don't draw it. mov ax, desty ; if it is completely below the cmp ax, clipbt ; lower clip bondry, jg done ; then don't draw it. add ax, destheight ; if it is above clip boundries dec ax ; then don't draw it. cmp ax, cliptp jl done mov ax, destx ; if it is to the right of the mov cx, cliprt ; right clip boundry cmp ax, cliprt ; then don't draw it. jg done add ax, destwidth ; if it is completely to the left dec ax ; of the left clip boundry, cmp ax, cliplt ; then don't draw it. jl done mov esi, bitmap ; make esi point to bitmap data lodsw ; get source x width mov sourcewidth,ax lodsw ; get source y height mov sourceheight,ax mov ax, destwidth ; clippedwidth is initially set to mov clippedwidth, ax ; the requested dest width. shl ax,1 ; initialize the x decision var neg ax ; to be -2*destwidth mov decisionx, ax ; mov ax, destheight ; clippedheight is initially set to mov clippedheight, ax ; the requested dest size. shl ax,1 ; initialize the y decision var neg ax ; to be -2*destheight mov decisiony, ax ; movsx eax, cliptp ; if y is below the top mov edx, eax ; clipping boundry, then we don't sub dx, desty ; need to clip the top, so we can js s notopclip ; jump over the clipping stuff. mov desty, ax ; this block performs clipping on the sub clippedheight, dx ; top of the bitmap. i have heavily movsx ecx, sourceheight ; optimized this block to use only 4 imul ecx, edx ; 32-bit registers, so i'm not even mov eax, ecx ; gonna try to explain what it's doing. cdq ; but i can tell you what results from movsx ebx, destheight ; this: the decisiony var is updated idiv ebx ; to start at the right clipped row. movsx edx, sourcewidth ; y is moved to the top clip imul edx, eax ; boundry. clippedheight is lowered since add esi, edx ; we won't be drawing all the requested imul eax, ebx ; rows. esi is changed to point over sub ecx, eax ; the bitmap data that is clipped off. sub ecx, ebx ; shl ecx, 1 ; mov decisiony, cx ; notopclip: mov ax, desty ; if the bitmap doesn't extend over the add ax, clippedheight ; bottom clipping boundry, then we dec ax ; don't need to clip the bottom, so we cmp ax, clipbt ; can jump over the bottom clip code. jle s nobottomclip ; mov ax, clipbt ; clip off the bottom by reducing the sub ax, desty ; clippedheight so that the bitmap won't inc ax ; extend over the lower clipping mov clippedheight, ax ; boundry. nobottomclip: movsx eax, cliplt ; if x is to the left of the mov edx, eax ; top clipping boundry, then we don't sub dx, destx ; need to clip the left, so we can js s noleftclip ; jump over the clipping stuff. mov destx, ax ; this block performs clipping on the sub clippedwidth, dx ; left of the bitmap. i have heavily movsx ecx, sourcewidth ; optimized this block to use only 4 imul ecx, edx ; 32-bit registers, so i'm not even mov eax, ecx ; gonna try to explain what it's doing. cdq ; but i can tell you what results from movsx ebx, destwidth ; this: the decisionx var is updated idiv ebx ; to start at the right clipped column. add esi, eax ; x is moved to the left clip imul eax, ebx ; boundry. clippedwidth is reduced since sub ecx, eax ; we won't be drawing all the requested sub ecx, ebx ; cols. esi is changed to point over shl ecx, 1 ; the bitmap data that is clipped off. mov decisionx, cx ; noleftclip: mov ax, destx ; if the bitmap doesn't extend over the add ax, clippedwidth ; right clipping boundry, then we dec ax ; don't need to clip the right, so we cmp ax, cliprt ; can jump over the right clip code. jle s noclipright ; mov ax, cliprt ; clip off the right by reducing the sub ax, destx ; clippedwidth so that the bitmap won't inc ax ; extend over the right clipping mov clippedwidth, ax ; boundry. ;calculate starting video address noclipright: movzx edi, desty ; we are going to set edi to start point imul edi, xactual/4 movzx eax, destx ; the offset edi is mov cx, ax ; calculated by: shr ax, 2 ; di = y*80+x/2 add eax, current_page add edi,eax ; edi is ready! mov dx, sc_index ; point the vga sequencer to the map mov al, map_mask ; mask register, so that we only need out dx, al ; to send out 1 byte per column. inc dx ; move to the sequencer's data register. and cx, 3 ; calculate the starting plane. this is mov al, 11h ; just: shl al, cl ; plane = (11h << (x and 3)) out dx, al ; select the first plane. movzx ecx, sourcewidth ; use cx for source width mov xad, ecx align 4 ; since this point gets jumped to a lot, ; make sure that it is dword aligned. rowloop: push esi ; save the starting source index push edi ; save the starting dest index push ax ; save the current plane mask push bp ; save the current base pointer mov cx, clippedheight ; use al for row counter (0-239) mov bx, decisiony ; use bx for decision variable mov dx, sourceheight ; use dx for source height * 2 shl dx, 1 mov bp, destheight ; use bp for dest height * 2 shl bp, 1 mov ah, [esi] ; get the first source pixel align 4 ; common jump point... align for speed. columnloop: mov [edi], ah ; draw a pixel dec cx ; decrement line counter jz s donewithcol ; see if we're done with this column add edi, xactual/4 ; go on to the next screen row add bx, dx ; increment the decision variable js s columnloop ; draw this source pixel again incsourcerow: add esi, xad ; move to the next source pixel sub bx, bp ; decrement the decision variable jns s incsourcerow ; see if we need to skip another source pixel mov ah, [esi] ; get the next source pixel jmp s columnloop ; start drawing this pixel donewithcol: pop bp ; restore bp to access variables pop ax ; restore al = plane mask pop edi ; restore di to top row of screen pop esi ; restore si to top row of source bits rol al, 1 ; move to next plane adc edi, 0 ; go on to next screen column mov dx, sc_data ; tell the vga what column we're in out dx, al ; by updating the map mask register mov bx, decisionx ; use bx for the x decision variable add bx, sourcewidth ; increment the x decision variable add bx, sourcewidth js s nextcol ; jump if we're still in the same source col. mov dx, destwidth ; dx = w * 2 shl dx, 1 incsourcecol: inc esi ; move to next source column sub bx, dx ; decrement x decision variable jns s incsourcecol ; see if we skip another source column nextcol: mov decisionx, bx ; free up bx for colloop dec clippedwidth ; if we're not at last column jnz rowloop ; then do another column done: ret ; we're done! ; draw transparent bitmap. any bytes that = 0 are skipped tdraw_scale: mov bitmap,esi mov destwidth,ax mov destheight,bx mov destx,cx mov desty,dx xscale2: cmp destwidth, 2 ; if destination width is less than 2 jl s done ; then don't draw it. cmp destheight, 2 ; if destination height is less than 2 jl s done ; then don't draw it. mov ax, desty ; if it is completely below the cmp ax, clipbt ; lower clip bondry, jg s done ; then don't draw it. add ax, destheight ; if it is above clip boundries dec ax ; then don't draw it. cmp ax, cliptp jl s done mov ax, destx ; if it is to the right of the mov cx, cliprt ; right clip boundry cmp ax, cliprt ; then don't draw it. jg s done add ax, destwidth ; if it is completely to the left dec ax ; of the left clip boundry, cmp ax, cliplt ; then don't draw it. jl s done mov esi, bitmap ; make esi point to bitmap data lodsw ; get source x width mov sourcewidth,ax lodsw ; get source y height mov sourceheight,ax mov ax, destwidth ; clippedwidth is initially set to mov clippedwidth, ax ; the requested dest width. shl ax,1 ; initialize the x decision var neg ax ; to be -2*destwidth mov decisionx, ax ; mov ax, destheight ; clippedheight is initially set to mov clippedheight, ax ; the requested dest size. shl ax,1 ; initialize the y decision var neg ax ; to be -2*destheight mov decisiony, ax ; movsx eax, cliptp ; if y is below the top mov edx, eax ; clipping boundry, then we don't sub dx, desty ; need to clip the top, so we can js s notopclip2 ; jump over the clipping stuff. mov desty, ax ; this block performs clipping on the sub clippedheight, dx ; top of the bitmap. i have heavily movsx ecx, sourceheight ; optimized this block to use only 4 imul ecx, edx ; 32-bit registers, so i'm not even mov eax, ecx ; gonna try to explain what it's doing. cdq ; but i can tell you what results from movsx ebx, destheight ; this: the decisiony var is updated idiv ebx ; to start at the right clipped row. movsx edx, sourcewidth ; y is moved to the top clip imul edx, eax ; boundry. clippedheight is lowered since add esi, edx ; we won't be drawing all the requested imul eax, ebx ; rows. esi is changed to point over sub ecx, eax ; the bitmap data that is clipped off. sub ecx, ebx ; shl ecx, 1 ; mov decisiony, cx ; notopclip2: mov ax, desty ; if the bitmap doesn't extend over the add ax, clippedheight ; bottom clipping boundry, then we dec ax ; don't need to clip the bottom, so we cmp ax, clipbt ; can jump over the bottom clip code. jle s nobottomclip2 mov ax, clipbt ; clip off the bottom by reducing the sub ax, desty ; clippedheight so that the bitmap won't inc ax ; extend over the lower clipping mov clippedheight, ax ; boundry. nobottomclip2: movsx eax, cliplt ; if x is to the left of the mov edx, eax ; top clipping boundry, then we don't sub dx, destx ; need to clip the left, so we can js s noleftclip2 ; jump over the clipping stuff. mov destx, ax ; this block performs clipping on the sub clippedwidth, dx ; left of the bitmap. i have heavily movsx ecx, sourcewidth ; optimized this block to use only 4 imul ecx, edx ; 32-bit registers, so i'm not even mov eax, ecx ; gonna try to explain what it's doing. cdq ; but i can tell you what results from movsx ebx, destwidth ; this: the decisionx var is updated idiv ebx ; to start at the right clipped column. add esi, eax ; x is moved to the left clip imul eax, ebx ; boundry. clippedwidth is reduced since sub ecx, eax ; we won't be drawing all the requested sub ecx, ebx ; cols. esi is changed to point over shl ecx, 1 ; the bitmap data that is clipped off. mov decisionx, cx ; noleftclip2: mov ax, destx ; if the bitmap doesn't extend over the add ax, clippedwidth ; right clipping boundry, then we dec ax ; don't need to clip the right, so we cmp ax, cliprt ; can jump over the right clip code. jle s noclipright2 mov ax, cliprt ; clip off the right by reducing the sub ax, destx ; clippedwidth so that the bitmap won't inc ax ; extend over the right clipping mov clippedwidth, ax ; boundry. ;calculate starting video address noclipright2: movsx edi, desty ; we are going to set edi to start point imul edi, xactual/4 movsx eax, destx ; the offset edi is mov cx, ax ; calculated by: shr eax, 2 ; edi = y*80+x/2 add eax, current_page add edi,eax ; edi is ready! mov dx, sc_index ; point the vga sequencer to the map mov al, map_mask ; mask register, so that we only need out dx, al ; to send out 1 byte per column. inc dx ; move to the sequencer's data register. and cx, 3 ; calculate the starting plane. this is mov al, 11h ; just: shl al, cl ; plane = (11h << (x and 3)) out dx, al ; select the first plane. movzx ecx, sourcewidth ; use cx for source width mov xad, ecx align 4 ; since this point gets jumped to a lot, ; make sure that it is dword aligned. rowloop2: push esi ; save the starting source index push edi ; save the starting dest index push ax ; save the current plane mask push bp ; save the current base pointer mov cx, clippedheight ; use al for row counter (0-239) mov bx, decisiony ; use bx for decision variable mov dx, sourceheight ; use dx for source height * 2 shl dx, 1 mov bp, destheight ; use bp for dest height * 2 shl bp, 1 mov ah, [esi] ; get the first source pixel cmp ah,0 je s null_loop ; if zero, perform null loop align 4 ; common jump point... align for speed. columnloop2: mov [edi], ah ; draw a pixel dec cx ; decrement line counter jz s donewithcol2 ; see if we're done with this column add edi, xactual/4 ; go on to the next screen row add bx, dx ; increment the decision variable js s columnloop2 ; draw this source pixel again incsourcerow2: add esi, xad ; move to the next source pixel sub bx, bp ; decrement the decision variable jns s incsourcerow2 ; see if we need to skip another source pixel mov ah, [esi] ; get the next source pixel cmp ah,0 jz s null_loop jmp s columnloop2 ; start drawing this pixel donewithcol2: pop bp ; restore bp to access variables pop ax ; restore al = plane mask pop edi ; restore di to top row of screen pop esi ; restore si to top row of source bits rol al, 1 ; move to next plane adc edi, 0 ; go on to next screen column mov dx, sc_data ; tell the vga what column we're in out dx, al ; by updating the map mask register mov bx, decisionx ; use bx for the x decision variable add bx, sourcewidth ; increment the x decision variable add bx, sourcewidth js s nextcol2 ; jump if we're still in the same source col. mov dx, destwidth ; dx = w * 2 shl dx, 1 incsourcecol2: inc esi ; move to next source column sub bx, dx ; decrement x decision variable jns s incsourcecol2 ; see if we skip another source column nextcol2: mov decisionx, bx ; free up bx for colloop dec clippedwidth ; if we're not at last column jnz rowloop2 ; then do another column done2: ret ; we're done! align 4 ; common jump point... align for speed. null_loop: dec cx ; decrement line counter jz s donewithcol2 ; see if we're done with this column add edi, xactual/4 ; go on to the next screen row add bx, dx ; increment the decision variable js s null_loop ; perform more increments jmp s incsourcerow2 ; draw repeated bit map. good for backgrounds in menus and title screens. ; routine is NOT intended for animation because it is slow. uses scale routine ; because scale routine clips bitmaps. sloppy routine just draws all over the ; place and lets the scale clip borders handle the rest. ; ; repeat_bitmap (seg bitmap, x1%, y1%, x2%, y2%) ; ; remember: first two words of bitmap define width and height rb_stack struc rb_wide dw ? ; height and width of bitmap rb_height dw ? rb_curx dw ? ; current bitmap location rb_cury dw ? rb_oldy2 dw ? ; old cliping borders save rb_oldx2 dw ? rb_oldy1 dw ? rb_oldx1 dw ? dd ?x3 ; edi, esi, ebp dd ? ; caller rb_y2 dw ? ; y2 rb_x2 dw ? ; x2 rb_y1 dw ? ; y1 rb_x1 dw ? ; x1 rb_bitmap dd ? ; offset to bitmap rb_stack ends repeat_bitmap: push ebp esi edi ; preserve important registers sub esp, 16 ; allocate workspace mov ebp, esp ; set up stack frame mov ax,cliplt ; save old borders just in case mov [ebp].rb_oldx1,ax mov ax,cliprt mov [ebp].rb_oldx2,ax mov ax,cliptp mov [ebp].rb_oldy1,ax mov ax,clipbt mov [ebp].rb_oldy2,ax mov x1,4 mov ax,[ebp].rb_x1 ; set new borders for clipping mov cliplt,ax mov ax,[ebp].rb_x2 mov cliprt,ax mov ax,[ebp].rb_y1 mov cliptp,ax mov ax,[ebp].rb_y2 mov clipbt,ax mov [ebp].rb_curx,0 ; we could start at x1,y1 but this mov [ebp].rb_cury,0 ; will make offset backgrounds mov esi,[ebp].rb_bitmap mov bitmap,esi lodsw ; set destination width same as original mov [ebp].rb_wide,ax lodsw mov [ebp].rb_height,ax nextline: mov ax,[ebp].rb_wide mov destwidth,ax mov ax,[ebp].rb_height mov destheight,ax mov ax,[ebp].rb_curx mov destx,ax mov ax,[ebp].rb_cury mov desty,ax push ebp call xscale2 ; draw a transparent bitmap pop ebp mov ax,[ebp].rb_curx add ax,[ebp].rb_wide mov [ebp].rb_curx,ax cmp ax,[ebp].rb_x2 jle s nextline mov [ebp].rb_curx,0 mov ax,[ebp].rb_cury add ax,[ebp].rb_height mov [ebp].rb_cury,ax cmp ax,[ebp].rb_y2 jle s nextline mov ax,[ebp].rb_oldx1 mov cliplt,ax mov ax,[ebp].rb_oldx2 mov cliprt,ax mov ax,[ebp].rb_oldy1 mov cliptp,ax mov ax,[ebp].rb_oldy2 mov clipbt,ax add esp, 16 pop edi esi ebp ret 12