Project

General

Profile

« Previous | Next » 

Revision 862fcd79

Added by Leszek Koltunski over 8 years ago

New app: PlainMonaLisa (Mona Lisa rendered on plain SurfaceView)

View differences:

build.gradle
6 6

  
7 7
    defaultConfig {
8 8
        applicationId "org.distorted.examples"
9
        minSdkVersion 15
9
        minSdkVersion 18
10 10
        targetSdkVersion 23
11 11
    }
12 12

  
src/main/AndroidManifest.xml
34 34
        <activity android:name=".cubes.CubesActivity" />       
35 35
        <activity android:name=".quaternion.QuaternionActivity" />          
36 36
        <activity android:name=".effects3d.Effects3DActivity" />  
37
        <activity android:name=".plainmonalisa.PlainMonaLisaActivity" />  
37 38
    </application>
38 39
</manifest>
src/main/java/org/distorted/examples/TableOfContents.java
36 36
import org.distorted.examples.cubes.CubesActivity;
37 37
import org.distorted.examples.quaternion.QuaternionActivity;
38 38
import org.distorted.examples.effects3d.Effects3DActivity;
39
import org.distorted.examples.plainmonalisa.PlainMonaLisaActivity;
39 40

  
40 41
///////////////////////////////////////////////////////////////////////////////////////////////////
41 42

  
......
238 239
      data.add(item);
239 240
      activityMapping.put(i++, Effects3DActivity.class);
240 241
   }
242

  
243
   {
244
      final Map<String, Object> item = new HashMap<String, Object>();
245
      item.put(ITEM_IMAGE, R.drawable.icon_example_monalisa);
246
      item.put(ITEM_TITLE, (i+1)+". "+getText(R.string.example_plainmonalisa));
247
      item.put(ITEM_SUBTITLE, getText(R.string.example_plainmonalisa_subtitle));
248
      data.add(item);
249
      activityMapping.put(i++, PlainMonaLisaActivity.class);
250
   }
241 251
     
242 252
   final SimpleAdapter dataAdapter = new SimpleAdapter(this, data, R.layout.toc_item, new String[] {ITEM_IMAGE, ITEM_TITLE, ITEM_SUBTITLE}, new int[] {R.id.Image, R.id.Title, R.id.SubTitle});
243 253
   setListAdapter(dataAdapter);  
src/main/java/org/distorted/examples/plainmonalisa/EglCore.java
1
package org.distorted.examples.plainmonalisa;
2

  
3
import android.graphics.SurfaceTexture;
4
import android.opengl.EGL14;
5
import android.opengl.EGLConfig;
6
import android.opengl.EGLContext;
7
import android.opengl.EGLDisplay;
8
import android.opengl.EGLExt;
9
import android.opengl.EGLSurface;
10
import android.util.Log;
11
import android.view.Surface;
12

  
13
///////////////////////////////////////////////////////////////////////////////////////////////////
14
/**
15
 * Core EGL state (display, context, config).
16
 * <p>
17
 * The EGLContext must only be attached to one thread at a time.  This class is not thread-safe.
18
 */
19
public final class EglCore
20
  {
21
  private static final String TAG = "EglCore";
22

  
23
  /**
24
   * Constructor flag: surface must be recordable.  This discourages EGL from using a
25
   * pixel format that cannot be converted efficiently to something usable by the video
26
   * encoder.
27
   */
28
  public static final int FLAG_RECORDABLE = 0x01;
29

  
30
  /**
31
   * Constructor flag: ask for GLES3, fall back to GLES2 if not available.  Without this
32
   * flag, GLES2 is used.
33
   */
34
  public static final int FLAG_TRY_GLES3 = 0x02;
35

  
36
  // Android-specific extension.
37
  private static final int EGL_RECORDABLE_ANDROID = 0x3142;
38

  
39
  private EGLDisplay mEGLDisplay = EGL14.EGL_NO_DISPLAY;
40
  private EGLContext mEGLContext = EGL14.EGL_NO_CONTEXT;
41
  private EGLConfig mEGLConfig = null;
42
  private int mGlVersion = -1;
43

  
44
///////////////////////////////////////////////////////////////////////////////////////////////////
45
  /**
46
   * Prepares EGL display and context.
47
   * <p>
48
   * Equivalent to EglCore(null, 0).
49
   */
50
  public EglCore() {
51
        this(null, 0);
52
    }
53

  
54
///////////////////////////////////////////////////////////////////////////////////////////////////
55
  /**
56
   * Prepares EGL display and context.
57
   * <p>
58
   * @param sharedContext The context to share, or null if sharing is not desired.
59
   * @param flags Configuration bit flags, e.g. FLAG_RECORDABLE.
60
   */
61
  public EglCore(EGLContext sharedContext, int flags)
62
    {
63
    if (mEGLDisplay != EGL14.EGL_NO_DISPLAY)
64
      {
65
      throw new RuntimeException("EGL already set up");
66
      }
67

  
68
    if (sharedContext == null)
69
      {
70
      sharedContext = EGL14.EGL_NO_CONTEXT;
71
      }
72

  
73
    mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
74

  
75
    if (mEGLDisplay == EGL14.EGL_NO_DISPLAY)
76
      {
77
      throw new RuntimeException("unable to get EGL14 display");
78
      }
79

  
80
    int[] version = new int[2];
81

  
82
    if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1))
83
      {
84
      mEGLDisplay = null;
85
      throw new RuntimeException("unable to initialize EGL14");
86
      }
87

  
88
    // Try to get a GLES3 context, if requested.
89
    if ((flags & FLAG_TRY_GLES3) != 0)
90
      {
91
      //Log.d(TAG, "Trying GLES 3");
92
      EGLConfig config = getConfig(flags, 3);
93

  
94
      if (config != null)
95
        {
96
        int[] attrib3_list =  { EGL14.EGL_CONTEXT_CLIENT_VERSION, 3, EGL14.EGL_NONE };
97
        EGLContext context = EGL14.eglCreateContext(mEGLDisplay, config, sharedContext, attrib3_list, 0);
98

  
99
        if (EGL14.eglGetError() == EGL14.EGL_SUCCESS)
100
          {
101
          //Log.d(TAG, "Got GLES 3 config");
102
          mEGLConfig = config;
103
          mEGLContext = context;
104
          mGlVersion = 3;
105
          }
106
        }
107
      }
108

  
109
    if (mEGLContext == EGL14.EGL_NO_CONTEXT)
110
      {  // GLES 2 only, or GLES 3 attempt failed
111
         //Log.d(TAG, "Trying GLES 2");
112
      EGLConfig config = getConfig(flags, 2);
113

  
114
      if (config == null)
115
        {
116
        throw new RuntimeException("Unable to find a suitable EGLConfig");
117
        }
118
      int[] attrib2_list = { EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_NONE };
119
      EGLContext context = EGL14.eglCreateContext(mEGLDisplay, config, sharedContext, attrib2_list, 0);
120
      checkEglError("eglCreateContext");
121
      mEGLConfig = config;
122
      mEGLContext = context;
123
      mGlVersion = 2;
124
      }
125

  
126
    // Confirm with query.
127
    int[] values = new int[1];
128
    EGL14.eglQueryContext(mEGLDisplay, mEGLContext, EGL14.EGL_CONTEXT_CLIENT_VERSION, values, 0);
129

  
130
    Log.d(TAG, "EGLContext created, client version " + values[0]);
131
    }
132

  
133
///////////////////////////////////////////////////////////////////////////////////////////////////
134
  /**
135
   * Finds a suitable EGLConfig.
136
   *
137
   * @param flags Bit flags from constructor.
138
   * @param version Must be 2 or 3.
139
   */
140
  private EGLConfig getConfig(int flags, int version)
141
    {
142
    int renderableType = EGL14.EGL_OPENGL_ES2_BIT;
143

  
144
    if (version >= 3)
145
      {
146
      renderableType |= EGLExt.EGL_OPENGL_ES3_BIT_KHR;
147
      }
148

  
149
    // The actual surface is generally RGBA or RGBX, so situationally omitting alpha
150
    // doesn't really help.  It can also lead to a huge performance hit on glReadPixels()
151
    // when reading into a GL_RGBA buffer.
152
    int[] attribList =
153
                {
154
                EGL14.EGL_RED_SIZE, 8,
155
                EGL14.EGL_GREEN_SIZE, 8,
156
                EGL14.EGL_BLUE_SIZE, 8,
157
                EGL14.EGL_ALPHA_SIZE, 8,
158
                //EGL14.EGL_DEPTH_SIZE, 16,
159
                //EGL14.EGL_STENCIL_SIZE, 8,
160
                EGL14.EGL_RENDERABLE_TYPE, renderableType,
161
                EGL14.EGL_NONE, 0,      // placeholder for recordable [@-3]
162
                EGL14.EGL_NONE
163
                };
164

  
165
    if ((flags & FLAG_RECORDABLE) != 0)
166
      {
167
      attribList[attribList.length - 3] = EGL_RECORDABLE_ANDROID;
168
      attribList[attribList.length - 2] = 1;
169
      }
170

  
171
    EGLConfig[] configs = new EGLConfig[1];
172
    int[] numConfigs = new int[1];
173

  
174
    if (!EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, configs, 0, configs.length, numConfigs, 0))
175
      {
176
      Log.w(TAG, "unable to find RGB8888 / " + version + " EGLConfig");
177
      return null;
178
      }
179

  
180
    return configs[0];
181
    }
182

  
183
///////////////////////////////////////////////////////////////////////////////////////////////////
184
  /**
185
   * Discards all resources held by this class, notably the EGL context.  This must be
186
   * called from the thread where the context was created.
187
   * <p>
188
   * On completion, no context will be current.
189
   */
190
  public void release()
191
    {
192
    if (mEGLDisplay != EGL14.EGL_NO_DISPLAY)
193
      {
194
      // Android is unusual in that it uses a reference-counted EGLDisplay.  So for
195
      // every eglInitialize() we need an eglTerminate().
196
      EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
197
      EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);
198
      EGL14.eglReleaseThread();
199
      EGL14.eglTerminate(mEGLDisplay);
200
      }
201

  
202
    mEGLDisplay = EGL14.EGL_NO_DISPLAY;
203
    mEGLContext = EGL14.EGL_NO_CONTEXT;
204
    mEGLConfig = null;
205
    }
206

  
207
///////////////////////////////////////////////////////////////////////////////////////////////////
208
  @Override
209
  protected void finalize() throws Throwable
210
    {
211
    try
212
      {
213
      if (mEGLDisplay != EGL14.EGL_NO_DISPLAY)
214
        {
215
        // We're limited here -- finalizers don't run on the thread that holds
216
        // the EGL state, so if a surface or context is still current on another
217
        // thread we can't fully release it here.  Exceptions thrown from here
218
        // are quietly discarded.  Complain in the log file.
219
        Log.w(TAG, "WARNING: EglCore was not explicitly released -- state may be leaked");
220
        release();
221
        }
222
      }
223
    finally
224
      {
225
      super.finalize();
226
      }
227
    }
228

  
229
///////////////////////////////////////////////////////////////////////////////////////////////////
230
  /**
231
   * Destroys the specified surface.  Note the EGLSurface won't actually be destroyed if it's
232
   * still current in a context.
233
   */
234
  public void releaseSurface(EGLSurface eglSurface)
235
    {
236
    EGL14.eglDestroySurface(mEGLDisplay, eglSurface);
237
    }
238

  
239
///////////////////////////////////////////////////////////////////////////////////////////////////
240
  /**
241
   * Creates an EGL surface associated with a Surface.
242
   * <p>
243
   * If this is destined for MediaCodec, the EGLConfig should have the "recordable" attribute.
244
   */
245
  public EGLSurface createWindowSurface(Object surface)
246
    {
247
    if (!(surface instanceof Surface) && !(surface instanceof SurfaceTexture))
248
      {
249
      throw new RuntimeException("invalid surface: " + surface);
250
      }
251

  
252
    // Create a window surface, and attach it to the Surface we received.
253
    int[] surfaceAttribs = { EGL14.EGL_NONE };
254
    EGLSurface eglSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mEGLConfig, surface, surfaceAttribs, 0);
255
    checkEglError("eglCreateWindowSurface");
256

  
257
    if (eglSurface == null)
258
      {
259
      throw new RuntimeException("surface was null");
260
      }
261
    return eglSurface;
262
    }
263

  
264
///////////////////////////////////////////////////////////////////////////////////////////////////
265
  /**
266
   * Makes our EGL context current, using the supplied surface for both "draw" and "read".
267
   */
268
  public void makeCurrent(EGLSurface eglSurface)
269
    {
270
    if (mEGLDisplay == EGL14.EGL_NO_DISPLAY)
271
      {
272
      // called makeCurrent() before create?
273
      Log.d(TAG, "NOTE: makeCurrent w/o display");
274
      }
275
    if (!EGL14.eglMakeCurrent(mEGLDisplay, eglSurface, eglSurface, mEGLContext))
276
      {
277
      throw new RuntimeException("eglMakeCurrent failed");
278
      }
279
    }
280

  
281
///////////////////////////////////////////////////////////////////////////////////////////////////
282
  /**
283
   * Makes no context current.
284
   */
285
  public void makeNothingCurrent()
286
    {
287
    if (!EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT))
288
      {
289
      throw new RuntimeException("eglMakeCurrent failed");
290
      }
291
    }
292

  
293
///////////////////////////////////////////////////////////////////////////////////////////////////
294
  /**
295
   * Returns the GLES version this context is configured for (currently 2 or 3).
296
   */
297
  public int getGlVersion()
298
    {
299
    return mGlVersion;
300
    }
301

  
302
///////////////////////////////////////////////////////////////////////////////////////////////////
303
  /**
304
   * Calls eglSwapBuffers.  Use this to "publish" the current frame.
305
   *
306
   * @return false on failure
307
   */
308
  public boolean swapBuffers(EGLSurface eglSurface)
309
    {
310
    return EGL14.eglSwapBuffers(mEGLDisplay, eglSurface);
311
    }
312

  
313
///////////////////////////////////////////////////////////////////////////////////////////////////
314
  /**
315
   * Checks for EGL errors.  Throws an exception if an error has been raised.
316
   */
317
  private void checkEglError(String msg)
318
    {
319
    int error;
320

  
321
    if ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS)
322
      {
323
      throw new RuntimeException(msg + ": EGL error: 0x" + Integer.toHexString(error));
324
      }
325
    }
326
  }
327
///////////////////////////////////////////////////////////////////////////////////////////////////
src/main/java/org/distorted/examples/plainmonalisa/PlainMonaLisaActivity.java
1
package org.distorted.examples.plainmonalisa;
2

  
3
import org.distorted.library.Distorted;
4

  
5
import android.app.Activity;
6
import android.os.Bundle;
7

  
8
///////////////////////////////////////////////////////////////////////////////////////////////////
9

  
10
public class PlainMonaLisaActivity extends Activity
11
{
12
    private PlainMonaLisaSurfaceView mView;
13

  
14
///////////////////////////////////////////////////////////////////////////////////////////////////
15
    
16
    @Override
17
    protected void onCreate(Bundle icicle) 
18
      {
19
      super.onCreate(icicle);
20
      mView = new PlainMonaLisaSurfaceView(this);
21
      setContentView(mView);
22
      }
23

  
24
///////////////////////////////////////////////////////////////////////////////////////////////////
25
    
26
    @Override
27
    protected void onPause() 
28
      {
29
      mView.onPause();
30
      super.onPause();
31
      }
32

  
33
///////////////////////////////////////////////////////////////////////////////////////////////////
34
    
35
    @Override
36
    protected void onResume() 
37
      {
38
      super.onResume();
39
      mView.onResume();
40
      }
41
    
42
///////////////////////////////////////////////////////////////////////////////////////////////////
43
    
44
    @Override
45
    protected void onDestroy() 
46
      {
47
      Distorted.onDestroy();  
48
      super.onDestroy();
49
      }
50
    
51
}
src/main/java/org/distorted/examples/plainmonalisa/PlainMonaLisaSurfaceView.java
1
package org.distorted.examples.plainmonalisa;
2

  
3
import android.content.Context;
4
import android.view.Choreographer;
5
import android.view.SurfaceView;
6
import android.view.SurfaceHolder;
7
import android.util.Log;
8

  
9
///////////////////////////////////////////////////////////////////////////////////////////////////
10

  
11
class PlainMonaLisaSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Choreographer.FrameCallback
12
  {
13
  private static final String TAG = "MonaLisaSurface";
14
  private RenderThread mRenderThread;
15

  
16
///////////////////////////////////////////////////////////////////////////////////////////////////
17

  
18
  public PlainMonaLisaSurfaceView(Context context)
19
    {
20
    super(context);
21
    getHolder().addCallback(this);
22
    }
23

  
24
///////////////////////////////////////////////////////////////////////////////////////////////////
25

  
26
  public void surfaceCreated(SurfaceHolder holder)
27
    {
28
    Log.d(TAG, "surfaceCreated holder=" + holder);
29

  
30
    mRenderThread = new RenderThread(holder, this);
31
    mRenderThread.setName("GL render");
32
    mRenderThread.start();
33
    mRenderThread.waitUntilReady();
34

  
35
    RenderHandler rh = mRenderThread.getHandler();
36

  
37
    if (rh != null)
38
      {
39
      rh.sendSurfaceCreated();
40
      }
41

  
42
    Choreographer.getInstance().postFrameCallback(this);
43
    }
44

  
45
///////////////////////////////////////////////////////////////////////////////////////////////////
46

  
47
  public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
48
    {
49
    Log.d(TAG, "surfaceChanged fmt=" + format + " size=" + width + "x" + height +" holder=" + holder);
50

  
51
    RenderHandler rh = mRenderThread.getHandler();
52

  
53
    if (rh != null)
54
      {
55
      rh.sendSurfaceChanged(format, width, height);
56
      }
57
    }
58

  
59
///////////////////////////////////////////////////////////////////////////////////////////////////
60

  
61
  public void surfaceDestroyed(SurfaceHolder holder)
62
    {
63
    Log.d(TAG, "surfaceDestroyed holder=" + holder);
64

  
65
    // We need to wait for the render thread to shut down before continuing because we
66
    // don't want the Surface to disappear out from under it mid-render.  The frame
67
    // notifications will have been stopped back in onPause(), but there might have
68
    // been one in progress.
69

  
70
    RenderHandler rh = mRenderThread.getHandler();
71

  
72
    if (rh != null)
73
      {
74
      rh.sendShutdown();
75

  
76
      try
77
        {
78
        mRenderThread.join();
79
        }
80
      catch (InterruptedException ie)
81
        {
82
        throw new RuntimeException("join was interrupted", ie);
83
        }
84
      }
85
    mRenderThread = null;
86

  
87
    Log.d(TAG, "surfaceDestroyed complete");
88
    }
89

  
90
///////////////////////////////////////////////////////////////////////////////////////////////////
91

  
92
  public void onPause()
93
    {
94
    Log.d(TAG, "onPause unhooking choreographer");
95
    Choreographer.getInstance().removeFrameCallback(this);
96
    }
97

  
98
///////////////////////////////////////////////////////////////////////////////////////////////////
99

  
100
  public void onResume()
101
    {
102
    if (mRenderThread != null)
103
      {
104
      Log.d(TAG, "onResume re-hooking choreographer");
105
      Choreographer.getInstance().postFrameCallback(this);
106
      }
107
    }
108

  
109
///////////////////////////////////////////////////////////////////////////////////////////////////
110

  
111
  @Override
112
  public void doFrame(long frameTimeNanos)
113
    {
114
    RenderHandler rh = mRenderThread.getHandler();
115

  
116
    if (rh != null)
117
      {
118
      Choreographer.getInstance().postFrameCallback(this);
119
      rh.sendDoFrame(frameTimeNanos);
120
      }
121
    }
122
  }
123

  
124

  
src/main/java/org/distorted/examples/plainmonalisa/RenderHandler.java
1
package org.distorted.examples.plainmonalisa;
2

  
3
import android.os.Handler;
4
import android.os.Message;
5
import android.util.Log;
6

  
7
import java.lang.ref.WeakReference;
8

  
9
///////////////////////////////////////////////////////////////////////////////////////////////////
10

  
11
/**
12
 * Handler for RenderThread.  Used for messages sent from the UI thread to the render thread.
13
 * <p>
14
 * The object is created on the render thread, and the various "send" methods are called
15
 * from the UI thread.
16
 */
17
public class RenderHandler extends Handler
18
  {
19
  private static final String TAG = "RenderHandler";
20

  
21
  private static final int MSG_SURFACE_CREATED = 0;
22
  private static final int MSG_SURFACE_CHANGED = 1;
23
  private static final int MSG_DO_FRAME = 2;
24
  private static final int MSG_SHUTDOWN = 4;
25

  
26
  // This shouldn't need to be a weak ref, since we'll go away when the Looper quits,
27
  // but no real harm in it.
28
  private WeakReference<RenderThread> mWeakRenderThread;
29

  
30
///////////////////////////////////////////////////////////////////////////////////////////////////
31
  /**
32
   * Call from render thread.
33
   */
34
  public RenderHandler(RenderThread rt)
35
      {
36
      mWeakRenderThread = new WeakReference<>(rt);
37
      }
38

  
39
///////////////////////////////////////////////////////////////////////////////////////////////////
40
  /**
41
   * Sends the "surface created" message.
42
   * <p>
43
   * Call from UI thread.
44
   */
45
  public void sendSurfaceCreated()
46
    {
47
    sendMessage(obtainMessage(RenderHandler.MSG_SURFACE_CREATED));
48
    }
49

  
50
///////////////////////////////////////////////////////////////////////////////////////////////////
51
  /**
52
   * Sends the "surface changed" message, forwarding what we got from the SurfaceHolder.
53
   * <p>
54
   * Call from UI thread.
55
   */
56
  public void sendSurfaceChanged(@SuppressWarnings("unused") int format, int width, int height)
57
    {
58
    sendMessage(obtainMessage(RenderHandler.MSG_SURFACE_CHANGED, width, height));
59
    }
60

  
61
///////////////////////////////////////////////////////////////////////////////////////////////////
62
  /**
63
   * Sends the "do frame" message, forwarding the Choreographer event.
64
   * <p>
65
   * Call from UI thread.
66
   */
67
  public void sendDoFrame(long frameTimeNanos)
68
    {
69
    sendMessage(obtainMessage(RenderHandler.MSG_DO_FRAME, (int) (frameTimeNanos >> 32), (int) frameTimeNanos));
70
    }
71

  
72
///////////////////////////////////////////////////////////////////////////////////////////////////
73
  /**
74
   * Sends the "shutdown" message, which tells the render thread to halt.
75
   * <p>
76
   * Call from UI thread.
77
   */
78
  public void sendShutdown()
79
      {
80
      sendMessage(obtainMessage(RenderHandler.MSG_SHUTDOWN));
81
      }
82

  
83
///////////////////////////////////////////////////////////////////////////////////////////////////
84
  @Override  // runs on RenderThread
85
  public void handleMessage(Message msg)
86
    {
87
    int what = msg.what;
88
    //Log.d(TAG, "RenderHandler [" + this + "]: what=" + what);
89
    RenderThread renderThread = mWeakRenderThread.get();
90

  
91
    if (renderThread == null)
92
      {
93
      Log.w(TAG, "RenderHandler.handleMessage: weak ref is null");
94
      return;
95
      }
96

  
97
    switch (what)
98
      {
99
      case MSG_SURFACE_CREATED: renderThread.surfaceCreated();
100
                                break;
101
      case MSG_SURFACE_CHANGED: renderThread.surfaceChanged(msg.arg1, msg.arg2);
102
                                break;
103
      case MSG_DO_FRAME:        long timestamp = (((long) msg.arg1) << 32) | (((long) msg.arg2) & 0xffffffffL);
104
                                renderThread.doFrame(timestamp);
105
                                break;
106
      case MSG_SHUTDOWN:        renderThread.shutdown();
107
                                break;
108
      default:                  throw new RuntimeException("unknown message " + what);
109
      }
110
    }
111
  }
112

  
113
///////////////////////////////////////////////////////////////////////////////////////////////////
src/main/java/org/distorted/examples/plainmonalisa/RenderThread.java
1
package org.distorted.examples.plainmonalisa;
2

  
3
import android.graphics.Bitmap;
4
import android.graphics.BitmapFactory;
5
import android.opengl.EGL14;
6
import android.opengl.EGLSurface;
7
import android.opengl.GLES20;
8
import android.os.Looper;
9
import android.os.Trace;
10
import android.util.Log;
11
import android.view.Surface;
12
import android.view.SurfaceHolder;
13
import android.view.SurfaceView;
14

  
15
import org.distorted.library.Distorted;
16
import org.distorted.library.DistortedBitmap;
17
import org.distorted.library.Float2D;
18
import org.distorted.library.Float3D;
19
import org.distorted.library.Float4D;
20
import org.distorted.examples.R;
21

  
22
import java.io.IOException;
23
import java.io.InputStream;
24

  
25
///////////////////////////////////////////////////////////////////////////////////////////////////
26
/**
27
 * This class handles all OpenGL rendering.
28
 * <p>
29
 * Start the render thread after the Surface has been created.
30
 */
31
public class RenderThread extends Thread
32
  {
33
  private static final String TAG = "RenderThread";
34

  
35
  // Object must be created on render thread to get correct Looper, but is used from
36
  // UI thread, so we need to declare it volatile to ensure the UI thread sees a fully
37
  // constructed object.
38
  private volatile RenderHandler mHandler;
39

  
40
  // Used to wait for the thread to start.
41
  private Object mStartLock = new Object();
42
  private boolean mReady = false;
43
  private volatile SurfaceHolder mSurfaceHolder;  // may be updated by UI thread
44
  private EglCore eglCore;
45
  private EGLSurface eglSurface;
46

  
47
  private DistortedBitmap monaLisa;
48
  private int bmpHeight, bmpWidth;
49

  
50
  private Float2D pLeft, pRight;
51
  private Float4D rLeft, rRight;
52
  private Float3D vLeft, vRight;
53

  
54
  SurfaceView mView;
55

  
56
///////////////////////////////////////////////////////////////////////////////////////////////////
57
  /**
58
   * Pass in the SurfaceView's SurfaceHolder.  Note the Surface may not yet exist.
59
   */
60
  public RenderThread(SurfaceHolder holder, SurfaceView view)
61
    {
62
    mSurfaceHolder = holder;
63
    mView = view;
64

  
65
    pLeft = new Float2D( 90, 258);
66
    pRight= new Float2D(176, 255);
67

  
68
    rLeft = new Float4D(-10,-10,25,25);
69
    rRight= new Float4D( 10, -5,25,25);
70

  
71
    vLeft = new Float3D(-20,-20,0);
72
    vRight= new Float3D( 20,-10,0);
73
    }
74

  
75
///////////////////////////////////////////////////////////////////////////////////////////////////
76
  /**
77
   * Thread entry point.
78
   * <p>
79
   * The thread should not be started until the Surface associated with the SurfaceHolder
80
   * has been created.  That way we don't have to wait for a separate "surface created"
81
   * message to arrive.
82
   */
83
  @Override
84
  public void run()
85
    {
86
    Looper.prepare();
87
    mHandler = new RenderHandler(this);
88
    eglCore = new EglCore(null, EglCore.FLAG_RECORDABLE | EglCore.FLAG_TRY_GLES3);
89

  
90
    synchronized (mStartLock)
91
      {
92
      mReady = true;
93
      mStartLock.notify();    // signal waitUntilReady()
94
      }
95

  
96
    Looper.loop();
97
    Log.d(TAG, "looper quit");
98

  
99
    checkGlError("releaseGl start");
100

  
101
    if (eglSurface != null)
102
      {
103
      eglCore.releaseSurface(eglSurface);
104
      eglSurface = EGL14.EGL_NO_SURFACE;
105
      }
106

  
107
    checkGlError("releaseGl done");
108

  
109
    eglCore.makeNothingCurrent();
110
    eglCore.release();
111

  
112
    synchronized (mStartLock)
113
      {
114
      mReady = false;
115
      }
116
    }
117

  
118
///////////////////////////////////////////////////////////////////////////////////////////////////
119
  /**
120
   * Waits until the render thread is ready to receive messages.
121
   * <p>
122
   * Call from the UI thread.
123
   */
124
  public void waitUntilReady()
125
    {
126
    synchronized (mStartLock)
127
      {
128
      while (!mReady)
129
        {
130
        try
131
          {
132
          mStartLock.wait();
133
          }
134
        catch (InterruptedException ie) { /* not expected */ }
135
        }
136
      }
137
    }
138

  
139
///////////////////////////////////////////////////////////////////////////////////////////////////
140
  /**
141
   * Shuts everything down.
142
   */
143
  void shutdown()
144
    {
145
    Log.d(TAG, "shutdown");
146
    Looper.myLooper().quit();
147
    }
148

  
149
///////////////////////////////////////////////////////////////////////////////////////////////////
150
  /**
151
   * Returns the render thread's Handler.  This may be called from any thread.
152
   */
153
  public RenderHandler getHandler()
154
      {
155
      return mHandler;
156
      }
157

  
158
///////////////////////////////////////////////////////////////////////////////////////////////////
159
  /**
160
   * Prepares the surface.
161
   */
162
  void surfaceCreated()
163
    {
164
    Surface surface = mSurfaceHolder.getSurface();
165

  
166
    eglSurface = eglCore.createWindowSurface(surface);
167
    eglCore.makeCurrent(eglSurface);
168

  
169
    InputStream is = mView.getContext().getResources().openRawResource(R.raw.monalisa);
170
    Bitmap bmp;
171

  
172
    try
173
      {
174
      bmp = BitmapFactory.decodeStream(is);
175
      }
176
    finally
177
      {
178
      try
179
        {
180
        is.close();
181
        }
182
      catch(IOException io) {}
183
      }
184

  
185
    monaLisa = new DistortedBitmap(bmp, 10);
186
    monaLisa.distort( vLeft, rLeft , pLeft, 1000, 0);
187
    monaLisa.distort(vRight, rRight, pRight,1000, 0);
188

  
189
    bmpHeight = bmp.getHeight();
190
    bmpWidth  = bmp.getWidth();
191

  
192
    try
193
      {
194
      Distorted.onSurfaceCreated(mView.getContext());
195
      }
196
    catch(Exception ex)
197
      {
198
      Log.e("MonaLisa", ex.getMessage() );
199
      }
200
    }
201

  
202
///////////////////////////////////////////////////////////////////////////////////////////////////
203

  
204
  void surfaceChanged(int width, int height)
205
    {
206
    Log.d(TAG, "surfaceChanged " + width + "x" + height);
207

  
208
    monaLisa.abortAllEffects(Distorted.TYPE_MATR);
209

  
210
    if( bmpHeight/bmpWidth > height/width )
211
      {
212
      int w = (height*bmpWidth)/bmpHeight;
213
      monaLisa.move((width-w)/2 ,0, 0);
214
      monaLisa.scale((float)height/bmpHeight);
215
      }
216
    else
217
      {
218
      int h = (width*bmpHeight)/bmpWidth;
219
      monaLisa.move(0 ,(height-h)/2, 0);
220
      monaLisa.scale((float)width/bmpWidth);
221
      }
222

  
223
    Distorted.onSurfaceChanged(width, height);
224
    }
225

  
226
///////////////////////////////////////////////////////////////////////////////////////////////////
227

  
228
  public void doFrame(long frameTimeNs)
229
    {
230
    Trace.beginSection("doFrame draw");
231
    eglCore.makeCurrent(eglSurface);
232

  
233
    GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
234
    GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
235
    monaLisa.draw(System.currentTimeMillis());
236

  
237
    eglCore.swapBuffers(eglSurface);
238
    Trace.endSection();
239
    }
240

  
241
///////////////////////////////////////////////////////////////////////////////////////////////////
242

  
243
  public static void checkGlError(String op)
244
    {
245
    int error = GLES20.glGetError();
246

  
247
    if (error != GLES20.GL_NO_ERROR)
248
      {
249
      String msg = op + ": glError 0x" + Integer.toHexString(error);
250
      Log.e(TAG, msg);
251
      throw new RuntimeException(msg);
252
      }
253
    }
254
  }
255
///////////////////////////////////////////////////////////////////////////////////////////////////
src/main/res/values/strings.xml
80 80
    <string name="example_quaternion_subtitle">Random rotations using quaternions.</string>
81 81
    <string name="example_effects3d">Effects3D</string>  
82 82
    <string name="example_effects3d_subtitle">Test results of all effects on a 3D object.</string>
83
    
83
    <string name="example_plainmonalisa">PlainMonaLisa</string>  
84
    <string name="example_plainmonalisa_subtitle">MonaLisa rendered on a plain SurfaceView</string>
85

  
84 86
    <string name="example_movingeffects_toast">Click on \'RESET\' and define your path by touching the screen. Then click on one of the effects and see it move along your path.</string>
85 87
    <string name="example_cubes_toast">Rotate the cubes by swiping the screen</string>
86 88
</resources>

Also available in: Unified diff