[JAVA] BitBlt 예제 (자바 BitBlt 예제)
자바에서 BitBlt 함수 사용하는 방법이다.
아무리 생각해도 나중에 까먹을 것 같아 잠을 아껴 여기 써둔다.
BitBlt 는 윈도우 OS 에서 제공하는 함수로, 화면의 고속복사를 위한 함수다.
단순히 빠른 속도로 화면에 그래픽을 찍어낼 수 있다는 이유 하나로, 어렸을 적 VB로 게임 만들 때 종종 사용하곤 했다.
필요한 라이브러리는 2개다.
(1) jna-4.5.0.jar
(2) jna-platform-4.5.0.jar
참고로 JNA란 Java Native Access의 약자로 자바에서 윈도우 네이티브 함수(ex : Win32 API)에 쉽게 접근하도록 돕는 라이브러리이다.
자바 자체가 크로스 플랫폼 언어인지라 이러한 라이브러리의 도움을 받지 않으면 윈도우 함수에 접근할 수가 없다.
BitBlt 함수를 자세히 설명하면, 윈도우의 DC(Device Context) 영역끼리 고속 복사를 수행하는 함수다.
참고로 DC란 Device Context의 약자이고, 윈도우에서 정의한 구조체로, 화면 출력에 대한 구조체라고 한다.
굳이 자바에서 BitBlt 사용하는 법을 찾아낸 것은, 자바로 1초에 수십번(60 FPS ~ 100 FPS) 여러 개의 이미지를 찍어내면 느리기 때문이다.
그 이유는 네이티브 자바가 느리기 때문이 아니라, 정확히는 자바 JFrame 에 BufferdImage 를 그리는 로직이 게임 프로그래밍에 알맞지 않기 때문이다.
자바의 JFrame 은 자체적으로 더블 버퍼링이 적용되어 있으므로 특히 그림 여러 개를 반복 출력할 경우 느리고, 운영체제 관계없이 동작하기 때문에 직접 윈도우 API를 사용하는 것보다 상대적으로 느리다.
1. 예제 코드 작성
우선 아래 코드를 작성한다. MainClass.java 와 WinGDIConf.java 를 작성한다.
1-1. MainClass.java
|
package com.thkmon.gamelib.main;
import java.util.HashMap;
import com.sun.jna.Native; import com.sun.jna.Pointer; import com.sun.jna.platform.win32.GDI32; import com.sun.jna.platform.win32.User32; import com.sun.jna.platform.win32.WinDef.HDC; import com.sun.jna.platform.win32.WinDef.HWND; import com.sun.jna.platform.win32.WinDef.RECT; import com.sun.jna.platform.win32.WinUser.WNDENUMPROC; import com.thkmon.gamelib.prototype.WinGDIConf;
public class MainClass {
public static void main(String[] args) {
try { HashMap<String, HWND> map = getHWNDMap(); // 그림판 핸들 윈도우(HWND) 가져오기 (미리 그림판을 열어둬야 함) HWND paintHWND = map.get(“MSPaintApp”); if (paintHWND == null) { throw new Exception(“그림판을 켠 후 다시 시도해주세요.”); } // 메모장 핸들 윈도우(HWND) 가져오기 (미리 메모장을 열어둬야 함) HWND notepadHWND = map.get(“Notepad”); if (notepadHWND == null) { throw new Exception(“메모장을 켠 후 다시 시도해주세요.”); } HDC paintHDC = User32.INSTANCE.GetDC(paintHWND); HDC notepadHDC = User32.INSTANCE.GetDC(notepadHWND); // 그림판 300, 300 위치에 메모장의 0~200, 0~100 영역을 고속복사 GDI32.INSTANCE.BitBlt(paintHDC, 300, 300, 200, 100, notepadHDC, 0, 0, WinGDIConf.SRCCOPY); } catch (Exception e) { e.printStackTrace(); } } /** * 윈도우 클래스 네임 문자열(String)을 put 하면 윈도우 핸들 윈도우(HWND)를 리턴하는 HashMap. * HashMap에는 첫번째 발견된 핸들 윈도우(HWND)만 저장하며, 클래스 네임이 중복될 경우 무시한다. * @return */ public static HashMap<String, HWND> getHWNDMap() {
final HashMap<String, HWND> hwndMap = new HashMap<String, HWND>();
User32.INSTANCE.EnumWindows(new WNDENUMPROC() { int count = 0;
public boolean callback(HWND hWnd, Pointer arg1) { char[] windowText = new char[512]; User32.INSTANCE.GetWindowText(hWnd, windowText, 512); String wText = Native.toString(windowText); RECT rectangle = new RECT(); User32.INSTANCE.GetWindowRect(hWnd, rectangle); if (wText.isEmpty() || !(User32.INSTANCE.IsWindowVisible(hWnd) && rectangle.left > -32000)) { return true; }
String clsName = getClassNameFromHandle(hWnd); if (clsName != null && clsName.length() > 0) {
StringBuffer buff = new StringBuffer();
buff.append(“번호:” + (++count)); buff.append(“,텍스트:” + wText); buff.append(“,” + “위치:(“); buff.append(rectangle.left + “,” + rectangle.top + “)~(“); buff.append(rectangle.right + “,” + rectangle.bottom); buff.append(“),” + “클래스네임:” + clsName);
System.out.println(buff.toString());
// HashMap에는 첫번째 발견된 핸들 윈도우(HWND)만 저장하며, 클래스 네임이 중복될 경우 무시한다. if (hwndMap.get(clsName) == null) { hwndMap.put(clsName, hWnd); } }
return true; } }, null);
return hwndMap; } /** * 핸들 윈도우(HWND)의 클래스 네임을 얻는다. * * @param hWnd * @return */ public static String getClassNameFromHandle(HWND hWnd) { if (hWnd == null) { return “”; }
// 핸들의 클래스 네임 얻기 char[] c = new char[512]; User32.INSTANCE.GetClassName(hWnd, c, 512); String clsName = String.valueOf(c).trim(); return clsName; } }
|
1-2. WinGDIConf.java
BitBlt 관련 상수들을 모아둔 인터페이스. MainClass에서 해당 인터페이스의 상수를 BitBlt 아규먼트에 활용할 수 있다.
|
package com.thkmon.gamelib.prototype;
public interface WinGDIConf { // SRCCOPY : 복사한다. public Integer SRCCOPY = new Integer(0x00CC0020); public Integer SRCPAINT = new Integer(0x00ee0086); public Integer SRCAND = new Integer(0x008800c6); public Integer SRCINVERT = new Integer(0x00660046); public Integer SRCERASE = new Integer(0x00440328);
public Integer NOTSRCCOPY = new Integer(0x00330008); public Integer NOTSRCERASE = new Integer(0x001100a6); // MERGECOPY : 비트맵을 AND 연산한다. public Integer MERGECOPY = new Integer(0x00c000ca); // MERGEPAINT : 비트맵을 OR 연산한다. public Integer MERGEPAINT = new Integer(0x00bb0226);
public Integer PATCOPY = new Integer(0x00f00021); public Integer PATPAINT = new Integer(0x00fb0a09); public Integer PATINVERT = new Integer(0x005a0049); // DSTINVERT : 화면을 반전시킨다. public Integer DSTINVERT = new Integer(0x00550009); // WHITENESS : 대상영역을 흰색으로 채운다. public Integer WHITENESS = new Integer(0x00ff0062); // BLACKNESS : 대상영역을 검정색으로 채운다. public Integer BLACKNESS = new Integer(0x00000042); public Integer CAPTUREBLT = new Integer(0x00CC0020 | 0x40000000); public Integer Black = new Integer(0x00000000);
public long WS_CHILD = 0x40000000L; public long WS_VISIBLE = 0x10000000L; public long MS_SHOWMAGNIFIEDCURSOR = 0x0001L;
public long WS_EX_TOPMOST = 0x00000008L; public long WS_EX_LAYERED = 0x00080000; public long WS_EX_TRANSPARENT = 0x00000020L;
public long WS_CLIPCHILDREN = 0x02000000L;
public long MW_FILTERMODE_EXCLUDE = 0; }
|
참고로, 프로그램 실행시 getHWNDMap 메서드에서, 핸들 윈도우(HWND) 정보를 콘솔에 출력하는 코드가 있다.
그림판이 열려 있다면 MSPaintApp 라는 문자열이, 메모장이 열려 있으면 Notepad 라는 문자열이 콘솔에 찍힐 것이다.
2. 예제 실행
예제 프로그램을 실행한다. 단, 그림판 1개와 메모장 1개를 열어서 화면에 가리지 않도록 위치시킨 후 실행한다.

3. 예제 실행 결과
그림판 300, 300 위치에 메모장의 0~200, 0~100 영역을 성공적으로 고속복사했다.
참고로 BitBlt 로 그려진 이미지는 해당 핸들 윈도우(HWND)를 움직이거나 최소화할 경우 곧바로 사라진다. 그만큼 가볍고 빠르게 화면을 복사하기에 유용한 함수이다.

아래 사이트를 참고하였다.
참고사이트) http://soen.kr/lecture/win32api/lec6/lec6-4-3.htm