FileDocCategorySizeDatePackage
NinePatch.javaAPI DocAndroid 1.5 API16383Wed May 06 22:41:10 BST 2009com.android.ninepatch

NinePatch

public class NinePatch extends Object
Represents a 9-Patch bitmap.

Fields Summary
public static final String
EXTENSION_9PATCH
private BufferedImage
mImage
private int
mMinWidth
private int
mMinHeight
private int[]
row
private int[]
column
private boolean
mVerticalStartWithPatch
private boolean
mHorizontalStartWithPatch
private List
mFixed
private List
mPatches
private List
mHorizontalPatches
private List
mVerticalPatches
private Pair
mHorizontalPadding
private Pair
mVerticalPadding
private float
mHorizontalPatchesSum
private float
mVerticalPatchesSum
private int
mRemainderHorizontal
private int
mRemainderVertical
private final URL
mFileUrl
Constructors Summary
private NinePatch(URL fileUrl, BufferedImage image)

        mFileUrl = fileUrl;
        mImage = image;
        
        findPatches();
    
Methods Summary
voidcomputePatches(int scaledWidth, int scaledHeight)

        boolean measuredWidth = false;
        boolean endRow = true;

        int remainderHorizontal = 0;
        int remainderVertical = 0;

        if (mFixed.size() > 0) {
            int start = mFixed.get(0).y;
            for (Rectangle rect : mFixed) {
                if (rect.y > start) {
                    endRow = true;
                    measuredWidth = true;
                }
                if (!measuredWidth) {
                    remainderHorizontal += rect.width;
                }
                if (endRow) {
                    remainderVertical += rect.height;
                    endRow = false;
                    start = rect.y;
                }
            }
        }

        mRemainderHorizontal = scaledWidth - remainderHorizontal;

        mRemainderVertical = scaledHeight - remainderVertical;

        mHorizontalPatchesSum = 0;
        if (mHorizontalPatches.size() > 0) {
            int start = -1;
            for (Rectangle rect : mHorizontalPatches) {
                if (rect.x > start) {
                    mHorizontalPatchesSum += rect.width;
                    start = rect.x;
                }
            }
        } else {
            int start = -1;
            for (Rectangle rect : mPatches) {
                if (rect.x > start) {
                    mHorizontalPatchesSum += rect.width;
                    start = rect.x;
                }
            }
        }

        mVerticalPatchesSum = 0;
        if (mVerticalPatches.size() > 0) {
            int start = -1;
            for (Rectangle rect : mVerticalPatches) {
                if (rect.y > start) {
                    mVerticalPatchesSum += rect.height;
                    start = rect.y;
                }
            }
        } else {
            int start = -1;
            for (Rectangle rect : mPatches) {
                if (rect.y > start) {
                    mVerticalPatchesSum += rect.height;
                    start = rect.y;
                }
            }
        }
    
private static java.awt.image.BufferedImageconvertTo9Patch(java.awt.image.BufferedImage image)

        BufferedImage buffer = GraphicsUtilities.createTranslucentCompatibleImage(
                image.getWidth() + 2, image.getHeight() + 2);

        Graphics2D g2 = buffer.createGraphics();
        g2.drawImage(image, 1, 1, null);
        g2.dispose();

        return buffer;
    
public voiddraw(java.awt.Graphics2D graphics2D, int x, int y, int scaledWidth, int scaledHeight)

        if (scaledWidth <= 1 || scaledHeight <= 1) {
            return;
        }

        Graphics2D g = (Graphics2D)graphics2D.create();
        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        

        try {
            if (mPatches.size() == 0) {
                g.drawImage(mImage, x, y, scaledWidth, scaledHeight, null);
                return;
            }

            g.translate(x, y);
            x = y = 0;
            
            computePatches(scaledWidth, scaledHeight);
    
            int fixedIndex = 0;
            int horizontalIndex = 0;
            int verticalIndex = 0;
            int patchIndex = 0;
    
            boolean hStretch;
            boolean vStretch;
    
            float vWeightSum = 1.0f;
            float vRemainder = mRemainderVertical;
    
            vStretch = mVerticalStartWithPatch;
            while (y < scaledHeight - 1) {
                hStretch = mHorizontalStartWithPatch;
    
                int height = 0;
                float vExtra = 0.0f;
    
                float hWeightSum = 1.0f;
                float hRemainder = mRemainderHorizontal;
    
                while (x < scaledWidth - 1) {
                    Rectangle r;
                    if (!vStretch) {
                        if (hStretch) {
                            r = mHorizontalPatches.get(horizontalIndex++);
                            float extra = r.width / mHorizontalPatchesSum;
                            int width = (int) (extra * hRemainder / hWeightSum);
                            hWeightSum -= extra;
                            hRemainder -= width;
                            g.drawImage(mImage, x, y, x + width, y + r.height, r.x, r.y,
                                    r.x + r.width, r.y + r.height, null);
                            x += width;
                        } else {
                            r = mFixed.get(fixedIndex++);
                            g.drawImage(mImage, x, y, x + r.width, y + r.height, r.x, r.y,
                                    r.x + r.width, r.y + r.height, null);
                            x += r.width;
                        }
                        height = r.height;
                    } else {
                        if (hStretch) {
                            r = mPatches.get(patchIndex++);
                            vExtra = r.height / mVerticalPatchesSum;
                            height = (int) (vExtra * vRemainder / vWeightSum);
                            float extra = r.width / mHorizontalPatchesSum;
                            int width = (int) (extra * hRemainder / hWeightSum);
                            hWeightSum -= extra;
                            hRemainder -= width;
                            g.drawImage(mImage, x, y, x + width, y + height, r.x, r.y,
                                    r.x + r.width, r.y + r.height, null);
                            x += width;
                        } else {
                            r = mVerticalPatches.get(verticalIndex++);
                            vExtra = r.height / mVerticalPatchesSum;
                            height = (int) (vExtra * vRemainder / vWeightSum);
                            g.drawImage(mImage, x, y, x + r.width, y + height, r.x, r.y,
                                    r.x + r.width, r.y + r.height, null);
                            x += r.width;
                        }
                        
                    }
                    hStretch = !hStretch;
                }
                x = 0;
                y += height;
                if (vStretch) {
                    vWeightSum -= vExtra;
                    vRemainder -= height;
                }
                vStretch = !vStretch;
            }
    
        } finally {
            g.dispose();
        }
    
private static voidensure9Patch(java.awt.image.BufferedImage image)

        int width = image.getWidth();
        int height = image.getHeight();
        for (int i = 0; i < width; i++) {
            int pixel = image.getRGB(i, 0);
            if (pixel != 0 && pixel != 0xFF000000) {
                image.setRGB(i, 0, 0);
            }
            pixel = image.getRGB(i, height - 1);
            if (pixel != 0 && pixel != 0xFF000000) {
                image.setRGB(i, height - 1, 0);
            }
        }
        for (int i = 0; i < height; i++) {
            int pixel = image.getRGB(0, i);
            if (pixel != 0 && pixel != 0xFF000000) {
                image.setRGB(0, i, 0);
            }
            pixel = image.getRGB(width - 1, i);
            if (pixel != 0 && pixel != 0xFF000000) {
                image.setRGB(width - 1, i, 0);
            }
        }
    
private voidfindPatches()

        int width = mImage.getWidth();
        int height = mImage.getHeight();

        row = GraphicsUtilities.getPixels(mImage, 0, 0, width, 1, row);
        column = GraphicsUtilities.getPixels(mImage, 0, 0, 1, height, column);

        boolean[] result = new boolean[1];
        Pair<List<Pair<Integer>>> left = getPatches(column, result);
        mVerticalStartWithPatch = result[0];

        result = new boolean[1];
        Pair<List<Pair<Integer>>> top = getPatches(row, result);
        mHorizontalStartWithPatch = result[0];

        mFixed = getRectangles(left.mFirst, top.mFirst);
        mPatches = getRectangles(left.mSecond, top.mSecond);

        if (mFixed.size() > 0) {
            mHorizontalPatches = getRectangles(left.mFirst, top.mSecond);
            mVerticalPatches = getRectangles(left.mSecond, top.mFirst);
        } else {
            if (top.mFirst.size() > 0) {
                mHorizontalPatches = new ArrayList<Rectangle>(0);
                mVerticalPatches = getVerticalRectangles(top.mFirst);
            } else if (left.mFirst.size() > 0) {
                mHorizontalPatches = getHorizontalRectangles(left.mFirst);
                mVerticalPatches = new ArrayList<Rectangle>(0);
            } else {
                mHorizontalPatches = mVerticalPatches = new ArrayList<Rectangle>(0);
            }
        }

        row = GraphicsUtilities.getPixels(mImage, 0, height - 1, width, 1, row);
        column = GraphicsUtilities.getPixels(mImage, width - 1, 0, 1, height, column);

        top = getPatches(row, result);
        mHorizontalPadding = getPadding(top.mFirst);

        left = getPatches(column, result);
        mVerticalPadding = getPadding(left.mFirst);
    
public intgetHeight()

        return mImage.getHeight() - 2;
    
private java.util.ListgetHorizontalRectangles(java.util.List leftPairs)

        List<Rectangle> rectangles = new ArrayList<Rectangle>();
        for (Pair<Integer> left : leftPairs) {
            int y = left.mFirst;
            int height = left.mSecond - left.mFirst;

            rectangles.add(new Rectangle(1, y, mImage.getWidth() - 2, height));
        }
        return rectangles;
    
private com.android.ninepatch.NinePatch$PairgetPadding(java.util.List pairs)

        if (pairs.size() == 0) {
            return new Pair<Integer>(0, 0);
        } else if (pairs.size() == 1) {
            if (pairs.get(0).mFirst == 1) {
                return new Pair<Integer>(pairs.get(0).mSecond - pairs.get(0).mFirst, 0);
            } else {
                return new Pair<Integer>(0, pairs.get(0).mSecond - pairs.get(0).mFirst);
            }
        } else {
            int index = pairs.size() - 1;
            return new Pair<Integer>(pairs.get(0).mSecond - pairs.get(0).mFirst,
                    pairs.get(index).mSecond - pairs.get(index).mFirst);
        }
    
public booleangetPadding(int[] padding)

param
padding array of left, top, right, bottom padding
return

        padding[0] = mHorizontalPadding.mFirst; // left
        padding[2] = mHorizontalPadding.mSecond; // right
        padding[1] = mVerticalPadding.mFirst; // top
        padding[3] = mVerticalPadding.mSecond; // bottom
        return true;
    
private com.android.ninepatch.NinePatch$PairgetPatches(int[] pixels, boolean[] startWithPatch)

        int lastIndex = 1;
        int lastPixel = pixels[1];
        boolean first = true;

        List<Pair<Integer>> fixed = new ArrayList<Pair<Integer>>();
        List<Pair<Integer>> patches = new ArrayList<Pair<Integer>>();

        for (int i = 1; i < pixels.length - 1; i++) {
            int pixel = pixels[i];
            if (pixel != lastPixel) {
                if (lastPixel == 0xFF000000) {
                    if (first) startWithPatch[0] = true;
                    patches.add(new Pair<Integer>(lastIndex, i));
                } else {
                    fixed.add(new Pair<Integer>(lastIndex, i));
                }
                first = false;

                lastIndex = i;
                lastPixel = pixel;
            }
        }
        if (lastPixel == 0xFF000000) {
            if (first) startWithPatch[0] = true;
            patches.add(new Pair<Integer>(lastIndex, pixels.length - 1));
        } else {
            fixed.add(new Pair<Integer>(lastIndex, pixels.length - 1));
        }

        if (patches.size() == 0) {
            patches.add(new Pair<Integer>(1, pixels.length - 1));
            startWithPatch[0] = true;
            fixed.clear();
        }

        return new Pair<List<Pair<Integer>>>(fixed, patches);
    
private java.util.ListgetRectangles(java.util.List leftPairs, java.util.List topPairs)

        List<Rectangle> rectangles = new ArrayList<Rectangle>();
        for (Pair<Integer> left : leftPairs) {
            int y = left.mFirst;
            int height = left.mSecond - left.mFirst;
            for (Pair<Integer> top : topPairs) {
                int x = top.mFirst;
                int width = top.mSecond - top.mFirst;

                rectangles.add(new Rectangle(x, y, width, height));
            }
        }
        return rectangles;
    
private java.util.ListgetVerticalRectangles(java.util.List topPairs)

        List<Rectangle> rectangles = new ArrayList<Rectangle>();
        for (Pair<Integer> top : topPairs) {
            int x = top.mFirst;
            int width = top.mSecond - top.mFirst;

            rectangles.add(new Rectangle(x, 1, width, mImage.getHeight() - 2));
        }
        return rectangles;
    
public intgetWidth()

        return mImage.getWidth() - 2;
    
public static com.android.ninepatch.NinePatchload(java.net.URL fileUrl, boolean convert)
Loads a 9 patch or regular bitmap.

param
fileUrl the URL of the file to load.
param
convert if true, non 9-patch bitmpa will be converted into a 9 patch. If false and the bitmap is not a 9 patch, the method will return null.
return
a {@link NinePatch} or null.
throws
IOException


                                                              
             
        BufferedImage image = null;
        try {
            image  = GraphicsUtilities.loadCompatibleImage(fileUrl);
        } catch (MalformedURLException e) {
            // really this shouldn't be happening since we're not creating the URL manually.
            return null;
        }
        
        boolean is9Patch = fileUrl.getPath().toLowerCase().endsWith(EXTENSION_9PATCH);
        
        if (is9Patch == false) {
            if (convert) {
                image = convertTo9Patch(image);
            } else {
                return null;
            }
        } else {
            ensure9Patch(image);
        }

        
        return new NinePatch(fileUrl, image);