FileDocCategorySizeDatePackage
PaletteBuilder.javaAPI DocJava SE 6 API13398Tue Jun 10 00:21:52 BST 2008com.sun.imageio.plugins.common

PaletteBuilder

public class PaletteBuilder extends Object
This class implements the octree quantization method as it is described in the "Graphics Gems" (ISBN 0-12-286166-3, Chapter 4, pages 297-293)

Fields Summary
protected static final int
MAXLEVEL
maximum of tree depth
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
Constructors Summary
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();

        if (transparency != Transparency.OPAQUE) {
            this.requiredSize = size - 1;
            transColor = new ColorNode();
            transColor.isLeaf = true;
        } else {
            this.requiredSize = size;
        }
    
Methods Summary
protected voidbuildPalette()

        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 of given image is not opaque we assume all 
                 * colors with alpha less than 1.0 as fully transparent.
                 */
                if (transparency != Transparency.OPAQUE &&
		    aColor.getAlpha() != 0xff)
		{
                    transColor = insertNode(transColor, aColor, 0);
                } else {
                    root = insertNode(root, aColor, 0);
                }
                if (currSize > requiredSize) {
                    reduceTree();
                }
	    }
        }
    
public static booleancanCreatePalette(javax.imageio.ImageTypeSpecifier type)
Returns true if PaletteBuilder is able to create palette for given image type.

param
type an instance of ImageTypeSpecifier to be indexed.
return
true if the PaletteBuilder is likely to be able to create palette for this image type.
exception
IllegalArgumentException if type is null.

	if (type == null) {
	    throw new IllegalArgumentException("type == null");
	}
        return true;
    
public static booleancanCreatePalette(java.awt.image.RenderedImage image)
Returns true if PaletteBuilder is able to create palette for given rendered image.

param
image an instance of RenderedImage to be indexed.
return
true if the PaletteBuilder is likely to be able to create palette for this image type.
exception
IllegalArgumentException if image is null.

	if (image == null) {
	    throw new IllegalArgumentException("image == null");
	}
	ImageTypeSpecifier type = new ImageTypeSpecifier(image);
	return canCreatePalette(type);
    
public static java.awt.image.IndexColorModelcreateIndexColorModel(java.awt.image.RenderedImage img)
Creates an palette representing colors from given image img. If number of colors in the given image exceeds maximum palette size closest colors would be merged.

exception
IllegalArgumentException if img is null.
exception
UnsupportedOperationException if implemented method is unable to create approximation of img and canCreatePalette returns false.
see
createIndexedImage
see
canCreatePalette

        PaletteBuilder pb = new PaletteBuilder(img);
        pb.buildPalette();
        return pb.getIndexColorModel();
    
public static java.awt.image.RenderedImagecreateIndexedImage(java.awt.image.RenderedImage src)
Creates an image representing given image src using IndexColorModel. Lossless conversion is not always possible (e.g. if number of colors in the given image exceeds maximum palette size). Result image then is an approximation constructed by octree quantization method.

exception
IllegalArgumentException if src is null.
exception
UnsupportedOperationException if implemented method is unable to create approximation of src and canCreatePalette returns false.
see
createIndexColorModel
see
canCreatePalette



                                                                             
         
        PaletteBuilder pb = new PaletteBuilder(src);
        pb.buildPalette();
        return pb.getIndexedImage();
    
protected intfindColorIndex(com.sun.imageio.plugins.common.PaletteBuilder$ColorNode aNode, java.awt.Color aColor)

        if (transparency != Transparency.OPAQUE &&
	    aColor.getAlpha() != 0xff)
	{
            return 0; // default transparnt pixel
        }

        if (aNode.isLeaf) {
            return aNode.paletteIndex;
        } else {
            int childIndex = getBranchIndex(aColor, aNode.level);
	
            return findColorIndex(aNode.children[childIndex], aColor);
        }
    
protected intfindPaletteEntry(com.sun.imageio.plugins.common.PaletteBuilder$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 com.sun.imageio.plugins.common.PaletteBuilder$ColorNodefreeTree(com.sun.imageio.plugins.common.PaletteBuilder$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 intgetBranchIndex(java.awt.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 java.awt.image.IndexColorModelgetIndexColorModel()

        int size = currSize;
        if (transparency == Transparency.BITMASK) {
            size ++; // we need place for transparent color;
        }

        byte[] red = new byte[size];
        byte[] green = new byte[size];
        byte[] blue = new byte[size];

        int index = 0;
        palette = new ColorNode[size];
        if (transparency == Transparency.BITMASK) {
            index ++;
        }

        int lastIndex = findPaletteEntry(root, index, red, green, blue);

        IndexColorModel icm = null;
        if (transparency == Transparency.BITMASK) {
            icm = new IndexColorModel(8, size, red, green, blue, 0);
        } else {
            icm = new IndexColorModel(8, currSize, red, green, blue);
        }
        return icm;
    
protected java.awt.image.RenderedImagegetIndexedImage()

        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;
    
private java.awt.ColorgetSrcColor(int x, int y)

	int argb = srcColorModel.getRGB(srcRaster.getDataElements(x, y, null));
	return new Color(argb, transparency != Transparency.OPAQUE);
    
protected com.sun.imageio.plugins.common.PaletteBuilder$ColorNodeinsertNode(com.sun.imageio.plugins.common.PaletteBuilder$ColorNode aNode, java.awt.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 voidreduceTree()

        int level = reduceList.length - 1;
	while (reduceList[level] == null && level >= 0) {
	    level--;
	}

        ColorNode thisNode = reduceList[level];
	if (thisNode == null) {
            // nothing to reduce
            return;
	}

        // look for element with lower color count
        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
        // NB: current color count for node could be changed in future
        if (thisNode == reduceList[level]) {
            reduceList[level] = thisNode.nextReducible;
        } else {
            pList = thisNode.nextReducible; // we need to process it
            thisNode.nextReducible = pList.nextReducible;
            thisNode = pList;
        }
	
        if (thisNode.isLeaf) {
            return;
        }

        // reduce node
        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;