安卓TextureView /绘图/绘画表现TextureView

2023-09-06 06:05:37 作者:旺仔牛逼糖

我试图让使用 TextureView Android上的绘图/绘画应用程序。我想支持高达4096×4096像素的绘图表面,这似乎是合理的,我的最低目标设备(我用的测试),这是一个谷歌Nexus 7 2013里面有一个漂亮的四核CPU和2GB内存。

I am attempting to make a drawing/painting app using TextureView on Android. I want to support a drawing surface of up to 4096x4096 pixels, which seems reasonable for my minimum target device (which I use for testing) which is a Google Nexus 7 2013 which has a nice quad core CPU and 2GB memory.

我的一个要求是,我认为必须有一个观点,即允许其进行放大,缩小和平移,这是所有的自定义code我已经写了(想想的UIScrollView从IOS)内。

One of my requirements is that my view must be inside of a view that allows it to be zoomed in and out and panned, which is all custom code I have written (think UIScrollView from iOS).

我使用的是普通视图(未 TextureView )与OnDraw的尝试和性能是绝对可怕的 - 不到1帧中的第二。这事就算我叫的Invalidate(矩形)只改变了RECT。我试着关闭硬件加速的观点,但没有呈现,我想是因为为4096x4096过大的软件。

I've tried using a regular View (not TextureView) with OnDraw and performance was absolutely horrible - less than 1 frame a second. This happened even if I called Invalidate(rect) with only the rect that changed. I tried turning off hardware acceleration for the view, but nothing rendered, I assume because 4096x4096 is too big for software.

然后我试着使用 TextureView 和性能是一个好一点的 - 大约每秒5-10帧(静得可怕,只有更好)。用户绘制成位图,然后将其购买吸入使用后台线程的质感。我使用Xamarin但希望code有意义的Java开发人员。

I then tried using TextureView and performance is a little better - about 5-10 frames per second (still terrible but better). The user draws into a bitmap, which is then later drawn into the texture using a background thread. I'm using Xamarin but hopefully the code makes sense to Java people.

private void RunUpdateThread()
{
    try
    {
        TimeSpan sleep = TimeSpan.FromSeconds(1.0f / 60.0f);
        while (true)
        {
            lock (dirtyRect)
            {
                if (dirtyRect.Width() > 0 && dirtyRect.Height() > 0)
                {
                    Canvas c = LockCanvas(dirtyRect);
                    if (c != null)
                    {
                        c.DrawBitmap(bitmap, dirtyRect, dirtyRect, bufferPaint);
                        dirtyRect.Set(0, 0, 0, 0);
                        UnlockCanvasAndPost(c);
                    }
                }
            }
            Thread.Sleep(sleep);
        }
    }
    catch
    {
    }
}

如果我改变lockCanvas为NULL传递,而不是一个矩形,业绩在60 fps的是伟大的,但 TextureView 闪烁的内容,并出现了问题,这是令人失望的。我本来以为会简单地用一个OpenGL帧缓存/渲染纹理底下,或者至少有一个选项,以preserve内容。

If I change lockCanvas to pass null instead of a rect, performance is great at 60 fps, but the contents of the TextureView flicker and get corrupted, which is disappointing. I would have thought it would simply be using an OpenGL frame buffer / render texture underneath or at least have an option to preserve contents.

是否有短路尽一切生OpenGL在机器人的高性能绘画在表面上的任何其他选项就是平局调用之间pserved在$ P $?

Are there any other options short of doing everything in raw OpenGL in Android for high performance drawing and painting on a surface that is preserved in between draw calls?

推荐答案

我在一个4x4的网格结束了平铺TextureView。根据不同的脏矩形的每一帧,我刷新相应TextureView意见。未更新的框架我称之为无效的任何观点。

I ended up tiling TextureView in a 4x4 grid. Depending on the dirty rect each frame, I refresh the appropriate TextureView views. Any view that is not updated that frame I call Invalidate on.

某些设备,如摩托3G手机有其中双缓冲损坏一帧的问题。您可以修复通过调用lockCanvas两次当父视图都有它的onLayout调用。

Some devices, such as the Moto G phone have an issue where the double buffering is corrupted for one frame. You can fix that by calling lockCanvas twice when the parent view has it's onLayout called.

private void InvalidateRect(int l, int t, int r, int b)
{
    dirtyRect.Set(l, t, r, b);

    foreach (DrawSubView v in drawViews)
    {
        if (Rect.Intersects(dirtyRect, v.Clip))
        {
            v.RedrawAsync();
        }
        else
        {
            v.Invalidate();
        }
    }

    Invalidate();
}

protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
    for (int i = 0; i < ChildCount; i++)
    {
        View v = GetChildAt(i);
        v.Layout(v.Left, v.Top, v.Right, v.Bottom);
        DrawSubView sv = v as DrawSubView;
        if (sv != null)
        {
            sv.RedrawAsync();

            // why are we re-drawing you ask? because of double buffering bugs in Android :)
            PostDelayed(() => sv.RedrawAsync(), 50);
        }
    }
}