Results 1 to 9 of 9

Thread: how to generate gradients and textures

  1. #1

    how to generate gradients and textures

    Hi all, I found this link today on how to generate gradients and textures (png files) using python and python standard libs only!

    http://eldarion.com/blog/2009/08/18/...-and-textures/

    I wonder if this is possible to do using Delphi and a zlib unit?

    Here is some example Python code that creates png files with gradients:

    import zlib
    import struct
    import array
    import random
    import colorsys

    def output_chunk(out, chunk_type, data):
    out.write(struct.pack("!I", len(data)))
    out.write(chunk_type)
    out.write(data)
    checksum = zlib.crc32(data, zlib.crc32(chunk_type))
    out.write(struct.pack("!I", checksum))

    def get_data(width, height, rgb_func):
    fw = float(width)
    fh = float(height)
    compressor = zlib.compressobj()
    data = array.array("B")
    for y in range(height):
    data.append(0)
    fy = float(y)
    for x in range(width):
    fx = float(x)
    data.extend([min(255, max(0, int(v * 255))) for v in rgb_func(fx / fw, fy / fh)])
    compressed = compressor.compress(data.tostring())
    flushed = compressor.flush()
    return compressed + flushed

    def write_png(filename, width, height, rgb_func):
    out = open(filename, "w")
    out.write(struct.pack("8B", 137, 80, 78, 71, 13, 10, 26, 10))
    output_chunk(out, "IHDR", struct.pack("!2I5B", width, height, 8, 2, 0, 0, 0))
    output_chunk(out, "IDAT", get_data(width, height, rgb_func))
    output_chunk(out, "IEND", "")
    out.close()


    def linear_gradient(start_value, stop_value, start_offset=0.0, stop_offset=1.0):
    return lambda offset: (start_value + ((offset - start_offset) / (stop_offset - start_offset) * (stop_value - start_value))) / 255.0

    def LINEAR_Y(x, y):
    return y

    def LINEAR_X(x, y):
    return x

    def RADIAL(center_x, center_y):
    return lambda x, y: (x - center_x) ** 2 + (y - center_y) ** 2

    def NO_NOISE(r, g, b):
    return r, g, b

    def GAUSSIAN(sigma):
    def add_noise(r, g, b):
    d = random.gauss(0, sigma)
    return r + d, g + d, b + d
    return add_noise

    def HSV(h, s, v):
    r, g, b = colorsys.hsv_to_rgb(h, s, v)
    return 255 * r, 255 * g, 255 * b

    def gradient(value_func, noise_func, DATA):
    def gradient_function(x, y):
    initial_offset = 0.0
    v = value_func(x, y)
    for offset, start, end in DATA:
    if v < offset:
    r = linear_gradient(start[0], end[0], initial_offset, offset)(v)
    g = linear_gradient(start[1], end[1], initial_offset, offset)(v)
    b = linear_gradient(start[2], end[2], initial_offset, offset)(v)
    return noise_func(r, g, b)
    initial_offset = offset
    return noise_func(end[0] / 255.0, end[1] / 255.0, end[2] / 255.0)
    return gradient_function


    cheers,
    Paul

  2. #2
    PGD Staff code_glitch's Avatar
    Join Date
    Oct 2009
    Location
    UK (England, the bigger bit)
    Posts
    933
    Blog Entries
    45

    Re: how to generate gradients and textures

    I'm no expert but i'm not seeing many things on there that seems to rely on the python libs you mentioned. Isn't there an SDL/OpenGL equivalent for textures from png and bmp files. I think I may have remebered reading about it in some tutorial but can't remember which unfortunately, becuase it was some time ago, but I do think the page background was black if it's of any help.
    I once tried to change the world. But they wouldn't give me the source code. Damned evil cunning.

  3. #3

    Re: how to generate gradients and textures

    I'm only going on that the website mentioned it generated png files using standard python libs, and no other dependencies...

    I just thought it was really neat that a simple png file could be created that way using the zlib library

    cheers,
    Paul

  4. #4
    PGD Staff code_glitch's Avatar
    Join Date
    Oct 2009
    Location
    UK (England, the bigger bit)
    Posts
    933
    Blog Entries
    45

    Re: how to generate gradients and textures

    I must say that it is useful... My main concern if you were going to use it would be efficiency. I know how surfaces dont like hi res but you are going to have some very HD content for good textures so how is it going to perform? On top of that python is kinda high evel so not too eficient (although better than java..)

    just wondering.
    I once tried to change the world. But they wouldn't give me the source code. Damned evil cunning.

  5. #5

    Re: how to generate gradients and textures

    Quote Originally Posted by code_glitch
    I must say that it is useful... My main concern if you were going to use it would be efficiency. I know how surfaces dont like hi res but you are going to have some very HD content for good textures so how is it going to perform? On top of that python is kinda high evel so not too eficient (although better than java..)

    just wondering.


    Obviously, I would prefer to do this in Delphi and not Python
    If I was going to do gradients, I also wouldn't do huge textures either...

    cheers,
    Paul

  6. #6

    Re: how to generate gradients and textures

    Have a look on how i do this in glvg.
    http://3das.noeska.com - create adventure games without programming

  7. #7

    Re: how to generate gradients and textures

    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
    The accessibility of a website from time to time must be refreshed with the blood of designers and owners. It is its natural manure

  8. #8

    Re: how to generate gradients and textures

    Nice, thanks

    cheers,
    Paul

  9. #9

    Re: how to generate gradients and textures

    Oh, and in porting that code to pascal/SDL, after almost eight years of not really doing ANY real programming in pascal, and working almost entirely in PHP/mySQL/HTML/CSC/JS

    I just need to say...

    GOD BLESS POINTERS, RECORDS and an OBJECT MODEL THAT MAKES SENSE!!!

    It's like coming home from a deployment.
    The accessibility of a website from time to time must be refreshed with the blood of designers and owners. It is its natural manure

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •