Joe Maller: FXScript Reference: Building Joe's Color Glow

How I built my FXScript color glow 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 Color Glow. If you're looking for the page about how to use this filter, click here.


This filter works by selecting a color and then applying a modified and filled selection back onto the original image. Several relatively simple operations are combined to create a complex effect. The filter uses a keying operation to build it's initial selection, and all of the various options modify that selection.

There are four main steps to Joe's Color Glow:

  1. Create a selection based on a color (tolerance, edge soften, invert)
  2. Modify that selection (blur, threshold)
  3. Create the application image (color or image through the selection)
  4. Composite the application image onto the original image.

Channels: The Key to Keying

After working with digital images and Photoshop for a long time, I've come to believe there is no diference between a selection and a greyscale image. Everytime something is selected in Photoshop, a temporary grayscale image is created corresponding to the selected area.

The color glow effect is based on a selected area. That selection is then modified, filled and composited back onto the original image. Since my selections are based on a selected color, I could extract the alpha channel generated by FXScript's RGBColorKey() functions as my selection.

My First YUV headache

This filter was the first where I encountered how YUV and RGB color spaces interact in FXScript. The filter does almost all of it's work in the RGB255 color space. I followed the lead set out in all Final Cut Pro's built in keying filters, which all convert to RGB before keying. I think the reason for converting color space is to match the the color picked on the RGB monitor to the results on the video output. Selecting an RGB color and applying it to a YUV key results in different keyed areas between the desktop preview and video output.

The variable ColorSpace is just shorthand for the GetPixelFormat(dest) function, this is something many of the included filters did to (i guess) separate the colo space from the color space query.

ColorSpace = GetPixelFormat(dest)

I'm not sure if the pixel format is stored when the ColorSpace variable is declared, or if the value is dynamically evaluated each time ColorSpace is referenced. To be safe, I sometimes check the value more than once in a filter.

The more I use this, the more absurd it seems. The code GetPixelFormat(dest) is not especially unwieldy, and checking when the infomation is needed would simplify the code in my mind. I plan to remove this variable in future versions of my filters.

How it works

Since each color conversion must be wrapped in an if/else statement to prevent double-conversions, the filter's source code looks worse than it is. The variable BlurType refers to the selection in the Use: popup menu, BlurType = 1 will fill with a color, BlurType = 2 will use the original image.

The code below uses BlurType and ColorSpace with nested if/then statements to fill the image buffer xbuffer with either the fill color or the original image:

if ColorSpace == kFormatYUV219;
   if BlurType == 1;
      setpixelformat(xbuffer, kFormatRGB255);
      ChannelFill(xbuffer, 255, Fill.r, Fill.g, Fill.b);
   end if
   if BlurType == 2;
      ConvertImage(src1, xbuffer, kFormatRGB255);
   end if
   if BlurType == 1;
      ChannelFill(xbuffer, 255, Fill.r, Fill.g, Fill.b);
   end if
   if BlurType == 2;
      xbuffer = src1;
   end if
end if

That might not be the most efficient way of getting information into xbuffer. When I have some time, I'm going to look at other ways of accomplishing that.

Next the script converts the orinal image to RGB255 to prepare for keying. I put this into an if/then statement to be safe even though all DV source starts as YUV219. RGB255 is the same format as the desktop display so the keying operation will match up between the video output and the desktop preview. The end goal of this code block is to get the image content of src1 (the original clip) into xbuffer2.

if GetPixelFormat(src1) != kFormatRGB255;
   ConvertImage(src1, xbuffer2, kFormatRGB255);
   setpixelformat(xbuffer2, kFormatRGB255);
   xbuffer2 = src1;
end if

Now xbuffer and xbuffer2 both contain RGB255 data and are ready for the keying operation which will specify the glowing area. Joe's Color Glow uses only the alpha channel generated by RGBColorKey(), using xbuffer2 as the source and xbuffer's alpha channel as the destination. This function has a lot of options, and they need to appear on the same line, all the following code is for the RGBColorKey() function.

RGBColorKey(xbuffer2, xbuffer, SampleColor.r, Tolerance, SampleColor.g, Tolerance, SampleColor.b, Tolerance, Soften, false);

Tolerance can be set for each color, using the same value for each averages the tolerance across all the colors. SampleColor is the first color in the filter's controls and the color the selections will be based upon. Soften extends the selection, resulting in an alpha channel with shades of gray between black and white. The false at the end tells the script not to affect the RGB channels of the target image buffer.

I've got a selection, now what do I do?

After establishing the selection, the script applies various effects to it before compositing back onto the image.

One of the simplest things to do is inverting the alpha channel of the image buffer which will be composited later. Below is a short if statement which will invert the channel based on the setting of a checkbox. Because the keying operation masks the area around SampleColor, the following code is set to invert the selection unless the user chooses invert, then the script leaves the inverted result of RGBColorKey() as is. I have the following statement on one line for cleanliness. This works on one line so long as semicolons follow each natural line of the statement.

if inverter==0;
   InvertChannel(xbuffer, xbuffer, 1, 0, 0, 0);
end if

The numbers after the destination and target image buffers refer to the channels to affect. The order is alpha, red, green and blue.


I borrowed code from several of my other filters for many of the options in Joe's Color Glow. Other changes to the composite image include pre and post blurring and a thresholding operation borrowed from Building Joe's Threshold and Posterize. My standard set of compositing controls finishes the filter.

The complete FXScript source code for Joe's Color Glow 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