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.Color; 036import java.awt.image.BufferedImage; 037 038import lucee.runtime.engine.ThreadLocalPageContext; 039import lucee.runtime.exp.FunctionException; 040import lucee.runtime.exp.PageException; 041import lucee.runtime.img.ImageUtil; 042import lucee.runtime.type.KeyImpl; 043import lucee.runtime.type.Struct; 044import lucee.runtime.type.util.CollectionUtil; 045 046/** 047 * An experimental filter which can be used for keying against a clean shot. Given a source image, a clean image and a destination image, 048 * the filter replaces all pixels in the source which nearly equal the equivalent clean pixel by destination pixels. 049 */ 050public class KeyFilter extends AbstractBufferedImageOp implements DynFiltering { 051 052 private float hTolerance = 0; 053 private float sTolerance = 0; 054 private float bTolerance = 0; 055 private BufferedImage destination; 056 private BufferedImage cleanImage; 057 058 public KeyFilter() { 059 } 060 061 /** 062 * Set the hue tolerance of the image in the range 0..1. 063 * @param hTolerance the tolerance 064 * @see #getHTolerance 065 */ 066 public void setHTolerance( float hTolerance ) { 067 this.hTolerance = hTolerance; 068 } 069 070 /** 071 * Get the hue tolerance. 072 * @return the tolerance 073 * @see #setHTolerance 074 */ 075 public float getHTolerance() { 076 return hTolerance; 077 } 078 079 /** 080 * Set the saturation tolerance of the image in the range 0..1. 081 * @param sTolerance the tolerance 082 * @see #getSTolerance 083 */ 084 public void setSTolerance( float sTolerance ) { 085 this.sTolerance = sTolerance; 086 } 087 088 /** 089 * Get the saturation tolerance. 090 * @return the tolerance 091 * @see #setSTolerance 092 */ 093 public float getSTolerance() { 094 return sTolerance; 095 } 096 097 /** 098 * Set the brightness tolerance of the image in the range 0..1. 099 * @param bTolerance the tolerance 100 * @see #getBTolerance 101 */ 102 public void setBTolerance( float bTolerance ) { 103 this.bTolerance = bTolerance; 104 } 105 106 /** 107 * Get the brightness tolerance. 108 * @return the tolerance 109 * @see #setBTolerance 110 */ 111 public float getBTolerance() { 112 return bTolerance; 113 } 114 115 /** 116 * Set the destination image. 117 * @param destination the destination image 118 * @see #getDestination 119 */ 120 public void setDestination( BufferedImage destination ) { 121 this.destination = destination; 122 } 123 124 /** 125 * Get the destination image. 126 * @return the destination image 127 * @see #setDestination 128 */ 129 public BufferedImage getDestination() { 130 return destination; 131 } 132 133 /** 134 * Get the clean image. 135 * @param cleanImage the clean image 136 * @see #getCleanImage 137 */ 138 public void setCleanImage( BufferedImage cleanImage ) { 139 this.cleanImage = cleanImage; 140 } 141 142 /** 143 * Get the clean image. 144 * @return the clean image 145 * @see #setCleanImage 146 */ 147 public BufferedImage getCleanImage() { 148 return cleanImage; 149 } 150 151 public BufferedImage filter( BufferedImage src, BufferedImage dst ) { 152 int width = src.getWidth(); 153 int height = src.getHeight(); 154 //int type = src.getType(); 155 //WritableRaster srcRaster = 156 src.getRaster(); 157 158 if ( dst == null ) 159 dst = createCompatibleDestImage( src, null ); 160 //WritableRaster dstRaster = 161 dst.getRaster(); 162 163 if ( destination != null && cleanImage != null ) { 164 float[] hsb1 = null; 165 float[] hsb2 = null; 166 int[] inPixels = null; 167 int[] outPixels = null; 168 int[] cleanPixels = null; 169 for ( int y = 0; y < height; y++ ) { 170 inPixels = getRGB( src, 0, y, width, 1, inPixels ); 171 outPixels = getRGB( destination, 0, y, width, 1, outPixels ); 172 cleanPixels = getRGB( cleanImage, 0, y, width, 1, cleanPixels ); 173 for ( int x = 0; x < width; x++ ) { 174 int rgb1 = inPixels[x]; 175 int out = outPixels[x]; 176 int rgb2 = cleanPixels[x]; 177 178 int r1 = (rgb1 >> 16) & 0xff; 179 int g1 = (rgb1 >> 8) & 0xff; 180 int b1 = rgb1 & 0xff; 181 int r2 = (rgb2 >> 16) & 0xff; 182 int g2 = (rgb2 >> 8) & 0xff; 183 int b2 = rgb2 & 0xff; 184 hsb1 = Color.RGBtoHSB( r1, b1, g1, hsb1 ); 185 hsb2 = Color.RGBtoHSB( r2, b2, g2, hsb2 ); 186// int tolerance = (int)(255*tolerance); 187// return Math.abs(r1-r2) <= tolerance && Math.abs(g1-g2) <= tolerance && Math.abs(b1-b2) <= tolerance; 188 189 // if ( PixelUtils.nearColors( in, clean, (int)(255*tolerance) ) ) 190 if ( Math.abs( hsb1[0] - hsb2[0] ) < hTolerance && Math.abs( hsb1[1] - hsb2[1] ) < sTolerance && Math.abs( hsb1[2] - hsb2[2] ) < bTolerance ) 191 inPixels[x] = out; 192 else 193 inPixels[x] = rgb1; 194 } 195 setRGB( dst, 0, y, width, 1, inPixels ); 196 } 197 } 198 199 return dst; 200 } 201 202 public String toString() { 203 return "Keying/Key..."; 204 } 205 public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src); 206 Object o; 207 if((o=parameters.removeEL(KeyImpl.init("HTolerance")))!=null)setHTolerance(ImageFilterUtil.toFloatValue(o,"HTolerance")); 208 if((o=parameters.removeEL(KeyImpl.init("STolerance")))!=null)setSTolerance(ImageFilterUtil.toFloatValue(o,"STolerance")); 209 if((o=parameters.removeEL(KeyImpl.init("BTolerance")))!=null)setBTolerance(ImageFilterUtil.toFloatValue(o,"BTolerance")); 210 if((o=parameters.removeEL(KeyImpl.init("CleanImage")))!=null)setCleanImage(ImageFilterUtil.toBufferedImage(o,"CleanImage")); 211 if((o=parameters.removeEL(KeyImpl.init("destination")))!=null)setDestination(ImageFilterUtil.toBufferedImage(o,"destination")); 212 213 // check for arguments not supported 214 if(parameters.size()>0) { 215 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 [HTolerance, STolerance, BTolerance, CleanImage]"); 216 } 217 218 return filter(src, dst); 219 } 220}