1/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8package com.skia;
9
10import javax.microedition.khronos.egl.EGL10;
11import javax.microedition.khronos.egl.EGLConfig;
12import javax.microedition.khronos.egl.EGLDisplay;
13import javax.microedition.khronos.opengles.GL10;
14
15import android.content.Context;
16import android.opengl.EGL14;
17import android.opengl.GLSurfaceView;
18import android.os.Build;
19import android.util.Log;
20import android.view.MotionEvent;
21
22public class SkiaSampleView extends GLSurfaceView {
23
24    private final SkiaSampleRenderer mSampleRenderer;
25    private boolean mRequestedOpenGLAPI; // true == use (desktop) OpenGL. false == use OpenGL ES.
26    private int mRequestedMSAASampleCount;
27
28    public SkiaSampleView(Context ctx, boolean useOpenGL, int msaaSampleCount) {
29        super(ctx);
30
31        mSampleRenderer = new SkiaSampleRenderer(this);
32        mRequestedMSAASampleCount = msaaSampleCount;
33
34        setEGLContextClientVersion(2);
35        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
36            setEGLConfigChooser(8, 8, 8, 8, 0, 8);
37        } else {
38            mRequestedOpenGLAPI = useOpenGL;
39            setEGLConfigChooser(new SampleViewEGLConfigChooser());
40        }
41        setRenderer(mSampleRenderer);
42        setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
43    }
44
45    @Override
46    public boolean onTouchEvent(MotionEvent event) {
47        int count = event.getPointerCount();
48        for (int i = 0; i < count; i++) {
49            final float x = event.getX(i);
50            final float y = event.getY(i);
51            final int owner = event.getPointerId(i);
52            int action = event.getAction() & MotionEvent.ACTION_MASK;
53            switch (action) {
54            case MotionEvent.ACTION_POINTER_UP:
55                action = MotionEvent.ACTION_UP;
56                break;
57            case MotionEvent.ACTION_POINTER_DOWN:
58                action = MotionEvent.ACTION_DOWN;
59                break;
60            default:
61                break;
62            }
63            final int finalAction = action;
64            queueEvent(new Runnable() {
65                @Override
66                public void run() {
67                    mSampleRenderer.handleClick(owner, x, y, finalAction);
68                }
69            });
70        }
71        return true;
72    }
73
74    public void inval() {
75        queueEvent(new Runnable() {
76            @Override
77            public void run() {
78                mSampleRenderer.postInval();
79            }
80        });
81    }
82
83    public void terminate() {
84        queueEvent(new Runnable() {
85            @Override
86            public void run() {
87                mSampleRenderer.term();
88            }
89        });
90    }
91
92    public void showOverview() {
93        queueEvent(new Runnable() {
94            @Override
95            public void run() {
96                mSampleRenderer.showOverview();
97            }
98        });
99    }
100
101    public void nextSample() {
102        queueEvent(new Runnable() {
103            @Override
104            public void run() {
105                mSampleRenderer.nextSample();
106            }
107        });
108    }
109
110    public void previousSample() {
111        queueEvent(new Runnable() {
112            @Override
113            public void run() {
114                mSampleRenderer.previousSample();
115            }
116        });
117    }
118
119    public void goToSample(final int position) {
120        queueEvent(new Runnable() {
121            @Override
122            public void run() {
123                mSampleRenderer.goToSample(position);
124            }
125        });
126    }
127
128    public void toggleRenderingMode() {
129        queueEvent(new Runnable() {
130            @Override
131            public void run() {
132                mSampleRenderer.toggleRenderingMode();
133            }
134        });
135    }
136
137    public void toggleSlideshow() {
138        queueEvent(new Runnable() {
139            @Override
140            public void run() {
141                mSampleRenderer.toggleSlideshow();
142            }
143        });
144    }
145
146    public void toggleFPS() {
147        queueEvent(new Runnable() {
148            @Override
149            public void run() {
150                mSampleRenderer.toggleFPS();
151            }
152        });
153    }
154
155    public void toggleTiling() {
156        queueEvent(new Runnable() {
157            @Override
158            public void run() {
159                mSampleRenderer.toggleTiling();
160            }
161        });
162    }
163
164    public void toggleBBox() {
165        queueEvent(new Runnable() {
166            @Override
167            public void run() {
168                mSampleRenderer.toggleBBox();
169            }
170        });
171    }
172
173    public void saveToPDF() {
174        queueEvent(new Runnable() {
175            @Override
176            public void run() {
177                mSampleRenderer.saveToPDF();
178            }
179        });
180    }
181
182    public boolean getUsesOpenGLAPI() {
183        return mRequestedOpenGLAPI;
184    }
185
186    public int getMSAASampleCount() {
187        return mSampleRenderer.getMSAASampleCount();
188    }
189
190    private class SampleViewEGLConfigChooser implements GLSurfaceView.EGLConfigChooser {
191
192        @Override
193        public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
194            int numConfigs = 0;
195            int[] configSpec = null;
196            int[] value = new int[1];
197
198            int[] validAPIs = new int[] {
199                EGL14.EGL_OPENGL_API,
200                EGL14.EGL_OPENGL_ES_API
201            };
202            int initialAPI = mRequestedOpenGLAPI ? 0 : 1;
203
204            for (int i = initialAPI; i < validAPIs.length && numConfigs == 0; i++) {
205                int currentAPI = validAPIs[i];
206                EGL14.eglBindAPI(currentAPI);
207
208                // setup the renderableType which will only be included in the
209                // spec if we are attempting to get access to the OpenGL APIs.
210                int renderableType = EGL14.EGL_OPENGL_BIT;
211                if (currentAPI == EGL14.EGL_OPENGL_API) {
212                    renderableType = EGL14.EGL_OPENGL_ES2_BIT;
213                }
214
215                if (mRequestedMSAASampleCount > 0) {
216                    configSpec = new int[] {
217                        EGL10.EGL_RED_SIZE, 8,
218                        EGL10.EGL_GREEN_SIZE, 8,
219                        EGL10.EGL_BLUE_SIZE, 8,
220                        EGL10.EGL_ALPHA_SIZE, 8,
221                        EGL10.EGL_DEPTH_SIZE, 0,
222                        EGL10.EGL_STENCIL_SIZE, 8,
223                        EGL10.EGL_SAMPLE_BUFFERS, 1,
224                        EGL10.EGL_SAMPLES, mRequestedMSAASampleCount,
225                        EGL10.EGL_RENDERABLE_TYPE, renderableType,
226                        EGL10.EGL_NONE
227                    };
228
229                    // EGL_RENDERABLE_TYPE is only needed when attempting to use
230                    // the OpenGL API (not ES) and causes many EGL drivers to fail
231                    // with a BAD_ATTRIBUTE error.
232                    if (!mRequestedOpenGLAPI) {
233                      configSpec[16] = EGL10.EGL_NONE;
234                      Log.i("Skia", "spec: " + configSpec);
235                    }
236
237                    if (!egl.eglChooseConfig(display, configSpec, null, 0, value)) {
238                        Log.i("Skia", "Could not get MSAA context count: " + mRequestedMSAASampleCount);
239                    }
240
241                    numConfigs = value[0];
242                }
243
244                if (numConfigs <= 0) {
245                    // Try without multisampling.
246                    configSpec = new int[] {
247                        EGL10.EGL_RED_SIZE, 8,
248                        EGL10.EGL_GREEN_SIZE, 8,
249                        EGL10.EGL_BLUE_SIZE, 8,
250                        EGL10.EGL_ALPHA_SIZE, 8,
251                        EGL10.EGL_DEPTH_SIZE, 0,
252                        EGL10.EGL_STENCIL_SIZE, 8,
253                        EGL10.EGL_RENDERABLE_TYPE, renderableType,
254                        EGL10.EGL_NONE
255                    };
256
257                    // EGL_RENDERABLE_TYPE is only needed when attempting to use
258                    // the OpenGL API (not ES) and causes many EGL drivers to fail
259                    // with a BAD_ATTRIBUTE error.
260                    if (!mRequestedOpenGLAPI) {
261                      configSpec[12] = EGL10.EGL_NONE;
262                      Log.i("Skia", "spec: " + configSpec);
263                    }
264
265                    if (!egl.eglChooseConfig(display, configSpec, null, 0, value)) {
266                      Log.i("Skia", "Could not get non-MSAA context count");
267                    }
268                    numConfigs = value[0];
269                }
270            }
271
272            if (numConfigs <= 0) {
273                throw new IllegalArgumentException("No configs match configSpec");
274            }
275
276            // Get all matching configurations.
277            EGLConfig[] configs = new EGLConfig[numConfigs];
278            if (!egl.eglChooseConfig(display, configSpec, configs, numConfigs, value)) {
279                throw new IllegalArgumentException("Could not get config data");
280            }
281
282            for (int i = 0; i < configs.length; ++i) {
283                EGLConfig config = configs[i];
284                if (findConfigAttrib(egl, display, config , EGL10.EGL_RED_SIZE, 0) == 8 &&
285                        findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0) == 8 &&
286                        findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0) == 8 &&
287                        findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0) == 8 &&
288                        findConfigAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0) == 8) {
289                    return config;
290                }
291            }
292
293            throw new IllegalArgumentException("Could not find suitable EGL config");
294        }
295
296        private int findConfigAttrib(EGL10 egl, EGLDisplay display,
297                EGLConfig config, int attribute, int defaultValue) {
298            int[] value = new int[1];
299            if (egl.eglGetConfigAttrib(display, config, attribute, value)) {
300                return value[0];
301            }
302            return defaultValue;
303        }
304
305    }
306}
307