001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019/* 020* 021 022Licensed under the Apache License, Version 2.0 (the "License"); 023you may not use this file except in compliance with the License. 024You may obtain a copy of the License at 025 026 http://www.apache.org/licenses/LICENSE-2.0 027 028Unless required by applicable law or agreed to in writing, software 029distributed under the License is distributed on an "AS IS" BASIS, 030WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 031See the License for the specific language governing permissions and 032limitations under the License. 033*/ 034 035package lucee.runtime.img.filter;import java.awt.Rectangle; 036import java.awt.image.BufferedImage; 037import java.util.Date; 038import java.util.Random; 039 040import lucee.runtime.engine.ThreadLocalPageContext; 041import lucee.runtime.exp.FunctionException; 042import lucee.runtime.exp.PageException; 043import lucee.runtime.img.ImageUtil; 044import lucee.runtime.type.KeyImpl; 045import lucee.runtime.type.Struct; 046import lucee.runtime.type.util.CollectionUtil; 047 048public class PlasmaFilter extends WholeImageFilter implements DynFiltering { 049 050 public float turbulence = 1.0f; 051 private float scaling = 0.0f; 052 private Colormap colormap = new LinearColormap(); 053 private Random randomGenerator; 054 private long seed = 567; 055 private boolean useColormap = false; 056 private boolean useImageColors = false; 057 058 public PlasmaFilter() { 059 randomGenerator = new Random(); 060 } 061 062 /** 063 * Specifies the turbulence of the texture. 064 * @param turbulence the turbulence of the texture. 065 * @min-value 0 066 * @max-value 10 067 * @see #getTurbulence 068 */ 069 public void setTurbulence(float turbulence) { 070 this.turbulence = turbulence; 071 } 072 073 /** 074 * Returns the turbulence of the effect. 075 * @return the turbulence of the effect. 076 * @see #setTurbulence 077 */ 078 public float getTurbulence() { 079 return turbulence; 080 } 081 082 public void setScaling(float scaling) { 083 this.scaling = scaling; 084 } 085 086 public float getScaling() { 087 return scaling; 088 } 089 090 /** 091 * Set the colormap to be used for the filter. 092 * @param colormap the colormap 093 * @see #getColormap 094 */ 095 public void setColormap(Colormap colormap) { 096 this.colormap = colormap; 097 } 098 099 /** 100 * Get the colormap to be used for the filter. 101 * @return the colormap 102 * @see #setColormap 103 */ 104 public Colormap getColormap() { 105 return colormap; 106 } 107 108 public void setUseColormap(boolean useColormap) { 109 this.useColormap = useColormap; 110 } 111 112 public boolean getUseColormap() { 113 return useColormap; 114 } 115 116 public void setUseImageColors(boolean useImageColors) { 117 this.useImageColors = useImageColors; 118 } 119 120 public boolean getUseImageColors() { 121 return useImageColors; 122 } 123 124 public void setSeed(int seed) { 125 this.seed = seed; 126 } 127 128 public int getSeed() { 129 return (int)seed; 130 } 131 132 public void randomize() { 133 seed = new Date().getTime(); 134 } 135 136 private int randomRGB(int[] inPixels, int x, int y) { 137 if (useImageColors) { 138 return inPixels[y*originalSpace.width+x]; 139 } 140 int r = (int)(255 * randomGenerator.nextFloat()); 141 int g = (int)(255 * randomGenerator.nextFloat()); 142 int b = (int)(255 * randomGenerator.nextFloat()); 143 return 0xff000000 | (r << 16) | (g << 8) | b; 144 } 145 146 private int displace(int rgb, float amount) { 147 int r = (rgb >> 16) & 0xff; 148 int g = (rgb >> 8) & 0xff; 149 int b = rgb & 0xff; 150 r = PixelUtils.clamp(r + (int)(amount * (randomGenerator.nextFloat()-0.5))); 151 g = PixelUtils.clamp(g + (int)(amount * (randomGenerator.nextFloat()-0.5))); 152 b = PixelUtils.clamp(b + (int)(amount * (randomGenerator.nextFloat()-0.5))); 153 return 0xff000000 | (r << 16) | (g << 8) | b; 154 } 155 156 private int average(int rgb1, int rgb2) { 157 return PixelUtils.combinePixels(rgb1, rgb2, PixelUtils.AVERAGE); 158 } 159 160 private int getPixel(int x, int y, int[] pixels, int stride) { 161 return pixels[y*stride+x]; 162 } 163 164 private void putPixel(int x, int y, int rgb, int[] pixels, int stride) { 165 pixels[y*stride+x] = rgb; 166 } 167 168 private boolean doPixel(int x1, int y1, int x2, int y2, int[] pixels, int stride, int depth, int scale) { 169 int mx, my; 170 171 if (depth == 0) { 172 int ml, mr, mt, mb, mm, t; 173 174 int tl = getPixel(x1, y1, pixels, stride); 175 int bl = getPixel(x1, y2, pixels, stride); 176 int tr = getPixel(x2, y1, pixels, stride); 177 int br = getPixel(x2, y2, pixels, stride); 178 179 float amount = (256.0f / (2.0f * scale)) * turbulence; 180 181 mx = (x1 + x2) / 2; 182 my = (y1 + y2) / 2; 183 184 if (mx == x1 && mx == x2 && my == y1 && my == y2) 185 return true; 186 187 if (mx != x1 || mx != x2) { 188 ml = average(tl, bl); 189 ml = displace(ml, amount); 190 putPixel(x1, my, ml, pixels, stride); 191 192 if (x1 != x2){ 193 mr = average(tr, br); 194 mr = displace(mr, amount); 195 putPixel(x2, my, mr, pixels, stride); 196 } 197 } 198 199 if (my != y1 || my != y2){ 200 if (x1 != mx || my != y2){ 201 mb = average(bl, br); 202 mb = displace(mb, amount); 203 putPixel(mx, y2, mb, pixels, stride); 204 } 205 206 if (y1 != y2){ 207 mt = average(tl, tr); 208 mt = displace(mt, amount); 209 putPixel(mx, y1, mt, pixels, stride); 210 } 211 } 212 213 if (y1 != y2 || x1 != x2) { 214 mm = average(tl, br); 215 t = average(bl, tr); 216 mm = average(mm, t); 217 mm = displace(mm, amount); 218 putPixel(mx, my, mm, pixels, stride); 219 } 220 221 if (x2-x1 < 3 && y2-y1 < 3) 222 return false; 223 return true; 224 } 225 226 mx = (x1 + x2) / 2; 227 my = (y1 + y2) / 2; 228 229 doPixel(x1, y1, mx, my, pixels, stride, depth-1, scale+1); 230 doPixel(x1, my, mx ,y2, pixels, stride, depth-1, scale+1); 231 doPixel(mx, y1, x2 , my, pixels, stride, depth-1, scale+1); 232 return doPixel(mx, my, x2, y2, pixels, stride, depth-1, scale+1); 233 } 234 235 protected int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace ) { 236 int[] outPixels = new int[width * height]; 237 238 randomGenerator.setSeed(seed); 239 240 int w1 = width-1; 241 int h1 = height-1; 242 putPixel(0, 0, randomRGB(inPixels, 0, 0), outPixels, width); 243 putPixel(w1, 0, randomRGB(inPixels, w1, 0), outPixels, width); 244 putPixel(0, h1, randomRGB(inPixels, 0, h1), outPixels, width); 245 putPixel(w1, h1, randomRGB(inPixels, w1, h1), outPixels, width); 246 putPixel(w1/2, h1/2, randomRGB(inPixels, w1/2, h1/2), outPixels, width); 247 putPixel(0, h1/2, randomRGB(inPixels, 0, h1/2), outPixels, width); 248 putPixel(w1, h1/2, randomRGB(inPixels, w1, h1/2), outPixels, width); 249 putPixel(w1/2, 0, randomRGB(inPixels, w1/2, 0), outPixels, width); 250 putPixel(w1/2, h1, randomRGB(inPixels, w1/2, h1), outPixels, width); 251 252 int depth = 1; 253 while (doPixel(0, 0, width-1, height-1, outPixels, width, depth, 0)) 254 depth++; 255 256 if (useColormap && colormap != null) { 257 int index = 0; 258 for (int y = 0; y < height; y++) { 259 for (int x = 0; x < width; x++) { 260 outPixels[index] = colormap.getColor((outPixels[index] & 0xff)/255.0f); 261 index++; 262 } 263 } 264 } 265 return outPixels; 266 } 267 268 public String toString() { 269 return "Texture/Plasma..."; 270 } 271 272 public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src); 273 Object o; 274 if((o=parameters.removeEL(KeyImpl.init("Colormap")))!=null)setColormap(ImageFilterUtil.toColormap(o,"Colormap")); 275 if((o=parameters.removeEL(KeyImpl.init("Turbulence")))!=null)setTurbulence(ImageFilterUtil.toFloatValue(o,"Turbulence")); 276 if((o=parameters.removeEL(KeyImpl.init("Scaling")))!=null)setScaling(ImageFilterUtil.toFloatValue(o,"Scaling")); 277 if((o=parameters.removeEL(KeyImpl.init("UseColormap")))!=null)setUseColormap(ImageFilterUtil.toBooleanValue(o,"UseColormap")); 278 if((o=parameters.removeEL(KeyImpl.init("UseImageColors")))!=null)setUseImageColors(ImageFilterUtil.toBooleanValue(o,"UseImageColors")); 279 if((o=parameters.removeEL(KeyImpl.init("Seed")))!=null)setSeed(ImageFilterUtil.toIntValue(o,"Seed")); 280 281 // check for arguments not supported 282 if(parameters.size()>0) { 283 throw new FunctionException(ThreadLocalPageContext.get(), "ImageFilter", 3, "parameters", "the parameter"+(parameters.size()>1?"s":"")+" ["+CollectionUtil.getKeyList(parameters,", ")+"] "+(parameters.size()>1?"are":"is")+" not allowed, only the following parameters are supported [Colormap, Turbulence, Scaling, UseColormap, UseImageColors, Seed]"); 284 } 285 286 return filter(src, dst); 287 } 288}