1a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin/*
2a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin * Copyright (C) 2012 The Android Open Source Project
3a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin *
4a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin * Licensed under the Apache License, Version 2.0 (the "License");
5a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin * you may not use this file except in compliance with the License.
6a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin * You may obtain a copy of the License at
7a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin *
8a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin *      http://www.apache.org/licenses/LICENSE-2.0
9a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin *
10a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin * Unless required by applicable law or agreed to in writing, software
11a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin * distributed under the License is distributed on an "AS IS" BASIS,
12a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin * See the License for the specific language governing permissions and
14a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin * limitations under the License.
15a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin */
16a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
17a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Linpackage com.android.gallery3d.ui;
18a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
19a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Linimport android.graphics.Bitmap;
20a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Linimport android.graphics.Bitmap.Config;
21a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Linimport android.graphics.Canvas;
22a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Linimport android.graphics.Color;
23a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Linimport android.graphics.Paint;
24a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Linimport android.graphics.PorterDuff.Mode;
25a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Linimport android.graphics.PorterDuffXfermode;
26a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Linimport android.graphics.RectF;
27d71a718afe02282153d86b78f6a44c4783203d54Owen Linimport android.os.SystemClock;
28a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
29a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Linimport com.android.gallery3d.ui.GLRoot.OnGLIdleListener;
30a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
31a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Linimport java.util.ArrayDeque;
32a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Linimport java.util.ArrayList;
33a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
34a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin// This class is similar to BitmapTexture, except the bitmap is
35a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin// split into tiles. By doing so, we may increase the time required to
36a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin// upload the whole bitmap but we reduce the time of uploading each tile
37a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin// so it make the animation more smooth and prevents jank.
38adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Linpublic class TiledTexture implements Texture {
3904d324e313b8ee36ce878f5e8c92949b32de33c0Owen Lin    private static final int CONTENT_SIZE = 254;
4004d324e313b8ee36ce878f5e8c92949b32de33c0Owen Lin    private static final int BORDER_SIZE = 1;
41a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private static final int TILE_SIZE = CONTENT_SIZE + 2 * BORDER_SIZE;
42a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private static final int INIT_CAPACITY = 8;
43a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
44d71a718afe02282153d86b78f6a44c4783203d54Owen Lin    // We are targeting at 60fps, so we have 16ms for each frame.
45d71a718afe02282153d86b78f6a44c4783203d54Owen Lin    // In this 16ms, we use about 4~8 ms to upload tiles.
46d71a718afe02282153d86b78f6a44c4783203d54Owen Lin    private static final long UPLOAD_TILE_LIMIT = 4; // ms
47d71a718afe02282153d86b78f6a44c4783203d54Owen Lin
48a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private static Tile sFreeTileHead = null;
49a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private static final Object sFreeTileLock = new Object();
50a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
51a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private static Bitmap sUploadBitmap;
52a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private static Canvas sCanvas;
5304d324e313b8ee36ce878f5e8c92949b32de33c0Owen Lin    private static Paint sBitmapPaint;
54a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private static Paint sPaint;
55a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
56a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private int mUploadIndex = 0;
57a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
5818b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong    private final Tile[] mTiles;  // Can be modified in different threads.
5918b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                                  // Should be protected by "synchronized."
60a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private final int mWidth;
61a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private final int mHeight;
62a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private final RectF mSrcRect = new RectF();
63a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private final RectF mDestRect = new RectF();
64a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
65a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    public static class Uploader implements OnGLIdleListener {
66a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        private final ArrayDeque<TiledTexture> mTextures =
67a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                new ArrayDeque<TiledTexture>(INIT_CAPACITY);
68a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
69a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        private final GLRoot mGlRoot;
70a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        private boolean mIsQueued = false;
71a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
72a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        public Uploader(GLRoot glRoot) {
73a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            mGlRoot = glRoot;
74a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
75a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
76a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        public synchronized void clear() {
77a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            mTextures.clear();
78a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
79a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
80a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        public synchronized void addTexture(TiledTexture t) {
81a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            if (t.isReady()) return;
82a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            mTextures.addLast(t);
83a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
84a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            if (mIsQueued) return;
85a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            mIsQueued = true;
86a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            mGlRoot.addOnGLIdleListener(this);
87a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
88a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
89a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        @Override
90a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        public boolean onGLIdle(GLCanvas canvas, boolean renderRequested) {
91a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            ArrayDeque<TiledTexture> deque = mTextures;
92a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            synchronized (this) {
93d71a718afe02282153d86b78f6a44c4783203d54Owen Lin                long now = SystemClock.uptimeMillis();
94d71a718afe02282153d86b78f6a44c4783203d54Owen Lin                long dueTime = now + UPLOAD_TILE_LIMIT;
9518b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                while (now < dueTime && !deque.isEmpty()) {
96a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                    TiledTexture t = deque.peekFirst();
97a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                    if (t.uploadNextTile(canvas)) {
98a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                        deque.removeFirst();
99a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                        mGlRoot.requestRender();
100a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                    }
101d71a718afe02282153d86b78f6a44c4783203d54Owen Lin                    now = SystemClock.uptimeMillis();
102a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                }
103a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                mIsQueued = !mTextures.isEmpty();
104a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
105a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                // return true to keep this listener in the queue
106a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                return mIsQueued;
107a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            }
108a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
109a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
110a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
111a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private static class Tile extends UploadedTexture {
112a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        public int offsetX;
113a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        public int offsetY;
114a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        public Bitmap bitmap;
115a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        public Tile nextFreeTile;
116a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        public int contentWidth;
117a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        public int contentHeight;
118a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
119a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        @Override
120a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        public void setSize(int width, int height) {
121a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            contentWidth = width;
122a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            contentHeight = height;
123a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            mWidth = width + 2 * BORDER_SIZE;
124a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            mHeight = height + 2 * BORDER_SIZE;
125a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            mTextureWidth = TILE_SIZE;
126a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            mTextureHeight = TILE_SIZE;
127a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
128a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
129a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        @Override
130a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        protected Bitmap onGetBitmap() {
131a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            int x = BORDER_SIZE - offsetX;
132a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            int y = BORDER_SIZE - offsetY;
13304d324e313b8ee36ce878f5e8c92949b32de33c0Owen Lin            int r = bitmap.getWidth() + x;
13418b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong            int b = bitmap.getHeight() + y;
13504d324e313b8ee36ce878f5e8c92949b32de33c0Owen Lin            sCanvas.drawBitmap(bitmap, x, y, sBitmapPaint);
136a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            bitmap = null;
137a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
138a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            // draw borders if need
139a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            if (x > 0) sCanvas.drawLine(x - 1, 0, x - 1, TILE_SIZE, sPaint);
140a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            if (y > 0) sCanvas.drawLine(0, y - 1, TILE_SIZE, y - 1, sPaint);
141a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            if (r < CONTENT_SIZE) sCanvas.drawLine(r, 0, r, TILE_SIZE, sPaint);
142a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            if (b < CONTENT_SIZE) sCanvas.drawLine(0, b, TILE_SIZE, b, sPaint);
143a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
144a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            return sUploadBitmap;
145a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
146a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
147a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        @Override
148a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        protected void onFreeBitmap(Bitmap bitmap) {
149a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            // do nothing
150a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
151a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
152a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
153a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private static void freeTile(Tile tile) {
154a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        tile.invalidateContent();
155a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        tile.bitmap = null;
156a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        synchronized (sFreeTileLock) {
157a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            tile.nextFreeTile = sFreeTileHead;
158a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            sFreeTileHead = tile;
159a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
160a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
161a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
162a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private static Tile obtainTile() {
163a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        synchronized (sFreeTileLock) {
164a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            Tile result = sFreeTileHead;
165a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            if (result == null) return new Tile();
166a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            sFreeTileHead = result.nextFreeTile;
167a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            result.nextFreeTile = null;
168a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            return result;
169a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
170a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
171a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
172a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private boolean uploadNextTile(GLCanvas canvas) {
173a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        if (mUploadIndex == mTiles.length) return true;
174adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin
17518b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong        synchronized (mTiles) {
17618b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong            Tile next = mTiles[mUploadIndex++];
177d54825d1cad2ab586b6d32853acc09dc6df5945fBobby Georgescu
17818b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong            // Make sure tile has not already been recycled by the time
17918b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong            // this is called (race condition in onGLIdle)
18018b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong            if (next.bitmap != null) {
18118b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                boolean hasBeenLoad = next.isLoaded();
18218b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                next.updateContent(canvas);
183d54825d1cad2ab586b6d32853acc09dc6df5945fBobby Georgescu
18418b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                // It will take some time for a texture to be drawn for the first
18518b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                // time. When scrolling, we need to draw several tiles on the screen
18618b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                // at the same time. It may cause a UI jank even these textures has
18718b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                // been uploaded.
18818b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                if (!hasBeenLoad) next.draw(canvas, 0, 0);
18918b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong            }
190d54825d1cad2ab586b6d32853acc09dc6df5945fBobby Georgescu        }
191a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        return mUploadIndex == mTiles.length;
192a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
193a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
194a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    public TiledTexture(Bitmap bitmap) {
195a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        mWidth = bitmap.getWidth();
196a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        mHeight = bitmap.getHeight();
197a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        ArrayList<Tile> list = new ArrayList<Tile>();
198a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
199a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        for (int x = 0, w = mWidth; x < w; x += CONTENT_SIZE) {
200a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            for (int y = 0, h = mHeight; y < h; y += CONTENT_SIZE) {
201a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                Tile tile = obtainTile();
202a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                tile.offsetX = x;
203a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                tile.offsetY = y;
204a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                tile.bitmap = bitmap;
205a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                tile.setSize(
206a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                        Math.min(CONTENT_SIZE, mWidth - x),
207a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                        Math.min(CONTENT_SIZE, mHeight - y));
208a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                list.add(tile);
209a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            }
210a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
211a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        mTiles = list.toArray(new Tile[list.size()]);
212a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
213a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
214a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    public boolean isReady() {
215a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        return mUploadIndex == mTiles.length;
216a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
217a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
21818b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong    // Can be called in UI thread.
219a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    public void recycle() {
22018b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong        synchronized (mTiles) {
22118b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong            for (int i = 0, n = mTiles.length; i < n; ++i) {
22218b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                freeTile(mTiles[i]);
22318b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong            }
224a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
225a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
226a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
227a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    public static void freeResources() {
228a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        sUploadBitmap = null;
229a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        sCanvas = null;
23004d324e313b8ee36ce878f5e8c92949b32de33c0Owen Lin        sBitmapPaint = null;
231a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        sPaint = null;
232a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
233a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
234a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    public static void prepareResources() {
235a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        sUploadBitmap = Bitmap.createBitmap(TILE_SIZE, TILE_SIZE, Config.ARGB_8888);
236a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        sCanvas = new Canvas(sUploadBitmap);
23704d324e313b8ee36ce878f5e8c92949b32de33c0Owen Lin        sBitmapPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
23804d324e313b8ee36ce878f5e8c92949b32de33c0Owen Lin        sBitmapPaint.setXfermode(new PorterDuffXfermode(Mode.SRC));
23904d324e313b8ee36ce878f5e8c92949b32de33c0Owen Lin        sPaint = new Paint();
24004d324e313b8ee36ce878f5e8c92949b32de33c0Owen Lin        sPaint.setXfermode(new PorterDuffXfermode(Mode.SRC));
241a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        sPaint.setColor(Color.TRANSPARENT);
242a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
243a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
244a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    // We want to draw the "source" on the "target".
245a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    // This method is to find the "output" rectangle which is
246a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    // the corresponding area of the "src".
247a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    //                                   (x,y)  target
248a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    // (x0,y0)  source                     +---------------+
249a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    //    +----------+                     |               |
250a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    //    | src      |                     | output        |
251a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    //    | +--+     |    linear map       | +----+        |
252a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    //    | +--+     |    ---------->      | |    |        |
253a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    //    |          | by (scaleX, scaleY) | +----+        |
254a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    //    +----------+                     |               |
255a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    //      Texture                        +---------------+
256a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    //                                          Canvas
257a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private static void mapRect(RectF output,
258a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            RectF src, float x0, float y0, float x, float y, float scaleX,
259a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            float scaleY) {
260a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        output.set(x + (src.left - x0) * scaleX,
261a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                y + (src.top - y0) * scaleY,
262a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                x + (src.right - x0) * scaleX,
263a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                y + (src.bottom - y0) * scaleY);
264a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
265a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
266a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    // Draws a mixed color of this texture and a specified color onto the
267a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    // a rectangle. The used color is: from * (1 - ratio) + to * ratio.
268a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    public void drawMixed(GLCanvas canvas, int color, float ratio,
269a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            int x, int y, int width, int height) {
270a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        RectF src = mSrcRect;
271a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        RectF dest = mDestRect;
27218b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong        float scaleX = (float) width / mWidth;
273a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        float scaleY = (float) height / mHeight;
27418b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong        synchronized (mTiles) {
27518b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong            for (int i = 0, n = mTiles.length; i < n; ++i) {
27618b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                Tile t = mTiles[i];
27718b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                src.set(0, 0, t.contentWidth, t.contentHeight);
27818b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                src.offset(t.offsetX, t.offsetY);
27918b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                mapRect(dest, src, 0, 0, x, y, scaleX, scaleY);
28018b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                src.offset(BORDER_SIZE - t.offsetX, BORDER_SIZE - t.offsetY);
28118b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                canvas.drawMixed(t, color, ratio, mSrcRect, mDestRect);
28218b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong            }
283a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
284a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
285a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
286a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    // Draws the texture on to the specified rectangle.
287adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin    @Override
288a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    public void draw(GLCanvas canvas, int x, int y, int width, int height) {
289a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        RectF src = mSrcRect;
290a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        RectF dest = mDestRect;
29118b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong        float scaleX = (float) width / mWidth;
292a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        float scaleY = (float) height / mHeight;
29318b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong        synchronized (mTiles) {
29418b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong            for (int i = 0, n = mTiles.length; i < n; ++i) {
29518b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                Tile t = mTiles[i];
29618b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                src.set(0, 0, t.contentWidth, t.contentHeight);
29718b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                src.offset(t.offsetX, t.offsetY);
29818b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                mapRect(dest, src, 0, 0, x, y, scaleX, scaleY);
29918b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                src.offset(BORDER_SIZE - t.offsetX, BORDER_SIZE - t.offsetY);
30018b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                canvas.drawTexture(t, mSrcRect, mDestRect);
30118b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong            }
302a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
303a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
304a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
305a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    // Draws a sub region of this texture on to the specified rectangle.
306a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    public void draw(GLCanvas canvas, RectF source, RectF target) {
307a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        RectF src = mSrcRect;
308a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        RectF dest = mDestRect;
309a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        float x0 = source.left;
310a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        float y0 = source.top;
311a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        float x = target.left;
312a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        float y = target.top;
313a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        float scaleX = target.width() / source.width();
314a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        float scaleY = target.height() / source.height();
315a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
31618b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong        synchronized (mTiles) {
31718b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong            for (int i = 0, n = mTiles.length; i < n; ++i) {
31818b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                Tile t = mTiles[i];
31918b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                src.set(0, 0, t.contentWidth, t.contentHeight);
32018b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                src.offset(t.offsetX, t.offsetY);
32118b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                if (!src.intersect(source)) continue;
32218b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                mapRect(dest, src, x0, y0, x, y, scaleX, scaleY);
32318b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                src.offset(BORDER_SIZE - t.offsetX, BORDER_SIZE - t.offsetY);
32418b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                canvas.drawTexture(t, src, dest);
32518b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong            }
326a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
327a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
328adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin
329adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin    @Override
330adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin    public int getWidth() {
331adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin        return mWidth;
332adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin    }
333adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin
334adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin    @Override
335adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin    public int getHeight() {
336adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin        return mHeight;
337adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin    }
338adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin
339adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin    @Override
340adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin    public void draw(GLCanvas canvas, int x, int y) {
341adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin        draw(canvas, x, y, mWidth, mHeight);
342adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin    }
343adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin
344adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin    @Override
345adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin    public boolean isOpaque() {
346adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin        return false;
347adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin    }
348a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin}
349