Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  package com.alibaba.simpleimage.util;
  
  /*
   * %W% %E% Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved. ORACLE PROPRIETARY/CONFIDENTIAL. Use
   * is subject to license terms.
   */
  
  import java.awt.Color;
 
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)
 
Oracle的实现有bug,而且性能有待改进,所以在Oracle的基础上实现自己的版本

Author(s):
wendell 2011-8-18 下午02:37:44
 
 public class PaletteBuilder {

    
maximum of tree depth
 
     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;

    
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.

Throws:
java.lang.IllegalArgumentException if src is null.
java.lang.UnsupportedOperationException if implemented method is unable to create approximation of src and canCreatePalette returns false.
See also:
createIndexColorModel
canCreatePalette
 
     public static RenderedImage createIndexedImage(RenderedImage src) {
         PaletteBuilder pb = new PaletteBuilder(src);
         pb.buildPalette();
         return pb.getIndexedImage();
     }

    
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.

Throws:
java.lang.IllegalArgumentException if img is null.
java.lang.UnsupportedOperationException if implemented method is unable to create approximation of img and canCreatePalette returns false.
See also:
createIndexedImage
canCreatePalette
 
     public static IndexColorModel createIndexColorModel(RenderedImage img) {
         PaletteBuilder pb = new PaletteBuilder(img);
         pb.buildPalette();
         return pb.getIndexColorModel();
     }

    
Returns true if PaletteBuilder is able to create palette for given image type.

Parameters:
type an instance of ImageTypeSpecifier to be indexed.
Returns:
true if the PaletteBuilder is likely to be able to create palette for this image type.
Throws:
java.lang.IllegalArgumentException if type is null.
 
     public static boolean canCreatePalette(ImageTypeSpecifier type) {
         if (type == null) {
             throw new IllegalArgumentException("type == null");
         }
         return true;
    }

    
Returns true if PaletteBuilder is able to create palette for given rendered image.

Parameters:
image an instance of RenderedImage to be indexed.
Returns:
true if the PaletteBuilder is likely to be able to create palette for this image type.
Throws:
java.lang.IllegalArgumentException if image is null.
    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();
        WritableRaster wr = dst.getRaster();
        for (int y = 0; y < dst.getHeight(); y++) {
            for (int x = 0; x < dst.getWidth(); x++) {
                int aColor = getSrcColor(xy);
                wr.setSample(xy, 0, findColorIndex(aColor));
            }
        }
        return dst;
    }
    protected PaletteBuilder(RenderedImage src){
        this(src, 256);
    }
    protected PaletteBuilder(RenderedImage srcint size){
        this. = src;
        this. = src.getColorModel();
        this. = src.getData();
        this. = .getTransparency();
        if ( != .) {
            this. = size - 1;
             = new ColorNode();
            . = true;
        } else {
            this. = size;
        }
    }

    
原方法签名 private Color getSrcColor(int x, int y),原实现返回一个Color对象,对于一张上百万像素的图片,将会产生 上百万个对象,对性能有严重影响,顾改为返回整形,作为替代

Parameters:
x
y
Returns:
    private int getSrcColor(int xint y) {
        int argb = .getRGB(.getDataElements(xynull));
        if ( == .) {
            argb = 0xff000000 | argb;
        }
        return argb;
    }
    private int getAlpha(int argb) {
        return (argb >> 24) & 0xff;
    }
    private int getBlue(int argb) {
        return (argb >> 0) & 0xFF;
    }
    
    private int getRed(int argb) {
        return (argb >> 16) & 0xFF;
    }
    
    private int getGreen(int argb) {
        return (argb >> 8) & 0xFF;
    }
    protected int findColorIndex(ColorNode aNodeint aColor) {
        if ( != . && getAlpha(aColor) != 0xff) {
            return 0; // default transparnt pixel
        }
        if (aNode.isLeaf) {
            return aNode.paletteIndex;
        } else {
            int childIndex = getBranchIndex(aColoraNode.level);
            return findColorIndex(aNode.children[childIndex], aColor);
        }
    }
    protected void buildPalette() {
         = new ColorNode[ + 1];
        for (int i = 0; i < .i++) {
            [i] = null;
        }
         = 0;
         = 0;
         = null;
         = 0;
         = ;
        /*
         * from the book
         */
        int w = .getWidth();
        int h = .getHeight();
        for (int y = 0; y < hy++) {
            for (int x = 0; x < wx++) {
                int 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 ( != . && getAlpha(aColor) != 0xff) {
                     = insertNode(aColor, 0);
                } else {
                     = insertNode(aColor, 0);
                }
                if ( > ) {
                    reduceTree();
                }
            }
        }
    }
    protected ColorNode insertNode(ColorNode aNodeint aColorint aLevel) {
        if (aNode == null) {
            aNode = new ColorNode();
            ++;
            if ( > ) {
                 = ;
            }
            aNode.level = aLevel;
            aNode.isLeaf = (aLevel > );
            if (aNode.isLeaf) {
                ++;
            }
        }
        aNode.colorCount++;
        aNode.red += getRed(aColor);
        aNode.green += getGreen(aColor);
        aNode.blue += getBlue(aColor);
        if (!aNode.isLeaf) {
            int branchIndex = getBranchIndex(aColoraLevel);
            if (aNode.children[branchIndex] == null) {
                aNode.childCount++;
                if (aNode.childCount == 2) {
                    aNode.nextReducible = [aLevel];
                    [aLevel] = aNode;
                }
            }
            aNode.children[branchIndex] = insertNode(aNode.children[branchIndex], aColoraLevel + 1);
        }
        return aNode;
    }
    protected IndexColorModel getIndexColorModel() {
        int size = ;
        // bugfix 原来的实现会导致带有透明的GIF图片重新索引颜色后变得不透明
        // if (transparency == Transparency.BITMASK) {
        if ( != .) {
            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;
         = new ColorNode[size];
        // bugfix
        // if (transparency == Transparency.BITMASK) {
        if ( != .) {
            index++;
        }
        // bugfix
        if ( != null) {
            findPaletteEntry(indexredgreenblue);
        } else {
            findPaletteEntry(, 0, redgreenblue);
        }
        IndexColorModel icm = null;
        // bugfix
        if ( != .) {
            icm = new IndexColorModel(8, sizeredgreenblue, 0);
        } else {
            icm = new IndexColorModel(8, redgreenblue);
        }
        return icm;
    }
    protected int findPaletteEntry(ColorNode aNodeint indexbyte[] redbyte[] greenbyte[] 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;
            [index] = aNode;
            index++;
        } else {
            for (int i = 0; i < 8; i++) {
                if (aNode.children[i] != null) {
                    index = findPaletteEntry(aNode.children[i], indexredgreenblue);
                }
            }
        }
        return index;
    }
    protected int getBranchIndex(int aColorint aLevel) {
        if (aLevel >  || aLevel < 0) {
            throw new IllegalArgumentException("Invalid octree node depth: " + aLevel);
        }
        int shift =  - aLevel;
        int red_index = 0x1 & ((0xff & getRed(aColor)) >> shift);
        int green_index = 0x1 & ((0xff & getGreen(aColor)) >> shift);
        int blue_index = 0x1 & ((0xff & getBlue(aColor)) >> shift);
        int index = (red_index << 2) | (green_index << 1) | blue_index;
        return index;
    }
    protected void reduceTree() {
        int level = . - 1;
        while ([level] == null && level >= 0) {
            level--;
        }
        ColorNode thisNode = [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 == [level]) {
            [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;
         -= (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]);
        }
        --;
        return null;
    }

    
The node of color tree.
    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(){
             = false;
             = 0;
             = 0;
             = new ColorNode[8];
            for (int i = 0; i < 8; i++) {
                [i] = null;
            }
             = 0;
             =  =  = 0;
             = 0;
        }
        public int getLeafChildCount() {
            if () {
                return 0;
            }
            int cnt = 0;
            for (int i = 0; i < .i++) {
                if ([i] != null) {
                    if ([i].) {
                        cnt++;
                    } else {
                        cnt += [i].getLeafChildCount();
                    }
                }
            }
            return cnt;
        }
        public int getRGB() {
            int r = (int / ;
            int g = (int / ;
            int b = (int / ;
            int c = 0xff << 24 | (0xff & r) << 16 | (0xff & g) << 8 | (0xff & b);
            return c;
        }
    }
New to GrepCode? Check out our FAQ X