【问题标题】:Can I create a Bitmap that uses malloced buffer I created in native code?我可以创建一个使用我在本机代码中创建的 malloced 缓冲区的位图吗?
【发布时间】:2011-07-08 23:01:03
【问题描述】:

我有一段本地代码正在分配(即分配)缓冲区。我喜欢使用 Canvas 绘制操作来绘制这个内存。 但 Canvas 代码使用 Bitmap 作为其支持平面。我想知道是否有办法用 Android Bitmap 包装原生内存块。

谢谢
视频人

【问题讨论】:

    标签: android bitmap native-code


    【解决方案1】:

    您可以从 JAVA 传递一个 Buffer,将其填充到 Native 代码中,然后使用 Canvas 进行渲染。完成,完美运行。

    编辑添加示例:

    警告,Java 提前膨胀

    /*
     * Copyright (C) 2009 The Android Open Source Project
     */
    package com.example.hellojni;
    
    import android.app.Activity;
    import android.widget.TextView;
    import android.os.Bundle;
    
    import android.app.AlertDialog;
    import android.content.DialogInterface;
    import android.widget.Toast;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.view.MotionEvent;
    import android.view.KeyEvent;
    import android.view.SurfaceHolder;
    import android.view.SurfaceView;
    import android.view.Window;
    
    import android.view.WindowManager;
    import android.graphics.PixelFormat;
    
    import java.nio.ByteBuffer;
    
    public class HelloJni extends Activity
    {
    
        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            requestWindowFeature(Window.FEATURE_NO_TITLE);
    
            setContentView(new Panel(this));
        }
    
        public void onDestroy() {
            super.onDestroy();
            myEngineDestroy();
        }
    
        class Panel extends SurfaceView implements SurfaceHolder.Callback {
        Bitmap renderbmp = null;
        Paint paint = null;
    
            public Panel(Context context) {
                super(context);
                getHolder().addCallback(this);
                getHolder().setFormat(PixelFormat.RGB_565);
                setFocusable(true);
                setFocusableInTouchMode(true);
            }
    
          @Override 
          public boolean onKeyDown(int i, KeyEvent event) {
          }
    
            @Override
            public boolean onTouchEvent(MotionEvent event) {
    
                if ((event.getAction() == MotionEvent.ACTION_DOWN) || 
                (event.getAction() == MotionEvent.ACTION_MOVE) )
            {
                if( myEngineMouseInput( (int) event.getX(), (int) event.getY(), 0 ) == 1 )
                drawFrame();
                return true;
                }
            return false;
            }
    
            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
                // TODO Auto-generated method stub
            }
    
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
    
            if( renderbmp == null )
            renderbmp = Bitmap.createBitmap( holder.getSurfaceFrame().right-holder.getSurfaceFrame().left, holder.getSurfaceFrame().bottom-holder.getSurfaceFrame().top,Bitmap.Config.RGB_565 );
    
            if( paint == null )
            paint = new Paint(Paint.FILTER_BITMAP_FLAG);
    
            myEngineInit( renderbmp, renderbmp.getWidth(), renderbmp.getHeight(), PixelFormat.RGB_565 );
    
            drawFrame();
            }
    
        @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
            }
    
        public void drawFrame() {
                Canvas c;
                c = getHolder().lockCanvas(null);
                c.drawBitmap(renderbmp, 0, 0, paint);
                if (c != null) getHolder().unlockCanvasAndPost(c);
        }
    
        }
    
        /* A native method that is implemented by the
         * 'hello-jni' native library, which is packaged
         * with this application.
         */
        public native void myEngineInit( Bitmap bmp, int w, int h, int pf );
        public native int  myEngineMouseInput( int x, int y, int mt );
        public native void myEngineDestroy();
    
        public native String  unimplementedStringFromJNI();
    
        static {
            System.loadLibrary("hello-jni");
        }
    }
    

    现在是 NDK 方面

    /*
     * Copyright (C) 2010 The Android Open Source Project
     */
    
    #include <android_native_app_glue.h>
    
    #include <errno.h>
    #include <jni.h>
    #include <sys/time.h>
    #include <time.h>
    #include <android/log.h>
    #include <android/bitmap.h>
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    
    #define  LOG_TAG    "myapp"
    #define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
    #define  LOGW(...)  __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
    #define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
    
    /* Set to 1 to enable debug log traces. */
    #define DEBUG 0
    
    /* Return current time in milliseconds */
    static double now_ms(void)
    {
        struct timeval tv;
        gettimeofday(&tv, NULL);
        return tv.tv_sec*1000. + tv.tv_usec/1000.;
    }
    
    jobject jbmp = NULL;
    ANativeWindow_Buffer draw_buffer = { 0 };
    
    static void cleanup_draw_buffer( JNIEnv* env )
    {
        if(jbmp) {
            AndroidBitmap_unlockPixels( env, jbmp);
        }
        draw_buffer.bits = 0;
    }
    
    static int init_draw_buffer( JNIEnv* env, jobject jbitmap, int width, int height )
    {
        int res = 0, ret;
        LOGI("init_draw_buffer");
    
        LOGI("window w:%d, h:%d, format: %d", width, height, 4 );
    
        if( draw_buffer.width != width || 
            draw_buffer.height != height )
        {   
            draw_buffer.width=width;
            draw_buffer.height=height;
            draw_buffer.stride = draw_buffer.width*2;
            res = 1;
        }
    
        jbmp = NULL;
        if ((ret = AndroidBitmap_lockPixels(env, jbitmap, &draw_buffer.bits)) < 0) {
            LOGE("AndroidBitmap-lockPixels() failed ! error=%d", ret);
        }
        else {
            LOGI("Successfully acquired bitmap pixels: %x", draw_buffer.bits ); 
            jbmp = jbitmap;
        }
    
        return res;
    }
    
    /* simple stats management */
    typedef struct {
        double  renderTime;
        double  frameTime;
    } FrameStats;
    
    #define  MAX_FRAME_STATS  200
    #define  MAX_PERIOD_MS    1500
    
    typedef struct {
        double  firstTime;
        double  lastTime;
        double  frameTime;
    
        int         firstFrame;
        int         numFrames;
        FrameStats  frames[ MAX_FRAME_STATS ];
    } Stats;
    
    static void
    stats_init( Stats*  s )
    {
        s->lastTime = now_ms();
        s->firstTime = 0.;
        s->firstFrame = 0;
        s->numFrames  = 0;
    }
    
    static void
    stats_startFrame( Stats*  s )
    {
        s->frameTime = now_ms();
    }
    
    static void
    stats_endFrame( Stats*  s )
    {
        double now = now_ms();
        double renderTime = now - s->frameTime;
        double frameTime  = now - s->lastTime;
        int nn;
    
        if (now - s->firstTime >= MAX_PERIOD_MS) {
            if (s->numFrames > 0) {
                double minRender, maxRender, avgRender;
                double minFrame, maxFrame, avgFrame;
                int count;
    
                nn = s->firstFrame;
                minRender = maxRender = avgRender = s->frames[nn].renderTime;
                minFrame  = maxFrame  = avgFrame  = s->frames[nn].frameTime;
                for (count = s->numFrames; count > 0; count-- ) {
                    nn += 1;
                    if (nn >= MAX_FRAME_STATS)
                        nn -= MAX_FRAME_STATS;
                    double render = s->frames[nn].renderTime;
                    if (render < minRender) minRender = render;
                    if (render > maxRender) maxRender = render;
                    double frame = s->frames[nn].frameTime;
                    if (frame < minFrame) minFrame = frame;
                    if (frame > maxFrame) maxFrame = frame;
                    avgRender += render;
                    avgFrame  += frame;
                }
                avgRender /= s->numFrames;
                avgFrame  /= s->numFrames;
    
                LOGI("frame/s (avg,min,max) = (%.1f,%.1f,%.1f) "
                     "render time ms (avg,min,max) = (%.1f,%.1f,%.1f)\n",
                     1000./avgFrame, 1000./maxFrame, 1000./minFrame,
                     avgRender, minRender, maxRender);
            }
            s->numFrames  = 0;
            s->firstFrame = 0;
            s->firstTime  = now;
        }
    
        nn = s->firstFrame + s->numFrames;
        if (nn >= MAX_FRAME_STATS)
            nn -= MAX_FRAME_STATS;
    
        s->frames[nn].renderTime = renderTime;
        s->frames[nn].frameTime  = frameTime;
    
        if (s->numFrames < MAX_FRAME_STATS) {
            s->numFrames += 1;
        } else {
            s->firstFrame += 1;
            if (s->firstFrame >= MAX_FRAME_STATS)
                s->firstFrame -= MAX_FRAME_STATS;
        }
    
        s->lastTime = now;
    }
    
    // ----------------------------------------------------------------------
    
    struct engine {
        struct android_app* app;
    
        Stats stats;
    
        int animating;
    };
    
    
    void
    Java_com_example_hellojni_HelloJni_myEngineDestroy( JNIEnv* env,
                                                      jobject thiz )
    {
        LOGI("Java_com_example_hellojni_HelloJni_myEngineDestroy");
        cleanup_draw_buffer(env);
    }
    
    void
    Java_com_example_hellojni_HelloJni_myEngineInit( JNIEnv* env,
    jobject thiz, jobject jbitmap, int w, int h, int pf )
    {
        LOGI("Java_com_example_hellojni_HelloJni_myEngineInit");    
        init_draw_buffer( env, jbitmap, w, h );
    }
    
    jint
    Java_com_example_hellojni_HelloJni_myEngineMouseInput( JNIEnv* env,
    jobject thiz, int x, int y, int mt )
    {
        if( menuvisible )
    //    LOGI("Java_com_example_hellojni_HelloJni_myEngineMouseInput, x:%d y:%d mt:%d", x, y, mt);
        return do_the_drawing_stuff(params_ommited); //drawing buffer is unsigned char *dest = draw_buffer.bits;
        else
        return 0;
    }
    

    所以上面几行调用了主要的渲染部分,如上所述,您的缓冲区位于 draw_buffer 结构中。

    请检查缓冲区技术的传递,并通过 JNI 接口跟踪其从 JAVA 到 C 的路径。我小心翼翼地在没有任何转换的情况下进行最终的位图渲染操作 - 最快的方法 IMO。

    您可以省略帧计数部分,因为我以某种示例为基础,只是想亲身体验我得到的新设备 :)

    【讨论】:

    • @Ulterior..感谢您提供详细示例!我正在寻找一种将位图使用的内部缓冲区指针设置为我提出的指针的方法(mmap()的结果)。 Bitmap_unlockPixels() 给出了它对我没用的指针。我仍然想从 Java 渲染。
    • 很好的例子,正是我想要的。谢谢!
    【解决方案2】:

    您可以做的是通过 JNI 将缓冲区传递到 java 并从中创建一个位图。见this page。然后您可以使用Canvas.setBitmap 使其绘制到缓冲区中。

    【讨论】:

    • 在画布上绘图不是一朝一夕的事。这是从计时器上的后台线程完成的。我不打算 malloc 内存,而是将内存映射到我的进程并将其用于渲染。当我的绘图代码不断更改它时,不同的进程也可以进行内存映射并访问缓冲区。我不太明白如何将缓冲区指针从 JNI 传递到 Java,以便位图对象使用它。
    【解决方案3】:

    默认位图use native memory

    【讨论】:

      猜你喜欢
      • 2014-07-27
      • 2021-06-23
      • 2021-10-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-08
      • 1970-01-01
      相关资源
      最近更新 更多