Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to draw a lot of rectangles on canvas with good performance?

I've got to render a cinema hall map with sectors, rows and seats. Currently I have around 1000 of seats (filled rectangles) to draw and I'm doing this for every seat by calling:

canvas.drawRect(seatRect, seatPaint)

My view must also support scaling, scrolling and fling. The performance is awful. I tried to improve it by explicitly enabling hardware acceleration but nothing changed, it seems it was enabled by default on my Nexus 4 (Api 22)

Could you please suggest any methods to render huge number of rectangles at high speed ? So movement, scaling animation is smooth.

Custom View class code:

@Override
public void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    canvas.save();
    canvas.scale(mScaleFactor, mScaleFactor); // scaling support
    canvas.translate(mTranslateX, mTranslateY); // scrolling support

    if (mEventMap != null) {
        mEventMap.paint(canvas);
    }

    canvas.restore();
}

EventMap code:

public void paint(Canvas canvas) {
    for (EventPlace place : places) {
        if (place.isSelected())
            placePaint.setColor(0xFF00FF00);
        else if (place.isAvailable())
            placePaint.setColor(place.getColor());
        else
            placePaint.setColor(0xFF000000);

        canvas.drawRect(place.getBounds(), placePaint);
    }
}

None of the methods called in onDraw create any instances. There are just a LOT of rectangles...

like image 486
ievgen Avatar asked Jul 23 '15 13:07

ievgen


People also ask

Which method draws a rectangle on canvas?

The rect() method creates a rectangle. Tip: Use the stroke() or the fill() method to actually draw the rectangle on the canvas.

How do you add a rectangle to a canvas?

To draw the rectangle onto a canvas, you can use the fill() or stroke() methods. Note: To both create and render a rectangle in one step, use the fillRect() or strokeRect() methods.


1 Answers

The main problem that you have right now is basically the fact that you perform a large amount of drawing cycles (equal to the number of Rects you have) it's better to store all those rects in Path object, in order to reduce number of drawing cycles. Since you have three states of seats I suggest creation of three Paths. I would suggest doing something like this:

private void init() {
    mSelectedPath = new Path();
    mAvailablePath = new Path();
    mUnavalablePath = new Path();

    mAvailablePaint = new Paint();
    mSelectedPaint = new Paint();
    mUnavalablePaint = new Paint();

    mUnavalablePaint.setColor(Color.RED);
    mSelectedPaint.setColor(Color.YELLOW);
    mAvailablePaint.setColor(Color.GREEN);

    for (EventPlace place : mData) {
        if (place.isSelected())
            mSelectedPath.addRect(rectF, Path.Direction.CW);
        else if (place.isAvailable())
            mAvailablePath.addRect(rectF, Path.Direction.CW);
        else
            mUnavalablePath.addRect(rectF, Path.Direction.CW);
    }
}

Then when you want to associate date with this View you should do something like this:

public void setData(List<EventPlace> data) {
    mData = data;
    init();
    invalidate();
}

Actually you can do it however you like, just keep in mind that you have to call init method and then invalidate. And in the onDraw:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    canvas.save();
    canvas.scale(mScaleFactor, mScaleFactor); // scaling support
    canvas.translate(mTranslateX, mTranslateY); // scrolling support

    canvas.drawPath(mAvailablePath, mAvailablePaint);
    canvas.drawPath(mUnavalablePath, mUnavalablePaint);
    canvas.drawPath(mSelectedPath, mSelectedPaint);

    canvas.restore();
}

Perhaps you would also want to add some logics to exclude those Rects that are not visible at the moment from paths in order to tweak performance.

I tried animating my approach on test set of Rects and it was running smoothly with 200 rects, I didn't try performing a serious benchmark though

like image 144
Chaosit Avatar answered Oct 05 '22 03:10

Chaosit