尽快从C中填充Android位图的数据

15
我已经成功创建了一个android.graphics.Bitmap,并通过SetPixels命令成功填充它。
问题在于,我最初使用的是RGBA数据,然后创建了一个jintArray。接着,我调用了SetIntArray(实际上将数据复制到缓冲区中)。然后,最后,我调用setPixels以实际设置像素(这可能会导致另一次复制)。
这样做的一个大问题是,无论我使用R8G8B8A8还是R5G6B5或A8,我仍然必须将像素数据转换为R8G8B8A8数据。
理想情况下,我希望找到一种方法只使用一次复制就可以填充缓冲区,并且能够在不进行像素格式转换的情况下完成操作。
有没有直接访问Bitmap中包含的缓冲区数据的方法?我看到JNI中有一个GetDirectBufferAddress函数,但我能找到的文档表明它仅限于java.nio.buffer。我可以使用此函数直接访问像素数据吗?也许是通过获取Bitmap类使用的内部缓冲区的方式?​
我的唯一方法是使用全局引用的Java.nio.buffer来更新每个时间点的像素数据并使用copyPixelsFromBuffer进行复制吗?这仍然涉及2次复制,但至少可以消除像素格式更改。这比我已经使用的方法更有效吗?
是否有更好的方法?
顺便说一下,我知道我可以使用android/bitmap.h中的函数,但我真的不想失去对Android 2.1和Android 2.2的支持...
提前致谢!

2.2支持<android/bitmap.h>,所以你只会失去2.1。目前为止,我认为这不是一个很大的损失... - Mike Ortiz
直接访问位图像素在2.1(甚至1.5)上也是可能的,我使用它并测试通过,但这在NDK中有点像黑客行为。如果可以的话,请尽量使用官方的bitmap.h和AndroidBitmap_lockPixels / AndroidBitmap_unlockPixels。 - Pointer Null
1
@mice “我使用过它,测试过可以工作,但在NDK中有点像黑客行为” - 你介意发布详细信息吗? - Idolon
2个回答

4

以下是一种虽然有些粗糙但有效的解决方案,适用于Android 1.5至4.0。代码为C++。

                              //decls of some partial classes from Skia library
class SkRefCnt{
public:
   virtual ~SkRefCnt(){}
private:
   mutable int fRefCnt;
};

//----------------------------

class SkPixelRef: public SkRefCnt{
public:
   virtual class Factory getFactory() const;
   virtual void flatten(class SkFlattenableWriteBuffer&) const;
protected:
   virtual void* onLockPixels(class SkColorTable**) = 0;
   virtual void onUnlockPixels() = 0;
public:
   void *GetPixels(){
      SkColorTable *ct;
      return onLockPixels(&ct);
   }
};

jobject java_bitmap;  //your Bitmap object
jclass java_bitmap_class = env.GetObjectClass(java_bitmap);
class SkBitmap;
SkBitmap *sk_bitmap = (SkBitmap*)env.CallIntMethod(java_bitmap, env.GetMethodID(java_bitmap_class, "ni", "()I"));
SkPixelRef *sk_pix_ref;
sk_pix_ref = (SkPixelRef*)((int*)sk_bitmap)[1];
// get pointer to Bitmap's pixel memory, and lenght of single line in bytes
int buffer_pitch = env.CallIntMethod(java_bitmap, env.GetMethodID(java_bitmap_class, "getRowBytes", "()I"));
void *buffer = sk_pix_ref->GetPixels();

1
Skia库到底是什么? - Goz
Android的一部分。图形库与Java类(Canvas、Bitmap、Paint)紧密相连。因此,它始终存在于每个设备上。 - Pointer Null
酷。这个有文档记录吗? - Goz
1
Skia是Android源代码中的一部分,您可以下载并学习。Java源代码也可用。 - Pointer Null
谢谢,这个几乎是开箱即用的,节省了很多时间。 - exebook

0

据我所知,静态方法:

public static Bitmap createBitmap (int[] colors, int offset, int stride, int width, int height, Bitmap.Config config)

不涉及复制数据,而是在现有像素数组上创建(不可变的)位图结构。这样,您可以提前准备好这样的位图,然后从本地代码中操作缓冲区。这将支持不同的像素格式。使用示例在此处提供:

http://javaocr.svn.sourceforge.net/viewvc/javaocr/trunk/demos/recognizer/src/net/sf/javaocr/demos/android/recognizer/Recognizer.java?revision=239&view=markup

(第520行)


如果我的像素已经是RGBA格式,为什么还要转换成ALPHA_8格式?我的链接展示了一个Android的OCR应用程序示例 - 其中涉及相当多的Java图像处理。 - Konstantin Pribluda
但是如果位图不是RGBA怎么办呢?我不使用RGBA,因为填充位图太慢了... - Goz
然后你必须复制一下。你总是用内存来换取速度 - 我决定使用整数像素在线性数组中工作,即使我只需要1位。我浪费了很多内存,但我能够在其上定义位图而不需要复制。 - Konstantin Pribluda
我在需要渲染时创建位图。我保留我的内存缓存,并在其上创建不可变位图对象,而无需复制它。这不会创建大量对象。您可以从RGBA8数据开始,基本上它是整数数组。将其转换为其他格式不会带来任何好处。 - Konstantin Pribluda
除了速度,这个问题所涉及的就是...到目前为止,我上面解释的缓冲方法已经被证明比你提出的方法快得多。这是实时处理和无法处理所有传入数据之间的区别。 - Goz
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接