1104c45677660586026a7e74ef8c47d396403d50eMichael Jurka/*
2104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Copyright (C) 2013 The Android Open Source Project
3104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *
4104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Licensed under the Apache License, Version 2.0 (the "License");
5104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * you may not use this file except in compliance with the License.
6104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * You may obtain a copy of the License at
7104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *
8104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *      http://www.apache.org/licenses/LICENSE-2.0
9104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *
10104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Unless required by applicable law or agreed to in writing, software
11104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * distributed under the License is distributed on an "AS IS" BASIS,
12104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * See the License for the specific language governing permissions and
14104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * limitations under the License.
15104c45677660586026a7e74ef8c47d396403d50eMichael Jurka */
16104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
17104c45677660586026a7e74ef8c47d396403d50eMichael Jurkapackage com.android.photos.views;
18104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
19104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.annotation.SuppressLint;
20104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.annotation.TargetApi;
21104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.content.Context;
22104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.graphics.Bitmap;
23104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.graphics.Canvas;
24104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.graphics.Color;
25104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.graphics.Matrix;
26104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.graphics.Paint;
27104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.graphics.Paint.Align;
28104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.graphics.RectF;
29104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.opengl.GLSurfaceView;
30104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.opengl.GLSurfaceView.Renderer;
31104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.os.Build;
32104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.util.AttributeSet;
33104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.view.Choreographer;
34104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.view.Choreographer.FrameCallback;
35104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.view.View;
36104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.widget.FrameLayout;
37104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
38104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport com.android.gallery3d.glrenderer.BasicTexture;
39104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport com.android.gallery3d.glrenderer.GLES20Canvas;
40104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport com.android.photos.views.TiledImageRenderer.TileSource;
41104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
42104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport javax.microedition.khronos.egl.EGLConfig;
43104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport javax.microedition.khronos.opengles.GL10;
44104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
45104c45677660586026a7e74ef8c47d396403d50eMichael Jurka/**
46104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Shows an image using {@link TiledImageRenderer} using either {@link GLSurfaceView}
47104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * or {@link BlockingGLTextureView}.
48104c45677660586026a7e74ef8c47d396403d50eMichael Jurka */
49104c45677660586026a7e74ef8c47d396403d50eMichael Jurkapublic class TiledImageView extends FrameLayout {
50104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
51104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final boolean USE_TEXTURE_VIEW = false;
52104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final boolean IS_SUPPORTED =
53104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
54104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final boolean USE_CHOREOGRAPHER =
55104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
56104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
57104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private BlockingGLTextureView mTextureView;
58104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private GLSurfaceView mGLSurfaceView;
59104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private boolean mInvalPending = false;
60104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private FrameCallback mFrameCallback;
61104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
62104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected static class ImageRendererWrapper {
63104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        // Guarded by locks
64104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public float scale;
65104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public int centerX, centerY;
668713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka        public int rotation;
67104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public TileSource source;
68104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Runnable isReadyCallback;
69104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
70104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        // GL thread only
71104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        TiledImageRenderer image;
72104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
73104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
74104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private float[] mValues = new float[9];
75104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
76104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // -------------------------
77104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // Guarded by mLock
78104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // -------------------------
79104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected Object mLock = new Object();
80104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected ImageRendererWrapper mRenderer;
81104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
82104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static boolean isTilingSupported() {
83104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return IS_SUPPORTED;
84104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
85104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
86104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public TiledImageView(Context context) {
87104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        this(context, null);
88104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
89104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
90104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public TiledImageView(Context context, AttributeSet attrs) {
91104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        super(context, attrs);
92104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (!IS_SUPPORTED) {
93104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return;
94104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
95104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
96104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mRenderer = new ImageRendererWrapper();
97104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mRenderer.image = new TiledImageRenderer(this);
98104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        View view;
99104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (USE_TEXTURE_VIEW) {
100104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mTextureView = new BlockingGLTextureView(context);
101104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mTextureView.setRenderer(new TileRenderer());
102104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            view = mTextureView;
103104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } else {
104104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mGLSurfaceView = new GLSurfaceView(context);
105104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mGLSurfaceView.setEGLContextClientVersion(2);
106104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mGLSurfaceView.setRenderer(new TileRenderer());
107104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mGLSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
108104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            view = mGLSurfaceView;
109104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
110104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        addView(view, new LayoutParams(
111104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
112104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        //setTileSource(new ColoredTiles());
113104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
114104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
115ffed65886ecd83f52dc1c052897511c95a5f371aSelim Cinek    @Override
116ffed65886ecd83f52dc1c052897511c95a5f371aSelim Cinek    public void setVisibility(int visibility) {
117ffed65886ecd83f52dc1c052897511c95a5f371aSelim Cinek        super.setVisibility(visibility);
118ffed65886ecd83f52dc1c052897511c95a5f371aSelim Cinek        // need to update inner view's visibility because it seems like we're causing it to draw
119ffed65886ecd83f52dc1c052897511c95a5f371aSelim Cinek        // from {@link #dispatchDraw} or {@link #invalidate} even if we are invisible.
120ffed65886ecd83f52dc1c052897511c95a5f371aSelim Cinek        if (USE_TEXTURE_VIEW) {
121ffed65886ecd83f52dc1c052897511c95a5f371aSelim Cinek            mTextureView.setVisibility(visibility);
122ffed65886ecd83f52dc1c052897511c95a5f371aSelim Cinek        } else {
123ffed65886ecd83f52dc1c052897511c95a5f371aSelim Cinek            mGLSurfaceView.setVisibility(visibility);
124ffed65886ecd83f52dc1c052897511c95a5f371aSelim Cinek        }
125ffed65886ecd83f52dc1c052897511c95a5f371aSelim Cinek    }
126ffed65886ecd83f52dc1c052897511c95a5f371aSelim Cinek
127104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void destroy() {
128104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (!IS_SUPPORTED) {
129104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return;
130104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
131104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (USE_TEXTURE_VIEW) {
132104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mTextureView.destroy();
133104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } else {
134104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mGLSurfaceView.queueEvent(mFreeTextures);
135104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
136104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
137104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
138104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private Runnable mFreeTextures = new Runnable() {
139104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
140104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        @Override
141104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public void run() {
142104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mRenderer.image.freeTextures();
143104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
144104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    };
145104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
146104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void onPause() {
147104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (!IS_SUPPORTED) {
148104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return;
149104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
150104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (!USE_TEXTURE_VIEW) {
151104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mGLSurfaceView.onPause();
152104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
153104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
154104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
155104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void onResume() {
156104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (!IS_SUPPORTED) {
157104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return;
158104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
159104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (!USE_TEXTURE_VIEW) {
160104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mGLSurfaceView.onResume();
161104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
162104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
163104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
164104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void setTileSource(TileSource source, Runnable isReadyCallback) {
165104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (!IS_SUPPORTED) {
166104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return;
167104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
168104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        synchronized (mLock) {
169104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mRenderer.source = source;
170104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mRenderer.isReadyCallback = isReadyCallback;
171104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mRenderer.centerX = source != null ? source.getImageWidth() / 2 : 0;
172104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mRenderer.centerY = source != null ? source.getImageHeight() / 2 : 0;
173104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mRenderer.rotation = source != null ? source.getRotation() : 0;
174104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mRenderer.scale = 0;
175104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            updateScaleIfNecessaryLocked(mRenderer);
176104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
177104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        invalidate();
178104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
179104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
180104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
181104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected void onLayout(boolean changed, int left, int top, int right,
182104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int bottom) {
183104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        super.onLayout(changed, left, top, right, bottom);
184104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (!IS_SUPPORTED) {
185104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return;
186104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
187104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        synchronized (mLock) {
188104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            updateScaleIfNecessaryLocked(mRenderer);
189104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
190104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
191104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
192104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void updateScaleIfNecessaryLocked(ImageRendererWrapper renderer) {
193104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (renderer == null || renderer.source == null
194104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                || renderer.scale > 0 || getWidth() == 0) {
195104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return;
196104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
197104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        renderer.scale = Math.min(
198104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                (float) getWidth() / (float) renderer.source.getImageWidth(),
199104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                (float) getHeight() / (float) renderer.source.getImageHeight());
200104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
201104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
202104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
203104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected void dispatchDraw(Canvas canvas) {
204104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (!IS_SUPPORTED) {
205104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return;
206104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
207104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (USE_TEXTURE_VIEW) {
208104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mTextureView.render();
209104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
210104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        super.dispatchDraw(canvas);
211104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
212104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
213104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @SuppressLint("NewApi")
214104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
215104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void setTranslationX(float translationX) {
216104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (!IS_SUPPORTED) {
217104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return;
218104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
219104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        super.setTranslationX(translationX);
220104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
221104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
222104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
223104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void invalidate() {
224104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (!IS_SUPPORTED) {
225104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return;
226104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
227104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (USE_TEXTURE_VIEW) {
228104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            super.invalidate();
229104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mTextureView.invalidate();
230104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } else {
231104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (USE_CHOREOGRAPHER) {
232104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                invalOnVsync();
233104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            } else {
234104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mGLSurfaceView.requestRender();
235104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
236104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
237104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
238104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
239104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
240104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void invalOnVsync() {
241104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (!mInvalPending) {
242104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mInvalPending = true;
243104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (mFrameCallback == null) {
244104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mFrameCallback = new FrameCallback() {
245104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    @Override
246104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    public void doFrame(long frameTimeNanos) {
247104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        mInvalPending = false;
248104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        mGLSurfaceView.requestRender();
249104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    }
250104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                };
251104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
252104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Choreographer.getInstance().postFrameCallback(mFrameCallback);
253104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
254104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
255104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
256104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private RectF mTempRectF = new RectF();
257104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void positionFromMatrix(Matrix matrix) {
258104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (!IS_SUPPORTED) {
259104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return;
260104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
261104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (mRenderer.source != null) {
262104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            final int rotation = mRenderer.source.getRotation();
263104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            final boolean swap = !(rotation % 180 == 0);
264104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            final int width = swap ? mRenderer.source.getImageHeight()
265104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    : mRenderer.source.getImageWidth();
266104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            final int height = swap ? mRenderer.source.getImageWidth()
267104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    : mRenderer.source.getImageHeight();
268104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mTempRectF.set(0, 0, width, height);
269104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            matrix.mapRect(mTempRectF);
270104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            matrix.getValues(mValues);
271104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int cx = width / 2;
272104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int cy = height / 2;
273104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            float scale = mValues[Matrix.MSCALE_X];
274104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int xoffset = Math.round((getWidth() - mTempRectF.width()) / 2 / scale);
275104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int yoffset = Math.round((getHeight() - mTempRectF.height()) / 2 / scale);
276104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (rotation == 90 || rotation == 180) {
277104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                cx += (mTempRectF.left / scale) - xoffset;
278104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            } else {
279104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                cx -= (mTempRectF.left / scale) - xoffset;
280104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
281104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (rotation == 180 || rotation == 270) {
282104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                cy += (mTempRectF.top / scale) - yoffset;
283104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            } else {
284104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                cy -= (mTempRectF.top / scale) - yoffset;
285104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
286104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mRenderer.scale = scale;
287104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mRenderer.centerX = swap ? cy : cx;
288104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mRenderer.centerY = swap ? cx : cy;
289104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            invalidate();
290104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
291104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
292104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
293104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private class TileRenderer implements Renderer {
294104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
295104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        private GLES20Canvas mCanvas;
296104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
297104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        @Override
298104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
299104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mCanvas = new GLES20Canvas();
300104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            BasicTexture.invalidateAllTextures();
301104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mRenderer.image.setModel(mRenderer.source, mRenderer.rotation);
302104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
303104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
304104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        @Override
305104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public void onSurfaceChanged(GL10 gl, int width, int height) {
306104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mCanvas.setSize(width, height);
307104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mRenderer.image.setViewSize(width, height);
308104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
309104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
310104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        @Override
311104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public void onDrawFrame(GL10 gl) {
312104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mCanvas.clearBuffer();
313104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Runnable readyCallback;
314104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            synchronized (mLock) {
315104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                readyCallback = mRenderer.isReadyCallback;
316104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mRenderer.image.setModel(mRenderer.source, mRenderer.rotation);
317104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mRenderer.image.setPosition(mRenderer.centerX, mRenderer.centerY,
318104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        mRenderer.scale);
319104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
320104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            boolean complete = mRenderer.image.draw(mCanvas);
321104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (complete && readyCallback != null) {
322104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                synchronized (mLock) {
323104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    // Make sure we don't trample on a newly set callback/source
324104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    // if it changed while we were rendering
325104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    if (mRenderer.isReadyCallback == readyCallback) {
326104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        mRenderer.isReadyCallback = null;
327104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    }
328104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
329104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                if (readyCallback != null) {
330104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    post(readyCallback);
331104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
332104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
333104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
334104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
335104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
336104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
337104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @SuppressWarnings("unused")
338104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static class ColoredTiles implements TileSource {
339104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        private static final int[] COLORS = new int[] {
340104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Color.RED,
341104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Color.BLUE,
342104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Color.YELLOW,
343104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Color.GREEN,
344104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Color.CYAN,
345104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Color.MAGENTA,
346104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Color.WHITE,
347104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        };
348104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
349104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        private Paint mPaint = new Paint();
350104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        private Canvas mCanvas = new Canvas();
351104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
352104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        @Override
353104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public int getTileSize() {
354104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return 256;
355104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
356104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
357104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        @Override
358104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public int getImageWidth() {
359104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return 16384;
360104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
361104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
362104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        @Override
363104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public int getImageHeight() {
364104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return 8192;
365104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
366104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
367104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        @Override
368104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public int getRotation() {
369104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return 0;
370104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
371104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
372104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        @Override
373104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public Bitmap getTile(int level, int x, int y, Bitmap bitmap) {
374104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int tileSize = getTileSize();
375104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (bitmap == null) {
376104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                bitmap = Bitmap.createBitmap(tileSize, tileSize,
377104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        Bitmap.Config.ARGB_8888);
378104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
379104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mCanvas.setBitmap(bitmap);
380104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mCanvas.drawColor(COLORS[level]);
381104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mPaint.setColor(Color.BLACK);
382104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mPaint.setTextSize(20);
383104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mPaint.setTextAlign(Align.CENTER);
384104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mCanvas.drawText(x + "x" + y, 128, 128, mPaint);
385104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            tileSize <<= level;
386104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            x /= tileSize;
387104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            y /= tileSize;
388104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mCanvas.drawText(x + "x" + y + " @ " + level, 128, 30, mPaint);
389104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mCanvas.setBitmap(null);
390104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return bitmap;
391104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
392104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
393104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        @Override
394104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public BasicTexture getPreview() {
395104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return null;
396104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
397104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
398104c45677660586026a7e74ef8c47d396403d50eMichael Jurka}
399