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;
036
037/**
038 * A Colormap implemented using Catmull-Rom colour splines. The map has a variable number
039 * of knots with a minimum of four. The first and last knots give the tangent at the end
040 * of the spline, and colours are interpolated from the second to the second-last knots.
041 */
042public class SplineColormap extends ArrayColormap {
043
044        private int numKnots = 4;
045    private int[] xKnots = {
046        0, 0, 255, 255
047    };
048    private int[] yKnots = {
049        0xff000000, 0xff000000, 0xffffffff, 0xffffffff,
050    };
051        
052        /**
053     * Construct a SplineColormap.
054     */
055    public SplineColormap() {
056                rebuildGradient();
057        }
058
059        /**
060     * Construct a SplineColormap.
061     * @param xKnots the knot positions
062     * @param yKnots the knot colors
063     */
064        public SplineColormap(int[] xKnots, int[] yKnots) {
065                this.xKnots = xKnots;
066                this.yKnots = yKnots;
067                numKnots = xKnots.length;
068                rebuildGradient();
069        }
070
071    /**
072     * Set a knot color.
073     * @param n the knot index
074     * @param color the color
075     * @see #getKnot
076     */
077        public void setKnot(int n, int color) {
078                yKnots[n] = color;
079                rebuildGradient();
080        }
081        
082    /**
083     * Get a knot color.
084     * @param n the knot index
085     * @return the knot color
086     * @see #setKnot
087     */
088        public int getKnot(int n) {
089                return yKnots[n];
090        }
091
092    /**
093     * Add a new knot.
094     * @param x the knot position
095     * @param color the color
096     * @see #removeKnot
097     */
098        public void addKnot(int x, int color) {
099                int[] nx = new int[numKnots+1];
100                int[] ny = new int[numKnots+1];
101                System.arraycopy(xKnots, 0, nx, 0, numKnots);
102                System.arraycopy(yKnots, 0, ny, 0, numKnots);
103                xKnots = nx;
104                yKnots = ny;
105                xKnots[numKnots] = x;
106                yKnots[numKnots] = color;
107                numKnots++;
108                sortKnots();
109                rebuildGradient();
110        }
111        
112    /**
113     * Remove a knot.
114     * @param n the knot index
115     * @see #addKnot
116     */
117        public void removeKnot(int n) {
118                if (numKnots <= 4)
119                        return;
120                if (n < numKnots-1) {
121                        System.arraycopy(xKnots, n+1, xKnots, n, numKnots-n-1);
122                        System.arraycopy(yKnots, n+1, yKnots, n, numKnots-n-1);
123                }
124                numKnots--;
125                rebuildGradient();
126        }
127        
128    /**
129     * Set a knot position.
130     * @param n the knot index
131     * @param x the knot position
132     */
133        public void setKnotPosition(int n, int x) {
134                xKnots[n] = PixelUtils.clamp(x);
135                sortKnots();
136                rebuildGradient();
137        }
138
139        private void rebuildGradient() {
140                xKnots[0] = -1;
141                xKnots[numKnots-1] = 256;
142                yKnots[0] = yKnots[1];
143                yKnots[numKnots-1] = yKnots[numKnots-2];
144                for (int i = 0; i < 256; i++)
145                        map[i] = ImageMath.colorSpline(i, numKnots, xKnots, yKnots);
146        }
147
148        private void sortKnots() {
149                for (int i = 1; i < numKnots; i++) {
150                        for (int j = 1; j < i; j++) {
151                                if (xKnots[i] < xKnots[j]) {
152                                        int t = xKnots[i];
153                                        xKnots[i] = xKnots[j];
154                                        xKnots[j] = t;
155                                        t = yKnots[i];
156                                        yKnots[i] = yKnots[j];
157                                        yKnots[j] = t;
158                                }
159                        }
160                }
161        }
162
163}