Joe Maller: FXScript Reference: Building Joe's Pixelizer

The FXScript concepts behind my pixelization filter for Final Cut Pro

Visit the New FXScript Reference and Joe's Filters sites. These pages will be phased out soon and may already be out of date.

Joe's Filters
FXScript Reference

This page describes the FXScript concepts behind Joe's Pixelizer. If you're looking for the page about how to use the filter, click here.

Overview

Joe's Pixelizer uses a pair of loops to represent the original image with large blocks of various shapes. The first loop repeats once for each row in the image, the second loop is nested inside the first loop and draws a row of blocks across one row based on the color of the spot it is over. After the second loop finishes, the first loop starts again one row lower. This sort of process is called a recursive operation.

Throughout the following explanations, "pixels" will refer to the original image pixels and "blocks" will refer to the drawn squares, circles or diamonds.

The first Block

The most important piece of information in this script is the location of the current block. The two loops will increment this value to fill the image with blocks. I called the variable WhereAmI and intially defined it's value with this code:

point WhereAmI

WhereAmI = {integer(w/-2 + blocksize/2),
   integer(h/-2 + blocksize/aspectOf(dest)/2)}

Because WhereAmI is a 2D point variable, it needs to have two coordinates{x,y}. To get to the upper left corner, the script divides the width, w, by -2. Coordinates in Final Cut pro measure from the center of the frame, so the upper left coner is negative half the width of the image horizontally and half the height of the image vertically. I added half the blocksize to place the blocks into the visible space, if not, the first block would be placed at {-360,-240} and only the bottom right coner would be visible. If the block was 20 pixels in size, it would need to appear at {-350, -230} to be seen. Because of interlacing and aspect ratio concerns, the y value (vertical) would also need to be divided by the aspect ratio to get the correct placement. (A lot of this is moot anyway because of video's title-safe and action-safe areas.)

Because DrawSoftDot() considers aspect ratio when drawing blocks, I had to include the aspect ratio in the vertical equasion for things to line up correctly. Sub-pixel values (decimal numbers) round to the closest integer value instead, the integer() function helps to prevent unexpected gaps between pixels, with raw results, gaps appeared between blocks.

Essence of the Pixelizer

Below is a simplified version of the two nested loops which make Joe's Pixelizer work. I removed several of the customizing options to make things easier to see.Values for increasing spacing or adding random scatter are just glommed on in the appropriate spot to modify the WhereAmI.x or WhereAmI.y.I also used zoomfactor to line up the display between the video output and the desktop display, zoomfactor is explained with Joe's Multi-Value Tester.

repeat while (WhereAmI.y < h + 2 * blocksize/aspectof(dest))
  repeat while (whereAmI.x < w + 2 * blocksize)
    DrawSoftDot(xbuffer, ScatterPoint, shapeArray[shape], blocksize, 0, 20, pointColor, 1, aspectOf(dest));
    WhereAmI.x = WhereAmI.x + blocksize
    End Repeat;
  WhereAmI.x = integer(w/-2 + (blocksize/2 + blocksize))
  WhereAmI.y = WhereAmI.y + blocksize / aspectof(dest))
end repeat;

Randomness

FXScript's RandomSeed() function how a script will generate random values. When RandomSeed() is set to zero, values are recalculated differently for each frame. Any value besides zero will return the same set of random results each for each frame. RandomSeed() is also discussed on the FXScript Functions page.

The "Dancing Scatter" checkbox in Joe's Pixelizer determines whether randomly spaced blocks will be in the same place for each frame or if their values will be recalculated for each frame. Beacause the checkbox will return a zero or one, it's value could be fed into RandomSeed() directly. Unfortunately, I wanted the checkbox to do the opposite of what it's value represented. When the checkbox was unchecked (returned a zero), I wanted the blocks position to be fixed, when it was checked (returned a one), the blocks should bounce around between frames. Because this behavior is opposite of what the checkbox ScatterDance returns, I needed to do something to flip the values.

I could have used a simple if/then statement to reverse the value, which might have looked like this:

if ScatterDance = 0;
  ScatterDance = 1
else
  ScatterDance = 0
end if

Instead I came up with a little arithmetic trick to reverse the values:

randomseed(ScatterDance * -1 + 1)

This makes me happy because of how simple it is. The checkbox returns either zero or one.

If the checkbox is unchecked (returns a zero): Anything multiplied by zero equals zero, so adding one to that will result in the opposite value.

If the checkbox is checked (returns a one): The initial multiplication will reverse the one, changing it to a negative. Adding one to negative one equals zero.

So that little statement will work to reverse both possible values. I don't know for sure if math is faster than if/then statements, but mathmatical solutions seem cleaner and more elegant.

There is an operation called bit-shifting, but I don't fully undersand that yet. After explaining the math above, I'm wondering if bit-shifting would be even faster. There's always more to learn (which is why sleep frustrates me).

Wrapping up

The above loops draw the pixels into an extra image buffer, xbuffer. My standard set of compositing controls is nested inside an if/then statement which considers the Transparent Background checkbox before finishing up the filter. If the checkbox is checked, xbuffer is placed directly into dest, otherwise xbuffer is composited onto the original image.

if transparentBG == 0
   [compositing statements omitted...]
else
   dest = xbuffer
end if

Conclusion

This is one of my most complicated filters, but is built around a very simple, repetitive process. My biggest stumbling point was the vertical gapping between blocks, which took a few days to figure out. Figuring out the main loops took the most time, adding extra adjustments and features onto the working loops went smoothly.

The complete FXScript source code for Joe's Pixelizer is included with the paid version of Joe's Filters.

Buy Joe's Filters Download the Free Trial
 
page last modified: October 23, 2017
Copyright © 1996-2003 Joe Maller