Bitmap Scaling

Intro

The problem at hand is to (attempt to) scale a bitmap to a larger or smaller size. Many games use different variations of the same idea. Here I’d like to start with a very simple example and build a faster and faster scaling algorythm. For now, you’ll have to settle with this simple example until i have more time on my hands. With no further ado, lets see some code!

C++ Code

  1. void ScaleToFit(short x,short y, short x2, short y2)
  2. {
  3.     float xstep,ystep,xratio,yratio;
  4.     short xdiff,ydiff,xcount,ycount;
  5.     long VStart= vid->Screen_Width*y2;
  6.  
  7.     ydiff=y2-y;
  8.     xdiff=x2-x;
  9.     xratio=(float)BitmapWidth/xdiff;
  10.     yratio=(float)BitmapHeight/ydiff;
  11.  
  12.     for(ystep=0,ycount=y;   ycount<ydiff; ystep+=yratio,ycount++)
  13.     {
  14.         for(xstep=0,xcount=x;xcount<xdiff; xstep+=xratio,xcount++)
  15.         {
  16.             video_buffer[(y2-ycount)*Screen_Width+xcount]=
  17.             Bmp[(short)ystep*(short)BitmapWidth+(short)xstep];
  18.         }
  19.     }
  20. }

That's all it takes to scale a bitmap. One thing I'd like to point out is that since .bmp files are stored in reverse scan line order, this function will flip them vertically. Since I'm going to be using this function for object bitmaps (vials, flames, potions, items) it won't matter. I was nice enough to flip the bitmap right side up though :)

We first want to find the distance between the x's and y's of where this thing is supposed to be going. To do that xdiff and ydiff are created and set accordingly. You should notice that this code is dependent on x2 and y2 being greater than x and y accordingly. If it isn't call it good bye :)

Now that we have our raw distance lets divide our bitmap dimensions by it, so we are actually splitting it up into that many pieces. xratio and yratio store this information. In our loops we know for sure that the pixels on the screen will have a 1:1 ratio, meaning that every iteration of the x loop, the pixel will move to the next pixel. Likewise in the y loop, the starting point for the next line will move down one pixel. ycount and xcount provide these mechanisms. Since they will be incrementing by 1 per loop, lets have there increment be ycount++ and xcount++ accordingly. Up to this point our function has the ability to move across and down on the screen, filling in the area requested, but with what??

This would make a pretty good solid colored rectangle filler, but hey that's not what it supposed to be is it :) Here comes the part that actually lets us *scale* the bitmap to fit the area. Since we have already divided up our source bitmap into pieces and put them into xratio and yratio, what if we used these as incrementers to other variables that were indexes into the bitmap!? This is where xstep and ystep come into play. Every iteration of the inner loop, xstep is increased by xratio (xstep+=xratio), and in the outer loop, ystep is increased by yratio. Both xstep and ystep are moving along in our divided increments decided by xratio and yratio. Since we can't access floating point elements in an array, we can type cast them as shorts and when our float gets to the next highest integer, it will switch to the next pixel in the source bitmap. This method works great because we can scale our bitmap larger or smaller than its original size. If we scale it smaller, then xratio and yratio will be numbers larger than one, and they will skip some pixels when it comes to the final drawing. Check back soon, i'll try to get some faster versions posted!

C++ Code Utilizing Fixed Point Math

  1. void ScaleToFit(short x0,short y0, short x1, short y1)
  2. {
  3.     int x,y,u,v,xratio,yratio,VStart=vid->Screen_Width*y1+x0;
  4.     unsigned char* destRow, *dest, *sourceRow;
  5.  
  6.     xratio=(BitmapWidth << 16)/(x1-x0);
  7.     yratio=(BitmapHeight << 16)/(y1-y0);
  8.     destRow=video_buffer;
  9.     destRow+=VStart;
  10.  
  11.     for(y=y0;y<y1;y++)
  12.     {
  13.         u=0;
  14.         sourceRow=Bmp + (v >> 16) *BitmapWidth;
  15.         dest=destRow;
  16.         for(x=x0;x<x1;x++)
  17.         {
  18.             *dest++ = sourceRow[u >> 16];
  19.             u+=xratio;
  20.         }
  21.         v+=yratio;
  22.         destRow-=Screen_Width;
  23.     }
  24. }

This one is very close to the first. It does the same thing, except it utilizes fixed point math. Again, this one flips the bitmap right side up. (man am i nice!) It's a LOT faster compared to the first one. One note: If you are using a 16bit compiler you have to change the shifts to 8 bits of precision instead of 16. While I'm at it, WHY ARE YOU USING A 16 BIT COMPILER!? DJGPP is a 32 bit Protected Mode compiler and its FREE! I've been using it for about 6 months now and its AWESOME!

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>