1 #Copyright 2006 DR0ID <dr0id@bluewin.ch> http://mypage.bluewin.ch/DR0ID
6 Allow to draw some gradients relatively easy.
9 __author__
= "$Author: DR0ID $"
10 __version__
= "$Revision: 109 $"
11 __date__
= "$Date: 2007-08-09 20:33:32 +0200 (Do, 09 Aug 2007) $"
16 BLEND_MODES_AVAILABLE
= False
17 vernum
= pygame
.vernum
18 if vernum
[0]>=1 and vernum
[1]>=8:
19 BLEND_MODES_AVAILABLE
= True
22 class ColorInterpolator(object):
24 ColorInterpolator(distance, color1, color2, rfunc, gfunc, bfunc, afunc)
26 interpolates a color over the distance using different functions for r,g,b,a
27 separately (a= alpha).
29 def __init__(self
, distance
, color1
, color2
, rfunc
, gfunc
, bfunc
, afunc
):
32 self
.rInterpolator
= FunctionInterpolator(color1
[0], color2
[0], distance
, rfunc
)
33 self
.gInterpolator
= FunctionInterpolator(color1
[1], color2
[1], distance
, gfunc
)
34 self
.bInterpolator
= FunctionInterpolator(color1
[2], color2
[2], distance
, bfunc
)
35 if len(color1
)==4 and len(color2
)==4:
36 self
.aInterpolator
= FunctionInterpolator(color1
[3], color2
[3], distance
, afunc
)
38 self
.aInterpolator
= FunctionInterpolator(255, 255, distance
, afunc
)
44 returns the color at the position 0<=x<=d (actually not bound to this interval).
46 ## print "colorInterp x", x, self.rInterpolator.eval(x), self.gInterpolator.eval(x), self.bInterpolator.eval(x)
47 return [self
.rInterpolator
.eval(x
),
48 self
.gInterpolator
.eval(x
),
49 self
.bInterpolator
.eval(x
),
50 self
.aInterpolator
.eval(x
)]
54 class FunctionInterpolator(object):
56 FunctionINterpolator(startvalue, endvalue, trange, func)
58 interpolates a function y=f(x) in the range trange with
61 using the function func
63 def __init__(self
, startvalue
, endvalue
, trange
, func
):
68 self
.a
= endvalue
-startvalue
73 self
.b
= 1./abs(trange
)
79 self
.d
= min(max(startvalue
,0),255)
85 return value at position x
87 # make sure that the returned value is in [0,255]
88 ## return int(round(min(max(self.a*self.func(self.b*(x+self.c))+self.d, 0), 255)))
89 return int(min(max(self
.a
*self
.func(self
.b
*(x
+self
.c
))+self
.d
, 0), 255))
93 ##def gradient(surface,
98 ## Rfunc = (lambda x:x),
99 ## Gfunc = (lambda x:x),
100 ## Bfunc = (lambda x:x),
101 ## Afunc = (lambda x:1),
105 ## surface : surface to draw on
106 ## startpoint: (x,y) point on surface
107 ## endpoint : (x,y) point on surface
108 ## startcolor: (r,g,b,a) color at startpoint
109 ## endcolor : (r,g,b,a) color at endpoint
110 ## Rfunc : function y = f(x) with startcolor =f(0) and endcolor = f(1) where 0 is at startpoint and 1 at endpoint
114 ## these functions are evaluated in the range 0 <= x <= 1 and 0<= y=f(x) <= 1
115 ## type : "line", "circle" or "rect"
116 ## mode : "+", "-", "*", None (how the pixels are drawen)
118 ## returns : surface with the color characteristics w,h = (d, 256) and d = length of endpoint-startpoint
121 ## dx = endpoint[0]-startpoint[0]
122 ## dy = endpoint[1]-startpoint[1]
123 ## d = int(round(math.hypot(dx, dy)))
124 ## angle = math.degrees( math.atan2(dy, dx) )
126 ## color = ColorInterpolator(d, startcolor, endcolor, Rfunc, Gfunc, Bfunc, Afunc)
129 ## h = int(2.*math.hypot(*surface.get_size()))
130 ### bigSurf = pygame.Surface((d, h)).convert_alpha()
131 ## bigSurf = pygame.Surface((d, h), pygame.SRCALPHA)#.convert_alpha()
132 ### bigSurf = pygame.Surface((d, 1), pygame.SRCALPHA)#.convert_alpha()
134 ## bigSurf.fill((0,0,0,0))
135 ## bigSurf.set_colorkey((0,0,0,0))
136 ## for x in range(d):
137 ## pygame.draw.line(bigSurf, color.eval(x), (x,0), (x,h), 1)
138 ### for x in range(d):
139 ### bigSurf.set_at((x, 0), color.eval(x))
140 ### bigSurf = pygame.transform.scale(bigSurf, (d, h))
142 ## bigSurf = pygame.transform.rotate(bigSurf, -angle) #rotozoom(bigSurf, -angle, 1)
143 ## bigSurf.set_colorkey((0,0,0, 0))
144 ## rect = bigSurf.get_rect()
145 ## srect = pygame.Rect(rect)
146 ## dx = d/2. * math.cos(math.radians(angle))
147 ## dy = d/2. * math.sin(math.radians(angle))
148 ## rect.center = startpoint
149 ## rect.move_ip(dx, dy)
152 ## elif type=="circle":
153 ## bigSurf = pygame.Surface((2*d, 2*d)).convert_alpha()
154 ## bigSurf.fill((0,0,0,0))
156 ## for x in range(d, 0, -1):
157 ## pygame.draw.circle(bigSurf, color.eval(x), (d,d), x)
159 ## rect = bigSurf.get_rect()
160 ## srect = pygame.Rect(rect)
161 ## rect.center = (startpoint[0], startpoint[1])
163 ## elif type=="rect":
164 ## bigSurf = pygame.Surface((2*d, 2*d)).convert_alpha()
165 ## bigSurf.fill((0,0,0,0))
166 ## c = bigSurf.get_rect().center
168 ## for x in range(d,-1,-1):
169 ## r = pygame.Rect(0,0,2*x,2*x)
171 ## pygame.draw.rect(bigSurf, color.eval(x), r)
173 ## bigSurf = pygame.transform.rotozoom(bigSurf, -angle, 1)
174 ## bigSurf.set_colorkey((0,0,0, 0))
176 ## rect = bigSurf.get_rect()
177 ## srect = pygame.Rect(rect)
178 ## rect.center = startpoint
180 ## raise NameError("type must be one of \"line\",\"circle\" or \"rect\"")
183 ## surface.blit(bigSurf, rect, srect)
186 ## cf = pygame.color.add
188 ## cf = pygame.color.multiply
190 ## cf = pygame.color.subtract
192 ## raise NameError("type must be one of \"+\", \"*\", \"-\" or None")
193 ## irect = surface.get_clip().clip(rect)
195 ## for x in range(irect.left, irect.left+irect.width):
196 ## for y in range(irect.top, irect.top+irect.height):
197 ## surface.set_at((x,y), cf(surface.get_at((x,y)), bigSurf.get_at((x-rect.left, y-rect.top)) ) )
201 ## char = pygame.Surface((d+1, 257))
202 ### char.fill((0,0,0))
204 ### oldcol = color.eval(0)
205 ### for x in range(d):
206 ### col = color.eval(x)
207 ### pygame.draw.line(char, (255,0,0), (x, 256-col[0]), (ox, 256-oldcol[0]))
208 ### pygame.draw.line(char, (0,255,0), (x, 256-col[1]), (ox, 256-oldcol[1]))
209 ### pygame.draw.line(char, (0,0,255), (x, 256-col[2]), (ox, 256-oldcol[2]))
210 ### pygame.draw.line(char, (255,255,255), (x, 256-col[3]), (ox, 256-oldcol[3]))
219 def vertical(size
, startcolor
, endcolor
):
221 Draws a vertical linear gradient filling the entire surface. Returns a
222 surface filled with the gradient (numeric is only 2-3 times faster).
225 bigSurf
= pygame
.Surface((1,height
)).convert_alpha()
227 sr
, sg
, sb
, sa
= startcolor
228 er
, eg
, eb
, ea
= endcolor
233 for y
in range(height
):
234 bigSurf
.set_at((0,y
),
240 return pygame
.transform
.scale(bigSurf
, size
)
243 def horizontal(size
, startcolor
, endcolor
):
245 Draws a horizontal linear gradient filling the entire surface. Returns a
246 surface filled with the gradient (numeric is only 2-3 times faster).
249 bigSurf
= pygame
.Surface((width
, 1)).convert_alpha()
251 sr
, sg
, sb
, sa
= startcolor
252 er
, eg
, eb
, ea
= endcolor
257 for y
in range(width
):
258 bigSurf
.set_at((y
,0),
264 return pygame
.transform
.scale(bigSurf
, size
)
267 def radial(radius
, startcolor
, endcolor
):
269 Draws a linear raidal gradient on a square sized surface and returns
272 bigSurf
= pygame
.Surface((2*radius
, 2*radius
)).convert_alpha()
273 bigSurf
.fill((0,0,0,0))
275 sr
, sg
, sb
, sa
= endcolor
276 er
, eg
, eb
, ea
= startcolor
282 draw_circle
= pygame
.draw
.circle
283 for rad
in range(radius
, 0, -1):
284 draw_circle(bigSurf
, (er
+ int(rm
*rad
),
287 ea
+ int(am
*rad
)), (radius
, radius
), rad
)
290 def squared(width
, startcolor
, endcolor
):
292 Draws a linear sqared gradient on a square sized surface and returns
295 bigSurf
= pygame
.Surface((width
, width
)).convert_alpha()
296 bigSurf
.fill((0,0,0,0))
298 sr
, sg
, sb
, sa
= endcolor
299 er
, eg
, eb
, ea
= startcolor
305 draw_rect
= pygame
.draw
.rect
306 for currentw
in range((width
/2), 0, -1):
307 pos
= (width
/2)-currentw
308 draw_rect(bigSurf
, (er
+ int(rm
*currentw
),
309 eg
+ int(gm
*currentw
),
310 eb
+ int(bm
*currentw
),
311 ea
+ int(am
*currentw
)), pygame
.Rect(pos
, pos
, 2*currentw
, 2*currentw
))
315 def vertical_func(size
, startcolor
, endcolor
, Rfunc
= (lambda x
:x
), Gfunc
= (lambda x
:x
), Bfunc
= (lambda x
:x
), Afunc
= (lambda x
:1)):
317 Draws a vertical linear gradient filling the entire surface. Returns a
318 surface filled with the gradient (numeric is only 2x faster).
319 Rfunc, Gfunc, Bfunc and Afunc are function like y = f(x). They define
320 how the color changes.
323 bigSurf
= pygame
.Surface((1,height
)).convert_alpha()
324 color
= ColorInterpolator(height
, startcolor
, endcolor
, Rfunc
, Gfunc
, Bfunc
, Afunc
)
325 for y
in range(0, height
):
326 bigSurf
.set_at((0,y
), color
.eval(y
+0.1))
327 return pygame
.transform
.scale(bigSurf
, size
)
330 def horizontal_func(size
, startcolor
, endcolor
, Rfunc
= (lambda x
:x
), Gfunc
= (lambda x
:x
), Bfunc
= (lambda x
:x
), Afunc
= (lambda x
:1)):
332 Draws a horizontal linear gradient filling the entire surface. Returns a
333 surface filled with the gradient (numeric is only 2x faster).
334 Rfunc, Gfunc, Bfunc and Afunc are function like y = f(x). They define
335 how the color changes.
338 bigSurf
= pygame
.Surface((width
, 1)).convert_alpha()
339 color
= ColorInterpolator(width
, startcolor
, endcolor
, Rfunc
, Gfunc
, Bfunc
, Afunc
)
340 for y
in range(0, width
):
341 bigSurf
.set_at((y
, 0), color
.eval(y
+0.1))
342 return pygame
.transform
.scale(bigSurf
, size
)
344 def radial_func(radius
, startcolor
, endcolor
, Rfunc
= (lambda x
:x
), Gfunc
= (lambda x
:x
), Bfunc
= (lambda x
:x
), Afunc
= (lambda x
:1), colorkey
=(0,0,0,0)):
346 Draws a linear raidal gradient on a square sized surface and returns
349 bigSurf
= pygame
.Surface((2*radius
, 2*radius
)).convert_alpha()
352 bigSurf
.fill(colorkey
)
353 color
= ColorInterpolator(radius
, startcolor
, endcolor
, Rfunc
, Gfunc
, Bfunc
, Afunc
)
354 draw_circle
= pygame
.draw
.circle
355 for rad
in range(radius
, 0, -1):
356 draw_circle(bigSurf
, color
.eval(rad
), (radius
, radius
), rad
)
359 def radial_func_offset(radius
, startcolor
, endcolor
, Rfunc
= (lambda x
:x
), Gfunc
= (lambda x
:x
), Bfunc
= (lambda x
:x
), Afunc
= (lambda x
:1), colorkey
=(0,0,0,0), offset
=(0,0)):
361 Draws a linear raidal gradient on a square sized surface and returns
363 offset is the amount the center of the gradient is displaced of the center of the image.
364 Unfotunately this function ignores alpha.
366 bigSurf
= pygame
.Surface((2*radius
, 2*radius
))#.convert_alpha()
368 mask
= pygame
.Surface((2*radius
, 2*radius
), pygame
.SRCALPHA
)#.convert_alpha()
370 mask
.set_colorkey((255,0,255))
371 pygame
.draw
.circle(mask
, (255,0,255), (radius
, radius
), radius
)
375 bigSurf
.fill(colorkey
)
377 color
= ColorInterpolator(radius
, startcolor
, endcolor
, Rfunc
, Gfunc
, Bfunc
, Afunc
)
378 draw_circle
= pygame
.draw
.circle
379 radi
= radius
+ int(math
.hypot(offset
[0], offset
[1])+1)
380 for rad
in range(radi
, 0, -1):
381 draw_circle(bigSurf
, color
.eval(rad
), (radius
+offset
[0], radius
+offset
[1]), rad
)
383 bigSurf
.blit(mask
, (0,0))
384 bigSurf
.set_colorkey(colorkey
)
388 def squared_func(width
, startcolor
, endcolor
, Rfunc
= (lambda x
:x
), Gfunc
= (lambda x
:x
), Bfunc
= (lambda x
:x
), Afunc
= (lambda x
:1), offset
=(0,0)):
390 Draws a linear sqared gradient on a square sized surface and returns
393 bigSurf
= pygame
.Surface((width
, width
)).convert_alpha()
394 bigSurf
.fill((0,0,0,0))
395 color
= ColorInterpolator(width
/2, startcolor
, endcolor
, Rfunc
, Gfunc
, Bfunc
, Afunc
)
396 draw_rect
= pygame
.draw
.rect
397 widthh
= width
+2*int(max(abs(offset
[0]),abs(offset
[1])))
398 for currentw
in range((widthh
/2), 0, -1):
399 ## pos = (width/2)-currentw
400 rect
= pygame
.Rect(0, 0, 2*currentw
, 2*currentw
)
401 rect
.center
= (width
/2+offset
[0], width
/2+offset
[1])
402 draw_rect(bigSurf
, color
.eval(currentw
), rect
)
405 def draw_gradient(surface
, startpoint
, endpoint
, startcolor
, endcolor
, Rfunc
= (lambda x
:x
), Gfunc
= (lambda x
:x
), Bfunc
= (lambda x
:x
), Afunc
= (lambda x
:1), mode
=0):
407 Instead of returning an Surface, this function draw it directy onto the
408 given Surface and returns the rect.
410 dx
= endpoint
[0]-startpoint
[0]
411 dy
= endpoint
[1]-startpoint
[1]
412 d
= int(round(math
.hypot(dx
, dy
)))
413 angle
= math
.degrees( math
.atan2(dy
, dx
) )
415 h
= int(2.*math
.hypot(*surface
.get_size()))
417 bigSurf
= horizontal_func((d
,h
), startcolor
, endcolor
, Rfunc
, Gfunc
, Bfunc
, Afunc
)
419 ## bigSurf = pygame.transform.rotate(bigSurf, -angle) #rotozoom(bigSurf, -angle, 1)
420 bigSurf
= pygame
.transform
.rotozoom(bigSurf
, -angle
, 1)
421 ## bigSurf.set_colorkey((0,0,0, 0))
422 rect
= bigSurf
.get_rect()
423 srect
= pygame
.Rect(rect
)
424 dx
= d
/2. * math
.cos(math
.radians(angle
))
425 dy
= d
/2. * math
.sin(math
.radians(angle
))
426 rect
.center
= startpoint
428 if BLEND_MODES_AVAILABLE
:
429 return surface
.blit(bigSurf
, rect
, None, mode
)
431 return surface
.blit(bigSurf
, rect
)
434 def draw_circle(surface
, startpoint
, endpoint
, startcolor
, endcolor
, Rfunc
= (lambda x
:x
), Gfunc
= (lambda x
:x
), Bfunc
= (lambda x
:x
), Afunc
= (lambda x
:1), mode
=0):
436 Instead of returning an Surface, this function draw it directy onto the
437 given Surface and returns the rect.
439 dx
= endpoint
[0]-startpoint
[0]
440 dy
= endpoint
[1]-startpoint
[1]
441 radius
= int(round(math
.hypot(dx
, dy
)))
442 pos
= (startpoint
[0]-radius
, startpoint
[1]-radius
)
443 if BLEND_MODES_AVAILABLE
:
444 return surface
.blit(radial_func(radius
, startcolor
, endcolor
, Rfunc
, Gfunc
, Bfunc
, Afunc
), pos
, None, mode
)
446 return surface
.blit(radial_func(radius
, startcolor
, endcolor
, Rfunc
, Gfunc
, Bfunc
, Afunc
), pos
)
448 def draw_squared(surface
, startpoint
, endpoint
, startcolor
, endcolor
, Rfunc
= (lambda x
:x
), Gfunc
= (lambda x
:x
), Bfunc
= (lambda x
:x
), Afunc
= (lambda x
:1), mode
=0):
450 Instead of returning an Surface, this function draw it directy onto the
451 given Surface and returns the rect.
453 dx
= endpoint
[0]-startpoint
[0]
454 dy
= endpoint
[1]-startpoint
[1]
455 angle
= math
.degrees( math
.atan2(dy
, dx
) )
456 width
= 2*int(round(math
.hypot(dx
, dy
)))
458 bigSurf
= squared_func(width
, startcolor
, endcolor
, Rfunc
, Gfunc
, Bfunc
, Afunc
)
460 bigSurf
= pygame
.transform
.rotozoom(bigSurf
, -angle
, 1)
461 ## bigSurf.set_colorkey((0,0,0, 0))
462 rect
= bigSurf
.get_rect()
463 rect
.center
= startpoint
464 if BLEND_MODES_AVAILABLE
:
465 return surface
.blit(bigSurf
, rect
, None, mode
)
467 return surface
.blit(bigSurf
, rect
)
470 def chart(startpoint
, endpoint
, startcolor
, endcolor
, Rfunc
= (lambda x
:x
), Gfunc
= (lambda x
:x
), Bfunc
= (lambda x
:x
), Afunc
= (lambda x
:1), scale
=None):
472 This returns a Surface where the change of the colors over the distance
473 (the width of the image) is showen as a line.
474 scale: a float, 1 is not scaling
476 dx
= endpoint
[0]-startpoint
[0]
477 dy
= endpoint
[1]-startpoint
[1]
478 distance
= int(round(math
.hypot(dx
, dy
)))
479 color
= ColorInterpolator(distance
, startcolor
, endcolor
, Rfunc
, Gfunc
, Bfunc
, Afunc
)
480 bigSurf
= pygame
.Surface((distance
, 256))
482 oldcol
= color
.eval(0)
483 for x
in range(distance
):
484 r
, g
, b
, a
= color
.eval(x
)
485 pygame
.draw
.line(bigSurf
, (255, 0, 0, 128), (x
-1, oldcol
[0]), (x
, r
))
486 pygame
.draw
.line(bigSurf
, (0, 255, 0, 128), (x
-1, oldcol
[1]), (x
, g
))
487 pygame
.draw
.line(bigSurf
, (0, 0, 255, 128), (x
-1, oldcol
[2]), (x
, b
))
488 pygame
.draw
.line(bigSurf
, (255, 255, 255, 128), (x
-1, oldcol
[3]), (x
, a
))
491 ## return pygame.transform.scale(bigSurf, size)
492 return pygame
.transform
.rotozoom(bigSurf
, 0, scale
)
493 return pygame
.transform
.flip(bigSurf
, 0, 1)
494 #------------------------------------------------------------------------------
499 def genericFxyGradient(surf
, clip
, color1
, color2
, func
, intx
, yint
, zint
=None):
501 genericFxyGradient(size, color1, color2,func, intx, yint, zint=None)
503 some sort of highfield drawer :-)
505 surf : surface to draw
506 clip : rect on surf to draw in
509 func : function z = func(x,y)
510 xint : interval in x direction where the function is evaluated
511 yint : interval in y direction where the function is evaluated
512 zint : if not none same as yint or xint, if None then the max and min value
513 of func is taken as z-interval
515 color = a*func(b*(x+c), d*(y+e))+f
517 # make shure that x1<x2 and y1<y2 and z1<z2
523 if zint
: # if user give us z intervall, then use it
526 else: # look for extrema of function (not best algorithme)
542 color1
= list(color1
)
545 color2
= list(color2
)
548 # calculate streching and displacement variables
549 a
= ((color2
[0]-color1
[0])/(z2
-z1
), \
550 (color2
[1]-color1
[1])/(z2
-z1
), \
551 (color2
[2]-color1
[2])/(z2
-z1
), \
552 (color2
[3]-color1
[3])/(z2
-z1
) ) # streching in z direction
553 b
= (x2
-x1
)/float(w
) # streching in x direction
554 d
= (y2
-y1
)/float(h
) # streching in y direction
555 f
= ( color1
[0]-a
[0]*z1
, \
558 color1
[3]-a
[3]*z1
)# z displacement
562 surff
= pygame
.surface
.Surface((w
,h
)).convert_alpha()
566 val
= func(b
*(j
+c
), d
*(i
+e
))
568 color
= ( max(min(a
[0]*val
+f
[0],255),0), \
569 max(min(a
[1]*val
+f
[1],255),0), \
570 max(min(a
[2]*val
+f
[2],255),0), \
571 max(min(a
[3]*val
+f
[3],255),0) )
572 surff
.set_at( (j
,i
), color
)
573 surf
.blit(surff
, clip
)