/******************************************************************************* * Copyright 2014 Geoscience Australia * * Licensed 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 au.gov.ga.earthsci.worldwind.common.layers.delegate.render; import gov.nasa.worldwind.avlist.AVList; import gov.nasa.worldwind.layers.Layer; import gov.nasa.worldwind.render.DrawContext; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.media.opengl.GL2; import org.w3c.dom.Element; import au.gov.ga.earthsci.worldwind.common.layers.delegate.IDelegate; import au.gov.ga.earthsci.worldwind.common.layers.delegate.IRenderDelegate; /** * {@link IRenderDelegate} that allows customization of the blend equation and * function used by OpenGL. * <p/> * Usage: * <p/> * <code><Delegate>Blending</Delegate></code></br> * <code><Delegate>Blending(src,dst)</Delegate></code></br> * <code><Delegate>Blending(src,dst,r,g,b,a)</Delegate></code></br> * <code><Delegate>Blending(src,dst,eq,r,g,b,a)</Delegate></code> * <ul> * <li>src = Specifies how the red, green, blue, and alpha source blending * factors are computed (default = <code>SrcAlpha</code>) * <li>dst = Specifies how the red, green, blue, and alpha destination blending * factors are computed (default = <code>OneMinusSrcAlpha</code>) * <li>eq = Specifies how the source and destination colors are combined * (default = <code>FuncAdd</code>) * <li>r = value to use for the current color's red value (use * <code>alpha</code> for the current layer's opacity) * <li>g = value to use for the current color's green value (use * <code>alpha</code> for the current layer's opacity) * <li>b = value to use for the current color's blue value (use * <code>alpha</code> for the current layer's opacity) * <li>a = value to use for the current color's alpha value (use * <code>alpha</code> for the current layer's opacity) * </ul> * <p/> * Default setup is * <code>Blending(SrcAlpha,OneMinusSrcAlpha,FuncAdd,1.0,1.0,1.0,alpha)</code>. * * @author Michael de Hoog (michael.dehoog@ga.gov.au) */ @SuppressWarnings("nls") public class BlendingRenderDelegate implements IRenderDelegate { protected final static String DEFINITION_STRING = "Blending"; protected final static Map<Integer, String> reverse = new HashMap<Integer, String>() { { put(GL2.GL_ZERO, "Zero"); put(GL2.GL_ONE, "One"); put(GL2.GL_SRC_COLOR, "Src_Color"); put(GL2.GL_ONE_MINUS_SRC_COLOR, "One_Minus_Src_Color"); put(GL2.GL_DST_COLOR, "Dst_Color"); put(GL2.GL_ONE_MINUS_DST_COLOR, "One_Minus_Dst_Color"); put(GL2.GL_SRC_ALPHA, "Src_Alpha"); put(GL2.GL_ONE_MINUS_SRC_ALPHA, "One_Minus_Src_Alpha"); put(GL2.GL_DST_ALPHA, "Dst_Alpha"); put(GL2.GL_ONE_MINUS_DST_ALPHA, "One_Minus_Dst_Alpha"); put(GL2.GL_CONSTANT_COLOR, "Constant_Color"); put(GL2.GL_ONE_MINUS_CONSTANT_COLOR, "One_Minus_Constant_Color"); put(GL2.GL_CONSTANT_ALPHA, "Constant_Alpha"); put(GL2.GL_ONE_MINUS_CONSTANT_ALPHA, "One_Minus_Constant_Alpha"); put(GL2.GL_SRC_ALPHA_SATURATE, "Src_Alpha_Saturate"); put(GL2.GL_FUNC_ADD, "Func_Add"); put(GL2.GL_FUNC_SUBTRACT, "Func_Subtract"); put(GL2.GL_FUNC_REVERSE_SUBTRACT, "Func_Reverse_Subtract"); put(GL2.GL_MIN, "Min"); put(GL2.GL_MAX, "Max"); } }; protected final static Map<String, Integer> constants = new HashMap<String, Integer>() { { for (Map.Entry<Integer, String> entry : reverse.entrySet()) { String key = entry.getValue().toLowerCase(); Integer value = entry.getKey(); put(key, value); put(key.replace("_", ""), value); } } }; protected final static String ALPHA_KEYWORD = "alpha"; protected final int srcFactor; protected final int dstFactor; protected final int equation; protected final double[] color; protected final boolean[] alpha; @SuppressWarnings("unused") private BlendingRenderDelegate() { this(GL2.GL_SRC_ALPHA, GL2.GL_ONE_MINUS_SRC_ALPHA); } public BlendingRenderDelegate(int srcFactor, int dstFactor) { this(srcFactor, dstFactor, new double[] { 1.0, 1.0, 1.0, 1.0 }, new boolean[] { false, false, false, true }); } public BlendingRenderDelegate(int srcFactor, int dstFactor, double[] color, boolean[] alpha) { this(srcFactor, dstFactor, GL2.GL_FUNC_ADD, color, alpha); } public BlendingRenderDelegate(int srcFactor, int dstFactor, int equation, double[] color, boolean[] alpha) { if (color.length != 4 || alpha.length != 4) { throw new IllegalArgumentException(); } this.srcFactor = srcFactor; this.dstFactor = dstFactor; this.equation = equation; this.color = color; this.alpha = alpha; } @Override public void preRender(DrawContext dc) { Layer currentLayer = dc.getCurrentLayer(); double opacity = currentLayer != null ? currentLayer.getOpacity() : 1.0; GL2 gl = dc.getGL().getGL2(); gl.glColor4d( alpha[0] ? opacity : color[0], alpha[1] ? opacity : color[1], alpha[2] ? opacity : color[2], alpha[3] ? opacity : color[3]); gl.glBlendFunc(srcFactor, dstFactor); gl.glBlendEquation(equation); } @Override public void postRender(DrawContext dc) { //color and blending state changes are popped out by layer classes } @Override public String toDefinition(Element layerElement) { String srcFactorString = reverse.get(this.srcFactor); String dstFactorString = reverse.get(this.dstFactor); String equationString = reverse.get(this.equation); return DEFINITION_STRING + "(" + (srcFactorString != null ? srcFactorString : this.srcFactor) + "," + (dstFactorString != null ? dstFactorString : this.dstFactor) + "," + (equationString != null ? equationString : this.equation) + "," + (alpha[0] ? ALPHA_KEYWORD : color[0]) + "," + (alpha[1] ? ALPHA_KEYWORD : color[1]) + "," + (alpha[2] ? ALPHA_KEYWORD : color[2]) + "," + (alpha[3] ? ALPHA_KEYWORD : color[3]) + ")"; } @Override public IDelegate fromDefinition(String definition, Element layerElement, AVList params) { String lower = definition.toLowerCase(); if (lower.startsWith(DEFINITION_STRING.toLowerCase())) { String srcFactor = null, dstFactor = null, equation = null, c0 = null, c1 = null, c2 = null, c3 = null; Pattern pattern1 = Pattern.compile("(?:\\(([\\w]+),([\\w]+),([\\w]+),([\\w.-]+),([\\w.-]+),([\\w.-]+),([\\w.-]+)\\))"); Pattern pattern2 = Pattern.compile("(?:\\(([\\w]+),([\\w]+),([\\w.-]+),([\\w.-]+),([\\w.-]+),([\\w.-]+)\\))"); Pattern pattern3 = Pattern.compile("(?:\\(([\\w]+),([\\w]+)\\))"); Matcher matcher; if ((matcher = pattern1.matcher(lower)).find()) { srcFactor = matcher.group(1); dstFactor = matcher.group(2); equation = matcher.group(3); c0 = matcher.group(4); c1 = matcher.group(5); c2 = matcher.group(6); c3 = matcher.group(7); } else if ((matcher = pattern2.matcher(lower)).find()) { srcFactor = matcher.group(1); dstFactor = matcher.group(2); c0 = matcher.group(3); c1 = matcher.group(4); c2 = matcher.group(5); c3 = matcher.group(6); } else if ((matcher = pattern3.matcher(lower)).find()) { srcFactor = matcher.group(1); dstFactor = matcher.group(2); } int sf = stringToConstant(srcFactor, GL2.GL_SRC_ALPHA); int df = stringToConstant(dstFactor, GL2.GL_ONE_MINUS_SRC_ALPHA); int eq = stringToConstant(equation, GL2.GL_FUNC_ADD); double[] c = new double[] { stringToColor(c0, 1.0), stringToColor(c1, 1.0), stringToColor(c2, 1.0), stringToColor(c3, 1.0) }; boolean[] a = new boolean[] { stringToAlpha(c0, false), stringToAlpha(c1, false), stringToAlpha(c2, false), stringToAlpha(c3, true) }; return new BlendingRenderDelegate(sf, df, eq, c, a); } return null; } private int stringToConstant(String s, int def) { if (s == null) { return def; } if (constants.containsKey(s)) { return constants.get(s); } try { return Integer.parseInt(s); } catch (NumberFormatException e) { return def; } } private double stringToColor(String s, double def) { try { return Double.parseDouble(s); } catch (NumberFormatException e) { return def; } } private boolean stringToAlpha(String s, boolean def) { return s == null ? def : ALPHA_KEYWORD.equals(s); } }