In Android, I used the following code to generate a gradient background that I need:
<gradient
android:angle="90"
android:startColor="#40000000"
android:endColor="#00000000"
android:type="linear" />
The background goes from light to relatively dark from top to bottom. I wonder how to do the same in Python with PIL, since I need the same effect on another program written in Python.
Here's something that shows ways to draw multicolor rectangular horizontal and vertical gradients.
rom PIL import Image, ImageDraw
BLACK, DARKGRAY, GRAY = ((0,0,0), (63,63,63), (127,127,127))
LIGHTGRAY, WHITE = ((191,191,191), (255,255,255))
BLUE, GREEN, RED = ((0, 0, 255), (0, 255, 0), (255, 0, 0))
class Point(object):
def __init__(self, x, y):
self.x, self.y = x, y
class Rect(object):
def __init__(self, x1, y1, x2, y2):
minx, maxx = (x1,x2) if x1 < x2 else (x2,x1)
miny, maxy = (y1,y2) if y1 < y2 else (y2,y1)
self.min = Point(minx, miny)
self.max = Point(maxx, maxy)
width = property(lambda self: self.max.x - self.min.x)
height = property(lambda self: self.max.y - self.min.y)
def gradient_color(minval, maxval, val, color_palette):
""" Computes intermediate RGB color of a value in the range of minval
to maxval (inclusive) based on a color_palette representing the range.
"""
max_index = len(color_palette)-1
delta = maxval - minval
if delta == 0:
delta = 1
v = float(val-minval) / delta * max_index
i1, i2 = int(v), min(int(v)+1, max_index)
(r1, g1, b1), (r2, g2, b2) = color_palette[i1], color_palette[i2]
f = v - i1
return int(r1 + f*(r2-r1)), int(g1 + f*(g2-g1)), int(b1 + f*(b2-b1))
def horz_gradient(draw, rect, color_func, color_palette):
minval, maxval = 1, len(color_palette)
delta = maxval - minval
width = float(rect.width) # Cache.
for x in range(rect.min.x, rect.max.x+1):
f = (x - rect.min.x) / width
val = minval + f * delta
color = color_func(minval, maxval, val, color_palette)
draw.line([(x, rect.min.y), (x, rect.max.y)], fill=color)
def vert_gradient(draw, rect, color_func, color_palette):
minval, maxval = 1, len(color_palette)
delta = maxval - minval
height = float(rect.height) # Cache.
for y in range(rect.min.y, rect.max.y+1):
f = (y - rect.min.y) / height
val = minval + f * delta
color = color_func(minval, maxval, val, color_palette)
draw.line([(rect.min.x, y), (rect.max.x, y)], fill=color)
if __name__ == '__main__':
# Draw a three color vertical gradient.
color_palette = [BLUE, GREEN, RED]
region = Rect(0, 0, 730, 350)
width, height = region.max.x+1, region.max.y+1
image = Image.new("RGB", (width, height), WHITE)
draw = ImageDraw.Draw(image)
vert_gradient(draw, region, gradient_color, color_palette)
image.show()
#image.save("vert_gradient.png", "PNG")
#print('image saved')
And here's the image it generates and displays:
This calculates the intermediate colors in the RGB color space, but other colorspaces could be used — for examples compare results of my answers to the question Range values to pseudocolor.
This could easily be extended to generate RGBA (RGB+Alpha) mode images.
If you only need two colours, this can be done very simply:
def generate_gradient(
colour1: str, colour2: str, width: int, height: int) -> Image:
"""Generate a vertical gradient."""
base = Image.new('RGB', (width, height), colour1)
top = Image.new('RGB', (width, height), colour2)
mask = Image.new('L', (width, height))
mask_data = []
for y in range(height):
mask_data.extend([int(255 * (y / height))] * width)
mask.putdata(mask_data)
base.paste(top, (0, 0), mask)
return base
This creates a layer in each colour, then creates a mask with transparency varying according to the y
position. You can replace y / height
in line 10 with x / width
for a horizontal gradient, or any function of x
and y
for another gradient.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With