在Android中创建位图之前,从Bytebuffer中翻转openGL纹理



我使用的是一个依赖于谷歌Grafika回购的直播API。我使用Grafika EGLSurfaceBase的saveFrame方法,允许用户在流式传输时捕捉视频的剧照。

https://github.com/google/grafika/blob/master/src/com/android/grafika/gles/EglSurfaceBase.java

实际的拍摄是有效的,但很明显,在某些相机方向上,图像是翻转的。

我发现了很多与从OpenGL纹理中获取的倒置位图有关的问题,但大多数问题似乎都是指绘制的图像,并依赖于其中之一:

a) 在OpenG中翻转纹理。但就我而言,我正在使用直播API,因此翻转纹理以捕捉图像实际上可能会翻转视频流上的图像捕捉。

b) 在基于资源生成位图之后翻转位图。在我的情况下,我没有资源,我从字节缓冲区创建位图,我宁愿不复制它来翻转它

这是API的基本EGLSurfaceBase方法-我将把相机方向传给它,但我的问题是:

        String filename = file.toString();
    int width = getWidth();
    int height = getHeight();
    ByteBuffer buf = ByteBuffer.allocateDirect(width * height * 4);
    buf.order(ByteOrder.LITTLE_ENDIAN);
    GLES20.glReadPixels(0, 0, width, height,
            GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buf);
    GlUtil.checkGlError("glReadPixels");
    buf.rewind();
    BufferedOutputStream bos = null;
    try {
        bos = new BufferedOutputStream(new FileOutputStream(filename));
        Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        bmp.copyPixelsFromBuffer(buf);
        bmp.compress(Bitmap.CompressFormat.PNG, 90, bos);
        bmp.recycle();
    } finally {
        if (bos != null) bos.close();
    }
    Log.d(TAG, "Saved " + width + "x" + height + " frame as '" + filename + "'");
}

我的首选解决方案是在BMP创建位图之前(或同时)找到翻转图像的方法。例如,我可以使用矩阵来翻转glReadPixels对像素的读取吗?

另一个注意事项/想法:创建后翻转位图的成本可能很小,因为这依赖于用户交互,所以这种情况不会经常发生,从而导致内存错误

您可以在glReadPixels之后反转ByteBuffer。它非常快,因为它只是内存复制。我的测试表明,反向操作只需不到10ms。

这是一个可以实现的:

    private void reverseBuf(ByteBuffer buf, int width, int height)
{
    long ts = System.currentTimeMillis();
    int i = 0;
    byte[] tmp = new byte[width * 4];
    while (i++ < height / 2)
    {
        buf.get(tmp);
        System.arraycopy(buf.array(), buf.limit() - buf.position(), buf.array(), buf.position() - width * 4, width * 4);
        System.arraycopy(tmp, 0, buf.array(), buf.limit() - buf.position(), width * 4);
    }
    buf.rewind();
    Log.d(TAG, "reverseBuf took " + (System.currentTimeMillis() - ts) + "ms");
}

使用读取像素时,图像似乎总是翻转的,因为openGL呈现的缓冲区中的第一个像素位于左下角。有两种方法可以得到正确的顺序。

一种是将其倒置绘制到缓冲区,这可以在单独的缓冲区上完成,并且不会干扰当前的绘制管道。这可能是一个非常好的主意,特别是如果你想在一个单独的线程上做这件事,或者调整图像的大小。这一切都可以在一次绘图调用中完成。

另一种是手动翻转数据,这并不像看上去那么糟糕,因为只需要翻转行(列会很糟糕)。不管怎样,你实际上可以在同一个缓冲区上这样做,你不需要缓冲区的副本。只需按顺序交换行,例如:保存第一行,用最后一行替换,用保存的行替换最后一行。。。然后继续第二行。

最新更新