반응형
블로그 이미지
개발자로서 현장에서 일하면서 새로 접하는 기술들이나 알게된 정보 등을 정리하기 위한 블로그입니다. 운 좋게 미국에서 큰 회사들의 프로젝트에서 컬설턴트로 일하고 있어서 새로운 기술들을 접할 기회가 많이 있습니다. 미국의 IT 프로젝트에서 사용되는 툴들에 대해 많은 분들과 정보를 공유하고 싶습니다.
솔웅

최근에 받은 트랙백

글 보관함

비트맵 마스크 사용하기

2012. 6. 1. 11:35 | Posted by 솔웅


How to Use Bitmap Masks

Posted by Jonathan Beebe

이번주 튜도리얼은 많은 프로젝트에서 꾸준히 애용되고 있는 기능이지만 한편으로는 특별한 use-case로 인해 여러분이 잘 모를 수 도 있는 기능을 다뤄볼까 합니다. 이 기능은 bitmap masking 인데요, graphics.newMask() 함수를 사용해서 구현합니다.

아마 bitmap masking이 무엇인지 잘 모를수도 있습니다. 또 어떻게 유용하게 사용될지에 대해서 알지 못할수도 있구요. 그러신 분들은 이번 강좌를 잘 보세요. 분명히 아주 유용한 툴일테니까요. Bitmap mask는 약간 tricky 한 방법으로 문제를 해결하는데 사용될 수 있습니다.

    - 이미지의 투명한 부분에는 touch 이벤트가 안 먹게 하기 (아주 많이 쓰이는 케이스 입니다)
    - 특정한 모양으로 이미지를 Clip 하기 (예를들어 스크롤 뷰 위젯에서 전체 스크린 내의 일부 sub-section 부분을 clip 하기 위해 bitmap mask 를 이용할 수 있습니다.)
    - solid background가 있는 이미지에 transparency 주기


위에 보시듯이 masking의 주요 목적은 이미지의 일 부분을 숨기는 겁니다. (혹은 전체 display 그룹의 일 부분을 숨길수도 있구요.) 즉 이미지를 masking 하는 겁니다. 그러려면 masking 할 이미지도 있어야 합니다. mask라는 단어를 사용하는 이유가 그냥 이미지나 그룹에 덮어씌우는 기능을 하기 때문입니다.





What a mask looks like

아래에 과일 이미지가 있습니다. (fruit.png) 그리고 bitmap mask image 가 있습니다. (fruit-mask.png) 이 두개의 이미지를 사용할 겁니다. 회색으로 서양의 체스판 모양으로 돼 있는 부분은 이미지의 투명한 부분입니다.





오른쪽의 bitmap mask의 검은 부분이 투명한 부분이 될 겁니다. 그리고 흰 부분은 이 mask가 정확하게 apply 됐을 때 보여지게 될 원래 source 이미지가 들어갈 부분입니다. 이 mask로 사용될 이미지에는 어떤 특별한 제약이 있는것이 아닙니다. 아무 이미지나 사용하실 수 있습니다.

What’s the point?

위 두개의 이미지를 사용할 건데 뭘 하려는 것일까요? mask 이미지에 있는 검은 색은 이미 source image에서 투명하게 돼 있는 부분인데요. 이 튜토리얼에서 다룰 것은 bitmap masking을 사용해서 특정 부분을 투명하게 만드는것 말고 투명한 부분에서는 touch 이벤트가 안 먹도록 만드는 방법을 알려 드릴 겁니다.

투명한 부분이 있는 이미지라도 touch listener를 달면 전체 이미지에서 이 touch 이벤트가 먹힐겁니다. 오직 저 사과만 터치했을 때 어떤 이벤트가 발생하도록 하려면 어떻게 해야 할까요? 그렇게 하려면 이 bitmap mask를 사용해야 합니다. 또 예를 들어 어떤 이미지가 있는데 그 이미지의 일 부분에만 터치가 먹히게 하고 싶을 때도 이 방법을 사용할 수 있습니다. 이미지내에 투명한 부분이 없을때도 말이죠.

Bitmap mask requirements

mask로 사용할 이미지를 만들때는 아래와 같아야 합니다.

   1. width와 height가 4의 배수여야 합니다.
   2. 적어도 3px의 검은테두리가 있어야 합니다.


위의 가이드라인을 따르지 않는다면 예상한 대로 masking이 일어나지 않을 것이고 이것을 debug 하는 것도 힘들겁니다. 추가적으로 고려해야 할 사항들을 아시려면 Bitmap Mask documentation 을 읽어 보세요.

Applying the mask

mask를 적용하려면 graphics.newMask()를 사용해서 새로운 mask를 만들어야 합니다. 그리고 object:setMask() 메소드를 사용해서 객체나 그룹에 apply 합니다.

local mask = graphics.newMask( "fruit-mask.png" )

local fruit = display.newImage( "fruit.png" )
fruit.x, fruit.y = display.contentCenterX, display.contentCenterY

-- apply mask to object
fruit:setMask( mask )


위의 코드를 보시면 우선 mask를 생성하고 display object를 만들고 그 마스크를 display object에 apply 했습니다. 이렇게만 하면 약간의 문제가 생길겁니다. 디폴트로 객체의 적용 좌표는 0,0 이 됩니다. (좌상단이 되겠죠) 그리고 mask는 중앙을 기준으로 하는 reference point가 적용 되면 아래와 같은 모양으로 object:setMask()가 적용 될 겁니다.





mask 가 적용될 객체의 정 중앙에 제대로 덮이길 원하시면 객체의 maskX와 maskY 프로퍼티를 수정해 줘야 합니다.

fruit.maskX = fruit.contentWidth*0.5
fruit.maskY = fruit.contentHeight*0.5


마스크는 대상 객체의 0,0 지점에 위치한다는 것을 꼭 기억해 두세요. 위의 코드를 추가하고 나면 아래와 같이 될 겁니다.



결과적으로 모바일 화면에는 아래와 같이 나오게 됩니다.



자 겉으로 보기에는 그냥 display.newImage() 해서 원래 배경이 투명한 이미지를 display 한것과 다를것이 없습니다. 만약 원래 배경색이 투명하지 않았다면 겉모습이 투명하게 바뀌었을 테니까 뭔가 변화가 있다고 느껴지겠죠. 어쨌든 위에 사용한 원래 이미지 파일에는 배경이 투명하게 이미 돼 있었습니다. 그러니까 위 예제가 유용하게 사용되려면 투명한 부분을 un-touchable 하게 만들어야 쓸모가 있게 되겠죠.

다행스럽게도 이미지가 이렇게 한번 mask 되고 나면 다음 사용법은 아주 쉽습니다. 그냥 간단하게 object.isHitTestMasked 프로프티를 true로 세팅하면 투명해진 부분에 touch 이벤트가 안 먹히게 할 수 있습니다.

fruit.isHitTestMasked = true

이제 여러분이 이 fruit 객체에 touch 리스너를 달게 되면 전체 256X256 이미지가 다 touchable 되는것이 아니라 투명하지 않은 부분만 touchable 될 겁니다. 이 예제의 경우엔 아주 정확하게 과일 부분에 터치할 경우에만 touch 이벤트가 적용 되겠죠.

Dynamic Content Scaling


bitmap mask는 dinamic content scaling을 지원하지 않습니다. 그렇다고 해서 방법이 전혀 없는 것도 아닙니다. 구현하시려면 high resolution asset를 로드해서 object.maskScaleX와 object.maskScaleY 프로퍼티를 원하는 크기에 맞게 세팅해 주세면 됩니다.

예를 들어, retina display에 맞춰서 개발을 하고 있고 아주 super-sharp 하게 되도록 mask를 사용하고 싶으시면 fruit-mask.png와 fruit-mask@2x.png 두개를 준비하세요. (원본보다 두배 크기의 이미지를 준비하시면 됩니다.) 그리고 mask를 생성할 때 (graphics.newMask()) @2x asset을 사용하시면 됩니다. 그리고 나서 fruit.maskScaleX 와 fruit.maskScaleY 를 display.contentScaleX와 display.contentScaleY로 세팅하세요.

아래 예제가 있습니다.

local mask = graphics.newMask( "fruit-mask@2x.png" )

local fruit = display.newImage( "fruit.png" )
fruit.x, fruit.y = display.contentCenterX, display.contentCenterY

-- apply mask to object
fruit:setMask( mask )
fruit.maskScaleX = display.contentScaleX
fruit.maskScaleY = display.contentScaleY
fruit.maskX = fruit.contentWidth*0.5
fruit.maskY = fruit.contentHeight*0.5


이런 방식을 사용하면 좀 더 다양한 해상도에 맞도록 이미지들을 준비하면 좀 더 super-sharp 하게 이 mask를 사용할 수 있겠죠. 물론 각각 다른 high resolution 이미지를 사용해야 겠죠. 같은 방법으로 안드로이드 디바이스들도 이렇게 적용하실 수 있습니다.

그리고 mask는 전체 display group 객체에도 apply 될 수 있다는 것을 기억해 두세요.

Removing a mask

객체에서 mask를 제거하고 싶으시면 간단하게 object:setMask(nil)을 해 주시면 됩니다. 또한 변수를 nil로 선언해도 이 mask를 제거하실 수 있습니다.

fruit:setMask( nil )
mask = nil


이 bitmap mask에 대한 또 다른 예제도 보실 수 있습니다. 다운로드 받으신 Corona SDK의 Flashlight 샘플을 한번 보세요. 경로는 /SampleCode/Graphics/Flashlight 폴더 입니다. 이 bitmap mask로 어떤 다른 좋은 효과를 줄 수 있을까요?

반응형

Comment

  1. Nai 2012.06.04 21:10

    이걸로 충돌도 이미지 영역만 할 수 있을까요?

    • 솔웅 2012.06.06 13:36 신고

      글쎄요.... 이걸로 충돌을 이미지 영역에만 적용되도록 해 보지는 않았는데...
      한번 해 볼만 한데요...
      전 이미지 영역만 혹은 특정 이미지 영역만 physics body 를 입히기 위해 physics editor 를 사용했습니다.
      제 글들 중에 physics editor에 관련한 글이 있을 겁니다.
      검색해서 보시면 되요. http://www.codeandweb.com/physicseditor 이곳으로 가서 보셔도 되구요.