Drawing very slow with Canvas and SurfaceView

  • Replies:0
Sepp Meier
  • Forum posts: 4

Jan 4, 2018, 10:42:27 PM via Website

Hello everybody,

I have created an Android 5.1 application with which I can write with a stylus on a tablet using SurfaceView and Canvas. You can find my code below. Unfortunately, the writing is very slow, especially when lot of text is written it starts getting slow. I think the problem is that it is rendered in software. Of course, a possibility to speedup would be to use OpenGL but I don't know it and I think this has a too steep learning curve for me right now.

Nevertheless, is there a possibility to speedup my code (i.e. to make it more responsible when writing)? Or else is it easy to change my code to OpenGL?

public class DrawingView extends SurfaceView implements OnTouchListener {
    private Bitmap mBitmap;
    private Canvas m_Canvas;
    private Path m_Path;
    private Paint m_Paint;
    private ArrayList<Pair<Path, Paint>> paths = new ArrayList<>();
    private float mX, mY;
    private static final float TOUCH_TOLERANCE = 4;
    private boolean isEraserActive = false;
    private int pathCount = 0;
    private View rectangleView;

    public DrawingView(Context context, AttributeSet attr) {
        super(context, attr);

        setFocusable(true);
        setFocusableInTouchMode(true);
        setBackgroundColor(Color.TRANSPARENT);
        this.setOnTouchListener(this);
        onCanvasInitialization();
        setAlpha(0.99f);
    }

    public void onCanvasInitialization() {
        m_Paint = new Paint();
        m_Paint.setAntiAlias(true);
        m_Paint.setDither(true);
        m_Paint.setColor(Color.parseColor("#000000"));
        m_Paint.setStyle(Paint.Style.STROKE);
        m_Paint.setStrokeJoin(Paint.Join.ROUND);
        m_Paint.setStrokeCap(Paint.Cap.ROUND);
        m_Paint.setStrokeWidth(2);

        m_Path = new Path();
        Paint newPaint = new Paint(m_Paint);
        paths.add(new Pair<>(m_Path, newPaint));
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_4444);
        m_Canvas = new Canvas(mBitmap);
    }

    @Override
    public boolean onTouch(View arg0, MotionEvent event) {
        float x = event.getX();
        float y = event.getY();

        if (rectangleView != null) {
            ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) rectangleView.getLayoutParams();
            params.leftMargin = (int) x;
            params.topMargin = (int) y;
            rectangleView.setLayoutParams(params);
            return true;
        } else {
            if (event.getToolType(0) == MotionEvent.TOOL_TYPE_ERASER) {
                isEraserActive = true;
            } else {
                isEraserActive = false;
            }

            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    touch_start(x, y);
                    invalidate();
                    break;
                case MotionEvent.ACTION_MOVE:
                    touch_move(x, y);
                    invalidate();
                    break;
                case MotionEvent.ACTION_UP:
                    touch_up();
                    invalidate();
                    break;
            }
            return true;
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        for (Pair<Path, Paint> p : paths) {
            canvas.drawPath(p.first, p.second);
        }
    }

    private void touch_start(float x, float y) {
        if (isEraserActive) {
            m_Paint.setStrokeWidth(20);
            m_Paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
            Paint newPaint = new Paint(m_Paint);
            paths.add(new Pair<>(m_Path, newPaint));
        } else { 
            m_Paint.setColor(Color.BLACK);
            m_Paint.setStrokeWidth(2);
            m_Paint.setXfermode(null);
            Paint newPaint = new Paint(m_Paint);
            paths.add(new Pair<>(m_Path, newPaint));
            pathCount++;
        }

        m_Path.reset();
        m_Path.moveTo(x, y);
        mX = x;
        mY = y;
    }

    private void touch_move(float x, float y) {
        float dx = Math.abs(x - mX);
        float dy = Math.abs(y - mY);
        if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
            m_Path.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
            mX = x;
            mY = y;
        }
    }

    private void touch_up() {
        m_Path.lineTo(mX, mY);
        m_Canvas.drawPath(m_Path, m_Paint);
        m_Path = new Path();
        Paint newPaint = new Paint(m_Paint);
        paths.add(new Pair<>(m_Path, newPaint));
    }

    public void reset() {
        for (Pair<Path, Paint> p : paths) {
            p.first.reset();
        }
        paths.clear();
        pathCount = 0;
        mBitmap.eraseColor(Color.TRANSPARENT);
        invalidate();
    }
}

Reply