본문 바로가기

프로그래밍/Android

[Android] EGL : 2 - 텍스처

텍스처 생성 : 버퍼

int createImageTexture( ByteBuffer data, int width, int height, int format ) {
    // 텍스처 생성
    int[] textures = new int[1];
    GLES20.glGenTextures( 1, textures, 0);
    
    // 여러개의 텍스처를 사용하는 경우 특정 texture unit 활성화
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    
    // 바인딩
    int texId = textures[0];
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId);
    
    // 필터링 설정
    GLES20.glTexParameteri( GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
    GLES20.glTexParameteri( GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
    
    // 텍스처에 데이터 연결
    GLES20.glTexImage2D( GLES20.GL_TEXTURE_2D, 0, format, width, height, 0, format, GLES20.GL_UNSIGNED_BYTE, data);
    
    return texId;
}

 

텍스처 생성 : 안드로이드 Bitmap

int createImageTexture( Bitmap bitmap ) {
    // 텍스처 생성
    int[] textures = new int[1];
    GLES20.glGenTextures( 1, textures, 0);
    
    // 여러개의 텍스처를 사용하는 경우 특정 texture unit 활성화
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    
    // 바인딩
    int texId = textures[0];
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId);
    
    // 필터링 설정
    GLES20.glTexParameteri( GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
    GLES20.glTexParameteri( GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
    
    // 텍스처에 데이터 연결
    GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
    
    return texId;
}

 

텍스처 생성 : 안드로이드 리소스

int createImageTexture( int resID ) {
    // 텍스처 생성
    int[] textures = new int[1];
    GLES20.glGenTextures( 1, textures, 0);
    
    // 여러개의 텍스처를 사용하는 경우 특정 texture unit 활성화
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    
    // 바인딩
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
    
    // 필터링 설정
    GLES20.glTexParameteri( GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
    GLES20.glTexParameteri( GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
    
    Bitmap bitmap = BitmapFactory.decodeStream( mContext.getResources().openRawResource( resId ));
    
    // 텍스처에 데이터 연결
    GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
    
    bitmap.recycle();
    
    return texId;
}

 

텍스처 핸들 : external

안드로이드 Camear2(api 21), MediaCodec, MediaPlayer, Allocation 은 별도 하드웨어 버퍼와 이미지 포맷을 사용하게 되는데, 이 경우 바인딩하는 텍스처 형식이 GL_TEXTURE_EXTERNAL_OES 로 변경된다. 이미지가 아닌 위 api등에서 데이터를 전달받기 위한 텍스처의 경우 해당 텍스처 핸들을 정의 하고, SurfaceTexture에 해당 핸들을 넘겨 주게 된다.
SurfaceTexture 내부에서 EGLImageKHR(GLeglImageOES) 를 생성하고,  바인딩하는 루틴이 추가되어 있으므로 별도로 데이터버퍼를 처리할 필요는 없다.

int createTextureObject() {
    int[] textures = new int[1];
    GLES20.glGenTextures( 1, textures, 0 );
    
    // 여러개의 텍스처를 사용하는 경우 특정 texture unit 활성화
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    
    // 바인딩
    int texId = textures[0];
    GLES20.glBindTexture( GLES20.GL_TEXTURE_EXTERNAL_OES, texId );
    
    
    GLES20.glTexParameterf( GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
    GLES20.glTexParameterf( GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
    GLES20.glTexParameteri( GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
    GLES20.glTexParameteri( GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
    return texId;
}

 

 

 

 

여러개의 텍스처를 사용하는 경우

int[] textureHandles = new int[TEXTURE_COUNT];
int target = oes? GLES11Ext.GL_TEXTURE_EXTERNAL_OES:GLES20.GL_TEXTURE_2D;

GLES20.glGenTextures(TEXTURE_COUNT, textureHandles, 0);

// 이미지의 경우 별도의 ByteBuffer 배열에 로드
int index=0;
for( int texture : textureHandles ) {
    GLES20.glBindTexture(target, texture);
    GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
    GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
    GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
    GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
    if( !oes ) {
    	GLES20.glTexImage2D( GLES20.GL_TEXTURE_2D, 
                             0, 
                             GLES20.GL_RGBA, 
                             너비, 
                             높이, 
                             0, 
                             GLES20.GL_RGBA, 
                             GLES20.GL_UNSIGNED_BYTE, 
                             buffer[index++]);
    }
}

 

 

텍스처를 버퍼에 저장

glReadPixels는 RGB 관련 형식만 지원하므로, 바인딩된 텍스처 타입이 GL_TEXTURE_EXTERNAL_OES의 경우 읽어올 수 없다.

int width = getSurfcaeWidth();
int height = getSurfaceHeight();
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);
buf.rewind();

Bitmap bmp = Bitmap.createBitmap( width, height, Bitmap.Config.ARGB_8888 );
bmp.copyPixelsFromBuffer(buf);
bmp.recycle();

 

 

// 텍스처 포맷 GLES20.GL_RGBA, GLES20.GL_RGB
int bytes = 4; // GL_RGBA
int stride = (width * (4-bytes)) %4;
int bytesPerLine = width * bytes + stride;
int size = height * bytesPerLine;

ByteBuffer buf = ByteBuffer.allocateDirect( size );
buf.order(ByteOrder.LITTLE_ENDIAN);

GLES20.glGetTexImage( GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buf);

 

 


extern oes

 

 

EGLSurface eglPixmap = eglCreatePixmapSurface( dpy,  config, pixmap, eglPixmap_attrib_list);
EGLImage eglImage = eglCreateImageKHR( dpy, ctx, EGL_NATIVE_PIXMAP_KHR,(EGLClientBuffer)eglPixmap, eglImage_attrib_list);

 

SurfaceTexture

err = mBufferQueue->acquireBuffer(&item);
if (err == NO_ERROR) {
    int buf = item.mBuf;
    // This buffer was newly allocated, so we need to clean up on our side
    if (item.mGraphicBuffer != NULL) {
        mEGLSlots[buf].mGraphicBuffer = 0;
        
        if (mEGLSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) {
            eglDestroyImageKHR(dpy, mEGLSlots[buf].mEglImage);
            mEGLSlots[buf].mEglImage = EGL_NO_IMAGE_KHR;
        }
        mEGLSlots[buf].mGraphicBuffer = item.mGraphicBuffer;
    }
    
    // we call the rejecter here, in case the caller has a reason to
    // not accept this buffer. this is used by SurfaceFlinger to
    // reject buffers which have the wrong size
    if (rejecter && rejecter->reject(mEGLSlots[buf].mGraphicBuffer, item)) {
        mBufferQueue->releaseBuffer(buf, dpy, mEGLSlots[buf].mFence);
        mEGLSlots[buf].mFence = EGL_NO_SYNC_KHR;
        glBindTexture(mTexTarget, mTexName);
        return NO_ERROR;
    }


    EGLImageKHR image = mEGLSlots[buf].mEglImage;
    if (image == EGL_NO_IMAGE_KHR) {
        if (mEGLSlots[buf].mGraphicBuffer == NULL) {
            ST_LOGE("updateTexImage: buffer at slot %d is null", buf);
            err = BAD_VALUE;
        } else {
            image = createImage(dpy, mEGLSlots[buf].mGraphicBuffer);
            mEGLSlots[buf].mEglImage = image;

            if (image == EGL_NO_IMAGE_KHR) {
                // NOTE: if dpy was invalid, createImage() is guaranteed to
                // fail. so we'd end up here.
                err = UNKNOWN_ERROR;
            }
        }
    }

    if (err == NO_ERROR) {
        GLint error;
        while ((error = glGetError()) != GL_NO_ERROR) {
            ST_LOGW("updateTexImage: clearing GL error: %#04x", error);
        }

        glBindTexture(mTexTarget, mTexName);
        glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);

        while ((error = glGetError()) != GL_NO_ERROR) {
            ST_LOGE("updateTexImage: error binding external texture image %p ""(slot %d): %#04x", image, buf, error);
            err = UNKNOWN_ERROR;
        }

        if (err == NO_ERROR) {
             err = syncForReleaseLocked(dpy);
        }
    }
    
    if (err != NO_ERROR) {
            // Release the buffer we just acquired.  It's not safe to
            // release the old buffer, so instead we just drop the new frame.
            mBufferQueue->releaseBuffer(buf, dpy, mEGLSlots[buf].mFence);
            mEGLSlots[buf].mFence = EGL_NO_SYNC_KHR;
            return err;
        }
        ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)",
                mCurrentTexture,
                mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0,
                buf, item.mGraphicBuffer != NULL ? item.mGraphicBuffer->handle : 0);
        // release old buffer
        if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
            status_t status = mBufferQueue->releaseBuffer(mCurrentTexture, dpy,
                    mEGLSlots[mCurrentTexture].mFence);
            mEGLSlots[mCurrentTexture].mFence = EGL_NO_SYNC_KHR;
            if (status == BufferQueue::STALE_BUFFER_SLOT) {
                freeBufferLocked(mCurrentTexture);
            } else if (status != NO_ERROR) {
                ST_LOGE("updateTexImage: released invalid buffer");
                err = status;
            }
        }
        // Update the SurfaceTexture state.
        mCurrentTexture = buf;
        mCurrentTextureBuf = mEGLSlots[buf].mGraphicBuffer;
        mCurrentCrop = item.mCrop;
        mCurrentTransform = item.mTransform;
        mCurrentScalingMode = item.mScalingMode;
        mCurrentTimestamp = item.mTimestamp;
        computeCurrentTransformMatrix();
}



 

EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy,
        const sp<GraphicBuffer>& graphicBuffer) {
    EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
    EGLint attrs[] = {
        EGL_IMAGE_PRESERVED_KHR,    EGL_TRUE,
        EGL_NONE,
    };
    EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT,
            EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs);
    if (image == EGL_NO_IMAGE_KHR) {
        EGLint error = eglGetError();
        ST_LOGE("error creating EGLImage: %#x", error);
    }
    return image;
}

 

eglGetNativeClientBufferANDROID

EGLClientBuffer

eglCreateImageKHR

elEGLImageTargetTexture2DOES

 

#define EGL_NATIVE_BUFFER_ANDROID 0x3140
#define EGL_IMAGE_PRESERVED_KHR   0x30D2

GraphicBuffer* buffer = new GraphicBuffer(1024, 1024, PIXEL_FORMAT_RGB_565,
                                          GraphicBuffer::USAGE_SW_WRITE_OFTEN |
                                          GraphicBuffer::USAGE_HW_TEXTURE);

unsigned char* bits = NULL;
buffer->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN, (void**)&bits);

// Write bitmap data into 'bits' here

buffer->unlock();

// Create the EGLImageKHR from the native buffer
EGLint eglImgAttrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE, EGL_NONE };
EGLImageKHR img = eglCreateImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), EGL_NO_CONTEXT,
                                    EGL_NATIVE_BUFFER_ANDROID,
                                    (EGLClientBuffer)buffer->getNativeBuffer(),
                                    eglImgAttrs);

// Create GL texture, bind to GL_TEXTURE_2D, etc.

// Attach the EGLImage to whatever texture is bound to GL_TEXTURE_2D
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, img);