NinePatchTexture.java revision f9a0a4306d589b4a4e20554fed512a603426bfa1
1f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin/*
2f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Copyright (C) 2010 The Android Open Source Project
3f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
4f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Licensed under the Apache License, Version 2.0 (the "License");
5f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * you may not use this file except in compliance with the License.
6f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * You may obtain a copy of the License at
7f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
8f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *      http://www.apache.org/licenses/LICENSE-2.0
9f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
10f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Unless required by applicable law or agreed to in writing, software
11f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * distributed under the License is distributed on an "AS IS" BASIS,
12f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * See the License for the specific language governing permissions and
14f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * limitations under the License.
15f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin */
16f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
17f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpackage com.android.gallery3d.ui;
18f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
19f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.common.Utils;
20f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
21f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.content.Context;
22f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.Bitmap;
23f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.BitmapFactory;
24f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.Rect;
25f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
26f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.nio.ByteBuffer;
27f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.nio.ByteOrder;
28f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.nio.FloatBuffer;
29f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.LinkedHashMap;
30f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.Map;
31f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport javax.microedition.khronos.opengles.GL11;
32f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
33f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// NinePatchTexture is a texture backed by a NinePatch resource.
34f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin//
35f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// getPaddings() returns paddings specified in the NinePatch.
36f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// getNinePatchChunk() returns the layout data specified in the NinePatch.
37f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin//
38f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpublic class NinePatchTexture extends ResourceTexture {
39f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @SuppressWarnings("unused")
40f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final String TAG = "NinePatchTexture";
41f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private NinePatchChunk mChunk;
42f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private MyCacheMap<Long, NinePatchInstance> mInstanceCache =
43f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            new MyCacheMap<Long, NinePatchInstance>();
44f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
45f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public NinePatchTexture(Context context, int resId) {
46f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        super(context, resId);
47f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
48f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
49f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
50f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    protected Bitmap onGetBitmap() {
51f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mBitmap != null) return mBitmap;
52f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
53f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        BitmapFactory.Options options = new BitmapFactory.Options();
54f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
55f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        Bitmap bitmap = BitmapFactory.decodeResource(
56f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mContext.getResources(), mResId, options);
57f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mBitmap = bitmap;
58f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        setSize(bitmap.getWidth(), bitmap.getHeight());
59f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        byte[] chunkData = bitmap.getNinePatchChunk();
60f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mChunk = chunkData == null
61f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                ? null
62f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                : NinePatchChunk.deserialize(bitmap.getNinePatchChunk());
63f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mChunk == null) {
64f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            throw new RuntimeException("invalid nine-patch image: " + mResId);
65f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
66f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return bitmap;
67f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
68f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
69f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public Rect getPaddings() {
70f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // get the paddings from nine patch
71f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mChunk == null) onGetBitmap();
72f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mChunk.mPaddings;
73f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
74f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
75f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public NinePatchChunk getNinePatchChunk() {
76f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mChunk == null) onGetBitmap();
77f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mChunk;
78f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
79f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
80f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static class MyCacheMap<K, V> extends LinkedHashMap<K, V> {
81f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private int CACHE_SIZE = 16;
82f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private V mJustRemoved;
83f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
84f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public MyCacheMap() {
85f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            super(4, 0.75f, true);
86f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
87f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
88f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
89f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
90f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (size() > CACHE_SIZE) {
91f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mJustRemoved = eldest.getValue();
92f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                return true;
93f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
94f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return false;
95f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
96f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
97f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public V getJustRemoved() {
98f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            V result = mJustRemoved;
99f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mJustRemoved = null;
100f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return result;
101f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
102f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
103f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
104f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private NinePatchInstance findInstance(GLCanvas canvas, int w, int h) {
105f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        long key = w;
106f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        key = (key << 32) | h;
107f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        NinePatchInstance instance = mInstanceCache.get(key);
108f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
109f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (instance == null) {
110f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            instance = new NinePatchInstance(this, w, h);
111f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mInstanceCache.put(key, instance);
112f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            NinePatchInstance removed = mInstanceCache.getJustRemoved();
113f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (removed != null) {
114f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                removed.recycle(canvas);
115f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
116f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
117f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
118f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return instance;
119f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
120f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
121f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
122f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void draw(GLCanvas canvas, int x, int y, int w, int h) {
123f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (!isLoaded(canvas)) {
124f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mInstanceCache.clear();
125f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
126f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
127f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (w != 0 && h != 0) {
128f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            findInstance(canvas, w, h).draw(canvas, this, x, y);
129f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
130f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
131f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
132f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
133f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void recycle() {
134f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        super.recycle();
135f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        GLCanvas canvas = mCanvasRef == null ? null : mCanvasRef.get();
136f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (canvas == null) return;
137f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (NinePatchInstance instance : mInstanceCache.values()) {
138f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            instance.recycle(canvas);
139f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
140f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mInstanceCache.clear();
141f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
142f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin}
143f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
144f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// This keeps data for a specialization of NinePatchTexture with the size
145f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// (width, height). We pre-compute the coordinates for efficiency.
146f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linclass NinePatchInstance {
147f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
148f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @SuppressWarnings("unused")
149f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final String TAG = "NinePatchInstance";
150f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
151f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // We need 16 vertices for a normal nine-patch image (the 4x4 vertices)
152f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int VERTEX_BUFFER_SIZE = 16 * 2;
153f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
154f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // We need 22 indices for a normal nine-patch image, plus 2 for each
155f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // transparent region. Current there are at most 1 transparent region.
156f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int INDEX_BUFFER_SIZE = 22 + 2;
157f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
158f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private FloatBuffer mXyBuffer;
159f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private FloatBuffer mUvBuffer;
160f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private ByteBuffer mIndexBuffer;
161f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
162f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Names for buffer names: xy, uv, index.
163f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int[] mBufferNames;
164f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
165f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mIdxCount;
166f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
167f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public NinePatchInstance(NinePatchTexture tex, int width, int height) {
168f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        NinePatchChunk chunk = tex.getNinePatchChunk();
169f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
170f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (width <= 0 || height <= 0) {
171f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            throw new RuntimeException("invalid dimension");
172f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
173f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
174f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // The code should be easily extended to handle the general cases by
175f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // allocating more space for buffers. But let's just handle the only
176f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // use case.
177f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (chunk.mDivX.length != 2 || chunk.mDivY.length != 2) {
178f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            throw new RuntimeException("unsupported nine patch");
179f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
180f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
181f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float divX[] = new float[4];
182f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float divY[] = new float[4];
183f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float divU[] = new float[4];
184f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float divV[] = new float[4];
185f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
186f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int nx = stretch(divX, divU, chunk.mDivX, tex.getWidth(), width);
187f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int ny = stretch(divY, divV, chunk.mDivY, tex.getHeight(), height);
188f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
189f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        prepareVertexData(divX, divY, divU, divV, nx, ny, chunk.mColor);
190f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
191f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
192f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    /**
193f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * Stretches the texture according to the nine-patch rules. It will
194f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * linearly distribute the strechy parts defined in the nine-patch chunk to
195f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * the target area.
196f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *
197f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * <pre>
198f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *                      source
199f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *          /--------------^---------------\
200f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *         u0    u1       u2  u3     u4   u5
201f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * div ---> |fffff|ssssssss|fff|ssssss|ffff| ---> u
202f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *          |    div0    div1 div2   div3  |
203f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *          |     |       /   /      /    /
204f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *          |     |      /   /     /    /
205f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *          |     |     /   /    /    /
206f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *          |fffff|ssss|fff|sss|ffff| ---> x
207f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *         x0    x1   x2  x3  x4   x5
208f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *          \----------v------------/
209f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *                  target
210f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *
211f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * f: fixed segment
212f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * s: stretchy segment
213f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * </pre>
214f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *
215f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param div the stretch parts defined in nine-patch chunk
216f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param source the length of the texture
217f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param target the length on the drawing plan
218f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param u output, the positions of these dividers in the texture
219f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *        coordinate
220f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param x output, the corresponding position of these dividers on the
221f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *        drawing plan
222f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @return the number of these dividers.
223f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     */
224f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static int stretch(
225f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            float x[], float u[], int div[], int source, int target) {
226f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int textureSize = Utils.nextPowerOf2(source);
227f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float textureBound = (float) source / textureSize;
228f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
229f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float stretch = 0;
230f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 0, n = div.length; i < n; i += 2) {
231f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            stretch += div[i + 1] - div[i];
232f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
233f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
234f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float remaining = target - source + stretch;
235f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
236f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float lastX = 0;
237f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float lastU = 0;
238f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
239f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        x[0] = 0;
240f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        u[0] = 0;
241f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 0, n = div.length; i < n; i += 2) {
242f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // Make the stretchy segment a little smaller to prevent sampling
243f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // on neighboring fixed segments.
244f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // fixed segment
245f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            x[i + 1] = lastX + (div[i] - lastU) + 0.5f;
246f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            u[i + 1] = Math.min((div[i] + 0.5f) / textureSize, textureBound);
247f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
248f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // stretchy segment
249f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            float partU = div[i + 1] - div[i];
250f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            float partX = remaining * partU / stretch;
251f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            remaining -= partX;
252f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            stretch -= partU;
253f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
254f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            lastX = x[i + 1] + partX;
255f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            lastU = div[i + 1];
256f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            x[i + 2] = lastX - 0.5f;
257f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            u[i + 2] = Math.min((lastU - 0.5f)/ textureSize, textureBound);
258f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
259f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // the last fixed segment
260f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        x[div.length + 1] = target;
261f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        u[div.length + 1] = textureBound;
262f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
263f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // remove segments with length 0.
264f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int last = 0;
265f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 1, n = div.length + 2; i < n; ++i) {
266f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if ((x[i] - x[last]) < 1f) continue;
267f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            x[++last] = x[i];
268f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            u[last] = u[i];
269f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
270f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return last + 1;
271f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
272f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
273f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void prepareVertexData(float x[], float y[], float u[], float v[],
274f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            int nx, int ny, int[] color) {
275f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        /*
276f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         * Given a 3x3 nine-patch image, the vertex order is defined as the
277f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         * following graph:
278f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         *
279f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         * (0) (1) (2) (3)
280f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         *  |  /|  /|  /|
281f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         *  | / | / | / |
282f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         * (4) (5) (6) (7)
283f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         *  | \ | \ | \ |
284f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         *  |  \|  \|  \|
285f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         * (8) (9) (A) (B)
286f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         *  |  /|  /|  /|
287f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         *  | / | / | / |
288f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         * (C) (D) (E) (F)
289f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         *
290f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         * And we draw the triangle strip in the following index order:
291f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         *
292f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         * index: 04152637B6A5948C9DAEBF
293f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         */
294f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int pntCount = 0;
295f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float xy[] = new float[VERTEX_BUFFER_SIZE];
296f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float uv[] = new float[VERTEX_BUFFER_SIZE];
297f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int j = 0; j < ny; ++j) {
298f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (int i = 0; i < nx; ++i) {
299f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                int xIndex = (pntCount++) << 1;
300f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                int yIndex = xIndex + 1;
301f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                xy[xIndex] = x[i];
302f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                xy[yIndex] = y[j];
303f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                uv[xIndex] = u[i];
304f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                uv[yIndex] = v[j];
305f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
306f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
307f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
308f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int idxCount = 1;
309f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        boolean isForward = false;
310f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        byte index[] = new byte[INDEX_BUFFER_SIZE];
311f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int row = 0; row < ny - 1; row++) {
312f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            --idxCount;
313f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            isForward = !isForward;
314f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
315f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            int start, end, inc;
316f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (isForward) {
317f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                start = 0;
318f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                end = nx;
319f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                inc = 1;
320f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            } else {
321f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                start = nx - 1;
322f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                end = -1;
323f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                inc = -1;
324f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
325f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
326f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (int col = start; col != end; col += inc) {
327f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                int k = row * nx + col;
328f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (col != start) {
329f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    int colorIdx = row * (nx - 1) + col;
330f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    if (isForward) colorIdx--;
331f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    if (color[colorIdx] == NinePatchChunk.TRANSPARENT_COLOR) {
332f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        index[idxCount] = index[idxCount - 1];
333f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        ++idxCount;
334f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        index[idxCount++] = (byte) k;
335f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
336f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
337f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
338f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                index[idxCount++] = (byte) k;
339f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                index[idxCount++] = (byte) (k + nx);
340f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
341f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
342f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
343f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mIdxCount = idxCount;
344f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
345f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int size = (pntCount * 2) * (Float.SIZE / Byte.SIZE);
346f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mXyBuffer = allocateDirectNativeOrderBuffer(size).asFloatBuffer();
347f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mUvBuffer = allocateDirectNativeOrderBuffer(size).asFloatBuffer();
348f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mIndexBuffer = allocateDirectNativeOrderBuffer(mIdxCount);
349f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
350f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mXyBuffer.put(xy, 0, pntCount * 2).position(0);
351f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mUvBuffer.put(uv, 0, pntCount * 2).position(0);
352f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mIndexBuffer.put(index, 0, idxCount).position(0);
353f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
354f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
355f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static ByteBuffer allocateDirectNativeOrderBuffer(int size) {
356f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
357f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
358f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
359f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void prepareBuffers(GLCanvas canvas) {
360f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mBufferNames = new int[3];
361f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        GL11 gl = canvas.getGLInstance();
362f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        gl.glGenBuffers(3, mBufferNames, 0);
363f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
364f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBufferNames[0]);
365f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        gl.glBufferData(GL11.GL_ARRAY_BUFFER,
366f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mXyBuffer.capacity() * (Float.SIZE / Byte.SIZE),
367f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mXyBuffer, GL11.GL_STATIC_DRAW);
368f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
369f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBufferNames[1]);
370f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        gl.glBufferData(GL11.GL_ARRAY_BUFFER,
371f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mUvBuffer.capacity() * (Float.SIZE / Byte.SIZE),
372f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mUvBuffer, GL11.GL_STATIC_DRAW);
373f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
374f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        gl.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, mBufferNames[2]);
375f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        gl.glBufferData(GL11.GL_ELEMENT_ARRAY_BUFFER,
376f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mIndexBuffer.capacity(),
377f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mIndexBuffer, GL11.GL_STATIC_DRAW);
378f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
379f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // These buffers are never used again.
380f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mXyBuffer = null;
381f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mUvBuffer = null;
382f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mIndexBuffer = null;
383f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
384f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
385f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void draw(GLCanvas canvas, NinePatchTexture tex, int x, int y) {
386f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mBufferNames == null) {
387f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            prepareBuffers(canvas);
388f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
389f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        canvas.drawMesh(tex, x, y, mBufferNames[0], mBufferNames[1],
390f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mBufferNames[2], mIdxCount);
391f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
392f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
393f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void recycle(GLCanvas canvas) {
394f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mBufferNames != null) {
395f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            canvas.deleteBuffer(mBufferNames[0]);
396f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            canvas.deleteBuffer(mBufferNames[1]);
397f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            canvas.deleteBuffer(mBufferNames[2]);
398f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mBufferNames = null;
399f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
400f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
401f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin}
402