본문 바로가기

안드로이드

Android NDK Bitmap에 대한 Description

NDK를 이용한 네이티브 코드 사용에는 다음과 같은 구조를 가진다. 예를 들어,

public native boolean nativeString(Bitmap bitmap);

 

우선 이렇게 간단한 메소드를 별도로 구현했다. 이 메소드가 속한 클래스는 안드로이드 스튜디오의 [New Module>Android Library]를 이용해서 새로운 모듈로 만들어 별도로 만들어 두었다.

전문은 다음과 같다

package com.example.ndklib;

import android.graphics.Bitmap;

public class NativeCode {
    static {
        System.loadLibrary("ndklib");
    }
        public native boolean nativeString(Bitmap bitmap);
}

여기서 ndklib가 이 모듈의 이름, NativeCode가 자바 클래스 파일 이름이다.

 

해당 클래스를 사용하기 위해 Android.mk 파일은 다음과 같다.

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := ndklib

FILES := NativeCode.cpp

LOCAL_SRC_FILES := $(FILES)

LOCAL_LDLIBS := -llog -ljnigraphics

include $(BUILD_SHARED_LIBRARY)

위에서도 볼 수 있듯이, 그래픽 처리를 이용하므로, 로드 라이브러리에 –ljnigraphics를 포함시킨 것을 알 수 있다.

 

해당 되는 함수의 구현은 C++코드로 구현한다.

#include "com_example_ndklib_NativeCode.h"
#include <android/log.h>
#include <android/bitmap.h>
#include 

#define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG, "Native",__VA_ARGS__)
#define RED 0xff0000
#define GREEN 0xff00
#define BLUE 0xff

extern "C"
JNIEXPORT jboolean JNICALL
Java_com_example_ndklib_NativeCode_nativeString(JNIEnv *env, jobject thiz, jobject bmp) {

    jboolean check = JNI_FALSE;
    AndroidBitmapInfo info;
    uint8_t *pBmp = NULL;

    int bmpData = AndroidBitmap_getInfo(env, bmp, &info);

    if (bmpData != ANDROID_BITMAP_RESULT_SUCCESS) {
        LOGD("get Bitmap Info failure");
        return check;
    }

    bmpData = AndroidBitmap_lockPixels(env, bmp, (void **) &pBmp);
    if (bmpData != ANDROID_BITMAP_RESULT_SUCCESS) {
        LOGD("lockPixels failure");
        return check;
    }
    uint8_t *checkPixel =
            pBmp + (info.height / 2) * info.stride + (info.width / 2)*4;

    int R = checkPixel[0];
    R = R << 16;
    int G = checkPixel[1];
    G = G << 8;
    int B = checkPixel[2];
    int RGB =R+G+B;

.

.

.


    pBmp = NULL;
    AndroidBitmap_unlockPixels(env, bmp);


    return check;

}

 

코드를 하나씩 살펴보도록 하자.

int bmpData = AndroidBitmap_getInfo(env, bmp, &info);

int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap, AndroidBitmapInfo* info);

함수를 이용해서 Bitmapinfo를 호출하였다. JavaBitmapObject로 넘어오고, 이를 인자로 활용한다. 이때 리턴값은 성공 시0, 실패 시 음수이다.

/** AndroidBitmap functions result code. */
enum {
/** Operation was successful. */
ANDROID_BITMAP_RESULT_SUCCESS = 0,
/** Bad parameter. */
ANDROID_BITMAP_RESULT_BAD_PARAMETER = -1,
/** JNI exception occured. */
ANDROID_BITMAP_RESULT_JNI_EXCEPTION = -2,
/** Allocation failed. */
ANDROID_BITMAP_RESULT_ALLOCATION_FAILED = -3,
};

정확한 리턴값은 위와 같은데, 여기서 이 값들이 각각 문자열로 define 되어있으므로, 가독성을 위해 이를 사용한다.

if (bmpData != ANDROID_BITMAP_RESULT_SUCCESS) {
LOGD("get Bitmap Info failure");
return check;
}

성공하지 않으면 로그를 띄우고 리턴.

성공했다면 이 bitmap의 호출이 성공한 것이므로 Argument로 쓴 AndroidBitmapInfo의 구조가 다음과 같게 된다.

typedef struct {
/** The bitmap width in pixels. */
uint32_t width;
/** The bitmap height in pixels. */
uint32_t height;
/** The number of byte per row. */
uint32_t stride;
/** The bitmap pixel format. See {@link AndroidBitmapFormat} */
int32_t format;
/** Unused. */
uint32_t flags; // 0 for now
} AndroidBitmapInfo;

 

이후 픽셀데이터의 포인터를 가져오는 함수인

int AndroidBitmap_lockPixels(JNIEnv* env, jobject jbitmap, void** addrPtr);

이것을 이용해 작업을 하고, 이를 자바에서 다시 쓸 수 있도록 해제함수인

int AndroidBitmap_unlockPixels(JNIEnv* env, jobject jbitmap);

이 함수를 이용해서 작업을 끝내면 된다. 전체 함수의 구조는 이런 식으로 이루어진다.

int AndroidBitmap_lockPixels(JNIEnv* env, jobject jbitmap, void** addrPtr);

AndroidBitmap_lockPixels은 호출에 성공하면 addrPtr에 픽셀 주소가 set된다.

AndroidBitmap_getInfoAndroidBitmap_lockPixels의 호출이 끝나면 이제, 각 픽셀에 접근할 수 있다.

AndroidBitmap_lockPixelsaddrPtr 부분에 (void**)&pBmp를 사용했으므로, pBmp에는 픽셀 주소가 위치할 것이다. 구조는 다음과 같다.

Pixel

(0,0)

(0,1)

(0,2)

uint8_t

R

G

B

A

R

G

B

A

R

G

B

A

*R=Red, G=Green, B=Blue, A=Alpha(투명도)

 

특정 픽셀에 접근하기 위해 다음과 같은 형태로 uint8_t의 포인터 변수를 선언하고 이 변수가 특정 픽셀을 가리키도록 만들면 된다.

우리가 찾고 싶은 좌표가 (X, Y) 일, 해당 픽셀의 R값의 포인터는

pBmp + (Y) * info.stride + (X) * 4;

여기서 info.stride는 열 당 바이트 개수이다. 폰을 실행했을 때 해상도가 1080X2340라면,

info.stride 1080*4,  4320이다.

uint8_t *checkPixel = pBmp + (X) * info.stride + (Y)*4;

이제 원하는 픽셀 위치를 checkPixel이 가리키고 있으니, 하고 싶은 연산을 하고 마지막으로 unlockPixels을 호출해주면 된다.

AndroidBitmap_unlockPixels(env, bmp);

Object인 bmp는 자바에서 할 연산이 남아있으므로 lock을 풀고, 리턴을 하면 된다.

'안드로이드' 카테고리의 다른 글

Android SDK 설정 이후 사용예시  (0) 2020.04.14
Android NDK 사용법  (0) 2020.04.14