001/*
002Copyright 2006 Jerry Huxtable
003
004Licensed under the Apache License, Version 2.0 (the "License");
005you may not use this file except in compliance with the License.
006You may obtain a copy of the License at
007
008   http://www.apache.org/licenses/LICENSE-2.0
009
010Unless required by applicable law or agreed to in writing, software
011distributed under the License is distributed on an "AS IS" BASIS,
012WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013See the License for the specific language governing permissions and
014limitations under the License.
015*/
016
017package com.jhlabs.image;
018
019import java.awt.Color;
020import java.util.Vector;
021import java.io.*;
022
023/**
024 * A Colormap implemented using Catmull-Rom colour splines. The map has a variable number
025 * of knots with a minimum of four. The first and last knots give the tangent at the end
026 * of the spline, and colours are interpolated from the second to the second-last knots.
027 */
028public class SplineColormap extends ArrayColormap {
029
030        private int numKnots = 4;
031    private int[] xKnots = {
032        0, 0, 255, 255
033    };
034    private int[] yKnots = {
035        0xff000000, 0xff000000, 0xffffffff, 0xffffffff,
036    };
037        
038        /**
039     * Construct a SplineColormap.
040     */
041    public SplineColormap() {
042                rebuildGradient();
043        }
044
045        /**
046     * Construct a SplineColormap.
047     * @param xKnots the knot positions
048     * @param yKnots the knot colors
049     */
050        public SplineColormap(int[] xKnots, int[] yKnots) {
051                this.xKnots = xKnots;
052                this.yKnots = yKnots;
053                numKnots = xKnots.length;
054                rebuildGradient();
055        }
056
057    /**
058     * Set a knot color.
059     * @param n the knot index
060     * @param color the color
061     * @see #getKnot
062     */
063        public void setKnot(int n, int color) {
064                yKnots[n] = color;
065                rebuildGradient();
066        }
067        
068    /**
069     * Get a knot color.
070     * @param n the knot index
071     * @return the knot color
072     * @see #setKnot
073     */
074        public int getKnot(int n) {
075                return yKnots[n];
076        }
077
078    /**
079     * Add a new knot.
080     * @param x the knot position
081     * @param color the color
082     * @see #removeKnot
083     */
084        public void addKnot(int x, int color) {
085                int[] nx = new int[numKnots+1];
086                int[] ny = new int[numKnots+1];
087                System.arraycopy(xKnots, 0, nx, 0, numKnots);
088                System.arraycopy(yKnots, 0, ny, 0, numKnots);
089                xKnots = nx;
090                yKnots = ny;
091                xKnots[numKnots] = x;
092                yKnots[numKnots] = color;
093                numKnots++;
094                sortKnots();
095                rebuildGradient();
096        }
097        
098    /**
099     * Remove a knot.
100     * @param n the knot index
101     * @see #addKnot
102     */
103        public void removeKnot(int n) {
104                if (numKnots <= 4)
105                        return;
106                if (n < numKnots-1) {
107                        System.arraycopy(xKnots, n+1, xKnots, n, numKnots-n-1);
108                        System.arraycopy(yKnots, n+1, yKnots, n, numKnots-n-1);
109                }
110                numKnots--;
111                rebuildGradient();
112        }
113        
114    /**
115     * Set a knot position.
116     * @param n the knot index
117     * @param x the knot position
118     */
119        public void setKnotPosition(int n, int x) {
120                xKnots[n] = PixelUtils.clamp(x);
121                sortKnots();
122                rebuildGradient();
123        }
124
125        private void rebuildGradient() {
126                xKnots[0] = -1;
127                xKnots[numKnots-1] = 256;
128                yKnots[0] = yKnots[1];
129                yKnots[numKnots-1] = yKnots[numKnots-2];
130                for (int i = 0; i < 256; i++)
131                        map[i] = ImageMath.colorSpline(i, numKnots, xKnots, yKnots);
132        }
133
134        private void sortKnots() {
135                for (int i = 1; i < numKnots; i++) {
136                        for (int j = 1; j < i; j++) {
137                                if (xKnots[i] < xKnots[j]) {
138                                        int t = xKnots[i];
139                                        xKnots[i] = xKnots[j];
140                                        xKnots[j] = t;
141                                        t = yKnots[i];
142                                        yKnots[i] = yKnots[j];
143                                        yKnots[j] = t;
144                                }
145                        }
146                }
147        }
148
149}