001 /* 002 * 003 004 Licensed under the Apache License, Version 2.0 (the "License"); 005 you may not use this file except in compliance with the License. 006 You may obtain a copy of the License at 007 008 http://www.apache.org/licenses/LICENSE-2.0 009 010 Unless required by applicable law or agreed to in writing, software 011 distributed under the License is distributed on an "AS IS" BASIS, 012 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 See the License for the specific language governing permissions and 014 limitations under the License. 015 */ 016 017 package railo.runtime.img.filter;import java.awt.Rectangle; 018 import java.awt.image.BufferedImage; 019 020 import railo.runtime.engine.ThreadLocalPageContext; 021 import railo.runtime.exp.FunctionException; 022 import railo.runtime.exp.PageException; 023 import railo.runtime.img.ImageUtil; 024 import railo.runtime.type.KeyImpl; 025 import railo.runtime.type.List; 026 import railo.runtime.type.Struct; 027 028 /** 029 * A filter which draws contours on an image at given brightness levels. 030 */ 031 public class ContourFilter extends WholeImageFilter implements DynFiltering { 032 033 private float levels = 5; 034 private float scale = 1; 035 private float offset = 0; 036 private int contourColor = 0xff000000; 037 038 public ContourFilter() { 039 } 040 041 public void setLevels( float levels ) { 042 this.levels = levels; 043 } 044 045 public float getLevels() { 046 return levels; 047 } 048 049 /** 050 * Specifies the scale of the contours. 051 * @param scale the scale of the contours. 052 * @min-value 0 053 * @max-value 1 054 * @see #getScale 055 */ 056 public void setScale( float scale ) { 057 this.scale = scale; 058 } 059 060 /** 061 * Returns the scale of the contours. 062 * @return the scale of the contours. 063 * @see #setScale 064 */ 065 public float getScale() { 066 return scale; 067 } 068 069 public void setOffset( float offset ) { 070 this.offset = offset; 071 } 072 073 public float getOffset() { 074 return offset; 075 } 076 077 public void setContourColor( int contourColor ) { 078 this.contourColor = contourColor; 079 } 080 081 public int getContourColor() { 082 return contourColor; 083 } 084 085 protected int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace ) { 086 int index = 0; 087 short[][] r = new short[3][width]; 088 int[] outPixels = new int[width * height]; 089 090 short[] table = new short[256]; 091 int offsetl = (int)(offset * 256 / levels); 092 for ( int i = 0; i < 256; i++ ) 093 table[i] = (short)PixelUtils.clamp( (int)(255 * Math.floor(levels*(i+offsetl) / 256) / (levels-1) - offsetl) ); 094 095 for (int x = 0; x < width; x++) { 096 int rgb = inPixels[x]; 097 r[1][x] = (short)PixelUtils.brightness( rgb ); 098 } 099 for (int y = 0; y < height; y++) { 100 boolean yIn = y > 0 && y < height-1; 101 int nextRowIndex = index+width; 102 if ( y < height-1) { 103 for (int x = 0; x < width; x++) { 104 int rgb = inPixels[nextRowIndex++]; 105 r[2][x] = (short)PixelUtils.brightness( rgb ); 106 } 107 } 108 for (int x = 0; x < width; x++) { 109 boolean xIn = x > 0 && x < width-1; 110 int w = x-1; 111 int e = x+1; 112 int v = 0; 113 114 if ( yIn && xIn ) { 115 short nwb = r[0][w]; 116 short neb = r[0][x]; 117 short swb = r[1][w]; 118 short seb = r[1][x]; 119 short nw = table[nwb]; 120 short ne = table[neb]; 121 short sw = table[swb]; 122 short se = table[seb]; 123 124 if (nw != ne || nw != sw || ne != se || sw != se) { 125 v = (int)(scale * (Math.abs(nwb - neb) + Math.abs(nwb - swb) + Math.abs(neb - seb) + Math.abs(swb - seb))); 126 // v /= 255; 127 if (v > 255) 128 v = 255; 129 } 130 } 131 132 if ( v != 0 ) 133 outPixels[index] = PixelUtils.combinePixels( inPixels[index], contourColor, PixelUtils.NORMAL, v ); 134 // outPixels[index] = PixelUtils.combinePixels( (contourColor & 0xff)|(v << 24), inPixels[index], PixelUtils.NORMAL ); 135 else 136 outPixels[index] = inPixels[index]; 137 index++; 138 } 139 short[] t; 140 t = r[0]; 141 r[0] = r[1]; 142 r[1] = r[2]; 143 r[2] = t; 144 } 145 146 return outPixels; 147 } 148 149 public String toString() { 150 return "Stylize/Contour..."; 151 } 152 153 public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src); 154 Object o; 155 if((o=parameters.removeEL(KeyImpl.init("Levels")))!=null)setLevels(ImageFilterUtil.toFloatValue(o,"Levels")); 156 if((o=parameters.removeEL(KeyImpl.init("ContourColor")))!=null)setContourColor(ImageFilterUtil.toColorRGB(o,"ContourColor")); 157 if((o=parameters.removeEL(KeyImpl.init("Offset")))!=null)setOffset(ImageFilterUtil.toFloatValue(o,"Offset")); 158 if((o=parameters.removeEL(KeyImpl.init("Scale")))!=null)setScale(ImageFilterUtil.toFloatValue(o,"Scale")); 159 160 // check for arguments not supported 161 if(parameters.size()>0) { 162 throw new FunctionException(ThreadLocalPageContext.get(), "ImageFilter", 3, "parameters", "the parameter"+(parameters.size()>1?"s":"")+" ["+List.arrayToList(parameters.keysAsString(),", ")+"] "+(parameters.size()>1?"are":"is")+" not allowed, only the following parameters are supported [Levels, ContourColor, Offset, Scale]"); 163 } 164 165 return filter(src, dst); 166 } 167 } 168