Showing posts with label Android code sample: SurfaceView. Show all posts
Showing posts with label Android code sample: SurfaceView. Show all posts

Sunday, March 16, 2014

Try Motion aftereffect on Android

The motion aftereffect (MAE) is a visual illusion experienced after viewing a moving visual stimulus for a time (tens of milliseconds to minutes) with stationary eyes, and then fixating a stationary stimulus. The stationary stimulus appears to move in the opposite direction to the original (physically moving) stimulus. The motion aftereffect is believed to be the result of motion adaptation. ~ Wikipedia.

This exercise TRY to simulate the effect on Android. The code for motion aftereffect generation is modified from http://en.wikipedia.org/wiki/File:Illusion_movie.ogg (c source code), with my optimization. But the effect seem not good! I test it on Nexus 7 tablet.

To see the illusions: Run the app on Android Tablet, and stare to the center of the image. After around one minute, look away (for example look to a face or to your hands). For few seconds everything you see will appear to distort. Or touch the motion picture on screen, it will stop and change to another ImageView.

motion aftereffect (MAE)
Motion AfterEffect (MAE)

The main part is in MySurfaceView.java.
package com.example.androidsurfaceview;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MySurfaceView extends SurfaceView {
 
    private SurfaceHolder surfaceHolder;
    private MyThread myThread;
    
    MainActivity mainActivity;
    
    int T;
    static final float freq = 80;

 public MySurfaceView(Context context) {
  super(context);
  init(context);
 }

 public MySurfaceView(Context context, 
   AttributeSet attrs) {
  super(context, attrs);
  init(context);
 }

 public MySurfaceView(Context context, 
   AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);
  init(context);
 }
 
 private void init(Context c){
  mainActivity = (MainActivity)c;
  T = 0;
  myThread = new MyThread(this);
  
  surfaceHolder = getHolder();

  
  surfaceHolder.addCallback(new SurfaceHolder.Callback(){

   @Override
   public void surfaceCreated(SurfaceHolder holder) {
    myThread.setRunning(true);
    myThread.start();
   }

   @Override
   public void surfaceChanged(SurfaceHolder holder, 
     int format, int width, int height) {
    // TODO Auto-generated method stub
    
   }

   @Override
   public void surfaceDestroyed(SurfaceHolder holder) {
    boolean retry = true;
                myThread.setRunning(false);
                while (retry) {
                       try {
                             myThread.join();
                             retry = false;
                       } catch (InterruptedException e) {
                       }
                }
   }});
 }

 protected void drawSomething(Canvas canvas) {
  
  int sizex = getWidth();
  int sizey = getHeight();

  float divby_sizex_sq = 1.0f/((float)sizex * (float)sizex);

  int[] data = new int[sizex * sizey];
  int m;
  
  T++;
  if(T >= 1200){
   T = 0;
  }
  float halfT = T * 0.5f;

  for (int j=0;j<sizey;j++){
   
   float y0 = j*2-sizey;
   float y2_2 = y0 * y0 * divby_sizex_sq;
   
   float absY = Math.abs(y0/(float)sizey);
            float halfT_plus_absY = absY + halfT;
            float halfT_neg_plus_absY = absY - halfT;
            
            int j_multi_sizex = j*sizex;
   
         for (int i=0;i<sizex;i++){
             float x=(i*2-sizex)/(float)sizex;
             
             //0.2 instead of 0.1 to have a bigger circle
             if ((x*x + y2_2)<0.2){
              m = (int) (Math.sin((halfT_plus_absY+Math.abs(x))*freq)*127.0+128) & 0xFF;
             }else {
              m = (int) (Math.sin((halfT_neg_plus_absY+Math.abs(x))*freq)*127.0+128) & 0xFF;
             }
             
             data[i+j_multi_sizex] = 0xFF000000
               + (m << 16)
               + (m << 8)
               + m;
         }
     }
     
     Bitmap bm = Bitmap.createBitmap(data, sizex, sizey, Bitmap.Config.ARGB_8888);

        canvas.drawBitmap(bm, 0, 0, null);

 }
}

MyThread.java
package com.example.androidsurfaceview;

import android.graphics.Canvas;

public class MyThread extends Thread {
 
 MySurfaceView myView;
 private boolean running = false;

 public MyThread(MySurfaceView view) {
  myView = view;
 }
 
 public void setRunning(boolean run) {
        running = run;
 }

 @Override
 public void run() {
  while(running){
   
   Canvas canvas = myView.getHolder().lockCanvas();
   
   if(canvas != null){
    synchronized (myView.getHolder()) {
     myView.drawSomething(canvas);
    }
    myView.getHolder().unlockCanvasAndPost(canvas);
   }
   
   /*
   try {
    sleep(30);
   } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   */
   
  }
 }

}

/res/layout/activity_main.xml, where @drawable/android_er is a 640x480 .png in /res/drawable/ folder.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@android:color/background_dark"
    tools:context="com.example.androidsurfaceview.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <FrameLayout 
        android:layout_width="640dp"
        android:layout_height="480dp"
        android:layout_gravity="center">
        <com.example.androidsurfaceview.MySurfaceView
            android:id="@+id/myview"
            android:layout_width="640dp"
            android:layout_height="480dp"
            android:layout_gravity="center" />
        <ImageView 
            android:id="@+id/imageicon"
            android:layout_width="640dp"
            android:layout_height="480dp"
            android:layout_gravity="center"
            android:src="@drawable/android_er"
            android:visibility="invisible"/>
    </FrameLayout>
    
</LinearLayout>

MainActivity.java. The Motion graph will run when the app start. When user touch on it, it will show the ImageView of "@drawable/android_er".
package com.example.androidsurfaceview;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;

public class MainActivity extends Activity {
 
 ImageView imageIcon;
 MySurfaceView mySurfaceView;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  mySurfaceView = (MySurfaceView)findViewById(R.id.myview);
  imageIcon = (ImageView)findViewById(R.id.imageicon);
  
  mySurfaceView.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    imageIcon.setVisibility(View.VISIBLE);
    mySurfaceView.setVisibility(View.INVISIBLE);
   }});

 }

}

Modify AndroidManifest.xml to force the app run in landscape mode.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.androidsurfaceview"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="19" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.androidsurfaceview.MainActivity"
            android:label="@string/app_name"
            android:screenOrientation="landscape" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>



download filesDownload the files.

Download and try the APK.

Saturday, March 15, 2014

Draw bitmap programmatically for SurfaceView

This exercise create Bitmap programmatically, then draw the bitmap on SurfaceView by calling canvas.drawBitmap().

Draw bitmap programmatically for SurfaceView


I show two approachs in the example:
  • prepareBitmap_A:
    - Create a array of int, fill in data point-by-point, then createBitmap from the array.
  • prepareBitmap_B:
    - Create a bitmap, then fill in pixels by calling setPixel.

I also add code to display the (approximate) processing time in various steps for reference. The videos on the bottom show the result.

MySurfaceView.java
package com.example.androidsurfaceview;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MySurfaceView extends SurfaceView {
 
    private SurfaceHolder surfaceHolder;
    private MyThread myThread;
    
    MainActivity mainActivity;
    
    long timeStart;
    long timeA;
    long timeB;
    long timeFillBackground;
    long timeDrawBitmap;
    long timeTotal;
    
    long numberOfPt;

 public MySurfaceView(Context context) {
  super(context);
  init(context);
 }

 public MySurfaceView(Context context, 
   AttributeSet attrs) {
  super(context, attrs);
  init(context);
 }

 public MySurfaceView(Context context, 
   AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);
  init(context);
 }
 
 private void init(Context c){
  mainActivity = (MainActivity)c;
  numberOfPt = 0;
  myThread = new MyThread(this);
  
  surfaceHolder = getHolder();

  
  surfaceHolder.addCallback(new SurfaceHolder.Callback(){

   @Override
   public void surfaceCreated(SurfaceHolder holder) {
    myThread.setRunning(true);
    myThread.start();
   }

   @Override
   public void surfaceChanged(SurfaceHolder holder, 
     int format, int width, int height) {
    // TODO Auto-generated method stub
    
   }

   @Override
   public void surfaceDestroyed(SurfaceHolder holder) {
    boolean retry = true;
                myThread.setRunning(false);
                while (retry) {
                       try {
                             myThread.join();
                             retry = false;
                       } catch (InterruptedException e) {
                       }
                }
   }});
 }
 
 private Bitmap prepareBitmap_A(int w, int h, long cnt){
  int[] data = new int[w*h];
  
  //fill with dummy data
  for(int x=0; x<w; x++){
   for(int y=0; y<h; y++){
    //data[x + y*w] = 0xFF000000 + x;

    if(cnt>=0){
     data[x + y*w] = 0xFFff0000;
     cnt--;
    }else{
     data[x + y*w] = 0xFFa0a0a0;
    }

   }
  }
  timeA = System.currentTimeMillis();
  Bitmap bm = Bitmap.createBitmap(data, w, h, Bitmap.Config.ARGB_8888);
  timeB = System.currentTimeMillis();
  return bm;
 }
 
 private Bitmap prepareBitmap_B(int w, int h, long cnt){

  Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
  timeA = System.currentTimeMillis();
  
  //fill with dummy data
  for(int x=0; x<w; x++){
   for(int y=0; y<h; y++){
    //data[x + y*w] = 0xFF000000 + x;

    if(cnt>=0){
     bm.setPixel(x, y, 0xFFff0000);
     cnt--;
    }else{
     bm.setPixel(x, y, 0xFFa0a0a0);
    }

   }
  }
  timeB = System.currentTimeMillis();
  return bm;
 }

 protected void drawSomething(Canvas canvas) {
  
  numberOfPt += 500;
  if(numberOfPt > (long)((getWidth()*getHeight()))){
   numberOfPt = 0;
  }
        
  timeStart = System.currentTimeMillis();
        Bitmap bmDummy = prepareBitmap_A(getWidth(), getHeight(), numberOfPt);

        canvas.drawColor(Color.BLACK);
        timeFillBackground = System.currentTimeMillis();
        canvas.drawBitmap(bmDummy, 
          0, 0, null);
        timeDrawBitmap = System.currentTimeMillis();

  mainActivity.runOnUiThread(new Runnable() {
   
   @Override
   public void run() {
    mainActivity.showDur(
     timeA - timeStart,
     timeB - timeA,
     timeFillBackground - timeB,
     timeDrawBitmap - timeFillBackground,
     timeDrawBitmap - timeStart);
   }
  });
 }

}

Modify activity_main.xml to add TextView to display processing time.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.androidsurfaceview.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />
    
    <TextView
        android:id="@+id/durA"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Duration: " />
    <TextView
        android:id="@+id/durB"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Duration: " />
    <TextView
        android:id="@+id/durFillBack"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Duration: " />
    <TextView
        android:id="@+id/durDrawBM"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Duration: " />
    <TextView
        android:id="@+id/durTotal"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Duration: " />

    <com.example.androidsurfaceview.MySurfaceView
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

MainActivity.java
package com.example.androidsurfaceview;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends Activity {
 
 TextView textDurA, textDurB, textDurFillBack, 
  textDurDrawBM, textDurTotal;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  textDurA = (TextView)findViewById(R.id.durA);
  textDurB = (TextView)findViewById(R.id.durB);
  textDurFillBack = (TextView)findViewById(R.id.durFillBack);
  textDurDrawBM = (TextView)findViewById(R.id.durDrawBM);
  textDurTotal = (TextView)findViewById(R.id.durTotal);

 }
 
 protected void showDur(long dA, long dB, long dFill, long dDraw, long dTotal){
  textDurA.setText("Duration(ms) - A: " + dA);
  textDurB.setText("Duration(ms) - B: " + dB);
  textDurFillBack.setText("Duration(ms) - Fill Background: " + dFill);
  textDurDrawBM.setText("Duration(ms) - drawBitmap: " + dDraw);
  textDurTotal.setText("Duration(ms) - Total: " + dTotal);
 }

}

MyThread.java refer to last exercise, "Create animation on SurfaceView in background Thread".


download filesDownload the files.

prepareBitmap_A:

prepareBitmap_B:

Friday, March 14, 2014

Create animation on SurfaceView in background Thread

Last post "Simple SurfaceView example" draw bitmap in surfaceCreated() callback. In this step, a customized Thread, MyThread, is implemented to draw the bitmap running across screen in background thread.


MyThread.java
package com.example.androidsurfaceview;

import android.graphics.Canvas;

public class MyThread extends Thread {
 
 MySurfaceView myView;
 private boolean running = false;

 public MyThread(MySurfaceView view) {
  myView = view;
 }
 
 public void setRunning(boolean run) {
        running = run;    
 }

 @Override
 public void run() {
  while(running){
   
   Canvas canvas = myView.getHolder().lockCanvas();
   
   if(canvas != null){
    synchronized (myView.getHolder()) {
     myView.drawSomething(canvas);
    }
    myView.getHolder().unlockCanvasAndPost(canvas);
   }
   
   try {
    sleep(30);
   } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   
  }
 }

}

Modify MySurfaceView.java in last post.
package com.example.androidsurfaceview;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MySurfaceView extends SurfaceView {
 
    private SurfaceHolder surfaceHolder;
    private Bitmap bmpIcon;
    private MyThread myThread;
    int xPos = 0;
    int yPos = 0;
    int deltaX = 5;
    int deltaY = 5;
    int iconWidth;
    int iconHeight;

 public MySurfaceView(Context context) {
  super(context);
  init();
 }

 public MySurfaceView(Context context, 
   AttributeSet attrs) {
  super(context, attrs);
  init();
 }

 public MySurfaceView(Context context, 
   AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);
  init();
 }
 
 private void init(){
  
  myThread = new MyThread(this);
  
  surfaceHolder = getHolder();
  bmpIcon = BitmapFactory.decodeResource(getResources(), 
    R.drawable.ic_launcher);

  iconWidth = bmpIcon.getWidth();
  iconHeight = bmpIcon.getHeight();
  
  surfaceHolder.addCallback(new SurfaceHolder.Callback(){

   @Override
   public void surfaceCreated(SurfaceHolder holder) {
    myThread.setRunning(true);
    myThread.start();
   }

   @Override
   public void surfaceChanged(SurfaceHolder holder, 
     int format, int width, int height) {
    // TODO Auto-generated method stub
    
   }

   @Override
   public void surfaceDestroyed(SurfaceHolder holder) {
    boolean retry = true;
                myThread.setRunning(false);
                while (retry) {
                       try {
                             myThread.join();
                             retry = false;
                       } catch (InterruptedException e) {
                       }
                }
   }});
 }

 protected void drawSomething(Canvas canvas) {
  canvas.drawColor(Color.BLACK);
        canvas.drawBitmap(bmpIcon, 
          getWidth()/2, getHeight()/2, null);
        
        xPos += deltaX;
        if(deltaX > 0){
         if(xPos >= getWidth() - iconWidth){
             deltaX *= -1;
            }
        }else{
         if(xPos <= 0){
             deltaX *= -1;
            }
        }
        
        yPos += deltaY;
        if(deltaY > 0){
         if(yPos >= getHeight() - iconHeight){
             deltaY *= -1;
            }
        }else{
         if(yPos <= 0){
             deltaY *= -1;
            }
        }

        canvas.drawColor(Color.BLACK);
        canvas.drawBitmap(bmpIcon, 
          xPos, yPos, null);

 }

}

Other files, /res/layout/activity_main.xml and MainActivity.java, refer to last post.

download filesDownload the files.

Next:
Draw bitmap programmatically for SurfaceView

Thursday, March 13, 2014

Simple SurfaceView example

It's a simple example to draw draw something on SurfaceView.

Simple SurfaceView example
Simple SurfaceView example

Create our custom MySurfaceView.java extends SurfaceView.
package com.example.androidsurfaceview;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MySurfaceView extends SurfaceView {
 
    private SurfaceHolder surfaceHolder;
    private Bitmap bmpIcon;

 public MySurfaceView(Context context) {
  super(context);
  init();
 }

 public MySurfaceView(Context context, 
   AttributeSet attrs) {
  super(context, attrs);
  init();
 }

 public MySurfaceView(Context context, 
   AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);
  init();
 }
 
 private void init(){
  surfaceHolder = getHolder();
  bmpIcon = BitmapFactory.decodeResource(getResources(), 
    R.drawable.ic_launcher);
  surfaceHolder.addCallback(new SurfaceHolder.Callback(){

   @Override
   public void surfaceCreated(SurfaceHolder holder) {
    Canvas canvas = holder.lockCanvas(null);
                drawSomething(canvas);
                holder.unlockCanvasAndPost(canvas);
   }

   @Override
   public void surfaceChanged(SurfaceHolder holder, 
     int format, int width, int height) {
    // TODO Auto-generated method stub
    
   }

   @Override
   public void surfaceDestroyed(SurfaceHolder holder) {
    // TODO Auto-generated method stub
    
   }});
 }

 protected void drawSomething(Canvas canvas) {
  canvas.drawColor(Color.BLACK);
        canvas.drawBitmap(bmpIcon, 
          getWidth()/2, getHeight()/2, null);
 }

}

Modify /res/layout/activity_main.xml to include <...MySurfaceView>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.androidsurfaceview.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <com.example.androidsurfaceview.MySurfaceView
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

MainActivity.java
package com.example.androidsurfaceview;

import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

 }

}


Next:
- Create animation on SurfaceView in background Thread