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.image.BufferedImage;
036
037import lucee.runtime.engine.ThreadLocalPageContext;
038import lucee.runtime.exp.FunctionException;
039import lucee.runtime.exp.PageException;
040import lucee.runtime.img.ImageUtil;
041import lucee.runtime.type.KeyImpl;
042import lucee.runtime.type.Struct;
043import lucee.runtime.type.util.CollectionUtil;
044
045
046
047public class CurvesFilter extends TransferFilter  implements DynFiltering {
048
049        private Curve[] curves = new Curve[1];
050        
051    public static class Curve {
052        public float[] x;
053        public float[] y;
054        
055        public Curve() {
056            x = new float[] { 0, 1 };
057            y = new float[] { 0, 1 };
058        }
059        
060        public Curve( Curve curve ) {
061            x = curve.x.clone();
062            y = curve.y.clone();
063        }
064        
065        public int addKnot( float kx, float ky ) {
066            int pos = -1;
067            int numKnots = x.length;
068            float[] nx = new float[numKnots+1];
069            float[] ny = new float[numKnots+1];
070            int j = 0;
071            for ( int i = 0; i < numKnots; i++ ) {
072                if ( pos == -1 && x[i] > kx ) {
073                    pos = j;
074                    nx[j] = kx;
075                    ny[j] = ky;
076                    j++;
077                }
078                nx[j] = x[i];
079                ny[j] = y[i];
080                j++;
081            }
082            if ( pos == -1 ) {
083                pos = j;
084                nx[j] = kx;
085                ny[j] = ky;
086            }
087            x = nx;
088            y = ny;
089            return pos;
090        }
091        
092        public void removeKnot( int n ) {
093            int numKnots = x.length;
094            if ( numKnots <= 2 )
095                return;
096            float[] nx = new float[numKnots-1];
097            float[] ny = new float[numKnots-1];
098            int j = 0;
099            for ( int i = 0; i < numKnots-1; i++ ) {
100                if ( i == n )
101                    j++;
102                nx[i] = x[j];
103                ny[i] = y[j];
104                j++;
105            }
106            x = nx;
107            y = ny;
108            for ( int i = 0; i < x.length; i++ )
109                System.out.println( i+": "+x[i]+" "+y[i]);
110        }
111
112        private void sortKnots() {
113            int numKnots = x.length;
114            for (int i = 1; i < numKnots-1; i++) {
115                for (int j = 1; j < i; j++) {
116                    if (x[i] < x[j]) {
117                        float t = x[i];
118                        x[i] = x[j];
119                        x[j] = t;
120                        t = y[i];
121                        y[i] = y[j];
122                        y[j] = t;
123                    }
124                }
125            }
126        }
127
128        protected int[] makeTable() {
129            int numKnots = x.length;
130            float[] nx = new float[numKnots+2];
131            float[] ny = new float[numKnots+2];
132            System.arraycopy( x, 0, nx, 1, numKnots);
133            System.arraycopy( y, 0, ny, 1, numKnots);
134            nx[0] = nx[1];
135            ny[0] = ny[1];
136            nx[numKnots+1] = nx[numKnots];
137            ny[numKnots+1] = ny[numKnots];
138
139            int[] table = new int[256];
140            for (int i = 0; i < 1024; i++) {
141                float f = i/1024.0f;
142                int x = (int)(255 * ImageMath.spline( f, nx.length, nx ) + 0.5f);
143                int y = (int)(255 * ImageMath.spline( f, nx.length, ny ) + 0.5f);
144                x = ImageMath.clamp( x, 0, 255 );
145                y = ImageMath.clamp( y, 0, 255 );
146                table[x] = y;
147            }
148//          System.out.println();
149//          for ( int i = 0; i < 256; i++ )
150//              System.out.println( i+": "+table[i]);
151            return table;
152        }
153    }
154    
155    public CurvesFilter() {
156        curves = new Curve[3];
157        curves[0] = new Curve();
158        curves[1] = new Curve();
159        curves[2] = new Curve();
160    }
161    
162        protected void initialize() {
163                initialized = true;
164                if ( curves.length == 1 )
165            rTable = gTable = bTable = curves[0].makeTable();
166        else {
167            rTable = curves[0].makeTable();
168            gTable = curves[1].makeTable();
169            bTable = curves[2].makeTable();
170        }
171        }
172
173        public void setCurve( Curve curve ) {
174        curves = new Curve[] { curve };
175                initialized = false;
176        }
177        
178        public void setCurves( Curve[] curves ) {
179                if ( curves == null || (curves.length != 1 && curves.length != 3) )
180            throw new IllegalArgumentException( "Curves must be length 1 or 3" );
181        this.curves = curves;
182                initialized = false;
183        }
184        
185        public Curve[] getCurves() {
186                return curves;
187        }
188
189        public String toString() {
190                return "Colors/Curves...";
191        }
192
193        public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src);
194                Object o;
195                if((o=parameters.removeEL(KeyImpl.init("Curves")))!=null)setCurves(ImageFilterUtil.toACurvesFilter$Curve(o,"Curves"));
196                if((o=parameters.removeEL(KeyImpl.init("Curve")))!=null)setCurve(ImageFilterUtil.toCurvesFilter$Curve(o,"Curve"));
197                if((o=parameters.removeEL(KeyImpl.init("Dimensions")))!=null){
198                        int[] dim=ImageFilterUtil.toDimensions(o,"Dimensions");
199                        setDimensions(dim[0],dim[1]);
200                }
201
202                // check for arguments not supported
203                if(parameters.size()>0) {
204                        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 [Curves, Curve, Dimensions]");
205                }
206
207                return filter(src, dst);
208        }
209}
210