Joe Maller: Joe's Debug and Explore Filters: Joe's Text Bugger

This is a description of the shareware reminder message from my FXScript filters for Final Cut Pro.

This text watermarking script snippet will fade up a text message near the end of clips longer than a specified value. I used this on all of my shareware filters as a way of reminding people to buy them in a way that does not completely cripple the filters' effectiveness.

Useful shareware with effective shareware reminders usually gets purchased before passive or crippled applications. This watermark seems like a reasonable solution that doesn't upset the balance of usefulness and annoyance. I know I'm kind of patting myself on the back a bit over this, but I do think it's an elegant hack.


How it works

I needed this solution to be portable, so this script snippet is self-contained and can be tacked onto the end of any FXScript. I start with a comment indicating that the rest of the code following is the reminder. This also helps to modify snippets later on.

//The following code is Joe's Text Bugger shareware reminder

Before the script gets started I define a new variable to hold the corrected duration. To help make sure this snippet is completely portable, I added a BUG to the beginning of all my variable names, to be sure they do not collide with any existing variables defined in the rest of the script:

float BUGduration2

The duration needs to be corrected because the video output and the desktop will show different values. The video output counts fields instead of frames so it's twice what the desktop displays (unless the desktop is displaying at 100%, non-square pixels). To correct for this, I multiply the duration constant by the integer value of the aspectOf() value. When interlaced, the aspect always seems to start with 2 while the desktop displays 1.

BUGduration2 = duration/integer(aspectof(dest))

Next the snippet is contained by a set of if/then statements. These statements are sort of like gates, they prevent the code from executing unless certain criteria are met, if the criteria is true, the gate opens. The first statement checks for either or not the clip being filtered is longer than three seconds:

if BUGduration2 >= fps * 3;

Fps is a constant, and always returns the number of frames per second and never returns fields per second. The number of seconds elapsed is always equal to any number multiplied by the fps constant.

The second if statement checks to see if the current frame is within one second of the end of the clip:

if BUGduration2 * ratio >= BUGduration2 - fps;

Ratio is a decimal value similar to frame, when multiplied by duration it returns the same value as the frame constant. However frame suffers the same fate as duration and doesn't match up between the video output and the desktop display. BUGduration2 * ratio refers to the same frame on both outputs.

BUGduration2 - fps takes the total frame count and subtracts one second's worth of frames from it.

Next the snippet defines all it's variables, image buffers and text strings:

float BUGwidth, BUGheight, BUGjoeratio, BUGzoomfactor;
string BUGshareware, BUGurl;
color BUGfontcolor;

dimensionsOf(Dest, BUGwidth, BUGheight);
image BUGxbuffer[BUGwidth][BUGheight];
image BUGxbuffer2[BUGwidth][BUGheight];

setpixelformat(BUGxbuffer, kFormatRGB255);
setpixelformat(BUGxbuffer2, kFormatRGB255);

The two preceding lines are only processed in YUVaware FXScripts and are ignored if the document is not set to work in a specific space. When the color space of an image buffer is not clearly stated, a filter can produce unexpected results. Before adding these lines, the reminder text would appear with greenish edges on YUVaware filters.

BUGzoomfactor = BUGwidth/720;

BUGshareware = "Joe’s Filters are Shareware";
BUGurl = "visit to purchase";

BUGfontcolor = {255,235,235,235};

It's a good idea to fill the image buffer where the type will go to prevent errors and/or halos around the type's edge.

channelFill(BUGxbuffer, 0, 235, 235, 235);

I wanted the bugger to fade up over the first 10 frames of the last second, the following isolates those frames and creates a ratio to use as the buggers opacity:

BUGjoeratio = ((BUGduration2 * ratio) - (BUGduration2 - fps)) / (1/3 * fps);

What that does is divide the number of frames in the last second by 1/3 of the fps. This creates a fractional amount from 0 to 1 based on the current frame's place in the targeted 10 frames. This value is used later as the opacity. Values greater than one are assumed to be one when assigned to opacity.

Next the text strings are defined and drawn into the BUGxbuffer image buffer, the height of the text lines is based on fractions of the images height:

setTextSize(34 * BUGzoomfactor);

DrawString(BUGshareware, 0, (0.75 * BUGheight * 0.5) - (30 * BUGzoomfactor/integer(aspectof(dest))), 2.25 * BUGzoomfactor, BUGxbuffer, BUGfontcolor, aspectof(dest))

setTextSize(28 * BUGzoomfactor);

Drawstring(BUGurl, 0, (0.75 * BUGheight * 0.5), 3.5 * BUGzoomfactor, BUGxbuffer, BUGfontcolor, aspectof(dest))

To make the text look better, I decided to blur it slightly before applying it to the image:

Blur(BUGxbuffer, BUGxbuffer2, 0.75 * BUGzoomfactor, aspectof(dest))

Finally the text is applied to the image. I used screen mode to sort of simulate the process of burning subtitles onto film. Note that the opacity is determined by 75% of BUGjoeratio, which was defined earlier. This is how the text is made to fade up over the first 10 frames of the last second of clipds longer than three seconds.

screen(dest, BUGxbuffer2, dest, BUGjoeratio * 0.75, kalpha)

Finally, both if statements are closed out and the bugger snippet is finished

One last note, in order for this to be a bug-free standalone filter, I needed to send something to dest when the clip wasn't far enough along to trigger the filter. Simply adding the line dest = src1; before the if statement solves that problem. Final Cut Pro freaks out a bit if an FXScript doesn't send anything to the destination buffer. Try it without that line and see what I mean.

Source Code

Unlocked versions of this and all of Joe's Debugging and Exploration Filters are included for free with the trial and paid versions of Joe's Filters. Or use the source code below to build your own.

(copy and paste the code into FXBuilder)

Defeating the watermark

If you really wanted to you could circumvent the watermark by extending each clip by one second, rendering the clip and then trimming the clip by one second. This works because FCP will trim rendered files instead of re-rendering. Another method would be to divide all clips longer than three seconds into smaller clips with the Razor tool. Still, it's a bit of an annoyance and easier just to buy the filters. They're not that expensive and a lot of time went into their creation and associated explanations. Besides, since I told you how to screw me, that sort of takes the thrill away, doesn't it? Now you can't even say you thought of how to do it.

Dude, ever heard of subroutines?

Uh, yes. At first I did build this as a subroutine and bits of it still exist in Joe's Multi-Value Tester, but I found that subroutines seemed to want to be at the top of a script instead of at the bottom like I was accustomed to. If the Text Bugger was a subroutine, I would have to paste the various parts of it into several locations in each filter. Because of this, I decided that the most portable solution was a block of code at the end of each script.

page last modified: