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