/*
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.flex.compiler.fxg.swf;
import java.awt.geom.Ellipse2D;
import java.awt.geom.PathIterator;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import org.apache.flex.compiler.definitions.ITypeDefinition;
import org.apache.flex.compiler.fxg.FXGConstants;
import org.apache.flex.compiler.fxg.IFXGTranscoder;
import org.apache.flex.compiler.fxg.dom.IFXGNode;
import org.apache.flex.compiler.fxg.flex.FXGSymbolClass;
import org.apache.flex.compiler.fxg.resources.IFXGResourceResolver;
import org.apache.flex.compiler.internal.fxg.dom.AbstractShapeNode;
import org.apache.flex.compiler.internal.fxg.dom.BitmapGraphicNode;
import org.apache.flex.compiler.internal.fxg.dom.DefinitionNode;
import org.apache.flex.compiler.internal.fxg.dom.EllipseNode;
import org.apache.flex.compiler.internal.fxg.dom.IFillNode;
import org.apache.flex.compiler.internal.fxg.dom.IFilterNode;
import org.apache.flex.compiler.internal.fxg.dom.GradientEntryNode;
import org.apache.flex.compiler.internal.fxg.dom.GraphicContentNode;
import org.apache.flex.compiler.internal.fxg.dom.GraphicContext;
import org.apache.flex.compiler.internal.fxg.dom.GraphicNode;
import org.apache.flex.compiler.internal.fxg.dom.GroupDefinitionNode;
import org.apache.flex.compiler.internal.fxg.dom.GroupNode;
import org.apache.flex.compiler.internal.fxg.dom.LineNode;
import org.apache.flex.compiler.internal.fxg.dom.IMaskableNode;
import org.apache.flex.compiler.internal.fxg.dom.IMaskingNode;
import org.apache.flex.compiler.internal.fxg.dom.PathNode;
import org.apache.flex.compiler.internal.fxg.dom.PlaceObjectNode;
import org.apache.flex.compiler.internal.fxg.dom.RectNode;
import org.apache.flex.compiler.internal.fxg.dom.RichTextNode;
import org.apache.flex.compiler.internal.fxg.dom.IStrokeNode;
import org.apache.flex.compiler.internal.fxg.dom.TextGraphicNode;
import org.apache.flex.compiler.internal.fxg.dom.fills.BitmapFillNode;
import org.apache.flex.compiler.internal.fxg.dom.fills.LinearGradientFillNode;
import org.apache.flex.compiler.internal.fxg.dom.fills.RadialGradientFillNode;
import org.apache.flex.compiler.internal.fxg.dom.fills.SolidColorFillNode;
import org.apache.flex.compiler.internal.fxg.dom.filters.BevelFilterNode;
import org.apache.flex.compiler.internal.fxg.dom.filters.BlurFilterNode;
import org.apache.flex.compiler.internal.fxg.dom.filters.ColorMatrixFilterNode;
import org.apache.flex.compiler.internal.fxg.dom.filters.DropShadowFilterNode;
import org.apache.flex.compiler.internal.fxg.dom.filters.GlowFilterNode;
import org.apache.flex.compiler.internal.fxg.dom.filters.GradientBevelFilterNode;
import org.apache.flex.compiler.internal.fxg.dom.filters.GradientGlowFilterNode;
import org.apache.flex.compiler.internal.fxg.dom.strokes.AbstractStrokeNode;
import org.apache.flex.compiler.internal.fxg.dom.strokes.LinearGradientStrokeNode;
import org.apache.flex.compiler.internal.fxg.dom.strokes.RadialGradientStrokeNode;
import org.apache.flex.compiler.internal.fxg.dom.strokes.SolidColorStrokeNode;
import org.apache.flex.compiler.internal.fxg.dom.transforms.ColorTransformNode;
import org.apache.flex.compiler.internal.fxg.dom.transforms.MatrixNode;
import org.apache.flex.compiler.internal.fxg.dom.types.BevelType;
import org.apache.flex.compiler.internal.fxg.dom.types.BlendMode;
import org.apache.flex.compiler.internal.fxg.dom.types.Caps;
import org.apache.flex.compiler.internal.fxg.dom.types.InterpolationMethod;
import org.apache.flex.compiler.internal.fxg.dom.types.Joints;
import org.apache.flex.compiler.internal.fxg.dom.types.MaskType;
import org.apache.flex.compiler.internal.fxg.dom.types.ScaleMode;
import org.apache.flex.compiler.internal.fxg.dom.types.ScalingGrid;
import org.apache.flex.compiler.internal.fxg.dom.types.SpreadMethod;
import org.apache.flex.compiler.internal.fxg.dom.types.Winding;
import org.apache.flex.compiler.internal.fxg.swf.DefineImage;
import org.apache.flex.compiler.internal.fxg.swf.ImageHelper;
import org.apache.flex.compiler.internal.fxg.swf.ShapeHelper;
import org.apache.flex.compiler.internal.fxg.swf.TypeHelper;
import org.apache.flex.compiler.internal.fxg.types.FXGMatrix;
import org.apache.flex.compiler.problems.FXGErrorEmbeddingImageProblem;
import org.apache.flex.compiler.problems.FXGMissingAttributeProblem;
import org.apache.flex.compiler.problems.FXGMissingGroupChildNodeProblem;
import org.apache.flex.compiler.problems.ICompilerProblem;
import org.apache.flex.swf.ISWFConstants;
import org.apache.flex.swf.builders.IShapeIterator;
import org.apache.flex.swf.builders.ShapeBuilder;
import org.apache.flex.swf.tags.DefineScalingGridTag;
import org.apache.flex.swf.tags.DefineShape4Tag;
import org.apache.flex.swf.tags.DefineShapeTag;
import org.apache.flex.swf.tags.DefineSpriteTag;
import org.apache.flex.swf.tags.ICharacterTag;
import org.apache.flex.swf.tags.ITag;
import org.apache.flex.swf.tags.PlaceObject3Tag;
import org.apache.flex.swf.types.BevelFilter;
import org.apache.flex.swf.types.BlurFilter;
import org.apache.flex.swf.types.CXFormWithAlpha;
import org.apache.flex.swf.types.DropShadowFilter;
import org.apache.flex.swf.types.FillStyle;
import org.apache.flex.swf.types.FillStyleArray;
import org.apache.flex.swf.types.Filter;
import org.apache.flex.swf.types.FocalGradient;
import org.apache.flex.swf.types.GlowFilter;
import org.apache.flex.swf.types.GradRecord;
import org.apache.flex.swf.types.Gradient;
import org.apache.flex.swf.types.GradientBevelFilter;
import org.apache.flex.swf.types.GradientGlowFilter;
import org.apache.flex.swf.types.LineStyle;
import org.apache.flex.swf.types.LineStyle2;
import org.apache.flex.swf.types.LineStyleArray;
import org.apache.flex.swf.types.Matrix;
import org.apache.flex.swf.types.RGB;
import org.apache.flex.swf.types.RGBA;
import org.apache.flex.swf.types.Rect;
import org.apache.flex.swf.types.Shape;
import org.apache.flex.swf.types.ShapeRecord;
import org.apache.flex.swf.types.ShapeWithStyle;
import org.apache.flex.swf.types.Styles;
/**
* Transcodes an FXG DOM into a tree of SWF DefineSpriteTags which use SWF graphics
* primitives to draw the document.
* Note that in this implementation, since FTE based text
* has no equivalent in SWF tags, text nodes are ignored.
*/
public class FXG2SWFTranscoder implements IFXGTranscoder
{
protected FXGSymbolClass graphicClass;
protected HashMap<String, DefineSpriteTag> definitions;
protected Stack<DefineSpriteTag> spriteStack;
protected Map<DefineSpriteTag, Integer> depthMap;
protected IFXGResourceResolver resourceResolver;
protected Map<ITag, ITag> extraTags;
protected Map<String, DefineImage> imageMap;
protected Collection<ICompilerProblem> problems;
public FXG2SWFTranscoder newInstance()
{
FXG2SWFTranscoder transcoder = new FXG2SWFTranscoder();;
transcoder.extraTags = extraTags;
transcoder.imageMap = imageMap;
transcoder.depthMap = depthMap;
return transcoder;
}
@Override
public void setResourceResolver(IFXGResourceResolver resolver)
{
resourceResolver = resolver;
}
protected int getSpriteDepth(DefineSpriteTag sprite)
{
if(depthMap.containsKey(sprite))
return depthMap.get(sprite);
else
depthMap.put(sprite, 0);
return 0;
}
private void setSpriteDepth(DefineSpriteTag sprite, Integer depth)
{
depthMap.put(sprite, depth);
}
@Override
public FXGSymbolClass transcode(IFXGNode fxgNode, String packageName, String className, Map<ITag, ITag> extraTags, Collection<ICompilerProblem> problems)
{
this.problems = problems;
this.extraTags = extraTags;
graphicClass = new FXGSymbolClass();
graphicClass.setPackageName(packageName);
graphicClass.setClassName(className);
GraphicNode node = (GraphicNode)fxgNode;
DefineSpriteTag sprite = createDefineSpriteTag("Graphic");
spriteStack.push(sprite);
// Process mask (if present)
if (node.mask != null)
mask(node, sprite);
// Handle 'scale 9' grid definition
if (node.definesScaleGrid())
{
DefineScalingGridTag grid = createDefineScalingGridTag(node.getScalingGrid());
grid.setCharacter(sprite);
extraTags.put(sprite, grid);
//sprite.scalingGrid = grid;
}
// Process child nodes
if (node.children != null)
graphicContentNodes(node.children);
spriteStack.pop();
graphicClass.setSymbol(sprite);
return graphicClass;
}
public FXG2SWFTranscoder()
{
spriteStack = new Stack<DefineSpriteTag>();
depthMap = new HashMap<DefineSpriteTag, Integer>();
imageMap = new HashMap<String, DefineImage>();
}
private PlaceObject3Tag bitmapWithClip(DefineImage defImage, BitmapGraphicNode node)
{
GraphicContext context = node.createGraphicContext();
//process the filters later to avoid masking
List<IFilterNode> filters = null;
if (context.filters != null)
{
filters = context.filters;
context.filters = null;
DefineSpriteTag filterSprite = createDefineSpriteTag("MaskFilter");
spriteStack.push(filterSprite);
}
DefineSpriteTag imageSprite = createDefineSpriteTag("BitmapGraphic");
spriteStack.push(imageSprite);
// First, generate the clipping mask
DefineSpriteTag clipSprite = createDefineSpriteTag("BitmapGraphic_Clip");
spriteStack.push(clipSprite);
double width = (defImage.getWidth() < node.width) ? defImage.getWidth() : node.width;
double height = (defImage.getHeight() < node.height) ? defImage.getHeight() : node.height;
List<ShapeRecord> shapeRecords = ShapeHelper.rectangle(0.0, 0.0, width, height);
DefineShapeTag clipShape = createDefineShapeTag(null, shapeRecords, new SolidColorFillNode(), null, context.getTransform());
PlaceObject3Tag(clipShape, new GraphicContext());
spriteStack.pop();
//place the clipping mask in the imageSprite
PlaceObject3Tag po3clip = PlaceObject3Tag(clipSprite, context);
po3clip.setClipDepth(po3clip.getDepth()+1);
po3clip.setHasClipDepth(true);
// Then, process the image
DefineShapeTag imageShape = ImageHelper.createShapeForImage(defImage, node);
PlaceObject3Tag(imageShape, context);
spriteStack.pop();
PlaceObject3Tag po3 = PlaceObject3Tag(imageSprite, new GraphicContext());
// If filters were not processed, place the topmost sprite in display list and apply filters
// This is done to force processing of masks before filters
if (filters != null)
{
DefineSpriteTag sprite = spriteStack.pop();
GraphicContext gc = new GraphicContext();
gc.filters = filters;
PlaceObject3Tag poFilter = PlaceObject3Tag(sprite, gc);
return poFilter;
}
return po3;
}
// --------------------------------------------------------------------------
//
// Graphic Content Nodes
//
// --------------------------------------------------------------------------
protected PlaceObject3Tag bitmap(BitmapGraphicNode node)
{
GraphicContext context = node.createGraphicContext();
String source = parseSource(node.source);
if (source == null)
{
// Missing source attribute in <BitmapGraphic> or <BitmapFill>.
problems.add(new FXGMissingAttributeProblem(node.getDocumentPath(), node.getStartLine(),
node.getStartColumn(), FXGConstants.FXG_SOURCE_ATTRIBUTE, node.getNodeName()));
return null;
}
DefineImage imageTag = createDefineBitsTag(node, source);
if(imageTag == null)
return null;
if ((node.visible) && (!node.isPartofClipMask))
{
DefineShapeTag imageShape;
ScalingGrid scalingGrid = context.scalingGrid;
if (scalingGrid != null)
{
Rect grid = TypeHelper.rect(scalingGrid.scaleGridLeft, scalingGrid.scaleGridTop, scalingGrid.scaleGridRight, scalingGrid.scaleGridBottom);
imageShape = ImageHelper.create9SlicedShape(imageTag, grid, Double.NaN, Double.NaN);
PlaceObject3Tag po3 = PlaceObject3Tag(imageShape, context);
return po3;
}
else
{
if (ImageHelper.bitmapImageNeedsClipping(imageTag, node))
{
PlaceObject3Tag p03 = bitmapWithClip(imageTag, node);
return p03;
}
else
{
imageShape = ImageHelper.createShapeForImage(imageTag, node);
PlaceObject3Tag po3 = PlaceObject3Tag(imageShape, context);
return po3;
}
}
}
else
{
if (!ImageHelper.bitmapImageNeedsClipping(imageTag, node))
{
double width = (Double.isNaN(node.width)) ? imageTag.getWidth() : node.width;
double height = (Double.isNaN(node.height)) ? imageTag.getHeight() : node.height;
List<ShapeRecord> shapeRecords = ShapeHelper.rectangle(0.0, 0.0, width, height);
DefineShapeTag shape = createDefineShapeTag(null, shapeRecords, new SolidColorFillNode(), null, context.getTransform());
PlaceObject3Tag po3 = PlaceObject3Tag(shape, context);
return po3;
}
else
{
double width = ((imageTag.getWidth() < node.width) || Double.isNaN(node.width)) ? imageTag.getWidth() : node.width;
double height = ((imageTag.getHeight() < node.height) || (Double.isNaN(node.height))) ? imageTag.getHeight() : node.height;
List<ShapeRecord> shapeRecords = ShapeHelper.rectangle(0.0, 0.0, width, height);
DefineShapeTag shape = createDefineShapeTag(null, shapeRecords, new SolidColorFillNode(), null, context.getTransform());
PlaceObject3Tag po3 = PlaceObject3Tag(shape, context);
return po3;
}
}
}
protected void graphicContentNodes(List<GraphicContentNode> nodes)
{
if (nodes == null) return;
Iterator<GraphicContentNode> iterator = nodes.iterator();
while (iterator.hasNext())
{
GraphicContentNode node = iterator.next();
graphicContentNode(node);
}
}
protected PlaceObject3Tag graphicContentNode(GraphicContentNode node)
{
PlaceObject3Tag po3 = null;
if (!node.visible)
{
ColorTransformNode ct = new ColorTransformNode();
ct.alphaMultiplier = 0;
ct.alphaOffset = 0;
ct.blueMultiplier = 1;
ct.blueOffset = 0;
ct.greenMultiplier = 1;
ct.greenOffset = 0;
ct.redMultiplier = 1;
ct.redOffset = 0;
node.colorTransform = ct;
if (node instanceof AbstractShapeNode)
{
AbstractShapeNode shapeNode = (AbstractShapeNode)node;
shapeNode.fill = null;
shapeNode.stroke = null;
}
}
if (node instanceof GroupNode)
{
group((GroupNode)node);
}
else
{
if (node.blendMode == BlendMode.AUTO)
node.blendMode = BlendMode.NORMAL;
// For non-group nodes, we process mask to clip only this shape
// node. Process the mask first to ensure the depth is correct.
List<IFilterNode> filters = null;
if (node.mask != null)
{
// Remove the filters from context and process them later to force Flash Player to process the masks first
if (node.filters != null)
{
filters = node.filters;
node.filters = null;
DefineSpriteTag filterSprite = createDefineSpriteTag("MaskFilter");
spriteStack.push(filterSprite);
}
DefineSpriteTag parentSprite = spriteStack.peek();
mask(node, parentSprite);
}
if (node instanceof EllipseNode)
po3 = ellipse((EllipseNode)node);
else if (node instanceof LineNode)
po3 = line((LineNode)node);
else if (node instanceof PathNode)
po3 = path((PathNode)node);
else if (node instanceof RectNode)
po3 = rect((RectNode)node);
else if (node instanceof PlaceObjectNode)
po3 = PlaceObject3TagInstance((PlaceObjectNode)node);
else if (node instanceof BitmapGraphicNode)
po3 = bitmap((BitmapGraphicNode)node);
else if (node instanceof TextGraphicNode)
po3 = text((TextGraphicNode)node);
else if (node instanceof RichTextNode)
po3 = richtext((RichTextNode)node);
// If filters were not processed, place the topmost sprite in display list and apply filters
// This is done to force processing of masks before filters
if (filters != null)
{
DefineSpriteTag sprite = spriteStack.pop();
GraphicContext gc = new GraphicContext();
gc.filters = filters;
PlaceObject3Tag poFilter = PlaceObject3Tag(sprite, gc);
return poFilter;
}
}
return po3;
}
protected PlaceObject3Tag ellipse(EllipseNode node)
{
// Note that we will apply node.x and node.y as a translation operation
// in the PlaceObject3Tag3 Matrix and instead start the shape from the
// origin (0.0, 0.0).
Ellipse2D.Double ellipse = new Ellipse2D.Double(0.0, 0.0, node.width, node.height);
ShapeBuilder builder = new ShapeBuilder();
IShapeIterator iterator = new PathIteratorWrapper(ellipse.getPathIterator(null));
builder.processShape(iterator);
Shape shape = builder.build();
return placeDefineShapeTag(node, shape.getShapeRecords(), node.fill, node.stroke, node.createGraphicContext());
}
protected PlaceObject3Tag group(GroupNode node)
{
//handle blendMode "auto"
if (node.blendMode == BlendMode.AUTO)
{
if ((node.alpha == 0) || (node.alpha == 1))
node.blendMode = BlendMode.NORMAL;
else
node.blendMode = BlendMode.LAYER;
}
DefineSpriteTag groupSprite = createDefineSpriteTag("Group");
GraphicContext context = node.createGraphicContext();
// Handle 'scale 9' grid definition
if (node.definesScaleGrid())
{
DefineScalingGridTag grid = createDefineScalingGridTag(context.scalingGrid);
grid.setCharacter(groupSprite);
extraTags.put(groupSprite,grid);
//groupSprite.scalingGrid = grid;
}
PlaceObject3Tag po3 = PlaceObject3Tag(groupSprite, context);
spriteStack.push(groupSprite);
// First, process mask (if present)
List <IFilterNode> filters = null;
if (node.mask != null)
{
// Remove the filters from context and process them later to force Flash Player to process the masks first
filters = node.filters;
if (filters == null)
{
List<GraphicContentNode> children = node.children;
if (children != null)
{
GraphicContentNode gcNode0 = (GraphicContentNode) children.get(0);
filters = gcNode0.filters;
if (filters != null)
{
//check if all the nodes share the same filter
for (int i = 1; ((i < children.size()) && filters!= null); i++)
{
GraphicContentNode gcNodeI = (GraphicContentNode) children.get(i);
if (gcNodeI.filters != filters)
filters = null;
}
}
if (filters != null)
{
for (int i = 0; (i < children.size()) ; i++)
{
GraphicContentNode gcNodeI = (GraphicContentNode) children.get(i);
gcNodeI.filters = null;
}
}
}
}
else
{
node.filters = null;
}
if (filters != null)
{
DefineSpriteTag filterSprite = createDefineSpriteTag("MaskFilter");
spriteStack.push(filterSprite);
}
DefineSpriteTag sprite = spriteStack.peek();
mask(node, sprite);
}
// Then process child nodes.
if (node.children != null)
graphicContentNodes(node.children);
// If filters were not processed, place the topmost sprite in display list and apply filters
// This is done to force processing of masks before filters
if (filters != null)
{
DefineSpriteTag sprite = spriteStack.pop();
GraphicContext gc = new GraphicContext();
gc.filters = filters;
PlaceObject3Tag poFilter = PlaceObject3Tag(sprite, gc);
return poFilter;
}
spriteStack.pop();
return po3;
}
protected PlaceObject3Tag line(LineNode node)
{
List<ShapeRecord> shapeRecords = ShapeHelper.line(node.xFrom, node.yFrom, node.xTo, node.yTo);
GraphicContext context = node.createGraphicContext();
PlaceObject3Tag po3 = placeDefineShapeTag(node, shapeRecords, node.fill, node.stroke, context);
return po3;
}
protected PlaceObject3Tag mask(IMaskableNode node, DefineSpriteTag parentSprite)
{
PlaceObject3Tag po3 = null;
IMaskingNode mask = node.getMask();
if (mask instanceof GroupNode)
{
// According to FXG Spec.: The masking element inherits the target
// group's coordinate space, as though it were a direct child
// element. In the case when mask is inside a shape, it doesn't
// automatically inherit the coordinates from the shape node
// but inherits from its parent node which is also parent of
// the shape node. To fix it, specifically concatenating the
// shape node matrix to the masking node matrix.
if (!(node instanceof GroupNode || node instanceof GraphicNode))
{
FXGMatrix nodeMatrix = null;
MatrixNode matrixNodeShape = ((GraphicContentNode)node).matrix;
if (matrixNodeShape == null)
// Convert shape node's discreet transform attributes to
// matrix.
nodeMatrix = FXGMatrix.convertToMatrix(((GraphicContentNode)node).scaleX, ((GraphicContentNode)node).scaleY, ((GraphicContentNode)node).rotation, ((GraphicContentNode)node).x, ((GraphicContentNode)node).y);
else
nodeMatrix = new FXGMatrix(matrixNodeShape);
// Get masking node matrix.
MatrixNode matrixNodeMasking = ((GraphicContentNode)mask).matrix;
// Create a new MatrixNode if the masking node doesn't have one.
if (matrixNodeMasking == null)
{
// Convert masking node's transform attributes to matrix
// so we can concatenate the shape node's matrix to it.
((GraphicContentNode)mask).convertTransformAttrToMatrix(problems);
matrixNodeMasking = ((GraphicContentNode)mask).matrix;
}
FXGMatrix maskMatrix = new FXGMatrix(matrixNodeMasking);
// Concatenate the shape node's matrix to the masking node's
// matrix.
maskMatrix.concat(nodeMatrix);
// Set the masking node's matrix with the concatenated values.
maskMatrix.setMatrixNodeValue(matrixNodeMasking);
}
markLeafNodesAsMask(node, (GroupNode) mask);
po3 = group((GroupNode)mask);
}
else if (mask instanceof PlaceObjectNode)
{
po3 = PlaceObject3TagInstance((PlaceObjectNode)mask);
}
if (po3 != null)
{
int clipDepth = 1;
// If we had a graphic or group, clip the depths for all children.
if (node instanceof GroupNode)
{
GroupNode group = (GroupNode)node;
if (group.children != null)
clipDepth = getSpriteDepth(parentSprite) + group.children.size();
}
else if (node instanceof GraphicNode)
{
GraphicNode graphic = (GraphicNode)node;
if (graphic.children != null)
clipDepth = getSpriteDepth(parentSprite) + graphic.children.size();
}
// ... otherwise, just clip the shape itself.
else
{//TODO
clipDepth = po3.getDepth() + 1;
}
po3.setClipDepth(clipDepth);
po3.setHasClipDepth(true);
if (node.getMaskType() == MaskType.ALPHA)
{
po3.setHasCacheAsBitmap(true);
}
}
return po3;
}
protected PlaceObject3Tag path(PathNode node)
{
List<ShapeRecord> shapeRecords = ShapeHelper.path(node, (node.fill != null), problems);
GraphicContext context = node.createGraphicContext();
Winding winding[] = new Winding[1];
winding[0] = node.winding;
PlaceObject3Tag po3 = placeDefineShapeTag(node, shapeRecords, node.fill, node.stroke, context, winding);
return po3;
}
protected void setPixelBenderBlendMode(PlaceObject3Tag po, BlendMode blendMode)
{
}
protected void setAlphaMask(PlaceObject3Tag po)
{
po.setHasCacheAsBitmap(true);
}
protected void setLuminosityMask(PlaceObject3Tag po)
{
}
protected PlaceObject3Tag PlaceObject3Tag(ICharacterTag symbol, GraphicContext context)
{
DefineSpriteTag sprite = spriteStack.peek();
PlaceObject3Tag po3 = new PlaceObject3Tag();
// po3.setName(name);
po3.setCharacter(symbol);
assert symbol != sprite;
po3.setHasCharacter(true);
Integer depthCount = getSpriteDepth(sprite) + 1;
setSpriteDepth(sprite, depthCount);
po3.setDepth(depthCount);
if (context.blendMode != null)
{
if (!context.blendMode.needsPixelBenderSupport())
{
int blendMode = createBlendMode(context.blendMode);
po3.setBlendMode(blendMode);
po3.setHasBlendMode(true);
}
else
{
setPixelBenderBlendMode(po3, context.blendMode);
}
}
if (context.filters != null)
{
List<Filter> filters = createFilters(context.filters);
Filter filterArray[] = {};
po3.setSurfaceFilterList(filters.toArray(filterArray));
po3.setHasFilterList(true);
}
// FXG angles are always clockwise.
Matrix matrix = context.getTransform().toSWFMatrix();
po3.setMatrix(matrix);
po3.setHasMatrix(true);
if (context.colorTransform != null)
{
ColorTransformNode t = context.colorTransform;
CXFormWithAlpha cx = TypeHelper.cxFormWithAlpha(t.alphaMultiplier, t.redMultiplier, t.greenMultiplier, t.blueMultiplier, t.alphaOffset, t.redOffset, t.greenOffset, t.blueOffset);
po3.setColorTransform(cx);
po3.setHasColorTransform(true);
}
if (context.maskType == MaskType.ALPHA)
{
setAlphaMask(po3);
}
else if (context.maskType == MaskType.LUMINOSITY)
{
setLuminosityMask(po3);
}
sprite.getControlTags().add(po3);
return po3;
}
protected PlaceObject3Tag rect(RectNode node)
{
// Note that we will apply node.x and node.y as a translation operation
// in the PlaceObject3Tag3 Matrix and instead start the shape from the
// origin (0.0, 0.0).
GraphicContext context = node.createGraphicContext();
List<ShapeRecord> shapeRecords;
if (node.radiusX != 0.0 || node.radiusY != 0.0
|| !Double.isNaN(node.topLeftRadiusX) || !Double.isNaN(node.topLeftRadiusY)
|| !Double.isNaN(node.topRightRadiusX) || !Double.isNaN(node.topRightRadiusY)
|| !Double.isNaN(node.bottomLeftRadiusX) || !Double.isNaN(node.bottomLeftRadiusY)
|| !Double.isNaN(node.bottomRightRadiusX) || !Double.isNaN(node.bottomRightRadiusY))
{
shapeRecords = ShapeHelper.rectangle(0.0, 0.0, node.width, node.height,
node.radiusX, node.radiusY, node.topLeftRadiusX, node.topLeftRadiusY,
node.topRightRadiusX, node.topRightRadiusY, node.bottomLeftRadiusX, node.bottomLeftRadiusY,
node.bottomRightRadiusX, node.bottomRightRadiusY);
}
else
{
shapeRecords = ShapeHelper.rectangle(0.0, 0.0, node.width, node.height);
}
PlaceObject3Tag po3 = placeDefineShapeTag(node, shapeRecords, node.fill, node.stroke, context);
return po3;
}
protected PlaceObject3Tag text(TextGraphicNode node)
{
// No operation - text is ignored in this implementation.
return null;
}
protected PlaceObject3Tag richtext(RichTextNode node)
{
// No operation - richtext is ignored in this implementation.
return null;
}
// --------------------------------------------------------------------------
//
// FXG Library Definitions
//
// --------------------------------------------------------------------------
protected PlaceObject3Tag PlaceObject3TagInstance(PlaceObjectNode node)
{
String definitionName = node.getNodeName();
if (definitions == null)
definitions = new HashMap<String, DefineSpriteTag>();
DefineSpriteTag definitionSprite = definitions.get(definitionName);
if (definitionSprite == null)
{
definitionSprite = createDefineSpriteTag("Definition");
FXG2SWFTranscoder graphics = newInstance();
graphics.setResourceResolver(resourceResolver);
definitions.put(definitionName, definitionSprite);
graphics.definitions = definitions;
graphics.problems = problems;
graphics.definition(node.definition, definitionSprite);
}
PlaceObject3Tag po3 = PlaceObject3Tag(definitionSprite, node.createGraphicContext());
return po3;
}
protected void definition(DefinitionNode node, DefineSpriteTag definitionSprite)
{
GroupDefinitionNode groupDefinition = node.groupDefinition;
if (groupDefinition == null)
{
// Definitions must define a single Group child node.
problems.add(new FXGMissingGroupChildNodeProblem(node.getDocumentPath(), node.getStartLine(),
node.getStartColumn()));
return;
}
spriteStack.push(definitionSprite);
if (groupDefinition.definesScaleGrid())
{
DefineScalingGridTag scalingGrid = createDefineScalingGridTag(groupDefinition.getScalingGrid());
scalingGrid.setCharacter(definitionSprite);
extraTags.put(definitionSprite,scalingGrid);
//definitionSprite.scalingGrid = scalingGrid;
}
graphicContentNodes(groupDefinition.children);
spriteStack.pop();
}
// --------------------------------------------------------------------------
//
// SWF Tags and Types Helper Methods
//
// --------------------------------------------------------------------------
protected DefineImage createDefineBitsTag(IFXGNode node, String source)
{
DefineImage imageTag = imageMap.get(source);
if (imageTag == null)
{
try
{
InputStream stream = resourceResolver.openStream(source);
imageTag = ImageHelper.createDefineBits(stream, ImageHelper.guessMimeType(source));
imageMap.put(source, imageTag);
}
catch (IOException ioe)
{
// Error {0} occurred while embedding image {1}.
problems.add(new FXGErrorEmbeddingImageProblem(node.getDocumentPath(), node.getStartLine(),
node.getStartColumn(), ioe.getMessage(), source));
return null;
}
}
return imageTag;
}
protected DefineScalingGridTag createDefineScalingGridTag(ScalingGrid grid)
{
DefineScalingGridTag scalingGrid = new DefineScalingGridTag();
scalingGrid.setSplitter(TypeHelper.rect(grid.scaleGridLeft, grid.scaleGridTop, grid.scaleGridRight, grid.scaleGridBottom));
return scalingGrid;
}
protected DefineSpriteTag createDefineSpriteTag(String name)
{
DefineSpriteTag sprite = new DefineSpriteTag(0, new ArrayList<ITag>());
if (name == null)
name = "";
return sprite;
}
protected DefineShapeTag createDefineShapeTag(AbstractShapeNode node, List<ShapeRecord> shapeRecords, IFillNode fill,
IStrokeNode stroke, FXGMatrix transform, Winding... windings)
{
// Calculate the bounds of the shape outline (without strokes) - edgeBounds
Rect edgeBounds = (node == null) ? ShapeHelper.getBounds(shapeRecords, null, (AbstractStrokeNode)stroke) : node.getBounds(shapeRecords, null);
Rect shapeBounds;
int lineStyleIndex = stroke == null ? 0 : 1;
int fillStyle0Index = fill == null ? 0 : 1;
int fillStyle1Index = 0;
FillStyleArray fillStyles = new FillStyleArray(1);
LineStyleArray lineStyles = new LineStyleArray();
if (fill != null)
{
FillStyle fillStyle = createFillStyle(fill, edgeBounds);
if(fillStyle != null)
fillStyles.add(fillStyle);
}
if (stroke != null)
{
//find the shapeBounds with stroke
LineStyle ls = createGenericLineStyle((AbstractStrokeNode)stroke);
shapeBounds = (node == null) ? ShapeHelper.getBounds(shapeRecords, ls, (AbstractStrokeNode)stroke) : node.getBounds(shapeRecords, ls);
LineStyle lineStyle = createLineStyle(stroke, shapeBounds);
lineStyles.add(lineStyle);
}
else
{
shapeBounds = edgeBounds;
}
Styles styles = new Styles(fillStyles, lineStyles);
if (windings.length > 0)
ShapeHelper.setPathStyles(shapeRecords, lineStyleIndex, fillStyle0Index, fillStyle1Index, styles);
else
ShapeHelper.setStyles(shapeRecords, lineStyleIndex, fillStyle0Index, fillStyle1Index, styles);
ShapeWithStyle sws = new ShapeWithStyle(styles);
sws.addShapeRecords(shapeRecords);
DefineShape4Tag DefineShapeTag4 = new DefineShape4Tag();
DefineShapeTag4.setShapes(sws);
DefineShapeTag4.setShapeBounds(shapeBounds);
DefineShapeTag4.setEdgeBounds(edgeBounds);
if ((fill != null) &&( windings.length > 0))
{
Winding windingValue = windings[0];
DefineShapeTag4.setUsesFillWindingRule(windingValue == Winding.NON_ZERO);
}
return DefineShapeTag4;
}
protected PlaceObject3Tag placeDefineShapeTag(AbstractShapeNode node, List<ShapeRecord> shapeRecords,
IFillNode fill, IStrokeNode stroke, GraphicContext context, Winding... windings )
{
if (node != null && fill!= null && !node.isPartofClipMask && ImageHelper.isBitmapFillWithClip(fill))
{
/* Support of fillMode=clip/scale is complicated since SWF does not
* support proper clipping of bitmaps. For fillMode=clip/scale, FXG defines
* the area outside of the bitmap fill area to be transparent.
* In SWF, the bitmap bleeds to fill the rest of the path/shape
* if bitmap is specified to be a clipping bitmap.
*
* In order to get the effect that FXG wants with SWF tags, the
* the path/shape is split into two ShapeRecords for a path
* with a stroke & fill. A clipping mask is applied to the fill
* but not the stroke.
*/
BitmapFillNode fillNode = (BitmapFillNode) fill;
// Calculate the bounds of the shape outline (without strokes)
Rect edgeBounds = node.getBounds(shapeRecords, null);
String source = parseSource(fillNode.source);
if (source == null)
{
// Missing source attribute in <BitmapGraphic> or <BitmapFill>.
problems.add(new FXGMissingAttributeProblem(node.getDocumentPath(), node.getStartLine(),
node.getStartColumn(), FXGConstants.FXG_SOURCE_ATTRIBUTE, node.getNodeName()));
return null;
}
DefineImage defImage = createDefineBitsTag(fill, source);
if(defImage == null)
return null;
//process the filters later to avoid masking
List<IFilterNode> filters = null;
if (context.filters != null)
{
filters = context.filters;
context.filters = null;
DefineSpriteTag filterSprite = createDefineSpriteTag("MaskFilter");
spriteStack.push(filterSprite);
}
DefineSpriteTag imageSprite = createDefineSpriteTag("BitmapFill");
spriteStack.push(imageSprite);
// First, generate the clipping mask
DefineSpriteTag clipSprite = createDefineSpriteTag("BitmapFill_Clip");
spriteStack.push(clipSprite);
List<ShapeRecord> clipRectRecords = ShapeHelper.rectangle(0.0, 0.0, defImage.getWidth(), defImage.getHeight());
DefineShapeTag clipShape = createDefineShapeTag(null, clipRectRecords, new SolidColorFillNode(), null, context.getTransform());
FXGMatrix bitmapMatrix = TypeHelper.bitmapFillMatrix(fillNode, defImage, edgeBounds);
FXGMatrix clipMatrix = new FXGMatrix(bitmapMatrix.a, bitmapMatrix.b, bitmapMatrix.c, bitmapMatrix.d, 0, 0);
clipMatrix.scale(1.0/ISWFConstants.TWIPS_PER_PIXEL, 1.0/ISWFConstants.TWIPS_PER_PIXEL);
clipMatrix.translate(bitmapMatrix.tx, bitmapMatrix.ty);
GraphicContext clipContext = new GraphicContext();
clipContext.setTransform(clipMatrix);
PlaceObject3Tag(clipShape, clipContext);
spriteStack.pop();
// Set the depth of the mask to that of the bitmap image fill
clipContext.setTransform(context.getTransform());
PlaceObject3Tag po3clip = PlaceObject3Tag(clipSprite, clipContext);
po3clip.setClipDepth(po3clip.getDepth() + 1);
po3clip.setHasClipDepth(true);
Styles styles = new Styles(new FillStyleArray(), new LineStyleArray());
// Then, process the bitmap image fill
ShapeWithStyle sws = new ShapeWithStyle(styles);
int lineStyleIndex = 0;
int fillStyle0Index = 1;
int fillStyle1Index = 0;
FillStyle fillStyle = createFillStyle(fill, edgeBounds);
if(fillStyle != null)
sws.getFillStyles().add(fillStyle);
if (windings.length > 0)
ShapeHelper.setPathStyles(shapeRecords, lineStyleIndex, fillStyle0Index, fillStyle1Index, styles);
else
ShapeHelper.setStyles(shapeRecords, lineStyleIndex, fillStyle0Index, fillStyle1Index, styles);
sws.addShapeRecords(shapeRecords);
DefineShape4Tag imageShape = new DefineShape4Tag();
imageShape.setShapes(sws);
imageShape.setShapeBounds(edgeBounds);
imageShape.setEdgeBounds(edgeBounds);
if ((fill != null) &&( windings.length > 0))
{
Winding windingValue = windings[0];
imageShape.setUsesFillWindingRule(windingValue == Winding.NON_ZERO);
}
PlaceObject3Tag po3 = PlaceObject3Tag(imageShape, context);
if (stroke != null)
{
//make a copy of ShapeRecord for strokes
ArrayList<ShapeRecord> shapeRecords2 = new ArrayList<ShapeRecord>(shapeRecords);
Collections.copy(shapeRecords2, shapeRecords);
Styles strokeStyles = new Styles(new FillStyleArray(), new LineStyleArray());
//generate the define sprite for the stroke object with no clipping
ShapeWithStyle swsStroke = new ShapeWithStyle(strokeStyles);
// Consider linestyle stroke widths with bounds calculation
AbstractStrokeNode strokeNode = (AbstractStrokeNode) stroke;
LineStyle ls = createGenericLineStyle(strokeNode);
Rect shapeBounds = node.getBounds(shapeRecords2, ls);
LineStyle lineStyle = createLineStyle(stroke, shapeBounds);
swsStroke.getLineStyles().add(lineStyle);
lineStyleIndex = 1;
fillStyle0Index = 0;
fillStyle1Index = 0;
ShapeHelper.replaceStyles(shapeRecords2, lineStyleIndex, fillStyle0Index, fillStyle1Index, strokeStyles);
swsStroke.addShapeRecords(shapeRecords2);
DefineShape4Tag strokeShape = new DefineShape4Tag();
strokeShape.setShapes(swsStroke);
strokeShape.setShapeBounds(shapeBounds);
strokeShape.setEdgeBounds(edgeBounds);
po3 = PlaceObject3Tag(strokeShape, context);
}
spriteStack.pop();
po3 = PlaceObject3Tag(imageSprite, new GraphicContext());
// If filters were not processed, place the topmost sprite in display list and apply filters
// This is done to force processing of masks before filters
if (filters != null)
{
DefineSpriteTag sprite = spriteStack.pop();
GraphicContext gc = new GraphicContext();
gc.filters = filters;
PlaceObject3Tag poFilter = PlaceObject3Tag(sprite, gc);
return poFilter;
}
return po3;
}
else
{
DefineShapeTag shape = createDefineShapeTag(node, shapeRecords, fill, stroke, context.getTransform(), windings);
PlaceObject3Tag po3 = PlaceObject3Tag(shape, context);
return po3;
}
}
protected FillStyle createFillStyle(IFillNode fill, Rect bounds)
{
if (fill instanceof SolidColorFillNode)
return createFillStyle((SolidColorFillNode)fill);
else if (fill instanceof LinearGradientFillNode)
return createFillStyle((LinearGradientFillNode)fill, bounds);
else if (fill instanceof RadialGradientFillNode)
return createFillStyle((RadialGradientFillNode)fill, bounds);
else if (fill instanceof BitmapFillNode)
return createFillStyle((BitmapFillNode)fill, bounds);
else
return null;
}
protected FillStyle createFillStyle(SolidColorFillNode fill)
{
FillStyle fs = new FillStyle();
fs.setColor(TypeHelper.splitColor(TypeHelper.colorARGB(fill.color, fill.alpha)));
fs.setFillStyleType(FillStyle.SOLID_FILL);
return fs;
}
protected FillStyle createFillStyle(BitmapFillNode fill, Rect bounds)
{
FillStyle fs = new FillStyle();
if (ImageHelper.bitmapFillModeIsRepeat(fill))
fs.setFillStyleType(FillStyle.REPEATING_BITMAP_FILL);
else
fs.setFillStyleType(FillStyle.CLIPPED_BITMAP_FILL);
String sourceFormatted = parseSource(fill.source);
if (sourceFormatted == null)
{
// Source is required after FXG 1.0
// Missing source attribute in <BitmapGraphic> or <BitmapFill>.
problems.add(new FXGMissingAttributeProblem(fill.getDocumentPath(), fill.getStartLine(),
fill.getStartColumn(), FXGConstants.FXG_SOURCE_ATTRIBUTE, fill.getNodeName()));
return null;
}
DefineImage img = createDefineBitsTag(fill, sourceFormatted);
if(img != null)
{
fs.setBitmapCharacter(img.getTag());
fs.setBitmapMatrix(TypeHelper.bitmapFillMatrix(fill, img, bounds).toSWFMatrix());
}
return fs;
}
protected FillStyle createFillStyle(LinearGradientFillNode node, Rect bounds)
{
FillStyle fs = new FillStyle();
fs.setFillStyleType(FillStyle.LINEAR_GRADIENT_FILL);
fs.setGradientMatrix(TypeHelper.linearGradientMatrix(node, bounds));
Gradient gradient = new Gradient();
populateGradient(gradient, node.entries, node.interpolationMethod, node.spreadMethod);
fs.setGradient(gradient);
return fs;
}
protected FillStyle createFillStyle(LinearGradientStrokeNode node, Rect bounds)
{
FillStyle fs = new FillStyle();
fs.setFillStyleType(FillStyle.LINEAR_GRADIENT_FILL);
fs.setGradientMatrix(TypeHelper.linearGradientMatrix(node, bounds));
Gradient gradient = new Gradient();
populateGradient(gradient, node.entries, node.interpolationMethod, node.spreadMethod);
fs.setGradient(gradient);
return fs;
}
protected FillStyle createFillStyle(RadialGradientFillNode node, Rect bounds)
{
FillStyle fs = new FillStyle();
fs.setFillStyleType(FillStyle.FOCAL_RADIAL_GRADIENT_FILL);
fs.setGradientMatrix(TypeHelper.radialGradientMatrix(node, bounds));
FocalGradient gradient = new FocalGradient();
populateGradient(gradient, node.entries, node.interpolationMethod, node.spreadMethod);
gradient.setFocalPoint((float)node.focalPointRatio);
fs.setGradient(gradient);
return fs;
}
protected FillStyle createFillStyle(RadialGradientStrokeNode node, Rect bounds)
{
FillStyle fs = new FillStyle();
fs.setFillStyleType(FillStyle.FOCAL_RADIAL_GRADIENT_FILL);
fs.setGradientMatrix(TypeHelper.radialGradientMatrix(node, bounds));
FocalGradient gradient = new FocalGradient();
populateGradient(gradient, node.entries, node.interpolationMethod, node.spreadMethod);
gradient.setFocalPoint((float)node.focalPointRatio);
fs.setGradient(gradient);
return fs;
}
protected LineStyle createLineStyle(IStrokeNode stroke, Rect bounds)
{
if (stroke instanceof SolidColorStrokeNode)
return createLineStyle((SolidColorStrokeNode)stroke);
else if (stroke instanceof LinearGradientStrokeNode)
return createLineStyle((LinearGradientStrokeNode)stroke, bounds);
else if (stroke instanceof RadialGradientStrokeNode)
return createLineStyle((RadialGradientStrokeNode)stroke, bounds);
else
return null;
}
private LineStyle2 createGenericLineStyle(AbstractStrokeNode stroke)
{
LineStyle2 ls = new LineStyle2();
ls.setWidth((int)StrictMath.rint(stroke.getWeight() * ISWFConstants.TWIPS_PER_PIXEL));
int startCapStyle = createCaps(stroke.caps);
int endCapStyle = startCapStyle;
int jointStyle = createJoints(stroke.joints);
boolean noHScaleFlag = stroke.scaleMode == ScaleMode.VERTICAL || stroke.scaleMode == ScaleMode.NONE;
boolean noVScaleFlag = stroke.scaleMode == ScaleMode.HORIZONTAL || stroke.scaleMode == ScaleMode.NONE;
// The 4.5.1 Flex Compiler switches these two flags.
// A bug has been logged in JIRA against the old compiler for this issue
// http://bugs.adobe.com/jira/browse/SDK-31114
ls.setNoHScaleFlag(noHScaleFlag);
ls.setNoVScaleFlag(noVScaleFlag);
ls.setJoinStyle(jointStyle);
ls.setStartCapStyle(startCapStyle);
ls.setEndCapStyle(endCapStyle);
ls.setPixelHintingFlag(stroke.pixelHinting);
if (jointStyle == 2)
{
// Encoded in SWF as an 8.8 fixed point value
ls.setMiterLimitFactor((float)(stroke.miterLimit));
}
return ls;
}
protected LineStyle createLineStyle(SolidColorStrokeNode stroke)
{
LineStyle ls = createGenericLineStyle(stroke);
ls.setColor(TypeHelper.splitColor(TypeHelper.colorARGB(stroke.color, stroke.alpha)));
return ls;
}
protected LineStyle2 createLineStyle(LinearGradientStrokeNode stroke, Rect bounds)
{
LineStyle2 ls = createGenericLineStyle(stroke);
ls.setFillType(createFillStyle(stroke, bounds));
ls.setHasFillFlag(true);
return ls;
}
protected LineStyle2 createLineStyle(RadialGradientStrokeNode stroke, Rect edgeBounds)
{
LineStyle2 ls = createGenericLineStyle(stroke);
ls.setFillType(createFillStyle(stroke, edgeBounds));
ls.setHasFillFlag(true);
return ls;
}
protected int createCaps(Caps value)
{
if (value != null)
return value.ordinal();
else
return Caps.NONE.ordinal();
}
protected int createJoints(Joints value)
{
if (value != null)
return value.ordinal();
else
return Joints.ROUND.ordinal();
}
protected int createSpreadMode(SpreadMethod value)
{
return value.ordinal();
}
protected int createBlendMode(BlendMode value)
{
return value.ordinal();
}
protected int createInterpolationMode(InterpolationMethod value)
{
return value.ordinal();
}
protected List<Filter> createFilters(List<IFilterNode> list)
{
List<Filter> filters = new ArrayList<Filter>(list.size());
Iterator<IFilterNode> iterator = list.iterator();
while (iterator.hasNext())
{
IFilterNode f = iterator.next();
if (f instanceof BevelFilterNode)
{
BevelFilterNode node = (BevelFilterNode)f;
BevelFilter filter = createBevelFilter(node);
Filter rec = new Filter();
rec.setFilterID(Filter.BEVEL);
rec.setBevelFilter(filter);
filters.add(rec);
}
else if (f instanceof BlurFilterNode)
{
BlurFilterNode node = (BlurFilterNode)f;
BlurFilter filter = createBlurFilter(node);
Filter rec = new Filter();
rec.setFilterID(Filter.BLUR);
rec.setBlurFilter(filter);
filters.add(rec);
}
else if (f instanceof ColorMatrixFilterNode)
{
ColorMatrixFilterNode node = (ColorMatrixFilterNode)f;
Filter rec = new Filter();
rec.setFilterID(Filter.COLOR_MATRIX);
rec.setColorMatrixFilter(node.matrix);
filters.add(rec);
}
else if (f instanceof DropShadowFilterNode)
{
DropShadowFilterNode node = (DropShadowFilterNode)f;
DropShadowFilter filter = createDropShadowFilter(node);
Filter rec = new Filter();
rec.setFilterID(Filter.DROP_SHADOW);
rec.setDropShadowFilter(filter);
filters.add(rec);
}
else if (f instanceof GlowFilterNode)
{
GlowFilterNode node = (GlowFilterNode)f;
GlowFilter filter = createGlowFilter(node);
Filter rec = new Filter();
rec.setFilterID(Filter.GLOW);
rec.setGlowFilter(filter);
filters.add(rec);
}
else if (f instanceof GradientBevelFilterNode)
{
GradientBevelFilterNode node = (GradientBevelFilterNode)f;
GradientBevelFilter filter = createGradientBevelFilter(node);
Filter rec = new Filter();
rec.setFilterID(Filter.GRADIENT_BEVEL);
rec.setGradientBevelFilter(filter);
filters.add(rec);
}
else if (f instanceof GradientGlowFilterNode)
{
GradientGlowFilterNode node = (GradientGlowFilterNode)f;
GradientGlowFilter filter = createGradientGlowFilter(node);
Filter rec = new Filter();
rec.setFilterID(Filter.GRADIENT_GLOW);
rec.setGradientGlowFilter(filter);
filters.add(rec);
}
}
return filters;
}
protected BevelFilter createBevelFilter(BevelFilterNode node)
{
BevelFilter filter = new BevelFilter();
filter.setAngle((float)(node.angle*Math.PI/180.0));
filter.setBlurX((float)node.blurX);
filter.setBlurY((float)node.blurY);
filter.setDistance((float)node.distance);
filter.setStrength((float)node.strength);
filter.setShadowColor(TypeHelper.splitColor(TypeHelper.colorARGB(node.shadowColor, node.shadowAlpha)));
filter.setHighlightColor(TypeHelper.splitColor(TypeHelper.colorARGB(node.highlightColor, node.highlightAlpha)));
filter.setOnTop(node.type == BevelType.FULL);
filter.setInnerShadow(node.type == BevelType.INNER);
filter.setPasses(node.quality);
filter.setKnockout(node.knockout);
filter.setCompositeSource(true);
return filter;
}
protected BlurFilter createBlurFilter(BlurFilterNode node)
{
BlurFilter filter = new BlurFilter();
filter.setBlurX((float)(node.blurX));
filter.setBlurY((float)(node.blurY));
filter.setPasses(node.quality);
return filter;
}
protected DropShadowFilter createDropShadowFilter(DropShadowFilterNode node)
{
DropShadowFilter filter = new DropShadowFilter();
filter.setDropShadowColor(TypeHelper.splitColor(TypeHelper.colorARGB(node.color, node.alpha)));
filter.setAngle((float)(node.angle*Math.PI/180.0));
filter.setBlurX((float)(node.blurX));
filter.setBlurY((float)(node.blurY));
filter.setDistance((float)(node.distance));
filter.setStrength((float)(node.strength));
filter.setPasses(node.quality);
filter.setCompositeSource(!node.hideObject);
filter.setKnockout(node.knockout);
filter.setInnerShadow(node.inner);
return filter;
}
protected GlowFilter createGlowFilter(GlowFilterNode node)
{
GlowFilter filter = new GlowFilter();
filter.setGlowColor(TypeHelper.splitColor(TypeHelper.colorARGB(node.color, node.alpha)));
filter.setBlurX((float)(node.blurX));
filter.setBlurY((float)(node.blurY));
filter.setStrength((float)(node.strength));
filter.setKnockout(node.knockout);
filter.setInnerGlow(node.inner);
filter.setCompositeSource(true);
filter.setPasses(node.quality);
return filter;
}
protected GradientBevelFilter createGradientBevelFilter(
GradientBevelFilterNode node)
{
GradientBevelFilter filter = new GradientBevelFilter();
if (node.entries != null)
{
byte count = (byte)node.entries.size();
filter.setNumColors(count);
RGBA gradientColors[] = new RGBA[count];
int gradientRatios[] = new int[count];
filter.setGradientColors(gradientColors);
filter.setGradientRatio(gradientRatios);
GradRecord[] records = createGradRecords(node.entries);
for (int i = 0; i < records.length; i++)
{
GradRecord record = records[i];
RGB color = record.getColor();
if (color instanceof RGBA)
gradientColors[i] = (RGBA)color;
else
gradientColors[i] = new RGBA(color.getRed(), color.getGreen(), color.getBlue(), 0xFF);
gradientRatios[i] = record.getRatio();
}
}
filter.setAngle((float)(node.angle*Math.PI/180.0));
filter.setBlurX((float)(node.blurX));
filter.setBlurY((float)(node.blurY));
filter.setDistance((float)(node.distance));
filter.setStrength((float)(node.strength));
filter.setKnockout(node.knockout);
filter.setPasses(node.quality);
filter.setCompositeSource(true);
filter.setInnerShadow(node.type == BevelType.INNER);
filter.setOnTop(node.type == BevelType.FULL);
return filter;
}
protected GradientGlowFilter createGradientGlowFilter(
GradientGlowFilterNode node)
{
GradientGlowFilter filter = new GradientGlowFilter();
if (node.entries != null)
{
byte count = (byte)node.entries.size();
filter.setNumColors(count);
RGBA gradientColors[] = new RGBA[count];
int gradientRatio[] = new int[count];
GradRecord[] records = createGradRecords(node.entries);
for (int i = 0; i < records.length; i++)
{
GradRecord record = records[i];
RGB color = record.getColor();
if (color instanceof RGBA)
gradientColors[i] = (RGBA)color;
else
gradientColors[i] = new RGBA(color.getRed(), color.getGreen(), color.getBlue(), 0xFF);
gradientRatio[i] = record.getRatio();
}
filter.setGradientColors(gradientColors);
filter.setGradientRatio(gradientRatio);
}
filter.setAngle((float)(node.angle*Math.PI/180.0));
filter.setBlurX((float)node.blurX);
filter.setBlurY((float)node.blurY);
filter.setDistance((float)node.distance);
filter.setStrength((float)node.strength);
filter.setPasses(node.quality);
filter.setKnockout(node.knockout);
filter.setInnerGlow(node.inner);
filter.setCompositeSource(true);
return filter;
}
protected void populateGradient(Gradient gradient,
List<GradientEntryNode> entries, InterpolationMethod interpolation,
SpreadMethod spread)
{
gradient.setGradientRecords(Arrays.asList(createGradRecords(entries)));
if (interpolation != null)
gradient.setInterpolationMode(createInterpolationMode(interpolation));
if (spread != null)
gradient.setSpreadMode(createSpreadMode(spread));
}
protected GradRecord[] createGradRecords(List<GradientEntryNode> entries)
{
int count = entries.size();
GradRecord[] records = new GradRecord[count];
double previousRatio = 0.0;
for (int currentIndex = 0; currentIndex < count; currentIndex++)
{
GradientEntryNode entry = entries.get(currentIndex);
double thisRatio = entry.ratio;
// Auto-calculate gradient ratio if omitted from an entry.
if (Double.isNaN(thisRatio))
{
// The first ratio is assumed to be 0.0.
if (currentIndex == 0)
{
thisRatio = 0.0;
}
// The last ratio is assumed to be 1.0.
else if (currentIndex == count - 1)
{
thisRatio = 1.0;
}
else
{
// Other omitted ratios are divided evenly between the last
// ratio and the next specified ratio (or 1.0 if none).
double nextRatio = 1.0;
int nextIndex = count - 1;
for (int i = currentIndex; i < count; i++)
{
GradientEntryNode nextEntry = entries.get(i);
if (!Double.isNaN(nextEntry.ratio))
{
nextRatio = nextEntry.ratio;
nextIndex = i;
break;
}
}
int entryGap = nextIndex - (currentIndex - 1);
if (entryGap > 0)
{
thisRatio = previousRatio + ((nextRatio - previousRatio) / (entryGap));
}
else
{
thisRatio = previousRatio;
}
}
}
GradRecord record = new GradRecord(TypeHelper.gradientRatio(thisRatio), TypeHelper.splitColor(TypeHelper.colorARGB(entry.color, entry.alpha)));
records[currentIndex] = record;
// Remember this ratio as the last one specified
previousRatio = thisRatio;
}
return records;
}
protected String parseSource(String source)
{
// TODO: Create a standard @Embed() parser.
if (source != null)
{
source = source.trim();
if (source.startsWith("@Embed("))
{
source = source.substring(7).trim();
if (source.endsWith(")"))
{
source = source.substring(0, source.length() - 1).trim();
}
if (source.charAt(0) == '\'' && source.charAt(source.length() - 1) == '\'')
{
source = source.substring(1, source.length() - 1).trim();
}
}
}
return source;
}
private void markLeafNodesAsMask(IMaskableNode maskableNode, GroupNode mask)
{
if ((mask == null) || (mask.children == null))
return;
Iterator<GraphicContentNode> iter = mask.children.iterator();
while (iter.hasNext())
{
GraphicContentNode gcNode = iter.next();
if (gcNode instanceof GroupNode)
{
markLeafNodesAsMask(maskableNode, (GroupNode) gcNode);
}
else
{
if (maskableNode.getMaskType() == MaskType.CLIP)
gcNode.isPartofClipMask = true;
}
}
}
@Override
public ITypeDefinition[] getDependencies()
{
return new ITypeDefinition[0];
}
/**
* This class wraps a PathIterator and adds a IShapeIterator
* implemenation.
*/
private static class PathIteratorWrapper implements IShapeIterator
{
private PathIterator pi;
public PathIteratorWrapper(PathIterator pi)
{
this.pi = pi;
}
@Override
public short currentSegment(double[] coords)
{
int code = pi.currentSegment(coords);
return (short) code;
}
@Override
public boolean isDone()
{
return pi.isDone();
}
@Override
public void next()
{
pi.next();
}
}
}