Flash에서 이미지를 표현하기 위한 방법은 VectorBitmap이 있다.

 

Vector은 점,선,면등의 단순한 수학적 정보만 이용하여 이미지를 표현한다. 그렇게 때문에 적은 용량으로 이미지를 표현할 수 있으며 특히 확대/축소에도 깨지지 않는 이미지를 표현하는데 탁월하다. 또한 곡선 및 각종 이펙트 표현력도 훌륭하다. 적은 용량으로 이미지를 표현할 수 있기 때문에 메모리 관리에 도움이 된다. 하지만 복잡한 이미지의 경우 도리어 퍼포먼스의 저하가 일어날 수 있으며 이 경우 이동과 같은 에니메이션을 줄 경우 CPU연산이 많아지는 단점이 있다.

 

Bitmap은 이미지를 표현하기 위해 Pixel정보를 이용한다. 하나의 Pixel정보에는 ARGB값을 가진다. Pixel 하나하나 정보를 가지고 다루므로 복잡한 이미지를 나타내는데 좋으며 Vector와 달리 이동 애니메이션을 주더라도 CPU연산이 적다. 하지만 이미지가 클수록 Pixel 정보량이 제곱단위로 커지기 때문에 확대/축소시에 메모리가 과다하게 사용될 여지가 있다. 잘못사용하면 너무 큰 이미지로 인해 Flash Player가 다운될 수도 있다.

 

아래는 필자가 다루고자 하는 Vector와 Bitmap 이미지의 장단점을 언급한다.(아래 장단점은 완벽한 것은 아니다. 가령 Vector라고 해도 객체수가 적으면 속도에 무리가 없기 때문이다. 적어도 필자가 하고 있는 프로젝트 내에서 이런 문제가 있다는 것을 미리 언급한다.)

 

  • Vector 이미지
    - 메모리 효율이 좋다.(확대/축소시에도 메모리 점유율 변화 없음)
    - 이동 애니메이션 처리가 느리다.(CPU 연산이 커짐)
  • Bitmap 이미지
    - 메모리 효율이 나쁘다.(확대시 메모리 점유율 커짐)
    - 이동 애니메이션 처리가 빠르다.(CPU 연산 적음)

 

Vector 이미지와 Bitmap 이미지를 연구하게된 배경은 필자가 스타플 서비스(http://starpl.com/)의 별지도를 만드는데 이 부분에 대한 정보가 필요했기 때문이다. 먼저 아래 글을 읽어보면 이해가 쉽겠다.

 

[Flash,ActionScript 3.0]DisplayObject cacheAsBitmap 속성의 적용시점 문제

[Flex/AIR] 메모리의 무서운 적! DisplayObject의 cacheAsBitmap 속성

 

스타플의 별지도

 

구글맵과 같은 일반 지도의 경우 타일(Tile)들의 구성요소는 Bitmap 이미지이다. 최적화된 Bitmap이미지이기 때문에 렌더링 시간도 짧고 이동도 빠르다. 필자가 작업한 별지도는 천체 데이터 및 사용자 별 데이터를 읽어와 Vector 이미지로 렌더링하는 방식이다. 따로 데이터 가공 및 렌더링 시간이 필요하고 Vector이미지는 일단 이동 애니메이션이 느리다. 별이 반짝거리므로 단순히 Bitmap으로 바꿔서 처리할 수 있는 것도 아니다. 결국 스타플 별지도는 기본적으로 Vector 이미지로 타일을 구성해야한다.

 

앞서 설명했듯이 Vector 이미지를 이용하면 메모리 문제는 발생하지 않는다. 문제는 이동시 속도가 느리다는 것이다. 마우스로 지도를 드래그해서 이동하는 경우 저사양 컴퓨터에서는 버벅거림이 발생한다. Vector 이미지 타일로 구성된 지도의 단점이다. 그래서 이것을 극복하기 위해 첫번째로 사용하려했던 기능이 flash.display.DisplayObject의 cacheAsBitmap 속성이였다. 이 속성을 true로 설정하면 타일이 Bitmap으로 캐싱되고 실제로 적용후 이동처리해보면 훨씬 부드러운 이동 애니메이션을 볼 수 있다.

 

하지만 cacheAsBitmap은 Bitmap 이미지를 만드는 것이므로 확대/축소에서 문제가 있다. 이 속성을 true로 설정하고 확대하면 메모리가 기하급수적으로 늘어난다. 그래서 간단하게 확대/축소전에 cacheAsBitmap을 false로 지정하면 되겠다고 생각했다. 하지만 이 속성이 false로 설정된 순간 캐싱된 Bitmap을 해제시켜주지 못했다. 결국 false로 지정하고 확대해도 여전히 메모리가 증가한다.

 

이를 극복하기 위해 BitmapData를 이용했다. 만들어진 타일(Tile) 클래스에 bitmapMode라는 속성을 정의하고 true이면 Bitmap이미지를 false이면 Vector이미지를 보일 수 있도록 만든다. 그렇게 해서 이동이 발생시에는 Bitmap이미지를 사용하고 평상시 또는 확대/축소시에는 Vector 이미지를 사용할 수 있도록 한다. 아래는 이것을 구현하는 간단한 코드이다.(완벽한 코드가 아니다. 단지 예시일 뿐이다.)

 

this.addEventListener( Event.REMOVED_FROM_STAGE, onRemovedFromStage, false, 0, true );

public function bitmapMode( value:Boolean ):void
{
	if( _bitmapMode == value ) return;
	_bitmapMode = value;
	
	.....

	if( _bitmapMode )
	{
		var bitmapData:BitmapData = new BitmapData( getWidth()+100, getHeight()+100, true, 0x00000000 );
		var matrix:Matrix = new Matrix();
		matrix.translate( 50, 50 );						
		bitmapData.draw( this, matrix, null, null, null, false );
		bitmapImage = new Bitmap( bitmapData );
		bitmapImage.x = -50;
		bitmapImage.y = -50;
		addChild( bitmapImage );
		removeChild( vectorImage );
	}
	else
	{
		addChild( vectorImage );
		removeChild( bitmapImage );
	}

	.....
}


public function disposeBitmap():void
{
	bitmapMode = false;
	if( bitmapImage )
	{
		bitmapImage.bitmapdata.dispose();
		bitmapImage = null;
	}
}

private function onRemovedFromStage( event:Event ):void
{
	this.removeEventListener( Event.REMOVED_FROM_STAGE, onRemovedFromStage, false );
	disposeBitmap();
}

 

bitmapMode 속성을 구현할때 주의할 사항은 bitmapMode가 true일 때마다 BitmapData를 생성하는 일은 매우 비효율적이라는 것이다. 왜냐하면 이동할때마다 BitmapData를 만드는 것이기 때문이다. 그러므로 처음 이동이 발생할때만 BitmapData를 만들도록 하는게 중요하다. 또한 bitmapMode가 false라고 해서 기존에 만든 BitmapData를 삭제해서는 안된다. 삭제하면 언급한데로 다시 BitmapData를 만들어야하기 때문이다.

 

또 한가지 여기서 발생하는 가장 큰 문제는 적절한 메모리 관리이다. BitmapData를 잘 삭제해주어야 메모리 문제가 발생하지 않기 때문에다. 단순히 위에서 언급한 bitmapMode 변경으로는 BitmapData를 제대로 삭제할 수 없다. 그러므로 타일(Tile)클래스에 disposeBitmap() 함수를 만들어 bitmap.bitmapdata.dispose()를 호출하여 비트맵을 삭제하도록 하고 타일이 removeChild 될때 이 disposeBitmap()을 호출하여 BitmapData를 완벽하게 삭제하도록 해준다. removeChild가 되는 시점은 Event.REMOVED_FROM_STAGE 이벤트 처리를 하면 바로 알 수 있다.

 

삭제된다는 이야기는 메모리에서 바로 삭제된다는 것을 의미하지 않는다. Flash Player는 가비지 컬렉터가 작동한다. 즉 메모리에서 삭제될 대상을 모아두었다가 어느 시점에 한꺼번에 삭제한다. 이는 메모리 관리의 효율적인 방법중에 하나로서 개발자는 가비지 컬렉터 대상이 되도록 모든 참조를 없애주도록 해야한다. 가비지 컬렉터에 대해서는 아래 필자의 글을 참고한다.

 

[Flex / AIR / ActionScript3] Event 청취자와 메모리 관리

Flash Player 의 가비지 컬렉션(GC) 동작 방식에 대해

 

메모리가 제대로 반환이 되는지 확인하기 위해 Flex Builder(현 Flash Builder)의 프로파일링 기능을 이용하거나 System.totalMemory를 적극적으로 활용하여 메모리 상태를 점검하는것이 좋겠다.

 

소개한 대로 Bitmap과 Vector 이미지를 필요할 때마다 바꿔가면서 처리하는 것이 필자가 발견한 최상 방법이였다. 적용시 키포인트는 적절한 메모리 관리와 BitmapData를 자주 생성하지 않도록 하는 것이다.

 

 

 

+ Recent posts