001 /* 002 * 003 004 Licensed under the Apache License, Version 2.0 (the "License"); 005 you may not use this file except in compliance with the License. 006 You may obtain a copy of the License at 007 008 http://www.apache.org/licenses/LICENSE-2.0 009 010 Unless required by applicable law or agreed to in writing, software 011 distributed under the License is distributed on an "AS IS" BASIS, 012 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 See the License for the specific language governing permissions and 014 limitations under the License. 015 */ 016 017 package railo.runtime.img.filter;import java.awt.AlphaComposite; 018 import java.awt.Graphics2D; 019 import java.awt.image.BufferedImage; 020 import java.awt.image.BufferedImageOp; 021 import java.beans.BeanInfo; 022 import java.beans.IntrospectionException; 023 import java.beans.Introspector; 024 import java.beans.PropertyDescriptor; 025 import java.lang.reflect.Method; 026 027 import railo.runtime.engine.ThreadLocalPageContext; 028 import railo.runtime.exp.FunctionException; 029 import railo.runtime.exp.PageException; 030 import railo.runtime.img.ImageUtil; 031 import railo.runtime.type.KeyImpl; 032 import railo.runtime.type.Struct; 033 import railo.runtime.type.util.CollectionUtil; 034 035 /** 036 * A filter which uses another filter to perform a transition. 037 * e.g. to create a blur transition, you could write: new TransitionFilter( new BoxBlurFilter(), "radius", 0, 100 ); 038 */ 039 public class TransitionFilter extends AbstractBufferedImageOp implements DynFiltering { 040 041 private float transition = 0; 042 private BufferedImage destination; 043 private String property; 044 private Method method; 045 046 /** 047 * The filter used for the transition. 048 */ 049 protected BufferedImageOp filter; 050 051 /** 052 * The start value for the filter property. 053 */ 054 protected float minValue; 055 056 /** 057 * The end value for the filter property. 058 */ 059 protected float maxValue; 060 061 062 /** 063 * Construct a TransitionFilter. 064 * @param filter the filter to use 065 * @param property the filter property which is changed over the transition 066 * @param minValue the start value for the filter property 067 * @param maxValue the end value for the filter property 068 */ 069 public TransitionFilter( BufferedImageOp filter, String property, float minValue, float maxValue ) { 070 this.filter = filter; 071 this.property = property; 072 this.minValue = minValue; 073 this.maxValue = maxValue; 074 try { 075 BeanInfo info = Introspector.getBeanInfo( filter.getClass() ); 076 PropertyDescriptor[] pds = info.getPropertyDescriptors(); 077 for ( int i = 0; i < pds.length; i++ ) { 078 PropertyDescriptor pd = pds[i]; 079 if ( property.equals( pd.getName() ) ) { 080 method = pd.getWriteMethod(); 081 break; 082 } 083 } 084 if ( method == null ) 085 throw new IllegalArgumentException( "No such property in object: "+property ); 086 } 087 catch (IntrospectionException e) { 088 throw new IllegalArgumentException( e.toString() ); 089 } 090 } 091 092 /** 093 * Set the transition of the image in the range 0..1. 094 * @param transition the transition 095 * @min-value 0 096 * @max-value 1 097 * @see #getTransition 098 */ 099 public void setTransition( float transition ) { 100 this.transition = transition; 101 } 102 103 /** 104 * Get the transition of the image. 105 * @return the transition 106 * @see #setTransition 107 */ 108 public float getTransition() { 109 return transition; 110 } 111 112 /** 113 * Set the destination image. 114 * @param destination the destination image 115 * @see #getDestination 116 */ 117 public void setDestination( BufferedImage destination ) { 118 this.destination = destination; 119 } 120 121 /** 122 * Get the destination image. 123 * @return the destination image 124 * @see #setDestination 125 */ 126 public BufferedImage getDestination() { 127 return destination; 128 } 129 130 /* 131 public void setFilter( BufferedImageOp filter ) { 132 this.filter = filter; 133 } 134 135 public int getFilter() { 136 return filter; 137 } 138 */ 139 140 /** 141 * Prepare the filter for the transiton at a given time. 142 * The default implementation sets the given filter property, but you could override this method to make other changes. 143 * @param transition the transition time in the range 0 - 1 144 */ 145 public void prepareFilter( float transition ) { 146 try { 147 method.invoke( filter, new Object[] { new Float( transition ) } ); 148 } 149 catch ( Exception e ) { 150 throw new IllegalArgumentException("Error setting value for property: "+property); 151 } 152 } 153 154 public BufferedImage filter( BufferedImage src, BufferedImage dst ) { 155 if ( dst == null ) 156 dst = createCompatibleDestImage( src, null ); 157 if ( destination == null ) 158 return dst; 159 160 float itransition = 1-transition; 161 162 Graphics2D g = dst.createGraphics(); 163 if ( transition != 1 ) { 164 float t = minValue + transition * ( maxValue-minValue ); 165 prepareFilter( t ); 166 g.drawImage( src, filter, 0, 0 ); 167 } 168 if ( transition != 0 ) { 169 g.setComposite( AlphaComposite.getInstance( AlphaComposite.SRC_OVER, transition ) ); 170 float t = minValue + itransition * ( maxValue-minValue ); 171 prepareFilter( t ); 172 g.drawImage( destination, filter, 0, 0 ); 173 } 174 g.dispose(); 175 176 return dst; 177 } 178 179 public String toString() { 180 return "Transitions/Transition..."; 181 } 182 public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src); 183 Object o; 184 if((o=parameters.removeEL(KeyImpl.init("Transition")))!=null)setTransition(ImageFilterUtil.toFloatValue(o,"Transition")); 185 if((o=parameters.removeEL(KeyImpl.init("destination")))!=null)setDestination(ImageFilterUtil.toBufferedImage(o,"destination")); 186 187 // check for arguments not supported 188 if(parameters.size()>0) { 189 throw new FunctionException(ThreadLocalPageContext.get(), "ImageFilter", 3, "parameters", "the parameter"+(parameters.size()>1?"s":"")+" ["+CollectionUtil.getKeyList(parameters,", ")+"] "+(parameters.size()>1?"are":"is")+" not allowed, only the following parameters are supported [Transition]"); 190 } 191 192 return filter(src, dst); 193 } 194 }