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:

No comments: