Pixel operations: getPixel and setPixel
July 4, 1999
by Pat McClellan
Dear Multimedia Handyman,
I am currently developing a 3x3 puzzle where the 9 squares shuffle to make up a whole image. This is not a problem apart from the fact that I need to allow a user to import his own picture into the process. This means dynamically 'cutting up' or cropping the picture within Director through Lingo, which is impossible, isn't it?
Sarah Davies
Dear Sarah,
Exactly what do you mean by "impossible"? The answer is... yes and no.
There is no documented way to do this in Director. However, at UCON 99, I heard about some undocumented Lingo commands in Director 7.02 which will make it possible (with some limitations). By "undocumented", I mean that these commands aren't in any of the manuals, the Lingo dictionary, or Director Help. You won't find any technotes about them on Macromedia's website. Also, you won't be able to ask Macromedia's tech support for help on them.
Undocumented Lingo generally exists because the engineers were working on adding some features to Director, but ran out of time to perfect it. So, they leave them in there but don't tell anybody about them. You're pretty much on your own figuring out the syntax, limitations and capabilities of these commands.
These new commands that we'll be looking at here are "getPixel" and "setPixel". Basically, getPixel allows you to get the color value for any pixel in a cast member. The syntax (best I can figure) is something like this...
getPixel(member whichMem, hPix, vPix)
You have to specify the member (name or number), followed by the horizontal pixel and vertical pixel as measured from the top left corner of the cast member (not the regPoint). The top-left pixel appears to be (0,0), not (1,1).
The value returned from getPixel is an integer, though I'm not sure why it returns the value it does. The value returned appears to be 255 - the index position of the pixels color in the 8-bit palette. So, let's say that the top left pixel of member "ball" is palette index color 14, then...
set x = getPixel(member "ball", 0,0) put x -- 241
I could only get this to work dependably in 8-bit. I've seen some posts on Direct-L where people were doing stuff with it in 16-bit, but I don't know how. The command still returns a value, but I couldn't do much with that value. This is another situation where undocumented really means "experimental". Apparently, the value returned was different in version 7.0 than in 7.02, and there was some variance between Mac and Windows. Luckily, for our purposes, we don't have to make any sense out of the number. We're just going to grab it from a pixel in one image and assign it to a corresponding pixel in another.
Now, assuming that we can get the pixel values in the image you want to import, we'll have to assign those values to your puzzle pieces. For that, we'll use the other command: setPixel. The syntax for this one appears to be...
setPixel(member whichMem, hPix, vPix, ¬ colorValue)
This command takes a fourth parameter: colorValue. Again, the value assigned to this needs to be 255 minus the palette index number for the color you want. For example, let's say that you want to set pixel (3, 3) of member "ball" to red in the Mac system palette (index number 35), then you'd use this command:
setPixel(member "ball", 3, 3, 220)
The colorValue is 255 - 35 = 220. Again, I have no idea why it works this way... it just does. Any member that you're setting a color value for needs to be 8-bit (at least in my experiments.) I tried using 16-bit members for both the getPixel and setPixel commands. You could tell what the image was, but the color was distorted.
Another limitation is that the cast member for which you're setting a pixel value must have some value for that pixel already. So if you're copying the value from one image to another, the "destination" image must be at least as large as the original.
Here's the demo movie I made, where the large images and small black squares are 8-bit. When you click on any of the large images, a handler called copyPix sequentially grabs the value of each pixel and resets the color value for the corresponding pixel in the small squares. This takes some serious processing, which is why you'll see the delay when you click one of the images.
You can download this movie in Mac or PC format.
Drag the small tiles around the stage and then click on one of the large images. You'll see that it's breaking up the large image and writing the appropriate segment to the small square cast members. Here's the Lingo handler I wrote to do the process:
on copyPixels originalMem originalWidth = the width of member originalMem originalHeight = the height of member originalMem widthA = originalWidth/3 widthB = widthA * 2 heightA = originalHeight/3 heightB = heightA * 2 topLeft = member "topLeft" topMid = member "topMid" topRight = member "topRight" centerLeft = member "centerLeft" centerMid = member "centerMid" centerRight = member "centerRight" bottomLeft = member "bottomLeft" bottomMid = member "bottomMid" bottomRight = member "bottomRight" repeat with i = 0 to widthA repeat with j = 0 to heightA x = getPixel(member originalMem, i, j) setPixel (topleft, i, j, x) end repeat end repeat repeat with i = 0 to widthA repeat with j = 0 to heightA x = getPixel(member originalMem, widthA + i, j) setPixel (topMid, i, j, x) end repeat end repeat repeat with i = 0 to widthA repeat with j = 0 to heightA x = getPixel(member originalMem, widthB + i, j) setPixel (topRight, i, j, x) end repeat end repeat repeat with i = 0 to widthA repeat with j = 0 to heightA x = getPixel(member originalMem, i, heightA + j) setPixel (centerLeft, i, j, x) end repeat end repeat repeat with i = 0 to widthA repeat with j = 0 to heightA x = getPixel(member originalMem, widthA + i, ¬ heightA + j) setPixel (centerMid, i, j, x) end repeat end repeat repeat with i = 0 to widthA repeat with j = 0 to heightA x = getPixel(member originalMem, widthB + i, ¬ heightA + j) setPixel (centerRight, i, j, x) end repeat end repeat repeat with i = 0 to widthA repeat with j = 0 to heightA x = getPixel(member originalMem, i, heightB + j) setPixel (member bottomLeft, i, j, x) end repeat end repeat repeat with i = 0 to widthA repeat with j = 0 to heightA x = getPixel(member originalMem, widthA + i, ¬ heightB + j) setPixel (bottomMid, i, j, x) end repeat end repeat repeat with i = 0 to widthA repeat with j = 0 to heightA x = getPixel(member originalMem, widthB + i, ¬ heightB + j) setPixel (bottomRight, i, j, x) end repeat end repeat end
It looks like a lot of code, but it's really just the same routine running 9 times with different parameters. I probably should have broken it out to be a separate handler, but I'm feeling lazy today. The first few lines of code simply do the math needed to chop the image into 9 pieces. Then, I use the resulting values as the starting and ending coordinates when I create each tile segment.
So, there's the solution, but don't send me any questions about these commands. I don't know any more than I've figured out here. Good luck!
Copyright 1997-2024, Director Online. Article content copyright by respective authors.