텍스처 생성 : 버퍼
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);
'프로그래밍 > Android' 카테고리의 다른 글
안드로이드 Command Line Tools 설치 (0) | 2024.01.29 |
---|---|
[Android] EGL : 4 - 안드로이드 렌더링 (2) | 2020.03.02 |
[Android] EGL : 3 - 쉐이더 (0) | 2020.03.02 |
[Android] EGL : 1 - 기본 구성 (0) | 2020.03.02 |
안드로이드 ndk 테스트 코드 (0) | 2020.02.20 |
[Android] gson , 복합적인 요소의 파싱 (0) | 2019.09.23 |
[Android] gradle android 빌드 구성 (0) | 2019.09.17 |
[Android] ConstraintSet (0) | 2019.08.22 |
Gradle Kotlin, AndroidX 설정 (0) | 2019.08.17 |
[Android] androidX Camera (0) | 2019.07.15 |