Better Gradients? [Resolved]

Started by
5 comments, last by Drew_Benton 18 years, 7 months ago
Right now I'm working on my own GUI system in SDL. I am having some problems trying to do proper gradiants. I have a little system implemented, but it's not giving me satisfactory results. What I want: Image Hosted by ImageShack.us What I have: Image Hosted by ImageShack.us So as you can see, I'm close to what I'm aiming for, I just can't figure out how to get it done correctly. I thought it was as simple as finding the range of values I want to transverse over a distance (width), but for some reason, I just can't get to the end! In addition, I'm trying to get it so it will work on a width of any size, well there will be a minimum of course for the range to work. This is what happens right now as well, the longer title looks nicer and is closer to what I want for shorter values: Free Image Hosting at www.ImageShack.us My code for the algorithm I made up is as follows:

int StartR = 10;
int StartG = 36;
int StartB = 106;

int EndR = 166;
int EndG = 202;
int EndB = 240;

int CurR = StartR;
int CurG = StartG;
int CurB = StartB;

float Cur_dR = 0;
float Cur_dG = 0;
float Cur_dB = 0;

float dR = (float)(EndR - StartR) / (float)(width - 6);
dR = ceil(1.0f / dR);

float dG = (float)(EndG - StartG) / (float)(width - 6);
dG = ceil(1.0f / dG);

float dB = (float)(EndB - StartB) / (float)(width - 6);
dB = ceil(1.0f / dB);

for( int ctr = 3; ctr < width - 3; ctr++ )
{
	destRect.x = ctr;
	destRect.y = 3;
	destRect.w = 1;
	destRect.h = 18;
	SDL_FillRect( surface, &destRect, SDL_MapRGB(displayScreen->format, CurR, CurG, CurB ) );
        Cur_dR++;
        Cur_dG++;
	Cur_dB++;

	if( Cur_dR >= dR )
	{
		Cur_dR = 0;
		CurR++;
	}
	if( Cur_dG >= dG )
	{
		Cur_dG = 0;
		CurG++;
	}
	if( Cur_dB >= dB )
	{
		Cur_dB = 0;
		CurB++;
	}
}




Does anyone have any ideas/suggestions as to how I can improve upon this? Thanks for your time. [Edited by - Drew_Benton on September 12, 2005 2:57:07 AM]
Advertisement
This might work better
int StartR = 10;int StartG = 36;int StartB = 106;int EndR = 166;int EndG = 202;int EndB = 240;for( int ctr = 3; ctr < width - 3; ctr++ ){	destRect.x = ctr;	destRect.y = 3;	destRect.w = 1;	destRect.h = 18;	float t = ((float)(ctr-3))/((float)(width-6));	int r = ((float)StartR)*(1.0f-t)+((float)EndR)*t;	int g = ((float)StartG)*(1.0f-t)+((float)EndG)*t;	int b= ((float)StartB)*(1.0f-t)+((float)EndB)*t;	SDL_FillRect( surface, &destRect, SDL_MapRGB(displayScreen->format, r, g, b ) );}
Quote:Original post by mike25025
This might work better*** Source Snippet Removed ***


Oh wow Mike, that's freaking perfect [grin] Thanks so much!

Image Hosted by ImageShack.us

Image Hosted by ImageShack.us

[smile] If I do anything with this, I'll make sure to put you in the credits [wink]. Quick question though, can you explain what you just did? I see it works great, but I don't really understand what's going on.
Its just simple interpolation.

Say we want to interpolate two numbers A and B. We need a factor to control how much of A and B to use (T). This factor is beween 0 (same as A with no interpolation) and 1 (same as B with no interpolaion). The math looks like this:

I = A(1-T) + BT
You can also do it the way you were originally trying to, which is faster and will get the same result, aside from any round-off error (totally insignificant in this case). You were on the right track, just a couple of squirrely calculations going on that I didn't quite understand :)

The changes I've made here are that CurR/G/B are now floats, and dR/G/B aren't reciprocal'd anymore. Then instead of all that incrementing and if statements, just add dR/G/B to CurR/G/B.
int StartR = 10;int StartG = 36;int StartB = 106;int EndR = 166;int EndG = 202;int EndB = 240;float CurR = (float)StartR;float CurG = (float)StartG;float CurB = (float)StartB;float dR = (float)(EndR - StartR) / (float)(width - 6);float dG = (float)(EndG - StartG) / (float)(width - 6);float dB = (float)(EndB - StartB) / (float)(width - 6);for( int ctr = 3; ctr < width - 3; ctr++ ){	destRect.x = ctr;	destRect.y = 3;	destRect.w = 1;	destRect.h = 18;	SDL_FillRect( surface, &destRect, SDL_MapRGB(displayScreen->format, (int)CurR, (int)CurG, (int)CurB ) );        Cur_dR += dR;        Cur_dG += dG;	Cur_dB += bB;}


It's the same interpolation technique as Mike used, but done incrementally instead of doing the full calculation every time though the loop.
it is just a linear interpolation and the results look spiffy. but I think it's better to calculate this with delta and incriments. for efficency sake.

for example the R channel

float dR = (Rstop - Rstart)/(float)width;
float R = rstart;

for(0 to width) {
plot((int)R);
R += dR;
}

this is completely equivilant to RStart*(1 - t) + t*Rstop

except for numerical drift. also if you're concerned about the incremental algorithm over long spans(you shouldn't tho) note that the linear interpolation fomula.

(1 - t)*start + t*end;

is equivilant to

start + t*(end - start);

which is more efficently implimented as it requires only one multiplication, also (end - start) can be precomputed.

Tim
Quote:Its just simple interpolation.

Say we want to interpolate two numbers A and B. We need a factor to control how much of A and B to use (T). This factor is beween 0 (same as A with no interpolation) and 1 (same as B with no interpolaion). The math looks like this:

I = A(1-T) + BT


Ok cool, thanks for that. You really do learn something new everyday [wink]. I'll have to look into more on the topic of interpolation, I mean I've heard of it and have an idea of how it's used a lot in graphics, but I've never used it before (just because I'm now just learning all this graphics stuff). Anyways, thanks a bunch again.

Quote:The changes I've made here are that CurR/G/B are now floats, and dR/G/B aren't reciprocal'd anymore. Then instead of all that incrementing and if statements, just add dR/G/B to CurR/G/B.


Oh wow, that works too [smile]. I just kinda though together that code and tweaked until it worked, but I see where I was a little off. The main reason for the recpricals were only because I couldn't see that I really should be using floats, which I thought about, but then knew the SDL_MapRGB takes ints (don't ask me why, but I had just dumbly assumed I couldn't use floats and typecast them [headshake] ). Anyways, a great thanks to you to for your example as well! I am wrapping it up into a quick little function that I will expand upon later.

Quote:it is just a linear interpolation and the results look spiffy. but I think it's better to calculate this with delta and incriments. for efficency sake.

for example the R channel
...
except for numerical drift. also if you're concerned about the incremental algorithm over long spans(you shouldn't tho) note that the linear interpolation fomula.
...
which is more efficently implimented as it requires only one multiplication, also (end - start) can be precomputed.


Ok cool, I see what you mean about simplifying a few operations. Thanks for pointing that out. I probabally won't use that specific code for this little function, but I know I will eventually need it for something else, so I'll make sure to keep it handy with the optimizations.

Ok thanks again to everyone! This is what the final code looks like.

void TitleBarGradient( SDL_Color start, SDL_Color end )	{		SDL_Rect destRect;		destRect.y = 3;		destRect.w = 1;		destRect.h = 18;		float CurR = (float)start.r;		float CurG = (float)start.g;		float CurB = (float)start.b;		float dR = (float)(end.r - start.r) / (float)(width - 6);		float dG = (float)(end.g - start.g) / (float)(width - 6);		float dB = (float)(end.b - start.b) / (float)(width - 6);		for( destRect.x = 3; destRect.x < width - 3; destRect.x++ )		{			SDL_FillRect( surface, &destRect, SDL_MapRGB( displayScreen->format, (int)CurR, (int)CurG, (int)CurB ) );			CurR += dR;			CurG += dG;			CurB += dB;		}	}


And the usage in the function that creates the GUI
// Create a gradient across the title bar emulating the windows styleSDL_Color start = { 10, 36, 106, 0 };SDL_Color end = { 166, 202, 240, 0 };TitleBarGradient( start, end );


It also works perfectly when I switch the gradiants, so this problem has been resolved perfectly! I plan to eventually make this data driven to allow for full customization, but for now, lots of hardcoding.

In case you are wondering, the way all this works is as follows -
I have my Dialog class that represents, well a dialog. It contains a SDL_Surface* that serves as the primary drawing surface for it. I then call a create function once to draw the entire format of the dialog (in this case, the Window's based design) onto the surface. Now, I just blit the single surface onto the main screen and it's all done! Ok back to work now, sorry for the delay, had a lot of problems posting for the past 9 hours.

This topic is closed to new replies.

Advertisement