scaleform.minarto.com

이미지 로드 및 관리 본문

Utils & Liblary

이미지 로드 및 관리

미나토 2012. 2. 20. 11:45

이제 CLIK 에 관한건 어느정도 마무리를 지어도 될 듯 싶네요.
일단 목표로 한 10개 포스팅도 채웠고, 중요한 클래스들은 대부분 다룬 것 같고, 나머지는 그걸 어떻게 응용하는 것인지라... 알아서 하셔야죠들...

CLIK 은 잘 만든 녀석입니다.
저도 다음이나 다다음 마일스톤 즈음에 적용할 생각이고요... 다만 현재시점에서는 4.x 대의 CLIK 은 덜 만들어졌습니다. 당장 쓰시려면 그것만 참고하시기 바랍니다. 




0. 이미지 관리



오늘은 이미지 로드에 관한 글을 써볼까 합니다


일단 기본적인 로드에 관해서는 이 글을 읽어보시고요... 오늘은 로드 그 이후에 관한 글을 쓰려합니다. 로드한 이미지를 어떻게 관리할 것이냐입니다.


as3에서의 이미지 로드라면 일반적으로 Loader 클래스를 씁니다. 로드가 완료되면 Loader 의 content 속성으로 해당 이미지를 참조하고 있습니다. 더불어 Loader 의 contentLoaderInfo 속성content 속성으로도 해당 이미지를 참조하고 있습니다.

그런데 이걸 일일히 Loader 객체를 생성해내고, 이벤트 처리를 하고, 또 그에 따라 메모리를 관리해야 하는 것은 너무나 귀찮고, 힘든 일입니다. 중복된 일이 생겨나면 함수나 클래스로 만들어 처리해야 하는 것이 우리의 해야할 일입니다.

일단 가칭으로 이 클래스의 이름을 ImageLoader 라고 해두겠습니다.
이 클래스에 구현되어야 할 기능을 적어볼까요.

1. 단일 객체를 사용해야 합니다.

2. 이미지 컨테이너가 필요합니다.

3. 이미지를 받아올 api가 필요합니다.

4. 이미지를 삭제할 api가 필요합니다.



1. 단일 객체 사용


게임 UI를 만들다보면 같은 이미지를 여러군데 써야할 일이 많이 생깁니다.
예를 들어 mmorpg라면 물약 이미지는 인벤토리에도 있고, 창고에도 있어야 하며, 상점에도 있어야 합니다. 아, 그리고 퀵슬롯에도 있어야 하는군요.

각각의 UI 마다 이미지를 따로 불러들인다면, 굉장한 낭비겠죠... 뭐, 메모리야 엔진을 잘만져서 하나로 통일해(?) 쓸 지도 모르겠지만 로드라는 그 행위도 cpu 와 메모리의 낭비입니다.

그럼 여기서 우린 한번만 불러서 BitmapData 만 참조해서 여러군데 쓸 방법을 찾아야 합니다.

자, 중복되면 안된다고 했으니 단일 이미지 로드 클래스부터 만들어야 하겠죠... Singleton 을 써서 만드시던지, 그냥 모든 api 를 static 으로 만드시던지 해서 만들면 될겁니다.



2. 이미지 컨테이너 생성

Loader 처럼 하나의 이미지를 불러서 처리하는 거라면 컨테이너가 아닌 그냥 content 참조만으로도 되지만 단일 클래스를 만들다 보니 해당 클래스에서 여러 이미지를 관리해야할 필요가 생겼습니다.
컨테이너라면 Array, Object, Vector.<Bitmap>, DisplayObjectContainer 등등이 있을겁니다. 컨테이너에 관한 글은 이글을 읽어보시기 바랍니다.

저는 Object 를 사용했습니다. 그 이유는 경로의 중복을 제거하기 위해서입니다.

예를 들어보겠습니다.

path = "a.png" 



이런 경로가 있어서 이미지를 불러왔다고 치죠. 불러와서 가장 흔히 사용하는 배열에다가 담으면

Array[0] = img



가 되겠죠. 그런데 다음에 또 불러와야 한다면 어떻게 해야하죠? 또 불러와서 1번에 넣을건가요? 그러면 데이터가 중복된겁니다.

여기까지가 초급자 코스라면, 중급자 코스로 넘어가겠습니다.

Array[0] = {path:"a.png", img:img}



이렇게 저장했다고 치죠. 여기서는 두가지의 문제가 발생합니다.

이걸 가져다 쓰려면 배열을 루프로 돌려 검색해서 해당 원소 객체의 path 속성이 가 불러올 path 속성과 같은지를 판단하여 가져오고 같은 경로의 객체가 없다면 그때 불러오는 방식을 써야 합니다.
그 전에는 또 {} 와 같은 객체를 생성했네요? 

런타임 상에서 다음과 같은 비용이 발생하겠죠.

1. 객체 생성 비용
2. 반복문 비용
3. 비교문 비용

데이터의 중복은 제거했지만 위와 같은 비용의 문제가 발생했습니다. 이제는 다음 상급자 코스로 넘어가겠습니다.

여기서 유니크한 것은 경로입니다. (BitmapData 도 유니크 하지만 유니크한 것인지를 판단하는데 비용이 많이 소모됩니다.)

경로가 같다면 같은 이미지이고, 다르다면 다른 이미지겠죠.

Object를 쓴다면 다음과 같습니다.

getContent(path:String):BitmapData



라고 api를 만들었다면,

BitmapData = Object[path];
if( BitmapData ) return  BitmapData;
else{
로드
return null
}

로드완료{
     Object[path] =  BitmapData 
}




이런 식으로 구현이 되는 겁니다. Object 는 키와 값으로 이루어진 해시맵이고 경로가 곧 키값이 되어서 검색이 가능하게 됩니다.

장점은

1. 객체를 추가로 생성하지 않는다
2. 반복문 검색을 하지 않는다. 대신 키검색을 하지만 반복문의 속도보다 월등하다
3. 비교문 또한 발생하지 않는다



3. 로드 구현


실제 로드를 구현해볼까요

_dic:Object = {}

function getContent($src:String, $handler:Function=null):BitmapData{
var loader:Loader, bd:BitmapData;
bd = _dic[$src];
if(bd){
if($handler) $handler(bd);
}
else{
                src = $src 
loader = new Loader;
                handler = $handler
loader.addEventListener(Event.COMPLETE, complete);
loader.load($src);
}
return bd; 
}
src:String
handler:Function
function complete(e:Event):void{
      _dic[src] =  e.content;
     handler(e.content);
}

function delContent($src:String):void{
delete _dic[$src];
}


이정도가 되겠죠... (정확하게는 구현하지 않았습니다.)

실제로는 전 이렇게 쓰지 않습니다. 예를 들어

1. Loader 는 Pooling 으로 재활용하세요
2. 클로저를 활용해서 멤버변수와 핸들러를 모두 없앨 수 있습니다
3. gc도 해야겠죠...

그 외에도 여러가지가 추가되어 있습니다.

이 포스팅이 제 코드를 베껴가라는 건 아니니까요... (하지만 이또한 구글 검색을 잘 해보면 어딘가 숨어있을지도...)


두번째 인자인 handler 를 보시면 BitmapData를 인자로 받는 함수를 받도록 해놨습니다.
우리가 이미지를 로드해서 쓸 때 그냥 쓸 수는 없잖아요. Bitmap 에 붙이기도 해야하고, 그 후에 크기도 늘려야 하죠. 그에 해당하는 함수를 받도록 해놨죠.

그래서 이미지가 이미 존재한다면 핸들러에 비트맵데이터를 인자로 보내 실행시키고, 없다면 로드를 해서 완료됐을 때, 해당 핸들러를 실행시킵니다.



4. 마무리


좀 덧붙이자면 완벽한 메모리 관리를 꿈꾸신다면 두군데를 더 신경 쓰셔야 합니다.

1. 사용 후에 BitmapData.dispose() 를 실행하여 메모리를 해지한다.
2. Loader.content 는(contentLoaderInfo.content 도) 여전히 해당 비트맵을 참조하고 있기에 gc 대상이 아니다.
3. BitmapData 만 활용하므로 불러온 Bitmap 은 더이상 이 클래스에서 쓸모가 없다. 풀링에 반환하여 재활용한다.

하지만 스케일폼에서는  BitmapData.dispose() 를 지원하지 않습니다. 그래서 가짜 이미지를 로드하는 방법도 있습니다. content 가 null 이 되게 만드는거죠.
(전 굳이 여기까지는 하지 않았습니다. 3번 정도만 사용하죠)



이번 포스팅은 아무래도 독자가 UI 개발자라는 전제하에 작성한 포스팅이 되었습니다.
코드가 좀 많고, 내용도 개발자만 알아들을 만한 내용으로 도배를 했네요...

플래시 개발 일을 어느정도 해오신 분이라면 충분히 알아들을 거라 생각합니다