///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
출처: http://gogorchg.tistory.com/entry/Android-Opengl-es-20-LoadTexture
1.0에서는 Texture에 Bitmap 파일만 넣어주면, Android에서 알아서 바꿔주었었다.
아주 간편하게 Texture를 적용 시킬 수가 있었습니다.
(안에서 어떻게 돌아가든 관계 없어..)
하지만, 2.0에서는 모든 것을 개발자에게 맡기게 되어있죠.
구글링을 해본 결과 , 안드로이드의 Bitmap 값은 ARGB로 32bit 픽셀로 되어 있다고 합니다.
하지만, Opengl은 RGBA로 되어 있어서 컨버팅 할 필요가 생긴거죠.
그래서 2.0에서는 glTexture2D 의 매개변수가 Buffer로 되어 있는 겁니다.
결과적으로 Texture에 Bitmap을 로드 시킬 때 다음과 같은 코드 형식으로 하면 되겠네요.
private static int loadTexture(InputStream is) { int[] textureId = new int[1]; Bitmap bitmap; bitmap = BitmapFactory.decodeStream(is); ByteBuffer byteBuffer = ByteBuffer.allocateDirect(bitmap.getWidth() * bitmap.getHeight() * 4); byteBuffer.order(ByteOrder.BIG_ENDIAN); IntBuffer ib = byteBuffer.asIntBuffer(); int[] pixels = new int[bitmap.getWidth() * bitmap.getHeight()]; bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight()); for(int i=0; i<pixels.length; i++){ ib.put(pixels[i] << 8 | pixels[i] >>> 24); } bitmap.recycle(); byteBuffer.position(0); GLES20.glGenTextures ( 1, textureId, 0 ); GLES20.glBindTexture ( GLES20.GL_TEXTURE_2D, textureId[0] ); GLES20.glTexImage2D ( GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, bitmap.getWidth(), bitmap.getHeight(), 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, byteBuffer ); 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.glTexParameteri ( GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE ); GLES20.glTexParameteri ( GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE ); return textureId[0]; }
Nexus S 이상 때부터 Opengl을 사용할 때,
본래 Bitmap 이미지의 크기를 2의 배수로 지정해주지 않으면,
그 Object를 하얗게 뿌려버리더군요.
그래서 다음과 같은 함수를 만들어서 사용을 합니다.
private int getMinPowerByTwo(int value) {
int result = 2;
do {
result *= 2;
} while(result < value);
return result;
}
위 함수는 계속 2씩 곱해 나가다가 value 값 보다 커졌을 경우,
그 값을 리턴 시켜주는 거지요.
즉, 500x168 이라는 이미지가 있을 경우에는
위 함수를 통하여 우선 512 x256의 크기로 Bitmap을 리사이징 하고
Texture에 저장을 시킨 후,
Texture 크기를 500x168로 맞추면 되는 거죠^^
width = getMinPowerByTwo(bmp.getWidth());
height = getMinPowerByTwo(bmp.getHeight());
Bitmap tmpBmp = Bitmap.createScaledBitmap(bmp, width, height, true);
먼가 부분적으로 설명을 하여 이해하기가 힘들실지도 모르지만,
분명 어느정도 기초가 쌓여가면서 예제 소스들 보시고
이걸 보시면 아하 ~ 하면서 이해를 금방 하실 수 있을겁니다.^^
그럼 오늘도 즐코딩요~ㅋ
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
출처: http://gogorchg.tistory.com/entry/Android-Opengl-es-20-LiveWallpaper-%EC%97%90%EC%84%9C-%EC%82%AC%EC%9A%A9-%EC%8B%9C-onSurfaceCreated-%EB%AC%B8%EC%A0%9C
이 문제는 제가 코딩을 잘못 하거나, 샘플 소스를 잘못 이용한 걸 수도 있지만,
경험의 유추했을 때 결론이 나와. 이렇게 글을 남깁니다.
혹시 잘못된 부분이 있으면 주저 말고 댓글을 달아주시면 감사하겠습니다.^^
Opengl es 2.0을 사용하여 개발하는 중.. 화면을 껏다가 다시 켜면..
뭔가 속도가 느려지다가 결국 Out of memory 오류와 함께 팅겨져 버립니다.
역시나 Out of memory 부분은 Texture 부분이 문제 이죠.
전 onSurfaceCreated 함수 내에 Texture를 셋팅 하는 소스를 코딩하였습니다.
loadTexture ( mContext.getResources().openRawResource( R.raw.background) ,"mBgTexId" );
디버깅을 해본 결과... 예전 Opengl es 1.1에서는 화면을 갱신 해도, onSurfaceCreated 함수가 불러지지 않았었습니다.
하지만, Opengl es 2.0으로 바뀌면서 static 메모리 함수로 바뀌고 나서
GLES20와 관련된 메모리 장치들이 초기화가 되는 것 같습니다.
더 웃긴 건... 초기화가 되면서, 메모리 Texture 버퍼들이 삭제가 되지 않는 점입니다.
결국.. 프로그램 실행 시 , 한번만 실행이 되었던 onSurfaceCreated 함수내에 예외 처리가 필요했습니다.
deleteTexture(texId);
texId = loadTexture ( mContext.getResources().openRawResource( R.raw.background) ,"mBgTexId" );
private void deleteTexture(int texId){
// 전에 저장되어 있는 texture 가 있는지 확인
if(texId == 0)
return;
int idArr[];
IntBuffer intbuf;
idArr = new int[]{texId};
intbuf = getIntBufferFromIntArray(idArr);
GLES20.glDeleteTextures(1, intbuf);
}
이렇게 계속 갱신 할때마다 Texture를 불러 올 경우, 매번 이미지를 불러와야 하므로 속도가 느려집니다.
결국, TextureBuffer를 저장 시켜놓고, 갱신 할때마다 TextureBuffer를 이용하는 쪽으로 수정하였습니다.
이럴 경우, Texture 설정 시간이 조금 걸리지만 그리 느려지는 현상이 줄어듭니다.
private int loadTexture ( InputStream is ,String strKey)
{
int[] textureId = new int[1];
// 기존의 존재하는지 확인
if(texBuffer.get(strKey) == null){
TexValue value = new TexValue();
Bitmap bitmap;
bitmap = BitmapFactory.decodeStream(is);
bitmap = Bitmap.createScaledBitmap(bitmap,getMinPowerByTwo(bitmap.getWidth()), getMinPowerByTwo(bitmap.getHeight()),false);
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(bitmap.getWidth() * bitmap.getHeight() * 4);
byteBuffer.order(ByteOrder.BIG_ENDIAN);
IntBuffer ib = byteBuffer.asIntBuffer();
int bitmapWidth = bitmap.getWidth();
int bitmapHeight = bitmap.getHeight();
int[] pixels = new int[bitmapWidth * bitmapHeight];
bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
bitmap.recycle();
bitmap = null;
for(int i=0; i<pixels.length; i++){
ib.put(pixels[i] << 8 | pixels[i] >>> 24);
}
value.bmpWidth = bitmapWidth;
value.bmpHeight = bitmapHeight;
value.texBuffer = byteBuffer;
// Buffer를 저장 시켜 놓는다.
texBuffer.put(strKey,value);
}
texBuffer.get(strKey).texBuffer.position(0);
GLES20.glGenTextures ( 1, textureId, 0 );
GLES20.glBindTexture ( GLES20.GL_TEXTURE_2D, textureId[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 );
GLES20.glTexParameteri ( GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE );
GLES20.glTexParameteri ( GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE );
GLES20.glTexImage2D ( GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, texBuffer.get(strKey).bmpWidth, texBuffer.get(strKey).bmpHeight, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, texBuffer.get(strKey).texBuffer );
return textureId[0];
}
지금 이건 저의 경험을 토대로 나온 것들입니다.
뭔가 문제가 있다고 생각되시면 언제든지! 꼭! 댓글 부탁드립니다.
감사합니다.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
죄송합니다. 꼭 저 위 형태가 아니라 가로 세로 길이를 일치 시키기만 하면 됩니다.
80x80,90x90 모두 표현 되요^^
-- 다시 죄송...
핸드폰 기기에 따라 다른 것 같습니다.
HTC 디자이어 HD 같은 경우는 꼭 64x64형태로 맞춰야 하지만,
Sky Vega X 같은 경우는 좌우만 일치하면 보이더라구요.
참조하세요^^
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
출처: http://gogorchg.tistory.com/entry/Android-Opengl-%ED%85%8D%EC%8A%A4%EC%B3%90-%EB%B3%80%EA%B2%BD-%EC%8B%9C
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
출처: http://gogorchg.tistory.com/entry/Android-%EB%B9%84%ED%8A%B8%EB%A7%B5%EC%97%90-%EC%9D%B4%EB%AF%B8%EC%A7%80%EB%82%98-%EB%AD%94%EA%B0%80%EB%A5%BC-%EA%B7%B8%EB%A6%B4-%EB%95%8C-%EB%9C%A8%EB%8A%94-%EC%97%90%EB%9F%AC
java.lang.IllegalStateException: Immutable bitmap passed to Canvas constructor
Bitmap bitmap = BitmapFactory.decodeFile(..);
Canvas canvas = new Canvas(bitmap);
canvas.save();
canvas.drawbitmap(...);
...
canvas.restore();
위 와 같은 형식에 소스에서 디버깅을 하면 회색 네모상자의 에러가 나옵니다.
BitmapFactory에서 불러온 이미지는 수정이 불가능 하다고 하네요.
크기나 뭐 옵션 같은 것은 줄 수 있겠지만요^^
그래서 똑같은 비트맵을 복사함으로써 해결이 됩니다.
Bitmap bitmap = BitmapFactory.decodeFile(..);
Bitmap copyBitmap = bitmap.copy(Bitmap.Config.ARGB_8888,true);
아니면 새로운 비트맵을 생성 해서 그 속에 이미지랑 다른 것들도 함께 넣어버리는 것이지요.
Bitmap paletBmp = Bitmap.createBitmap(imgWidth, imgHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(paletBmp);
....
간단하죠??^^
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
출처: http://gogorchg.tistory.com/entry/Android-%EB%B0%B0%EA%B2%BD%ED%99%94%EB%A9%B4%EC%97%90-%EC%9D%B4%EB%AF%B8%EC%A7%80%EB%A5%BC-%ED%91%9C%ED%98%84%ED%95%A0-%EB%95%8C-%EC%B0%B8%EA%B3%A0%EC%82%AC%ED%95%AD
배경화면에 이미지를 출력할 때,
offset까지 생각을 해서 화면보다 좌우를 더 크게할 경우가 많으시죠.
그럴 경우, 이미지가 커지고 출력할 때
과부하가 생기기도 합니다.
즉, 큰 이미지의 딱 화면 크기 정도만 가져와서
표현을 하게 되면, 과부하가 생길 이유가 없겠죠.
하늘색 부분만 보이도록 하는 것이지요!
소스는 다음과 같습니다.
Matrix matrix = new Matrix();
matrix.mapRect(rect);
matrix.setTranslate(mOffsetX, 0); // offset설정
mainCanvas.drawBitmap(mainBmp,matrix,mainPaint); // 이미지에 적용해서 출력
'구글과인터넷 > 안드로이드' 카테고리의 다른 글
안드로이드 android Opengl es 2.0 다시 그리기 (0) | 2012.12.12 |
---|---|
안드로이드 ndk Opengl es 그림을 배수로 맞추지 않는경우 화면이 하얗게 보이는 현상. (0) | 2012.12.12 |
안드로이드 android ndk 디버그 하는법 (0) | 2012.12.12 |