Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Blurry offset paths when canvas is scaled under hardware acceleration

My application uses a canvas that I scale so that I can specify path points in meters instead of pixels. When I scale the canvas, then draw a line using path.lineTo(), with hardware acceleration on, the line is blurry and offset. This does not happen with hardware acceleration off or with canvas.drawLine().

Here is the code to reproduce the problem:

package com.example.canvasproblem;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends Activity {
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(new MyView(this));
    }

    class MyView extends View {
        Paint pen = new Paint();
        public MyView(Context context) {
            super(context);
            pen.setStyle(Paint.Style.STROKE);
            pen.setStrokeWidth(1f); // 1 meters wide
            //this.setLayerType(LAYER_TYPE_SOFTWARE, null);
        }
        protected void onDraw(Canvas canvas) {
            float width_meter = 10.0f; // width of canvas in meters
            float width_pxl = canvas.getWidth(); // width of canvas in pixels
            float height_pxl = canvas.getHeight(); // height of canvas in pixels

            canvas.save();
            canvas.translate(width_pxl/2, height_pxl/2); // make center of canvas (0,0)
            canvas.scale(width_pxl/width_meter, width_pxl/width_meter); // convert to meters

            // path
            Path path = new Path();
            path.moveTo(0, 0);
            path.lineTo(0, 4);
            canvas.drawPath(path, pen);

            // line
            canvas.drawLine(0, 0, 0, 4, pen);

            canvas.restore();
        }
    }
}

Here is a screenshot of the problem output (the correct drawLine() is shown on top of lineTo()):

Screenshot.png

The hardware is a 1024x768 tablet, running android 4.1.1. The processor is a Rockchip RK30.

My preference is to use Path's with hardware acceleration, for rounded joins between points and speed. Please let me know if I am doing something wrong to create this problem. Thank you

Be gentle, this is my first post.

like image 261
Mark Woodward Avatar asked Apr 18 '13 18:04

Mark Woodward


2 Answers

This is a limitation of the hardware accelerated renderer. Paths are rasterized at their native size before transform. In your case, the Path is transformed into a 1x4 texture. That texture is then scaled at draw time. To work around this, scale your Path directly by using Path.transform(Matrix). You can also use scaled coordinates when building the path.

like image 54
Romain Guy Avatar answered Oct 04 '22 20:10

Romain Guy


Thanks to Romain Guy's answer, here is a wrapper for the drawPath() method that produces the same results for hardware acceleration on and off. It only handles the case where the x & y scaling in the existing matrix are the same, and it might not be the most efficient.

void drawPath(Canvas canvas, Path path, final Paint pen) {
    canvas.save();

    // get the current matrix
    Matrix mat = canvas.getMatrix();

    // reverse the effects of the current matrix
    Matrix inv = new Matrix();
    mat.invert(inv);
    canvas.concat(inv);

    // transform the path
    path.transform(mat);

    // get the scale for transforming the Paint
    float[] pts = {0, 0, 1, 0}; // two points 1 unit away from each other
    mat.mapPoints(pts);
    float scale = (float) Math.sqrt(Math.pow(pts[0]-pts[2], 2) + Math.pow(pts[1]-pts[3], 2));

    // copy the existing Paint
    Paint pen2 = new Paint();
    pen2.set(pen);

    // scale the Paint
    pen2.setStrokeMiter(pen.getStrokeMiter()*scale);
    pen2.setStrokeWidth(pen.getStrokeWidth()*scale);

    // draw the path
    canvas.drawPath(path, pen2);

    canvas.restore();
}
like image 31
Mark Woodward Avatar answered Oct 04 '22 22:10

Mark Woodward