【问题标题】:Punch a hole in a rectangle overlay with HW acceleration enabled on View在 View 上启用 HW 加速的矩形叠加层中打孔
【发布时间】:2012-11-11 18:53:33
【问题描述】:

我有一个可以做一些基本绘图的视图。在此之后,我想绘制一个带有打孔的矩形,这样只有前一个绘图的一个区域是可见的。而且我想通过为我的视图启用硬件加速来实现这一点,以获得最佳性能。

目前我有两种方法可以使用,但只有在禁用硬件加速时才有效,另一种方法太慢。

方法一: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:硬件加速(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:硬件加速(速度快,在某些设备上不起作用)

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

与硬件加速兼容方法相同,但不需要额外的画布。但这有一个主要问题——它可以强制使用 SW 渲染,但在 HTC One X(Android 4.0.4——可能还有其他一些设备)上,至少在启用了硬件渲染的情况下,它会使圆圈完全变黑。这可能与22361有关。

方法 4:硬件加速(可接受,适用于所有设备)

根据 Jan 对改进方法 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。

问题

如何以与硬件加速兼容的方式实现相同的效果(并且在设备上始终如一地工作)?提高 SW 渲染性能的方法也是可以接受的。

注意:当移动圆圈时,我只是在使一个区域无效——而不是整个画布——所以那里没有性能改进的空间。

【问题讨论】:

  • 只是缓存Bitmap bCanvas c 可能吗?会有帮助吗?
  • 我不这么认为——如果你只是让bc 说在onSizeChanged 而不是onDraw 那么你最终会在同一个画布上多次绘制没有清除(所以你最终会得到多层)。实际上,如果我可以清除 onDraw 中的 b,这很有可能——我会试试的,谢谢!
  • @Jan:谢谢这确实有效——我在onSizeChanged 中创建了位图b 和画布c,然后我只需要在进行任何绘图之前调用b.eraseColor(Color.Transparent) 以擦除任何之前在c 上的绘图。发表您的评论作为答案,如果我在第二天左右没有得到任何有用的答案,我会将其标记为正确。它仍然不如方法 3 流畅,但比方法 2 好很多。
  • c.drawColor不是有重置画布的作用,让b.eraseColor变得多余吗?
  • @Jan 如果用c.drawColor 绘制的颜色不是完全不透明的,这比没有这个视图的用例更有可能;)方法4我的问题是基于你的回答。

标签: android graphics drawing hardware-acceleration porter-duff


【解决方案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);

【讨论】:

  • 有点晚了,不过我确实有一个问题。我想做同样的事情(矩形孔),但是我的问题是我的视图需要占据整个屏幕,因此创建的位图将具有大约 4 MB - 10 MB 的屏幕大小,具体取决于关于屏幕的大小。创建如此大的位图可能会导致 OutOfMemoryError。有什么解决方法吗?
  • @MScott 我无法想象 10 MB 对今天的智能手机来说是什么挑战。不要忘记,GPU 无论如何都必须拥有这么多的内存,以便它可以将它提供给 LCD。两次,最有可能 - 一个副本显示给用户,另一个同时由软件更新。您可以摆弄绘制四个矩形,每边一个,但如果它们是半透明的并且只能产生矩形孔,则抗锯齿​​效果不佳。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多