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.composite.*; 023 024/** 025 * A filter which renders "glints" on bright parts of the image. 026 */ 027public class GlintFilter extends AbstractBufferedImageOp { 028 029 private float threshold = 1.0f; 030 private int length = 5; 031 private float blur = 0.0f; 032 private float amount = 0.1f; 033 private boolean glintOnly = false; 034 private Colormap colormap = new LinearColormap( 0xffffffff, 0xff000000 ); 035 036 public GlintFilter() { 037 } 038 039 /** 040 * Set the threshold value. 041 * @param threshold the threshold value 042 * @see #getThreshold 043 */ 044 public void setThreshold( float threshold ) { 045 this.threshold = threshold; 046 } 047 048 /** 049 * Get the threshold value. 050 * @return the threshold value 051 * @see #setThreshold 052 */ 053 public float getThreshold() { 054 return threshold; 055 } 056 057 /** 058 * Set the amount of glint. 059 * @param amount the amount 060 * @min-value 0 061 * @max-value 1 062 * @see #getAmount 063 */ 064 public void setAmount( float amount ) { 065 this.amount = amount; 066 } 067 068 /** 069 * Get the amount of glint. 070 * @return the amount 071 * @see #setAmount 072 */ 073 public float getAmount() { 074 return amount; 075 } 076 077 /** 078 * Set the length of the stars. 079 * @param length the length 080 * @see #getLength 081 */ 082 public void setLength( int length ) { 083 this.length = length; 084 } 085 086 /** 087 * Get the length of the stars. 088 * @return the length 089 * @see #setLength 090 */ 091 public int getLength() { 092 return length; 093 } 094 095 /** 096 * Set the blur that is applied before thresholding. 097 * @param blur the blur radius 098 * @see #getBlur 099 */ 100 public void setBlur(float blur) { 101 this.blur = blur; 102 } 103 104 /** 105 * Set the blur that is applied before thresholding. 106 * @return the blur radius 107 * @see #setBlur 108 */ 109 public float getBlur() { 110 return blur; 111 } 112 113 /** 114 * Set whether to render the stars and the image or only the stars. 115 * @param glintOnly true to render only stars 116 * @see #getGlintOnly 117 */ 118 public void setGlintOnly(boolean glintOnly) { 119 this.glintOnly = glintOnly; 120 } 121 122 /** 123 * Get whether to render the stars and the image or only the stars. 124 * @return true to render only stars 125 * @see #setGlintOnly 126 */ 127 public boolean getGlintOnly() { 128 return glintOnly; 129 } 130 131 /** 132 * Set the colormap to be used for the filter. 133 * @param colormap the colormap 134 * @see #getColormap 135 */ 136 public void setColormap(Colormap colormap) { 137 this.colormap = colormap; 138 } 139 140 /** 141 * Get the colormap to be used for the filter. 142 * @return the colormap 143 * @see #setColormap 144 */ 145 public Colormap getColormap() { 146 return colormap; 147 } 148 149 public BufferedImage filter( BufferedImage src, BufferedImage dst ) { 150 int width = src.getWidth(); 151 int height = src.getHeight(); 152 int[] pixels = new int[width]; 153 int length2 = (int)(length / 1.414f); 154 int[] colors = new int[length+1]; 155 int[] colors2 = new int[length2+1]; 156 157 if ( colormap != null ) { 158 for (int i = 0; i <= length; i++) { 159 int argb = colormap.getColor( (float)i/length ); 160 int r = (argb >> 16) & 0xff; 161 int g = (argb >> 8) & 0xff; 162 int b = argb & 0xff; 163 argb = (argb & 0xff000000) | ((int)(amount*r) << 16) | ((int)(amount*g) << 8) | (int)(amount*b); 164 colors[i] = argb; 165 } 166 for (int i = 0; i <= length2; i++) { 167 int argb = colormap.getColor( (float)i/length2 ); 168 int r = (argb >> 16) & 0xff; 169 int g = (argb >> 8) & 0xff; 170 int b = argb & 0xff; 171 argb = (argb & 0xff000000) | ((int)(amount*r) << 16) | ((int)(amount*g) << 8) | (int)(amount*b); 172 colors2[i] = argb; 173 } 174 } 175 176 BufferedImage mask = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); 177 178 int threshold3 = (int)(threshold*3*255); 179 for ( int y = 0; y < height; y++ ) { 180 getRGB( src, 0, y, width, 1, pixels ); 181 for ( int x = 0; x < width; x++ ) { 182 int rgb = pixels[x]; 183 int a = rgb & 0xff000000; 184 int r = (rgb >> 16) & 0xff; 185 int g = (rgb >> 8) & 0xff; 186 int b = rgb & 0xff; 187 int l = r + g + b; 188 if (l < threshold3) 189 pixels[x] = 0xff000000; 190 else { 191 l /= 3; 192 pixels[x] = a | (l << 16) | (l << 8) | l; 193 } 194 } 195 setRGB( mask, 0, y, width, 1, pixels ); 196 } 197 198 if ( blur != 0 ) 199 mask = new GaussianFilter(blur).filter( mask, null ); 200 201 if ( dst == null ) 202 dst = createCompatibleDestImage( src, null ); 203 int[] dstPixels; 204 if ( glintOnly ) 205 dstPixels = new int[width*height]; 206 else 207 dstPixels = getRGB( src, 0, 0, width, height, null );//FIXME - only need 2*length 208 209 for ( int y = 0; y < height; y++ ) { 210 int index = y*width; 211 getRGB( mask, 0, y, width, 1, pixels ); 212 int ymin = Math.max( y-length, 0 )-y; 213 int ymax = Math.min( y+length, height-1 )-y; 214 int ymin2 = Math.max( y-length2, 0 )-y; 215 int ymax2 = Math.min( y+length2, height-1 )-y; 216 for ( int x = 0; x < width; x++ ) { 217 if ( (pixels[x] & 0xff) > threshold*255 ) { 218 int xmin = Math.max( x-length, 0 )-x; 219 int xmax = Math.min( x+length, width-1 )-x; 220 int xmin2 = Math.max( x-length2, 0 )-x; 221 int xmax2 = Math.min( x+length2, width-1 )-x; 222 223 // Horizontal 224 for ( int i = 0, k = 0; i <= xmax; i++, k++ ) 225 dstPixels[index+i] = PixelUtils.combinePixels( dstPixels[index+i], colors[k], PixelUtils.ADD ); 226 for ( int i = -1, k = 1; i >= xmin; i--, k++ ) 227 dstPixels[index+i] = PixelUtils.combinePixels( dstPixels[index+i], colors[k], PixelUtils.ADD ); 228 // Vertical 229 for ( int i = 1, j = index+width, k = 0; i <= ymax; i++, j += width, k++ ) 230 dstPixels[j] = PixelUtils.combinePixels( dstPixels[j], colors[k], PixelUtils.ADD ); 231 for ( int i = -1, j = index-width, k = 0; i >= ymin; i--, j -= width, k++ ) 232 dstPixels[j] = PixelUtils.combinePixels( dstPixels[j], colors[k], PixelUtils.ADD ); 233 234 // Diagonals 235 int xymin = Math.max( xmin2, ymin2 ); 236 int xymax = Math.min( xmax2, ymax2 ); 237 // SE 238 int count = Math.min( xmax2, ymax2 ); 239 for ( int i = 1, j = index+width+1, k = 0; i <= count; i++, j += width+1, k++ ) 240 dstPixels[j] = PixelUtils.combinePixels( dstPixels[j], colors2[k], PixelUtils.ADD ); 241 // NW 242 count = Math.min( -xmin2, -ymin2 ); 243 for ( int i = 1, j = index-width-1, k = 0; i <= count; i++, j -= width+1, k++ ) 244 dstPixels[j] = PixelUtils.combinePixels( dstPixels[j], colors2[k], PixelUtils.ADD ); 245 // NE 246 count = Math.min( xmax2, -ymin2 ); 247 for ( int i = 1, j = index-width+1, k = 0; i <= count; i++, j += -width+1, k++ ) 248 dstPixels[j] = PixelUtils.combinePixels( dstPixels[j], colors2[k], PixelUtils.ADD ); 249 // SW 250 count = Math.min( -xmin2, ymax2 ); 251 for ( int i = 1, j = index+width-1, k = 0; i <= count; i++, j += width-1, k++ ) 252 dstPixels[j] = PixelUtils.combinePixels( dstPixels[j], colors2[k], PixelUtils.ADD ); 253 } 254 index++; 255 } 256 } 257 setRGB( dst, 0, 0, width, height, dstPixels ); 258 259 return dst; 260 } 261 262 public String toString() { 263 return "Effects/Glint..."; 264 } 265}