A while ago on another forums we were talking about doing this with php - not so much to do it for production use, but as a general coding exercise... What I came up with was this:
Code:
<?php
function styleGradient($width,$height,$color) {
$image=imagecreatetruecolor($width,$height);
$scaleWidth=$width-1;
$scaleHeight=$height-1;
if (is_array($color[0])) {
$r=$color[0][0];
$g=$color[0][1];
$b=$color[0][2];
} else {
$r=($color[0] >> 16) & 0xFF;
$g=($color[0] >> 8) & 0xFF;
$b=$color[0] & 0xFF;
}
if (is_array($color[3])) {
$endR=$color[3][0];
$endG=$color[3][1];
$endB=$color[3][2];
} else {
$endR=($color[3] >> 16) & 0xFF;
$endG=($color[3] >> 8) & 0xFF;
$endB=$color[3] & 0xFF;
}
$incR=($endR-$r)/$scaleHeight;
$incG=($endG-$g)/$scaleHeight;
$incB=($endB-$b)/$scaleHeight;
$leftColors=array();
for ($y=0; $y<=$height; $y++) {
$leftColors[$y]=array($r,$g,$b);
$r+=$incR;
$g+=$incG;
$b+=$incB;
}
if (is_array($color[1])) {
$endR=$color[2][0];
$endG=$color[2][1];
$endB=$color[2][2];
} else {
$r=($color[1] >> 16) & 0xFF;
$g=($color[1] >> 8) & 0xFF;
$b=$color[1] & 0xFF;
}
if (is_array($color[2])) {
$r=$color[1][0];
$g=$color[1][1];
$b=$color[1][2];
} else {
$endR=($color[2] >> 16) & 0xFF;
$endG=($color[2] >> 8) & 0xFF;
$endB=$color[2] & 0xFF;
}
$incR=($endR-$r)/$scaleHeight;
$incG=($endG-$g)/$scaleHeight;
$incB=($endB-$b)/$scaleHeight;
$rightColors=array();
for ($y=0; $y<$height; $y++) {
$rightColors[$y]=array($r,$g,$b);
$r+=$incR;
$g+=$incG;
$b+=$incB;
}
$style=array();
for ($y=0; $y<$height; $y++) {
$r=$leftColors[$y][0];
$g=$leftColors[$y][1];
$b=$leftColors[$y][2];
$incR=($rightColors[$y][0]-$r)/$scaleWidth;
$incG=($rightColors[$y][1]-$g)/$scaleWidth;
$incB=($rightColors[$y][2]-$b)/$scaleWidth;
for ($x=0; $x<$width; $x++) {
$style[$x]=imagecolorallocate($image,$r,$g,$b);
$r+=$incR;
$g+=$incG;
$b+=$incB;
}
imagesetstyle($image,$style);
imageline($image,0,$y,$scaleWidth,$y,IMG_COLOR_STYLED);
}
return $image;
}
$width=is_numeric($_GET['w']) ? $_GET['w'] : 256;
$height=is_numeric($_GET['h']) ? $_GET['h'] : 256;
$color=array(
empty($_GET['c1']) ? 0x000000 : hexdec($_GET['c1']),
empty($_GET['c2']) ? 0xFF0000 : hexdec($_GET['c2']),
empty($_GET['c3']) ? 0x00FF00 : hexdec($_GET['c3']),
empty($_GET['c4']) ? 0x0000FF : hexdec($_GET['c4'])
);
header("Content-type: image/png");
$image=styleGradient($width,$height,$color);
imagepng($image);
imagedestroy($image);
?>
Which works pretty good at a different color at each corner gradient. First I build a buffer line that contains the colors down the left side, then I do it for the colors on the right side, and then I go between the two points...
Let's see, written up for free pascal (using SDL for expediency) that would be...
Code:
unit SDLGradient;
{$COPERATORS ON}
interface
uses
sdl;
type
t4Colors=array[0..3] of dword;
tColorQuad=packed record
b,g,r,a:byte;
end;
function SDL_Gradient(width,height:longint; color:t4Colors):pSDL_Surface;
implementation
function SDL_Gradient(width,height:longint; color:t4Colors):pSDL_Surface;
var
scaleWidth,scaleHeight:longint;
sideBuffersize:dword;
r,g,b,a,
endR,endG,endB,endA,
incR,incG,incB,incA:double;
tempSurface:pSDL_Surface;
leftColors,rightColors,
lPoint,rPoint,bPoint,
lPointEnd,rPointEnd,bPointEnd:^tColorQuad;
begin
tempSurface:=SDL_CreateRGBSurface(SDL_SWSURFACE,width,height,32,0,0,0,0);
scaleWidth:=width-1;
scaleHeight:=height-1;
sideBufferSize:=height*4;
r:=color[0] shr 24;
g:=(color[0] shr 16) and $FF;
b:=(color[0] shr 8) and $FF;
a:=color[0] and $FF;
endR:=color[3] shr 24;
endG:=(color[3] shr 16) and $FF;
endB:=(color[3] shr 8) and $FF;
endA:=color[3] and $FF;
incR:=(endR-r)/scaleHeight;
incG:=(endG-g)/scaleHeight;
incB:=(endB-b)/scaleHeight;
incA:=(endA-a)/scaleHeight;
getmem(leftColors,sideBufferSize);
getmem(rightColors,sideBufferSize);
lPoint:=leftColors;
lPointEnd:=lPoint+height;
while (lpoint<lPointEnd) do begin
lPoint^.r:=trunc(r);
lPoint^.g:=trunc(g);
lPoint^.b:=trunc(b);
lPoint^.a:=trunc(a);
inc(lPoint);
r+=incR;
g+=incG;
b+=incB;
a+=incA;
end;
r:=color[1] shr 24;
g:=(color[1] shr 16) and $FF;
b:=(color[1] shr 8) and $FF;
a:=color[1] and $FF;
endR:=color[2] shr 24;
endG:=(color[2] shr 16) and $FF;
endB:=(color[2] shr 8) and $FF;
endA:=color[2] and $FF;
incR:=(endR-r)/scaleHeight;
incG:=(endG-g)/scaleHeight;
incB:=(endB-b)/scaleHeight;
incA:=(endA-a)/scaleHeight;
rPoint:=rightColors;
rPointEnd:=rPoint+height;
while rPoint<rPointEnd do begin
rPoint^.r:=trunc(r);
rPoint^.g:=trunc(g);
rPoint^.b:=trunc(b);
rPoint^.a:=trunc(a);
inc(rPoint);
r+=incR;
g+=incG;
b+=incB;
a+=incA;
end;
lPoint:=leftColors;
rPoint:=rightColors;
bPoint:=pointer(tempSurface^.pixels);
while (lPoint<lPointEnd) do begin
r:=lPoint^.r;
g:=lPoint^.g;
b:=lPoint^.b;
a:=lPoint^.a;
inc(lPoint);
incR:=(rPoint^.r-r)/scaleWidth;
incG:=(rPoint^.g-g)/scaleWidth;
incB:=(rPoint^.b-b)/scaleWidth;
incA:=(rPoint^.a-a)/scaleWidth;
inc(rPoint);
bPointEnd:=bPoint+width;
while (bPoint<bPointend) do begin
bPoint^.r:=trunc(r);
bPoint^.g:=trunc(g);
bPoint^.b:=trunc(b);
bPoint^.a:=trunc(a);
inc(bPoint);
r+=incR;
g+=incG;
b+=incB;
a+=incA;
end;
end;
freemem(rightColors);
freemem(leftColors);
SDL_Gradient:=tempSurface;
end;
end.
Which renders to a nice little SDL surface so you can save it, show it, whatever. Right now it uses floats, which is kind-of messy/slow. Might be better to use fixed integer based on the dimensions being created, or possibly Bresenham algorithm - I'd probably go with the former since the notion of having four separate line-drawing algos running simultaneously just sounds like a really bad idea.
One cool modification I made while in there was to make it also do alpha per corner... just another parameter really.
Can demo that real quick thus:
Code:
program demoGradient;
{$COPERATORS ON}
{$APPTYPE GUI}
uses
SDL,
SDLGradient;
const
demoGradientColor:t4Colors=(
$FF0000FF,
$00FF00FF,
$0000FFFF,
$FFFFFFFF
);
var
screen:pSDL_SURFACE;
SDLTestEvent:pSDL_EVENT;
procedure showGradient;
var
gradient:pSDL_SURFACE;
gradientRect,screenRect:pSDL_RECT;
begin
gradient:=SDL_Gradient(512,512,demoGradientColor);
new(gradientRect);
with gradientRect^ do begin
x:=0;
y:=0;
w:=512;
h:=512;
end;
new(screenRect);
with screenRect^ do begin
x:=0;
y:=0;
w:=512;
h:=512;
end;
SDL_BLITSURFACE(gradient,gradientRect,screen,screenRect);
SDL_FLIP(screen);
dispose(screenRect);
dispose(gradientRect);
SDL_FREESURFACE(gradient);
end;
function SDL_poll:boolean;
var
windowEnd:boolean;
begin
windowEnd:=false;
if SDL_pollEvent(SDLTestEvent)=1 then begin
case SDLTestEvent^.type_ of
SDL_KEYDOWN:begin
if (SDLTestEvent^.key.keysym.sym=SDLK_ESCAPE) then begin
windowEnd:=true;
end;
end;
SDL_QUITEV:begin
windowEnd:=true;
end;
end;
end;
SDL_poll:=windowEnd;
end;
begin
SDL_INIT(SDL_INIT_VIDEO);
screen:=SDL_SETVIDEOMODE(512,512,32,SDL_SWSURFACE);
if screen=nil then begin
writeln('Failed to initialize SDL video');
end else begin
showGradient;
new(SDLTestEvent);
repeat until SDL_poll;
SDL_FREESURFACE(screen);
SDL_QUIT;
end;
end.
Let's see... bah, you don't have attachments enabled. That's ok, I'll just upload the source and a win32 compiled .exe in a rar file to my server.
http://www.cutcodedown.com/fpcDemoCode/gradients.rar
Enjoy - if anyone actually finds a USE for that, well, more power to you.
A better version would probably use integer math, and have detection for if you are doing horizontal or vertical gradients instead of unique corners, since those could be generated a heck of a lot faster than how I did this here.
-- edit -- oops, already had someone ask about SDL.DLL on that, so I added it to the .rar
Bookmarks