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
17a4eae1abb4f2547dfbda84301ee764ce35464881John Reckpackage com.android.gallery3d.glrenderer;
18f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
19f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.content.Context;
20f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.Bitmap;
21f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.BitmapFactory;
22f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.Rect;
23f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
242b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Linimport com.android.gallery3d.common.Utils;
252b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Lin
26f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.nio.ByteBuffer;
27f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.nio.ByteOrder;
28f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.nio.FloatBuffer;
292b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Lin
30f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// NinePatchTexture is a texture backed by a NinePatch resource.
31f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin//
32f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// getPaddings() returns paddings specified in the NinePatch.
33f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// getNinePatchChunk() returns the layout data specified in the NinePatch.
34f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin//
35f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpublic class NinePatchTexture extends ResourceTexture {
36f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @SuppressWarnings("unused")
37f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final String TAG = "NinePatchTexture";
38f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private NinePatchChunk mChunk;
3995018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang    private SmallCache<NinePatchInstance> mInstanceCache
4095018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang            = new SmallCache<NinePatchInstance>();
41f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
42f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public NinePatchTexture(Context context, int resId) {
43f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        super(context, resId);
44f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
45f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
46f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
47f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    protected Bitmap onGetBitmap() {
48f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mBitmap != null) return mBitmap;
49f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
50f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        BitmapFactory.Options options = new BitmapFactory.Options();
51f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
52f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        Bitmap bitmap = BitmapFactory.decodeResource(
53f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mContext.getResources(), mResId, options);
54f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mBitmap = bitmap;
55f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        setSize(bitmap.getWidth(), bitmap.getHeight());
56f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        byte[] chunkData = bitmap.getNinePatchChunk();
57f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mChunk = chunkData == null
58f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                ? null
59f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                : NinePatchChunk.deserialize(bitmap.getNinePatchChunk());
60f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mChunk == null) {
61f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            throw new RuntimeException("invalid nine-patch image: " + mResId);
62f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
63f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return bitmap;
64f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
65f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
66f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public Rect getPaddings() {
67f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // get the paddings from nine patch
68f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mChunk == null) onGetBitmap();
69f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mChunk.mPaddings;
70f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
71f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
72f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public NinePatchChunk getNinePatchChunk() {
73f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mChunk == null) onGetBitmap();
74f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mChunk;
75f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
76f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
7795018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang    // This is a simple cache for a small number of things. Linear search
7895018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang    // is used because the cache is small. It also tries to remove less used
7995018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang    // item when the cache is full by moving the often-used items to the front.
8095018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang    private static class SmallCache<V> {
8195018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang        private static final int CACHE_SIZE = 16;
8295018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang        private static final int CACHE_SIZE_START_MOVE = CACHE_SIZE / 2;
8395018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang        private int[] mKey = new int[CACHE_SIZE];
8495018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang        private V[] mValue = (V[]) new Object[CACHE_SIZE];
8595018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang        private int mCount;  // number of items in this cache
8695018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang
8795018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang        // Puts a value into the cache. If the cache is full, also returns
8895018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang        // a less used item, otherwise returns null.
8995018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang        public V put(int key, V value) {
9095018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang            if (mCount == CACHE_SIZE) {
9195018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang                V old = mValue[CACHE_SIZE - 1];  // remove the last item
9295018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang                mKey[CACHE_SIZE - 1] = key;
9395018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang                mValue[CACHE_SIZE - 1] = value;
9495018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang                return old;
9595018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang            } else {
9695018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang                mKey[mCount] = key;
9795018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang                mValue[mCount] = value;
9895018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang                mCount++;
9995018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang                return null;
10095018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang            }
10195018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang        }
102f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
10395018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang        public V get(int key) {
10495018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang            for (int i = 0; i < mCount; i++) {
10595018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang                if (mKey[i] == key) {
10695018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang                    // Move the accessed item one position to the front, so it
10795018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang                    // will less likely to be removed when cache is full. Only
10895018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang                    // do this if the cache is starting to get full.
10995018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang                    if (mCount > CACHE_SIZE_START_MOVE && i > 0) {
11095018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang                        int tmpKey = mKey[i];
11195018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang                        mKey[i] = mKey[i - 1];
11295018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang                        mKey[i - 1] = tmpKey;
11395018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang
11495018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang                        V tmpValue = mValue[i];
11595018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang                        mValue[i] = mValue[i - 1];
11695018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang                        mValue[i - 1] = tmpValue;
11795018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang                    }
11895018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang                    return mValue[i];
11995018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang                }
12095018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang            }
12195018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang            return null;
122f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
123f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
12495018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang        public void clear() {
12595018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang            for (int i = 0; i < mCount; i++) {
12695018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang                mValue[i] = null;  // make sure it's can be garbage-collected.
127f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
12895018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang            mCount = 0;
12995018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang        }
13095018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang
13195018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang        public int size() {
13295018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang            return mCount;
133f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
134f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
13595018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang        public V valueAt(int i) {
13695018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang            return mValue[i];
137f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
138f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
139f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
140f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private NinePatchInstance findInstance(GLCanvas canvas, int w, int h) {
14195018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang        int key = w;
14295018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang        key = (key << 16) | h;
143f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        NinePatchInstance instance = mInstanceCache.get(key);
144f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
145f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (instance == null) {
146f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            instance = new NinePatchInstance(this, w, h);
14795018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang            NinePatchInstance removed = mInstanceCache.put(key, instance);
148f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (removed != null) {
149f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                removed.recycle(canvas);
150f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
151f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
152f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
153f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return instance;
154f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
155f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
156f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
157f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void draw(GLCanvas canvas, int x, int y, int w, int h) {
15837c605949219b8bf54c165c34d6405f5f2989f50Owen Lin        if (!isLoaded()) {
159f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mInstanceCache.clear();
160f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
161f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
162f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (w != 0 && h != 0) {
163f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            findInstance(canvas, w, h).draw(canvas, this, x, y);
164f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
165f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
166f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
167f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
168f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void recycle() {
169f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        super.recycle();
1703950038a697470bb8b7cd6798716aecd8285eb00Chih-Chung Chang        GLCanvas canvas = mCanvasRef;
171f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (canvas == null) return;
17295018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang        int n = mInstanceCache.size();
17395018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang        for (int i = 0; i < n; i++) {
17495018d10aa68ecbea7ebce434733341a642510deChih-Chung Chang            NinePatchInstance instance = mInstanceCache.valueAt(i);
175f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            instance.recycle(canvas);
176f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
177f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mInstanceCache.clear();
178f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
179f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin}
180f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
181f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// This keeps data for a specialization of NinePatchTexture with the size
182f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// (width, height). We pre-compute the coordinates for efficiency.
183f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linclass NinePatchInstance {
184f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
185f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @SuppressWarnings("unused")
186f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final String TAG = "NinePatchInstance";
187f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
188f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // We need 16 vertices for a normal nine-patch image (the 4x4 vertices)
189f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int VERTEX_BUFFER_SIZE = 16 * 2;
190f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
191f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // We need 22 indices for a normal nine-patch image, plus 2 for each
192f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // transparent region. Current there are at most 1 transparent region.
193f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int INDEX_BUFFER_SIZE = 22 + 2;
194f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
195f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private FloatBuffer mXyBuffer;
196f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private FloatBuffer mUvBuffer;
197f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private ByteBuffer mIndexBuffer;
198f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
199f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Names for buffer names: xy, uv, index.
2006eb33768a15e2b4cc647bc55474568cf710876dbGeorge Mount    private int mXyBufferName = -1;
2016eb33768a15e2b4cc647bc55474568cf710876dbGeorge Mount    private int mUvBufferName;
2026eb33768a15e2b4cc647bc55474568cf710876dbGeorge Mount    private int mIndexBufferName;
203f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
204f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mIdxCount;
205f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
206f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public NinePatchInstance(NinePatchTexture tex, int width, int height) {
207f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        NinePatchChunk chunk = tex.getNinePatchChunk();
208f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
209f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (width <= 0 || height <= 0) {
210f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            throw new RuntimeException("invalid dimension");
211f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
212f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
213f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // The code should be easily extended to handle the general cases by
214f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // allocating more space for buffers. But let's just handle the only
215f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // use case.
216f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (chunk.mDivX.length != 2 || chunk.mDivY.length != 2) {
217f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            throw new RuntimeException("unsupported nine patch");
218f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
219f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
220f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float divX[] = new float[4];
221f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float divY[] = new float[4];
222f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float divU[] = new float[4];
223f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float divV[] = new float[4];
224f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
225f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int nx = stretch(divX, divU, chunk.mDivX, tex.getWidth(), width);
226f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int ny = stretch(divY, divV, chunk.mDivY, tex.getHeight(), height);
227f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
228f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        prepareVertexData(divX, divY, divU, divV, nx, ny, chunk.mColor);
229f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
230f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
231f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    /**
232f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * Stretches the texture according to the nine-patch rules. It will
233f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * linearly distribute the strechy parts defined in the nine-patch chunk to
234f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * the target area.
235f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *
236f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * <pre>
237f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *                      source
238f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *          /--------------^---------------\
239f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *         u0    u1       u2  u3     u4   u5
240f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * div ---> |fffff|ssssssss|fff|ssssss|ffff| ---> u
241f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *          |    div0    div1 div2   div3  |
242f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *          |     |       /   /      /    /
243f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *          |     |      /   /     /    /
244f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *          |     |     /   /    /    /
245f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *          |fffff|ssss|fff|sss|ffff| ---> x
246f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *         x0    x1   x2  x3  x4   x5
247f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *          \----------v------------/
248f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *                  target
249f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *
250f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * f: fixed segment
251f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * s: stretchy segment
252f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * </pre>
253f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *
254f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param div the stretch parts defined in nine-patch chunk
255f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param source the length of the texture
256f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param target the length on the drawing plan
257f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param u output, the positions of these dividers in the texture
258f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *        coordinate
259f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param x output, the corresponding position of these dividers on the
260f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *        drawing plan
261f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @return the number of these dividers.
262f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     */
263f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static int stretch(
264f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            float x[], float u[], int div[], int source, int target) {
265f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int textureSize = Utils.nextPowerOf2(source);
266f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float textureBound = (float) source / textureSize;
267f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
268f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float stretch = 0;
269f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 0, n = div.length; i < n; i += 2) {
270f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            stretch += div[i + 1] - div[i];
271f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
272f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
273f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float remaining = target - source + stretch;
274f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
275f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float lastX = 0;
276f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float lastU = 0;
277f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
278f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        x[0] = 0;
279f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        u[0] = 0;
280f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 0, n = div.length; i < n; i += 2) {
281f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // Make the stretchy segment a little smaller to prevent sampling
282f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // on neighboring fixed segments.
283f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // fixed segment
284f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            x[i + 1] = lastX + (div[i] - lastU) + 0.5f;
285f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            u[i + 1] = Math.min((div[i] + 0.5f) / textureSize, textureBound);
286f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
287f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // stretchy segment
288f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            float partU = div[i + 1] - div[i];
289f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            float partX = remaining * partU / stretch;
290f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            remaining -= partX;
291f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            stretch -= partU;
292f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
293f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            lastX = x[i + 1] + partX;
294f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            lastU = div[i + 1];
295f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            x[i + 2] = lastX - 0.5f;
296f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            u[i + 2] = Math.min((lastU - 0.5f)/ textureSize, textureBound);
297f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
298f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // the last fixed segment
299f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        x[div.length + 1] = target;
300f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        u[div.length + 1] = textureBound;
301f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
302f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // remove segments with length 0.
303f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int last = 0;
304f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 1, n = div.length + 2; i < n; ++i) {
305f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if ((x[i] - x[last]) < 1f) continue;
306f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            x[++last] = x[i];
307f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            u[last] = u[i];
308f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
309f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return last + 1;
310f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
311f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
312f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void prepareVertexData(float x[], float y[], float u[], float v[],
313f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            int nx, int ny, int[] color) {
314f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        /*
315f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         * Given a 3x3 nine-patch image, the vertex order is defined as the
316f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         * following graph:
317f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         *
318f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         * (0) (1) (2) (3)
319f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         *  |  /|  /|  /|
320f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         *  | / | / | / |
321f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         * (4) (5) (6) (7)
322f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         *  | \ | \ | \ |
323f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         *  |  \|  \|  \|
324f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         * (8) (9) (A) (B)
325f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         *  |  /|  /|  /|
326f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         *  | / | / | / |
327f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         * (C) (D) (E) (F)
328f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         *
329f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         * And we draw the triangle strip in the following index order:
330f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         *
331f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         * index: 04152637B6A5948C9DAEBF
332f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         */
333f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int pntCount = 0;
334f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float xy[] = new float[VERTEX_BUFFER_SIZE];
335f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float uv[] = new float[VERTEX_BUFFER_SIZE];
336f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int j = 0; j < ny; ++j) {
337f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (int i = 0; i < nx; ++i) {
338f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                int xIndex = (pntCount++) << 1;
339f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                int yIndex = xIndex + 1;
340f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                xy[xIndex] = x[i];
341f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                xy[yIndex] = y[j];
342f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                uv[xIndex] = u[i];
343f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                uv[yIndex] = v[j];
344f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
345f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
346f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
347f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int idxCount = 1;
348f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        boolean isForward = false;
349f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        byte index[] = new byte[INDEX_BUFFER_SIZE];
350f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int row = 0; row < ny - 1; row++) {
351f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            --idxCount;
352f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            isForward = !isForward;
353f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
354f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            int start, end, inc;
355f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (isForward) {
356f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                start = 0;
357f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                end = nx;
358f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                inc = 1;
359f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            } else {
360f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                start = nx - 1;
361f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                end = -1;
362f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                inc = -1;
363f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
364f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
365f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (int col = start; col != end; col += inc) {
366f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                int k = row * nx + col;
367f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (col != start) {
368f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    int colorIdx = row * (nx - 1) + col;
369f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    if (isForward) colorIdx--;
370f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    if (color[colorIdx] == NinePatchChunk.TRANSPARENT_COLOR) {
371f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        index[idxCount] = index[idxCount - 1];
372f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        ++idxCount;
373f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        index[idxCount++] = (byte) k;
374f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
375f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
376f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
377f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                index[idxCount++] = (byte) k;
378f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                index[idxCount++] = (byte) (k + nx);
379f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
380f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
381f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
382f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mIdxCount = idxCount;
383f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
384f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int size = (pntCount * 2) * (Float.SIZE / Byte.SIZE);
385f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mXyBuffer = allocateDirectNativeOrderBuffer(size).asFloatBuffer();
386f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mUvBuffer = allocateDirectNativeOrderBuffer(size).asFloatBuffer();
387f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mIndexBuffer = allocateDirectNativeOrderBuffer(mIdxCount);
388f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
389f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mXyBuffer.put(xy, 0, pntCount * 2).position(0);
390f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mUvBuffer.put(uv, 0, pntCount * 2).position(0);
391f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mIndexBuffer.put(index, 0, idxCount).position(0);
392f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
393f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
394f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static ByteBuffer allocateDirectNativeOrderBuffer(int size) {
395f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
396f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
397f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
398f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void prepareBuffers(GLCanvas canvas) {
3996eb33768a15e2b4cc647bc55474568cf710876dbGeorge Mount        mXyBufferName = canvas.uploadBuffer(mXyBuffer);
4006eb33768a15e2b4cc647bc55474568cf710876dbGeorge Mount        mUvBufferName = canvas.uploadBuffer(mUvBuffer);
4016eb33768a15e2b4cc647bc55474568cf710876dbGeorge Mount        mIndexBufferName = canvas.uploadBuffer(mIndexBuffer);
402f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
403f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // These buffers are never used again.
404f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mXyBuffer = null;
405f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mUvBuffer = null;
406f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mIndexBuffer = null;
407f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
408f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
409f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void draw(GLCanvas canvas, NinePatchTexture tex, int x, int y) {
4106eb33768a15e2b4cc647bc55474568cf710876dbGeorge Mount        if (mXyBufferName == -1) {
411f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            prepareBuffers(canvas);
412f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
4136eb33768a15e2b4cc647bc55474568cf710876dbGeorge Mount        canvas.drawMesh(tex, x, y, mXyBufferName, mUvBufferName, mIndexBufferName, mIdxCount);
414f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
415f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
416f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void recycle(GLCanvas canvas) {
4176eb33768a15e2b4cc647bc55474568cf710876dbGeorge Mount        if (mXyBuffer == null) {
4186eb33768a15e2b4cc647bc55474568cf710876dbGeorge Mount            canvas.deleteBuffer(mXyBufferName);
4196eb33768a15e2b4cc647bc55474568cf710876dbGeorge Mount            canvas.deleteBuffer(mUvBufferName);
4206eb33768a15e2b4cc647bc55474568cf710876dbGeorge Mount            canvas.deleteBuffer(mIndexBufferName);
4216eb33768a15e2b4cc647bc55474568cf710876dbGeorge Mount            mXyBufferName = -1;
422f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
423f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
424f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin}
425