001    /*
002     * UniformPaletteQuantizer
003     * 
004     * Copyright (c) 2001, 2002, 2003 Marco Schmidt.
005     * All rights reserved.
006     */
007    
008    package net.sourceforge.jiu.color.quantization;
009    
010    import net.sourceforge.jiu.color.quantization.RGBQuantizer;
011    import net.sourceforge.jiu.data.MemoryPaletted8Image;
012    import net.sourceforge.jiu.data.Palette;
013    import net.sourceforge.jiu.data.Paletted8Image;
014    import net.sourceforge.jiu.data.RGB24Image;
015    import net.sourceforge.jiu.data.RGBIndex;
016    import net.sourceforge.jiu.ops.ImageToImageOperation;
017    
018    /**
019     * A color quantizer that maps to a palette which is equidistantly distributed 
020     * in the RGB color cube.
021     * Equidistantly distributed only within each channel. 
022     * @author Marco Schmidt
023     */
024    public class UniformPaletteQuantizer extends ImageToImageOperation implements
025            RGBIndex,
026            RGBQuantizer
027    {
028            private final int RED_BITS;
029            private final int RED_LEFT_SHIFT;
030            private final int RED_RIGHT_SHIFT;
031            private final int[] RED_VALUES;
032            private final int GREEN_BITS;
033            private final int GREEN_LEFT_SHIFT;
034            private final int GREEN_RIGHT_SHIFT;
035            private final int[] GREEN_VALUES;
036            private final int BLUE_BITS;
037            private final int BLUE_RIGHT_SHIFT;
038            private final int[] BLUE_VALUES;
039            private final int TOTAL_BITS;
040            private int[] PALETTE_RED;
041            private int[] PALETTE_GREEN;
042            private int[] PALETTE_BLUE;
043    
044            public UniformPaletteQuantizer(int redBits, int greenBits, int blueBits)
045            {
046                    if (redBits < 1)
047                    {
048                            throw new IllegalArgumentException("Must have at least 1 bit for red.");
049                    }
050                    if (greenBits < 1)
051                    {
052                            throw new IllegalArgumentException("Must have at least 1 bit for green.");
053                    }
054                    if (blueBits < 1)
055                    {
056                            throw new IllegalArgumentException("Must have at least 1 bit for blue.");
057                    }
058                    BLUE_BITS = blueBits;
059                    BLUE_RIGHT_SHIFT = 8 - BLUE_BITS;
060                    BLUE_VALUES = new int[1 << BLUE_BITS];
061                    for (int i = 0; i < BLUE_VALUES.length; i++)
062                            BLUE_VALUES[i] = i * 255 / (BLUE_VALUES.length - 1);
063                    GREEN_BITS = greenBits;
064                    GREEN_RIGHT_SHIFT = 8 - GREEN_BITS;
065                    GREEN_LEFT_SHIFT = BLUE_BITS;
066                    GREEN_VALUES = new int[1 << GREEN_BITS];
067                    for (int i = 0; i < GREEN_VALUES.length; i++)
068                            GREEN_VALUES[i] = i * 255 / (GREEN_VALUES.length - 1);
069                    RED_BITS = redBits;
070                    RED_RIGHT_SHIFT = 8 - RED_BITS;
071                    RED_LEFT_SHIFT = GREEN_BITS + BLUE_BITS;
072                    RED_VALUES = new int[1 << RED_BITS];
073                    for (int i = 0; i < RED_VALUES.length; i++)
074                            RED_VALUES[i] = i * 255 / (RED_VALUES.length - 1);
075                    TOTAL_BITS = RED_BITS + GREEN_BITS + BLUE_BITS;
076                    if (TOTAL_BITS > 8)
077                    {
078                            throw new IllegalArgumentException("Sum of red / green / blue bits must not exceed 8.");
079                    }
080            }
081    
082            public Palette createPalette()
083            {
084                    int numEntries = 1 << TOTAL_BITS;
085                    Palette result = new Palette(numEntries, 255);
086                    PALETTE_RED = new int[numEntries];
087                    PALETTE_GREEN = new int[numEntries];
088                    PALETTE_BLUE = new int[numEntries];
089                    int index = 0;
090                    for (int r = 0; r < (1 << RED_BITS); r++)
091                    {
092                            for (int g = 0; g < (1 << GREEN_BITS); g++)
093                            {
094                                    for (int b = 0; b < (1 << BLUE_BITS); b++)
095                                    {
096                                            //System.out.println(index + ":" + r + ", " + g + ", " + b);
097                                            result.putSample(INDEX_RED, index, RED_VALUES[r]);
098                                            PALETTE_RED[index] = RED_VALUES[r];
099                                            result.putSample(INDEX_GREEN, index, GREEN_VALUES[g]);
100                                            PALETTE_GREEN[index] = GREEN_VALUES[g];
101                                            result.putSample(INDEX_BLUE, index, BLUE_VALUES[b]);
102                                            PALETTE_BLUE[index] = BLUE_VALUES[b];
103                                            index++;
104                                    }
105                            }
106                    }
107                    return result;
108            }
109    
110            public int map(int[] origRgb, int[] quantizedRgb)
111            {
112                    int index = mapToIndex(origRgb[INDEX_RED], origRgb[INDEX_GREEN], origRgb[INDEX_BLUE]);
113                    quantizedRgb[INDEX_RED] = PALETTE_RED[index];
114                    quantizedRgb[INDEX_GREEN] = PALETTE_GREEN[index];
115                    quantizedRgb[INDEX_BLUE] = PALETTE_BLUE[index];
116                    return index;
117            }
118    
119            public final int mapToIndex(int red, int green, int blue)
120            {
121                    return
122                            ((red >> RED_RIGHT_SHIFT) << RED_LEFT_SHIFT) |
123                            ((green >> GREEN_RIGHT_SHIFT) << GREEN_LEFT_SHIFT) |
124                            (blue >> BLUE_RIGHT_SHIFT);
125            }
126    
127            private void process(RGB24Image in, Paletted8Image out)
128            {
129                    final int WIDTH = in.getWidth();
130                    final int HEIGHT = in.getHeight();
131                    if (out == null)
132                    {
133                            out = new MemoryPaletted8Image(WIDTH, HEIGHT, createPalette());
134                    }
135                    for (int y = 0; y < HEIGHT; y++)
136                    {
137                            for (int x = 0; x < WIDTH; x++)
138                            {
139                                    int r = in.getSample(INDEX_RED, x, y);
140                                    int g = in.getSample(INDEX_GREEN, x, y);
141                                    int b = in.getSample(INDEX_BLUE, x, y);
142                                    out.putSample(0, x, y, mapToIndex(r, g, b));
143                            }
144                            setProgress(y, HEIGHT);
145                    }
146                    setOutputImage(out);
147            }
148    
149            public void process()
150            {
151                    process((RGB24Image)getInputImage(), (Paletted8Image)getOutputImage());
152            }
153    }