달력

102021  이전 다음

  •  
  •  
  •  
  •  
  •  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  •  
  •  
  •  
  •  
  •  
  •  
Core Java Technologies Technical Tips
 Core Java Technologies Tech Tip에 오신 여러분을 환영합니다
Core Java Technologies
TECHNICAL TIPS
2006년 5월 12일자
 
<5월 개발자 이벤트>

썬 자바 스튜디오 크리에이터 무료 체험 및 웍스테이션 파격가 한정 판매

   설문에 참여하시고 PSP, 아이팟 등 푸짐한 선물을 받아가세요!!!
   자세한 내용 보기 »

5월의 Sun Technology Day - University Sun Day

   일시: 2006년 5월 23일(화) 10:00-14:00
   장소: 서울 대학교 제2공학관(302동) 105호
   주제: 자바의 현재와 미래, 오픈 솔라리스, 코드 캠프
   자세한 내용 보기 »

  이번 호에서는,

» 빈에서 Indexed Property 변경 사항 보고하기
» J2SE 5.0에서의 Java 2D API 기능 강화

에 대해 다룹니다.

이 문서는 Java 2 Platform, Standard Edition Development Kit 5.0 (JDK 5.0)을 기반으로 개발되었습니다. PDF 파일받기    

빈에서 Indexed Property 변경 사항 보고하기
 

J2SE 5.0의 JavaBeans 컴포넌트 API에 여러 다양한 기능들이 추가되었는데, IndexedPropertyChangeEvent에 대한 지원도 그 중 하나이다. JavaBeans 컴포넌트 API는 JavaBeans 컴포넌트(JavaBean 또는 그냥 '빈'이라고도 한다)의 Regular Property에 대한 변경 내용을 보고하는 수단을 제공했었는데, IndexedPropertyChangeEvent에 대한 지원으로 빈의 Indexed Property 변경에 관한 추가 정보를 보고하는 기능이 부가되었다.

대부분의 개발자들은 JavaBean 컴포넌트 속성에 관해 익숙한 편이다. 즉, 각자의 클래스에 set과 get 메소드를 간단히 추가하기만 하면 set과 get 다음의 이름에 의해 정의되는 읽기/쓰기 속성을 설정할 수 있다. 다시 말해, 클래스가 setName()getName()으로 명명된 메소드를 가지고 있다면 이 클래스는 name이라는 이름의 JavaBean 컴포넌트 속성을 가지게 된다.

컴포넌트 속성에는 Regular와 Indexed의 두 종류가 있는데, Indexed Property는 각각의 값이 Index에 의해 액세스되는 복수의 값을 가진다는 점에서 Regular Property와 구분된다. 한편 name과 같은 Regular Property를 위한 set과 get 메소드는 다음과 같은 형태를 띠게 된다.

Regular Property:
  • public void setName(String name)
  • public String getName()
name이 Indexed Property인 경우에 메소드는 다음과 같다.

Indexed Property:
  • public void setName(int index, String name)
  • public String getName(int index)
  • public void setName(String[] names)
  • public String[] getName()
빈은 속성값의 변경사항이 사용자에게 통지되도록 설계될 수 있으며, addPropertyChangeListener() 메소드를 이용하여 PropertyChangeListener 오브젝트를 빈에 등록할 수도 있다. 이 경우 PropertyChangeEvent 오브젝트나 IndexedPropertyChangeEvent 오브젝트를 통해 리스너에 변경 사항이 통지된다. 값 변경 시 PropertyChangeEvent를 생성하는 속성을 Bound Property라고 한다.

PropertyChangeListener 클래스는 다음과 같이 하나의 메소드를 가진다.
public void propertyChange(PropertyChangeEvent pce)그렇다면 propertyChange()에 대한 인자가 PropertyChangeEvent 타입인 경우에는 IndexedPropertyChangeEvent를 어떻게 통지받게 되는가? 그 대답은 IndexedPropertyChangeEvent 클래스가 PropertyChangeEvent의 서브클래스라는 사실에서 찾아볼 수 있다. 따라서, propertyChange()의 내부에서는 어떤 타입의 인자를 얻는지 확인하기 위한 instanceof 체크가 필요하다.

public void propertyChange(PropertyChangeEvent pce) { String name = pce.getPropertyName(); if (pce instanceof IndexedPropertyChangeEvent) { IndexedPropertyChangeEvent ipce = (IndexedPropertyChangeEvent) pce; int index = ipce.getIndex(); System.out.println("Property: " + name + "; index: " + index); } else { System.out.println("Property: " + name); } System.out.println("; value: " + pce.getNewValue()); }IndexedPropertyChangeEvent를 사용하는 예제 프로그램을 살펴보기에 앞서 Bound Indexed Property에 대한 변경을 보고하는 방법에 초점을 맞추어 보도록 하자. 다음은 앞서 살펴본 Name Indexed Propterty의 업데이트를 보고하는 코드이다.

private PropertyChangeSupport changeSupport; public ClassConstructor() { changeSupport = new PropertyChangeSupport(this); } public void setName(int index, String name) { String oldName = this.name; this.name = name; changeSupport.fireIndexedPropertyChange("name", index, oldName, name); }PropertyChangeSupport 클래스는 java.beans 패키지(JavaBeans 컴포넌트 API 패키지)에 들어 있는 지원 클래스이고, fireIndexedPropertyChange() 메소드는 Bound Indexed Property(이 경우에는 name)에 대한 변경을 addPropertyChangeListener() 메소드를 통해 등록된 모든 리스너에게 보고한다.

다음은 전체 예제의 내용이다.
import java.beans.*; import java.util.*; public class IndexedSampleBean { private PropertyChangeSupport changeSupport; private Map<Integer, String> names; private String title; public IndexedSampleBean() { changeSupport = new PropertyChangeSupport(this); names = new HashMap<Integer, String>(); } public void setTitle(String title) { String oldTitle = this.title; this.title = title; changeSupport.firePropertyChange("title", oldTitle, title); } public String getTitle() { return title; } public void setName(int index, String name) { String oldName = names.get(index); names.put(index, name); changeSupport.fireIndexedPropertyChange("name", index, oldName, name); } public String getName(int index) { return names.get(index); } public void addPropertyChangeListener( PropertyChangeListener l) { changeSupport.addPropertyChangeListener(l); } public void removePropertyChangeListener( PropertyChangeListener l) { changeSupport.removePropertyChangeListener(l); } public static void main(String[] args) throws Exception { IndexedSampleBean bean = new IndexedSampleBean(); PropertyChangeListener listener = new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent pce) { String name = pce.getPropertyName(); if (pce instanceof IndexedPropertyChangeEvent) { IndexedPropertyChangeEvent ipce = (IndexedPropertyChangeEvent) pce; int index = ipce.getIndex(); System.out.print("Property: " + name + "; index: " + index); } else { System.out.print("Property: " + name); } System.out.println("; value: " + pce.getNewValue()); } }; bean.addPropertyChangeListener(listener); bean.setName(1, "John"); bean.setName(2, "Ed"); bean.setName(3, "Mary"); bean.setName(4, "Joan"); bean.setTitle("Captain"); System.out.println("Name at 3 is: " + bean.getName(3)); System.out.println("Title is: " + bean.getTitle()); } }클래스는 name으로 명명된 Indexed Property를 정의하고 4개의 위치에서 name을 설정한다. 이와 함께 title로 명명된 Regular Bound Property 역시 생성되어 설정된다. 리스너는 이름이나 타이틀을 설정하고 이어서 속성 이름, 인덱스(해당될 경우), 값을 프린트하기 위한 각 호출을 통지받는다. 그러면 클래스는 특정 위치에서 이름을 얻고 단일 타이틀을 프린트하기 전에 이를 먼저 프린트한다.

프로그램을 실행하면 다음과 같은 결과가 나온다.
>java IndexedSample Property: name index: 1 value: John Property: name index: 2 value: Ed Property: name index: 3 value: Mary Property: name index: 4 value: Joan Property: title; value: Captain Name at 3 is: Mary Title is: CaptainJavaBeans component API에서의 IndexedPropertyChangeEvent 지원에 관한 자세한 내용은 J2SE 5.0의 JavaBeans Component API에 대한 API 강화를 참조하기 바란다. 자바 튜토리얼의 JavaBeans Trail도 함께 참조할 것.

맨위로

J2SE 5.0의 Java 2D API 기능 강화
 

자바 플랫폼 스탠다드 에디션의 버전이 새로 나올 때마다 크고 작게 기능이 개선되었다. 대부분의 사람들은 Generics나 새로운 병행 유틸리티(Concurrency Utilities) 패키지처럼 중요한 신기능에 관해서는 충분히 들어 내용을 잘 알고 있는 반면 자잘한 신기능들에 관한 정보를 입수할 수 있는 기회는 그다지 흔하지 않다. 또한 이런 기능들은 중요한 특성에 비해 소수의 사용자에게 유용하기 때문에 자주 언급되지도 않는다. 따라서 본 팁에서는 J2SE 5.0에 추가된 작지만 새로 강화된 Java 2D API 신기능들을 몇 가지 소개하고자 한다. 이러한 신기능은 각자의 운영체제와 하드웨어에 따라 실제 성능이 좌우되는 경우가 많으며, 경우에 따라서는 이러한 기능을 사용하는 것이 별 효과가 없을 수도 있다.

Bicubic Interpolation

Java 2D API에서 강화된 기능 중 하나는 이미지 스케일링에 관한 것으로, 회전을 비롯한 어파인 변형(Affine Transformation)에도 영향을 미친다. RenderingHints 클래스는 다양한 보간법(interpolation) 옵션을 처리하는 다음 2개의 상수를 가진다: VALUE_INTERPOLATION_BILINEARVALUE_INTERPOLATION_BICUBIC. KEY_INTERPOLATION 힌트를 2개 옵션 중 하나로 설정하면 기본 시스템이 이미지를 스케일링할 때 따라야 할 규칙이 지정된다. 한편, J2SE 5.0 이전까지는 2개의 힌트 값이 동일한 결과를 산출했다. RenderingHints는 단순한 힌트일 뿐이므로 무시해도 상관 없는데, 실제로 Bicubic 설정은 항상 무시되어 왔고, 대신 Bilinear Interpolation이 사용되었다.

2개의 보간법 옵션에 익숙치 않을 경우에는 이미지를 스케일업할 때 어떤 일이 벌어지는지 이해하면 상당한 도움이 된다. 예를 들어, 스케일업된 이미지의 새로운 픽셀들은 모두 어디서 나오는 것일까? Bilinear 스케일링의 경우 픽셀은 스케일된 픽셀에 가장 가까운 소스 이미지의 2x2 픽셀 직사각형에서 공급된다. 픽셀 직사격형은 X와 Y의 선형 함수를 이용하여 섞이게 되는데, Bicubic 스케일링의 경우 이들은 스케일된 픽셀을 둘러싼 4x4 면적에서 공급된다. 한편, 픽셀 직사각형은 X와 Y의 3차 함수를 이용하여 섞이게 된다. Bicubic 스케일링을 이용할 경우, 증가된 면적과 알고리즘의 복잡성으로 인해 스케일된 이미지의 품질을 향상되지만 대신 성능이 저하된다.

J2SE 5.0에서는 Bicubic 설정이 상당히 중시되는데, 다음의 테스트 프로그램을 실행해보면 이 사실을 알 수 있다. 프로그램을 실행할 때 이미지 파일을 지정한다. 프로그램은 RenderingHints를 2개의 상수 VALUE_INTERPOLATION_BILINEARVALUE_INTERPOLATION_BICUBIC으로 설정하고 보간법 알고리즘의 차이(있을 경우)를 보고한다. 먼저 JDK 1.4로 프로그램을 실행한 다음 JDK 5.0로 실행한다. 보간법 알고리즘의 차이는 신 버전의 JDK에서만 보고된다는 것을 알 수 있다.
import java.awt.*; import java.awt.image.*; import java.io.*; import javax.swing.*; import javax.imageio.*; public class Bicubic { public static void main(String args[]) throws IOException { if (args.length == 0) { System.err.println( "Provide image name on command line"); System.exit(-1); } Image image = ImageIO.read(new File(args[0])); int w = image.getWidth(null); int h = image.getHeight(null); BufferedImage bilinear = new BufferedImage(2*w, 2*h, BufferedImage.TYPE_INT_RGB); BufferedImage bicubic = new BufferedImage(2*w, 2*h, BufferedImage.TYPE_INT_RGB); Graphics2D bg = bilinear.createGraphics(); bg.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); bg.scale(2, 2); bg.drawImage(image, 0, 0, null); bg.dispose(); bg = bicubic.createGraphics(); bg.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); bg.scale(2, 2); bg.drawImage(image, 0, 0, null); bg.dispose(); for(int i=0; i<2*w; i++) for(int j=0; j<2*h; j++) if (bilinear.getRGB(i, j) != bicubic.getRGB(i, j)) System.out.println("Interpolation algo differ"); } } JDK 1.4 환경에서는 프로그램을 실행해도 아웃풋이 생성되지 않는다.
> java Bicubic image.jpg [No Output] JDK 1.5 환경에서는 차이가 있는 모든 픽셀이 보고된다.
> java Bicubic image.jpg Interpolation algo differ Interpolation algo differ Interpolation algo differ Interpolation algo differ Interpolation algo differ .... repeated many timesOpenGL 가속

2005년 3월 30일자 테크 팁 JOGL 개요에서는 OpenGL 3D 그래픽 API를 위한 자바 프로그래밍 언어 바인딩에 관해 설명한 바 있다. J2SE 5.0에는 2D API의 핵심 기능을 가속화하는 데 이용할 수 있는 몇 가지 GL 옵션이 포함되어 있는데, 이 옵션들은 '시트 밑에' 감추어져 있으므로 사용자는 OpenGL로 프로그래밍하는 방법을 알 필요가 없다. 가속 옵션은 기본적으로 "사용하지 않음" 상태로 설정되어 있고, 옵션을 사용하려면 sun.java2d.opengl 시스템 속성을 true로 설정하면 된다. OpenGL 기반 파이프라인과 관련하여 전송된 아웃풋을 verbose하고 싶으면 t 대신 True(대문자T) 값을 이용하고, 결과를 보려면 JDK에 포함된 Java 2D 데모 코드를 이용하면 된다.
java -Dsun.java2d.opengl=True -jar Java2Demo.jarOpenGL 기반의 파이프라인에 관한 자세한 내용은 Behind the Graphics2D: The OpenGL-based Pipeline을 참조할 것.

폰트 생성하기

J2SE 5.0 이전에는 Font 클래스를 이용하여 createFont() 메소드를 통해 InputStream에서 트루타입 폰트를 생성할 수 있었다.
public static Font createFont(int fontFormat, InputStream fontStream)이 때, fontFormatFont.TRUETYPE_FONT이다. J2SE 5.0의 경우 이 지원 항목에서 두 가지의 변경 사항이 있는데, 첫째는 Adobe Type 1 폰트를 생성할 수 있고, 두번째는 새로운 createFont() 메소드 시그니처를 통해 File 오브젝트에서 직접 폰트를 생성할 수 있다는 점이다.
public static Font createFont(int fontFormat, File fontFile)단순히 폰트를 설치하고자 한다면, 즉 어느 메소드로도 폰트를 생성할 생각이 없다면, $JREHOME/lib/fonts 디렉토리에 폰트 파일을 복사하면 된다. 그러면 폰트는 해당 디렉토리에 설치될 때 런타임 환경에 의해 자동으로 픽업된다.

이미지 가속(Acceleration)

Java 2D의 새로운 특징 중에서 마지막으로 살펴볼 내용은 이미지 가속에 관한 것이다. 이 기능들은 각 시스템의 기본 지원 여부에 의해 좌우되는데, 시스템이 이 기능들을 지원하지 않는다면 단순히 무시되어 버린다.

첫 번째 이미지 가속 기능은 버퍼된 이미지 캐싱인데, 비디오 메모리가 관리 이미지의 캐싱을 지원한다. 일반적으로, 관리 가능한 종류의 이미지를 사용하는 프로그램의 경우 관리되지 않는 이미지를 사용하는 프로그램에 비해 성능이 뛰어난 편이다. J2SE 5.0 이전에는 ComponentcreateImage() 메소드 또는 GraphicsConfigurationcreateCompatibleImage() 메소드를 통해 생성된 이미지만 Java 2D 구현에 의해 관리되었지만 이제는 BufferedImage 생성자 중 하나로 생성된 모든 이미지를 Java 2D 구현을 통해서도 관리할 수 있게 되었다.

또 하나의 새로운 이미지 가속 기능으로 이미지의 하드웨어 가속을 제어하는 능력을 들 수 있다. 하지만 J2SE 5.0에서는 이미지의 하드웨어 가속을 제어하는 데 사용되는 메소드가 완전하게 작동하지 않는다는 점에 유의할 필요가 있다. 자세한 내용은 이미지의 하드웨어 가속 제어를 위한 메소드를 참조하기 바란다.

이 지원의 가용성 여부는 이미지 컨텐트의 플랫폼과 타입에 의해 좌우되며, Microsoft Windows 플랫폼 상에서 결과를 보려면 경우에 따라 sun.java2d.translaccel 시스템 속성을 true로 설정해 주어야 한다. 플래그에 관한 자세한 내용을 보려면 Microsoft Windows 플랫폼을 위한 시스템 속성translaccel에 관한 설명을 참조할 것.

자바 프로그램 작성 시, 특정 이미지 연산을 최적화하기를 원한다는 것을 기본 시스템에 알려줄 수 있다. 이 작업은 시스템이 가속화된 메모리에 특정 이미지를 저장하는 방식의 우선순위 정함으로써 가능한데(가능한 경우 ), 여기서 '가능한 경우'라는 단서가 중요하다. J2SE 5.0 이전에는 VolatileImage 오브젝트가 가속화된 메모리에 저장되도록 요청할 수 있었는데, J2SE 5.0에서는 추가로 레귤러 Image 오브젝트를 그곳에 배치하도록 요청할 수 있게 되었다. 또한, 투명한 VolatileImage 오브젝트를 생성할 수도 있다 (이전에는 불투명한 오브젝트 생성만 가능했음).

getCapabilities() 메소드는 이제 VolatileImage로만 정의되는 것이 아니라 ImageVolatileImage 모두에 의해 정의되며, 이 메소드는 특정 이미지가 현재 가속화되고 있는지 확인하는 데 이용될 수도 있다. 또한 setAccelerationPriority() 메소드로 가속 우선순위를 지정하거나 getAccelerationPriority() 메소드로 우선순위 설정을 확인할 수 있다. 가속 우선순위가 0으로 되어 있는 경우에는 특정 이미지의 가속화 기능은 사용되지 않는다. 하지만 우선순위 설정은 단지 힌트일 뿐이며, JDK 구현은 적절한 판단 기준에 따라 이를 중시(또는 무시)할 수 있다.

투명한 VolatileImage 오브젝트를 생성하려면 두 가지의 새로운 createCompatibleVolatileImage() 메소드 중 하나를 이용해야 한다.
  • createCompatibleVolatileImage( int width, int height, int transparency)
  • createCompatibleVolatileImage( int width, int height, ImageCapabilities caps, int transparency)
요약

자바 플랫폼이 계속 발전함에 따라, 크게 주목을 끄는 주요 핵심 기능 이외의 사소한 특성들에 대해서도 관심을 가질 필요가 있다. 실례로, 솔라리스 및 Linux를 위한 CUPS(Common Unix Printer System) 프린터 지원 같은 작은 기능들이 소리 소문 없이 추가되었는데, 만약 사용자가 CUPS 프린터를 사용하고 있다면 이 새로운 기능이 상당히 중요한 역할을 하게 될 것이다. 따라서 평소에 원하던 기능이 새로 추가되지는 않았는지 릴리즈 노트를 잘 살펴보기 바란다. 아울러, J2SE 5.0의 새로운 특징과 강화된 기능에 대한 설명도 함께 참조할 것.

맨위로

본 메일은 수신을 동의한 회원님에게만 발송됩니다.
본 메일의 수신을 거부하거나 수신주소를 변경하려면 SKDN@Sun.com으로 문의 주시기 바랍니다.

SKDN(Sun Korea Developers Network)에서 J2EE/J2SE 테크팁 등 다양한 아티클들을 참고하세요.

Copyright 2003-2006 Sun Korea, Ltd. All rights reserved.


Posted by Tornado tornado

댓글을 달아 주세요