/* gnu_java_awt_peer_wce_WCEGraphics2D.c
   Copyright (C) 2006 Free Software Foundation, Inc.

This file is part of GNU Classpath.

GNU Classpath is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.

GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA.

Linking this library statically or dynamically with other modules is
making a combined work based on this library.  Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.

As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module.  An independent module is a module which is not derived from
or based on this library.  If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so.  If you do not wish to do so, delete this
exception statement from your version. */

#include <windows.h>
#include <jni.h>
#include <assert.h>
#include "wce-peer.h"
#include <gnu_java_awt_peer_wce_WCEGraphics2D.h>

#define BufferedImage_TYPE_CUSTOM			0 
#define BufferedImage_TYPE_INT_RGB			1 
#define BufferedImage_TYPE_INT_ARGB			2 
#define BufferedImage_TYPE_INT_ARGB_PRE		3 
#define BufferedImage_TYPE_INT_BGR			4 
#define BufferedImage_TYPE_3BYTE_BGR		5 
#define BufferedImage_TYPE_4BYTE_ABGR		6 
#define BufferedImage_TYPE_4BYTE_ABGR_PRE	7 
#define BufferedImage_TYPE_USHORT_565_RGB	8 
#define BufferedImage_TYPE_USHORT_555_RGB	9 
#define BufferedImage_TYPE_BYTE_GRAY		10 
#define BufferedImage_TYPE_USHORT_GRAY		11 
#define BufferedImage_TYPE_BYTE_BINARY		12 
#define BufferedImage_TYPE_BYTE_INDEXED		13 

/**
 * グラデーション情報管理用構造体
 */
typedef struct gradient_paint_t {
	int x1;
	int y1;
	COLORREF c1;
	int x2;
	int y2;
	COLORREF c2;
	BOOL cyclic;
} gradient_paint;

/**
 * TexturePaint情報管理用構造体
 */
typedef struct texture_paint_t {
	/**
	 * パターン用ビットマップ
	 */
	HBITMAP hBitmap;
	int bitmapWidth;
	int bitmapHeight;

	/**
	 * アンカー領域
	 */
	int anchorX;
	int anchorY;
	int anchorWidth;
	int anchorHeight;
} texture_paint;


typedef enum paint_info_type_t {
	
	PAINT_TYPE_UNDEFINED = 0,		// 未定義
	PAINT_TYPE_COLOR,				// Color
	PAINT_TYPE_GRADIENT_PAINT,		// GradientPaint
	PAINT_TYPE_TEXTURE_PAINT,		// TexturePaint
	
	PAINT_TYPE_PAINT,				// 上記以外のPaint派生クラス
} paint_info_type;

/**
 * Paint情報管理用構造体
 */
typedef struct paint_info_t {
	/**
	 * Paintの種別フラグ
	 */
	paint_info_type	type;

	union {
		/**
		 * Colorクラスに対応する情報
		 */
		COLORREF	color;

		/**
		 * GradientPaintクラスに対応する情報
		 */
		gradient_paint	gradient;

		/**
		 * TexturePaintクラスに対応する情報
		 */
		texture_paint	texture;

		/**
		 * その他のPaint派生クラス
		 */
		jobject paint_object;
	};
} paint_info;

/**
 * Stroke情報管理用構造体
 */
typedef struct storoke_info_t {
	/**
	 * 幅
	 */
	int width;

	// ToDo: その他の情報
} stroke_info;

/**
 * ネイティブデータ。
 * Graphics2Dオブジェクトに関連する情報を保持しておく
 */
typedef struct native_graphics_t {
	/**
	 * ウインドウハンドル
	 */
	HWND hWnd;

	/**
	 * デバイスコンテキスト本体
	 */
	HDC hDC;

	/**
	 * 描画対象となるビットマップ
	 * スクリーンに描画する場合はNULL
	 */
	HBITMAP	hBitmap;

	/**
	 * BufferedImageの更新が必要な場合はtrue
	 */
	BOOL bufferedImageGraphics;

	/**
	 * CreateCompatibleDC()呼び出し時にWindowsが生成した
	 * ビットマップのハンドル
	 * DCをGetDC()で取得している場合はNULL
	 */
	HBITMAP	hDefaultBitmap;
	
	/**
	 * 原点のx座標
	 */
	int offsetX;

	/**
	 * 原点のy座標
	 */
	int offsetY;

	/**
	 * ペイントモードが xorModeの場合
	 */
	BOOL	 xorMode;

	/**
	 * XORモード時に使用する色
	 */
	COLORREF xorColor;

	/**
	 * Stroke情報
	 */
	stroke_info stroke;

	/**
	 * Paint情報
	 */
	paint_info paint;

	// その他、さまざまな情報を保持する
} native_graphics;

/**
 * 描画対象となるビットマップを選択状態にする
 * 描画前にかならずこのマクロを呼び出す必要がある
 */
#define PREPARE_BITMAP(ngr) \
{ \
	if (ngr->hBitmap) { \
		HBITMAP hDefaultBitmap = SelectObject(ngr->hDC, ngr->hBitmap); \
		assert(hDefaultBitmap == ngr->hDefaultBitmap); \
	} \
}

/**
 * 描画対象となるビットマップを非選択状態にする
 * 描画後にかならずこのマクロを呼び出す必要がある
 */
#define UNPREPARE_BITMAP(ngr) \
{ \
	if (ngr->hBitmap) { \
		HBITMAP hBitmap = SelectObject(ngr->hDC, ngr->hDefaultBitmap); \
		assert(hBitmap == ngr->hBitmap); \
	} \
}

/**
 * ブラシを作成して選択状態にする
 */
#define SELECT_BRUSH_FOR_PAINT(env, ngr, x, y, width, height) \
{ \
	HBRUSH hBrush = create_brush(env, ngr, x, y, width, height); \
	DeleteObject(SelectObject(ngr->hDC, hBrush)); \
}

/**
 * ブラシを非選択状態にした後削除する
 */
#define UNSELECT_BRUSH_FOR_PAINT(env, ngr) \
{ \
	HBRUSH hBrush = SelectObject(ngr->hDC, GetStockObject(NULL_BRUSH)); \
	DeleteObject(hBrush); \
}

/**
 * WCEGraphics2D.updateBufferedImage()を呼び出す
 */
#define UPDATE_BUFFERED_IMAGE(env, obj, ngr) \
{ \
 if (ngr->bufferedImageGraphics) { \
	 (*env)->CallVoidMethod(env, obj, g_updateBufferedImage_mid); \
 } \
}

/**
 * updateBufferedImage()のメソッドID
 */
static jmethodID g_updateBufferedImage_mid;


/**
 * ネイティブペイント情報を削除する
 */
static void delete_paint(native_graphics* ngr) {
	if (ngr->paint.type == PAINT_TYPE_TEXTURE_PAINT) {
		// テクスチャ用のビットマップを削除する
		DeleteObject(ngr->paint.texture.hBitmap);
		ngr->paint.type = PAINT_TYPE_UNDEFINED;
	}
}

/**
 * グラデーションパターンを持つビットマップを生成する
 *
 * @param	x	描画領域の左上座標(x)
 * @param	y	描画領域の左上座標(y)
 * @param	w	描画領域の幅
 * @param	h	描画領域の高さ
 * @param	g	グラデーション情報
 */
static HBITMAP create_gradient_pattern(int x, int y, int w, int h,
									   native_graphics* ngd) {
	gradient_paint* g = &ngd->paint.gradient;
	int x1 = g->x1;
	int y1 = g->y1;
	COLORREF c1 = g->c1;
	int x2 = g->x2;
	int y2 = g->y2;
	COLORREF c2 = g->c2;
	BOOL cyclic = g->cyclic;
	BITMAPINFO_RGB565 biBMP = {0};
	double distance;	
	HBITMAP result;
	WORD* data;

	// XORモードの処理を行う
	if (ngd->xorMode) {
		c1 ^= ngd->xorColor;
		c2 ^= ngd->xorColor;
	}

	// グラデーションパターンを生成する
	if (x1 == x2) {
		// 開始／終了のx座標が等しい（＝垂直方向のグラデーション）
		distance = fabs(y2 - y1);
		
		// 生成するパターンの幅は2ピクセルで十分
		w = 2;

	} else if (y1 == y2) {
		// 開始／終了のy座標が等しい（＝水平方向のグラデーション）
		distance = fabs(x2 - x1);

		// 生成するパターンの高さは2ピクセルで十分
		h = 2;
	} else {
		// 任意の方向のグラデーション
		distance = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
	}

	// 幅または高さは2以上にしないとおかしくなる
	// この関数が返したブラシに対して、SetBrushOrgEx()で原点を変更した場合に、
	// 「新しい原点がビットマップの幅（または高さ）を超えた場合、幅（または高さ）で新しい原点の値を除した余りが設定される」
	// ことが原因？
	// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wceui40/html/cerefsetbrushorgex.asp
	if (w < 2) {
		w = 2;
	}
	if (h < 2) {
		h = 2;
	}

	// DIBセクションを作成する
	// フォーマットは RGB565 とする
	biBMP.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);
	biBMP.bmiHeader.biBitCount    = 16;				// 
	biBMP.bmiHeader.biCompression = BI_BITFIELDS;	// biBitCount = 16, biCompression = BI_BITFIELDSの場合はRGB565を意味する
	biBMP.bmiHeader.biPlanes      = 1;
	biBMP.bmiHeader.biWidth       = w;
	biBMP.bmiHeader.biHeight      = -h;	// トップダウンDIB
	biBMP.dwRedMask               = 0x1f << (5 + 6);
	biBMP.dwGreenMask			  = 0x3f << 5;
	biBMP.dwBlueMask              = 0x1f;
	result = CreateDIBSection(NULL,
							  (BITMAPINFO*) &biBMP,
							  DIB_RGB_COLORS,
							  (&data),
							  NULL,
							  0);
	if (result) {
		int r;
		for (r = 0; r < h; r++) {
			int c;
			for (c = 0; c < w; c++) {
				double u, ratio;
				BYTE red, green, blue;

				if (distance != 0) {
				  u = (((x + c) - x1) * (x2 - x1) + ((y + r) - y1) * (y2 - y1)) 
						  / distance;
				} else {
				  u = 0.0;
				}
				ratio = u / distance;
				if (cyclic) {
					ratio = fabs(ratio - floor((ratio + 1.0) / 2.0) * 2.0);
				} else {
					ratio = max(0.0, min(1.0, ratio));
				}

				// 8ビットの値をred=5bit, green=6bit, blue=5bit に変換する
				red   = (BYTE) (GetRValue(c1) + ratio * (GetRValue(c2) - GetRValue(c1))) >> 3;
				green = (BYTE) (GetGValue(c1) + ratio * (GetGValue(c2) - GetGValue(c1))) >> 2;
				blue  = (BYTE) (GetBValue(c1) + ratio * (GetBValue(c2) - GetBValue(c1))) >> 3;

				// RGB565形式で値を格納する
				data[r * w + c] =  (WORD) (((BYTE)(blue)|(((WORD)green)<<5))|(((WORD)red)<<(5+6)));
			}
		}
	}
	return result;
}

/**
 * ペンを作成する
 */
static HPEN create_pen(JNIEnv* env, native_graphics* ngd) {

	HPEN result = NULL;

	switch (ngd->paint.type) {
		case PAINT_TYPE_COLOR:
			// ToDo: Strokeを考慮する
			result = CreatePen(PS_SOLID,
								  ngd->stroke.width,
								  (ngd->xorMode ? ngd->paint.color ^ ngd->xorColor : ngd->paint.color));
			break;
		
		case PAINT_TYPE_GRADIENT_PAINT:
			// ToDo: Strokeを考慮する
			// ToDo: グラデーションをサポートする
			result = CreatePen(PS_SOLID,
							  ngd->stroke.width,
							  (ngd->xorMode ? ngd->paint.gradient.c1 ^ ngd->xorColor : ngd->paint.gradient.c1));
			break;

		default:
			// ToDo: Strokeを考慮する
			// ToDo: 正しい色を持つペンを生成する
			result = CreatePen(PS_SOLID,
							  ngd->stroke.width,
							  RGB(0, 0, 0));
	}
	return result;
}

/**
 * ブラシを作成する
 */
static HBRUSH create_brush(JNIEnv* env,
						  native_graphics* ngd,
						  int x,
						  int y,
						  int width,
						  int height) {
	char errmsg[256];
	HBRUSH result = NULL;

	if (! width || ! height) {
		// 大きさが0の場合には作成できない
		return (HBRUSH) GetStockObject(NULL_BRUSH);
	}

	switch (ngd->paint.type) {
		case PAINT_TYPE_COLOR:
			// 単一色ブラシ
			result = CreateSolidBrush(ngd->xorMode ? (ngd->paint.color ^ ngd->xorColor) : ngd->paint.color);
			break;

		case PAINT_TYPE_GRADIENT_PAINT:
			// GradientPaint
			{
				DWORD lasterror;
				HBITMAP hBitmap = create_gradient_pattern(x, y, width, height, ngd);
				if (! hBitmap) {
					lasterror = GetLastError();
					_snprintf(errmsg,
							   sizeof(errmsg) / sizeof(errmsg[0]),
							   "create_gradient_pattern(%d, %d, %d, %d) failed: GetLastError()=%u",
							   x,
							   y,
							   width,
							   height,
							   lasterror);
					throw_AWTError(env, errmsg);
					break;
				}
				result = CreatePatternBrush(hBitmap);
				lasterror = GetLastError();
				DeleteObject(hBitmap);	// ビットマップはすぐに削除してしまってよい
				if (! result) {
					_snprintf(errmsg,
							   sizeof(errmsg) / sizeof(errmsg[0]),
							   "CreatePatternBrush() failed: GetLastError()=%u", lasterror);
					throw_AWTError(env, "CreatePatternBrush() failed");
					break;
				}
				// ブラシの原点を移動しておく
				SetBrushOrgEx(ngd->hDC, x, y, NULL);
			}
			break;
		
		case PAINT_TYPE_TEXTURE_PAINT:
			// TexturePaint
			{
				BITMAP bitmap = {0};
				HBITMAP hResized = NULL;
				HBITMAP hbmpPattern = NULL;
				char msg[128];

				// アンカー領域のサイズとイメージのサイズとを比較する
				if (! GetObject(ngd->paint.texture.hBitmap, sizeof(BITMAP), &bitmap)) {
					_snprintf(msg,
							  sizeof(msg) / sizeof(msg[0]),
							  "GetObject() failed: GetLastError()=%d", GetLastError());
					throw_AWTError(env, msg);
					break;
				}
				if ((ngd->paint.texture.anchorWidth != bitmap.bmWidth)
						|| (ngd->paint.texture.anchorHeight != bitmap.bmHeight)) {
					// リサイズする
					HBITMAP hOldSrcBitmap = NULL;
					HBITMAP hOldDestBitmap = NULL;
					HDC hdcSrc = CreateCompatibleDC(ngd->hDC);
					HDC hdcDest = CreateCompatibleDC(ngd->hDC);
					if (! hdcSrc || ! hdcDest) {
						_snprintf(msg,
							  sizeof(msg) / sizeof(msg[0]),
							  "CreateCompatibleDC() failed: GetLastError()=%d", GetLastError());
						throw_AWTError(env, msg);
						break;
					}
					hResized = CreateCompatibleBitmap(ngd->hDC, ngd->paint.texture.anchorWidth, ngd->paint.texture.anchorHeight);
					if (! hResized) {
						DeleteDC(hdcSrc);
						DeleteDC(hdcDest);
						_snprintf(msg,
							  sizeof(msg) / sizeof(msg[0]),
							  "CreateCompatibleBitmap() failed: GetLastError()=%d", GetLastError());
						throw_AWTError(env, msg);
						break;
					}
					hOldDestBitmap = (HBITMAP) SelectObject(hdcDest, hResized);
					hOldSrcBitmap = (HBITMAP) SelectObject(hdcSrc, ngd->paint.texture.hBitmap);
					StretchBlt(hdcDest,
						       0,
							   0,
							   ngd->paint.texture.anchorWidth,
							   ngd->paint.texture.anchorHeight,
							   hdcSrc,
							   0,
							   0,
							   bitmap.bmWidth,
							   bitmap.bmHeight,
							   SRCCOPY);
					SelectObject(hdcDest, hOldDestBitmap);
					SelectObject(hdcSrc, hOldSrcBitmap);

					DeleteDC(hdcSrc);
					DeleteDC(hdcDest);

					hbmpPattern = hResized;
				} else {
					hbmpPattern = ngd->paint.texture.hBitmap;
				}
				
				result = CreatePatternBrush(hbmpPattern);
				if (! result) {
					_snprintf(msg,
						  sizeof(msg) / sizeof(msg[0]),
						  "CreatePatternBrush() failed: GetLastError()=%d", GetLastError());
					throw_AWTError(env, msg);
				} else {
					// ブラシの原点を変更する
					SetBrushOrgEx(ngd->hDC, -ngd->paint.texture.anchorX, -ngd->paint.texture.anchorY, NULL);
				}

				if (hResized) {
					DeleteObject(hResized);
				}
			}
			break;

		default:
			// ToDo: 実装する
			throw_AWTError(env, "Unsupported Paint type");
	}
	return result;
}

/**
 * テキストカラーを設定する
 */
static void set_text_color(JNIEnv* env, native_graphics* ngr) {
	COLORREF color;
	switch (ngr->paint.type) {
		case PAINT_TYPE_COLOR:
			color = ngr->xorMode ? ngr->paint.color ^ ngr->xorColor : ngr->paint.color;
			break;
		
		case PAINT_TYPE_GRADIENT_PAINT:
			// ToDo: グラデーションをサポートする
			color = ngr->xorMode ? ngr->paint.gradient.c1 ^ ngr->xorColor : ngr->paint.gradient.c1;
			break;

		default:
			// ToDo: Strokeを考慮する
			// ToDo: 正しい色を持つペンを生成する
			color = RGB(0, 0, 0);
	}
	SetTextColor(ngr->hDC, color);
}

/**
 * カレントのPaintを設定する
 */
static void set_paint(JNIEnv* env, native_graphics* ngr) {
	HPEN hPen;

	// ペンを作成する
	hPen = create_pen(env, ngr);
	DeleteObject(SelectObject(ngr->hDC, hPen));

	// テキストカラーを設定する
	set_text_color(env, ngr);

	// ブラシは描画を行うたびに作成する
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics2D
 * Method:    initIDs
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_initIDs(JNIEnv *env, jclass clazz) {
	g_updateBufferedImage_mid = (*env)->GetMethodID(env, clazz, "updateBufferedImage", "()V");
	assert(g_updateBufferedImage_mid);
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics2D
 * Method:    createNativeGraphicsForScreen
 * Signature: (I)I
 */
JNIEXPORT jint JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_createNativeGraphicsForScreen(JNIEnv *env, jobject obj, jint windowHandle) {
	int result;
	HWND hWnd = (HWND) windowHandle;
	native_graphics* ngr = (native_graphics*) calloc(sizeof(native_graphics), 1);
	if (ngr) {
		ngr->hWnd = hWnd;
		ngr->hDC  =	GetDC(hWnd);
		init_DC(ngr->hDC);
		result = (jint) ngr;
	} else {
		throw_AWTError(env, "No memory");
		result = 0;
	}
	return result;
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics2D
 * Method:    createNativeGraphicsForBitmap
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_createNativeGraphicsForBitmap(JNIEnv *env, jobject obj, jint windowHandle, jint bitmapHandle) {
	HWND hWnd = (HWND) windowHandle;
	HBITMAP hBitmap = (HBITMAP) bitmapHandle;
	HDC hCompatibleDC = NULL;
	jint result;

	// native_graphicsを作成する
	native_graphics* ngr = (native_graphics*) calloc(sizeof(native_graphics), 1);

	if (! ngr) {
		throw_AWTError(env, "No memory");
		result = 0;
	} else {
		// Compatible DCを作成する
		{
			HDC hDC = GetDC(hWnd);
			hCompatibleDC = CreateCompatibleDC(hDC);
			ReleaseDC(hWnd, hDC);
		}
		init_DC(hCompatibleDC);

		// 後始末ができるように、デフォルトのビットマップを保存しておく
		ngr->hDefaultBitmap = (HBITMAP) GetCurrentObject(hCompatibleDC, OBJ_BITMAP);
		ngr->hWnd = hWnd;
		ngr->hDC = hCompatibleDC;
		ngr->hBitmap = hBitmap;

		result = (jint) ngr;
	}
	return result;
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics2D
 * Method:    createNativeGraphicsForBufferedImage
 * Signature: (I)I
 */
JNIEXPORT jint JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_createNativeGraphicsForBufferedImage(JNIEnv *env, jobject obj, jint bitmapHandle) {
	HBITMAP hBitmap = (HBITMAP) bitmapHandle;
	HDC hCompatibleDC = NULL;
	jint result;

	// native_graphicsを作成する
	native_graphics* ngr = (native_graphics*) calloc(sizeof(native_graphics), 1);

	if (! ngr) {
		throw_AWTError(env, "No memory");
		result = 0;
	} else {
		// Compatible DCを作成する
		{
			HDC hDC = GetDC(NULL);
			hCompatibleDC = CreateCompatibleDC(hDC);
			ReleaseDC(NULL, hDC);
		}
		init_DC(hCompatibleDC);

		// 後始末ができるように、デフォルトのビットマップを保存しておく
		ngr->hDefaultBitmap = (HBITMAP) GetCurrentObject(hCompatibleDC, OBJ_BITMAP);
		ngr->hWnd = NULL;
		ngr->hDC = hCompatibleDC;
		ngr->hBitmap = hBitmap;

		// BufferedImage用のGraphicsであることを示すフラグを設定する
		ngr->bufferedImageGraphics = TRUE;

		result = (jint) ngr;
	}
	return result;
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics2D
 * Method:    translateNative
 * Signature: (III)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_translateNative(JNIEnv *env, jobject obj, jint nativeGraphics, jint x, jint y) {
	native_graphics* ngr = (native_graphics*) nativeGraphics;
	assert(ngr);

	ngr->offsetX = x;
	ngr->offsetY = y;
}


/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics2D
 * Method:    setNativeColor
 * Signature: (IIII)V
 */
JNIEXPORT VOID JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_setNativeColor(JNIEnv *env, jobject obj, jint nativeGraphics, jint red, jint green, jint blue) {
	native_graphics* ngr = (native_graphics*) nativeGraphics;

	// 以前に設定されていたリソースを削除する
	delete_paint(ngr);

	// 指定された値を設定する
	ngr->paint.type = PAINT_TYPE_COLOR;
	ngr->paint.color = RGB(red, green, blue);
	
	set_paint(env, ngr);
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics2D
 * Method:    setNativePaintMode
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_setNativePaintMode(JNIEnv *env, jobject obj, jint nativeGraphics) {
	native_graphics* ngr = (native_graphics*) nativeGraphics;
	ngr->xorMode = FALSE;
	ngr->xorColor = 0;
	SetROP2(ngr->hDC, R2_COPYPEN);

	set_paint(env, ngr);
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics2D
 * Method:    setNativeXORMode
 * Signature: (IIII)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_setNativeXORMode(JNIEnv *env, jobject obj, jint nativeGraphics, jint red, jint green, jint blue) {
	native_graphics* ngr = (native_graphics*) nativeGraphics;
	ngr->xorMode = TRUE;
	ngr->xorColor = RGB(red, green, blue);
	SetROP2(ngr->hDC, R2_XORPEN);

	set_paint(env, ngr);
}


/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics2D
 * Method:    setNativeFont
 * Signature: (II)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_setNativeFont(JNIEnv *env, jobject obj, jint nativeGraphics, jint fontHandle) {
	native_graphics* ngr = (native_graphics*) nativeGraphics;
	SelectObject(ngr->hDC, (HFONT) fontHandle);
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics
 * Method:    copyNativeArea
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_copyNativeArea(JNIEnv *env,
																			   jobject peer_obj,
																			   jint nativeGraphics,
																			   int x,
																			   int y, int width, int height, int dx, int dy) {
	native_graphics* ngr = (native_graphics*) nativeGraphics; 
	HDC hDC = ngr->hDC;
	
	PREPARE_BITMAP(ngr);
	BitBlt(hDC,
		   x + dx,
		   y + dy,
		   width,
		   height,
		   hDC,
		   x,
		   y,
		   SRCCOPY);
	UNPREPARE_BITMAP(ngr);

	// BufferedImageを更新する
	UPDATE_BUFFERED_IMAGE(env, peer_obj, ngr);

	// BufferedImageを更新する
	UPDATE_BUFFERED_IMAGE(env, peer_obj, ngr);
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics2D
 * Method:    setNativeClip
 * Signature: (IZIIII)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_setNativeClip(JNIEnv *env,
																			  jobject obj,
																			  jint nativeGraphics,
																			  jboolean clipEnabled,
																			  jint clipx,
																			  jint clipy,
																			  jint clipWidth,
																			  jint clipHeight) {
	native_graphics* ngd = (native_graphics*) nativeGraphics;
	if (clipEnabled) {
		HRGN region = CreateRectRgn(clipx,
									clipy,
									clipx + clipWidth + 1,
									clipy + clipHeight + 1);
		if (! region) {
			throw_AWTError(env, "CreateRectRgn() failed.");
			return;
		}
		SelectClipRgn(ngd->hDC, region);
		DeleteObject(region);	// すぐに削除してよい
	} else {
		SelectClipRgn(ngd->hDC, NULL);
	}
}


/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics
 * Method:    drawNativeLine
 * Signature: (IIIII)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_drawNativeLine(JNIEnv *env, jobject peer_obj, jint nativeGraphics, int x1, int y1, int x2, int y2) {
	native_graphics* ngd = (native_graphics*) nativeGraphics;
	HDC hDC = ngd->hDC;

	PREPARE_BITMAP(ngd);
	MoveToEx(hDC, x1, y1, NULL);
	LineTo(hDC, x2, y2);
	UNPREPARE_BITMAP(ngd);

	// BufferedImageを更新する
	UPDATE_BUFFERED_IMAGE(env, peer_obj, ngd);
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics
 * Method:    drawNativeRect
 * Signature: (IIIII)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_drawNativeRect(JNIEnv *env, jobject peer_obj, jint nativeGraphics, jint x, jint y, jint width, jint height) {
	native_graphics* ngr = (native_graphics*) nativeGraphics;
	HDC hDC = ngr->hDC;
	HBRUSH hBrush = SelectObject(hDC, GetStockObject(NULL_BRUSH));

	PREPARE_BITMAP(ngr);

	Rectangle(hDC,
			  x,
			  y,
			  x + width + 1,
			  y + height + 1);
	SelectObject(hDC, hBrush);

	UNPREPARE_BITMAP(ngr);

	// BufferedImageを更新する
	UPDATE_BUFFERED_IMAGE(env, peer_obj, ngr);
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics
 * Method:    fillNativeRect
 * Signature: (IIIII)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_fillNativeRect(JNIEnv *env, jobject peer_obj, jint nativeGraphics, jint x, jint y, jint width, jint height) {
	native_graphics* ngr = (native_graphics*) nativeGraphics;

	SELECT_BRUSH_FOR_PAINT(env, ngr, x, y, width, height);
	PREPARE_BITMAP(ngr);
	PatBlt(ngr->hDC,
			  x,
			  y,
			  width,
			  height,
			  PATCOPY);
	UNPREPARE_BITMAP(ngr);
	UNSELECT_BRUSH_FOR_PAINT(env, ngr);

	// BufferedImageを更新する
	UPDATE_BUFFERED_IMAGE(env, peer_obj, ngr);
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics
 * Method:    clearNativeRect
 * Signature: (IIIIII)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_clearNativeRect(JNIEnv *env, jobject peer_obj, jint nativeGraphics, jint x, jint y, jint width, jint height, jint rgb) {
	native_graphics* ngr = (native_graphics*) nativeGraphics;
	HDC hdc = ngr->hDC;

	// クリアする色でブラシを作成する
	HBRUSH brush = CreateSolidBrush(TO_COLORREF(rgb));
	HBRUSH oldbrush = SelectObject(hdc, brush);

	PREPARE_BITMAP(ngr);
	PatBlt(hdc, x, y, width + 1, height + 1, PATCOPY);
	UNPREPARE_BITMAP(ngr);

	// ブラシを削除する
	DeleteObject(SelectObject(hdc, oldbrush));

	// BufferedImageを更新する
	UPDATE_BUFFERED_IMAGE(env, peer_obj, ngr);
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics
 * Method:    nativeRoundRect
 * Signature: (IIIIIIIZ)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_nativeRoundRect(JNIEnv *env, jobject peer_obj, jint nativeGraphics, jint x, jint y, jint width, jint height, jint arcWidth, jint arcHeight, jboolean fill) {
	native_graphics* ngr = (native_graphics*) nativeGraphics;
	HDC hdc = ngr->hDC;
	HBRUSH brush;
	int x2, y2;

	if (! fill) {
		brush = SelectObject(hdc, GetStockObject(NULL_BRUSH));
		x2 = x + width + 1;
		y2 = y + height + 1;
	} else {
		SELECT_BRUSH_FOR_PAINT(env, ngr, x, y, width, height);
		x2 = x + width;
		y2 = y + height;
	}

	PREPARE_BITMAP(ngr);
	RoundRect(hdc,
			  x,
			  y,
			  x2,
			  y2,
			  arcWidth,
			  arcHeight);
	UNPREPARE_BITMAP(ngr);

	if (! fill) {
		SelectObject(hdc, brush);
	} else {
		UNSELECT_BRUSH_FOR_PAINT(env, ngr);
	}

	// BufferedImageを更新する
	UPDATE_BUFFERED_IMAGE(env, peer_obj, ngr);
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics
 * Method:    nativeOval
 * Signature: (IIIIIZ)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_nativeOval(JNIEnv *env, jobject peer_obj, jint nativeGraphics, jint x, jint y, jint width, jint height, jboolean fill) {
	native_graphics* ngr = (native_graphics*) nativeGraphics;
	HDC hdc = ngr->hDC;
	HBRUSH brush;
	if (! fill) {
		brush = SelectObject(hdc, GetStockObject(NULL_BRUSH));
	} else {
		SELECT_BRUSH_FOR_PAINT(env, ngr, x, y, width, height);
	}

	PREPARE_BITMAP(ngr);
	Ellipse(hdc,
			x,
			y,
			x + width,
			y + height);
	UNPREPARE_BITMAP(ngr);

	if (! fill) {
		SelectObject(hdc, brush);
	} else {
		UNSELECT_BRUSH_FOR_PAINT(env, ngr);
	}

	// BufferedImageを更新する
	UPDATE_BUFFERED_IMAGE(env, peer_obj, ngr);
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics2D
 * Method:    nativeArc
 * Signature: (IIIIIIIZ)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_nativeArc(JNIEnv *env,
																		  jobject obj,
																		  jint nativeGraphics,
																		  jint x,
																		  jint y,
																		  jint width,
																		  jint height,
																		  jint startAngle,
																		  jint arcAngle,
																		  jboolean fill) {
	// 残念ながらWindows CEには対応するAPIがない
	throw_AWTError(env, "Not implemented");
}


/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics
 * Method:    drawNativePolyline
 * Signature: (I[I[II)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_drawNativePolyline(JNIEnv *env, jobject peer_obj, jint nativeGraphics, jintArray xPoints, jintArray yPoints, jint nPoints) {
	native_graphics* ngr = (native_graphics*) nativeGraphics;

	int* x = (*env)->GetIntArrayElements(env, xPoints, NULL);
	int* y = (*env)->GetIntArrayElements(env, yPoints, NULL);
	POINT* pt = (POINT*) malloc(sizeof(POINT) * nPoints);
	jint i;

	if (x && y && pt) {
		for (i = 0; i < nPoints; ++i) {
			pt[i].x = x[i];
			pt[i].y = y[i];
		}

		PREPARE_BITMAP(ngr);
		if (! Polyline(ngr->hDC, pt, nPoints)) {
			// AWTErrorを投げる
			throw_AWTError(env, "Polyline() failed");
		}
		UNPREPARE_BITMAP(ngr);
		free(pt);
	}
	(*env)->ReleaseIntArrayElements(env, xPoints, x, 0);
	(*env)->ReleaseIntArrayElements(env, yPoints, y, 0);

	// BufferedImageを更新する
	UPDATE_BUFFERED_IMAGE(env, peer_obj, ngr);
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics
 * Method:    drawNativePolygon
 * Signature: (I[I[II)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_drawNativePolygon(JNIEnv *env, jobject peer_obj, jint nativeGraphics, jintArray xPoints, jintArray yPoints, jint nPoints) {
	native_graphics* ngr = (native_graphics*) nativeGraphics;
	int* x = (*env)->GetIntArrayElements(env, xPoints, NULL);
	int* y = (*env)->GetIntArrayElements(env, yPoints, NULL);
	POINT* pt = (POINT*) malloc(sizeof(POINT) * nPoints);
	jint i;
	HDC hdc = ngr->hDC;

	if (x && y && pt) {
		HBRUSH hbrushOrg;
		for (i = 0; i < nPoints; ++i) {
			pt[i].x = x[i];
			pt[i].y = y[i];
		}
		hbrushOrg = SelectObject(hdc, GetStockObject(NULL_BRUSH));
		PREPARE_BITMAP(ngr);
		if (! Polygon(hdc, pt, nPoints)) {
			// AWTErrorを投げる
			throw_AWTError(env, "Polygon() failed");
		}
		UNPREPARE_BITMAP(ngr);
		free(pt);
		SelectObject(hdc, hbrushOrg);
	}
	(*env)->ReleaseIntArrayElements(env, xPoints, x, 0);
	(*env)->ReleaseIntArrayElements(env, yPoints, y, 0);

	// BufferedImageを更新する
	UPDATE_BUFFERED_IMAGE(env, peer_obj, ngr);
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics
 * Method:    fillNativePolygon
 * Signature: (I[I[II)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_fillNativePolygon(JNIEnv *env, jobject peer_obj, jint nativeGraphics, jintArray xPoints, jintArray yPoints, jint nPoints) {
	native_graphics* ngr = (native_graphics*) nativeGraphics;
	int* x = (*env)->GetIntArrayElements(env, xPoints, NULL);
	int* y = (*env)->GetIntArrayElements(env, yPoints, NULL);
	POINT* pt = (POINT*) malloc(sizeof(POINT) * nPoints);
	jint i;
	if (x && y && pt) {
		int minx = x[0];
		int miny = y[0];
		int maxx = minx;
		int maxy = miny;

		for (i = 0; i < nPoints; ++i) {
			pt[i].x = x[i];
			pt[i].y = y[i];
			if (minx > x[i]) {
				minx = x[i];
			} else if (maxx < x[i]) {
				maxx = x[i];
			}
			if (miny > y[i]) {
				miny = y[i];
			} else if (maxy < y[i]) {
				maxy = y[i];
			}
		}
		
		SELECT_BRUSH_FOR_PAINT(env, ngr, minx, miny, (maxx - minx), (maxy - miny));

		PREPARE_BITMAP(ngr);
		if (! Polygon(ngr->hDC, pt, nPoints)) {
			// AWTErrorを投げる
			throw_AWTError(env, "Polygon() failed");
		}
		UNPREPARE_BITMAP(ngr);
		UNSELECT_BRUSH_FOR_PAINT(env, ngr);
		free(pt);
	}
	(*env)->ReleaseIntArrayElements(env, xPoints, x, 0);
	(*env)->ReleaseIntArrayElements(env, yPoints, y, 0);

	// BufferedImageを更新する
	UPDATE_BUFFERED_IMAGE(env, peer_obj, ngr);
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics
 * Method:    drawNativeString
 * Signature: (ILjava/lang/String;IIZ)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_drawNativeString(JNIEnv *env, jobject peer_obj, jint nativeGraphics, jstring str, jint x, jint y, jboolean xorMode) {
	native_graphics* ngr = (native_graphics*) nativeGraphics;
	HDC hdc = ngr->hDC;
	jint len = (*env)->GetStringLength(env, str);
	const _TCHAR* buff = (*env)->GetStringChars(env, str, NULL);
	TEXTMETRIC tm;
	GetTextMetrics(hdc, &tm);

	PREPARE_BITMAP(ngr);
	if (buff) {
		// ベースラインにあわせる必要がある
		RECT rect = { x, y - tm.tmAscent, x, y };
		if (xorMode) {
			// ToDo: DrawTextはSetROP2()の設定を無視するので、対応が必要
			DrawText(hdc,
					  buff,
					  len,
					  &rect,
					  DT_TOP | DT_LEFT | DT_NOCLIP | DT_NOPREFIX | DT_SINGLELINE);

		} else {	
			DrawText(hdc,
					  buff,
					  len,
					  &rect,
					  DT_TOP | DT_LEFT | DT_NOCLIP | DT_NOPREFIX | DT_SINGLELINE);
		}
		(*env)->ReleaseStringChars(env, str, buff);
	}
	UNPREPARE_BITMAP(ngr);

	UPDATE_BUFFERED_IMAGE(env, peer_obj, ngr);
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics
 * Method:    nativeBitBlt
 * Signature: (IIIIII)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_nativeBitBlt(JNIEnv *env,
																			 jobject obj,
																			 jint destinationNativeGraphics,
																			 jint x,
																			 jint y,
																			 jint width,
																			 jint height,
																			 jint sourceWindowHandle,
																			 jint sourceBitmapHandle) {
	native_graphics* dest_g = (native_graphics*) destinationNativeGraphics;
	HDC hDC = GetDC((HWND) sourceWindowHandle);
	HDC hdcSrc = CreateCompatibleDC(hDC);
	HBITMAP hOldBitmap = SelectObject(hdcSrc, (HBITMAP) sourceBitmapHandle);
	ReleaseDC((HWND) sourceWindowHandle, hDC);

	PREPARE_BITMAP(dest_g);
	BitBlt(dest_g->hDC,
			x,
			y,
			width,
			height,
			hdcSrc,
			0,
			0,
			SRCCOPY);
	UNPREPARE_BITMAP(dest_g);

	SelectObject(hdcSrc, hOldBitmap);
	DeleteDC(hdcSrc);

	UPDATE_BUFFERED_IMAGE(env, obj, dest_g);
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics2D
 * Method:    nativeTransparentBlt
 * Signature: (IIIIIIII)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_nativeTransparentBlt(JNIEnv *env,
																			 jobject obj,
																			 jint destinationNativeGraphics,
																			 jint x,
																			 jint y,
																			 jint width,
																			 jint height,
																			 jint sourceWindowHandle,
																			 jint sourceBitmapHandle,
																			 jint transparentColor) {
	native_graphics* dest_g = (native_graphics*) destinationNativeGraphics;
	HDC hDC = GetDC((HWND) sourceWindowHandle);
	HDC hdcSrc = CreateCompatibleDC(hDC);
	HBITMAP hOldBitmap = SelectObject(hdcSrc, (HBITMAP) sourceBitmapHandle);
	ReleaseDC((HWND) sourceWindowHandle, hDC);

	PREPARE_BITMAP(dest_g);
	TransparentBlt(dest_g->hDC,
			x,
			y,
			width,
			height,
			hdcSrc,
			0,
			0,
			width,
			height,
			(COLORREF) transparentColor);
	UNPREPARE_BITMAP(dest_g);

	SelectObject(hdcSrc, hOldBitmap);
	DeleteDC(hdcSrc);

	UPDATE_BUFFERED_IMAGE(env, obj, dest_g);
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics
 * Method:    nativeStretchBlt
 * Signature: (IIIIIIIII)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_nativeStretchBlt(JNIEnv *env, jobject obj, jint nativeGraphics, jint x, jint y, jint width, jint height, jint sourceWindowHandle, jint sourceBitmapHandle, jint srcx, jint srcy, jint srcwidth, jint srcheight) {
	native_graphics* ngr = (native_graphics*) nativeGraphics;
	HDC hDC = GetDC((HWND) sourceWindowHandle);
	HDC hdcSrc = CreateCompatibleDC(hDC);
	HBITMAP hOldBitmap = SelectObject(hdcSrc, (HBITMAP) sourceBitmapHandle);
	ReleaseDC((HWND) sourceWindowHandle, hDC);

	PREPARE_BITMAP(ngr);

	StretchBlt(ngr->hDC,
				x,
				y,
				width,
				height,
				hdcSrc,
				srcx,
				srcy,
				srcwidth,
				srcheight,
				SRCCOPY);
	UNPREPARE_BITMAP(ngr);
	SelectObject(hdcSrc, hOldBitmap);
	DeleteDC(hdcSrc);

	UPDATE_BUFFERED_IMAGE(env, obj, ngr);
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics
 * Method:    disposeNative
 * Signature: (II)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_disposeNative(JNIEnv *env, jobject peer_obj, jint nativeGraphics) {
	native_graphics* ngr = (native_graphics*) nativeGraphics;
	
	// デバイスコンテキストからGDIオブジェクトを剥がしていく
	HDC hDC = ngr->hDC;

	// PenとBrushを削除する
	DeleteObject(SelectObject(hDC, GetStockObject(NULL_PEN)));		// ペン
	DeleteObject(SelectObject(hDC, GetStockObject(NULL_BRUSH)));	// ブラシ

	// クリッピング領域を削除する(不要？）
	SelectClipRgn(hDC, NULL);

	// デバイスコンテキストを解放する
	if (ngr->hBitmap) {
		// CreateCompatibleDC()で作成されたDC
		SelectObject(hDC, ngr->hDefaultBitmap);
		DeleteDC(hDC);
	} else {
		// GetDC()で取得したDC
		ReleaseDC(ngr->hWnd, hDC);
	}

	// ネイティブペイント情報を削除する
	delete_paint(ngr);

	// メモリを開放する
	free(ngr);
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics2D
 * Method:    createNativeDIBSection
 * Signature: (IIII)I
 */
JNIEXPORT jint JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_createNativeDIBSection(JNIEnv *env, jobject obj, jint width, jint height, jint bitCount, jint type) {
	HBITMAP result = NULL;
	BOOL invalid_type = FALSE;

	if (bitCount == 32) {
		// 32ビット
		if (type == BufferedImage_TYPE_INT_RGB || type == BufferedImage_TYPE_INT_ARGB) {
			BITMAPINFO bi = {0};
			bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
			bi.bmiHeader.biBitCount    = bitCount;
			bi.bmiHeader.biCompression = BI_RGB;
			bi.bmiHeader.biPlanes      = 1;
			bi.bmiHeader.biWidth       = width;
			bi.bmiHeader.biHeight      = -height;	// トップダウンDIB
			result = CreateDIBSection(NULL,
									  &bi,
									  DIB_RGB_COLORS,
									  NULL,
									  NULL,
									  0);
		} else {
			invalid_type = TRUE;
		}

	} else if (bitCount == 16) {
		// 16ビット
		if (type == BufferedImage_TYPE_USHORT_555_RGB) {
				BITMAPINFO bi = {0};
				bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
				bi.bmiHeader.biBitCount    = bitCount;
				bi.bmiHeader.biCompression = BI_RGB;
				bi.bmiHeader.biPlanes      = 1;
				bi.bmiHeader.biWidth       = width;
				bi.bmiHeader.biHeight      = -height;	// トップダウンDIB
				result = CreateDIBSection(NULL,
										  &bi,
										  DIB_RGB_COLORS,
										  NULL,
										  NULL,
										  0);
		} else if (type == BufferedImage_TYPE_USHORT_565_RGB) {
			// RGB565
			BITMAPINFO_RGB565 bi = {0};
			bi.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);
			bi.bmiHeader.biBitCount    = bitCount;
			bi.bmiHeader.biCompression = BI_BITFIELDS;	// biBitCount = 16, biCompression = BI_BITFIELDSの場合はRGB565を意味する
			bi.bmiHeader.biPlanes      = 1;
			bi.bmiHeader.biWidth       = width;
			bi.bmiHeader.biHeight      = height;	// トップダウンDIB
			bi.dwRedMask               = 0x1f << (5 + 6);
			bi.dwGreenMask			   = 0x3f << 5;
			bi.dwBlueMask              = 0x1f;
			result = CreateDIBSection(NULL,
									  (BITMAPINFO*) &bi,
									  DIB_RGB_COLORS,
									  NULL,
									  NULL,
									  0);
		} else {
			invalid_type = TRUE;
		}
	} else {
		// 16ビット, 32ビット以外
		invalid_type = TRUE;
	}

	if (invalid_type) {
		char msg[128];
		_snprintf(msg,
				 sizeof(msg) / sizeof(msg[0]),
				 "Unsupported format bitCount=%d type=%d",
				 bitCount,
				 type);
		throw_AWTError(env, msg);
	}

	return (jint) result;
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics2D
 * Method:    deleteNativeObject
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_deleteNativeObject(JNIEnv *env, jobject obj, jint gdiObjectHandle) {
	if (! DeleteObject((HGDIOBJ) gdiObjectHandle)) {
		char msg[128];
		_snprintf(msg,
				  sizeof(msg) / sizeof(msg[0]),
				  "DeleteObject() failed. GetLastError()=%d",
				  GetLastError());
		throw_AWTError(env, msg);
	}
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics2D
 * Method:    writeNativeDIBSectionInt
 * Signature: (I[II)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_writeNativeDIBSectionInt(JNIEnv *env, jobject obj, jint bitmapHandle, jintArray pixels, jint type) {
	BITMAP bitmap = {0};
	jint* source = NULL;
	int maxpixels;
	jint array_length;

	// 指定されたタイプを調べる
//	if (type != BufferedImage_TYPE_INT_RGB && type != BufferedImage_TYPE_INT_ARGB) {
//		throw_AWTError(env, "Invalid type");
//		return;
//	}
	if (! GetObject((HBITMAP) bitmapHandle, sizeof(BITMAP), &bitmap)) {
		char msg[128];
		_snprintf(msg,
				  sizeof(msg) / sizeof(msg[0]),
				  "GetObject() failed. GetLastError()=%d", GetLastError());
		throw_AWTError(env, msg);
		return;
	}

	if (bitmap.bmBitsPixel != 32) {
		// 32ビットでない場合はエラーにする
		char msg[128];
		_snprintf(msg,
				  sizeof(msg) / sizeof(msg[0]),
				  "Pixel format unmatched:%d", bitmap.bmBitsPixel);
		throw_AWTError(env, msg);
	}

	// 転送ピクセル数を計算する
	array_length = (*env)->GetArrayLength(env, pixels);
	source = (*env)->GetIntArrayElements(env, pixels, NULL);

	maxpixels = bitmap.bmWidth * bitmap.bmHeight;
	if (maxpixels > array_length) {
		// 配列サイズが小さい場合にはサイズを調整する
		// (エラーにすべき？）
		maxpixels = array_length;
	}

	// DIBにデータを転送する
	memcpy(bitmap.bmBits, source, sizeof(jint) * maxpixels);

	(*env)->ReleaseIntArrayElements(env, pixels, source, 0);
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics2D
 * Method:    writeNativeDIBSectionUShort
 * Signature: (I[SI)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_writeNativeDIBSectionUShort(JNIEnv *env, jobject obj, jint bitmapHandle, jshortArray pixels, jint type) {
	BITMAP bitmap = {0};
	jshort* source = NULL;
	int maxpixels;
	jint array_length;

	// 指定されたタイプを調べる
	if (type != BufferedImage_TYPE_USHORT_565_RGB && type != BufferedImage_TYPE_USHORT_555_RGB) {
		throw_AWTError(env, "Invalid type");
		return;
	}
	if (! GetObject((HBITMAP) bitmapHandle, sizeof(BITMAP), &bitmap)) {
		char msg[128];
		_snprintf(msg,
				  sizeof(msg) / sizeof(msg[0]),
				  "GetObject() failed. GetLastError()=%d", GetLastError());
		throw_AWTError(env, msg);
		return;
	}

	if (bitmap.bmBitsPixel != 16) {
		// 16ビットでない場合はエラーにする
		char msg[128];
		_snprintf(msg,
				  sizeof(msg) / sizeof(msg[0]),
				  "Pixel format unmatched:%d", bitmap.bmBitsPixel);
		throw_AWTError(env, msg);
	}

	// 転送ピクセル数を計算する
	array_length = (*env)->GetArrayLength(env, pixels);
	source = (*env)->GetShortArrayElements(env, pixels, NULL);

	maxpixels = bitmap.bmWidth * bitmap.bmHeight;
	if (maxpixels > array_length) {
		// 配列サイズが小さい場合にはサイズを調整する
		// (エラーにすべき？）
		maxpixels = array_length;
	}

	// DIBにデータを転送する
	memcpy(bitmap.bmBits, source, sizeof(unsigned short) * maxpixels);

	(*env)->ReleaseShortArrayElements(env, pixels, source, 0);
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics2D
 * Method:    readNativeDIBSectionInt
 * Signature: (I[II)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_readNativeDIBSectionInt(JNIEnv *env, jobject obj, jint bitmapHandle, jintArray pixels, jint type) {
	BITMAP bitmap = {0};
	jint* dest = NULL;
	int maxpixels;
	jint array_length;

	// 指定されたタイプを調べる
//	if (type != BufferedImage_TYPE_INT_RGB && type != BufferedImage_TYPE_INT_ARGB) {
//		throw_AWTError(env, "Invalid type");
//		return;
//	}
	if (! GetObject((HBITMAP) bitmapHandle, sizeof(BITMAP), &bitmap)) {
		char msg[128];
		_snprintf(msg,
				  sizeof(msg) / sizeof(msg[0]),
				  "GetObject() failed. GetLastError()=%d", GetLastError());
		throw_AWTError(env, msg);
		return;
	}

	if (bitmap.bmBitsPixel != 32) {
		// 32ビットでない場合はエラーにする
		char msg[128];
		_snprintf(msg,
				  sizeof(msg) / sizeof(msg[0]),
				  "Pixel format unmatched:%d", bitmap.bmBitsPixel);
		throw_AWTError(env, msg);
	}

	// 転送ピクセル数を計算する
	array_length = (*env)->GetArrayLength(env, pixels);
	dest = (*env)->GetIntArrayElements(env, pixels, NULL);

	maxpixels = bitmap.bmWidth * bitmap.bmHeight;
	if (maxpixels > array_length) {
		// 配列サイズが小さい場合にはサイズを調整する
		// (エラーにすべき？）
		maxpixels = array_length;
	}

	// DIBからデータを転送する
	memcpy(dest, bitmap.bmBits, sizeof(jint) * maxpixels);

	(*env)->ReleaseIntArrayElements(env, pixels, dest, 0);
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics2D
 * Method:    readNativeDIBSectionUShort
 * Signature: (I[SI)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_readNativeDIBSectionUShort(JNIEnv *env, jobject obj, jint bitmapHandle, jshortArray pixels, jint type) {
	BITMAP bitmap = {0};
	jshort* dest = NULL;
	int maxpixels;
	jint array_length;

	// 指定されたタイプを調べる
	if (type != BufferedImage_TYPE_USHORT_565_RGB && type != BufferedImage_TYPE_USHORT_555_RGB) {
		throw_AWTError(env, "Invalid type");
		return;
	}
	if (! GetObject((HBITMAP) bitmapHandle, sizeof(BITMAP), &bitmap)) {
		char msg[128];
		_snprintf(msg,
				  sizeof(msg) / sizeof(msg[0]),
				  "GetObject() failed. GetLastError()=%d", GetLastError());
		throw_AWTError(env, msg);
		return;
	}

	if (bitmap.bmBitsPixel != 16) {
		// 16ビットでない場合はエラーにする
		char msg[128];
		_snprintf(msg,
				  sizeof(msg) / sizeof(msg[0]),
				  "Pixel format unmatched:%d", bitmap.bmBitsPixel);
		throw_AWTError(env, msg);
	}

	// 転送ピクセル数を計算する
	array_length = (*env)->GetArrayLength(env, pixels);
	dest = (*env)->GetShortArrayElements(env, pixels, NULL);

	maxpixels = bitmap.bmWidth * bitmap.bmHeight;
	if (maxpixels > array_length) {
		// 配列サイズが小さい場合にはサイズを調整する
		// (エラーにすべき？）
		maxpixels = array_length;
	}

	// DIBからデータを転送する
	memcpy(dest, bitmap.bmBits, sizeof(unsigned short) * maxpixels);

	(*env)->ReleaseShortArrayElements(env, pixels, dest, 0);
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics2D
 * Method:    setNativeGradientPaint
 * Signature: (IIIIIIIIIII)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_setNativeGradientPaint(JNIEnv *env, jobject obj, jint nativeGraphics, jint x1, jint y1, jint red1, jint green1, jint blue1, jint x2, jint y2, jint red2, jint green2, jint blue2, jboolean cyclic) {
	native_graphics* ngr = (native_graphics*) nativeGraphics;
	gradient_paint* gpaint;

	// 以前に設定されていたリソースを削除する
	delete_paint(ngr);

	gpaint = &ngr->paint.gradient;
	ngr->paint.type = PAINT_TYPE_GRADIENT_PAINT;
	
	gpaint->x1 = x1;
	gpaint->y1 = y1;
	gpaint->c1 = RGB(red1, green1, blue1);

	gpaint->x2 = x2;
	gpaint->y2 = y2;
	gpaint->c2 = RGB(red2, green2, blue2);

	gpaint->cyclic = (cyclic == JNI_TRUE) ? TRUE : FALSE;

	set_paint(env, ngr);
}
	
/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics2D
 * Method:    setNativeTexturePaint
 * Signature: (IIIIII)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_setNativeTexturePaint(JNIEnv *env, jobject obj, jint nativeGraphics, jint bitmapHandle, jint anchorX, jint anchorY, jint anchorWidth, jint anchorHeight) {
	native_graphics* ngr = (native_graphics*) nativeGraphics;
	assert(ngr);

	ngr->paint.type = PAINT_TYPE_TEXTURE_PAINT;
	ngr->paint.texture.hBitmap = (HBITMAP) bitmapHandle;
	ngr->paint.texture.anchorX = anchorX;
	ngr->paint.texture.anchorY = anchorY;
	ngr->paint.texture.anchorWidth = anchorWidth;
	ngr->paint.texture.anchorHeight = anchorHeight;

	set_paint(env, ngr);
}

/*
 * Class:     gnu_java_awt_peer_wce_WCEGraphics2D
 * Method:    setNativeBasicStroke
 * Signature: (FIIF[FF)V
 */
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_wce_WCEGraphics2D_setNativeBasicStroke(JNIEnv *env, jobject obj, jint nativeGraphics, jfloat lineWidth, jint endCap, jint lineJoin, jfloat miterLimit, jfloatArray dashArray, jfloat dashPhase) {
	HPEN hPen;
	native_graphics* ngr = (native_graphics*) nativeGraphics;
	assert(ngr);

	ngr->stroke.width = lineWidth;
	hPen = create_pen(env, ngr);

	// 新しいPenを SelectObject() する
	// （それまでに設定していた Pen は DeleteObject() する)
	DeleteObject(SelectObject(ngr->hDC, hPen));
}

