Пробейте отверстие в оверлейном прямоугольнике с включенным ускорением HW при просмотре

У меня есть вид, который делает некоторые основные рисунки. После этого я хочу нарисовать прямоугольник с пробитым отверстием, чтобы была видна только область предыдущего рисунка. И я хотел бы сделать это с включенным аппаратным ускорением, чтобы обеспечить лучшую производительность.

В настоящее время у меня есть два метода, которые работают, но работает только при отключенном аппаратном ускорении, а другой работает слишком медленно.

Метод 1: Ускорение SW (медленно)

final int saveCount = canvas.save();

// Clip out a circle.
circle.reset();
circle.addCircle(cx, cy, radius, Path.Direction.CW);
circle.close();
canvas.clipPath(circle, Region.Op.DIFFERENCE);

// Draw the rectangle color.
canvas.drawColor(backColor);

canvas.restoreToCount(saveCount);

Это не работает с включенным аппаратным ускорением для представления, потому что "canvas.clipPath" не поддерживается в этом режиме (я знаю, что могу форсировать рендеринг SW, но я бы хотел этого избежать).

Метод 2: Ускорение HW (V. Медленный)

// Create a new canvas.
final Bitmap b = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
final Canvas c = new Canvas(b);

// Draw the rectangle colour.
c.drawColor(backColor);

// Erase a circle.
c.drawCircle(cx, cy, radius, eraser);

// Draw the bitmap on our views  canvas.
canvas.drawBitmap(b, 0, 0, null);

Где ластик создан как

eraser = new Paint()
eraser.setColor(0xFFFFFFFF);
eraser.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));

Это явно медленно - новый Bitmap Размер вида создается при каждом вызове рисования.

Метод 3: Ускорение HW (быстро, не работает на некоторых устройствах)

canvas.drawColor(backColor);
canvas.drawCircle(cx, cy, radius, eraser);

То же, что метод ускорения HW, но не требуется дополнительный холст. Однако с этим есть серьезная проблема - он работает с принудительным рендерингом SW, но в HTC One X (Android 4.0.4 и, возможно, на некоторых других устройствах), по крайней мере, с включенным рендерингом HW, он оставляет круг полностью черным. Это, вероятно, связано с 22361.

Метод 4: Ускорение HW (приемлемо, работает на всех устройствах)

Согласно предложению Яна по улучшению метода 2, я избегал создания растрового изображения в каждом вызове onDrawвместо этого в onSizeChanged:

if (w != oldw || h != oldh) {
    b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
    c = new Canvas(b);
}

А потом просто использовал их в onDraw:

if (overlayBitmap == null) {
   b = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
   c = new Canvas(b);
}
b.eraseColor(Color.TRANSPARENT);
c.drawColor(backColor);
c.drawCircle(cx, cy, radius, eraser);
canvas.drawBitmap(b, 0, 0, null);

Производительность не так хороша, как в методе 3, но намного лучше, чем 2 и немного лучше, чем 1.

Вопрос

Как я могу достичь того же эффекта, но сделать это способом, совместимым с ускорением HW (И это работает последовательно на устройствах)? Способ, который увеличивает производительность рендеринга SW, также был бы приемлемым.

NB. При перемещении по кругу я просто лишаю законной силы регион, а не весь холст, поэтому там нет места для улучшения производительности.

1 ответ

Решение

Вместо того, чтобы выделять новый холст на каждом перекрашивании, вы должны иметь возможность выделить его один раз, а затем повторно использовать холст на каждом перекрашивании.

при инициализации и при изменении размера:

Bitmap b = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);

на перекрас:

b.eraseColor(Color.TRANSPARENT);
    // needed if backColor is not opaque; thanks @JosephEarl
c.drawColor(backColor);
c.drawCircle(cx, cy, radius, eraser);

canvas.drawBitmap(b, 0, 0, null);
Другие вопросы по тегам