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.*; 022 023/** 024 * A filter which performs the popular whirl-and-pinch distortion effect. 025 */ 026public class PinchFilter extends TransformFilter { 027 028 private float angle = 0; 029 private float centreX = 0.5f; 030 private float centreY = 0.5f; 031 private float radius = 100; 032 private float amount = 0.5f; 033 034 private float radius2 = 0; 035 private float icentreX; 036 private float icentreY; 037 private float width; 038 private float height; 039 040 public PinchFilter() { 041 } 042 043 /** 044 * Set the angle of twirl in radians. 0 means no distortion. 045 * @param angle the angle of twirl. This is the angle by which pixels at the nearest edge of the image will move. 046 * @see #getAngle 047 */ 048 public void setAngle(float angle) { 049 this.angle = angle; 050 } 051 052 /** 053 * Get the angle of twist. 054 * @return the angle in radians. 055 * @see #setAngle 056 */ 057 public float getAngle() { 058 return angle; 059 } 060 061 /** 062 * Set the centre of the effect in the X direction as a proportion of the image size. 063 * @param centreX the center 064 * @see #getCentreX 065 */ 066 public void setCentreX( float centreX ) { 067 this.centreX = centreX; 068 } 069 070 /** 071 * Get the centre of the effect in the X direction as a proportion of the image size. 072 * @return the center 073 * @see #setCentreX 074 */ 075 public float getCentreX() { 076 return centreX; 077 } 078 079 /** 080 * Set the centre of the effect in the Y direction as a proportion of the image size. 081 * @param centreY the center 082 * @see #getCentreY 083 */ 084 public void setCentreY( float centreY ) { 085 this.centreY = centreY; 086 } 087 088 /** 089 * Get the centre of the effect in the Y direction as a proportion of the image size. 090 * @return the center 091 * @see #setCentreY 092 */ 093 public float getCentreY() { 094 return centreY; 095 } 096 097 /** 098 * Set the centre of the effect as a proportion of the image size. 099 * @param centre the center 100 * @see #getCentre 101 */ 102 public void setCentre( Point2D centre ) { 103 this.centreX = (float)centre.getX(); 104 this.centreY = (float)centre.getY(); 105 } 106 107 /** 108 * Get the centre of the effect as a proportion of the image size. 109 * @return the center 110 * @see #setCentre 111 */ 112 public Point2D getCentre() { 113 return new Point2D.Float( centreX, centreY ); 114 } 115 116 /** 117 * Set the radius of the effect. 118 * @param radius the radius 119 * @min-value 0 120 * @see #getRadius 121 */ 122 public void setRadius(float radius) { 123 this.radius = radius; 124 } 125 126 /** 127 * Get the radius of the effect. 128 * @return the radius 129 * @see #setRadius 130 */ 131 public float getRadius() { 132 return radius; 133 } 134 135 /** 136 * Set the amount of pinch. 137 * @param amount the amount 138 * @min-value -1 139 * @max-value 1 140 * @see #getAmount 141 */ 142 public void setAmount(float amount) { 143 this.amount = amount; 144 } 145 146 /** 147 * Get the amount of pinch. 148 * @return the amount 149 * @see #setAmount 150 */ 151 public float getAmount() { 152 return amount; 153 } 154 155 public BufferedImage filter( BufferedImage src, BufferedImage dst ) { 156 width = src.getWidth(); 157 height = src.getHeight(); 158 icentreX = width * centreX; 159 icentreY = height * centreY; 160 if ( radius == 0 ) 161 radius = Math.min(icentreX, icentreY); 162 radius2 = radius*radius; 163 return super.filter( src, dst ); 164 } 165 166 protected void transformInverse(int x, int y, float[] out) { 167 float dx = x-icentreX; 168 float dy = y-icentreY; 169 float distance = dx*dx + dy*dy; 170 171 if ( distance > radius2 || distance == 0 ) { 172 out[0] = x; 173 out[1] = y; 174 } else { 175 float d = (float)Math.sqrt( distance / radius2 ); 176 float t = (float)Math.pow( Math.sin( Math.PI*0.5 * d ), -amount); 177 178 dx *= t; 179 dy *= t; 180 181 float e = 1 - d; 182 float a = angle * e * e; 183 184 float s = (float)Math.sin( a ); 185 float c = (float)Math.cos( a ); 186 187 out[0] = icentreX + c*dx - s*dy; 188 out[1] = icentreY + s*dx + c*dy; 189 } 190 } 191 192 public String toString() { 193 return "Distort/Pinch..."; 194 } 195 196}