#!/usr/bin/env python # # cback.py - v0.2 # # Copyright (c) 2009 - Rich Burridge - Sun Microsystems Inc. # All Rights Reserved. # # A color random image generator. # # Color squares inspired by James Goslings' Mondrian picture generator. # # Color circles based on a program by Nigel Bond on a Perq in 1983. # # Wallpaper patterns based on Computer Recreations article by # A. K. Dewdney in Scientific American. # # Textured patterns based on an article from Computer Graphics Vol 22, # Number 1, February 1988. # # Uses Python Image Library (PIL) free from: # http://www.pythonware.com/products/pil/index.htm # # Based on my old cback.c color random background generator for X11. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published # by the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. import Image import ImageDraw import getopt import os import random import sys class CBack: def __init__(self): random.seed() self.pname = os.path.basename(sys.argv[0]) self.INT_MAX = 2147483647 self.color = 0 # Current color of square or circle. self.cpos = 0 # Current column position for usage output. self.dir = 0 # Current direction of square chain. self.len = 0 # Current length of square chain. self.maxno = 0 # Maximum number of circles or squares. self.norows = 0 # Number of rows of squares. self.nocols = 0 # Number of columns of squares. self.pval = 0 # Current random start position of square chain. self.ssize = 0 # Square size. self.total = 0 # Current number of displayed squares. self.colors = None self.csize = 32 # Default colormap size. self.WHITE = 0 self.BLACK = self.csize-1 self.win_height = 1500 self.win_width = 2000 self.image = Image.new("RGB", (self.win_width, self.win_height)) self.bmp = ImageDraw.Draw(self.image) self.sqs = None # No square array initially. self.option = 's' self.output_file = "cback_image.jpg" self.version = "0.1" def color_area(self, x, y, width, height, color): """Draw a colored rectangle in the drawing area.""" self.bmp.rectangle([(x, y), (x+width, y+height)], fill=self.colors[color]) def draw_circle(self, cx, cy, r, color): """Draw a filled circle in a given color.""" if r <= 0: return self.bmp.pieslice((cx -r, cy - r, cx + r, cy + r), 0, 360, fill=self.colors[color]) def draw_square(self, val, color): """Draw colored square at position pos.""" # White background first. self.color_area((val % self.nocols) * self.ssize, (int) ((val / self.nocols) * self.ssize), self.ssize, self.ssize, self.WHITE) # Colored square inside. self.color_area((val % self.nocols) * self.ssize + 2, (int) ((val / self.nocols) * self.ssize + 2), self.ssize-4, self.ssize-4, color) self.sqs[val] = color self.pval = val self.total += 1 def fill_screen(self): """Draw next screen load of squares or circles.""" # Blackout the drawing area. self.color_area(0, 0, self.win_width, self.win_height, self.BLACK) if self.option == 'c': # Generate random circles. self.make_circles() elif self.option == 's': # Generate random squares. self.make_squares() elif self.option == 't': # Generate textured patterns. self.make_texture() elif self.option == 'w': # Generate wallpaper patterns. self.make_wallpaper() def get_options(self): """Extract command line options.""" try: opts, args = getopt.getopt(sys.argv[1:], "cho:stvw", ["circles", "help", "output_file=", "squares", "texture", "version", "wallpaper"]) except getopt.GetoptError, e: self.usage("Illegal option -- %s" % e.opt) for opt, val in opts: if opt in ("-c", "--circles"): self.option = 'c' if opt in ("-h", "--help"): self.usage() if opt in ("-o", "--output_file"): self.output_file = val.strip() if opt in ("-s", "--squares"): self.option = 's' if opt in ("-t", "--texture"): self.option = 't' if opt in ("-v", "--version"): print >> sys.stderr, self.pname + " version " + self.version sys.exit(1) if opt in ("-w", "--wallpaper"): self.option = 'w' def get_rdir(self): """Get random direction.""" d = self.rrange(0, 3) if d == 0: # North. if (self.pval - self.nocols) < 0: d = 0 else: d = -self.nocols elif d == 1: # West. if (self.pval - 1) < 0: d = 0 else: d = -1 elif d == 2: # East. if (self.pval + 1) >= self.maxno: d = 0 else: d = 1 elif d == 3: # South. if (self.pval + self.nocols) >= self.maxno: d = 0 else: d = self.nocols return d def get_start(self): """Get random start position.""" p = self.rrange(0, self.maxno-1) while self.sqs[p] != 0: p = (p + 1) % self.maxno return p def init_colors(self): """Initialise colormap entries.""" self.colors = [ None ] * self.csize inc = self.rrange(1, 6) # Colormap increment. while True: # If set, this color is incremented. rinc = self.rrange(0, 1) ginc = self.rrange(0, 1) binc = self.rrange(0, 1) if rinc or ginc or binc: # At least one must incremented. break # Random start positions in the colormap. ired = self.rrange(0, 255) igreen = self.rrange(0, 255) iblue = self.rrange(0, 255) self.colors[self.WHITE] = (255, 255, 255) # First value is white. self.colors[self.BLACK] = (0, 0, 0) # Last value is black. for i in range(1, self.csize-1): while True: good_color = 1 red = (ired + (inc * i * rinc)) % 256 green = (igreen + (inc * i * ginc)) % 256 blue = (iblue + (inc * i * binc)) % 256 if red > 245 and green > 245 and blue > 245: good_color = 0 if red < 10 and green < 10 and blue < 10: good_color = 0 if good_color: self.colors[i] = (red, blue, green) break def main(self): """Get command line options, initialize a palette of colors, create a random image and save it.""" self.get_options() self.init_colors() self.fill_screen() self.image.save(cback.output_file) def make_array(self, ssize): """Create array for square testing.""" self.norows = self.win_height / ssize + 1 self.nocols = self.win_width / ssize + 1 self.maxno = self.norows * self.nocols self.sqs = [ 0 ] * self.maxno def make_circles(self): """Generate random circles.""" maxno = self.rrange(50, 200) # Maximum number of circles. for self.total in range(0, maxno): cx = self.rrange(0, self.win_width-1) # Center X coordinate. cy = self.rrange(0, self.win_height-1) # Centre Y coordinate. r = self.rrange(30, 125) # Circle radius. color = self.rrange(1, self.csize-2) # Get random color. width = self.rrange(2, 12) # Get random thickness. self.draw_circle(cx, cy, r+width, color) # Draw colored circle. self.draw_circle(cx, cy, r, self.BLACK) # Fill with black. def make_squares(self): """Generate random squares.""" self.ssize = self.rrange(60, 250) # Size of color circle or square. self.make_array(self.ssize) # Create array for square testing. self.total = 0 while self.total < self.maxno: # While there are squares to be filled. self.pval = self.get_start() # Get random start position. color = self.rrange(1, self.csize-2) # Get random color. self.draw_square(self.pval, color) # Draw colored square. random_len = self.rrange(3, 9) # Get random length. for i in range(0, random_len): self.dir = self.get_rdir() # Get random direction. if self.dir == 0: continue # Square[pval + dir] empty? if self.sqs[self.pval + self.dir] == 0: # Draw next square in chain. self.draw_square(self.pval + self.dir, color) else: break # Else get new start position. def make_texture(self): """Generate random texture patterns.""" color = 0 h = [ 0 ] * 10 v = [ 0 ] * 10 max_points = self.rrange(3, 10) # Number of points in set. sub = self.rrange(10, 130) div = self.rrange(sub, 2*sub) for i in range(0, max_points): h[i] = self.rrange(0, self.win_width) v[i] = self.rrange(0, self.win_height) for y in range(0, self.win_height, 2): for x in range(0, self.win_width, 2): least = self.INT_MAX for i in range(0, max_points): if x > h[i]: da = x - h[i] else: da = h[i] - x if y > v[i]: db = y - v[i] else: db = v[i] - y da = da - sub da = da & div db = db - sub db = db & div n = (db - sub) * (db - sub) & 16383 if n == 0: n = 1 distance = (da - sub) * (da - sub) / n if distance < least: least = distance color = i self.color_area(x, y, 2, 2, color) def make_wallpaper(self): """Generate wallpaper patterns.""" corna = self.rrange(-50, 50) cornb = self.rrange(-50, 50) side = self.rrange(5, 100) base = self.rrange(100, 250) for i in range(0, self.win_width, 2): for j in range(0, self.win_height, 2): x = corna + (side * i / base) y = cornb + (side * j / base) z = (x * x) + (y * y) n = z % self.csize if n and n != self.csize-1: self.color_area(i, j, 2, 2, n) def rrange(self, low, high): """Return a random integer between low and high.""" return random.randint(low, high) def usage(self): print >> sys.stderr, """\ Usage: cback [OPTION...] -c, --circles Generate image using random colored circles -h, --help Show this help message -o, --output_file=filename The name of the output file to write the image to -s, --squares Generate image using random colored squares -t, --texture Generate image using random texture pattern -v, --version Print out the cback version number -w, --wallpaper Generate image using random wallpaper pattern.""" sys.exit(2) if __name__ == "__main__": cback = CBack() cback.main() sys.exit(0) # ChangeLog # 1st Apr 2009 - 0.2 - richb - Rewritten to use Python Image Library (PIL) # 25th Mar 2009 - 0.1 - richb - Initial version (using bmp).