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