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.image.*; 021 022/** 023 * A filter which distorts and image by performing coordinate conversions between rectangular and polar coordinates. 024 */ 025public class PolarFilter extends TransformFilter { 026 027 /** 028 * Convert from rectangular to polar coordinates. 029 */ 030 public final static int RECT_TO_POLAR = 0; 031 032 /** 033 * Convert from polar to rectangular coordinates. 034 */ 035 public final static int POLAR_TO_RECT = 1; 036 037 /** 038 * Invert the image in a circle. 039 */ 040 public final static int INVERT_IN_CIRCLE = 2; 041 042 private int type; 043 private float width, height; 044 private float centreX, centreY; 045 private float radius; 046 047 /** 048 * Construct a PolarFilter. 049 */ 050 public PolarFilter() { 051 this(RECT_TO_POLAR); 052 } 053 054 /** 055 * Construct a PolarFilter. 056 * @param type the distortion type 057 */ 058 public PolarFilter(int type) { 059 this.type = type; 060 setEdgeAction(CLAMP); 061 } 062 063 public BufferedImage filter( BufferedImage src, BufferedImage dst ) { 064 this.width = src.getWidth(); 065 this.height = src.getHeight(); 066 centreX = width/2; 067 centreY = height/2; 068 radius = Math.max(centreY, centreX); 069 return super.filter( src, dst ); 070 } 071 072 /** 073 * Set the distortion type. 074 * @param type the distortion type 075 * @see #getType 076 */ 077 public void setType(int type) { 078 this.type = type; 079 } 080 081 /** 082 * Get the distortion type. 083 * @return the distortion type 084 * @see #setType 085 */ 086 public int getType() { 087 return type; 088 } 089 090 private float sqr(float x) { 091 return x*x; 092 } 093 094 protected void transformInverse(int x, int y, float[] out) { 095 float theta, t; 096 float m, xmax, ymax; 097 float r = 0; 098 099 switch (type) { 100 case RECT_TO_POLAR: 101 theta = 0; 102 if (x >= centreX) { 103 if (y > centreY) { 104 theta = ImageMath.PI - (float)Math.atan(((float)(x - centreX))/((float)(y - centreY))); 105 r = (float)Math.sqrt(sqr (x - centreX) + sqr (y - centreY)); 106 } else if (y < centreY) { 107 theta = (float)Math.atan (((float)(x - centreX))/((float)(centreY - y))); 108 r = (float)Math.sqrt (sqr (x - centreX) + sqr (centreY - y)); 109 } else { 110 theta = ImageMath.HALF_PI; 111 r = x - centreX; 112 } 113 } else if (x < centreX) { 114 if (y < centreY) { 115 theta = ImageMath.TWO_PI - (float)Math.atan (((float)(centreX -x))/((float)(centreY - y))); 116 r = (float)Math.sqrt (sqr (centreX - x) + sqr (centreY - y)); 117 } else if (y > centreY) { 118 theta = ImageMath.PI + (float)Math.atan (((float)(centreX - x))/((float)(y - centreY))); 119 r = (float)Math.sqrt (sqr (centreX - x) + sqr (y - centreY)); 120 } else { 121 theta = 1.5f * ImageMath.PI; 122 r = centreX - x; 123 } 124 } 125 if (x != centreX) 126 m = Math.abs (((float)(y - centreY)) / ((float)(x - centreX))); 127 else 128 m = 0; 129 130 if (m <= ((float)height / (float)width)) { 131 if (x == centreX) { 132 xmax = 0; 133 ymax = centreY; 134 } else { 135 xmax = centreX; 136 ymax = m * xmax; 137 } 138 } else { 139 ymax = centreY; 140 xmax = ymax / m; 141 } 142 143 out[0] = (width-1) - (width - 1)/ImageMath.TWO_PI * theta; 144 out[1] = height * r / radius; 145 break; 146 case POLAR_TO_RECT: 147 theta = x / width * ImageMath.TWO_PI; 148 float theta2; 149 150 if (theta >= 1.5f * ImageMath.PI) 151 theta2 = ImageMath.TWO_PI - theta; 152 else if (theta >= ImageMath.PI) 153 theta2 = theta - ImageMath.PI; 154 else if (theta >= 0.5f * ImageMath.PI) 155 theta2 = ImageMath.PI - theta; 156 else 157 theta2 = theta; 158 159 t = (float)Math.tan(theta2); 160 if (t != 0) 161 m = 1.0f / t; 162 else 163 m = 0; 164 165 if (m <= ((float)(height) / (float)(width))) { 166 if (theta2 == 0) { 167 xmax = 0; 168 ymax = centreY; 169 } else { 170 xmax = centreX; 171 ymax = m * xmax; 172 } 173 } else { 174 ymax = centreY; 175 xmax = ymax / m; 176 } 177 178 r = radius * (float)(y / (float)(height)); 179 180 float nx = -r * (float)Math.sin(theta2); 181 float ny = r * (float)Math.cos(theta2); 182 183 if (theta >= 1.5f * ImageMath.PI) { 184 out[0] = (float)centreX - nx; 185 out[1] = (float)centreY - ny; 186 } else if (theta >= Math.PI) { 187 out[0] = (float)centreX - nx; 188 out[1] = (float)centreY + ny; 189 } else if (theta >= 0.5 * Math.PI) { 190 out[0] = (float)centreX + nx; 191 out[1] = (float)centreY + ny; 192 } else { 193 out[0] = (float)centreX + nx; 194 out[1] = (float)centreY - ny; 195 } 196 break; 197 case INVERT_IN_CIRCLE: 198 float dx = x-centreX; 199 float dy = y-centreY; 200 float distance2 = dx*dx+dy*dy; 201 out[0] = centreX + centreX*centreX * dx/distance2; 202 out[1] = centreY + centreY*centreY * dy/distance2; 203 break; 204 } 205 } 206 207 public String toString() { 208 return "Distort/Polar Coordinates..."; 209 } 210 211}