스마트폰 카메라 앱에 여러 가지 기능이 있지만 수많은 카메라앱에서 스티커 기능을 기본적으로 제공하고 있다. 구현 방법은 다양하겠지만 대충 간단한 스티커 기능을 제공하는 카메라 예제를 만들어 보았다. 상용 수준엔 절대 못 미치고 그냥 이런 식으로 하면 스티커 기능이 된다라는 정도의 가벼운 예제이다. 날코딩 예제 ㄱㄱ
이 예제에 스티커 기능을 추가할 것이다. 앱 이름에서 알 수 있듯이 이 앱은 raw 파일까지 저장한다. 스티커를 만드는 것과는 아무 상관이 없고 그냥 이 예제를 예전에 받아 놓은 적이 있어서 사용했을 뿐이다. raw 파일이 저장되는 게 불편하면 해당 부분을 주석 처리하거나 지우 거나하면 된다. ^^;
몇 가지 제약 사항들이 있다.
1. 사진을 찍어보면 세로 모드에서 촬영 시 사진이 가로로 저장되는 것을 알 수 있다. 그에 맞는 별도의 스티커 방향 처리를 하지 않을 것이기 때문에 그냥 가로모드로 고정하도록 할 것이다. 스티커가 들어간 이미지를 만드는 게 목적이기 때문에 해당 사항은 필요시 추가하면 된다.
2. 프리뷰에서 보이는 스티커 위치와 실제 저장되는 이미지에서 스티커 위치가 완전히 일치하지 않는다. 이 오차는 프리뷰에서 보이고 있는 해상도(스마트폰 화면 해상도)와 실제 저장되는 이미지의 해상도가 불일치하기 때문에 정확히 맞추기 어려웠다. (비율을 계산해서 적절한 리소스를 사용해야함) 따라서 비슷한 위치에 놓이도록 캘리브레이션 했다. (대충 하드 코딩했다는 소리) 그래서 스티커 위치 조정이나 크기 변경 같은 것도 없다. 그냥 고정이다. 나중에 해봐야겠다.
이미지뷰의 배경은 투명으로 지정했다. 스티커로 사용될 이미지(pengsoo_bg.png)도 배경은 투명한 파일이기 때문에 좀 더 자연스러운 모습을 연출할 수 있다. (쓸데없는 디테일)
스티커로 사용될 직접 그린 펭수
스티커로 사용할 이미지는 요즘 취미로 그림 그리기를 하고 있어서 직접 그린 펭수 이미지이다. (또 잡소리;)
이렇게 하면 카메라 프리뷰 실행 시 펭수 이미지가 오버레이 되어 스티커 효과를 낼 수 있다.
그럼 이제 JPEG가 만들어지는 부분을 찾아서 파일이 저장되기 전에 이미지를 합성하는 처리를 하면 된다.
이미지 합성은 Camera2RawFragment.java에서 run()을 검색하여 다음 내용을 추가하면 된다.
추가한 내용을 살펴보자.
일단 resizingImage는 촬영한 사진 이미지 해상도를 조정한 이미지이다. 원본 이미지 해상도가 너무 높기 때문에 거기에 200dp x 200dp 사이즈의 스티커를 넣으면 매우 작게 보여 프리뷰에서 보는 것과 차이가 심하다. 그래서 createScaledBitmap()를 사용하여 그냥 적당히 1920x1440으로 조정했다. 이렇게 날코딩한 것에도 나름의 이유가 있다. 다음과 같이 카메라 앱을 실행하고 해당 화면을 캡처한 후 거기서 프리뷰가 나오는 영역의 사이즈(빨간 박스)가 1920x1440인 것이다. (매우 과학적)
createScaledBitmap()의 마지막 인자는 filter인데 true로 해주면 사이즈를 조절할 때 이미지가 덜 깨지도록 해준다.
이렇게 나온 결과물과 펭수 스티커 이미지를 합성하기 위해서 일단 비어있는 Canvas를 하나 만든다. Canvas는 말 그대로 캔버스이다. 개인적으로 좀 더 쉽게 캔버스 개념을 생각하자면 이젤(easel)에 가깝다고 생각한다. 실제 캔버스(도화지)는 Paint 객체나 지금 사용하는 것처럼 bitmap을 설정해주는 것이 아닐까 싶다.
여기까지 진행하면 합성된 이미지가 되는 것이다. 말로만 들으면 와 닿지 않을 수 있어서 다음과 같이 그림을 그려봤다.
1. Canvas 생성
2. 투명 값 사용 가능한 빈 이미지 resultImage를 도화지로 설정
3. (크기를 조절한) 촬영한 사진 이미지를 도화지에 그림
4. 펭수도 추가로 그림
이렇게 열심히 준비한 이미지는 compress() 함수를 이용하여 파일로 저장한다.
compress()의 첫 번째 인자는 압축할 포맷이고 두 번째 인자는 퀄리티인데 100으로 주면 최상의 퀄리티이다. 이미지에서 퀄리티의 수치이니 압축률 정도가 되겠다. 100%로 압축한다는 말은 곧 압축하지 않는다는 의미이니 최상의 퀄리티인 것이다. 추가적으로 설명하면 png 포맷이 비손실 압축이라 퀄리티 값은 무시된다. 세 번째 인자는 OutputStream인데 파일로 저장하기 위해서 미리 생성한 FileOutputSteam을 넘겨주고 있다.