TiledImageView.java revision d8732610ff48dc57063559d55ea3c84775e30e41
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.Canvas;
22d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.graphics.Color;
23d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.graphics.Paint;
24d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.graphics.Paint.Align;
25d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.opengl.GLSurfaceView.Renderer;
26d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.util.AttributeSet;
27d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.view.MotionEvent;
28d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.view.ScaleGestureDetector;
29d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.view.ScaleGestureDetector.OnScaleGestureListener;
30d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.widget.FrameLayout;
31d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport com.android.gallery3d.glrenderer.GLES20Canvas;
32d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport com.android.photos.views.TiledImageRenderer.TileSource;
33d8732610ff48dc57063559d55ea3c84775e30e41John Reck
34d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport javax.microedition.khronos.egl.EGLConfig;
35d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport javax.microedition.khronos.opengles.GL10;
36d8732610ff48dc57063559d55ea3c84775e30e41John Reck
37d8732610ff48dc57063559d55ea3c84775e30e41John Reck
38d8732610ff48dc57063559d55ea3c84775e30e41John Reckpublic class TiledImageView extends FrameLayout implements OnScaleGestureListener {
39d8732610ff48dc57063559d55ea3c84775e30e41John Reck
40d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private BlockingGLTextureView mTextureView;
41d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private float mLastX, mLastY;
42d8732610ff48dc57063559d55ea3c84775e30e41John Reck
43d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private static class ImageRendererWrapper {
44d8732610ff48dc57063559d55ea3c84775e30e41John Reck        // Guarded by locks
45d8732610ff48dc57063559d55ea3c84775e30e41John Reck        float scale;
46d8732610ff48dc57063559d55ea3c84775e30e41John Reck        int centerX, centerY;
47d8732610ff48dc57063559d55ea3c84775e30e41John Reck        int rotation;
48d8732610ff48dc57063559d55ea3c84775e30e41John Reck        TileSource source;
49d8732610ff48dc57063559d55ea3c84775e30e41John Reck
50d8732610ff48dc57063559d55ea3c84775e30e41John Reck        // GL thread only
51d8732610ff48dc57063559d55ea3c84775e30e41John Reck        TiledImageRenderer image;
52d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
53d8732610ff48dc57063559d55ea3c84775e30e41John Reck
54d8732610ff48dc57063559d55ea3c84775e30e41John Reck    // TODO: left/right paging
55d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private ImageRendererWrapper mRenderers[] = new ImageRendererWrapper[1];
56d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private ImageRendererWrapper mFocusedRenderer;
57d8732610ff48dc57063559d55ea3c84775e30e41John Reck
58d8732610ff48dc57063559d55ea3c84775e30e41John Reck    // -------------------------
59d8732610ff48dc57063559d55ea3c84775e30e41John Reck    // Guarded by mLock
60d8732610ff48dc57063559d55ea3c84775e30e41John Reck    // -------------------------
61d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private Object mLock = new Object();
62d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private ScaleGestureDetector mScaleGestureDetector;
63d8732610ff48dc57063559d55ea3c84775e30e41John Reck
64d8732610ff48dc57063559d55ea3c84775e30e41John Reck    public TiledImageView(Context context) {
65d8732610ff48dc57063559d55ea3c84775e30e41John Reck        this(context, null);
66d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
67d8732610ff48dc57063559d55ea3c84775e30e41John Reck
68d8732610ff48dc57063559d55ea3c84775e30e41John Reck    public TiledImageView(Context context, AttributeSet attrs) {
69d8732610ff48dc57063559d55ea3c84775e30e41John Reck        super(context, attrs);
70d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mTextureView = new BlockingGLTextureView(context);
71d8732610ff48dc57063559d55ea3c84775e30e41John Reck        addView(mTextureView, new LayoutParams(
72d8732610ff48dc57063559d55ea3c84775e30e41John Reck                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
73d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mTextureView.setRenderer(new TileRenderer());
74d8732610ff48dc57063559d55ea3c84775e30e41John Reck        setTileSource(new ColoredTiles());
75d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mScaleGestureDetector = new ScaleGestureDetector(context, this);
76d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
77d8732610ff48dc57063559d55ea3c84775e30e41John Reck
78d8732610ff48dc57063559d55ea3c84775e30e41John Reck    public void destroy() {
79d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mTextureView.destroy();
80d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
81d8732610ff48dc57063559d55ea3c84775e30e41John Reck
82d8732610ff48dc57063559d55ea3c84775e30e41John Reck    public void setTileSource(TileSource source) {
83d8732610ff48dc57063559d55ea3c84775e30e41John Reck        synchronized (mLock) {
84d8732610ff48dc57063559d55ea3c84775e30e41John Reck            for (int i = 0; i < mRenderers.length; i++) {
85d8732610ff48dc57063559d55ea3c84775e30e41John Reck                ImageRendererWrapper renderer = mRenderers[i];
86d8732610ff48dc57063559d55ea3c84775e30e41John Reck                if (renderer == null) {
87d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    renderer = mRenderers[i] = new ImageRendererWrapper();
88d8732610ff48dc57063559d55ea3c84775e30e41John Reck                }
89d8732610ff48dc57063559d55ea3c84775e30e41John Reck                renderer.source = source;
90d8732610ff48dc57063559d55ea3c84775e30e41John Reck                renderer.centerX = renderer.source.getImageWidth() / 2;
91d8732610ff48dc57063559d55ea3c84775e30e41John Reck                renderer.centerY = renderer.source.getImageHeight() / 2;
92d8732610ff48dc57063559d55ea3c84775e30e41John Reck                renderer.rotation = 0;
93d8732610ff48dc57063559d55ea3c84775e30e41John Reck                renderer.scale = 0;
94d8732610ff48dc57063559d55ea3c84775e30e41John Reck                renderer.image = new TiledImageRenderer(this);
95d8732610ff48dc57063559d55ea3c84775e30e41John Reck                updateScaleIfNecessaryLocked(renderer);
96d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
97d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
98d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mFocusedRenderer = mRenderers[0];
99d8732610ff48dc57063559d55ea3c84775e30e41John Reck        invalidate();
100d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
101d8732610ff48dc57063559d55ea3c84775e30e41John Reck
102d8732610ff48dc57063559d55ea3c84775e30e41John Reck    @Override
103d8732610ff48dc57063559d55ea3c84775e30e41John Reck    public boolean onScaleBegin(ScaleGestureDetector detector) {
104d8732610ff48dc57063559d55ea3c84775e30e41John Reck        return true;
105d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
106d8732610ff48dc57063559d55ea3c84775e30e41John Reck
107d8732610ff48dc57063559d55ea3c84775e30e41John Reck    @Override
108d8732610ff48dc57063559d55ea3c84775e30e41John Reck    public boolean onScale(ScaleGestureDetector detector) {
109d8732610ff48dc57063559d55ea3c84775e30e41John Reck        // Don't need the lock because this will only fire inside of onTouchEvent
110d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mFocusedRenderer.scale *= detector.getScaleFactor();
111d8732610ff48dc57063559d55ea3c84775e30e41John Reck        invalidate();
112d8732610ff48dc57063559d55ea3c84775e30e41John Reck        return true;
113d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
114d8732610ff48dc57063559d55ea3c84775e30e41John Reck
115d8732610ff48dc57063559d55ea3c84775e30e41John Reck    @Override
116d8732610ff48dc57063559d55ea3c84775e30e41John Reck    public void onScaleEnd(ScaleGestureDetector detector) {
117d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
118d8732610ff48dc57063559d55ea3c84775e30e41John Reck
119d8732610ff48dc57063559d55ea3c84775e30e41John Reck    @Override
120d8732610ff48dc57063559d55ea3c84775e30e41John Reck    public boolean onTouchEvent(MotionEvent event) {
121d8732610ff48dc57063559d55ea3c84775e30e41John Reck        int action = event.getActionMasked();
122d8732610ff48dc57063559d55ea3c84775e30e41John Reck        final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP;
123d8732610ff48dc57063559d55ea3c84775e30e41John Reck        final int skipIndex = pointerUp ? event.getActionIndex() : -1;
124d8732610ff48dc57063559d55ea3c84775e30e41John Reck
125d8732610ff48dc57063559d55ea3c84775e30e41John Reck        // Determine focal point
126d8732610ff48dc57063559d55ea3c84775e30e41John Reck        float sumX = 0, sumY = 0;
127d8732610ff48dc57063559d55ea3c84775e30e41John Reck        final int count = event.getPointerCount();
128d8732610ff48dc57063559d55ea3c84775e30e41John Reck        for (int i = 0; i < count; i++) {
129d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (skipIndex == i) continue;
130d8732610ff48dc57063559d55ea3c84775e30e41John Reck            sumX += event.getX(i);
131d8732610ff48dc57063559d55ea3c84775e30e41John Reck            sumY += event.getY(i);
132d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
133d8732610ff48dc57063559d55ea3c84775e30e41John Reck        final int div = pointerUp ? count - 1 : count;
134d8732610ff48dc57063559d55ea3c84775e30e41John Reck        float x = sumX / div;
135d8732610ff48dc57063559d55ea3c84775e30e41John Reck        float y = sumY / div;
136d8732610ff48dc57063559d55ea3c84775e30e41John Reck
137d8732610ff48dc57063559d55ea3c84775e30e41John Reck        synchronized (mLock) {
138d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mScaleGestureDetector.onTouchEvent(event);
139d8732610ff48dc57063559d55ea3c84775e30e41John Reck            switch (action) {
140d8732610ff48dc57063559d55ea3c84775e30e41John Reck            case MotionEvent.ACTION_MOVE:
141d8732610ff48dc57063559d55ea3c84775e30e41John Reck                mFocusedRenderer.centerX += (mLastX - x) / mFocusedRenderer.scale;
142d8732610ff48dc57063559d55ea3c84775e30e41John Reck                mFocusedRenderer.centerY += (mLastY - y) / mFocusedRenderer.scale;
143d8732610ff48dc57063559d55ea3c84775e30e41John Reck                invalidate();
144d8732610ff48dc57063559d55ea3c84775e30e41John Reck                break;
145d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
146d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
147d8732610ff48dc57063559d55ea3c84775e30e41John Reck
148d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mLastX = x;
149d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mLastY = y;
150d8732610ff48dc57063559d55ea3c84775e30e41John Reck        return true;
151d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
152d8732610ff48dc57063559d55ea3c84775e30e41John Reck
153d8732610ff48dc57063559d55ea3c84775e30e41John Reck    @Override
154d8732610ff48dc57063559d55ea3c84775e30e41John Reck    protected void onLayout(boolean changed, int left, int top, int right,
155d8732610ff48dc57063559d55ea3c84775e30e41John Reck            int bottom) {
156d8732610ff48dc57063559d55ea3c84775e30e41John Reck        super.onLayout(changed, left, top, right, bottom);
157d8732610ff48dc57063559d55ea3c84775e30e41John Reck        synchronized (mLock) {
158d8732610ff48dc57063559d55ea3c84775e30e41John Reck            for (ImageRendererWrapper renderer : mRenderers) {
159d8732610ff48dc57063559d55ea3c84775e30e41John Reck                updateScaleIfNecessaryLocked(renderer);
160d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
161d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
162d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
163d8732610ff48dc57063559d55ea3c84775e30e41John Reck
164d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private void updateScaleIfNecessaryLocked(ImageRendererWrapper renderer) {
165d8732610ff48dc57063559d55ea3c84775e30e41John Reck        if (renderer.scale > 0 || getWidth() == 0) return;
166d8732610ff48dc57063559d55ea3c84775e30e41John Reck        renderer.scale = Math.min(
167d8732610ff48dc57063559d55ea3c84775e30e41John Reck                (float) getWidth() / (float) renderer.source.getImageWidth(),
168d8732610ff48dc57063559d55ea3c84775e30e41John Reck                (float) getHeight() / (float) renderer.source.getImageHeight());
169d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
170d8732610ff48dc57063559d55ea3c84775e30e41John Reck
171d8732610ff48dc57063559d55ea3c84775e30e41John Reck    @Override
172d8732610ff48dc57063559d55ea3c84775e30e41John Reck    protected void dispatchDraw(Canvas canvas) {
173d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mTextureView.render();
174d8732610ff48dc57063559d55ea3c84775e30e41John Reck        super.dispatchDraw(canvas);
175d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
176d8732610ff48dc57063559d55ea3c84775e30e41John Reck
177d8732610ff48dc57063559d55ea3c84775e30e41John Reck    @Override
178d8732610ff48dc57063559d55ea3c84775e30e41John Reck    public void invalidate() {
179d8732610ff48dc57063559d55ea3c84775e30e41John Reck        super.invalidate();
180d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mTextureView.invalidate();
181d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
182d8732610ff48dc57063559d55ea3c84775e30e41John Reck
183d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private class TileRenderer implements Renderer {
184d8732610ff48dc57063559d55ea3c84775e30e41John Reck
185d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private GLES20Canvas mCanvas;
186d8732610ff48dc57063559d55ea3c84775e30e41John Reck
187d8732610ff48dc57063559d55ea3c84775e30e41John Reck        @Override
188d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
189d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mCanvas = new GLES20Canvas();
190d8732610ff48dc57063559d55ea3c84775e30e41John Reck            for (ImageRendererWrapper renderer : mRenderers) {
191d8732610ff48dc57063559d55ea3c84775e30e41John Reck                renderer.image.setModel(renderer.source, renderer.rotation);
192d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
193d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
194d8732610ff48dc57063559d55ea3c84775e30e41John Reck
195d8732610ff48dc57063559d55ea3c84775e30e41John Reck        @Override
196d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public void onSurfaceChanged(GL10 gl, int width, int height) {
197d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mCanvas.setSize(width, height);
198d8732610ff48dc57063559d55ea3c84775e30e41John Reck            for (ImageRendererWrapper renderer : mRenderers) {
199d8732610ff48dc57063559d55ea3c84775e30e41John Reck                renderer.image.setViewSize(width, height);
200d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
201d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
202d8732610ff48dc57063559d55ea3c84775e30e41John Reck
203d8732610ff48dc57063559d55ea3c84775e30e41John Reck        @Override
204d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public void onDrawFrame(GL10 gl) {
205d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mCanvas.clearBuffer();
206d8732610ff48dc57063559d55ea3c84775e30e41John Reck            synchronized (mLock) {
207d8732610ff48dc57063559d55ea3c84775e30e41John Reck                for (ImageRendererWrapper renderer : mRenderers) {
208d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    renderer.image.setModel(renderer.source, renderer.rotation);
209d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    renderer.image.setPosition(renderer.centerX, renderer.centerY, renderer.scale);
210d8732610ff48dc57063559d55ea3c84775e30e41John Reck                }
211d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
212d8732610ff48dc57063559d55ea3c84775e30e41John Reck            for (ImageRendererWrapper renderer : mRenderers) {
213d8732610ff48dc57063559d55ea3c84775e30e41John Reck                renderer.image.draw(mCanvas);
214d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
215d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
216d8732610ff48dc57063559d55ea3c84775e30e41John Reck
217d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
218d8732610ff48dc57063559d55ea3c84775e30e41John Reck
219d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private static class ColoredTiles implements TileSource {
220d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private static int[] COLORS = new int[] {
221d8732610ff48dc57063559d55ea3c84775e30e41John Reck            Color.RED,
222d8732610ff48dc57063559d55ea3c84775e30e41John Reck            Color.BLUE,
223d8732610ff48dc57063559d55ea3c84775e30e41John Reck            Color.YELLOW,
224d8732610ff48dc57063559d55ea3c84775e30e41John Reck            Color.GREEN,
225d8732610ff48dc57063559d55ea3c84775e30e41John Reck            Color.CYAN,
226d8732610ff48dc57063559d55ea3c84775e30e41John Reck            Color.MAGENTA,
227d8732610ff48dc57063559d55ea3c84775e30e41John Reck            Color.WHITE,
228d8732610ff48dc57063559d55ea3c84775e30e41John Reck        };
229d8732610ff48dc57063559d55ea3c84775e30e41John Reck
230d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private Paint mPaint = new Paint();
231d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private Canvas mCanvas = new Canvas();
232d8732610ff48dc57063559d55ea3c84775e30e41John Reck
233d8732610ff48dc57063559d55ea3c84775e30e41John Reck        @Override
234d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public int getTileSize() {
235d8732610ff48dc57063559d55ea3c84775e30e41John Reck            return 256;
236d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
237d8732610ff48dc57063559d55ea3c84775e30e41John Reck
238d8732610ff48dc57063559d55ea3c84775e30e41John Reck        @Override
239d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public int getImageWidth() {
240d8732610ff48dc57063559d55ea3c84775e30e41John Reck            return 16384;
241d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
242d8732610ff48dc57063559d55ea3c84775e30e41John Reck
243d8732610ff48dc57063559d55ea3c84775e30e41John Reck        @Override
244d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public int getImageHeight() {
245d8732610ff48dc57063559d55ea3c84775e30e41John Reck            return 8192;
246d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
247d8732610ff48dc57063559d55ea3c84775e30e41John Reck
248d8732610ff48dc57063559d55ea3c84775e30e41John Reck        @Override
249d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public Bitmap getTile(int level, int x, int y, Bitmap bitmap) {
250d8732610ff48dc57063559d55ea3c84775e30e41John Reck            int tileSize = getTileSize();
251d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (bitmap == null) {
252d8732610ff48dc57063559d55ea3c84775e30e41John Reck                bitmap = Bitmap.createBitmap(tileSize, tileSize,
253d8732610ff48dc57063559d55ea3c84775e30e41John Reck                        Bitmap.Config.ARGB_8888);
254d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
255d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mCanvas.setBitmap(bitmap);
256d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mCanvas.drawColor(COLORS[level]);
257d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mPaint.setColor(Color.BLACK);
258d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mPaint.setTextSize(20);
259d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mPaint.setTextAlign(Align.CENTER);
260d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mCanvas.drawText(x + "x" + y, 128, 128, mPaint);
261d8732610ff48dc57063559d55ea3c84775e30e41John Reck            tileSize <<= level;
262d8732610ff48dc57063559d55ea3c84775e30e41John Reck            x /= tileSize;
263d8732610ff48dc57063559d55ea3c84775e30e41John Reck            y /= tileSize;
264d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mCanvas.drawText(x + "x" + y + " @ " + level, 128, 30, mPaint);
265d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mCanvas.setBitmap(null);
266d8732610ff48dc57063559d55ea3c84775e30e41John Reck            return bitmap;
267d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
268d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
269d8732610ff48dc57063559d55ea3c84775e30e41John Reck}
270