본문 바로가기

프로그래밍/Android 짜투리 지식

[android] ImageView Matrix

android에서 사진을 회전시켜야 할 때가 있다.

이때까지 내가 찾은 방법은 3가지


1. bitmap을 회전시킨다. (Bitmap)

2. View를 회전시킨다. (Canavs)

3. 카메라의 위치를 바꾼다. (Opengl)


여기서 1번에 대해서도 2가지 방법이 있다.

1. bitmap을 회전시켜 bitmap을 재생성한다.

2. bitmap을 회전시키지만 따로 저장하지 않는다.


여기서 판단의 기준이 되는건 역시나 메모리

물론 요즘 OS가 좋아지고 힙 메모리도 좋아져서 왠만한 메모리 사용은 허용된다.

하지만 그렇다고 메모리를 막 쓸 수는 없다. 혹시나 모를 기근에 대비하자.


때문에 나는 처음에 bitmap을 재 생성해서 다루던 코드를 matrix로 view만 회전시키는 방법으로 바꿨다.

이렇게 하면 적어도 bitmap을 생성하는 시간절감 + 메모리에 같은 용량의 bitmap을 2개 올리는 번거로움은 없어질 것이다.

  - 이유 : Bitmap rotatedBitmap = Bitmap.createBitmap(oldBitmap, 0, 0, width, height, matrix, true);

  - 물론 oldBitmap을 recycle 해주겠지만, recycle 자체가 바로바로 반환되는 것도 아니고, 그냥 처리 해주겠지. 라고 믿고 맡기는 부분이어서 100% 메모리 반환을 기대하기 어렵다.

  - 그리고 중요한건 순간적으로 oldBitmap, rotateBitmap 2개가 메모리가 동시에 존재하는 때가 있다. 눈으로 봐도 확인 가능한 코드.

  - 때문에 단순히 보기 용 회전이라면 bitmap생성 대신 view만 회전시켜도 충분할 것이다. 또 이걸 회전 된 bitmap으로 얻길 원한다면 rotate의 속성을 최종적으로 1번만 적용해서 생성하는 코드를 작성하는게 앱을 위해서나 메모리를 다루는 개발자를 위해서나 이로울 것으로 판단된다.

						Matrix matrix = new Matrix();
						report.setRotate();
						int bWidth = bitmap.getWidth();
						int bHeight = bitmap.getHeight();
						int vWidth = img.getWidth();
						int vHeight = img.getHeight();

						// bitmap의 크기를 가질 rect
						RectF bRect;
						// view의 크기를 가질 rect
						RectF vRect = new RectF(0, 0, vWidth, vHeight);

						// 0, 180도 90 ,270도 일때의 width가 다르므로 bitmap의 길이를 다르게 설정해준다.
						
						if(report.getRotate() == 90 || report.getRotate() == 270) {
							bRect = new RectF(0, 0, bHeight, bWidth);
						} else {
							bRect = new RectF(0, 0, bWidth, bHeight);
						}
						float wRatio ;
						float hRatio ;
						if(bRect.right < vRect.right)
							wRatio = bRect.right / vRect.right;
						else
							wRatio = vRect.right / bRect.right;
						
						if(bRect.bottom < vRect.bottom) 
							hRatio = bRect.bottom / vRect.bottom;
						else
							hRatio = vRect.bottom / bRect.bottom ;
						
						// setRectToRect는 scale과 translate를 동시에 해준다.
						// mode는 fit start로 0,0에 맞춘다.
						matrix.setRectToRect(bRect, vRect, Matrix.ScaleToFit.START);
						float[] f = new float[9]; // 좌표 확인용 배열
						matrix.getValues(f);
						
						// 0,0에 있는 이미지를 가운데로 이동시킴
						matrix.postTranslate(img.getWidth()/2 - ((bWidth * f[0]) / 2) , img.getHeight()/2 - (bHeight * f[4] /2));
						// 가운데에 있는 이미지를 가운데 중심으로 회전시킴
						matrix.postRotate(report.getRotate(), img.getWidth() / 2, img.getHeight() /2);
						img.setImageMatrix(matrix);

여기서 실질적으로 연산을 해주는곳은 34, 39, 41matirx에 set, post 해주는 부분이다.

중요한건 적용 순서에 따라서 결과물이 다르다. 애초에 setRectToRect 함수의 마지막 전달인자에 Center 값이 있지만 잘 되지 않아서 START로 준 후 정중앙으로 view를 끌고 왔다. 때문에 39의 코드가 추가됐다.


예제파일 : 

ImageRoate.zip