| /* |
| * |
| * 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 com.adobe.fxg.swf; |
| |
| import java.awt.geom.Ellipse2D; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Stack; |
| import java.util.Random; |
| |
| import com.adobe.fxg.FXGTranscoder; |
| import com.adobe.fxg.FXGException; |
| import com.adobe.internal.fxg.dom.AbstractFXGNode; |
| import com.adobe.internal.fxg.dom.AbstractShapeNode; |
| import com.adobe.internal.fxg.dom.BitmapGraphicNode; |
| import com.adobe.internal.fxg.dom.DefinitionNode; |
| import com.adobe.internal.fxg.dom.EllipseNode; |
| import com.adobe.internal.fxg.dom.FillNode; |
| import com.adobe.internal.fxg.dom.FilterNode; |
| import com.adobe.internal.fxg.dom.GradientEntryNode; |
| import com.adobe.internal.fxg.dom.GraphicContentNode; |
| import com.adobe.internal.fxg.dom.GraphicContext; |
| import com.adobe.internal.fxg.dom.GraphicNode; |
| import com.adobe.internal.fxg.dom.GroupDefinitionNode; |
| import com.adobe.internal.fxg.dom.GroupNode; |
| import com.adobe.internal.fxg.dom.LineNode; |
| import com.adobe.internal.fxg.dom.MaskableNode; |
| import com.adobe.internal.fxg.dom.MaskingNode; |
| import com.adobe.internal.fxg.dom.PathNode; |
| import com.adobe.internal.fxg.dom.PlaceObjectNode; |
| import com.adobe.internal.fxg.dom.RectNode; |
| import com.adobe.internal.fxg.dom.RichTextNode; |
| import com.adobe.internal.fxg.dom.StrokeNode; |
| import com.adobe.internal.fxg.dom.TextGraphicNode; |
| import com.adobe.internal.fxg.dom.fills.BitmapFillNode; |
| import com.adobe.internal.fxg.dom.fills.LinearGradientFillNode; |
| import com.adobe.internal.fxg.dom.fills.RadialGradientFillNode; |
| import com.adobe.internal.fxg.dom.fills.SolidColorFillNode; |
| import com.adobe.internal.fxg.dom.filters.BevelFilterNode; |
| import com.adobe.internal.fxg.dom.filters.BlurFilterNode; |
| import com.adobe.internal.fxg.dom.filters.ColorMatrixFilterNode; |
| import com.adobe.internal.fxg.dom.filters.DropShadowFilterNode; |
| import com.adobe.internal.fxg.dom.filters.GlowFilterNode; |
| import com.adobe.internal.fxg.dom.filters.GradientBevelFilterNode; |
| import com.adobe.internal.fxg.dom.filters.GradientGlowFilterNode; |
| import com.adobe.internal.fxg.dom.strokes.AbstractStrokeNode; |
| import com.adobe.internal.fxg.dom.strokes.LinearGradientStrokeNode; |
| import com.adobe.internal.fxg.dom.strokes.RadialGradientStrokeNode; |
| import com.adobe.internal.fxg.dom.strokes.SolidColorStrokeNode; |
| import com.adobe.internal.fxg.dom.transforms.ColorTransformNode; |
| import com.adobe.internal.fxg.dom.transforms.MatrixNode; |
| import com.adobe.internal.fxg.dom.types.BevelType; |
| import com.adobe.internal.fxg.dom.types.BlendMode; |
| import com.adobe.internal.fxg.dom.types.Caps; |
| import com.adobe.internal.fxg.dom.types.InterpolationMethod; |
| import com.adobe.internal.fxg.dom.types.Joints; |
| import com.adobe.internal.fxg.dom.types.MaskType; |
| import com.adobe.internal.fxg.dom.types.ScaleMode; |
| import com.adobe.internal.fxg.dom.types.ScalingGrid; |
| import com.adobe.internal.fxg.dom.types.SpreadMethod; |
| import com.adobe.internal.fxg.dom.types.Winding; |
| import com.adobe.internal.fxg.swf.ImageHelper; |
| import com.adobe.internal.fxg.swf.ShapeHelper; |
| import com.adobe.internal.fxg.swf.TypeHelper; |
| import com.adobe.internal.fxg.types.FXGMatrix; |
| |
| import flash.swf.SwfConstants; |
| import flash.swf.Tag; |
| import flash.swf.builder.types.PathIteratorWrapper; |
| import flash.swf.builder.types.ShapeBuilder; |
| import flash.swf.builder.types.ShapeIterator; |
| import flash.swf.tags.DefineBits; |
| import flash.swf.tags.DefineScalingGrid; |
| import flash.swf.tags.DefineShape; |
| import flash.swf.tags.DefineSprite; |
| import flash.swf.tags.DefineTag; |
| import flash.swf.tags.PlaceObject; |
| import flash.swf.types.BevelFilter; |
| import flash.swf.types.BlurFilter; |
| import flash.swf.types.CXFormWithAlpha; |
| import flash.swf.types.ColorMatrixFilter; |
| import flash.swf.types.DropShadowFilter; |
| import flash.swf.types.FillStyle; |
| import flash.swf.types.Filter; |
| import flash.swf.types.FocalGradient; |
| import flash.swf.types.GlowFilter; |
| import flash.swf.types.GradRecord; |
| import flash.swf.types.Gradient; |
| import flash.swf.types.GradientBevelFilter; |
| import flash.swf.types.GradientGlowFilter; |
| import flash.swf.types.LineStyle; |
| import flash.swf.types.Matrix; |
| import flash.swf.types.Rect; |
| import flash.swf.types.Shape; |
| import flash.swf.types.ShapeRecord; |
| import flash.swf.types.ShapeWithStyle; |
| import flash.swf.types.TagList; |
| import com.adobe.fxg.dom.FXGNode; |
| import com.adobe.fxg.util.FXGLog; |
| import com.adobe.fxg.util.FXGLogger; |
| import com.adobe.fxg.util.FXGResourceResolver; |
| |
| /** |
| * Transcodes an FXG DOM into a tree of SWF DefineSprites 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 FXGTranscoder |
| { |
| protected HashMap<String, DefineSprite> definitions; |
| protected Stack<DefineSprite> spriteStack; |
| protected FXGResourceResolver resourceResolver; |
| protected int spriteCount; |
| protected static Random random = new Random(); |
| |
| /** |
| * Create a new instance of the transcoder. |
| * |
| * @return the transcoder |
| */ |
| public FXG2SWFTranscoder newInstance() |
| { |
| return new FXG2SWFTranscoder(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void setResourceResolver(FXGResourceResolver resolver) |
| { |
| resourceResolver = resolver; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public Object transcode(FXGNode fxgNode) |
| { |
| GraphicNode node = (GraphicNode)fxgNode; |
| DefineSprite sprite = createDefineSprite("Graphic"); |
| spriteStack.push(sprite); |
| |
| // Process mask (if present) |
| if (node.mask != null) |
| mask(node, sprite); |
| |
| // Handle 'scale 9' grid definition |
| if (node.definesScaleGrid()) |
| { |
| DefineScalingGrid grid = createDefineScalingGrid(node.getScalingGrid()); |
| grid.scalingTarget = sprite; |
| sprite.scalingGrid = grid; |
| } |
| |
| // Process child nodes |
| if (node.children != null) |
| graphicContentNodes(node.children); |
| |
| spriteStack.pop(); |
| return sprite; |
| } |
| |
| /** |
| * Instantiates a new transcoder. |
| */ |
| public FXG2SWFTranscoder() |
| { |
| spriteStack = new Stack<DefineSprite>(); |
| } |
| |
| |
| private PlaceObject bitmapWithClip(DefineBits imageTag, BitmapGraphicNode node) |
| { |
| GraphicContext context = node.createGraphicContext(); |
| |
| //process the filters later to avoid masking |
| List<FilterNode> filters = null; |
| if (context.filters != null) |
| { |
| filters = context.filters; |
| context.filters = null; |
| DefineSprite filterSprite = createDefineSprite("MaskFilter"); |
| spriteStack.push(filterSprite); |
| } |
| |
| DefineSprite imageSprite = createDefineSprite("BitmapGraphic"); |
| spriteStack.push(imageSprite); |
| |
| // First, generate the clipping mask |
| DefineSprite clipSprite = createDefineSprite("BitmapGraphic_Clip"); |
| spriteStack.push(clipSprite); |
| double width = (imageTag.width < node.width) ? imageTag.width : node.width; |
| double height = (imageTag.height < node.height) ? imageTag.height : node.height; |
| List<ShapeRecord> shapeRecords = ShapeHelper.rectangle(0.0, 0.0, width, height); |
| DefineShape clipShape = createDefineShape(null, shapeRecords, new SolidColorFillNode(), null, context.getTransform()); |
| placeObject(clipShape, new GraphicContext()); |
| spriteStack.pop(); |
| |
| //place the clipping mask in the imageSprite |
| PlaceObject po3clip = placeObject(clipSprite, context); |
| po3clip.setClipDepth(po3clip.depth+1); |
| |
| // Then, process the image |
| DefineShape imageShape = ImageHelper.createShapeForImage(imageTag, node); |
| placeObject(imageShape, context); |
| spriteStack.pop(); |
| |
| PlaceObject po3 = placeObject(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) |
| { |
| DefineSprite sprite = spriteStack.pop(); |
| GraphicContext gc = new GraphicContext(); |
| gc.filters = filters; |
| PlaceObject poFilter = placeObject(sprite, gc); |
| return poFilter; |
| } |
| return po3; |
| } |
| |
| // -------------------------------------------------------------------------- |
| // |
| // Graphic Content Nodes |
| // |
| // -------------------------------------------------------------------------- |
| protected PlaceObject bitmap(BitmapGraphicNode node) |
| { |
| GraphicContext context = node.createGraphicContext(); |
| String source = parseSource(node.source); |
| |
| if (source == null) |
| { |
| // Exception: Missing source attribute in <BitmapGraphic> or <BitmapFill>. |
| throw new FXGException(node.getStartLine(), node.getStartColumn(), "MissingSourceAttribute"); |
| } |
| DefineBits imageTag = createDefineBits(node, source); |
| |
| if ((node.visible) && (!node.isPartofClipMask)) |
| { |
| |
| DefineShape 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); |
| PlaceObject po3 = placeObject(imageShape, context); |
| return po3; |
| } |
| else |
| { |
| if (ImageHelper.bitmapImageNeedsClipping(imageTag, node)) |
| { |
| PlaceObject p03 = bitmapWithClip(imageTag, node); |
| return p03; |
| } |
| else |
| { |
| imageShape = ImageHelper.createShapeForImage(imageTag, node); |
| PlaceObject po3 = placeObject(imageShape, context); |
| return po3; |
| } |
| } |
| } |
| else |
| { |
| if (!ImageHelper.bitmapImageNeedsClipping(imageTag, node)) |
| { |
| double width = (Double.isNaN(node.width)) ? imageTag.width : node.width; |
| double height = (Double.isNaN(node.height)) ? imageTag.height : node.height; |
| List<ShapeRecord> shapeRecords = ShapeHelper.rectangle(0.0, 0.0, width, height); |
| DefineShape shape = createDefineShape(null, shapeRecords, new SolidColorFillNode(), null, context.getTransform()); |
| PlaceObject po3 = placeObject(shape, context); |
| return po3; |
| } |
| else |
| { |
| double width = ((imageTag.width < node.width) || Double.isNaN(node.width)) ? imageTag.width : node.width; |
| double height = ((imageTag.height < node.height) || (Double.isNaN(node.height))) ? imageTag.height : node.height; |
| List<ShapeRecord> shapeRecords = ShapeHelper.rectangle(0.0, 0.0, width, height); |
| DefineShape shape = createDefineShape(null, shapeRecords, new SolidColorFillNode(), null, context.getTransform()); |
| PlaceObject po3 = placeObject(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 PlaceObject graphicContentNode(GraphicContentNode node) |
| { |
| PlaceObject 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<FilterNode> 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; |
| DefineSprite filterSprite = createDefineSprite("MaskFilter"); |
| spriteStack.push(filterSprite); |
| } |
| DefineSprite 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 = placeObjectInstance((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) |
| { |
| DefineSprite sprite = spriteStack.pop(); |
| GraphicContext gc = new GraphicContext(); |
| gc.filters = filters; |
| PlaceObject poFilter = placeObject(sprite, gc); |
| return poFilter; |
| } |
| } |
| |
| return po3; |
| } |
| |
| protected PlaceObject ellipse(EllipseNode node) |
| { |
| // Note that we will apply node.x and node.y as a translation operation |
| // in the PlaceObject3 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(); |
| ShapeIterator iterator = new PathIteratorWrapper(ellipse.getPathIterator(null)); |
| builder.processShape(iterator); |
| Shape shape = builder.build(); |
| |
| return placeDefineShape(node, shape.shapeRecords, node.fill, node.stroke, node.createGraphicContext()); |
| |
| } |
| |
| protected PlaceObject group(GroupNode node) |
| { |
| //handle blendMode "auto" |
| if (node.blendMode == BlendMode.AUTO) |
| { |
| if ((node.alpha == 0) || (node.alpha == 1)) |
| node.blendMode = BlendMode.NORMAL; |
| else |
| { |
| if (node.isForMobile()) |
| node.blendMode = BlendMode.NORMAL; |
| else |
| node.blendMode = BlendMode.LAYER; |
| } |
| |
| |
| } |
| |
| DefineSprite groupSprite = createDefineSprite("Group"); |
| GraphicContext context = node.createGraphicContext(); |
| |
| // Handle 'scale 9' grid definition |
| if (node.definesScaleGrid()) |
| { |
| DefineScalingGrid grid = createDefineScalingGrid(context.scalingGrid); |
| grid.scalingTarget = groupSprite; |
| groupSprite.scalingGrid = grid; |
| } |
| |
| PlaceObject po3 = placeObject(groupSprite, context); |
| spriteStack.push(groupSprite); |
| |
| // First, process mask (if present) |
| List <FilterNode> 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) |
| { |
| DefineSprite filterSprite = createDefineSprite("MaskFilter"); |
| spriteStack.push(filterSprite); |
| } |
| DefineSprite 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) |
| { |
| DefineSprite sprite = spriteStack.pop(); |
| GraphicContext gc = new GraphicContext(); |
| gc.filters = filters; |
| PlaceObject poFilter = placeObject(sprite, gc); |
| return poFilter; |
| } |
| spriteStack.pop(); |
| return po3; |
| } |
| |
| protected PlaceObject line(LineNode node) |
| { |
| List<ShapeRecord> shapeRecords = ShapeHelper.line(node.xFrom, node.yFrom, node.xTo, node.yTo); |
| GraphicContext context = node.createGraphicContext(); |
| PlaceObject po3 = placeDefineShape(node, shapeRecords, node.fill, node.stroke, context); |
| return po3; |
| } |
| |
| protected PlaceObject mask(MaskableNode node, DefineSprite parentSprite) |
| { |
| PlaceObject po3 = null; |
| |
| MaskingNode 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(); |
| 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 = placeObjectInstance((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 = parentSprite.depthCounter + group.children.size(); |
| } |
| else if (node instanceof GraphicNode) |
| { |
| GraphicNode graphic = (GraphicNode)node; |
| if (graphic.children != null) |
| clipDepth = parentSprite.depthCounter + graphic.children.size(); |
| } |
| // ... otherwise, just clip the shape itself. |
| else |
| { |
| clipDepth = po3.depth + 1; |
| } |
| |
| po3.setClipDepth(clipDepth); |
| |
| if (node.getMaskType() == MaskType.ALPHA) |
| { |
| po3.setCacheAsBitmap(true); |
| } |
| } |
| |
| return po3; |
| } |
| |
| protected PlaceObject path(PathNode node) |
| { |
| List<ShapeRecord> shapeRecords = ShapeHelper.path(node, (node.fill != null)); |
| GraphicContext context = node.createGraphicContext(); |
| Winding winding[] = new Winding[1]; |
| winding[0] = node.winding; |
| PlaceObject po3 = placeDefineShape(node, shapeRecords, node.fill, node.stroke, context, winding); |
| return po3; |
| } |
| |
| protected void setPixelBenderBlendMode(PlaceObject po, BlendMode blendMode) |
| { |
| |
| } |
| |
| protected void setAlphaMask(PlaceObject po) |
| { |
| po.setCacheAsBitmap(true); |
| } |
| |
| protected void setLuminosityMask(PlaceObject po) |
| { |
| |
| } |
| |
| protected PlaceObject placeObject(DefineTag symbol, GraphicContext context) |
| { |
| DefineSprite sprite = spriteStack.peek(); |
| |
| PlaceObject po3 = new PlaceObject(Tag.stagPlaceObject3); |
| // po3.setName(name); |
| po3.setRef(symbol); |
| po3.depth = ++sprite.depthCounter; |
| |
| if (context.blendMode != null) |
| { |
| if (!context.blendMode.needsPixelBenderSupport()) |
| { |
| int blendMode = createBlendMode(context.blendMode); |
| po3.setBlendMode(blendMode); |
| } |
| else |
| { |
| setPixelBenderBlendMode(po3, context.blendMode); |
| } |
| } |
| |
| if (context.filters != null) |
| { |
| List<Filter> filters = createFilters(context.filters); |
| po3.setFilterList(filters); |
| } |
| |
| // FXG angles are always clockwise. |
| Matrix matrix = context.getTransform().toSWFMatrix(); |
| po3.setMatrix(matrix); |
| |
| 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.setCxform(cx); |
| } |
| |
| |
| if (context.maskType == MaskType.ALPHA) |
| { |
| setAlphaMask(po3); |
| } |
| else if (context.maskType == MaskType.LUMINOSITY) |
| { |
| setLuminosityMask(po3); |
| } |
| |
| sprite.tagList.placeObject3(po3); |
| return po3; |
| } |
| |
| protected PlaceObject rect(RectNode node) |
| { |
| // Note that we will apply node.x and node.y as a translation operation |
| // in the PlaceObject3 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); |
| } |
| |
| PlaceObject po3 = placeDefineShape(node, shapeRecords, node.fill, node.stroke, context); |
| return po3; |
| } |
| |
| protected PlaceObject text(TextGraphicNode node) |
| { |
| // No operation - text is ignored in this implementation. |
| return null; |
| } |
| |
| protected PlaceObject richtext(RichTextNode node) |
| { |
| // No operation - richtext is ignored in this implementation. |
| return null; |
| } |
| |
| // -------------------------------------------------------------------------- |
| // |
| // FXG Library Definitions |
| // |
| // -------------------------------------------------------------------------- |
| |
| protected PlaceObject placeObjectInstance(PlaceObjectNode node) |
| { |
| String definitionName = node.getNodeName(); |
| |
| if (definitions == null) |
| definitions = new HashMap<String, DefineSprite>(); |
| |
| DefineSprite definitionSprite = definitions.get(definitionName); |
| if (definitionSprite == null) |
| { |
| definitionSprite = createDefineSprite("Definition"); |
| FXG2SWFTranscoder graphics = newInstance(); |
| graphics.setResourceResolver(resourceResolver); |
| definitions.put(definitionName, definitionSprite); |
| graphics.definitions = definitions; |
| graphics.definition(node.definition, definitionSprite); |
| } |
| |
| PlaceObject po3 = placeObject(definitionSprite, node.createGraphicContext()); |
| return po3; |
| } |
| |
| protected void definition(DefinitionNode node, DefineSprite definitionSprite) |
| { |
| GroupDefinitionNode groupDefinition = node.groupDefinition; |
| |
| if (groupDefinition == null) |
| { |
| //Exception:Definitions must define a single Group child node. |
| throw new FXGException(node.getStartLine(), node.getStartColumn(), "MissingGroupChildNode"); |
| } |
| spriteStack.push(definitionSprite); |
| |
| if (groupDefinition.definesScaleGrid()) |
| { |
| definitionSprite.scalingGrid = createDefineScalingGrid(groupDefinition.getScalingGrid()); |
| definitionSprite.scalingGrid.scalingTarget = definitionSprite; |
| } |
| |
| graphicContentNodes(groupDefinition.children); |
| |
| spriteStack.pop(); |
| } |
| |
| // -------------------------------------------------------------------------- |
| // |
| // SWF Tags and Types Helper Methods |
| // |
| // -------------------------------------------------------------------------- |
| |
| protected DefineBits createDefineBits(FXGNode node, String source) |
| { |
| try |
| { |
| InputStream stream = resourceResolver.openStream(source); |
| DefineBits imageTag = ImageHelper.createDefineBits(stream, ImageHelper.guessMimeType(source)); |
| return imageTag; |
| } |
| catch (IOException ioe) |
| { |
| // Exception:Error {0} occurred while embedding image {1}. |
| throw new FXGException(node.getStartLine(), node.getStartColumn(), "ErrorEmbeddingImage", ioe.getMessage(), source); |
| } |
| } |
| |
| protected DefineScalingGrid createDefineScalingGrid(ScalingGrid grid) |
| { |
| DefineScalingGrid scalingGrid = new DefineScalingGrid(); |
| scalingGrid.rect = TypeHelper.rect(grid.scaleGridLeft, grid.scaleGridTop, grid.scaleGridRight, grid.scaleGridBottom); |
| return scalingGrid; |
| } |
| |
| protected DefineSprite createDefineSprite(String name) |
| { |
| DefineSprite sprite = new DefineSprite(); |
| sprite.tagList = new TagList(); |
| sprite.framecount = 1; |
| if (name == null) name = ""; |
| sprite.name = name + random.nextInt(); |
| return sprite; |
| } |
| |
| protected DefineShape createDefineShape(AbstractShapeNode node, List<ShapeRecord> shapeRecords, FillNode fill, |
| StrokeNode 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; |
| |
| ShapeWithStyle sws = new ShapeWithStyle(); |
| sws.shapeRecords = shapeRecords; |
| |
| int lineStyleIndex = stroke == null ? 0 : 1; |
| int fillStyle0Index = fill == null ? 0 : 1; |
| int fillStyle1Index = 0; |
| if (windings.length > 0) |
| ShapeHelper.setPathStyles(shapeRecords, lineStyleIndex, fillStyle0Index, fillStyle1Index); |
| else |
| ShapeHelper.setStyles(shapeRecords, lineStyleIndex, fillStyle0Index, fillStyle1Index); |
| |
| if (fill != null) |
| { |
| FillStyle fillStyle = createFillStyle(fill, edgeBounds); |
| sws.fillstyles = new ArrayList<FillStyle>(1); |
| sws.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); |
| sws.linestyles = new ArrayList<LineStyle>(); |
| sws.linestyles.add(lineStyle); |
| } |
| else |
| { |
| shapeBounds = edgeBounds; |
| } |
| |
| DefineShape defineShape4 = new DefineShape(Tag.stagDefineShape4); |
| defineShape4.shapeWithStyle = sws; |
| defineShape4.bounds = shapeBounds; |
| defineShape4.edgeBounds = edgeBounds; |
| if ((fill != null) &&( windings.length > 0)) |
| { |
| Winding windingValue = windings[0]; |
| defineShape4.usesFillWindingRule = (windingValue == Winding.NON_ZERO); |
| } |
| |
| return defineShape4; |
| } |
| |
| |
| protected PlaceObject placeDefineShape(AbstractShapeNode node, List<ShapeRecord> shapeRecords, |
| FillNode fill, StrokeNode 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) |
| { |
| // Exception: Missing source attribute in <BitmapGraphic> or <BitmapFill>. |
| throw new FXGException(fill.getStartLine(), fill.getStartColumn(), "MissingSourceAttribute"); |
| } |
| DefineBits imageTag = createDefineBits(fill, source); |
| |
| //process the filters later to avoid masking |
| List<FilterNode> filters = null; |
| if (context.filters != null) |
| { |
| filters = context.filters; |
| context.filters = null; |
| DefineSprite filterSprite = createDefineSprite("MaskFilter"); |
| spriteStack.push(filterSprite); |
| } |
| |
| DefineSprite imageSprite = createDefineSprite("BitmapFill"); |
| spriteStack.push(imageSprite); |
| |
| // First, generate the clipping mask |
| DefineSprite clipSprite = createDefineSprite("BitmapFill_Clip"); |
| spriteStack.push(clipSprite); |
| List<ShapeRecord> clipRectRecords = ShapeHelper.rectangle(0.0, 0.0, imageTag.width, imageTag.height); |
| DefineShape clipShape = createDefineShape(null, clipRectRecords, new SolidColorFillNode(), null, context.getTransform()); |
| FXGMatrix bitmapMatrix = TypeHelper.bitmapFillMatrix(fillNode, imageTag, edgeBounds); |
| FXGMatrix clipMatrix = new FXGMatrix(bitmapMatrix.a, bitmapMatrix.b, bitmapMatrix.c, bitmapMatrix.d, 0, 0); |
| clipMatrix.scale(1.0/SwfConstants.TWIPS_PER_PIXEL, 1.0/SwfConstants.TWIPS_PER_PIXEL); |
| clipMatrix.translate(bitmapMatrix.tx, bitmapMatrix.ty); |
| GraphicContext clipContext = new GraphicContext(); |
| clipContext.setTransform(clipMatrix); |
| placeObject(clipShape, clipContext); |
| spriteStack.pop(); |
| |
| // Set the depth of the mask to that of the bitmap image fill |
| clipContext.setTransform(context.getTransform()); |
| PlaceObject po3clip = placeObject(clipSprite, clipContext); |
| po3clip.setClipDepth(po3clip.depth+1); |
| |
| // Then, process the bitmap image fill |
| ShapeWithStyle sws = new ShapeWithStyle(); |
| sws.shapeRecords = shapeRecords; |
| int lineStyleIndex = 0; |
| int fillStyle0Index = 1; |
| int fillStyle1Index = 0; |
| if (windings.length > 0) |
| ShapeHelper.setPathStyles(shapeRecords, lineStyleIndex, fillStyle0Index, fillStyle1Index); |
| else |
| ShapeHelper.setStyles(shapeRecords, lineStyleIndex, fillStyle0Index, fillStyle1Index); |
| |
| FillStyle fillStyle = createFillStyle(fill, edgeBounds); |
| sws.fillstyles = new ArrayList<FillStyle>(1); |
| sws.fillstyles.add(fillStyle); |
| |
| DefineShape imageShape = new DefineShape(Tag.stagDefineShape4); |
| imageShape.shapeWithStyle = sws; |
| imageShape.bounds = edgeBounds; |
| imageShape.edgeBounds = edgeBounds; |
| if ((fill != null) &&( windings.length > 0)) |
| { |
| Winding windingValue = windings[0]; |
| imageShape.usesFillWindingRule = (windingValue == Winding.NON_ZERO); |
| } |
| PlaceObject po3 = placeObject(imageShape, context); |
| |
| if (stroke != null) |
| { |
| //make a copy of ShapeRecord for strokes |
| ArrayList<ShapeRecord> shapeRecords2 = new ArrayList<ShapeRecord>(shapeRecords); |
| Collections.copy(shapeRecords2, shapeRecords); |
| |
| //generate the define sprite for the stroke object with no clipping |
| ShapeWithStyle swsStroke = new ShapeWithStyle(); |
| swsStroke.shapeRecords = shapeRecords2; |
| |
| lineStyleIndex = 1; |
| fillStyle0Index = 0; |
| fillStyle1Index = 0; |
| ShapeHelper.replaceStyles(shapeRecords2, lineStyleIndex, fillStyle0Index, fillStyle1Index); |
| |
| // 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.linestyles = new ArrayList<LineStyle>(1); |
| swsStroke.linestyles.add(lineStyle); |
| |
| DefineShape strokeShape = new DefineShape(Tag.stagDefineShape4); |
| strokeShape.shapeWithStyle = swsStroke; |
| strokeShape.bounds = shapeBounds; |
| strokeShape.edgeBounds = edgeBounds; |
| po3 = placeObject(strokeShape, context); |
| } |
| spriteStack.pop(); |
| |
| po3 = placeObject(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) |
| { |
| DefineSprite sprite = spriteStack.pop(); |
| GraphicContext gc = new GraphicContext(); |
| gc.filters = filters; |
| PlaceObject poFilter = placeObject(sprite, gc); |
| return poFilter; |
| } |
| |
| return po3; |
| |
| } |
| else |
| { |
| DefineShape shape = createDefineShape(node, shapeRecords, fill, stroke, context.getTransform(), windings); |
| PlaceObject po3 = placeObject(shape, context); |
| return po3; |
| } |
| |
| } |
| |
| protected FillStyle createFillStyle(FillNode 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.color = TypeHelper.colorARGB(fill.color, fill.alpha); |
| fs.type = FillStyle.FILL_SOLID; |
| return fs; |
| } |
| |
| protected FillStyle createFillStyle(BitmapFillNode fill, Rect bounds) |
| { |
| FillStyle fs = new FillStyle(); |
| |
| if (ImageHelper.bitmapFillModeIsRepeat(fill)) |
| fs.type = FillStyle.FILL_BITS; |
| else |
| fs.type = FillStyle.FILL_BITS | FillStyle.FILL_BITS_CLIP; |
| |
| String sourceFormatted = parseSource(fill.source); |
| |
| if (sourceFormatted == null) |
| { |
| // Source is required after FXG 1.0 |
| // Exception: Missing source attribute in <BitmapGraphic> or <BitmapFill>. |
| throw new FXGException(fill.getStartLine(), fill.getStartColumn(), "MissingSourceAttribute"); |
| } |
| |
| DefineBits img = createDefineBits(fill, sourceFormatted); |
| fs.bitmap = img; |
| |
| fs.matrix = TypeHelper.bitmapFillMatrix(fill, img, bounds).toSWFMatrix(); |
| |
| return fs; |
| } |
| |
| protected FillStyle createFillStyle(LinearGradientFillNode node, Rect bounds) |
| { |
| FillStyle fs = new FillStyle(); |
| fs.type = FillStyle.FILL_LINEAR_GRADIENT; |
| fs.matrix = TypeHelper.linearGradientMatrix(node, bounds); |
| Gradient gradient = new Gradient(); |
| populateGradient(gradient, node.entries, node.interpolationMethod, node.spreadMethod); |
| fs.gradient = gradient; |
| |
| return fs; |
| } |
| |
| protected FillStyle createFillStyle(LinearGradientStrokeNode node, Rect bounds) |
| { |
| FillStyle fs = new FillStyle(); |
| fs.type = FillStyle.FILL_LINEAR_GRADIENT; |
| fs.matrix = TypeHelper.linearGradientMatrix(node, bounds); |
| Gradient gradient = new Gradient(); |
| populateGradient(gradient, node.entries, node.interpolationMethod, node.spreadMethod); |
| fs.gradient = gradient; |
| |
| return fs; |
| } |
| |
| protected FillStyle createFillStyle(RadialGradientFillNode node, Rect bounds) |
| { |
| FillStyle fs = new FillStyle(); |
| fs.type = FillStyle.FILL_FOCAL_RADIAL_GRADIENT; |
| fs.matrix = TypeHelper.radialGradientMatrix(node, bounds); |
| FocalGradient gradient = new FocalGradient(); |
| populateGradient(gradient, node.entries, node.interpolationMethod, node.spreadMethod); |
| gradient.focalPoint = (float)node.focalPointRatio; |
| fs.gradient = gradient; |
| |
| return fs; |
| } |
| |
| protected FillStyle createFillStyle(RadialGradientStrokeNode node, Rect bounds) |
| { |
| FillStyle fs = new FillStyle(); |
| fs.type = FillStyle.FILL_FOCAL_RADIAL_GRADIENT; |
| fs.matrix = TypeHelper.radialGradientMatrix(node, bounds); |
| FocalGradient gradient = new FocalGradient(); |
| populateGradient(gradient, node.entries, node.interpolationMethod, node.spreadMethod); |
| gradient.focalPoint = (float)node.focalPointRatio; |
| fs.gradient = gradient; |
| |
| return fs; |
| } |
| |
| protected LineStyle createLineStyle(StrokeNode 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 LineStyle createGenericLineStyle(AbstractStrokeNode stroke) |
| { |
| LineStyle ls = new LineStyle(); |
| ls.width = (int)StrictMath.rint(stroke.getWeight() * SwfConstants.TWIPS_PER_PIXEL); |
| |
| int flags = 0; |
| int startCapStyle = createCaps(stroke.caps); |
| int endCapStyle = startCapStyle; |
| int jointStyle = createJoints(stroke.joints); |
| int noHorizonalScale = 1; |
| int noVerticalScale = 1; |
| |
| |
| // First set of 8 bit flags |
| if (stroke.scaleMode == ScaleMode.VERTICAL || stroke.scaleMode == ScaleMode.NONE) |
| flags |= noHorizonalScale << 1; |
| if (stroke.scaleMode == ScaleMode.HORIZONTAL || stroke.scaleMode == ScaleMode.NONE) |
| flags |= noVerticalScale << 2; |
| flags |= jointStyle << 4; |
| flags |= startCapStyle << 6; |
| if (stroke.pixelHinting) |
| flags |= 1; |
| |
| // Second set of 8 bit flags |
| flags |= endCapStyle << 8; |
| |
| if (jointStyle == 2) |
| { |
| // Encoded in SWF as an 8.8 fixed point value |
| ls.miterLimit = TypeHelper.fixed8(stroke.miterLimit); |
| } |
| |
| ls.flags = flags; |
| return ls; |
| } |
| protected LineStyle createLineStyle(SolidColorStrokeNode stroke) |
| { |
| LineStyle ls = createGenericLineStyle(stroke); |
| ls.color = TypeHelper.colorARGB(stroke.color, stroke.alpha); |
| return ls; |
| } |
| |
| protected LineStyle createLineStyle(LinearGradientStrokeNode stroke, Rect bounds) |
| { |
| LineStyle ls = createGenericLineStyle(stroke); |
| ls.fillStyle = createFillStyle(stroke, bounds); |
| int hasFillStyle = 1; |
| ls.flags |= hasFillStyle << 3; |
| return ls; |
| } |
| |
| protected LineStyle createLineStyle(RadialGradientStrokeNode stroke, Rect edgeBounds) |
| { |
| LineStyle ls = createGenericLineStyle(stroke); |
| ls.fillStyle = createFillStyle(stroke, edgeBounds); |
| int hasFillStyle = 1; |
| ls.flags |= hasFillStyle << 3; |
| 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<FilterNode> list) |
| { |
| List<Filter> filters = new ArrayList<Filter>(list.size()); |
| Iterator<FilterNode> iterator = list.iterator(); |
| while (iterator.hasNext()) |
| { |
| FilterNode f = iterator.next(); |
| if (f instanceof BevelFilterNode) |
| { |
| BevelFilterNode node = (BevelFilterNode)f; |
| BevelFilter filter = createBevelFilter(node); |
| filters.add(filter); |
| } |
| else if (f instanceof BlurFilterNode) |
| { |
| BlurFilterNode node = (BlurFilterNode)f; |
| BlurFilter filter = createBlurFilter(node); |
| filters.add(filter); |
| } |
| else if (f instanceof ColorMatrixFilterNode) |
| { |
| ColorMatrixFilterNode node = (ColorMatrixFilterNode)f; |
| ColorMatrixFilter filter = new ColorMatrixFilter(); |
| filter.values = node.matrix; |
| filters.add(filter); |
| } |
| else if (f instanceof DropShadowFilterNode) |
| { |
| DropShadowFilterNode node = (DropShadowFilterNode)f; |
| DropShadowFilter filter = createDropShadowFilter(node); |
| filters.add(filter); |
| } |
| else if (f instanceof GlowFilterNode) |
| { |
| GlowFilterNode node = (GlowFilterNode)f; |
| GlowFilter filter = createGlowFilter(node); |
| filters.add(filter); |
| } |
| else if (f instanceof GradientBevelFilterNode) |
| { |
| GradientBevelFilterNode node = (GradientBevelFilterNode)f; |
| GradientBevelFilter filter = createGradientBevelFilter(node); |
| filters.add(filter); |
| } |
| else if (f instanceof GradientGlowFilterNode) |
| { |
| GradientGlowFilterNode node = (GradientGlowFilterNode)f; |
| GradientGlowFilter filter = createGradientGlowFilter(node); |
| filters.add(filter); |
| } |
| } |
| return filters; |
| } |
| |
| protected BevelFilter createBevelFilter(BevelFilterNode node) |
| { |
| BevelFilter filter = new BevelFilter(); |
| filter.angle = TypeHelper.fixed(node.angle*Math.PI/180.0); |
| filter.blurX = TypeHelper.fixed(node.blurX); |
| filter.blurY = TypeHelper.fixed(node.blurY); |
| filter.distance = TypeHelper.fixed(node.distance); |
| filter.strength = TypeHelper.fixed8(node.strength); |
| filter.shadowColor = TypeHelper.colorARGB(node.shadowColor, node.shadowAlpha); |
| filter.highlightColor = TypeHelper.colorARGB(node.highlightColor, node.highlightAlpha); |
| filter.flags = node.quality; |
| if (node.type == BevelType.FULL) |
| filter.flags |= 1 << 4; |
| filter.flags |= 1 << 5; // Always a composite source |
| if (node.knockout) |
| filter.flags |= 1 << 6; |
| if (node.type == BevelType.INNER) |
| filter.flags |= 1 << 7; |
| return filter; |
| } |
| |
| protected BlurFilter createBlurFilter(BlurFilterNode node) |
| { |
| BlurFilter filter = new BlurFilter(); |
| filter.blurX = TypeHelper.fixed(node.blurX); |
| filter.blurY = TypeHelper.fixed(node.blurY); |
| filter.passes = node.quality << 3; |
| return filter; |
| } |
| |
| protected DropShadowFilter createDropShadowFilter(DropShadowFilterNode node) |
| { |
| DropShadowFilter filter = new DropShadowFilter(); |
| filter.color = TypeHelper.colorARGB(node.color, node.alpha); |
| filter.angle = TypeHelper.fixed(node.angle*Math.PI/180.0); |
| filter.blurX = TypeHelper.fixed(node.blurX); |
| filter.blurY = TypeHelper.fixed(node.blurY); |
| filter.distance = TypeHelper.fixed(node.distance); |
| filter.strength = TypeHelper.fixed8(node.strength); |
| filter.flags = node.quality; |
| if (!node.hideObject) // Set CompositeSource bit for non-hiddenObject |
| filter.flags |= 1 << 5; |
| if (node.knockout) |
| filter.flags |= 1 << 6; |
| if (node.inner) |
| filter.flags |= 1 << 7; |
| return filter; |
| } |
| |
| protected GlowFilter createGlowFilter(GlowFilterNode node) |
| { |
| GlowFilter filter = new GlowFilter(); |
| filter.color = TypeHelper.colorARGB(node.color, node.alpha); |
| filter.blurX = TypeHelper.fixed(node.blurX); |
| filter.blurY = TypeHelper.fixed(node.blurY); |
| filter.strength = TypeHelper.fixed8(node.strength); |
| filter.flags = node.quality; |
| filter.flags |= 1 << 5; // Always a composite source |
| if (node.knockout) |
| filter.flags |= 1 << 6; |
| if (node.inner) |
| filter.flags |= 1 << 7; |
| |
| return filter; |
| } |
| |
| protected GradientBevelFilter createGradientBevelFilter( |
| GradientBevelFilterNode node) |
| { |
| GradientBevelFilter filter = new GradientBevelFilter(); |
| if (node.entries != null) |
| { |
| byte count = (byte)node.entries.size(); |
| filter.numcolors = count; |
| filter.gradientColors = new int[count]; |
| filter.gradientRatio = new int[count]; |
| |
| GradRecord[] records = createGradRecords(node.entries); |
| for (int i = 0; i < records.length; i++) |
| { |
| GradRecord record = records[i]; |
| filter.gradientColors[i] = record.color; |
| filter.gradientRatio[i] = record.ratio; |
| } |
| } |
| |
| filter.angle = TypeHelper.fixed(node.angle*Math.PI/180.0); |
| filter.blurX = TypeHelper.fixed(node.blurX); |
| filter.blurY = TypeHelper.fixed(node.blurY); |
| filter.distance = TypeHelper.fixed(node.distance); |
| filter.strength = TypeHelper.fixed8(node.strength); |
| filter.flags = node.quality; // Quality encoded with 4 bits |
| if (node.type == BevelType.FULL) |
| filter.flags |= 1 << 4; |
| filter.flags |= 1 << 5; // Always a composite source |
| if (node.knockout) |
| filter.flags |= 1 << 6; |
| if (node.type == BevelType.INNER) |
| filter.flags |= 1 << 7; |
| |
| return filter; |
| } |
| |
| protected GradientGlowFilter createGradientGlowFilter( |
| GradientGlowFilterNode node) |
| { |
| GradientGlowFilter filter = new GradientGlowFilter(); |
| |
| if (node.entries != null) |
| { |
| byte count = (byte)node.entries.size(); |
| filter.numcolors = count; |
| filter.gradientColors = new int[count]; |
| filter.gradientRatio = new int[count]; |
| |
| GradRecord[] records = createGradRecords(node.entries); |
| for (int i = 0; i < records.length; i++) |
| { |
| GradRecord record = records[i]; |
| filter.gradientColors[i] = record.color; |
| filter.gradientRatio[i] = record.ratio; |
| } |
| } |
| |
| filter.angle = TypeHelper.fixed(node.angle*Math.PI/180.0); |
| filter.blurX = TypeHelper.fixed(node.blurX); |
| filter.blurY = TypeHelper.fixed(node.blurY); |
| filter.distance = TypeHelper.fixed(node.distance); |
| filter.strength = TypeHelper.fixed8(node.strength); |
| filter.flags = node.quality; // Quality encoded with 4 bits |
| filter.flags |= 1 << 5; // Always a composite source |
| if (node.knockout) |
| filter.flags |= 1 << 6; |
| if (node.inner) |
| filter.flags |= 1 << 7; |
| |
| return filter; |
| } |
| |
| protected void populateGradient(Gradient gradient, |
| List<GradientEntryNode> entries, InterpolationMethod interpolation, |
| SpreadMethod spread) |
| { |
| |
| gradient.records = createGradRecords(entries); |
| |
| if (interpolation != null) |
| gradient.interpolationMode = createInterpolationMode(interpolation); |
| |
| if (spread != null) |
| gradient.spreadMode = 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(); |
| record.color = TypeHelper.colorARGB(entry.color, entry.alpha); |
| record.ratio = TypeHelper.gradientRatio(thisRatio); |
| 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(MaskableNode 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; |
| } |
| } |
| } |
| |
| } |