[Android] 텍스트에 외곽선 효과주기

안드로이는 2D Drawing API가 상당히 뛰어납니다. 일반 PC에서 제공하는 API 수준.. 그 이상인데요. 안드로이에서 제공하는 2D Drawing API 중에서 텍스트에 외곽선 효과를 주는 코드에 대해 간단히 소개해 드리겠습니다. 결과를 먼저 보여드리면 다음과 같습니다.

사용자 삽입 이미지
위의 시스템은 지오서비스에서 현재 개발중인 지적도 현장 검색 시스템으로.. 항공사진과 지적도를 표현하고 있습니다. 지적도의 특성상 라벨로.. 지번을 텍스트로 표현해야 합니다. 텍스트를 그냥 표현하게 되면 지도와 텍스트가 섞여 라벨이 눈에 잘 들어오지 않게 됩니다. 해서.. 라벨에 외곽선을 그려주게 되면 라벨의 가독성이 향상됩니다. 위의 그림에서처럼 글자는 하얀색으로 하고 외곽선은 검정색으로 하는 코드는 다음과 같습니다.

textPaint = new Paint();
textPaint.setAntiAlias(true);
textPaint.setColor(Color.WHITE);
textPaint.setTextSize(textSize);
  
strokePaint = new Paint();
strokePaint.setAntiAlias(true);       
strokePaint.setTextSize(textSize);       
strokePaint.setColor(Color.BLACK);       
strokePaint.setStyle(Paint.Style.STROKE);       
strokePaint.setStrokeWidth(4);

위의 코드에서 보시는 것처럼.. 하얀색의 글자와 외곽선의 검정색에 대한 Paint 객체를 2개 만듭니다. 즉 글자 자체의 하얀색에 대한 textPaint와 검정색 외곽선에 대한 strokePaint 변수가 바로 그 것입니다. 이렇게 만들어진 Paint 객체를 다음의 텍스트 그리기 함수에서 사용합니다.

String text = "label";

canvas.drawText(text, x, y, strokePaint);
canvas.drawText(text, x, y, textPaint);

텍스트 그리기 한번 그리고 외곽선 그리기 한번.. 이렇게 총 2번을 그려서 텍스트의 외곽선을 표현해 줄 수 있습니다. 외곽선의 굵기를 원하는 만큼 지정할 수 있는데요. 방법은 strokePaint의 setStrokeWidth 매서드에 원하는 굵기만큼 값을 주면 쉽게 원하는 바를 얻을 수 있습니다.

[Android] 3D API, OpenGL ES – 1 : 초기화

안드로이드에서 3차원 그래픽을 위한 API는 OpenGL ES입니다. 꽤 오래전부터 3차원 그래픽 API인 OpenGL에 대해 관심이 많은 저로써는 모바일에서 3D API인 OpenGL ES에 대해서도 관심이 많았습니다. 해서 모바일 OS 중의 하나이면서.. 모바일 OS 중에서 가장 관심이 많은 안드로이드에서 3D API인 OpenGL ES에 대한 글을 체계적으로 남겨 보려고 합니다. 그중 가장 먼저 초기화입니다.

이 글의 대상은 안드로이드에 대한 기본적인 내용(Activity, View의 개념)에 대해 알고 있는 개발자 분입니다. 이클립스에서 새로운 안드로이드 프로젝트를 생성할 수 있으며 애뮬레이터이든.. 가지고 계시는 디바이스에서든.. 실행해 그 결과를 살펴보실수 있는 분에 한합니다. 아울러 OpenGL ES가 토대로 하고 있는 OpenGL API를 알고 있다면 매우 쉽게 이 글을 이해하실 수 있을 것입니다. OpenGL ES를 보시기 전에 먼저 OpenGL을 선행 학습하시면 이글을 훨씬 쉽게 이해할 수 있습니다.

먼저 Android Project를 생성합니다. 나타나는 대화상자에서 입력해야할 곳에 아래 그림을 참조해 입력하시기 바랍니다.

사용자 삽입 이미지
위와 같이 입력한 뒤에 Finish 버튼을 클릭하면 OpenGLES_Tutorial1Activity 라는 클래스가 생성됩니다. 여기서 다음과 같이 필드 변수 하나와 onCreate 매서드의 코드를 수정합니다.

package ogl.tutorial1;

import android.app.Activity;
import android.os.Bundle;

public class OpenGLES_Tutorial1Activity extends Activity {
    private MyView myView;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        myView = new MyView(this);
        
        setContentView(myView);
    }
}

MyView라는 View 파생 클래스를 하나 생성할 것인데.. 생성했다고 가정하고 일단 필드로써 정의해 onCreate에서 생성하고 setContentView로 지정했습니다. 여기까지는 안드로이드에서 일반적인 프로젝트를 생성하고 코딩하는 내용입니다. OpenGL ES의 내용은 아직 언급되지 않았구요. 이제 MyView 클래스 작성에서부터 OpenGL ES가 시작됩니다. MyView라는 새로운 클래스를 생성합니다.

사용자 삽입 이미지
상속받을 Superclass는 android.opengl 패키지의 GLSurfaceView라는 점이 중요합니다. 이 클래스는 View라는 클래스를 상속받는 클래스로 다음과 같은 책임을 가지는 클래스입니다.

  • OpenGL ES와 View 시스템을 연결
  • 서페이스에 OpenGL이 렌더링 될 수 있도록 EGLS Display를 관리함
  • Activity의 라이프 사이클과 함께 OpenGL ES가 작동하도록 함
  • 적당한 프레임버퍼의 픽셀 포맷 선택을 쉽게 해줌
  • 렌더링에 대한 별도의 스레드를 만들어 줌
  • OpenGL ES API 호출과 에러에 대한 검사를 위한 디버깅 도구 지원

MyView 클래스에 하나의 필드 변수를 추가하고 생성자를 다음과 같이 코딩합니다.

public class MyView extends GLSurfaceView {
    private MyRenderer renderer;

    public MyView(Context context) {
        super(context);
        renderer = new MyRenderer();
        setRenderer(renderer);
    }

    ....

MyRenderer라는 앞으로 추가할 또 다른 새로운 클래스에 대한 내부 필드를 선언했고 생성자에서 이 필드를 생성한 후 setRenderer로 렌더러로써 지정했습니다. 새롭게 추가할 MyRenderer라는 클래스를 생성합니다.

사용자 삽입 이미지
구현할 Interface를 지정해야 하는데.. android.opengl.GLServiceView 클래스의 Inner Interface인 Renderer 인터페이스를 추가합니다. Finish를 클릭합니다. 이 Renderer 인터페이스에서 구현해 줘야 하는 매서드는 총 3가지입니다.

  • onSufaceCreated – 초기화 코드 부분으로 렌더링 될때 변경되지 않는 것에 대한 설정 코드가 실행되면 적합하다. 화면을 지울 배경 색이나 z-buffer에 대한 활성화 여부 등등
  • onSurfaceChanged – 화면이 가로로 회전될때 등과 같이 화면의 크기가 변경될때 이와 관련된 코드가 실행되면 적합하다. 예를 들어서 Viewport의 크기 지정이라든지 카메라의 재설정 코드 등등
  • onDrawFrame – 프레임을 그리는 코드가 오면 적합하다.

구현해야할 매서드에 대해 내용을 파악했으니.. 이제 MyRenderer에 대한 코드를 작성해 보겠습니다. 색상에 대한 RGB값을 위해 3개의 float 변수를 추가하고 앞의 3개의 메서드를 구현해 보면 다음과 같습니다.

public class MyRenderer implements Renderer {
    private float red = 0.9f;
    private float green = 0.2f;
    private float blue = 0.2f;

    @Override
    public void onDrawFrame(GL10 gl) {
        gl.glClearColor(red, green, blue, 1.0f);
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        gl.glViewport(0, 0, width, height);
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        //.
    }

일단.. 이해를 쉽게 하기 위해 단순히 화면을 red, green, blue 필드값으로 지정된 색상으로 지우는 것이 그리는 것의 전부입니다. 즉 onDrawFrame에서는 배경을 Clear할 색상을 지정하기 위해 glClearColor 매서드를 사용했고 실제 지우는 함수는 glClear 입니다. 그리고 onSurfaceChanged에서는 뷰포트의 크기를 지정하는 glViewport를 호출하고 있습니다. onSurfaceCreated에서는 특별하 무엇가를 그리는 것이 없기 때문에 별다른 코드는 존재하지 않습니다. 하지만 무언가 3D 오브젝트를 그릴때 이 부분에 코드가 필요할 것입니다. 실행해 보면 다음과 같은 결과가 나타나게 됩니다.

사용자 삽입 이미지
여기에 잠시 응용을 해보겠습니다.. 사용자가 화면에 손을 대고 스크롤하면 색상이 스크롤한 내용에 따라 변경되게 말입니다. 색상에 대한 값이 MyRenderer에 정의되어 있고.. 사용자의 스크롤에 대한 이벤트는 MyView에서 발생하니.. MyRenderer의 색상값을 변경할 수 있는 매서드를 MyRenderer에 추가해해 줘야 합니다.

public void setColor(float r, float g, float b) {
    red = r;
    green = g;
    blue = b;
}

이제 MyView에 터치 이벤트를 작성합니다.

public boolean onTouchEvent(final MotionEvent event) {
    queueEvent(new Runnable() {
        public void run() {
            renderer.setColor(
                event.getX()/getWidth(), event.getY()/getHeight(), 1.0f
            );
        }
    });
  
    return true;
}

네.. 화면에 손을 터치하면 터치된 좌표값을 통해 색상값을 재설정합니다. OpenGL에서 색상값은 0~1.0까지이므로 이 값의 범위에 맞춰주고 있습니다. 실행해서 손을 터치하거나 스크롤해보면 색상값이 그에 따라 변하는 것을 살펴볼 수 있습니다.

[GIS] BlackPoint, 항공영상 레이어 지원

현재 지오서비스에서 개발중인 안드로이드 기반의 GIS 솔루션인 블랙포인트에서 대용량의 항공영상 파일을 빠르게 화면상에 표시할 수 있는 기능이 추가되었습니다. 블랙포인트는 모바일 GIS 엔진으로 현장 중심의 데이터 편집 시스템 개발을 주목적으로 합니다. 네트워크가 되지 않은 현장을 지원하기 위해 SHP, 항공영상 등을 SD 메모리에 담아 어디서든 화면상에 표시하고 GPS를 통해 현재의 위치를 쉽게 파악하여 시설물등에 대한 도형 데이터를 생성하여 SHP 파일등으로 생성할 수 있습니다.

사용자 삽입 이미지
블랙포인트의 항공영상 표시 기능은 다음과 같은 장점을 갖습니다.

  1. 메모리를 거의 사용하지 않아 모바일 디바이스의 리소스 제약에 영향을 전혀 받지 않는다.
  2. 항공영상 파일을 로딩할때 준비하는 과정 없이 바로 화면에 표시된다.
  3. 일반 PC에서 항공영상을 표시하는 것처럼 매우 빠르다.

다음은 블랙포인트에서 SHP 파일을 표시하는 기능에 대한 화면입니다. 블랙포인트에서 SHP 파일을 표시하는데 사용하는 SHP 라이브러리는 지오서비스에서 개발하여 오픈소스로 제공하고 있는 SimpleSHP 라이브러리(SimpleSHP API)를 사용하였습니다.

사용자 삽입 이미지

ActionScript 코드 최적화 항목

이번 핑거아이즈의 속도 개선을 위한 전반적인 코드 리팩토링 시에 고려했던 최적화 항목입니다. 위의 항목중 2/3정도만 반영되었고.. 나머지는 반영하지 못했습니다. 코드 최적화 작업 시간을 따로 마련해 놓지 말고.. 코드 작성 시에 미리 미리 고려해 적용해 놓아야한다는 당연한 진리를 새삼 깨닫게 되었습니다.

  1. 가능하다면 Sprite 대신에 Shape 사용하기
  2. cacheAsBitmap 적절히 사용하기
  3. 라벨에 대해 TextField 보다는 FTE(Flash Text Engine) 사용하기
  4. 가능하다면 mouseChildren, mouseEnable을 false 지정하기
  5. Array 대신 Vector. 사용하기
  6. 드로잉 API(drawPath, drawGraphicsData, drawTriangles) 사용하기
  7. object cache 전략 사용하기
  8. 폴리곤 내부 판정은 hitTestPoint보다 직접 구현해 사용하기
  9. 파생될 일이 없는 클래스는 final로 지정하기
  10. floor, ceil은 int 형변환으로 대체하기(floor(1.5) -> int(1.5), ceil(1.5) -> int(1.5)+1)
  11. abs보다는 직접 -1을 곱하는 방식으로 처리하기
  12. for 반복문 대신 for each 문 사용하기

여하튼… 위의 최적화 코드등을 통해 기존 속도 대비 10%정도 향상된듯합니다만… 체감하기는 어려운 향상인지라 다소 아쉬움이 많이 남습니다.