Image Stretching
I''m working on implementing a GUI for a game I''m working on now. I want my GUI to be resolution independent so all of my controls graphics will need to be stretched from their original size to a suitable size for the current resolution (I understand how to use a virtual coordinate system to accomplish this). I''ve been messing with creating all of my control images so they look good with no stretching at 1600x1200 (the largest resolution I allow) and then stretch them down when dealing with lower resolutions. My results so far have been horrible looking stretched version at my lowest allowed resolution of 640x480. What is the best way to handle a situation like this? Right now I am simply using DirectDraw to blit from the larger surface to the newly created smaller surface (512x512 -> 256x256). What would be a really good formula to use for scaling such surfaces?
// this is what I am currently using to determine
// the size of the new surface based on the current resolution and original surf width
surfwidth = cur res width / (largest res width / original surf width)
// same for height
There will be some loss when dealing with the divisions but I don''t think it will even be visibly noticeable. Is this the way to approach this problem? I know many games use some type of approach like this but I cannot see a visible degrade in the image qualities. I have implemented a system like this for my font engine but have found that using the smallest font as the original (the one that looks good at 640x480) and then scaling up from that gives better results then scaling down from the highest possible resolution. What would be the best way to handle all of this stretching? Thanks for any help/suggestions!
Todd
I don't know if I can solve your problem but I can inform you.
The proper way to reduce an image is to down sample it. The improper way (and much faster) is to remove pixels.
Let's say you downsample an image (reduce it from 500 x 500 to 250 x 250). Each new pixel in the smaller image should contain all the info that was in 4 pixels of the larger image. So 1 pixel is derived from adding the values of a 4 x 4 pixel block together and dividing by 4.
Mipmaps provide a hardware method of interpolating between a series of multiple maps to produce any size downsampled image with fairly good quality.
Update: I realized my error after it was too late. I knew this, just wasn't really thinking as I typed: It shouild be 2x2, not 4x4.
Edited by - bishop_pass on July 7, 2000 4:32:10 AM
The proper way to reduce an image is to down sample it. The improper way (and much faster) is to remove pixels.
Let's say you downsample an image (reduce it from 500 x 500 to 250 x 250). Each new pixel in the smaller image should contain all the info that was in 4 pixels of the larger image. So 1 pixel is derived from adding the values of a 4 x 4 pixel block together and dividing by 4.
Mipmaps provide a hardware method of interpolating between a series of multiple maps to produce any size downsampled image with fairly good quality.
Update: I realized my error after it was too late. I knew this, just wasn't really thinking as I typed: It shouild be 2x2, not 4x4.
Edited by - bishop_pass on July 7, 2000 4:32:10 AM
_______________________________
"To understand the horse you'll find that you're going to be working on yourself. The horse will give you the answers and he will question you to see if you are sure or not."
- Ray Hunt, in Think Harmony With Horses
ALU - SHRDLU - WORDNET - CYC - SWALE - AM - CD - J.M. - K.S. | CAA - BCHA - AQHA - APHA - R.H. - T.D. | 395 - SPS - GORDIE - SCMA - R.M. - G.R. - V.C. - C.F.
"To understand the horse you'll find that you're going to be working on yourself. The horse will give you the answers and he will question you to see if you are sure or not."
- Ray Hunt, in Think Harmony With Horses
ALU - SHRDLU - WORDNET - CYC - SWALE - AM - CD - J.M. - K.S. | CAA - BCHA - AQHA - APHA - R.H. - T.D. | 395 - SPS - GORDIE - SCMA - R.M. - G.R. - V.C. - C.F.
The problem with down sampling is that the resulting image becomes blurred at best. Down sampling is ok for images that or not static (i.e. textures on the side of a space craft, the space craft will be moving most of the time, so you wont have time to see the imperfections).
Another method is to pick a median size say 800 by 600 or 1024,768 then down sample that to smaller resolutions, the blurring effect from 800,600 to 640,480 is not as bad as from 1600,1200.
For larger images you can magnify the image and interpolate the new pixels.
i.e. pixel {10,20} is red and {11,20} is blue
magnify by 2 and you have an extra pixel between {10,20} and {11,20} (now {20,40} and {22,40}), that pixel becomes the average of its neighbors.
The problem with this method is that it softens hard edges. Text becomes ''hazy'', in this case the interpolation takes into account brightness and contrast. You can also apply some filtering on the resulting image (see the filtering article on GDNet, it discusses edge enhancement).
This may seem like a lot of work and may be a bit slow, but if it is for a static display then you need only do it once.
------------------------
Another method is to keep a vector list of all the components on the screen, then just draw the list with the appropriate resolution. Overkill maybe.
Another method is to pick a median size say 800 by 600 or 1024,768 then down sample that to smaller resolutions, the blurring effect from 800,600 to 640,480 is not as bad as from 1600,1200.
For larger images you can magnify the image and interpolate the new pixels.
i.e. pixel {10,20} is red and {11,20} is blue
magnify by 2 and you have an extra pixel between {10,20} and {11,20} (now {20,40} and {22,40}), that pixel becomes the average of its neighbors.
The problem with this method is that it softens hard edges. Text becomes ''hazy'', in this case the interpolation takes into account brightness and contrast. You can also apply some filtering on the resulting image (see the filtering article on GDNet, it discusses edge enhancement).
This may seem like a lot of work and may be a bit slow, but if it is for a static display then you need only do it once.
------------------------
Another method is to keep a vector list of all the components on the screen, then just draw the list with the appropriate resolution. Overkill maybe.
D.V.Carpe Diem
Well, downsampling is actually IMHO the only decent way to go. Forget about mipmapping, as it''s only suitable for random image scaling (i.e. not when you have fixed-size resolutions).
Downsampling should be implemented in hardware in most recent cards, otherwise the algorithm isn''t very complicated anyway. The only trick is to downsample the images once for all, otherwise it''ll be awfully slow without an hardware implementation.
Not much else to add, hope it helps
~Lamtd.
Downsampling should be implemented in hardware in most recent cards, otherwise the algorithm isn''t very complicated anyway. The only trick is to downsample the images once for all, otherwise it''ll be awfully slow without an hardware implementation.
Not much else to add, hope it helps
~Lamtd.
---All you base are belong to us !
Here is a simple example of scaling an image... This is more like of a pseudo code... then you can use different techniques to smoothen out the image a bit...
Hope this helps!
..-=ViKtOr=-..
float StepX = (float) CurrentResX / NewResX;float StepY = (float) CurrentResY / NewResY;float CurX = 0.0,float CurY = 0.0;for (int y = 0; y < NewResY; y++){ for (int x = 0; x < NewResX; x++) { DestSurface[y*NewResX+x] = SrcSurface[(int)CurY * CurrentResX + (int) CurY]; CurX += StepX; } CurY += StepY;}
Hope this helps!
..-=ViKtOr=-..
Thanks for all of the great info. I knew that I had to be doing something wrong but I had no clue how wrong. Thanks!
Todd
Todd
I have been trying to get downsampling to work using a 500x500 image and trying to downsample the image to 250x250. Would I not use a 2x2 pixel block (sum of those divided by 4)? My test have resulted in an image that mostly visible but has some added noise all over the place. What am I missing or doing wrong? I have locked both the source and destination surfaces and obtained the proper pointers to each surface (BTW this is in 16bit mode). Below is the code I am using.
// curwidth, curheight = 500, 500
// newwidth, newheight = 250, 250
StepX = (float) curwidth / newwidth;
StepY = (float) curheight / newheight;
CurX = 0.0f;
CurY = 0.0f;
for(y = 0; y < newheight; y++)
{
for(int x = 0; x < newwidth; x++)
{
USHORT avg =
(source[(int)CurX+(int)CurY*slpitch] +
source[(int)CurX+(int)(CurY+1)*slpitch] +
source[(int)(CurX+1)+(int)CurY*slpitch] +
source[(int)(CurX+1)+(int)(CurY+1)*slpitch]) / 4;
dest[x+y*dlpitch] = avg;
CurX += StepX;
}
CurX = 0.0f;
CurY += StepY;
}
I am sure I must be overlooking something simple. Thanks for any help!
Todd
// curwidth, curheight = 500, 500
// newwidth, newheight = 250, 250
StepX = (float) curwidth / newwidth;
StepY = (float) curheight / newheight;
CurX = 0.0f;
CurY = 0.0f;
for(y = 0; y < newheight; y++)
{
for(int x = 0; x < newwidth; x++)
{
USHORT avg =
(source[(int)CurX+(int)CurY*slpitch] +
source[(int)CurX+(int)(CurY+1)*slpitch] +
source[(int)(CurX+1)+(int)CurY*slpitch] +
source[(int)(CurX+1)+(int)(CurY+1)*slpitch]) / 4;
dest[x+y*dlpitch] = avg;
CurX += StepX;
}
CurX = 0.0f;
CurY += StepY;
}
I am sure I must be overlooking something simple. Thanks for any help!
Todd
Is it possible that you are losing bits while you are doing the averaging, because you are combining 4 shorts into another short...
Which means that you could lose high-order bits.
Thus, try changing avg to DWORD, and then when you place the colour onto the surface type cast it to USHORT.
i.e.:
Hope this helps
--------
Nekosion
--------
Which means that you could lose high-order bits.
Thus, try changing avg to DWORD, and then when you place the colour onto the surface type cast it to USHORT.
i.e.:
// curwidth, curheight = 500, 500
// newwidth, newheight = 250, 250
StepX = (float) curwidth / newwidth;
StepY = (float) curheight / newheight;
CurX = 0.0f;
CurY = 0.0f;
for(y = 0; y < newheight; y++)
{
for(int x = 0; x < newwidth; x++)
{
DWORD avg =
(source[(int)CurX+(int)CurY*slpitch] +
source[(int)CurX+(int)(CurY+1)*slpitch] +
source[(int)(CurX+1)+(int)CurY*slpitch] +
source[(int)(CurX+1)+(int)(CurY+1)*slpitch]) / 4;
dest[x+y*dlpitch] = (USHORT) avg;
CurX += StepX;
}
CurX = 0.0f;
CurY += StepY;
}
Hope this helps
--------
Nekosion
--------
Regards,Nekosion
Good idea but it still has the same result. Since your posting I have taken out the assignment to the "avg" variable and gone directly to assigning it to the actual surface (just to make sure and to remove an unessecary steps). Should the downsampling code work or have I botched the algorithm? Thanks again!
Todd
Todd
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement
Recommended Tutorials
Advertisement