我可以创建一个使用我在本机代码中创建的 malloced 缓冲区的位图吗?

发布于 2024-11-19 10:38:45 字数 139 浏览 4 评论 0原文

我有一段本机代码,我正在其中分配(即分配)缓冲区。我喜欢使用 Canvas 绘制操作绘制到该内存中。 但 Canvas 代码使用 Bitmap 作为其支持平面。我想知道是否有一种方法可以用 Android 位图包装本机内存块。

谢谢
视频人

I have a piece of native code where I am mallocing (i.e. allocating) a buffer. I like to draw into this memory using Canvas draw operations.
But Canvas code uses Bitmap as its backing plane. I am wondering if there is a way to wrap native block of memory with Android Bitmap.

Thanks
Videoguy

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

雨落□心尘 2024-11-26 10:38:45

您可以从 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 最快的方法。

你可以省略帧计数部分,因为我以某种示例为基础,只是想把手放在我得到的新设备上:)

You can pass a Buffer from JAVA, fill it in Native code and then render it using Canvas. Done, works perfectly.

edited to add an example:

warning, Java bloat ahead

/*
 * 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");
    }
}

and now the NDK side

/*
 * 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;
}

So the main rendering part is called a few lines above, and as stated, your buffer is located at draw_buffer structure.

Please examine the passing of a buffer technique and trace its path from the JAVA to C via JNI interface. I took care to make a final bitmap rendering operation without any conversions - fastest approach IMO.

You could ommit that frame counting part, as I took some kind of example as a base and just wanted to lay my hands on that new device I got :)

毁我热情 2024-11-26 10:38:45

您可以做的是将缓冲区通过 JNI 传递到 java 中,并从中创建一个位图。请参阅此页面。然后你可以使用 Canvas.setBitmap 使其绘制到缓冲区中。

What you could do is pass the buffer through JNI into the java and create a bitmap from it. see this page. Then you can use Canvas.setBitmap to cause it to draw into the buffer.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文