Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between drawing directly to canvas and drawing on bitmap and then canvas in Android onDraw()

I'm writing a custom view that displays signals. In order to shorten my onDraw() time I cache everything I've drawn so far in a Bitmap and just append to that in every onDraw() call. By doing this I can save huge amounts of time since I only need to draw a few fixels at a time instead of redoing the whole thing.

There is on thing bothering me though - it appears as drawing directly to the provided canvas provides a more "accurate" drawing than drawing on the bitmap first and then drawing the bitmap on the canvas. By looking at the lower part of the following picture you can see the difference:

Look at the lower part of the signal to see difference - there are the same drawLine calls

I uploaded a demo project displaying the discrepancy at https://github.com/gardarh/android-uglybitmapdrawing/ but the relevant code is as follows:

@Override
public void onDraw(Canvas canvas) {
    if(cachedBitmap == null) {
        cachedBitmap = Bitmap.createBitmap(getWidth(), 200, Config.ARGB_8888);
        cachedCanvas = new Canvas(cachedBitmap);
    }

    for(int i = 0; i < COORDS.length; i++) {
        float[] curCoords = COORDS[i];
        canvas.drawLine(curCoords[0], curCoords[1], curCoords[2], curCoords[3], linePaint);
        cachedCanvas.drawLine(curCoords[0], curCoords[1], curCoords[2], curCoords[3], linePaint);
    }
    canvas.drawBitmap(cachedBitmap, 0, 120, null);
}

Why are the two traces not the same and more importantly, how can I make the lower trace look like the upper one?

like image 646
gardarh Avatar asked Jun 10 '13 13:06

gardarh


People also ask

What is canvas and Bitmap in Android?

Canvas is the place or medium where perfroms/executes the operation of drawing, and Bitmap is responsible for storing the pixel of the picture you draw.

What is onDraw method in Android?

Override onDraw() The parameter to onDraw() is a Canvas object that the view can use to draw itself. The Canvas class defines methods for drawing text, lines, bitmaps, and many other graphics primitives. You can use these methods in onDraw() to create your custom user interface (UI).

What is the use of Bitmap canvas and paint class?

Canvas API is a drawing framework that is provided in Android, with the help of which we can create custom shapes like rectangle, circle, and many more in our UI design. With the help of this API, we can draw any type of shape for our app. The drawing of the different shapes is done using Bitmap.


2 Answers

The reason for the differences is that the canvas drawing is done by hardware acceleration (GPU), and the bitmap drawing is done by software (CPU). If you disable hardware acceleration, they become the exact same. If you multiply the X coordinates by 10, you will see that the difference is in the way lines are joined. These are minor one pixel difference and I wouldn't bother with them. I am not sure which one is the more accurate, they seem like just slightly different implementations.

like image 52
yoah Avatar answered Oct 06 '22 19:10

yoah


Android framework API provides 2D drawing APIs for simple animation that does not require major dynamic changes. There are two ways of implementation using these API.

1. Drawing to a View
2. Drawing on a Canvas

1.Drawing a circle to View

Drawing to view is a better option when your UI does not require dynamic changes in the application. This can be achieved simply by extending the View class and define an onDraw() callback method.
Use the Canvas given to you for all your drawing,
using various Canvas.draw...() methods (Ex: canvas.drawCircle(x / 2, y / 2, radius, paint);). onDraw() is a callback method invoked when the view is initially drawn.

Below is a simple example code to draw a circle:-

MainActivity.java

 public class MainActivity extends Activity {

     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(new MyView(this));
     }

     public class MyView extends View {
         public MyView(Context context) {
              super(context);
              // TODO Auto-generated constructor stub
         }

         @Override
         protected void onDraw(Canvas canvas) {
            // TODO Auto-generated method stub
            super.onDraw(canvas);
            int x = getWidth();
            int y = getHeight();
            int radius;
            radius = 100;
            Paint paint = new Paint();
            paint.setStyle(Paint.Style.FILL);
            paint.setColor(Color.WHITE);
            canvas.drawPaint(paint);
            // Use Color.parseColor to define HTML colors
            paint.setColor(Color.parseColor("#FB9J2F"));
            canvas.drawCircle(x / 2, y / 2, radius, paint);
        }
     } }

2. Drawing rectangle on a canvas

To draw dynamic 2D graphics where in your application needs to regularly re draw itself, drawing on a canvas is a better option. A Canvas works for you as an interface, to the actual surface upon which your graphics will be drawn.

If you need to create a new Canvas, then you must define the bitmap upon which drawing will actually be performed. The Bitmap is always required for a Canvas.

The below example explains to draw a rectangle:-

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/mylayout">     </LinearLayout>

MainActivity.java

public class MainActivity extends Activity {
     @Override
     public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Paint paint = new Paint();
        paint.setColor(Color.parseColor("#DD4N5C"));
        Bitmap bitmap = Bitmap.createBitmap(512, 800, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap); 
        canvas.drawRect(150, 150, 250, 250, paint); 
        LinearLayout layout = (LinearLayout) findViewById(R.id.mylayout);
        layout.setBackgroundDrawable(new BitmapDrawable(bitmap));   
     }

     }
like image 32
Praveen Kumar Verma Avatar answered Oct 06 '22 18:10

Praveen Kumar Verma