#!/usr/bin/env python ################################################################################ # The MIT License # # Copyright (c) 2009 Brian Schott (Sir Alaran) # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. ################################################################################ import random import sys import getopt import cairo def printUsage(): print "Usage: " + sys.argv[0] + " [options] width height" print " options:" print " -s --shiny Draws a translucent gradient over the result" print " -b --base-color=NUMBER Sets the brightness of the metal to NUMBER." print " NUMBER can be any number between 0.0 and 1.0" print " Default value is 0.8" print " -a --alpha=NUMBER Sets the translucency for the gradient overlay" print " Has no effect if -s is not specified." print " Default value is 0.7" print " -v --variation=NUMBER Amound that the color of grooves will vary from" print " the base color. Default value is 0.05" print " -g --grain-strength=NUMBER The strength of the \"grain\" of the brushing." print " Keep this value fairly low. Defaults to 0.05" print " -o --output-file=FILENAME Writes the output to a PNG file named FILENAME." print " Default is brushed_output.png" def processOptions(v, a, b, g, shiny, fileName, width, height): optlist, args = getopt.getopt(sys.argv[1:], "sb:a:v:o:g:", ["shiny", "base-color=", "alpha=", "variation=", "output-file=", "grain-strength="]) for option, argument in optlist: if option in ("-s", "--shiny"): shiny = True elif option in ("-b", "--base-color"): try: b = float(argument) except ValueError: print "Error, could not convert " + argument + " to a number." elif option in ("-a", "--alpha"): try: a = float(argument) except ValueError: print "Error, could not convert " + argument + " to a number." elif option in ("-v", "--variation"): try: v = float(argument) except ValueError: print "Error, could not convert " + argument + " to a number." elif option in ("-g", "--grain-strength"): try: g = float(argument) except ValueError: print "Error, could not convert " + argument + " to a number." elif option in ("-o", "--output-file"): fileName = argument else: assert False, "Unhandled option" try: width = int(args[0]) except ValueError: print "Could not convert " + args[0] + " to a width value" try: height = int(args[1]) except ValueError: print "Could not convert " + args[1] + " to a height value" return v, a, b, g, shiny, fileName, width, height def drawBrushedMetal(context, width, height, baseColor, variation, grain): # Base lines for i in range(height): r = random.randint(0, 1) c = baseColor if r == 0: c = c - (variation * random.random()) else: c = c + (variation * random.random()) context.set_source_rgba(c, c, c, 1.0) context.set_line_width(1.0) context.move_to(0, i + 0.5) context.line_to(width, i + 0.5) context.stroke() # Random gradient scratches for i in range(max(width, height) // 4): r = random.randint(0, 1) c = baseColor if r == 0: c = c - (variation * random.random()) else: c = c + (variation * random.random()) context.set_source_rgba(c, c, c, 1.0) x = random.randint(0, width) y = random.randint(0, height) w = ((width / 2.0) + (width * random.random() / 5.0)) g = cairo.LinearGradient(x, y, x + w, y) g.add_color_stop_rgba(0.0, c, c, c, 0.0) g.add_color_stop_rgba(0.25, c, c, c, 1.0) g.add_color_stop_rgba(0.75, c, c, c, 1.0) g.add_color_stop_rgba(1.0, c, c, c, 0.0) context.set_source(g) context.rectangle(x, y, w, 1.0) context.fill() # Mostly transparent stripes. This improves the look of the result quite a bit for i in range(0, height, 2): context.set_source_rgba(0.0, 0.0, 0.0, grain) context.set_line_width(1.0) context.move_to(0, i + 0.5) context.line_to(width, i + 0.5) context.stroke() def drawShine(context, shineAlpha, width): g = cairo.LinearGradient(0.0, 0.0, width, 0.0) g.add_color_stop_rgba(0.0, 1.0, 1.0, 1.0, 0.0) g.add_color_stop_rgba(0.3, 1.0, 1.0, 1.0, shineAlpha * 0.75) g.add_color_stop_rgba(0.5, 1.0, 1.0, 1.0, shineAlpha) g.add_color_stop_rgba(0.7, 1.0, 1.0, 1.0, shineAlpha * 0.75) g.add_color_stop_rgba(1.0, 1.0, 1.0, 1.0, 0.0) context.set_source(g) context.paint() def main(): # At least a width and height need to be specified, so quit if we don't have # enough arguments if len(sys.argv) < 3: printUsage() sys.exit(1) # Option variables v = 0.05 a = 0.7 b = 0.8 g = 0.05 shiny = False fileName = "brushed_output.png" w = None h = None v, a, b, g, shiny, fileName, w, h = processOptions(v, a, b, g, shiny, fileName, w, h) s = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h) context = cairo.Context(s) drawBrushedMetal(context, w, h, b, v, g) if shiny == True: drawShine(context, a, w) s.write_to_png(fileName) sys.exit(0) if __name__ == "__main__": main()