| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package com.sun.imageio.plugins.common;  | 
 | 
 | 
 | 
import java.awt.Transparency;  | 
 | 
import java.awt.image.BufferedImage;  | 
 | 
import java.awt.image.RenderedImage;  | 
 | 
import java.awt.image.ColorModel;  | 
 | 
import java.awt.image.IndexColorModel;  | 
 | 
import java.awt.image.Raster;  | 
 | 
import java.awt.image.WritableRaster;  | 
 | 
import java.awt.Color;  | 
 | 
import javax.imageio.ImageTypeSpecifier;  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
public class PaletteBuilder { | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    protected static final int MAXLEVEL = 8;  | 
 | 
 | 
 | 
    protected RenderedImage src;  | 
 | 
    protected ColorModel srcColorModel;  | 
 | 
    protected Raster srcRaster;  | 
 | 
 | 
 | 
    protected int requiredSize;  | 
 | 
 | 
 | 
    protected ColorNode root;  | 
 | 
 | 
 | 
    protected int numNodes;  | 
 | 
    protected int maxNodes;  | 
 | 
    protected int currLevel;  | 
 | 
    protected int currSize;  | 
 | 
 | 
 | 
    protected ColorNode[] reduceList;  | 
 | 
    protected ColorNode[] palette;  | 
 | 
 | 
 | 
    protected int transparency;  | 
 | 
    protected ColorNode transColor;  | 
 | 
 | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public static RenderedImage createIndexedImage(RenderedImage src) { | 
 | 
        PaletteBuilder pb = new PaletteBuilder(src);  | 
 | 
        pb.buildPalette();  | 
 | 
        return pb.getIndexedImage();  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public static IndexColorModel createIndexColorModel(RenderedImage img) { | 
 | 
        PaletteBuilder pb = new PaletteBuilder(img);  | 
 | 
        pb.buildPalette();  | 
 | 
        return pb.getIndexColorModel();  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public static boolean canCreatePalette(ImageTypeSpecifier type) { | 
 | 
        if (type == null) { | 
 | 
            throw new IllegalArgumentException("type == null"); | 
 | 
        }  | 
 | 
        return true;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public static boolean canCreatePalette(RenderedImage image) { | 
 | 
        if (image == null) { | 
 | 
            throw new IllegalArgumentException("image == null"); | 
 | 
        }  | 
 | 
        ImageTypeSpecifier type = new ImageTypeSpecifier(image);  | 
 | 
        return canCreatePalette(type);  | 
 | 
    }  | 
 | 
 | 
 | 
    protected RenderedImage getIndexedImage() { | 
 | 
        IndexColorModel icm = getIndexColorModel();  | 
 | 
 | 
 | 
        BufferedImage dst =  | 
 | 
            new BufferedImage(src.getWidth(), src.getHeight(),  | 
 | 
                              BufferedImage.TYPE_BYTE_INDEXED, icm);  | 
 | 
 | 
 | 
        WritableRaster wr = dst.getRaster();  | 
 | 
        for (int y =0; y < dst.getHeight(); y++) { | 
 | 
            for (int x = 0; x < dst.getWidth(); x++) { | 
 | 
                Color aColor = getSrcColor(x,y);  | 
 | 
                wr.setSample(x, y, 0, findColorIndex(root, aColor));  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        return dst;  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    protected PaletteBuilder(RenderedImage src) { | 
 | 
        this(src, 256);  | 
 | 
    }  | 
 | 
 | 
 | 
    protected PaletteBuilder(RenderedImage src, int size) { | 
 | 
        this.src = src;  | 
 | 
        this.srcColorModel = src.getColorModel();  | 
 | 
        this.srcRaster = src.getData();  | 
 | 
 | 
 | 
        this.transparency =  | 
 | 
            srcColorModel.getTransparency();  | 
 | 
 | 
 | 
        this.requiredSize = size;  | 
 | 
    }  | 
 | 
 | 
 | 
    private Color getSrcColor(int x, int y) { | 
 | 
        int argb = srcColorModel.getRGB(srcRaster.getDataElements(x, y, null));  | 
 | 
        return new Color(argb, transparency != Transparency.OPAQUE);  | 
 | 
    }  | 
 | 
 | 
 | 
    protected int findColorIndex(ColorNode aNode, Color aColor) { | 
 | 
        if (transparency != Transparency.OPAQUE &&  | 
 | 
            aColor.getAlpha() != 0xff)  | 
 | 
        { | 
 | 
            return 0;   | 
 | 
        }  | 
 | 
 | 
 | 
        if (aNode.isLeaf) { | 
 | 
            return aNode.paletteIndex;  | 
 | 
        } else { | 
 | 
            int childIndex = getBranchIndex(aColor, aNode.level);  | 
 | 
 | 
 | 
            return findColorIndex(aNode.children[childIndex], aColor);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    protected void buildPalette() { | 
 | 
        reduceList = new ColorNode[MAXLEVEL + 1];  | 
 | 
        for (int i = 0; i < reduceList.length; i++) { | 
 | 
            reduceList[i] = null;  | 
 | 
        }  | 
 | 
 | 
 | 
        numNodes = 0;  | 
 | 
        maxNodes = 0;  | 
 | 
        root = null;  | 
 | 
        currSize = 0;  | 
 | 
        currLevel = MAXLEVEL;  | 
 | 
 | 
 | 
        /*  | 
 | 
          from the book  | 
 | 
 | 
 | 
        */  | 
 | 
 | 
 | 
        int w = src.getWidth();  | 
 | 
        int h = src.getHeight();  | 
 | 
        for (int y = 0; y < h; y++) { | 
 | 
            for (int x = 0; x < w; x++) { | 
 | 
 | 
 | 
                Color aColor = getSrcColor(w - x - 1, h - y - 1);  | 
 | 
                  | 
 | 
 | 
 | 
 | 
 | 
                 */  | 
 | 
                if (transparency != Transparency.OPAQUE &&  | 
 | 
                    aColor.getAlpha() != 0xff)  | 
 | 
                { | 
 | 
                    if (transColor == null) { | 
 | 
                        this.requiredSize --;   | 
 | 
 | 
 | 
                        transColor = new ColorNode();  | 
 | 
                        transColor.isLeaf = true;  | 
 | 
                    }  | 
 | 
                    transColor = insertNode(transColor, aColor, 0);  | 
 | 
                } else { | 
 | 
                    root = insertNode(root, aColor, 0);  | 
 | 
                }  | 
 | 
                if (currSize > requiredSize) { | 
 | 
                    reduceTree();  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    protected ColorNode insertNode(ColorNode aNode, Color aColor, int aLevel) { | 
 | 
 | 
 | 
        if (aNode == null) { | 
 | 
            aNode = new ColorNode();  | 
 | 
            numNodes++;  | 
 | 
            if (numNodes > maxNodes) { | 
 | 
                maxNodes = numNodes;  | 
 | 
            }  | 
 | 
            aNode.level = aLevel;  | 
 | 
            aNode.isLeaf = (aLevel > MAXLEVEL);  | 
 | 
            if (aNode.isLeaf) { | 
 | 
                currSize++;  | 
 | 
            }  | 
 | 
        }  | 
 | 
        aNode.colorCount++;  | 
 | 
        aNode.red   += aColor.getRed();  | 
 | 
        aNode.green += aColor.getGreen();  | 
 | 
        aNode.blue  += aColor.getBlue();  | 
 | 
 | 
 | 
        if (!aNode.isLeaf) { | 
 | 
            int branchIndex = getBranchIndex(aColor, aLevel);  | 
 | 
            if (aNode.children[branchIndex] == null) { | 
 | 
                aNode.childCount++;  | 
 | 
                if (aNode.childCount == 2) { | 
 | 
                    aNode.nextReducible = reduceList[aLevel];  | 
 | 
                    reduceList[aLevel] = aNode;  | 
 | 
                }  | 
 | 
            }  | 
 | 
            aNode.children[branchIndex] =  | 
 | 
                insertNode(aNode.children[branchIndex], aColor, aLevel + 1);  | 
 | 
        }  | 
 | 
        return aNode;  | 
 | 
    }  | 
 | 
 | 
 | 
    protected IndexColorModel getIndexColorModel() { | 
 | 
        int size = currSize;  | 
 | 
        if (transColor != null) { | 
 | 
            size ++;   | 
 | 
        }  | 
 | 
 | 
 | 
        byte[] red = new byte[size];  | 
 | 
        byte[] green = new byte[size];  | 
 | 
        byte[] blue = new byte[size];  | 
 | 
 | 
 | 
        int index = 0;  | 
 | 
        palette = new ColorNode[size];  | 
 | 
        if (transColor != null) { | 
 | 
            index ++;  | 
 | 
        }  | 
 | 
 | 
 | 
        if (root != null) { | 
 | 
            findPaletteEntry(root, index, red, green, blue);  | 
 | 
        }  | 
 | 
 | 
 | 
        IndexColorModel icm = null;  | 
 | 
        if (transColor  != null) { | 
 | 
            icm = new IndexColorModel(8, size, red, green, blue, 0);  | 
 | 
        } else { | 
 | 
            icm = new IndexColorModel(8, currSize, red, green, blue);  | 
 | 
        }  | 
 | 
        return icm;  | 
 | 
    }  | 
 | 
 | 
 | 
    protected int findPaletteEntry(ColorNode aNode, int index,  | 
 | 
                                   byte[] red, byte[] green, byte[] blue)  | 
 | 
        { | 
 | 
            if (aNode.isLeaf) { | 
 | 
                red[index]   = (byte)(aNode.red/aNode.colorCount);  | 
 | 
                green[index] = (byte)(aNode.green/aNode.colorCount);  | 
 | 
                blue[index]  = (byte)(aNode.blue/aNode.colorCount);  | 
 | 
                aNode.paletteIndex = index;  | 
 | 
 | 
 | 
                palette[index] = aNode;  | 
 | 
 | 
 | 
                index++;  | 
 | 
            } else { | 
 | 
                for (int i = 0; i < 8; i++) { | 
 | 
                    if (aNode.children[i] != null) { | 
 | 
                        index = findPaletteEntry(aNode.children[i], index,  | 
 | 
                                                 red, green, blue);  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            }  | 
 | 
            return index;  | 
 | 
        }  | 
 | 
 | 
 | 
    protected int getBranchIndex(Color aColor, int aLevel) { | 
 | 
        if (aLevel > MAXLEVEL || aLevel < 0) { | 
 | 
            throw new IllegalArgumentException("Invalid octree node depth: " + | 
 | 
                                               aLevel);  | 
 | 
        }  | 
 | 
 | 
 | 
        int shift = MAXLEVEL - aLevel;  | 
 | 
        int red_index = 0x1 & ((0xff & aColor.getRed()) >> shift);  | 
 | 
        int green_index = 0x1 & ((0xff & aColor.getGreen()) >> shift);  | 
 | 
        int blue_index = 0x1 & ((0xff & aColor.getBlue()) >> shift);  | 
 | 
        int index = (red_index << 2) | (green_index << 1) | blue_index;  | 
 | 
        return index;  | 
 | 
    }  | 
 | 
 | 
 | 
    protected void reduceTree() { | 
 | 
        int level = reduceList.length - 1;  | 
 | 
        while (reduceList[level] == null && level >= 0) { | 
 | 
            level--;  | 
 | 
        }  | 
 | 
 | 
 | 
        ColorNode thisNode = reduceList[level];  | 
 | 
        if (thisNode == null) { | 
 | 
              | 
 | 
            return;  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        ColorNode pList = thisNode;  | 
 | 
        int minColorCount = pList.colorCount;  | 
 | 
 | 
 | 
        int cnt = 1;  | 
 | 
        while (pList.nextReducible != null) { | 
 | 
            if (minColorCount > pList.nextReducible.colorCount) { | 
 | 
                thisNode = pList;  | 
 | 
                minColorCount = pList.colorCount;  | 
 | 
            }  | 
 | 
            pList = pList.nextReducible;  | 
 | 
            cnt++;  | 
 | 
        }  | 
 | 
 | 
 | 
        // save pointer to first reducible node  | 
 | 
          | 
 | 
        if (thisNode == reduceList[level]) { | 
 | 
            reduceList[level] = thisNode.nextReducible;  | 
 | 
        } else { | 
 | 
            pList = thisNode.nextReducible;   | 
 | 
            thisNode.nextReducible = pList.nextReducible;  | 
 | 
            thisNode = pList;  | 
 | 
        }  | 
 | 
 | 
 | 
        if (thisNode.isLeaf) { | 
 | 
            return;  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        int leafChildCount = thisNode.getLeafChildCount();  | 
 | 
        thisNode.isLeaf = true;  | 
 | 
        currSize -= (leafChildCount - 1);  | 
 | 
        int aDepth = thisNode.level;  | 
 | 
        for (int i = 0; i < 8; i++) { | 
 | 
            thisNode.children[i] = freeTree(thisNode.children[i]);  | 
 | 
        }  | 
 | 
        thisNode.childCount = 0;  | 
 | 
    }  | 
 | 
 | 
 | 
    protected ColorNode freeTree(ColorNode aNode) { | 
 | 
        if (aNode == null) { | 
 | 
            return null;  | 
 | 
        }  | 
 | 
        for (int i = 0; i < 8; i++) { | 
 | 
            aNode.children[i] = freeTree(aNode.children[i]);  | 
 | 
        }  | 
 | 
 | 
 | 
        numNodes--;  | 
 | 
        return null;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    protected class ColorNode { | 
 | 
        public boolean isLeaf;  | 
 | 
        public int childCount;  | 
 | 
        ColorNode[] children;  | 
 | 
 | 
 | 
        public int colorCount;  | 
 | 
        public long red;  | 
 | 
        public long blue;  | 
 | 
        public long green;  | 
 | 
 | 
 | 
        public int paletteIndex;  | 
 | 
 | 
 | 
        public int level;  | 
 | 
        ColorNode nextReducible;  | 
 | 
 | 
 | 
        public ColorNode() { | 
 | 
            isLeaf = false;  | 
 | 
            level = 0;  | 
 | 
            childCount = 0;  | 
 | 
            children = new ColorNode[8];  | 
 | 
            for (int i = 0; i < 8; i++) { | 
 | 
                children[i] = null;  | 
 | 
            }  | 
 | 
 | 
 | 
            colorCount = 0;  | 
 | 
            red = green = blue = 0;  | 
 | 
 | 
 | 
            paletteIndex = 0;  | 
 | 
        }  | 
 | 
 | 
 | 
        public int getLeafChildCount() { | 
 | 
            if (isLeaf) { | 
 | 
                return 0;  | 
 | 
            }  | 
 | 
            int cnt = 0;  | 
 | 
            for (int i = 0; i < children.length; i++) { | 
 | 
                if (children[i] != null) { | 
 | 
                    if (children[i].isLeaf) { | 
 | 
                        cnt ++;  | 
 | 
                    } else { | 
 | 
                        cnt += children[i].getLeafChildCount();  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            }  | 
 | 
            return cnt;  | 
 | 
        }  | 
 | 
 | 
 | 
        public int getRGB() { | 
 | 
            int r = (int)red/colorCount;  | 
 | 
            int g = (int)green/colorCount;  | 
 | 
            int b = (int)blue/colorCount;  | 
 | 
 | 
 | 
            int c = 0xff << 24 | (0xff&r) << 16 | (0xff&g) << 8 | (0xff&b);  | 
 | 
            return c;  | 
 | 
        }  | 
 | 
    }  | 
 | 
}  |