auto import from //depot/cupcake/@135843
diff --git a/tools/ninepatch/Android.mk b/tools/ninepatch/Android.mk
new file mode 100644
index 0000000..42e0205
--- /dev/null
+++ b/tools/ninepatch/Android.mk
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+
+LOCAL_MODULE := ninepatch
+
+include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/ninepatch/src/com/android/ninepatch/GraphicsUtilities.java b/tools/ninepatch/src/com/android/ninepatch/GraphicsUtilities.java
new file mode 100644
index 0000000..7a823ec
--- /dev/null
+++ b/tools/ninepatch/src/com/android/ninepatch/GraphicsUtilities.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ninepatch;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.awt.image.Raster;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsEnvironment;
+import java.awt.Graphics;
+import java.awt.Transparency;
+import java.net.URL;
+import java.io.IOException;
+
+public class GraphicsUtilities {
+    public static BufferedImage loadCompatibleImage(URL resource) throws IOException {
+        BufferedImage image = ImageIO.read(resource);
+        return toCompatibleImage(image);
+    }
+
+    public static BufferedImage createCompatibleImage(int width, int height) {
+        return getGraphicsConfiguration().createCompatibleImage(width, height);
+    }
+
+    public static BufferedImage toCompatibleImage(BufferedImage image) {
+        if (isHeadless()) {
+            return image;
+        }
+
+        if (image.getColorModel().equals(getGraphicsConfiguration().getColorModel())) {
+            return image;
+        }
+
+        BufferedImage compatibleImage = getGraphicsConfiguration().createCompatibleImage(
+                    image.getWidth(), image.getHeight(), image.getTransparency());
+        Graphics g = compatibleImage.getGraphics();
+        g.drawImage(image, 0, 0, null);
+        g.dispose();
+
+        return compatibleImage;
+    }
+
+    public static BufferedImage createCompatibleImage(BufferedImage image, int width, int height) {
+        return getGraphicsConfiguration().createCompatibleImage(width, height,
+                                                   image.getTransparency());
+    }
+
+    private static GraphicsConfiguration getGraphicsConfiguration() {
+        GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment();
+        return environment.getDefaultScreenDevice().getDefaultConfiguration();
+    }
+
+    private static boolean isHeadless() {
+        return GraphicsEnvironment.isHeadless();
+    }
+
+    public static BufferedImage createTranslucentCompatibleImage(int width, int height) {
+        return getGraphicsConfiguration().createCompatibleImage(width, height,
+                Transparency.TRANSLUCENT);
+    }
+
+    public static int[] getPixels(BufferedImage img, int x, int y, int w, int h, int[] pixels) {
+        if (w == 0 || h == 0) {
+            return new int[0];
+        }
+
+        if (pixels == null) {
+            pixels = new int[w * h];
+        } else if (pixels.length < w * h) {
+            throw new IllegalArgumentException("Pixels array must have a length >= w * h");
+        }
+
+        int imageType = img.getType();
+        if (imageType == BufferedImage.TYPE_INT_ARGB || imageType == BufferedImage.TYPE_INT_RGB) {
+            Raster raster = img.getRaster();
+            return (int[]) raster.getDataElements(x, y, w, h, pixels);
+        }
+
+        // Unmanages the image
+        return img.getRGB(x, y, w, h, pixels, 0, w);
+    }
+}
diff --git a/tools/ninepatch/src/com/android/ninepatch/NinePatch.java b/tools/ninepatch/src/com/android/ninepatch/NinePatch.java
new file mode 100644
index 0000000..39e05c6
--- /dev/null
+++ b/tools/ninepatch/src/com/android/ninepatch/NinePatch.java
@@ -0,0 +1,474 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ninepatch;
+
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a 9-Patch bitmap.
+ */
+public class NinePatch {
+    public static final String EXTENSION_9PATCH = ".9.png";
+
+    private BufferedImage mImage;
+    
+    private int mMinWidth;
+    private int mMinHeight;
+
+    private int[] row;
+    private int[] column;
+
+    private boolean mVerticalStartWithPatch;
+    private boolean mHorizontalStartWithPatch;
+
+    private List<Rectangle> mFixed;
+    private List<Rectangle> mPatches;
+    private List<Rectangle> mHorizontalPatches;
+    private List<Rectangle> mVerticalPatches;
+
+    private Pair<Integer> mHorizontalPadding;
+    private Pair<Integer> mVerticalPadding;
+    
+    private float mHorizontalPatchesSum;
+    private float mVerticalPatchesSum;
+
+    private int mRemainderHorizontal;
+
+    private int mRemainderVertical;
+
+    private final URL mFileUrl;
+
+    /**
+     * Loads a 9 patch or regular bitmap.
+     * @param fileUrl the URL of the file to load.
+     * @param convert if <code>true</code>, non 9-patch bitmpa will be converted into a 9 patch.
+     * If <code>false</code> and the bitmap is not a 9 patch, the method will return
+     * <code>null</code>.
+     * @return a {@link NinePatch} or <code>null</code>.
+     * @throws IOException
+     */
+    public static NinePatch load(URL fileUrl, boolean convert) 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);
+    }
+    
+    public int getWidth() {
+        return mImage.getWidth() - 2;
+    }
+
+    public int getHeight() {
+        return mImage.getHeight() - 2;
+    }
+    
+    /**
+     * 
+     * @param padding array of left, top, right, bottom padding
+     * @return
+     */
+    public boolean getPadding(int[] padding) {
+        padding[0] = mHorizontalPadding.mFirst; // left
+        padding[2] = mHorizontalPadding.mSecond; // right
+        padding[1] = mVerticalPadding.mFirst; // top
+        padding[3] = mVerticalPadding.mSecond; // bottom
+        return true;
+    }
+
+
+    public void draw(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 || mHorizontalPatches.size() == 0 ||
+                    mVerticalPatches.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();
+        }
+    }
+    
+    void computePatches(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;
+                }
+            }
+        }
+
+        mVerticalPatchesSum = 0;
+        if (mVerticalPatches.size() > 0) {
+            int start = -1;
+            for (Rectangle rect : mVerticalPatches) {
+                if (rect.y > start) {
+                    mVerticalPatchesSum += rect.height;
+                    start = rect.y;
+                }
+            }
+        }
+    }
+
+    
+    private NinePatch(URL fileUrl, BufferedImage image) {
+        mFileUrl = fileUrl;
+        mImage = image;
+        
+        findPatches();
+    }
+    
+    private void findPatches() {
+        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];
+        
+        // compute the min size, based on the list of fixed sections, which is stored in 
+        // Pair.mFirst
+        mMinHeight = 0;
+        List<Pair<Integer>> fixedSections = left.mFirst;
+        for (Pair<Integer> section : fixedSections) {
+            mMinHeight += section.mSecond - section.mFirst;
+        }
+
+        result = new boolean[1];
+        Pair<List<Pair<Integer>>> top = getPatches(row, result);
+        mHorizontalStartWithPatch = result[0];
+
+        // compute the min size, based on the list of fixed sections, which is stored in 
+        // Pair.mFirst
+
+        mMinWidth = 0;
+        fixedSections = top.mFirst;
+        for (Pair<Integer> section : fixedSections) {
+            mMinWidth += section.mSecond - section.mFirst;
+        }
+
+        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 {
+            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);
+        
+        mHorizontalPatchesSum = 0;
+        if (mHorizontalPatches.size() > 0) {
+            int start = -1;
+            for (Rectangle rect : mHorizontalPatches) {
+                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;
+                }
+            }
+        }
+
+    }
+    
+    private Pair<Integer> getPadding(List<Pair<Integer>> 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);
+        }
+    }
+    
+    private List<Rectangle> getRectangles(List<Pair<Integer>> leftPairs,
+            List<Pair<Integer>> 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 Pair<List<Pair<Integer>>> getPatches(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 static void ensure9Patch(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 static BufferedImage convertTo9Patch(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;
+    }
+    
+    static class Pair<E> {
+        E mFirst;
+        E mSecond;
+
+        Pair(E first, E second) {
+            mFirst = first;
+            mSecond = second;
+        }
+
+        @Override
+        public String toString() {
+            return "Pair[" + mFirst + ", " + mSecond + "]";
+        }
+    }
+}