| /* |
| |
| 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.batik.svggen; |
| |
| import java.awt.AlphaComposite; |
| import java.util.HashMap; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.batik.ext.awt.g2d.GraphicContext; |
| import org.w3c.dom.Element; |
| |
| /** |
| * Utility class that converts an AlphaComposite object into |
| * a set of SVG properties and definitions. Here is |
| * how AlphaComposites are mapped to SVG: |
| * + AlphaComposite.SRC_OVER with extra alpha is mapped |
| * to the opacity attribute |
| * + AlphaComposite's other rules are translated into |
| * predefined filter effects. |
| * One of the big differences between AlphaComposite and |
| * the SVG feComposite filter is that feComposite does not |
| * have the notion of extra alpha applied to the source. |
| * The extra alpha equivalent is obtained by setting the |
| * opacity property on the nodes to be composited. |
| * |
| * @author <a href="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a> |
| * @version $Id$ |
| * @see org.apache.batik.svggen.SVGAlphaComposite |
| */ |
| public class SVGAlphaComposite extends AbstractSVGConverter { |
| /** |
| * Map of all possible AlphaComposite filter equivalents |
| */ |
| private Map compositeDefsMap = new HashMap(); |
| |
| /** |
| * By default, access to the background is not required. |
| */ |
| private boolean backgroundAccessRequired = false; |
| |
| /** |
| * @param generatorContext for use by SVGAlphaComposite to build Elements |
| */ |
| public SVGAlphaComposite(SVGGeneratorContext generatorContext) { |
| super(generatorContext); |
| |
| // |
| // Initialize map of AlphaComposite filter definitions |
| // |
| compositeDefsMap.put(AlphaComposite.Src, |
| compositeToSVG(AlphaComposite.Src)); |
| compositeDefsMap.put(AlphaComposite.SrcIn, |
| compositeToSVG(AlphaComposite.SrcIn)); |
| compositeDefsMap.put(AlphaComposite.SrcOut, |
| compositeToSVG(AlphaComposite.SrcOut)); |
| compositeDefsMap.put(AlphaComposite.DstIn, |
| compositeToSVG(AlphaComposite.DstIn)); |
| compositeDefsMap.put(AlphaComposite.DstOut, |
| compositeToSVG(AlphaComposite.DstOut)); |
| compositeDefsMap.put(AlphaComposite.DstOver, |
| compositeToSVG(AlphaComposite.DstOver)); |
| compositeDefsMap.put(AlphaComposite.Clear, |
| compositeToSVG(AlphaComposite.Clear)); |
| } |
| |
| /** |
| * @return set of all AlphaComposite filter definitions |
| */ |
| public List getAlphaCompositeFilterSet() { |
| return new LinkedList(compositeDefsMap.values()); |
| } |
| |
| /** |
| * @return true if background access is required for any |
| * of the converted AlphaComposite rules |
| */ |
| public boolean requiresBackgroundAccess() { |
| return backgroundAccessRequired; |
| } |
| |
| /** |
| * Converts part or all of the input GraphicContext into |
| * a set of attribute/value pairs and related definitions |
| * |
| * @param gc GraphicContext to be converted |
| * @return descriptor of the attributes required to represent |
| * some or all of the GraphicContext state, along |
| * with the related definitions |
| * @see org.apache.batik.svggen.SVGDescriptor |
| */ |
| public SVGDescriptor toSVG(GraphicContext gc) { |
| return toSVG((AlphaComposite)gc.getComposite()); |
| } |
| |
| /** |
| * @param composite the AlphaComposite object to convert to SVG |
| * @return an SVGCompositeDescriptor that defines how to map the |
| * input composite in SVG |
| */ |
| public SVGCompositeDescriptor toSVG(AlphaComposite composite) { |
| SVGCompositeDescriptor compositeDesc = |
| (SVGCompositeDescriptor)descMap.get(composite); |
| |
| if(compositeDesc == null){ |
| // Process the composite opacity |
| String opacityValue = doubleString(composite.getAlpha()); |
| |
| // For all rules different than SRC_OVER, a filter is |
| // needed to represent the composition rule. |
| String filterValue = null; |
| Element filterDef = null; |
| if(composite.getRule() != AlphaComposite.SRC_OVER){ |
| // Note that the extra alpha is ignored by using the |
| // majorComposite. The extra alpha is already represented |
| // by the SVG_OPACITY_ATTRIBUTE value. |
| AlphaComposite majorComposite = |
| AlphaComposite.getInstance(composite.getRule()); |
| filterDef = (Element)compositeDefsMap.get(majorComposite); |
| defSet.add(filterDef); |
| |
| // Process the filter value |
| StringBuffer filterAttrBuf = new StringBuffer(URL_PREFIX); |
| filterAttrBuf.append(SIGN_POUND); |
| filterAttrBuf.append(filterDef.getAttributeNS(null, SVG_ID_ATTRIBUTE)); |
| filterAttrBuf.append(URL_SUFFIX); |
| |
| filterValue = filterAttrBuf.toString(); |
| } else |
| filterValue = SVG_NONE_VALUE; |
| |
| compositeDesc = new SVGCompositeDescriptor(opacityValue, filterValue, |
| filterDef); |
| |
| descMap.put(composite, compositeDesc); |
| } |
| |
| if (composite.getRule() != AlphaComposite.SRC_OVER) |
| backgroundAccessRequired = true; |
| |
| return compositeDesc; |
| } |
| |
| /** |
| * @param composite AlphaComposite to convert to a filter effect |
| * @exception Error if an AlphaComposite with SRC_OVER rule in passed to |
| * this method. |
| */ |
| private Element compositeToSVG(AlphaComposite composite) { |
| // operator is equivalent to rule |
| String operator = null; |
| |
| // input1 is equivalent to Src |
| String input1 = null; |
| |
| // input2 is equivalent to Dst |
| String input2 = null; |
| |
| // k2 is used only for arithmetic |
| // to obtain the equivalent of SRC |
| String k2 = "0"; |
| |
| // ID used to identify the composite |
| String id = null; |
| |
| switch(composite.getRule()){ |
| case AlphaComposite.CLEAR: |
| operator = SVG_ARITHMETIC_VALUE; |
| input1 = SVG_SOURCE_GRAPHIC_VALUE; |
| input2 = SVG_BACKGROUND_IMAGE_VALUE; |
| id = ID_PREFIX_ALPHA_COMPOSITE_CLEAR; |
| break; |
| case AlphaComposite.SRC: |
| operator = SVG_ARITHMETIC_VALUE; |
| input1 = SVG_SOURCE_GRAPHIC_VALUE; |
| input2 = SVG_BACKGROUND_IMAGE_VALUE; |
| id = ID_PREFIX_ALPHA_COMPOSITE_SRC; |
| k2 = SVG_DIGIT_ONE_VALUE; |
| break; |
| case AlphaComposite.SRC_IN: |
| operator = SVG_IN_VALUE; |
| input1 = SVG_SOURCE_GRAPHIC_VALUE; |
| input2 = SVG_BACKGROUND_IMAGE_VALUE; |
| id = ID_PREFIX_ALPHA_COMPOSITE_SRC_IN; |
| break; |
| case AlphaComposite.SRC_OUT: |
| operator = SVG_OUT_VALUE; |
| input1 = SVG_SOURCE_GRAPHIC_VALUE; |
| input2 = SVG_BACKGROUND_IMAGE_VALUE; |
| id = ID_PREFIX_ALPHA_COMPOSITE_SRC_OUT; |
| break; |
| case AlphaComposite.DST_IN: |
| operator = SVG_IN_VALUE; |
| input2 = SVG_SOURCE_GRAPHIC_VALUE; |
| input1 = SVG_BACKGROUND_IMAGE_VALUE; |
| id = ID_PREFIX_ALPHA_COMPOSITE_DST_IN; |
| break; |
| case AlphaComposite.DST_OUT: |
| operator = SVG_OUT_VALUE; |
| input2 = SVG_SOURCE_GRAPHIC_VALUE; |
| input1 = SVG_BACKGROUND_IMAGE_VALUE; |
| id = ID_PREFIX_ALPHA_COMPOSITE_DST_OUT; |
| break; |
| case AlphaComposite.DST_OVER: |
| operator = SVG_OVER_VALUE; |
| input2 = SVG_SOURCE_GRAPHIC_VALUE; |
| input1 = SVG_BACKGROUND_IMAGE_VALUE; |
| id = ID_PREFIX_ALPHA_COMPOSITE_DST_OVER; |
| break; |
| default: |
| throw new Error("invalid rule:" + composite.getRule() ); |
| } |
| |
| Element compositeFilter = |
| generatorContext.domFactory.createElementNS(SVG_NAMESPACE_URI, |
| SVG_FILTER_TAG); |
| compositeFilter.setAttributeNS(null, SVG_ID_ATTRIBUTE, id); |
| compositeFilter.setAttributeNS(null, SVG_FILTER_UNITS_ATTRIBUTE, |
| SVG_OBJECT_BOUNDING_BOX_VALUE); |
| compositeFilter.setAttributeNS(null, SVG_X_ATTRIBUTE, SVG_ZERO_PERCENT_VALUE); |
| compositeFilter.setAttributeNS(null, SVG_Y_ATTRIBUTE, SVG_ZERO_PERCENT_VALUE); |
| compositeFilter.setAttributeNS(null, SVG_WIDTH_ATTRIBUTE, |
| SVG_HUNDRED_PERCENT_VALUE); |
| compositeFilter.setAttributeNS(null, SVG_HEIGHT_ATTRIBUTE, |
| SVG_HUNDRED_PERCENT_VALUE); |
| |
| Element feComposite = |
| generatorContext.domFactory.createElementNS(SVG_NAMESPACE_URI, |
| SVG_FE_COMPOSITE_TAG); |
| feComposite.setAttributeNS(null, SVG_OPERATOR_ATTRIBUTE, operator); |
| feComposite.setAttributeNS(null, SVG_IN_ATTRIBUTE, input1); |
| feComposite.setAttributeNS(null, SVG_IN2_ATTRIBUTE, input2); |
| feComposite.setAttributeNS(null, SVG_K2_ATTRIBUTE, k2); |
| feComposite.setAttributeNS(null, SVG_RESULT_ATTRIBUTE, SVG_COMPOSITE_VALUE); |
| |
| Element feFlood = |
| generatorContext.domFactory.createElementNS(SVG_NAMESPACE_URI, |
| SVG_FE_FLOOD_TAG); |
| feFlood.setAttributeNS(null, SVG_FLOOD_COLOR_ATTRIBUTE, "white"); |
| feFlood.setAttributeNS(null, SVG_FLOOD_OPACITY_ATTRIBUTE, "1"); |
| feFlood.setAttributeNS(null, SVG_RESULT_ATTRIBUTE, SVG_FLOOD_VALUE); |
| |
| |
| Element feMerge = |
| generatorContext.domFactory.createElementNS(SVG_NAMESPACE_URI, |
| SVG_FE_MERGE_TAG); |
| Element feMergeNodeFlood = |
| generatorContext.domFactory.createElementNS(SVG_NAMESPACE_URI, |
| SVG_FE_MERGE_NODE_TAG); |
| feMergeNodeFlood.setAttributeNS(null, SVG_IN_ATTRIBUTE, SVG_FLOOD_VALUE); |
| Element feMergeNodeComposite = |
| generatorContext.domFactory.createElementNS(SVG_NAMESPACE_URI, |
| SVG_FE_MERGE_NODE_TAG); |
| feMergeNodeComposite.setAttributeNS(null, SVG_IN_ATTRIBUTE, |
| SVG_COMPOSITE_VALUE); |
| |
| feMerge.appendChild(feMergeNodeFlood); |
| feMerge.appendChild(feMergeNodeComposite); |
| |
| compositeFilter.appendChild(feFlood); |
| compositeFilter.appendChild(feComposite); |
| compositeFilter.appendChild(feMerge); |
| |
| return compositeFilter; |
| } |
| } |