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