Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Canvas.drawVertices(...) draws nothing

The next class is a view for a red triangle:

public class FreeStyleViewII extends View {

private final Paint paint = new Paint();
private final int[] colors = new int[] {
        Color.RED,
        Color.RED,
        Color.RED,
        0xFF000000, 0xFF000000, 0xFF000000
};
private final float[] verts = new float[] {
    1f/2f * 200f, 1f/4f * 200f,
    1f/4f * 200f, 3f/4f * 200f,
    3f/4f * 200f, 3f/4f * 200f
};
private final Path path = new Path();
{
    path.moveTo(1/2 * 200, 1/4 * 200);
    path.lineTo(1/4 * 200, 3/4 * 200);
    path.lineTo(3/4 * 200, 3/4 * 200);
    path.lineTo(1/2 * 200, 1/4 * 200);
}

private final RectF bounds = new RectF();

    public FreeStyleViewII(Context context) {
        super(context);
    }

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

public FreeStyleViewII(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
}

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.clipRect(bounds);
        canvas.drawRGB(0, 0, 0);

        paint.setStyle(Style.FILL);
        paint.setColor(Color.RED);

        // HERE. WHY DRAWVERTICES DOESN'T WORK BUT DRAWPATH DOES?... 
        canvas.drawVertices(Canvas.VertexMode.TRIANGLES, verts.length, verts, 0, null, 0, colors, 0, null, 0, 0, paint);
        // canvas.drawPath(path, paint);

        paint.setStyle(Style.STROKE);
        paint.setColor(Color.LTGRAY);
        canvas.drawLine(0, bounds.bottom / 2, bounds.right, bounds.bottom / 2, paint);
        canvas.drawLine(bounds.right / 2, 0, bounds.right / 2, bounds.bottom, paint);

        // Delay
    try {  
        Thread.sleep(30);  
    } catch (InterruptedException e) { }

        invalidate();

    }

@Override
public void onSizeChanged(int w, int h, int oldW, int oldH) {
    bounds.set(1, 1, w - 1, h - 1);
    System.out.println(bounds.left + " " + bounds.top + " " + bounds.right + " " + bounds.bottom);

    verts[0] = bounds.left + ((bounds.right - bounds.left) * (1f / 2f));
    verts[1] = bounds.top + ((bounds.bottom - bounds.top)  * (1f / 4f));
    System.out.println(" Point: X ----> " + verts[0] + " | Y ----> " + verts[1]);

    verts[2] = bounds.left + ((bounds.right - bounds.left) * (1f / 4f));
    verts[3] = bounds.top + ((bounds.bottom - bounds.top)  * (3f / 4f));
    System.out.println(" Point: X ----> " + verts[2] + " | Y ----> " + verts[3]);

    verts[4] = bounds.left + ((bounds.right - bounds.left) * (3f / 4f));
    verts[5] = bounds.top + ((bounds.bottom - bounds.top)  * (3f / 4f));
    System.out.println(" Point: X ----> " + verts[4] + " | Y ----> " + verts[5]);

    path.reset();

    path.moveTo(verts[0], verts[1]);
    path.lineTo(verts[2], verts[3]);
    path.lineTo(verts[4], verts[5]);
    path.lineTo(verts[0], verts[1]);

}
}

When I use Canvas.drawPath method, it works fine. However, if I change to Canvas.drawVertices, it draws nothing. I have checked what is said in Bug in Canvas.drawVertices? (with repro code and logcat) and Method drawVertices() didn't drawing anything on Android Canvas, but the result is the same in my case.

I am using AndroVM(v 4.1.1) in VirtualBox(v 4.1.22) for testing. Could it be the emulator?

Any idea?

like image 726
GYLZ Avatar asked Feb 04 '13 00:02

GYLZ


1 Answers

Short answer:

Disable hardware acceleration and you will be fine!

Long answer:

I had the exact same problem. In my custom View code, I have a call to Canvas.drawVertices() that looks like this:

canvas.drawVertices(Canvas.VertexMode.TRIANGLES, mTriVerts.length, mTriVerts, 0, null, 0, mTriangleColors, 0, null, 0, 0, mTriPaint);

This works exactly like expected on many devices, but on my 4.x test device the triangle which I am trying to draw simply disappears, and there are no traces in the logcat to indicate as to why.

After not finding a solution at S.O. I started digging around and found by chance the following shocking solution: a bunch of standard stuff won't work if hardware acceleration is enabled, including any call to Canvas.drawVertices(), and hardware acceleration is turned on by default on all devices that support it for Android 4.x and newer.

The problem and a good solution is outlined in the official android docs. To summarize it here, just put this on the line preceeding your unsupported calls:

setLayerType(View.LAYER_TYPE_SOFTWARE, null);

This is on View level, and it is unfortunately not possible to re-enable hardware acceleration on a view that has been set to software mode as of now, but reading the docs it felt like that might be an option in the future. If you feel your code needs hardware acceleration, simply avoid using the calls, or collect you non compliant code in a view by itself, and profile it to see that you are actually gaining something by doing it this way.

IMPORTANT EDIT:

The setLayerType() call is only supported on Android API level 11 and later, that means you must wrap your calls to this function in a version check for it to work on earlier versions like this:

if (android.os.Build.VERSION.SDK_INT >= 11) {
    setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}

NOW you should be all set!

PS: I hope this answer reaches you in time!

like image 62
Lennart Rolland Avatar answered Nov 02 '22 12:11

Lennart Rolland