The title kinda says it all; if you were creating filters visually (as I was the other day) but needed to ultimately produce them dynamically in ActionScript (as I did), then you may wish (as I did) for a JSFL command to spit out the necessary ActionScript based on your visual, on-stage filters.

As you can guess, I went ahead and wrote that JSFL command. Here it is (viewable as a text file here):

var sel = fl.getDocumentDOM().selection;
fl.outputPanel.clear();

var iLen = sel.length;
var qualities = {
	low:1,
	medium:2,
	high:3
}
for (var i = 0; i < iLen; i++) {
	var s = sel[i];
	if (s.instanceType != "symbol") continue;
	fl.trace("FILTERS FOR " + (s.name == "" ? "UNNAMED INSTANCE" : s.name) + " -------------------------------------");
	var filters = s.filters;
	var jLen = filters.length;
	for (var j = 0; j < jLen; j++) {
		var f = filters[j];

		for (var p in f) {
			fl.trace(p + " :: " + f[p]);
		}

		var en = f.enabled ? "" : "//";
		var q = qualities[f.quality];
		if (f.color) {
			var colorInfo = getColorAndAlpha(f.color);
		}
		if (f.highlightColor) {
			var highlightColorInfo = getColorAndAlpha(f.highlightColor);
		}
		if (f.shadowColor) {
			var shadowColorInfo = getColorAndAlpha(f.shadowColor);
		}
		if (f.colorArray) {
			var colorArray = [];
			var alphaArray = [];
			var iLen = f.colorArray.length;
			for (var i = 0; i < iLen; i++) {
				var info = getColorAndAlpha(f.colorArray[i]);
				colorArray.push(info.color);
				alphaArray.push (info.alpha);
			}
		}
		var s = f.strength / 100;

		switch (f.name) {
			case "glowFilter":
				fl.trace(en + "new GlowFilter("+colorInfo.color+", "+colorInfo.alpha+", "+f.blurX+", "+f.blurY+", "+s+", "+q+", "+f.inner+", "+f.knockout+")");
				break;
			case "dropShadowFilter":
				fl.trace(en + "new DropShadowFilter("+f.distance+", "+f.angle+", "+colorInfo.color+", "+colorInfo.alpha+", "+f.blurX+", "+f.blurY+", "+s+", "+q+", "+f.inner+", "+f.knockout+", "+f.hideObject+")");
				break;
			case "blurFilter":
				fl.trace(en + "new BlurFilter("+f.blurX+", "+f.blurY+", "+q+")");
				break;
			case "bevelFilter":
				fl.trace(en + "new BevelFilter("+f.distance+", "+f.angle+", "+highlightColorInfo.color+", "+highlightColorInfo.alpha+", "+shadowColorInfo.color+", "+shadowColorInfo.alpha+", "+f.blurX+", "+f.blurY+", "+f.strength+", "+q+", \""+f.type+"\", "+f.knockout+")");
				break;
			case "gradientBevelFilter":
				fl.trace(en + "new GradientBevelFilter("+f.distance+", "+f.angle+", ["+colorArray+"], ["+alphaArray+"], ["+f.posArray+"], "+f.blurX+", "+f.blurY+", "+f.strength+", "+q+", \""+f.type+"\", "+f.knockout+")");
				break;
			case "gradientGlowFilter":
				fl.trace(en + "new GradientGlowFilter("+f.distance+", "+f.angle+", ["+colorArray+"], ["+alphaArray+"], ["+f.posArray+"], "+f.blurX+", "+f.blurY+", "+f.strength+", "+q+", \""+f.type+"\", "+f.knockout+"");
				break;
			case "adjustColorFilter":
				var m = new ColorMatrix();
				m.adjustColor(f.brightness, f.contrast, f.saturation, f.hue);
				fl.trace(en + "new ColorMatrixFilter(["+m.matrix+"]);");
				break;
		}

	}
	fl.trace("\n");
}

/**
 * Returns an Object with two properties, color and alpha, as strings, based on the parsing of the color string coming in.  The color string
 * will be something like "#FF9933" or "#FF993366".  If it's a 32-bit color, the alpha channel is contained in the last two bytes
 * (66 in this case).
**/
function getColorAndAlpha(color) {
	var colorInfo = {};
	if (color.length == 9) {
		colorInfo.alpha = Math.round(parseInt(color.substr(7, 2), 16) / 0xFF * 100) / 100;
		colorInfo.color = color.substr(0, 7).replace(/^#/, "0x")
	} else if (color.length == 7) {
		colorInfo.alpha = 1;
		colorInfo.color = color.replace(/^#/, "0x")
	} else {
		fl.trace("Problem parsing color.")
	}
	return colorInfo;
}

// CLASSES ============================================================================

/**
 * Allows for conversion of simple color adjustment properties into an ActionScript-ready matrix Array.
**/
function ColorMatrix(m) {

	this._matrix = m?m.concat():ColorMatrix.IDENTITY_MATRIX.concat();

	/**
	 * Copies the supplied matrix to the internal matrix.
	 * @param	m	Array
	**/
	this.copyMatrix = function(m) {
		this._matrix = m.concat();
	}
	/**
	 * Ensures that the supplied matrix Array is of the correct length.  It slices off extraneous elements, or
	 * fills in missing elements with IDENTITY elements.
	 * @param	m	Array
	 * @return	Fixed Array
	**/
	this.fixMatrix = function(m) {
		if (m.length  ColorMatrix.LENGTH) {
			m = m.slice(0, ColorMatrix.LENGTH);
		}
		return m;
	}

	/**
	 * Makes sure the incoming value in between a negative and positive limit value.
	 * @param	val		The value to clean
	 * @param	limit	The absolute value of the range.  If the limit is 100, then the value is ensured to be between -100 and 100.
	 * @return	Number; the cleaned value.
	**/
	this.cleanValue = function(val, limit) {
		return Math.min(limit, Math.max(-limit, val));
	}

	/**
	 * Core function for translating color adjustments to matrix arrays.  The internal matrix is affected.
	 * @param	m	Array
	**/
	this.multiplyMatrix = function(m) {
		var col = [];

		for (var i=0; i<5; i++) {
			for (j=0; j<5; j++) {
				col[j] = this._matrix[j+i*5];
			}
			for (var j=0; j<5; j++) {
				var val=0;
				for (var k=0; k<5; k++) {
					val += m[j+k*5]*col[k];
				}
				this._matrix[j+i*5] = val;
			}
		}
	}

	/**
	 * Adjusts the internal matrix array to reflect a brightness adjustment.
	 * @param	val		The brightness, from -100 to 100.
	**/
	this.adjustBrightness = function(val) {
		val = this.cleanValue(val,100);
		if (val == 0 || isNaN(val)) { return; }
		this.multiplyMatrix([
			1,0,0,0,val,
			0,1,0,0,val,
			0,0,1,0,val,
			0,0,0,1,0,
			0,0,0,0,1
		]);
	}

	/**
	 * @param	val	The Contrast, from -100 to 100
	**/
	this.adjustContrast = function(val) {
		val = this.cleanValue(val,100);
		if (val == 0 || isNaN(val)) { return; }
		var x;
		if (val < 0) {
			x = 127 + (val / 100) * 127
		} else {
			x = val % 1;
			if (x == 0) {
				x = ColorMatrix.DELTA_INDEX[val];
			} else {
				//x = ColorMatrix.DELTA_INDEX[(val<<0)]; // this is how the IDE does it.
				x = ColorMatrix.DELTA_INDEX[(val<<0)]*(1-x)+ColorMatrix.DELTA_INDEX[(val< 0) ? 3*val/100 : val/100);
		var lumR = 0.3086;
		var lumG = 0.6094;
		var lumB = 0.0820;
		this.multiplyMatrix([
			lumR*(1-x)+x,lumG*(1-x),lumB*(1-x),0,0,
			lumR*(1-x),lumG*(1-x)+x,lumB*(1-x),0,0,
			lumR*(1-x),lumG*(1-x),lumB*(1-x)+x,0,0,
			0,0,0,1,0,
			0,0,0,0,1
		]);
	}

	/**
	 * @param	val	The Hue, from -180 to 180
	**/
	this.adjustHue = function(val) {
		val = this.cleanValue(val,180)/180*Math.PI;
		if (val == 0 || isNaN(val)) { return; }
		var cosVal = Math.cos(val);
		var sinVal = Math.sin(val);
		var lumR = 0.213;
		var lumG = 0.715;
		var lumB = 0.072;
		this.multiplyMatrix([
			lumR+cosVal*(1-lumR)+sinVal*(-lumR),lumG+cosVal*(-lumG)+sinVal*(-lumG),lumB+cosVal*(-lumB)+sinVal*(1-lumB),0,0,
			lumR+cosVal*(-lumR)+sinVal*(0.143),lumG+cosVal*(1-lumG)+sinVal*(0.140),lumB+cosVal*(-lumB)+sinVal*(-0.283),0,0,
			lumR+cosVal*(-lumR)+sinVal*(-(1-lumR)),lumG+cosVal*(-lumG)+sinVal*(lumG),lumB+cosVal*(1-lumB)+sinVal*(lumB),0,0,
			0,0,0,1,0,
			0,0,0,0,1
		]);
	}

	/**
	 * Adjust all four properties at once
	 * @param	brightness (-100 - 100)
	 * @param	contrast (-100 - 100)
	 * @param	saturation (-100 - 100)
	 * @param	hue (-180 - 180)
	**/
	this.adjustColor = function(brightness, contrast, saturation, hue) {
		this.adjustHue(hue);
		this.adjustContrast(contrast);
		this.adjustBrightness(brightness);
		this.adjustSaturation(saturation);
	}

	this.__defineGetter__("matrix", function() {return this._matrix.concat(); });
	this.toString = function() {
		//return "ColorMatrix: " + this._matrix.toString();
		var out = "ColorMatrix:\n";
		var v;
		var iLen = this._matrix.length;
		var l = 4;
		for (var i = 0; i  l) {
				v = v.substr(0, l);
			} else {
				while (v.length < l) {
					v += " ";
				}
			}
			out += v;
			if (i % 5 == 4) {
				out += "\n";
			} else {
				out += " ";
			}
		}
		return out;
	}

}

ColorMatrix._DELTA_INDEX = [
	0,    0.01, 0.02, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1,  0.11,
	0.12, 0.14, 0.15, 0.16, 0.17, 0.18, 0.20, 0.21, 0.22, 0.24,
	0.25, 0.27, 0.28, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.42,
	0.44, 0.46, 0.48, 0.5,  0.53, 0.56, 0.59, 0.62, 0.65, 0.68,
	0.71, 0.74, 0.77, 0.80, 0.83, 0.86, 0.89, 0.92, 0.95, 0.98,
	1.0,  1.06, 1.12, 1.18, 1.24, 1.30, 1.36, 1.42, 1.48, 1.54,
	1.60, 1.66, 1.72, 1.78, 1.84, 1.90, 1.96, 2.0,  2.12, 2.25,
	2.37, 2.50, 2.62, 2.75, 2.87, 3.0,  3.2,  3.4,  3.6,  3.8,
	4.0,  4.3,  4.7,  4.9,  5.0,  5.5,  6.0,  6.5,  6.8,  7.0,
	7.3,  7.5,  7.8,  8.0,  8.4,  8.7,  9.0,  9.4,  9.6,  9.8,
	10.0
];
ColorMatrix._IDENTITY_MATRIX = [
	1,0,0,0,0,
	0,1,0,0,0,
	0,0,1,0,0,
	0,0,0,1,0,
	0,0,0,0,1
]
ColorMatrix._LENGTH = ColorMatrix._IDENTITY_MATRIX.length;

ColorMatrix.__defineGetter__("DELTA_INDEX",     function() { return ColorMatrix._DELTA_INDEX; } );
ColorMatrix.__defineGetter__("IDENTITY_MATRIX", function() { return ColorMatrix._IDENTITY_MATRIX; } );
ColorMatrix.__defineGetter__("LENGTH",          function() { return ColorMatrix._LENGTH; } );

I have tested this with a variety of symbol instances with a variety of filters applied to them. Seems to work for me, but I really only used it for my recent application. Please let us know if you run into issues that break the script or produce erroneous results.

A few caveats:

This script doesn’t do two things, both of which are significant to know about.

(UPDATE: I have since resolved the first item below. See this post for more information. The code above as well as the linked text file have been updated with the new code)

(UPDATE 2: I’ve also resolved the second item. This post has more information, and the code on this page has been updated.)

  1. For some reason, the JSFL Filter object doesn’t contain information about the color’s alpha of a filter. All filters except for the Blur and Adjust Color (ColorMatrixFilter) have an option for alpha, both in the IDE’s interface and in the ActionScript object. But this information is not contained in the JSFL information. Go figure. So, in all cases where an alpha parameter is expected in the ActionScript constructor, I supply a value of 1.
  2. I trust it’s possible to work out the logic to convert the Adjust Color filter in the IDE to a ColorMatrixFilter in ActionScript, but that requires conversion of “saturation” and “contrast” settings in the IDE to an Array of numbers in ActionScript. I hope to eventually do this, but for now, I just wanted to post this script.

I also hope to reconsider how to abstract some of this functionality into some JSFL classes. Someday…

So, feel free to use, and I hope I just saved you a few minutes.

About these ads