001/* 002Copyright 2006 Jerry Huxtable 003 004Licensed under the Apache License, Version 2.0 (the "License"); 005you may not use this file except in compliance with the License. 006You may obtain a copy of the License at 007 008 http://www.apache.org/licenses/LICENSE-2.0 009 010Unless required by applicable law or agreed to in writing, software 011distributed under the License is distributed on an "AS IS" BASIS, 012WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013See the License for the specific language governing permissions and 014limitations under the License. 015*/ 016 017package com.jhlabs.image; 018 019import java.awt.*; 020import java.awt.geom.*; 021import java.awt.image.*; 022import com.jhlabs.math.*; 023 024/** 025 * A filter which produces a water ripple distortion. 026 */ 027public class WaterFilter extends TransformFilter { 028 029 private float wavelength = 16; 030 private float amplitude = 10; 031 private float phase = 0; 032 private float centreX = 0.5f; 033 private float centreY = 0.5f; 034 private float radius = 50; 035 036 private float radius2 = 0; 037 private float icentreX; 038 private float icentreY; 039 040 public WaterFilter() { 041 setEdgeAction( CLAMP ); 042 } 043 044 /** 045 * Set the wavelength of the ripples. 046 * @param wavelength the wavelength 047 * @see #getWavelength 048 */ 049 public void setWavelength(float wavelength) { 050 this.wavelength = wavelength; 051 } 052 053 /** 054 * Get the wavelength of the ripples. 055 * @return the wavelength 056 * @see #setWavelength 057 */ 058 public float getWavelength() { 059 return wavelength; 060 } 061 062 /** 063 * Set the amplitude of the ripples. 064 * @param amplitude the amplitude 065 * @see #getAmplitude 066 */ 067 public void setAmplitude(float amplitude) { 068 this.amplitude = amplitude; 069 } 070 071 /** 072 * Get the amplitude of the ripples. 073 * @return the amplitude 074 * @see #setAmplitude 075 */ 076 public float getAmplitude() { 077 return amplitude; 078 } 079 080 /** 081 * Set the phase of the ripples. 082 * @param phase the phase 083 * @see #getPhase 084 */ 085 public void setPhase(float phase) { 086 this.phase = phase; 087 } 088 089 /** 090 * Get the phase of the ripples. 091 * @return the phase 092 * @see #setPhase 093 */ 094 public float getPhase() { 095 return phase; 096 } 097 098 /** 099 * Set the centre of the effect in the X direction as a proportion of the image size. 100 * @param centreX the center 101 * @see #getCentreX 102 */ 103 public void setCentreX( float centreX ) { 104 this.centreX = centreX; 105 } 106 107 /** 108 * Get the centre of the effect in the X direction as a proportion of the image size. 109 * @return the center 110 * @see #setCentreX 111 */ 112 public float getCentreX() { 113 return centreX; 114 } 115 116 /** 117 * Set the centre of the effect in the Y direction as a proportion of the image size. 118 * @param centreY the center 119 * @see #getCentreY 120 */ 121 public void setCentreY( float centreY ) { 122 this.centreY = centreY; 123 } 124 125 /** 126 * Get the centre of the effect in the Y direction as a proportion of the image size. 127 * @return the center 128 * @see #setCentreY 129 */ 130 public float getCentreY() { 131 return centreY; 132 } 133 134 /** 135 * Set the centre of the effect as a proportion of the image size. 136 * @param centre the center 137 * @see #getCentre 138 */ 139 public void setCentre( Point2D centre ) { 140 this.centreX = (float)centre.getX(); 141 this.centreY = (float)centre.getY(); 142 } 143 144 /** 145 * Get the centre of the effect as a proportion of the image size. 146 * @return the center 147 * @see #setCentre 148 */ 149 public Point2D getCentre() { 150 return new Point2D.Float( centreX, centreY ); 151 } 152 153 /** 154 * Set the radius of the effect. 155 * @param radius the radius 156 * @min-value 0 157 * @see #getRadius 158 */ 159 public void setRadius(float radius) { 160 this.radius = radius; 161 } 162 163 /** 164 * Get the radius of the effect. 165 * @return the radius 166 * @see #setRadius 167 */ 168 public float getRadius() { 169 return radius; 170 } 171 172 private boolean inside(int v, int a, int b) { 173 return a <= v && v <= b; 174 } 175 176 public BufferedImage filter( BufferedImage src, BufferedImage dst ) { 177 icentreX = src.getWidth() * centreX; 178 icentreY = src.getHeight() * centreY; 179 if ( radius == 0 ) 180 radius = Math.min(icentreX, icentreY); 181 radius2 = radius*radius; 182 return super.filter( src, dst ); 183 } 184 185 protected void transformInverse(int x, int y, float[] out) { 186 float dx = x-icentreX; 187 float dy = y-icentreY; 188 float distance2 = dx*dx + dy*dy; 189 if (distance2 > radius2) { 190 out[0] = x; 191 out[1] = y; 192 } else { 193 float distance = (float)Math.sqrt(distance2); 194 float amount = amplitude * (float)Math.sin(distance / wavelength * ImageMath.TWO_PI - phase); 195 amount *= (radius-distance)/radius; 196 if ( distance != 0 ) 197 amount *= wavelength/distance; 198 out[0] = x + dx*amount; 199 out[1] = y + dy*amount; 200 } 201 } 202 203 public String toString() { 204 return "Distort/Water Ripples..."; 205 } 206 207}