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
17a4eae1abb4f2547dfbda84301ee764ce35464881John Reckpackage com.android.gallery3d.glrenderer;
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
29a4eae1abb4f2547dfbda84301ee764ce35464881John Reckimport com.android.gallery3d.ui.GLRoot;
30a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Linimport com.android.gallery3d.ui.GLRoot.OnGLIdleListener;
31a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
32a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Linimport java.util.ArrayDeque;
33a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Linimport java.util.ArrayList;
34a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
35a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin// This class is similar to BitmapTexture, except the bitmap is
36a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin// split into tiles. By doing so, we may increase the time required to
37a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin// upload the whole bitmap but we reduce the time of uploading each tile
38a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin// so it make the animation more smooth and prevents jank.
39adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Linpublic class TiledTexture implements Texture {
4004d324e313b8ee36ce878f5e8c92949b32de33c0Owen Lin    private static final int CONTENT_SIZE = 254;
4104d324e313b8ee36ce878f5e8c92949b32de33c0Owen Lin    private static final int BORDER_SIZE = 1;
42a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private static final int TILE_SIZE = CONTENT_SIZE + 2 * BORDER_SIZE;
43a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private static final int INIT_CAPACITY = 8;
44a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
45d71a718afe02282153d86b78f6a44c4783203d54Owen Lin    // We are targeting at 60fps, so we have 16ms for each frame.
46d71a718afe02282153d86b78f6a44c4783203d54Owen Lin    // In this 16ms, we use about 4~8 ms to upload tiles.
47d71a718afe02282153d86b78f6a44c4783203d54Owen Lin    private static final long UPLOAD_TILE_LIMIT = 4; // ms
48d71a718afe02282153d86b78f6a44c4783203d54Owen Lin
49a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private static Tile sFreeTileHead = null;
50a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private static final Object sFreeTileLock = new Object();
51a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
52a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private static Bitmap sUploadBitmap;
53a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private static Canvas sCanvas;
5404d324e313b8ee36ce878f5e8c92949b32de33c0Owen Lin    private static Paint sBitmapPaint;
55a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private static Paint sPaint;
56a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
57a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private int mUploadIndex = 0;
58a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
5918b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong    private final Tile[] mTiles;  // Can be modified in different threads.
6018b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                                  // Should be protected by "synchronized."
61a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private final int mWidth;
62a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private final int mHeight;
63a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private final RectF mSrcRect = new RectF();
64a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private final RectF mDestRect = new RectF();
65a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
66a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    public static class Uploader implements OnGLIdleListener {
67a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        private final ArrayDeque<TiledTexture> mTextures =
68a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                new ArrayDeque<TiledTexture>(INIT_CAPACITY);
69a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
70a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        private final GLRoot mGlRoot;
71a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        private boolean mIsQueued = false;
72a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
73a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        public Uploader(GLRoot glRoot) {
74a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            mGlRoot = glRoot;
75a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
76a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
77a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        public synchronized void clear() {
78a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            mTextures.clear();
79a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
80a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
81a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        public synchronized void addTexture(TiledTexture t) {
82a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            if (t.isReady()) return;
83a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            mTextures.addLast(t);
84a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
85a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            if (mIsQueued) return;
86a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            mIsQueued = true;
87a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            mGlRoot.addOnGLIdleListener(this);
88a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
89a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
90a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        @Override
91a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        public boolean onGLIdle(GLCanvas canvas, boolean renderRequested) {
92a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            ArrayDeque<TiledTexture> deque = mTextures;
93a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            synchronized (this) {
94d71a718afe02282153d86b78f6a44c4783203d54Owen Lin                long now = SystemClock.uptimeMillis();
95d71a718afe02282153d86b78f6a44c4783203d54Owen Lin                long dueTime = now + UPLOAD_TILE_LIMIT;
9618b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                while (now < dueTime && !deque.isEmpty()) {
97a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                    TiledTexture t = deque.peekFirst();
98a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                    if (t.uploadNextTile(canvas)) {
99a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                        deque.removeFirst();
100a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                        mGlRoot.requestRender();
101a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                    }
102d71a718afe02282153d86b78f6a44c4783203d54Owen Lin                    now = SystemClock.uptimeMillis();
103a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                }
104a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                mIsQueued = !mTextures.isEmpty();
105a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
106a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                // return true to keep this listener in the queue
107a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                return mIsQueued;
108a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            }
109a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
110a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
111a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
112a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private static class Tile extends UploadedTexture {
113a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        public int offsetX;
114a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        public int offsetY;
115a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        public Bitmap bitmap;
116a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        public Tile nextFreeTile;
117a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        public int contentWidth;
118a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        public int contentHeight;
119a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
120a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        @Override
121a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        public void setSize(int width, int height) {
122a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            contentWidth = width;
123a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            contentHeight = height;
124a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            mWidth = width + 2 * BORDER_SIZE;
125a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            mHeight = height + 2 * BORDER_SIZE;
126a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            mTextureWidth = TILE_SIZE;
127a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            mTextureHeight = TILE_SIZE;
128a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
129a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
130a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        @Override
131a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        protected Bitmap onGetBitmap() {
132da713ad257719520234fc071c3d11deea8125ed6Mangesh Ghiware            // make a local copy of the reference to the bitmap,
133da713ad257719520234fc071c3d11deea8125ed6Mangesh Ghiware            // since it might be null'd in a different thread. b/8694871
134da713ad257719520234fc071c3d11deea8125ed6Mangesh Ghiware            Bitmap localBitmapRef = bitmap;
135a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            bitmap = null;
136a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
137da713ad257719520234fc071c3d11deea8125ed6Mangesh Ghiware            if (localBitmapRef != null) {
138da713ad257719520234fc071c3d11deea8125ed6Mangesh Ghiware                int x = BORDER_SIZE - offsetX;
139da713ad257719520234fc071c3d11deea8125ed6Mangesh Ghiware                int y = BORDER_SIZE - offsetY;
140da713ad257719520234fc071c3d11deea8125ed6Mangesh Ghiware                int r = localBitmapRef.getWidth() + x;
141da713ad257719520234fc071c3d11deea8125ed6Mangesh Ghiware                int b = localBitmapRef.getHeight() + y;
142da713ad257719520234fc071c3d11deea8125ed6Mangesh Ghiware                sCanvas.drawBitmap(localBitmapRef, x, y, sBitmapPaint);
143da713ad257719520234fc071c3d11deea8125ed6Mangesh Ghiware                localBitmapRef = null;
144da713ad257719520234fc071c3d11deea8125ed6Mangesh Ghiware
145da713ad257719520234fc071c3d11deea8125ed6Mangesh Ghiware                // draw borders if need
146da713ad257719520234fc071c3d11deea8125ed6Mangesh Ghiware                if (x > 0) sCanvas.drawLine(x - 1, 0, x - 1, TILE_SIZE, sPaint);
147da713ad257719520234fc071c3d11deea8125ed6Mangesh Ghiware                if (y > 0) sCanvas.drawLine(0, y - 1, TILE_SIZE, y - 1, sPaint);
148da713ad257719520234fc071c3d11deea8125ed6Mangesh Ghiware                if (r < CONTENT_SIZE) sCanvas.drawLine(r, 0, r, TILE_SIZE, sPaint);
149da713ad257719520234fc071c3d11deea8125ed6Mangesh Ghiware                if (b < CONTENT_SIZE) sCanvas.drawLine(0, b, TILE_SIZE, b, sPaint);
150da713ad257719520234fc071c3d11deea8125ed6Mangesh Ghiware            }
151a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
152a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            return sUploadBitmap;
153a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
154a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
155a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        @Override
156a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        protected void onFreeBitmap(Bitmap bitmap) {
157a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            // do nothing
158a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
159a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
160a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
161a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private static void freeTile(Tile tile) {
162a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        tile.invalidateContent();
163a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        tile.bitmap = null;
164a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        synchronized (sFreeTileLock) {
165a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            tile.nextFreeTile = sFreeTileHead;
166a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            sFreeTileHead = tile;
167a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
168a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
169a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
170a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private static Tile obtainTile() {
171a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        synchronized (sFreeTileLock) {
172a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            Tile result = sFreeTileHead;
173a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            if (result == null) return new Tile();
174a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            sFreeTileHead = result.nextFreeTile;
175a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            result.nextFreeTile = null;
176a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            return result;
177a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
178a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
179a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
180a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private boolean uploadNextTile(GLCanvas canvas) {
181a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        if (mUploadIndex == mTiles.length) return true;
182adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin
18318b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong        synchronized (mTiles) {
18418b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong            Tile next = mTiles[mUploadIndex++];
185d54825d1cad2ab586b6d32853acc09dc6df5945fBobby Georgescu
18618b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong            // Make sure tile has not already been recycled by the time
18718b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong            // this is called (race condition in onGLIdle)
18818b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong            if (next.bitmap != null) {
18918b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                boolean hasBeenLoad = next.isLoaded();
19018b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                next.updateContent(canvas);
191d54825d1cad2ab586b6d32853acc09dc6df5945fBobby Georgescu
19218b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                // It will take some time for a texture to be drawn for the first
19318b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                // time. When scrolling, we need to draw several tiles on the screen
19418b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                // at the same time. It may cause a UI jank even these textures has
19518b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                // been uploaded.
19618b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                if (!hasBeenLoad) next.draw(canvas, 0, 0);
19718b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong            }
198d54825d1cad2ab586b6d32853acc09dc6df5945fBobby Georgescu        }
199a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        return mUploadIndex == mTiles.length;
200a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
201a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
202a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    public TiledTexture(Bitmap bitmap) {
203a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        mWidth = bitmap.getWidth();
204a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        mHeight = bitmap.getHeight();
205a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        ArrayList<Tile> list = new ArrayList<Tile>();
206a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
207a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        for (int x = 0, w = mWidth; x < w; x += CONTENT_SIZE) {
208a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            for (int y = 0, h = mHeight; y < h; y += CONTENT_SIZE) {
209a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                Tile tile = obtainTile();
210a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                tile.offsetX = x;
211a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                tile.offsetY = y;
212a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                tile.bitmap = bitmap;
213a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                tile.setSize(
214a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                        Math.min(CONTENT_SIZE, mWidth - x),
215a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                        Math.min(CONTENT_SIZE, mHeight - y));
216a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                list.add(tile);
217a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            }
218a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
219a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        mTiles = list.toArray(new Tile[list.size()]);
220a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
221a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
222a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    public boolean isReady() {
223a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        return mUploadIndex == mTiles.length;
224a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
225a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
22618b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong    // Can be called in UI thread.
227a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    public void recycle() {
22818b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong        synchronized (mTiles) {
22918b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong            for (int i = 0, n = mTiles.length; i < n; ++i) {
23018b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                freeTile(mTiles[i]);
23118b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong            }
232a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
233a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
234a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
235a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    public static void freeResources() {
236a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        sUploadBitmap = null;
237a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        sCanvas = null;
23804d324e313b8ee36ce878f5e8c92949b32de33c0Owen Lin        sBitmapPaint = null;
239a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        sPaint = null;
240a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
241a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
242a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    public static void prepareResources() {
243a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        sUploadBitmap = Bitmap.createBitmap(TILE_SIZE, TILE_SIZE, Config.ARGB_8888);
244a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        sCanvas = new Canvas(sUploadBitmap);
24504d324e313b8ee36ce878f5e8c92949b32de33c0Owen Lin        sBitmapPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
24604d324e313b8ee36ce878f5e8c92949b32de33c0Owen Lin        sBitmapPaint.setXfermode(new PorterDuffXfermode(Mode.SRC));
24704d324e313b8ee36ce878f5e8c92949b32de33c0Owen Lin        sPaint = new Paint();
24804d324e313b8ee36ce878f5e8c92949b32de33c0Owen Lin        sPaint.setXfermode(new PorterDuffXfermode(Mode.SRC));
249a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        sPaint.setColor(Color.TRANSPARENT);
250a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
251a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
252a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    // We want to draw the "source" on the "target".
253a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    // This method is to find the "output" rectangle which is
254a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    // the corresponding area of the "src".
255a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    //                                   (x,y)  target
256a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    // (x0,y0)  source                     +---------------+
257a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    //    +----------+                     |               |
258a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    //    | src      |                     | output        |
259a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    //    | +--+     |    linear map       | +----+        |
260a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    //    | +--+     |    ---------->      | |    |        |
261a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    //    |          | by (scaleX, scaleY) | +----+        |
262a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    //    +----------+                     |               |
263a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    //      Texture                        +---------------+
264a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    //                                          Canvas
265a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private static void mapRect(RectF output,
266a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            RectF src, float x0, float y0, float x, float y, float scaleX,
267a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            float scaleY) {
268a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        output.set(x + (src.left - x0) * scaleX,
269a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                y + (src.top - y0) * scaleY,
270a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                x + (src.right - x0) * scaleX,
271a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin                y + (src.bottom - y0) * scaleY);
272a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
273a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
274a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    // Draws a mixed color of this texture and a specified color onto the
275a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    // a rectangle. The used color is: from * (1 - ratio) + to * ratio.
276a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    public void drawMixed(GLCanvas canvas, int color, float ratio,
277a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            int x, int y, int width, int height) {
278a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        RectF src = mSrcRect;
279a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        RectF dest = mDestRect;
28018b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong        float scaleX = (float) width / mWidth;
281a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        float scaleY = (float) height / mHeight;
28218b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong        synchronized (mTiles) {
28318b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong            for (int i = 0, n = mTiles.length; i < n; ++i) {
28418b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                Tile t = mTiles[i];
28518b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                src.set(0, 0, t.contentWidth, t.contentHeight);
28618b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                src.offset(t.offsetX, t.offsetY);
28718b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                mapRect(dest, src, 0, 0, x, y, scaleX, scaleY);
28818b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                src.offset(BORDER_SIZE - t.offsetX, BORDER_SIZE - t.offsetY);
28918b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                canvas.drawMixed(t, color, ratio, mSrcRect, mDestRect);
29018b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong            }
291a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
292a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
293a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
294a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    // Draws the texture on to the specified rectangle.
295adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin    @Override
296a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    public void draw(GLCanvas canvas, int x, int y, int width, int height) {
297a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        RectF src = mSrcRect;
298a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        RectF dest = mDestRect;
29918b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong        float scaleX = (float) width / mWidth;
300a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        float scaleY = (float) height / mHeight;
30118b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong        synchronized (mTiles) {
30218b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong            for (int i = 0, n = mTiles.length; i < n; ++i) {
30318b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                Tile t = mTiles[i];
30418b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                src.set(0, 0, t.contentWidth, t.contentHeight);
30518b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                src.offset(t.offsetX, t.offsetY);
30618b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                mapRect(dest, src, 0, 0, x, y, scaleX, scaleY);
30718b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                src.offset(BORDER_SIZE - t.offsetX, BORDER_SIZE - t.offsetY);
30818b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                canvas.drawTexture(t, mSrcRect, mDestRect);
30918b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong            }
310a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
311a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
312a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
313a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    // Draws a sub region of this texture on to the specified rectangle.
314a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    public void draw(GLCanvas canvas, RectF source, RectF target) {
315a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        RectF src = mSrcRect;
316a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        RectF dest = mDestRect;
317a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        float x0 = source.left;
318a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        float y0 = source.top;
319a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        float x = target.left;
320a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        float y = target.top;
321a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        float scaleX = target.width() / source.width();
322a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        float scaleY = target.height() / source.height();
323a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
32418b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong        synchronized (mTiles) {
32518b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong            for (int i = 0, n = mTiles.length; i < n; ++i) {
32618b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                Tile t = mTiles[i];
32718b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                src.set(0, 0, t.contentWidth, t.contentHeight);
32818b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                src.offset(t.offsetX, t.offsetY);
32918b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                if (!src.intersect(source)) continue;
33018b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                mapRect(dest, src, x0, y0, x, y, scaleX, scaleY);
33118b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                src.offset(BORDER_SIZE - t.offsetX, BORDER_SIZE - t.offsetY);
33218b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong                canvas.drawTexture(t, src, dest);
33318b388839ed9858a3b35cec9a636e5cde58a528aAngus Kong            }
334a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
335a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
336adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin
337adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin    @Override
338adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin    public int getWidth() {
339adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin        return mWidth;
340adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin    }
341adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin
342adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin    @Override
343adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin    public int getHeight() {
344adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin        return mHeight;
345adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin    }
346adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin
347adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin    @Override
348adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin    public void draw(GLCanvas canvas, int x, int y) {
349adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin        draw(canvas, x, y, mWidth, mHeight);
350adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin    }
351adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin
352adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin    @Override
353adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin    public boolean isOpaque() {
354adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin        return false;
355adee31f028d839e7baa8f9e052dc4e4d60b137daOwen Lin    }
356a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin}
357