Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 123 additions & 38 deletions exporter/src/main/as/flump/SwfTexture.as
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ package flump {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.display.StageQuality;
import flash.filters.ColorMatrixFilter;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flump.xfl.XflMovie;

import flump.executor.load.LoadedSwf;
import flump.mold.MovieMold;
Expand All @@ -19,11 +23,11 @@ import flump.xfl.XflTexture;

public class SwfTexture
{
public var symbol :String;
public var origin :Point;
public var w :int, h :int, a :int;
public var scale :Number;
public var quality :String;
public function get symbol():String { return _symbol; }
public function get origin():Point { return new Point(_origin.x*_scale, _origin.y*_scale); }
public function get w():int { return Math.ceil(_w*_scale); }
public function get h():int { return Math.ceil(_h*_scale); }
public function get a():int { return this.w*this.h; }

public static function fromFlipbook (lib :XflLibrary, movie :MovieMold, frame :int,
quality :String = StageQuality.BEST, scale :Number = 1) :SwfTexture {
Expand All @@ -42,50 +46,131 @@ public class SwfTexture
const instance :Object = new klass();
const disp :DisplayObject = (instance is BitmapData) ?
new Bitmap(BitmapData(instance)) : DisplayObject(instance);
disp.filters = disp.filters.concat(tex.filters);
return new SwfTexture(tex.symbol, disp, scale, quality);
}

public function SwfTexture (symbol :String, disp :DisplayObject, scale :Number, quality :String) {
this.symbol = symbol;
this.scale = scale;
this.quality = quality;
_disp = disp;

origin = getOrigin(_disp, scale);

const size :Point = getSize(_disp, scale);
w = size.x;
h = size.y;
a = w * h;
_symbol = symbol;
_quality = quality;

// wrap object twice for convenience
var wrapper:Sprite = new Sprite();
wrapper.addChild(disp);
_disp = new Sprite();
_disp.addChild(wrapper);

// set the scale and size info
_treatAsFiltered = hasPotentiallySizeAlteringFilters(disp);
setScale(scale);
}

// scale can be changed after creation, if desired
public function setScale(value:Number):void
{
// To compensate for the fact that filters don't "scale", do the following
// if filtered:
// render at scale 1
// then scale bitmapData to target size
// if not filtered:
// set the scale in the wrapper
// render directly at target size with vector renderer
if (_treatAsFiltered) {
// cache the scale to use later
_scale = value;
// only need to calculate size once, since _disp is not changing
if (_visualBounds == null) {
recalculateSizeInfo();
}
} else {
// embed the scale in _disp
var wrapper:DisplayObject = _disp.getChildAt(0);
wrapper.scaleX = wrapper.scaleY = value;
_scale = 1;
// recalculate size since _disp has changed
recalculateSizeInfo();
}
}

public function toBitmapData (borderPadding :int = 0) :BitmapData {
const bmd :BitmapData = Util.renderToBitmapData(_disp, w, h, quality, scale);
// render with vector renderer
var bmd :BitmapData = new BitmapData(Math.ceil(_w), Math.ceil(_h), true, 0x00);
var m :Matrix = new Matrix();
m.translate(_origin.x, _origin.y);
bmd.drawWithQuality(_disp, m, null, null, null, true, _quality);

// scale bitmap to target size if necessary (only used if _disp contains filters)
if (_scale != 1.0) {
bmd = Util.renderToBitmapData(bmd, this.w, this.h, _quality, _scale);
}

// add padding if necessary
return (borderPadding > 0 ? Util.padBitmapBorder(bmd, borderPadding) : bmd);
}

public function toString () :String { return "a " + a + " w " + w + " h " + h; }

protected static function getSize (disp :DisplayObject, scale :Number) :Point {
const bounds :Rectangle = getBounds(disp, scale);
return new Point(Math.ceil(bounds.width), Math.ceil(bounds.height));
}

protected static function getOrigin (disp :DisplayObject, scale :Number) :Point {
const bounds :Rectangle = getBounds(disp, scale);
return new Point(-bounds.x, -bounds.y);
public function toString () :String { return "a " + this.a + " w " + this.w + " h " + this.h; }

private function recalculateSizeInfo() :void {
// get normal bounds
_strictBounds = _disp.getChildAt(0).getBounds(_disp);
_visualBounds = _strictBounds;

// possibly increase the visual bounds (due to filter action)
if (_treatAsFiltered) {
// render to bmd
var topLeft:Point = new Point(_s_filteredBmd.width / 2 - _strictBounds.width / 2 - _strictBounds.x, _s_filteredBmd.height / 2 - _strictBounds.height / 2 - _strictBounds.y);
var m :Matrix = new Matrix(1,0,0,1, topLeft.x, topLeft.y);
_s_filteredBmd.drawWithQuality(_disp, m, null, null, null, true, _quality);

// calculate visual bounds
_visualBounds = _s_filteredBmd.getColorBoundsRect(0xff000000, 0x00000000, false);
_s_filteredBmd.fillRect(_visualBounds, 0x0);

// adjust registration point
_visualBounds.x = -(topLeft.x - _visualBounds.x);
_visualBounds.y = -(topLeft.y - _visualBounds.y);
}

// calculate derivative info
_origin = new Point(-_visualBounds.x, -_visualBounds.y);
_w = _visualBounds.width;
_h = _visualBounds.height;
}

protected static function getBounds (disp :DisplayObject, scale :Number) :Rectangle {
const oldScale :Number = disp.scaleX;
disp.scaleX = disp.scaleY = scale;
const holder :Sprite = new Sprite();
holder.addChild(disp);
const bounds :Rectangle = disp.getBounds(holder);
disp.scaleX = disp.scaleY = oldScale;
return bounds;

private function hasPotentiallySizeAlteringFilters(dObj:DisplayObject) :Boolean {
// check dObj's filter list
var filters:Array = dObj.filters;
for (var ff:int = 0, nf:int = filters.length; ff < nf; ++ff) {
// all standard filters except ColorMatrixFilter can change the visual bounds
if (!(filters[ff] is ColorMatrixFilter)) {
return true;
}
}
// recursively check children
var dObjContainer:flash.display.DisplayObjectContainer = dObj as flash.display.DisplayObjectContainer;
if (dObjContainer) {
for (var cc:int = 0, nc:int = dObjContainer.numChildren; cc < nc; ++cc) {
var child:DisplayObject = dObjContainer.getChildAt(cc);
if (hasPotentiallySizeAlteringFilters(child)) {
return true;
}
}
}
// all clear
return false;
}

protected var _disp :DisplayObject;

private var _symbol :String;
private var _disp :DisplayObjectContainer;
private var _w :int, _h :int;
private var _origin :Point;
private var _strictBounds :Rectangle;
private var _visualBounds :Rectangle;
private var _scale :Number;
private var _quality :String;
private var _treatAsFiltered:Boolean;

static private var _s_filteredBmd:BitmapData = new BitmapData(2048, 2048, true, 0x0);
{ _s_filteredBmd.lock(); }
}
}
111 changes: 111 additions & 0 deletions exporter/src/main/as/flump/xfl/XflKeyframe.as
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@

package flump.xfl {

import fl.motion.AdjustColor;
import flash.filters.BevelFilter;
import flash.filters.BitmapFilter;
import flash.filters.BlurFilter;
import flash.filters.ColorMatrixFilter;
import flash.filters.DropShadowFilter;
import flash.filters.GlowFilter;
import flash.geom.Matrix;
import flash.utils.Dictionary;

import flump.mold.KeyframeMold;

Expand Down Expand Up @@ -106,7 +114,110 @@ public class XflKeyframe
kf.alpha = XmlUtil.getNumberAttr(colorXml, "alphaMultiplier", 1);
}
}

parseFilters(lib, location, kf, symbolXml);

return kf;
}

// Parse filters for this symbol+keyframe, store them in a static lookup table
protected static function parseFilters (lib :XflLibrary, location :String, kf :KeyframeMold, symbolXml :XML) :void {
// if filter list is empty, early out
if (symbolXml.filters == null) {
return;
}

// initialize lookup table entry
_s_filtersByKeyframe[kf] = [];

// for each filter nodes, parse the xml and add a BitmapFilter to the lookup table
var filter:BitmapFilter;
for each (var filterXml:XML in symbolXml.filters.elements()) {
// parse different filter types into their native flash.filter types
if (filterXml.name().localName == "AdjustColorFilter") {
// <AdjustColorFilter brightness="-100" hue="-32"/>
var colorFilter:AdjustColor = new AdjustColor();
colorFilter.hue = XmlUtil.getNumberAttr(filterXml, "hue", 0);
colorFilter.saturation = XmlUtil.getNumberAttr(filterXml, "saturation", 0);
colorFilter.brightness = XmlUtil.getNumberAttr(filterXml, "brightness", 0);
colorFilter.contrast = XmlUtil.getNumberAttr(filterXml, "contrast", 0);
var mMatrix:Array = colorFilter.CalculateFinalFlatArray();
filter = new ColorMatrixFilter(mMatrix);
_s_filtersByKeyframe[kf].push(filter);
} else if (filterXml.name().localName == "BlurFilter") {
// <BlurFilter blurX="4" blurY="4" quality="2"/>
filter = new BlurFilter(
XmlUtil.getNumberAttr(filterXml, "blurX", 5),
XmlUtil.getNumberAttr(filterXml, "blurY", 5),
XmlUtil.getNumberAttr(filterXml, "quality", 1)
);
_s_filtersByKeyframe[kf].push(filter);
} else if (filterXml.name().localName == "BevelFilter") {
// <BevelFilter blurX="11" blurY="11" quality="2" angle="80.0000022767297" distance="-7"
// highlightColor="#33FF00" shadowColor="#CC00FF" strength="1.28"/>
filter = new BevelFilter(
XmlUtil.getNumberAttr(filterXml, "distance", 5.0),
XmlUtil.getNumberAttr(filterXml, "angle", 45),
parseInt(XmlUtil.getStringAttr(filterXml, "highlightColor", "#ffffff").substr(1), 16),
XmlUtil.getNumberAttr(filterXml, "highlightAlpha", 1.0),
parseInt(XmlUtil.getStringAttr(filterXml, "shadowColor", "#000000").substr(1), 16),
XmlUtil.getNumberAttr(filterXml, "shadowAlpha", 1.0),
XmlUtil.getNumberAttr(filterXml, "blurX", 5),
XmlUtil.getNumberAttr(filterXml, "blurY", 5),
XmlUtil.getNumberAttr(filterXml, "strength", 1),
XmlUtil.getNumberAttr(filterXml, "quality", 1),
XmlUtil.getStringAttr(filterXml, "type", "inner"),
XmlUtil.getBooleanAttr(filterXml, "knockout", false)
);
_s_filtersByKeyframe[kf].push(filter);
} else if (filterXml.name().localName == "DropShadowFilter") {
// <DropShadowFilter angle="15.9999994308176" blurX="12" blurY="12" color="#9933CC"
// distance="18" hideObject="true" inner="true" knockout="true" quality="3" strength="0.77"/>
filter = new DropShadowFilter(
XmlUtil.getNumberAttr(filterXml, "distance", 5.0),
XmlUtil.getNumberAttr(filterXml, "angle", 45),
parseInt(XmlUtil.getStringAttr(filterXml, "color", "#000000").substr(1), 16),
XmlUtil.getNumberAttr(filterXml, "alpha", 1.0),
XmlUtil.getNumberAttr(filterXml, "blurX", 5),
XmlUtil.getNumberAttr(filterXml, "blurY", 5),
XmlUtil.getNumberAttr(filterXml, "strength", 1),
XmlUtil.getNumberAttr(filterXml, "quality", 1),
XmlUtil.getBooleanAttr(filterXml, "inner", false),
XmlUtil.getBooleanAttr(filterXml, "knockout", false),
XmlUtil.getBooleanAttr(filterXml, "hideObject", false)
);
_s_filtersByKeyframe[kf].push(filter);
} else if (filterXml.name().localName == "GlowFilter") {
// <GlowFilter blurX="6" blurY="6" color="#00CC66" inner="true" knockout="true" quality="2" strength="0.9"/>
filter = new GlowFilter(
parseInt(XmlUtil.getStringAttr(filterXml, "color", "#ff0000").substr(1), 16),
XmlUtil.getNumberAttr(filterXml, "alpha", 1),
XmlUtil.getNumberAttr(filterXml, "blurX", 5),
XmlUtil.getNumberAttr(filterXml, "blurY", 5),
XmlUtil.getNumberAttr(filterXml, "strength", 1),
XmlUtil.getNumberAttr(filterXml, "quality", 1),
XmlUtil.getBooleanAttr(filterXml, "inner", false),
XmlUtil.getBooleanAttr(filterXml, "knockout", false)
);
_s_filtersByKeyframe[kf].push(filter);
} else {
// parsing for this filter type is unimplemented
lib.addError(location, ParseError.WARN, "Unimplemented parsing for filter type: '" + filterXml.name().localName + "'");
}

// if we only parsed unsupported filter types, remove the lookup table entry
if (_s_filtersByKeyframe[kf].length == 0) {
delete _s_filtersByKeyframe[kf];
}
}
}

// lookup table for filters associated with a given KeyframeMold
static private var _s_filtersByKeyframe:Dictionary = new Dictionary(true);
static public function getFiltersForKeyframe(kf:KeyframeMold):Array
{
return (kf in _s_filtersByKeyframe) ? _s_filtersByKeyframe[kf] : null;
}

}
}
Loading