« Creating and Saving Bitmaps to Files via C#: GDI+ and Direct2D compared | Main | Natively viewing Visio Diagrams in your web browser – a practical use of Embedding SVG in XHTML »
Monday
Apr052010

IronPython: Stealing Colors with System.Drawing.Bitmap (and yes Visio)

I collect color sets for a hobby. Every time I see a page like this one about the traditional colors of Japan or this one about the of Crayola crayon colors I can’t help but capture them in a drawing so that I might be inspired later to use them in my own diagrams.

In this post, I’m going to show you how to use IronPython to help automate this task. Along the way we’ll learn a little about working with the System.Drawing namespace and of course I’ll do mix in some Visio interop.

FIRST THE COLORS

I found an interesting set of colors here: http://corte.si/posts/code/sortvis-fruitsalad/index.html

image

First step is to just save the image

image

I saved it as “D:\swatch.png”

Let’s start IronPython 2.6 with my favorite command-line options

"c:\Program Files (x86)\IronPython 2.6\ipy.exe" -X:TabCompletion -X:ColorfulConsole

image

Now import clr and System and add a reference to System.Drawing and then import System.Drawing

image

Now we are going to load the bitmap and fetch each pixel on the top row. Then we examine how many pixels we have (we should have 512 since that’s the width of the bitmap) and we’ll print out the contents of the first pixel

bmp = System.Drawing.Bitmap( "D:\\swatch.png" )
pixels = [ bmp.GetPixel(x,0) for x in xrange( bmp.Width ) ]
print len(pixels)
print pixels[0]

image

Now we’re going to do the Visio thing – we’ll launch it and get a reference to the active page

clr.AddReference("Microsoft.Office.Interop.Visio")
import Microsoft.Office.Interop.Visio
IVisio = Microsoft.Office.Interop.Visio
visapp = IVisio.ApplicationClass()
doc = visapp.Documents.Add("")
page = visapp.ActivePage

image

running those commands caused Visio to appear with a blank document …

image

Before we do the drawing let’s consider that we have 512 pixels

We could just draw a long line of squares, but that would make for an inconvenient drawing. We’d like something a bit more compact – place all the squares into a roughly rectangular area.

Thinking about this, I’ll approach the problem as one of mapping a series of numbers 0 to 511 to (x,y) coordinates

Now that means I have to figure out how many shapes I need on the edges … calling System.Math.Sqrt shows that I’ll need around 23 shapes for each side

image

So now I map the coordinates like this …

coords = [ (i%23,i/23) for i in xrange( len(pixels) ) ]

then I calculate the rectangle coordinates for all the shapes based on those coordinates

length = 0.25
rects = [ ( x*length, y*length, (x+1)*length, (y+1)*length ) for x,y in coords ]

 

then I draw the shapes

shapes = [ page.DrawRectangle( *rect ) for rect in rects ]

NOTE: there are better ways of drawing so many shapes (for example using the DropMany method) but to keep the example simple I’ll do it the simple way

image

What it rendered is this …

image

page.ResizeToFitContents()

image

Okay, that’s a bit easier to see.

So, now we get to the colors. We have a list of pixels. We have a list of shapes. what we want to do is set the FillForegnd cell of each shape to the appropiate rgb formula

for a single shape it would look like this…

shape1.Cells("FillForegnd").Formula = “=rgb(255,128,0)”

ok, so we calculate the formulas like this

formulas = [ "=RGB({0},{1},{2})".format(p.R, p.G, p.B) for p in pixels ]

and then we pair each formula with a shape and set the cell accordingly

for s,f in zip(shapes,formulas) : s.Cells("FillForegnd").Formula = f

image

And this yields…

image

 

 

 

 

 

 

 

 

 

 

 

 

And here’s the full script

import clr
import System
clr.AddReference("System.Drawing")
import System.Drawing

# Get the colors from the bitmap
bmp = System.Drawing.Bitmap( "D:\\swatch.png" )
pixels = [ bmp.GetPixel(x,0) for x in xrange( bmp.Width ) ]

# Start Visio
clr.AddReference("Microsoft.Office.Interop.Visio")
import Microsoft.Office.Interop.Visio
IVisio = Microsoft.Office.Interop.Visio
visapp = IVisio.ApplicationClass()
doc = visapp.Documents.Add("")
page = visapp.ActivePage

# Draw the shapes
coords = [ (i%23,i/23) for i in xrange( len(pixels) ) ]
length = 0.25
rects = [ ( x*length, y*length, (x+1)*length, (y+1)*length ) for x,y in coords ]
shapes = [ page.DrawRectangle( *rect ) for rect in rects ]

#Adjust the page
page.ResizeToFitContents()

# Set the colors
formulas = [ "=RGB({0},{1},{2})".format(p.R, p.G, p.B) for p in pixels ]
for s,f in zip(shapes,formulas) : s.Cells("FillForegnd").Formula = f

 

The full script running looks like this…

image

PARTING THOUGHTS

PrintView Printer Friendly Version

EmailEmail Article to Friend

Reader Comments

There are no comments for this journal entry. To create a new comment, use the form below.

PostPost a New Comment

Enter your information below to add a new comment.
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>