1d8732610ff48dc57063559d55ea3c84775e30e41John Reck/*
2d8732610ff48dc57063559d55ea3c84775e30e41John Reck * Copyright (C) 2013 The Android Open Source Project
3d8732610ff48dc57063559d55ea3c84775e30e41John Reck *
4d8732610ff48dc57063559d55ea3c84775e30e41John Reck * Licensed under the Apache License, Version 2.0 (the "License");
5d8732610ff48dc57063559d55ea3c84775e30e41John Reck * you may not use this file except in compliance with the License.
6d8732610ff48dc57063559d55ea3c84775e30e41John Reck * You may obtain a copy of the License at
7d8732610ff48dc57063559d55ea3c84775e30e41John Reck *
8d8732610ff48dc57063559d55ea3c84775e30e41John Reck *      http://www.apache.org/licenses/LICENSE-2.0
9d8732610ff48dc57063559d55ea3c84775e30e41John Reck *
10d8732610ff48dc57063559d55ea3c84775e30e41John Reck * Unless required by applicable law or agreed to in writing, software
11d8732610ff48dc57063559d55ea3c84775e30e41John Reck * distributed under the License is distributed on an "AS IS" BASIS,
12d8732610ff48dc57063559d55ea3c84775e30e41John Reck * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d8732610ff48dc57063559d55ea3c84775e30e41John Reck * See the License for the specific language governing permissions and
14d8732610ff48dc57063559d55ea3c84775e30e41John Reck * limitations under the License.
15d8732610ff48dc57063559d55ea3c84775e30e41John Reck */
16d8732610ff48dc57063559d55ea3c84775e30e41John Reck
17d8732610ff48dc57063559d55ea3c84775e30e41John Reckpackage com.android.photos.views;
18d8732610ff48dc57063559d55ea3c84775e30e41John Reck
19d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.content.Context;
20d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.graphics.Bitmap;
21d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.graphics.Rect;
22d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.graphics.RectF;
23d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.support.v4.util.LongSparseArray;
24d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.util.DisplayMetrics;
25d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.util.Log;
26f5ef4801465a48ce3e33e1a75568060b2cf61185John Reckimport android.util.Pools.Pool;
27f5ef4801465a48ce3e33e1a75568060b2cf61185John Reckimport android.util.Pools.SynchronizedPool;
28d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.view.View;
29d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.view.WindowManager;
30d8732610ff48dc57063559d55ea3c84775e30e41John Reck
31d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport com.android.gallery3d.common.Utils;
32f5ef4801465a48ce3e33e1a75568060b2cf61185John Reckimport com.android.gallery3d.glrenderer.BasicTexture;
33d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport com.android.gallery3d.glrenderer.GLCanvas;
34d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport com.android.gallery3d.glrenderer.UploadedTexture;
35d8732610ff48dc57063559d55ea3c84775e30e41John Reck
36f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck/**
37f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck * Handles laying out, decoding, and drawing of tiles in GL
38f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck */
39d8732610ff48dc57063559d55ea3c84775e30e41John Reckpublic class TiledImageRenderer {
40d8732610ff48dc57063559d55ea3c84775e30e41John Reck    public static final int SIZE_UNKNOWN = -1;
41d8732610ff48dc57063559d55ea3c84775e30e41John Reck
42d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private static final String TAG = "TiledImageRenderer";
43d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private static final int UPLOAD_LIMIT = 1;
44d8732610ff48dc57063559d55ea3c84775e30e41John Reck
45d8732610ff48dc57063559d55ea3c84775e30e41John Reck    /*
46d8732610ff48dc57063559d55ea3c84775e30e41John Reck     *  This is the tile state in the CPU side.
47d8732610ff48dc57063559d55ea3c84775e30e41John Reck     *  Life of a Tile:
48d8732610ff48dc57063559d55ea3c84775e30e41John Reck     *      ACTIVATED (initial state)
49d8732610ff48dc57063559d55ea3c84775e30e41John Reck     *              --> IN_QUEUE - by queueForDecode()
50d8732610ff48dc57063559d55ea3c84775e30e41John Reck     *              --> RECYCLED - by recycleTile()
51d8732610ff48dc57063559d55ea3c84775e30e41John Reck     *      IN_QUEUE --> DECODING - by decodeTile()
52d8732610ff48dc57063559d55ea3c84775e30e41John Reck     *               --> RECYCLED - by recycleTile)
53d8732610ff48dc57063559d55ea3c84775e30e41John Reck     *      DECODING --> RECYCLING - by recycleTile()
54d8732610ff48dc57063559d55ea3c84775e30e41John Reck     *               --> DECODED  - by decodeTile()
55d8732610ff48dc57063559d55ea3c84775e30e41John Reck     *               --> DECODE_FAIL - by decodeTile()
56d8732610ff48dc57063559d55ea3c84775e30e41John Reck     *      RECYCLING --> RECYCLED - by decodeTile()
57d8732610ff48dc57063559d55ea3c84775e30e41John Reck     *      DECODED --> ACTIVATED - (after the decoded bitmap is uploaded)
58d8732610ff48dc57063559d55ea3c84775e30e41John Reck     *      DECODED --> RECYCLED - by recycleTile()
59d8732610ff48dc57063559d55ea3c84775e30e41John Reck     *      DECODE_FAIL -> RECYCLED - by recycleTile()
60d8732610ff48dc57063559d55ea3c84775e30e41John Reck     *      RECYCLED --> ACTIVATED - by obtainTile()
61d8732610ff48dc57063559d55ea3c84775e30e41John Reck     */
62d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private static final int STATE_ACTIVATED = 0x01;
63d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private static final int STATE_IN_QUEUE = 0x02;
64d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private static final int STATE_DECODING = 0x04;
65d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private static final int STATE_DECODED = 0x08;
66d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private static final int STATE_DECODE_FAIL = 0x10;
67d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private static final int STATE_RECYCLING = 0x20;
68d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private static final int STATE_RECYCLED = 0x40;
69d8732610ff48dc57063559d55ea3c84775e30e41John Reck
70f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck    private static Pool<Bitmap> sTilePool = new SynchronizedPool<Bitmap>(64);
71d8732610ff48dc57063559d55ea3c84775e30e41John Reck
72d8732610ff48dc57063559d55ea3c84775e30e41John Reck    // TILE_SIZE must be 2^N
73d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private int mTileSize;
74d8732610ff48dc57063559d55ea3c84775e30e41John Reck
75d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private TileSource mModel;
76f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck    private BasicTexture mPreview;
77d8732610ff48dc57063559d55ea3c84775e30e41John Reck    protected int mLevelCount;  // cache the value of mScaledBitmaps.length
78d8732610ff48dc57063559d55ea3c84775e30e41John Reck
79d8732610ff48dc57063559d55ea3c84775e30e41John Reck    // The mLevel variable indicates which level of bitmap we should use.
80d8732610ff48dc57063559d55ea3c84775e30e41John Reck    // Level 0 means the original full-sized bitmap, and a larger value means
81d8732610ff48dc57063559d55ea3c84775e30e41John Reck    // a smaller scaled bitmap (The width and height of each scaled bitmap is
82d8732610ff48dc57063559d55ea3c84775e30e41John Reck    // half size of the previous one). If the value is in [0, mLevelCount), we
83d8732610ff48dc57063559d55ea3c84775e30e41John Reck    // use the bitmap in mScaledBitmaps[mLevel] for display, otherwise the value
84d8732610ff48dc57063559d55ea3c84775e30e41John Reck    // is mLevelCount
85d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private int mLevel = 0;
86d8732610ff48dc57063559d55ea3c84775e30e41John Reck
87d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private int mOffsetX;
88d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private int mOffsetY;
89d8732610ff48dc57063559d55ea3c84775e30e41John Reck
90d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private int mUploadQuota;
91d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private boolean mRenderComplete;
92d8732610ff48dc57063559d55ea3c84775e30e41John Reck
93d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private final RectF mSourceRect = new RectF();
94d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private final RectF mTargetRect = new RectF();
95d8732610ff48dc57063559d55ea3c84775e30e41John Reck
96d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private final LongSparseArray<Tile> mActiveTiles = new LongSparseArray<Tile>();
97d8732610ff48dc57063559d55ea3c84775e30e41John Reck
98d8732610ff48dc57063559d55ea3c84775e30e41John Reck    // The following three queue are guarded by mQueueLock
99d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private final Object mQueueLock = new Object();
100d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private final TileQueue mRecycledQueue = new TileQueue();
101d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private final TileQueue mUploadQueue = new TileQueue();
102d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private final TileQueue mDecodeQueue = new TileQueue();
103d8732610ff48dc57063559d55ea3c84775e30e41John Reck
104d8732610ff48dc57063559d55ea3c84775e30e41John Reck    // The width and height of the full-sized bitmap
105d8732610ff48dc57063559d55ea3c84775e30e41John Reck    protected int mImageWidth = SIZE_UNKNOWN;
106d8732610ff48dc57063559d55ea3c84775e30e41John Reck    protected int mImageHeight = SIZE_UNKNOWN;
107d8732610ff48dc57063559d55ea3c84775e30e41John Reck
108d8732610ff48dc57063559d55ea3c84775e30e41John Reck    protected int mCenterX;
109d8732610ff48dc57063559d55ea3c84775e30e41John Reck    protected int mCenterY;
110d8732610ff48dc57063559d55ea3c84775e30e41John Reck    protected float mScale;
111d8732610ff48dc57063559d55ea3c84775e30e41John Reck    protected int mRotation;
112d8732610ff48dc57063559d55ea3c84775e30e41John Reck
113d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private boolean mLayoutTiles;
114d8732610ff48dc57063559d55ea3c84775e30e41John Reck
115d8732610ff48dc57063559d55ea3c84775e30e41John Reck    // Temp variables to avoid memory allocation
116d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private final Rect mTileRange = new Rect();
117d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private final Rect mActiveRange[] = {new Rect(), new Rect()};
118d8732610ff48dc57063559d55ea3c84775e30e41John Reck
119d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private TileDecoder mTileDecoder;
120d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private boolean mBackgroundTileUploaded;
121d8732610ff48dc57063559d55ea3c84775e30e41John Reck
122d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private int mViewWidth, mViewHeight;
123d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private View mParent;
124d8732610ff48dc57063559d55ea3c84775e30e41John Reck
125f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck    /**
126f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck     * Interface for providing tiles to a {@link TiledImageRenderer}
127f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck     */
128d8732610ff48dc57063559d55ea3c84775e30e41John Reck    public static interface TileSource {
129f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck
130f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck        /**
131f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck         * If the source does not care about the tile size, it should use
132f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck         * {@link TiledImageRenderer#suggestedTileSize(Context)}
133f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck         */
134d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public int getTileSize();
135d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public int getImageWidth();
136d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public int getImageHeight();
137f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck        public int getRotation();
138f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck
139f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck        /**
140f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck         * Return a Preview image if available. This will be used as the base layer
141f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck         * if higher res tiles are not yet available
142f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck         */
143f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck        public BasicTexture getPreview();
144f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck
145f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck        /**
146f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck         * The tile returned by this method can be specified this way: Assuming
147f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck         * the image size is (width, height), first take the intersection of (0,
148f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck         * 0) - (width, height) and (x, y) - (x + tileSize, y + tileSize). If
149f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck         * in extending the region, we found some part of the region is outside
150f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck         * the image, those pixels are filled with black.
151f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck         *
152f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck         * If level > 0, it does the same operation on a down-scaled version of
153f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck         * the original image (down-scaled by a factor of 2^level), but (x, y)
154f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck         * still refers to the coordinate on the original image.
155f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck         *
156f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck         * The method would be called by the decoder thread.
157f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck         */
158d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public Bitmap getTile(int level, int x, int y, Bitmap reuse);
159d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
160d8732610ff48dc57063559d55ea3c84775e30e41John Reck
161d8732610ff48dc57063559d55ea3c84775e30e41John Reck    public static int suggestedTileSize(Context context) {
162d8732610ff48dc57063559d55ea3c84775e30e41John Reck        return isHighResolution(context) ? 512 : 256;
163d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
164d8732610ff48dc57063559d55ea3c84775e30e41John Reck
165d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private static boolean isHighResolution(Context context) {
166d8732610ff48dc57063559d55ea3c84775e30e41John Reck        DisplayMetrics metrics = new DisplayMetrics();
167d8732610ff48dc57063559d55ea3c84775e30e41John Reck        WindowManager wm = (WindowManager)
168d8732610ff48dc57063559d55ea3c84775e30e41John Reck                context.getSystemService(Context.WINDOW_SERVICE);
169d8732610ff48dc57063559d55ea3c84775e30e41John Reck        wm.getDefaultDisplay().getMetrics(metrics);
170d8732610ff48dc57063559d55ea3c84775e30e41John Reck        return metrics.heightPixels > 2048 ||  metrics.widthPixels > 2048;
171d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
172d8732610ff48dc57063559d55ea3c84775e30e41John Reck
173d8732610ff48dc57063559d55ea3c84775e30e41John Reck    public TiledImageRenderer(View parent) {
174d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mParent = parent;
175d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mTileDecoder = new TileDecoder();
176d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mTileDecoder.start();
177d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
178d8732610ff48dc57063559d55ea3c84775e30e41John Reck
179d8732610ff48dc57063559d55ea3c84775e30e41John Reck    public int getViewWidth() {
180d8732610ff48dc57063559d55ea3c84775e30e41John Reck        return mViewWidth;
181d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
182d8732610ff48dc57063559d55ea3c84775e30e41John Reck
183d8732610ff48dc57063559d55ea3c84775e30e41John Reck    public int getViewHeight() {
184d8732610ff48dc57063559d55ea3c84775e30e41John Reck        return mViewHeight;
185d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
186d8732610ff48dc57063559d55ea3c84775e30e41John Reck
187d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private void invalidate() {
188d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mParent.postInvalidate();
189d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
190d8732610ff48dc57063559d55ea3c84775e30e41John Reck
191d8732610ff48dc57063559d55ea3c84775e30e41John Reck    public void setModel(TileSource model, int rotation) {
192d8732610ff48dc57063559d55ea3c84775e30e41John Reck        if (mModel != model) {
193d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mModel = model;
194d8732610ff48dc57063559d55ea3c84775e30e41John Reck            notifyModelInvalidated();
195d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
196d8732610ff48dc57063559d55ea3c84775e30e41John Reck        if (mRotation != rotation) {
197d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mRotation = rotation;
198d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mLayoutTiles = true;
199d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
200d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
201d8732610ff48dc57063559d55ea3c84775e30e41John Reck
202f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck    private void calculateLevelCount() {
203f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck        if (mPreview != null) {
204f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            mLevelCount = Math.max(0, Utils.ceilLog2(
205f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                mImageWidth / (float) mPreview.getWidth()));
206f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck        } else {
207f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            int levels = 1;
208f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            int maxDim = Math.max(mImageWidth, mImageHeight);
209f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            int t = mTileSize;
210f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            while (t < maxDim) {
211f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                t <<= 1;
212f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                levels++;
213f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            }
214f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            mLevelCount = levels;
215d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
216d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
217d8732610ff48dc57063559d55ea3c84775e30e41John Reck
218d8732610ff48dc57063559d55ea3c84775e30e41John Reck    public void notifyModelInvalidated() {
219d8732610ff48dc57063559d55ea3c84775e30e41John Reck        invalidateTiles();
220d8732610ff48dc57063559d55ea3c84775e30e41John Reck        if (mModel == null) {
221d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mImageWidth = 0;
222d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mImageHeight = 0;
223d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mLevelCount = 0;
224f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            mPreview = null;
225d8732610ff48dc57063559d55ea3c84775e30e41John Reck        } else {
226d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mImageWidth = mModel.getImageWidth();
227d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mImageHeight = mModel.getImageHeight();
228f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            mPreview = mModel.getPreview();
229d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mTileSize = mModel.getTileSize();
230f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            calculateLevelCount();
231d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
232d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mLayoutTiles = true;
233d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
234d8732610ff48dc57063559d55ea3c84775e30e41John Reck
235d8732610ff48dc57063559d55ea3c84775e30e41John Reck    public void setViewSize(int width, int height) {
236d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mViewWidth = width;
237d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mViewHeight = height;
238d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
239d8732610ff48dc57063559d55ea3c84775e30e41John Reck
240d8732610ff48dc57063559d55ea3c84775e30e41John Reck    public void setPosition(int centerX, int centerY, float scale) {
241d8732610ff48dc57063559d55ea3c84775e30e41John Reck        if (mCenterX == centerX && mCenterY == centerY
242f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                && mScale == scale) {
243f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            return;
244f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck        }
245d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mCenterX = centerX;
246d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mCenterY = centerY;
247d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mScale = scale;
248d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mLayoutTiles = true;
249d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
250d8732610ff48dc57063559d55ea3c84775e30e41John Reck
251d8732610ff48dc57063559d55ea3c84775e30e41John Reck    // Prepare the tiles we want to use for display.
252d8732610ff48dc57063559d55ea3c84775e30e41John Reck    //
253d8732610ff48dc57063559d55ea3c84775e30e41John Reck    // 1. Decide the tile level we want to use for display.
254d8732610ff48dc57063559d55ea3c84775e30e41John Reck    // 2. Decide the tile levels we want to keep as texture (in addition to
255d8732610ff48dc57063559d55ea3c84775e30e41John Reck    //    the one we use for display).
256d8732610ff48dc57063559d55ea3c84775e30e41John Reck    // 3. Recycle unused tiles.
257d8732610ff48dc57063559d55ea3c84775e30e41John Reck    // 4. Activate the tiles we want.
258d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private void layoutTiles() {
259d8732610ff48dc57063559d55ea3c84775e30e41John Reck        if (mViewWidth == 0 || mViewHeight == 0 || !mLayoutTiles) {
260d8732610ff48dc57063559d55ea3c84775e30e41John Reck            return;
261d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
262d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mLayoutTiles = false;
263d8732610ff48dc57063559d55ea3c84775e30e41John Reck
264d8732610ff48dc57063559d55ea3c84775e30e41John Reck        // The tile levels we want to keep as texture is in the range
265d8732610ff48dc57063559d55ea3c84775e30e41John Reck        // [fromLevel, endLevel).
266d8732610ff48dc57063559d55ea3c84775e30e41John Reck        int fromLevel;
267d8732610ff48dc57063559d55ea3c84775e30e41John Reck        int endLevel;
268d8732610ff48dc57063559d55ea3c84775e30e41John Reck
269d8732610ff48dc57063559d55ea3c84775e30e41John Reck        // We want to use a texture larger than or equal to the display size.
270d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mLevel = Utils.clamp(Utils.floorLog2(1f / mScale), 0, mLevelCount);
271d8732610ff48dc57063559d55ea3c84775e30e41John Reck
272d8732610ff48dc57063559d55ea3c84775e30e41John Reck        // We want to keep one more tile level as texture in addition to what
273d8732610ff48dc57063559d55ea3c84775e30e41John Reck        // we use for display. So it can be faster when the scale moves to the
274d8732610ff48dc57063559d55ea3c84775e30e41John Reck        // next level. We choose the level closest to the current scale.
275d8732610ff48dc57063559d55ea3c84775e30e41John Reck        if (mLevel != mLevelCount) {
276d8732610ff48dc57063559d55ea3c84775e30e41John Reck            Rect range = mTileRange;
277d8732610ff48dc57063559d55ea3c84775e30e41John Reck            getRange(range, mCenterX, mCenterY, mLevel, mScale, mRotation);
278d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mOffsetX = Math.round(mViewWidth / 2f + (range.left - mCenterX) * mScale);
279d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mOffsetY = Math.round(mViewHeight / 2f + (range.top - mCenterY) * mScale);
280d8732610ff48dc57063559d55ea3c84775e30e41John Reck            fromLevel = mScale * (1 << mLevel) > 0.75f ? mLevel - 1 : mLevel;
281d8732610ff48dc57063559d55ea3c84775e30e41John Reck        } else {
282d8732610ff48dc57063559d55ea3c84775e30e41John Reck            // Activate the tiles of the smallest two levels.
283d8732610ff48dc57063559d55ea3c84775e30e41John Reck            fromLevel = mLevel - 2;
284d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mOffsetX = Math.round(mViewWidth / 2f - mCenterX * mScale);
285d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mOffsetY = Math.round(mViewHeight / 2f - mCenterY * mScale);
286d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
287d8732610ff48dc57063559d55ea3c84775e30e41John Reck
288d8732610ff48dc57063559d55ea3c84775e30e41John Reck        fromLevel = Math.max(0, Math.min(fromLevel, mLevelCount - 2));
289d8732610ff48dc57063559d55ea3c84775e30e41John Reck        endLevel = Math.min(fromLevel + 2, mLevelCount);
290d8732610ff48dc57063559d55ea3c84775e30e41John Reck
291d8732610ff48dc57063559d55ea3c84775e30e41John Reck        Rect range[] = mActiveRange;
292d8732610ff48dc57063559d55ea3c84775e30e41John Reck        for (int i = fromLevel; i < endLevel; ++i) {
293d8732610ff48dc57063559d55ea3c84775e30e41John Reck            getRange(range[i - fromLevel], mCenterX, mCenterY, i, mRotation);
294d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
295d8732610ff48dc57063559d55ea3c84775e30e41John Reck
296d8732610ff48dc57063559d55ea3c84775e30e41John Reck        // If rotation is transient, don't update the tile.
297f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck        if (mRotation % 90 != 0) {
298f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            return;
299f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck        }
300d8732610ff48dc57063559d55ea3c84775e30e41John Reck
301d8732610ff48dc57063559d55ea3c84775e30e41John Reck        synchronized (mQueueLock) {
302d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mDecodeQueue.clean();
303d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mUploadQueue.clean();
304d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mBackgroundTileUploaded = false;
305d8732610ff48dc57063559d55ea3c84775e30e41John Reck
306d8732610ff48dc57063559d55ea3c84775e30e41John Reck            // Recycle unused tiles: if the level of the active tile is outside the
307d8732610ff48dc57063559d55ea3c84775e30e41John Reck            // range [fromLevel, endLevel) or not in the visible range.
308d8732610ff48dc57063559d55ea3c84775e30e41John Reck            int n = mActiveTiles.size();
309d8732610ff48dc57063559d55ea3c84775e30e41John Reck            for (int i = 0; i < n; i++) {
310d8732610ff48dc57063559d55ea3c84775e30e41John Reck                Tile tile = mActiveTiles.valueAt(i);
311d8732610ff48dc57063559d55ea3c84775e30e41John Reck                int level = tile.mTileLevel;
312d8732610ff48dc57063559d55ea3c84775e30e41John Reck                if (level < fromLevel || level >= endLevel
313d8732610ff48dc57063559d55ea3c84775e30e41John Reck                        || !range[level - fromLevel].contains(tile.mX, tile.mY)) {
314d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    mActiveTiles.removeAt(i);
315d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    i--;
316d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    n--;
317d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    recycleTile(tile);
318d8732610ff48dc57063559d55ea3c84775e30e41John Reck                }
319d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
320d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
321d8732610ff48dc57063559d55ea3c84775e30e41John Reck
322d8732610ff48dc57063559d55ea3c84775e30e41John Reck        for (int i = fromLevel; i < endLevel; ++i) {
323d8732610ff48dc57063559d55ea3c84775e30e41John Reck            int size = mTileSize << i;
324d8732610ff48dc57063559d55ea3c84775e30e41John Reck            Rect r = range[i - fromLevel];
325d8732610ff48dc57063559d55ea3c84775e30e41John Reck            for (int y = r.top, bottom = r.bottom; y < bottom; y += size) {
326d8732610ff48dc57063559d55ea3c84775e30e41John Reck                for (int x = r.left, right = r.right; x < right; x += size) {
327d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    activateTile(x, y, i);
328d8732610ff48dc57063559d55ea3c84775e30e41John Reck                }
329d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
330d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
331d8732610ff48dc57063559d55ea3c84775e30e41John Reck        invalidate();
332d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
333d8732610ff48dc57063559d55ea3c84775e30e41John Reck
334d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private void invalidateTiles() {
335d8732610ff48dc57063559d55ea3c84775e30e41John Reck        synchronized (mQueueLock) {
336d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mDecodeQueue.clean();
337d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mUploadQueue.clean();
338d8732610ff48dc57063559d55ea3c84775e30e41John Reck
339f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            // TODO(xx): disable decoder
340d8732610ff48dc57063559d55ea3c84775e30e41John Reck            int n = mActiveTiles.size();
341d8732610ff48dc57063559d55ea3c84775e30e41John Reck            for (int i = 0; i < n; i++) {
342d8732610ff48dc57063559d55ea3c84775e30e41John Reck                Tile tile = mActiveTiles.valueAt(i);
343d8732610ff48dc57063559d55ea3c84775e30e41John Reck                recycleTile(tile);
344d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
345d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mActiveTiles.clear();
346d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
347d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
348d8732610ff48dc57063559d55ea3c84775e30e41John Reck
349d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private void getRange(Rect out, int cX, int cY, int level, int rotation) {
350d8732610ff48dc57063559d55ea3c84775e30e41John Reck        getRange(out, cX, cY, level, 1f / (1 << (level + 1)), rotation);
351d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
352d8732610ff48dc57063559d55ea3c84775e30e41John Reck
353d8732610ff48dc57063559d55ea3c84775e30e41John Reck    // If the bitmap is scaled by the given factor "scale", return the
354d8732610ff48dc57063559d55ea3c84775e30e41John Reck    // rectangle containing visible range. The left-top coordinate returned is
355d8732610ff48dc57063559d55ea3c84775e30e41John Reck    // aligned to the tile boundary.
356d8732610ff48dc57063559d55ea3c84775e30e41John Reck    //
357d8732610ff48dc57063559d55ea3c84775e30e41John Reck    // (cX, cY) is the point on the original bitmap which will be put in the
358d8732610ff48dc57063559d55ea3c84775e30e41John Reck    // center of the ImageViewer.
359d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private void getRange(Rect out,
360d8732610ff48dc57063559d55ea3c84775e30e41John Reck            int cX, int cY, int level, float scale, int rotation) {
361d8732610ff48dc57063559d55ea3c84775e30e41John Reck
362d8732610ff48dc57063559d55ea3c84775e30e41John Reck        double radians = Math.toRadians(-rotation);
363d8732610ff48dc57063559d55ea3c84775e30e41John Reck        double w = mViewWidth;
364d8732610ff48dc57063559d55ea3c84775e30e41John Reck        double h = mViewHeight;
365d8732610ff48dc57063559d55ea3c84775e30e41John Reck
366d8732610ff48dc57063559d55ea3c84775e30e41John Reck        double cos = Math.cos(radians);
367d8732610ff48dc57063559d55ea3c84775e30e41John Reck        double sin = Math.sin(radians);
368d8732610ff48dc57063559d55ea3c84775e30e41John Reck        int width = (int) Math.ceil(Math.max(
369d8732610ff48dc57063559d55ea3c84775e30e41John Reck                Math.abs(cos * w - sin * h), Math.abs(cos * w + sin * h)));
370d8732610ff48dc57063559d55ea3c84775e30e41John Reck        int height = (int) Math.ceil(Math.max(
371d8732610ff48dc57063559d55ea3c84775e30e41John Reck                Math.abs(sin * w + cos * h), Math.abs(sin * w - cos * h)));
372d8732610ff48dc57063559d55ea3c84775e30e41John Reck
373d8732610ff48dc57063559d55ea3c84775e30e41John Reck        int left = (int) Math.floor(cX - width / (2f * scale));
374d8732610ff48dc57063559d55ea3c84775e30e41John Reck        int top = (int) Math.floor(cY - height / (2f * scale));
375d8732610ff48dc57063559d55ea3c84775e30e41John Reck        int right = (int) Math.ceil(left + width / scale);
376d8732610ff48dc57063559d55ea3c84775e30e41John Reck        int bottom = (int) Math.ceil(top + height / scale);
377d8732610ff48dc57063559d55ea3c84775e30e41John Reck
378d8732610ff48dc57063559d55ea3c84775e30e41John Reck        // align the rectangle to tile boundary
379d8732610ff48dc57063559d55ea3c84775e30e41John Reck        int size = mTileSize << level;
380d8732610ff48dc57063559d55ea3c84775e30e41John Reck        left = Math.max(0, size * (left / size));
381d8732610ff48dc57063559d55ea3c84775e30e41John Reck        top = Math.max(0, size * (top / size));
382d8732610ff48dc57063559d55ea3c84775e30e41John Reck        right = Math.min(mImageWidth, right);
383d8732610ff48dc57063559d55ea3c84775e30e41John Reck        bottom = Math.min(mImageHeight, bottom);
384d8732610ff48dc57063559d55ea3c84775e30e41John Reck
385d8732610ff48dc57063559d55ea3c84775e30e41John Reck        out.set(left, top, right, bottom);
386d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
387d8732610ff48dc57063559d55ea3c84775e30e41John Reck
388d8732610ff48dc57063559d55ea3c84775e30e41John Reck    public void freeTextures() {
389d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mLayoutTiles = true;
390d8732610ff48dc57063559d55ea3c84775e30e41John Reck
391f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck        mTileDecoder.finishAndWait();
392d8732610ff48dc57063559d55ea3c84775e30e41John Reck        synchronized (mQueueLock) {
393d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mUploadQueue.clean();
394d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mDecodeQueue.clean();
395d8732610ff48dc57063559d55ea3c84775e30e41John Reck            Tile tile = mRecycledQueue.pop();
396d8732610ff48dc57063559d55ea3c84775e30e41John Reck            while (tile != null) {
397d8732610ff48dc57063559d55ea3c84775e30e41John Reck                tile.recycle();
398d8732610ff48dc57063559d55ea3c84775e30e41John Reck                tile = mRecycledQueue.pop();
399d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
400d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
401d8732610ff48dc57063559d55ea3c84775e30e41John Reck
402d8732610ff48dc57063559d55ea3c84775e30e41John Reck        int n = mActiveTiles.size();
403d8732610ff48dc57063559d55ea3c84775e30e41John Reck        for (int i = 0; i < n; i++) {
404d8732610ff48dc57063559d55ea3c84775e30e41John Reck            Tile texture = mActiveTiles.valueAt(i);
405d8732610ff48dc57063559d55ea3c84775e30e41John Reck            texture.recycle();
406d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
407d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mActiveTiles.clear();
408d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mTileRange.set(0, 0, 0, 0);
409d8732610ff48dc57063559d55ea3c84775e30e41John Reck
410f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck        while (sTilePool.acquire() != null) {}
411d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
412d8732610ff48dc57063559d55ea3c84775e30e41John Reck
413f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck    public boolean draw(GLCanvas canvas) {
414d8732610ff48dc57063559d55ea3c84775e30e41John Reck        layoutTiles();
415d8732610ff48dc57063559d55ea3c84775e30e41John Reck        uploadTiles(canvas);
416d8732610ff48dc57063559d55ea3c84775e30e41John Reck
417d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mUploadQuota = UPLOAD_LIMIT;
418d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mRenderComplete = true;
419d8732610ff48dc57063559d55ea3c84775e30e41John Reck
420d8732610ff48dc57063559d55ea3c84775e30e41John Reck        int level = mLevel;
421d8732610ff48dc57063559d55ea3c84775e30e41John Reck        int rotation = mRotation;
422d8732610ff48dc57063559d55ea3c84775e30e41John Reck        int flags = 0;
423f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck        if (rotation != 0) {
424f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            flags |= GLCanvas.SAVE_FLAG_MATRIX;
425f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck        }
426d8732610ff48dc57063559d55ea3c84775e30e41John Reck
427d8732610ff48dc57063559d55ea3c84775e30e41John Reck        if (flags != 0) {
428d8732610ff48dc57063559d55ea3c84775e30e41John Reck            canvas.save(flags);
429d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (rotation != 0) {
430d8732610ff48dc57063559d55ea3c84775e30e41John Reck                int centerX = mViewWidth / 2, centerY = mViewHeight / 2;
431d8732610ff48dc57063559d55ea3c84775e30e41John Reck                canvas.translate(centerX, centerY);
432d8732610ff48dc57063559d55ea3c84775e30e41John Reck                canvas.rotate(rotation, 0, 0, 1);
433d8732610ff48dc57063559d55ea3c84775e30e41John Reck                canvas.translate(-centerX, -centerY);
434d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
435d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
436d8732610ff48dc57063559d55ea3c84775e30e41John Reck        try {
437d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (level != mLevelCount) {
438d8732610ff48dc57063559d55ea3c84775e30e41John Reck                int size = (mTileSize << level);
439d8732610ff48dc57063559d55ea3c84775e30e41John Reck                float length = size * mScale;
440d8732610ff48dc57063559d55ea3c84775e30e41John Reck                Rect r = mTileRange;
441d8732610ff48dc57063559d55ea3c84775e30e41John Reck
442d8732610ff48dc57063559d55ea3c84775e30e41John Reck                for (int ty = r.top, i = 0; ty < r.bottom; ty += size, i++) {
443d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    float y = mOffsetY + i * length;
444d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    for (int tx = r.left, j = 0; tx < r.right; tx += size, j++) {
445d8732610ff48dc57063559d55ea3c84775e30e41John Reck                        float x = mOffsetX + j * length;
446d8732610ff48dc57063559d55ea3c84775e30e41John Reck                        drawTile(canvas, tx, ty, level, x, y, length);
447d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    }
448d8732610ff48dc57063559d55ea3c84775e30e41John Reck                }
449f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            } else if (mPreview != null) {
450f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                mPreview.draw(canvas, mOffsetX, mOffsetY,
451f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                        Math.round(mImageWidth * mScale),
452f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                        Math.round(mImageHeight * mScale));
453d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
454d8732610ff48dc57063559d55ea3c84775e30e41John Reck        } finally {
455f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            if (flags != 0) {
456f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                canvas.restore();
457f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            }
458d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
459d8732610ff48dc57063559d55ea3c84775e30e41John Reck
460d8732610ff48dc57063559d55ea3c84775e30e41John Reck        if (mRenderComplete) {
461d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (!mBackgroundTileUploaded) {
462d8732610ff48dc57063559d55ea3c84775e30e41John Reck                uploadBackgroundTiles(canvas);
463d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
464d8732610ff48dc57063559d55ea3c84775e30e41John Reck        } else {
465d8732610ff48dc57063559d55ea3c84775e30e41John Reck            invalidate();
466d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
467f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck        return mRenderComplete || mPreview != null;
468d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
469d8732610ff48dc57063559d55ea3c84775e30e41John Reck
470d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private void uploadBackgroundTiles(GLCanvas canvas) {
471d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mBackgroundTileUploaded = true;
472d8732610ff48dc57063559d55ea3c84775e30e41John Reck        int n = mActiveTiles.size();
473d8732610ff48dc57063559d55ea3c84775e30e41John Reck        for (int i = 0; i < n; i++) {
474d8732610ff48dc57063559d55ea3c84775e30e41John Reck            Tile tile = mActiveTiles.valueAt(i);
475d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (!tile.isContentValid()) {
476d8732610ff48dc57063559d55ea3c84775e30e41John Reck                queueForDecode(tile);
477d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
478d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
479d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
480d8732610ff48dc57063559d55ea3c84775e30e41John Reck
481d8732610ff48dc57063559d55ea3c84775e30e41John Reck   private void queueForDecode(Tile tile) {
482d8732610ff48dc57063559d55ea3c84775e30e41John Reck       synchronized (mQueueLock) {
483d8732610ff48dc57063559d55ea3c84775e30e41John Reck           if (tile.mTileState == STATE_ACTIVATED) {
484d8732610ff48dc57063559d55ea3c84775e30e41John Reck               tile.mTileState = STATE_IN_QUEUE;
485d8732610ff48dc57063559d55ea3c84775e30e41John Reck               if (mDecodeQueue.push(tile)) {
486d8732610ff48dc57063559d55ea3c84775e30e41John Reck                   mQueueLock.notifyAll();
487d8732610ff48dc57063559d55ea3c84775e30e41John Reck               }
488d8732610ff48dc57063559d55ea3c84775e30e41John Reck           }
489d8732610ff48dc57063559d55ea3c84775e30e41John Reck       }
490d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
491d8732610ff48dc57063559d55ea3c84775e30e41John Reck
492f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck    private void decodeTile(Tile tile) {
493d8732610ff48dc57063559d55ea3c84775e30e41John Reck        synchronized (mQueueLock) {
494f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            if (tile.mTileState != STATE_IN_QUEUE) {
495f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                return;
496f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            }
497d8732610ff48dc57063559d55ea3c84775e30e41John Reck            tile.mTileState = STATE_DECODING;
498d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
499d8732610ff48dc57063559d55ea3c84775e30e41John Reck        boolean decodeComplete = tile.decode();
500d8732610ff48dc57063559d55ea3c84775e30e41John Reck        synchronized (mQueueLock) {
501d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (tile.mTileState == STATE_RECYCLING) {
502d8732610ff48dc57063559d55ea3c84775e30e41John Reck                tile.mTileState = STATE_RECYCLED;
503d8732610ff48dc57063559d55ea3c84775e30e41John Reck                if (tile.mDecodedTile != null) {
504f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                    sTilePool.release(tile.mDecodedTile);
505d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    tile.mDecodedTile = null;
506d8732610ff48dc57063559d55ea3c84775e30e41John Reck                }
507d8732610ff48dc57063559d55ea3c84775e30e41John Reck                mRecycledQueue.push(tile);
508f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                return;
509d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
510d8732610ff48dc57063559d55ea3c84775e30e41John Reck            tile.mTileState = decodeComplete ? STATE_DECODED : STATE_DECODE_FAIL;
511f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            if (!decodeComplete) {
512f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                return;
513f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            }
514f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            mUploadQueue.push(tile);
515d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
516f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck        invalidate();
517d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
518d8732610ff48dc57063559d55ea3c84775e30e41John Reck
519d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private Tile obtainTile(int x, int y, int level) {
520d8732610ff48dc57063559d55ea3c84775e30e41John Reck        synchronized (mQueueLock) {
521d8732610ff48dc57063559d55ea3c84775e30e41John Reck            Tile tile = mRecycledQueue.pop();
522d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (tile != null) {
523d8732610ff48dc57063559d55ea3c84775e30e41John Reck                tile.mTileState = STATE_ACTIVATED;
524d8732610ff48dc57063559d55ea3c84775e30e41John Reck                tile.update(x, y, level);
525d8732610ff48dc57063559d55ea3c84775e30e41John Reck                return tile;
526d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
527d8732610ff48dc57063559d55ea3c84775e30e41John Reck            return new Tile(x, y, level);
528d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
529d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
530d8732610ff48dc57063559d55ea3c84775e30e41John Reck
531d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private void recycleTile(Tile tile) {
532d8732610ff48dc57063559d55ea3c84775e30e41John Reck        synchronized (mQueueLock) {
533d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (tile.mTileState == STATE_DECODING) {
534d8732610ff48dc57063559d55ea3c84775e30e41John Reck                tile.mTileState = STATE_RECYCLING;
535d8732610ff48dc57063559d55ea3c84775e30e41John Reck                return;
536d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
537d8732610ff48dc57063559d55ea3c84775e30e41John Reck            tile.mTileState = STATE_RECYCLED;
538d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (tile.mDecodedTile != null) {
539f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                sTilePool.release(tile.mDecodedTile);
540d8732610ff48dc57063559d55ea3c84775e30e41John Reck                tile.mDecodedTile = null;
541d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
542d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mRecycledQueue.push(tile);
543d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
544d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
545d8732610ff48dc57063559d55ea3c84775e30e41John Reck
546d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private void activateTile(int x, int y, int level) {
547d8732610ff48dc57063559d55ea3c84775e30e41John Reck        long key = makeTileKey(x, y, level);
548d8732610ff48dc57063559d55ea3c84775e30e41John Reck        Tile tile = mActiveTiles.get(key);
549d8732610ff48dc57063559d55ea3c84775e30e41John Reck        if (tile != null) {
550d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (tile.mTileState == STATE_IN_QUEUE) {
551d8732610ff48dc57063559d55ea3c84775e30e41John Reck                tile.mTileState = STATE_ACTIVATED;
552d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
553d8732610ff48dc57063559d55ea3c84775e30e41John Reck            return;
554d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
555d8732610ff48dc57063559d55ea3c84775e30e41John Reck        tile = obtainTile(x, y, level);
556d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mActiveTiles.put(key, tile);
557d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
558d8732610ff48dc57063559d55ea3c84775e30e41John Reck
559d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private Tile getTile(int x, int y, int level) {
560d8732610ff48dc57063559d55ea3c84775e30e41John Reck        return mActiveTiles.get(makeTileKey(x, y, level));
561d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
562d8732610ff48dc57063559d55ea3c84775e30e41John Reck
563d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private static long makeTileKey(int x, int y, int level) {
564d8732610ff48dc57063559d55ea3c84775e30e41John Reck        long result = x;
565d8732610ff48dc57063559d55ea3c84775e30e41John Reck        result = (result << 16) | y;
566d8732610ff48dc57063559d55ea3c84775e30e41John Reck        result = (result << 16) | level;
567d8732610ff48dc57063559d55ea3c84775e30e41John Reck        return result;
568d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
569d8732610ff48dc57063559d55ea3c84775e30e41John Reck
570d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private void uploadTiles(GLCanvas canvas) {
571d8732610ff48dc57063559d55ea3c84775e30e41John Reck        int quota = UPLOAD_LIMIT;
572d8732610ff48dc57063559d55ea3c84775e30e41John Reck        Tile tile = null;
573d8732610ff48dc57063559d55ea3c84775e30e41John Reck        while (quota > 0) {
574d8732610ff48dc57063559d55ea3c84775e30e41John Reck            synchronized (mQueueLock) {
575d8732610ff48dc57063559d55ea3c84775e30e41John Reck                tile = mUploadQueue.pop();
576d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
577f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            if (tile == null) {
578f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                break;
579f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            }
580d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (!tile.isContentValid()) {
581f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                if (tile.mTileState == STATE_DECODED) {
582f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                    tile.updateContent(canvas);
583f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                    --quota;
584f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                } else {
585f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                    Log.w(TAG, "Tile in upload queue has invalid state: " + tile.mTileState);
586f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                }
587d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
588d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
589d8732610ff48dc57063559d55ea3c84775e30e41John Reck        if (tile != null) {
590d8732610ff48dc57063559d55ea3c84775e30e41John Reck            invalidate();
591d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
592d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
593d8732610ff48dc57063559d55ea3c84775e30e41John Reck
594d8732610ff48dc57063559d55ea3c84775e30e41John Reck    // Draw the tile to a square at canvas that locates at (x, y) and
595d8732610ff48dc57063559d55ea3c84775e30e41John Reck    // has a side length of length.
596d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private void drawTile(GLCanvas canvas,
597d8732610ff48dc57063559d55ea3c84775e30e41John Reck            int tx, int ty, int level, float x, float y, float length) {
598d8732610ff48dc57063559d55ea3c84775e30e41John Reck        RectF source = mSourceRect;
599d8732610ff48dc57063559d55ea3c84775e30e41John Reck        RectF target = mTargetRect;
600d8732610ff48dc57063559d55ea3c84775e30e41John Reck        target.set(x, y, x + length, y + length);
601d8732610ff48dc57063559d55ea3c84775e30e41John Reck        source.set(0, 0, mTileSize, mTileSize);
602d8732610ff48dc57063559d55ea3c84775e30e41John Reck
603d8732610ff48dc57063559d55ea3c84775e30e41John Reck        Tile tile = getTile(tx, ty, level);
604d8732610ff48dc57063559d55ea3c84775e30e41John Reck        if (tile != null) {
605d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (!tile.isContentValid()) {
606d8732610ff48dc57063559d55ea3c84775e30e41John Reck                if (tile.mTileState == STATE_DECODED) {
607d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    if (mUploadQuota > 0) {
608d8732610ff48dc57063559d55ea3c84775e30e41John Reck                        --mUploadQuota;
609d8732610ff48dc57063559d55ea3c84775e30e41John Reck                        tile.updateContent(canvas);
610d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    } else {
611d8732610ff48dc57063559d55ea3c84775e30e41John Reck                        mRenderComplete = false;
612d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    }
613d8732610ff48dc57063559d55ea3c84775e30e41John Reck                } else if (tile.mTileState != STATE_DECODE_FAIL){
614d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    mRenderComplete = false;
615d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    queueForDecode(tile);
616d8732610ff48dc57063559d55ea3c84775e30e41John Reck                }
617d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
618f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            if (drawTile(tile, canvas, source, target)) {
619f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                return;
620f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            }
621f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck        }
622f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck        if (mPreview != null) {
623f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            int size = mTileSize << level;
624f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            float scaleX = (float) mPreview.getWidth() / mImageWidth;
625f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            float scaleY = (float) mPreview.getHeight() / mImageHeight;
626f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            source.set(tx * scaleX, ty * scaleY, (tx + size) * scaleX,
627f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                    (ty + size) * scaleY);
628f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            canvas.drawTexture(mPreview, source, target);
629d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
630d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
631d8732610ff48dc57063559d55ea3c84775e30e41John Reck
632d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private boolean drawTile(
633d8732610ff48dc57063559d55ea3c84775e30e41John Reck            Tile tile, GLCanvas canvas, RectF source, RectF target) {
634d8732610ff48dc57063559d55ea3c84775e30e41John Reck        while (true) {
635d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (tile.isContentValid()) {
636d8732610ff48dc57063559d55ea3c84775e30e41John Reck                canvas.drawTexture(tile, source, target);
637d8732610ff48dc57063559d55ea3c84775e30e41John Reck                return true;
638d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
639d8732610ff48dc57063559d55ea3c84775e30e41John Reck
640d8732610ff48dc57063559d55ea3c84775e30e41John Reck            // Parent can be divided to four quads and tile is one of the four.
641d8732610ff48dc57063559d55ea3c84775e30e41John Reck            Tile parent = tile.getParentTile();
642f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            if (parent == null) {
643f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                return false;
644f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            }
645d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (tile.mX == parent.mX) {
646d8732610ff48dc57063559d55ea3c84775e30e41John Reck                source.left /= 2f;
647d8732610ff48dc57063559d55ea3c84775e30e41John Reck                source.right /= 2f;
648d8732610ff48dc57063559d55ea3c84775e30e41John Reck            } else {
649d8732610ff48dc57063559d55ea3c84775e30e41John Reck                source.left = (mTileSize + source.left) / 2f;
650d8732610ff48dc57063559d55ea3c84775e30e41John Reck                source.right = (mTileSize + source.right) / 2f;
651d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
652d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (tile.mY == parent.mY) {
653d8732610ff48dc57063559d55ea3c84775e30e41John Reck                source.top /= 2f;
654d8732610ff48dc57063559d55ea3c84775e30e41John Reck                source.bottom /= 2f;
655d8732610ff48dc57063559d55ea3c84775e30e41John Reck            } else {
656d8732610ff48dc57063559d55ea3c84775e30e41John Reck                source.top = (mTileSize + source.top) / 2f;
657d8732610ff48dc57063559d55ea3c84775e30e41John Reck                source.bottom = (mTileSize + source.bottom) / 2f;
658d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
659d8732610ff48dc57063559d55ea3c84775e30e41John Reck            tile = parent;
660d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
661d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
662d8732610ff48dc57063559d55ea3c84775e30e41John Reck
663d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private class Tile extends UploadedTexture {
664d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public int mX;
665d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public int mY;
666d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public int mTileLevel;
667d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public Tile mNext;
668d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public Bitmap mDecodedTile;
669d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public volatile int mTileState = STATE_ACTIVATED;
670d8732610ff48dc57063559d55ea3c84775e30e41John Reck
671d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public Tile(int x, int y, int level) {
672d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mX = x;
673d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mY = y;
674d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mTileLevel = level;
675d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
676d8732610ff48dc57063559d55ea3c84775e30e41John Reck
677d8732610ff48dc57063559d55ea3c84775e30e41John Reck        @Override
678d8732610ff48dc57063559d55ea3c84775e30e41John Reck        protected void onFreeBitmap(Bitmap bitmap) {
679f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            sTilePool.release(bitmap);
680d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
681d8732610ff48dc57063559d55ea3c84775e30e41John Reck
682d8732610ff48dc57063559d55ea3c84775e30e41John Reck        boolean decode() {
683d8732610ff48dc57063559d55ea3c84775e30e41John Reck            // Get a tile from the original image. The tile is down-scaled
684d8732610ff48dc57063559d55ea3c84775e30e41John Reck            // by (1 << mTilelevel) from a region in the original image.
685d8732610ff48dc57063559d55ea3c84775e30e41John Reck            try {
686f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                Bitmap reuse = sTilePool.acquire();
687f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                if (reuse != null && reuse.getWidth() != mTileSize) {
688f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                    reuse = null;
689f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                }
690d8732610ff48dc57063559d55ea3c84775e30e41John Reck                mDecodedTile = mModel.getTile(mTileLevel, mX, mY, reuse);
691d8732610ff48dc57063559d55ea3c84775e30e41John Reck            } catch (Throwable t) {
692d8732610ff48dc57063559d55ea3c84775e30e41John Reck                Log.w(TAG, "fail to decode tile", t);
693d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
694d8732610ff48dc57063559d55ea3c84775e30e41John Reck            return mDecodedTile != null;
695d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
696d8732610ff48dc57063559d55ea3c84775e30e41John Reck
697d8732610ff48dc57063559d55ea3c84775e30e41John Reck        @Override
698d8732610ff48dc57063559d55ea3c84775e30e41John Reck        protected Bitmap onGetBitmap() {
699d8732610ff48dc57063559d55ea3c84775e30e41John Reck            Utils.assertTrue(mTileState == STATE_DECODED);
700d8732610ff48dc57063559d55ea3c84775e30e41John Reck
701d8732610ff48dc57063559d55ea3c84775e30e41John Reck            // We need to override the width and height, so that we won't
702d8732610ff48dc57063559d55ea3c84775e30e41John Reck            // draw beyond the boundaries.
703d8732610ff48dc57063559d55ea3c84775e30e41John Reck            int rightEdge = ((mImageWidth - mX) >> mTileLevel);
704d8732610ff48dc57063559d55ea3c84775e30e41John Reck            int bottomEdge = ((mImageHeight - mY) >> mTileLevel);
705d8732610ff48dc57063559d55ea3c84775e30e41John Reck            setSize(Math.min(mTileSize, rightEdge), Math.min(mTileSize, bottomEdge));
706d8732610ff48dc57063559d55ea3c84775e30e41John Reck
707d8732610ff48dc57063559d55ea3c84775e30e41John Reck            Bitmap bitmap = mDecodedTile;
708d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mDecodedTile = null;
709d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mTileState = STATE_ACTIVATED;
710d8732610ff48dc57063559d55ea3c84775e30e41John Reck            return bitmap;
711d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
712d8732610ff48dc57063559d55ea3c84775e30e41John Reck
713d8732610ff48dc57063559d55ea3c84775e30e41John Reck        // We override getTextureWidth() and getTextureHeight() here, so the
714d8732610ff48dc57063559d55ea3c84775e30e41John Reck        // texture can be re-used for different tiles regardless of the actual
715d8732610ff48dc57063559d55ea3c84775e30e41John Reck        // size of the tile (which may be small because it is a tile at the
716d8732610ff48dc57063559d55ea3c84775e30e41John Reck        // boundary).
717d8732610ff48dc57063559d55ea3c84775e30e41John Reck        @Override
718d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public int getTextureWidth() {
719d8732610ff48dc57063559d55ea3c84775e30e41John Reck            return mTileSize;
720d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
721d8732610ff48dc57063559d55ea3c84775e30e41John Reck
722d8732610ff48dc57063559d55ea3c84775e30e41John Reck        @Override
723d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public int getTextureHeight() {
724d8732610ff48dc57063559d55ea3c84775e30e41John Reck            return mTileSize;
725d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
726d8732610ff48dc57063559d55ea3c84775e30e41John Reck
727d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public void update(int x, int y, int level) {
728d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mX = x;
729d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mY = y;
730d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mTileLevel = level;
731d8732610ff48dc57063559d55ea3c84775e30e41John Reck            invalidateContent();
732d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
733d8732610ff48dc57063559d55ea3c84775e30e41John Reck
734d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public Tile getParentTile() {
735f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            if (mTileLevel + 1 == mLevelCount) {
736f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                return null;
737f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            }
738d8732610ff48dc57063559d55ea3c84775e30e41John Reck            int size = mTileSize << (mTileLevel + 1);
739d8732610ff48dc57063559d55ea3c84775e30e41John Reck            int x = size * (mX / size);
740d8732610ff48dc57063559d55ea3c84775e30e41John Reck            int y = size * (mY / size);
741d8732610ff48dc57063559d55ea3c84775e30e41John Reck            return getTile(x, y, mTileLevel + 1);
742d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
743d8732610ff48dc57063559d55ea3c84775e30e41John Reck
744d8732610ff48dc57063559d55ea3c84775e30e41John Reck        @Override
745d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public String toString() {
746d8732610ff48dc57063559d55ea3c84775e30e41John Reck            return String.format("tile(%s, %s, %s / %s)",
747d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    mX / mTileSize, mY / mTileSize, mLevel, mLevelCount);
748d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
749d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
750d8732610ff48dc57063559d55ea3c84775e30e41John Reck
751d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private static class TileQueue {
752d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private Tile mHead;
753d8732610ff48dc57063559d55ea3c84775e30e41John Reck
754d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public Tile pop() {
755d8732610ff48dc57063559d55ea3c84775e30e41John Reck            Tile tile = mHead;
756f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            if (tile != null) {
757f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                mHead = tile.mNext;
758f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            }
759d8732610ff48dc57063559d55ea3c84775e30e41John Reck            return tile;
760d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
761d8732610ff48dc57063559d55ea3c84775e30e41John Reck
762d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public boolean push(Tile tile) {
763f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            if (contains(tile)) {
764f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                Log.w(TAG, "Attempting to add a tile already in the queue!");
765f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                return false;
766f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            }
767d8732610ff48dc57063559d55ea3c84775e30e41John Reck            boolean wasEmpty = mHead == null;
768d8732610ff48dc57063559d55ea3c84775e30e41John Reck            tile.mNext = mHead;
769d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mHead = tile;
770d8732610ff48dc57063559d55ea3c84775e30e41John Reck            return wasEmpty;
771d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
772d8732610ff48dc57063559d55ea3c84775e30e41John Reck
773f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck        private boolean contains(Tile tile) {
774f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            Tile other = mHead;
775f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            while (other != null) {
776f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                if (other == tile) {
777f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                    return true;
778f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                }
779f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                other = other.mNext;
780f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            }
781f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            return false;
782f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck        }
783f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck
784d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public void clean() {
785d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mHead = null;
786d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
787d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
788d8732610ff48dc57063559d55ea3c84775e30e41John Reck
789d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private class TileDecoder extends Thread {
790d8732610ff48dc57063559d55ea3c84775e30e41John Reck
791d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public void finishAndWait() {
792d8732610ff48dc57063559d55ea3c84775e30e41John Reck            interrupt();
793d8732610ff48dc57063559d55ea3c84775e30e41John Reck            try {
794d8732610ff48dc57063559d55ea3c84775e30e41John Reck                join();
795d8732610ff48dc57063559d55ea3c84775e30e41John Reck            } catch (InterruptedException e) {
796d8732610ff48dc57063559d55ea3c84775e30e41John Reck                Log.w(TAG, "Interrupted while waiting for TileDecoder thread to finish!");
797d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
798d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
799d8732610ff48dc57063559d55ea3c84775e30e41John Reck
800d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private Tile waitForTile() throws InterruptedException {
801f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck            synchronized (mQueueLock) {
802d8732610ff48dc57063559d55ea3c84775e30e41John Reck                while (true) {
803d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    Tile tile = mDecodeQueue.pop();
804d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    if (tile != null) {
805d8732610ff48dc57063559d55ea3c84775e30e41John Reck                        return tile;
806d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    }
807d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    mQueueLock.wait();
808d8732610ff48dc57063559d55ea3c84775e30e41John Reck                }
809d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
810d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
811d8732610ff48dc57063559d55ea3c84775e30e41John Reck
812d8732610ff48dc57063559d55ea3c84775e30e41John Reck        @Override
813d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public void run() {
814d8732610ff48dc57063559d55ea3c84775e30e41John Reck            try {
815d8732610ff48dc57063559d55ea3c84775e30e41John Reck                while (!isInterrupted()) {
816d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    Tile tile = waitForTile();
817f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                    decodeTile(tile);
818d8732610ff48dc57063559d55ea3c84775e30e41John Reck                }
819d8732610ff48dc57063559d55ea3c84775e30e41John Reck            } catch (InterruptedException ex) {
820f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck                // We were finished
821d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
822d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
823d8732610ff48dc57063559d55ea3c84775e30e41John Reck
824d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
825d8732610ff48dc57063559d55ea3c84775e30e41John Reck}
826