/* * * 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.internal.fxg.dom; import static org.apache.flex.compiler.fxg.FXGConstants.*; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.apache.flex.compiler.fxg.FXGVersion; import org.apache.flex.compiler.fxg.dom.IFXGNode; import org.apache.flex.compiler.fxg.logging.FXGLog; import org.apache.flex.compiler.fxg.logging.IFXGLogger; 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.BlendMode; import org.apache.flex.compiler.internal.fxg.dom.types.MaskType; import org.apache.flex.compiler.internal.fxg.types.FXGMatrix; import org.apache.flex.compiler.problems.FXGInvalidChildColorTransformNodeProblem; import org.apache.flex.compiler.problems.FXGInvalidChildMatrixNodeProblem; import org.apache.flex.compiler.problems.FXGInvalidNodeAttributeProblem; import org.apache.flex.compiler.problems.FXGUnknownAttributeValueProblem; import org.apache.flex.compiler.problems.ICompilerProblem; /** * Base class for all nodes that present graphic content or represent groups * of graphic content. Children inherit parent context information for * transforms, blend modes and masks. */ public abstract class GraphicContentNode extends AbstractFXGNode implements IMaskableNode { //-------------------------------------------------------------------------- // // Attributes // //-------------------------------------------------------------------------- //------------ // id //------------ protected String id = "undefined"; /** * An id attribute provides a well defined name to a text node. */ public String getId() { return id; } /** * Sets the node id. * @param value - the node id as a String. */ public void setId(String value) { id = value; } public boolean visible = true; public double x = 0.0; public double y = 0.0; public double scaleX = 1.0; public double scaleY = 1.0; public double rotation = 0.0; public double alpha = 1.0; public BlendMode blendMode = BlendMode.AUTO; public MaskType maskType = MaskType.CLIP; public boolean luminosityClip = false; public boolean luminosityInvert = false; protected boolean translateSet; protected boolean scaleSet; protected boolean rotationSet; protected boolean alphaSet; protected boolean maskTypeSet; //is part of clip mask public boolean isPartofClipMask = false; //-------------------------------------------------------------------------- // // Children // //-------------------------------------------------------------------------- public List<IFilterNode> filters; public IMaskingNode mask; public MatrixNode matrix; public ColorTransformNode colorTransform; @Override public List<IFXGNode> getChildren() { List<IFXGNode> children = new ArrayList<IFXGNode>(); children.addAll(super.getChildren()); if(filters != null) children.addAll(filters); children.add(mask); children.add(matrix); children.add(colorTransform); return children; } //-------------------------------------------------------------------------- // // IFXGNode Implementation // //-------------------------------------------------------------------------- /** * Adds an FXG child node to this node. * <p> * Graphic content nodes support child property nodes <filter>, * <mask>, <matrix>, or <colorTransform>. * </p> * * @param child - a child FXG node to be added to this node. * @param problems problem collection used to collect problems occurred within this method */ @Override public void addChild(IFXGNode child, Collection<ICompilerProblem> problems) { if (child instanceof IFilterNode) { if (filters == null) filters = new ArrayList<IFilterNode>(); filters.add((IFilterNode)child); } else if (child instanceof MaskPropertyNode) { mask = ((MaskPropertyNode)child).mask; if (mask instanceof GraphicContentNode) { ((GraphicContentNode)mask).setParentGraphicContext(createGraphicContext()); } } else if (child instanceof MatrixNode) { if (translateSet || scaleSet || rotationSet) { //Cannot supply a matrix child if transformation attributes were provided problems.add(new FXGInvalidChildMatrixNodeProblem(getDocumentPath(), child.getStartLine(), child.getStartColumn())); return; } matrix = (MatrixNode)child; } else if (child instanceof ColorTransformNode) { if (alphaSet) { //Cannot supply a colorTransform child if alpha attribute was provided. problems.add(new FXGInvalidChildColorTransformNodeProblem(getDocumentPath(), child.getStartLine(), child.getStartColumn())); return; } colorTransform = (ColorTransformNode)child; } else { super.addChild(child, problems); } } /** * Sets an FXG attribute on this FXG node. * <p> * Graphic content nodes support the following attributes: * <ul> * <li><b>rotation</b> (ASDegrees): Defaults to 0.</li> * <li><b>scaleX</b> (Number): Defaults to 1.</li> * <li><b>scaleY</b> (Number): Defaults to 1.</li> * <li><b>x</b> (Number): The horizontal placement of the left edge of the * text box, relative to the parent grouping element. Defaults to 0.</li> * <li><b>y</b> (Number): The vertical placement of the top edge of the * text box, relative to the parent grouping element. Defaults to 0.</li> * <li><b>blendMode</b> (String): [normal, add, alpha, darken, difference, * erase, hardlight, invert, layer, lighten, multiply, normal, subtract, * screen, overlay, auto, colordodge, colorburn, exclusion, softlight, * hue, saturation, color, luminosity] Defaults to auto.</li> * <li><b>alpha</b> (ASAlpha): Defaults to 1.</li> * <li><b>maskType</b> (String):[clip, alpha]: Defaults to clip.</li> * <li><b>visible</b> (Boolean): Whether or not the text box is visible. * Defaults to true.</li> * </ul> * </p> * <p> * Graphic content nodes also support an id attribute. * </p> * * @param name - the unqualified attribute name * @param value - the attribute value * @param problems problem collection used to collect problems occurred within this method */ @Override public void setAttribute(String name, String value, Collection<ICompilerProblem> problems) { if (FXG_X_ATTRIBUTE.equals(name)) { x = DOMParserHelper.parseDouble(this, value, name, x, problems); translateSet = true; } else if (FXG_Y_ATTRIBUTE.equals(name)) { y = DOMParserHelper.parseDouble(this, value, name, y, problems); translateSet = true; } else if (FXG_ROTATION_ATTRIBUTE.equals(name)) { rotation = DOMParserHelper.parseDouble(this, value, name, rotation, problems); rotationSet = true; } else if (FXG_SCALEX_ATTRIBUTE.equals(name)) { scaleX = DOMParserHelper.parseDouble(this, value, name, scaleX, problems); scaleSet = true; } else if (FXG_SCALEY_ATTRIBUTE.equals(name)) { scaleY = DOMParserHelper.parseDouble(this, value, name, scaleY, problems); scaleSet = true; } else if (FXG_ALPHA_ATTRIBUTE.equals(name)) { alpha = DOMParserHelper.parseDouble(this, value, name, ALPHA_MIN_INCLUSIVE, ALPHA_MAX_INCLUSIVE, alpha, problems); alphaSet = true; } else if (FXG_BLENDMODE_ATTRIBUTE.equals(name)) { blendMode = parseBlendMode(value, blendMode, problems); } else if (FXG_VISIBLE_ATTRIBUTE.equals(name)) { visible = DOMParserHelper.parseBoolean(this, value, name, visible, problems); } else if (FXG_ID_ATTRIBUTE.equals(name)) { id = value; } else if (FXG_MASKTYPE_ATTRIBUTE.equals(name)) { maskType = DOMParserHelper.parseMaskType(this, value, maskType, problems); maskTypeSet = true; } else if (getFileVersion().equalTo(FXGVersion.v1_0)) { // Rest of the attributes are not supported by FXG 1.0 // Attribute {0} not supported by node {1}. problems.add(new FXGInvalidNodeAttributeProblem(getDocumentPath(), getStartLine(), getStartColumn(), name, getNodeName())); return; } else if (FXG_LUMINOSITYCLIP_ATTRIBUTE.equals(name)) { luminosityClip = DOMParserHelper.parseBoolean(this, value, name, luminosityClip, problems); } else if (FXG_LUMINOSITYINVERT_ATTRIBUTE.equals(name)) { luminosityInvert = DOMParserHelper.parseBoolean(this, value, name, luminosityInvert, problems); } else { super.setAttribute(name, value, problems); } } //-------------------------------------------------------------------------- // // IMaskableNode Implementation // //-------------------------------------------------------------------------- @Override public IMaskingNode getMask() { return mask; } @Override public MaskType getMaskType() { return maskType; } @Override public boolean getLuminosityClip() { return luminosityClip; } @Override public boolean getLuminosityInvert() { return luminosityInvert; } //-------------------------------------------------------------------------- // // Helper Methods // //-------------------------------------------------------------------------- private GraphicContext parentGraphicContext; public GraphicContext createGraphicContext() { GraphicContext graphicContext = new GraphicContext(); if (parentGraphicContext != null) graphicContext.scalingGrid = parentGraphicContext.scalingGrid; FXGMatrix transform = graphicContext.getTransform(); if (matrix != null) { FXGMatrix t = new FXGMatrix(matrix); transform.concat(t); } else { if (scaleSet) transform.scale(scaleX, scaleY); if (rotationSet) transform.rotate(rotation); if (translateSet) transform.translate(x, y); } if (colorTransform != null) { graphicContext.colorTransform = colorTransform; } else if (alphaSet) { if (graphicContext.colorTransform == null) graphicContext.colorTransform = new ColorTransformNode(); graphicContext.colorTransform.alphaMultiplier = alpha; } graphicContext.blendMode = blendMode; if (filters != null) graphicContext.addFilters(filters); if (maskTypeSet) graphicContext.maskType = maskType; else if (parentGraphicContext != null) graphicContext.maskType = parentGraphicContext.maskType; return graphicContext; } public void setParentGraphicContext(GraphicContext context) { parentGraphicContext = context; } /** * Convert an FXG String value to a BlendMode enumeration. * * @param value - the FXG String value * @param problems problem collection used to collect problems occurred within this method * @return the matching BlendMode */ protected BlendMode parseBlendMode(String value, BlendMode defMode, Collection<ICompilerProblem> problems) { FXGVersion fileVersion = ((GraphicNode)(this.getDocumentNode())).getVersion(); if (FXG_BLENDMODE_ADD_VALUE.equals(value)) { return BlendMode.ADD; } else if (FXG_BLENDMODE_ALPHA_VALUE.equals(value)) { return BlendMode.ALPHA; } else if (FXG_BLENDMODE_DARKEN_VALUE.equals(value)) { return BlendMode.DARKEN; } else if (FXG_BLENDMODE_DIFFERENCE_VALUE.equals(value)) { return BlendMode.DIFFERENCE; } else if (FXG_BLENDMODE_ERASE_VALUE.equals(value)) { return BlendMode.ERASE; } else if (FXG_BLENDMODE_HARDLIGHT_VALUE.equals(value)) { return BlendMode.HARDLIGHT; } else if (FXG_BLENDMODE_INVERT_VALUE.equals(value)) { return BlendMode.INVERT; } else if (FXG_BLENDMODE_LAYER_VALUE.equals(value)) { return BlendMode.LAYER; } else if (FXG_BLENDMODE_LIGHTEN_VALUE.equals(value)) { return BlendMode.LIGHTEN; } else if (FXG_BLENDMODE_MULTIPLY_VALUE.equals(value)) { return BlendMode.MULTIPLY; } else if (FXG_BLENDMODE_NORMAL_VALUE.equals(value)) { return BlendMode.NORMAL; } else if (FXG_BLENDMODE_OVERLAY_VALUE.equals(value)) { return BlendMode.OVERLAY; } else if (FXG_BLENDMODE_SCREEN_VALUE.equals(value)) { return BlendMode.SCREEN; } else if (FXG_BLENDMODE_SUBTRACT_VALUE.equals(value)) { return BlendMode.SUBTRACT; } else if (fileVersion.equalTo(FXGVersion.v1_0)) { // Rest of the blend modes are unknown for FXG 1.0 //Unknown blend mode: {0}. problems.add(new FXGUnknownAttributeValueProblem(getDocumentPath(), getStartLine(), getStartColumn(), FXG_BLENDMODE_ATTRIBUTE, value)); } else if (FXG_BLENDMODE_COLORDOGE_VALUE.equals(value)) { return BlendMode.COLORDODGE; } else if (FXG_BLENDMODE_COLORBURN_VALUE.equals(value)) { return BlendMode.COLORBURN; } else if (FXG_BLENDMODE_EXCLUSION_VALUE.equals(value)) { return BlendMode.EXCLUSION; } else if (FXG_BLENDMODE_SOFTLIGHT_VALUE.equals(value)) { return BlendMode.SOFTLIGHT; } else if (FXG_BLENDMODE_HUE_VALUE.equals(value)) { return BlendMode.HUE; } else if (FXG_BLENDMODE_SATURATION_VALUE.equals(value)) { return BlendMode.SATURATION; } else if (FXG_BLENDMODE_COLOR_VALUE.equals(value)) { return BlendMode.COLOR; } else if (FXG_BLENDMODE_LUMINOSITY_VALUE.equals(value)) { return BlendMode.LUMINOSITY; } else if (FXG_BLENDMODE_AUTO_VALUE.equals(value)) { return BlendMode.AUTO; } else { if (isVersionGreaterThanCompiler()) { // Warning: Minor version of this FXG file is greater than minor // version supported by this compiler. Log a warning for an unknown // blend mode. FXGLog.getLogger().log(IFXGLogger.WARN, "UnknownBlendMode", null, getDocumentPath(), startLine, startColumn); } else { //Unknown blend mode: {0} for FXGVersion 2.0. problems.add(new FXGUnknownAttributeValueProblem(getDocumentPath(), getStartLine(), getStartColumn(), FXG_BLENDMODE_ATTRIBUTE, value)); } } return defMode; } /** * Convert discreet transform attributes to child matrix. This allows * concatenation of another matrix. */ public void convertTransformAttrToMatrix(Collection<ICompilerProblem> problems) { try { MatrixNode matrixNode = MatrixNode.class.newInstance(); // Convert discreet transform attributes to FXGMatrix. FXGMatrix matrix = FXGMatrix.convertToMatrix(scaleX, scaleY, rotation, x, y); // Set matrix attributes to FXGMatrix values. matrix.setMatrixNodeValue(matrixNode); // Reset all discreet transform attributes since matrix // and discreet transform attributes cannot coexist. resetTransformAttr(); // Add child matrix to the node. this.addChild(matrixNode, problems); } catch (Exception e) { problems.add(new FXGInvalidChildMatrixNodeProblem(mask.getDocumentPath(), mask.getStartLine(), mask.getStartColumn())); } } /** * Reset discreet transform attributes to their default value. This allows * child matrix can be set instead. */ private void resetTransformAttr() { x = 0.0; y = 0.0; scaleX = 1.0; scaleY = 1.0; rotation = 0.0; translateSet = false; scaleSet = false; rotationSet = false; } }