/*
 * @history		2019_12_27  [SDK3.0] タスクをTA_FPU属性付で起動していない不具合を修正 (uC3版) (#2698)
 * @history		2019_12_27  [SDK3.0] キャプチャのラベリング終了時にメモリ解放が重複する不具合を修正 (#2608)
 * @history		2019_12_27  [SDK3.0] キャプチャのラベリングでMAXIDの間違いによるメモリリークを修正 (#2594)
 * @history		2022_03_31  [SDK3.4] キャプチャのラベリングでラベル数がオーバーフローした場合の不具合を修正 (#3551)
 */
/*
 * This program was created by AXELL CORPORATION.
 * Copyright (C) 2017-2022 AXELL CORPORATION, all rights reserved.
 */
#include "kernel.h"
#include "sample_common.h"
#include "com.h"
#include "cap_labeling.h"
#include "cap_common.h"

#include "dsp/dspmgr.h"
#include "via/viamgr.h"
#include "pgp/pgpmgr.h"

/* サンプル固有定義 */
#define CAP_LBL_VWIN				4

typedef struct cap_lbl_table_header {
	int id;
	int index_use;
	int start_line;
	int end_line;
	int frame_id;
	int port;
	int oflow;
	int icpl;
	int end;
} CAP_LBL_TABLE_HEADER;
	

extern int32_t gCAPErrCode;		/* エラーチェック用 */

static AG903_PgpMgrPipelineHandle	*pgp;
static bool							fLabelingDone;
static void							*gLabeling;
static void							*gAnalyzed;
static CAP_Framebuffer				gFramebuffer;
static bool							gFlagQuit;
static ER_ID						gListenTaskID;


/* ラべリング完了割り込みハンドラ */
static void labeling_done_handler(int32_t id, void *handle);
static void listen_tsk(VP_INT exinf);
static void CAP_unpackLabelingTableHeader(CAP_LBL_TABLE_HEADER *hed, const uint64_t *table);
static void CAP_showLabelingArea(void);
static int32_t dsp_ch = 1;

static const T_CTSK ctsk_listen = {
	TA_HLNG | TA_ACT | TA_FPU, (VP_INT)0, (FP)listen_tsk, 6, 0x800, 0, "Listen"
};

int32_t CAP_Labeling_Demo(uint32_t mode)
{
	int32_t 	pipe_num = -1;
	uint32_t	in_width;
	uint32_t	in_height;
	uint32_t	status;
	uint32_t	loop;

	AG903_ViaMgrInputHandle		*via		= NULL;
	AG903_VidMgrOutputHandle	*vid		= NULL;
	AG903_DSPMgrHandle			*dsp		= NULL;
	AG903_DSPMgrWinAttribute	*attr[5]	= {NULL};
	AG903_DSPMgrWindowParam		win_param = {0};

	if (mode >= CAP_MODE_MAX) {
		CAP_ErrPrintf("ERROR: INVALID MODE. ABORT.\r\n");
		return -AG903_EINVAL;
	}

	/* 変数初期化 */
	{
		gCAPErrCode		= AG903_ENONE;
		pgp				= NULL;
		fLabelingDone	= false;
		gFlagQuit		= false;
		gLabeling		= NULL;
		gAnalyzed		= NULL;
		sys_memset(&gFramebuffer, 0, sizeof(CAP_Framebuffer));
	}

	if (CAP_MODE_VIA_LVDS == mode || CAP_MODE_VID == mode) {
		dsp_ch = 1;
	} else {
		dsp_ch = 0;
	}

	if(CAP_MODE_VIA_LVDS == mode || CAP_MODE_VIA_CMOS == mode) {
		via = CAP_GetVIAHandle(0);
		in_width  = CAP_ANALOG_IN_WIDTH;
		in_height = CAP_ANALOG_IN_HEIGHT;
	}
	else {
		vid = CAP_GetVIDOutHandle();
		in_width	= CAP_DIGITAL_IN_WIDTH;
		in_height	= CAP_DIGITAL_IN_HEIGHT;
	}

	/* ラべリングサイズ制限 */
	if(CAP_LABELING_MAX_WIDTH < in_width) {
		in_width = CAP_LABELING_MAX_WIDTH;
	}

	/* ラべリング結果用メモリ確保 (8バイト境界) */
	gLabeling = sys_memalign(1536, 8);
	if (gLabeling == NULL) {
		CAP_ErrPrintf("ERROR: Framebuffer allocate failed.\r\n");
		return -AG903_ENOMEM;
	}
	sys_memset(gLabeling, 0, 1536);

	/* フレームバッファ確保 */
	gFramebuffer.size	= in_width*in_height*4;
	gFramebuffer.buf	= sys_memalign(gFramebuffer.size, 0x1000);
	if (gFramebuffer.buf == NULL) {
		CAP_ErrPrintf("ERROR: Framebuffer allocate failed.\r\n");
		return -AG903_ENOMEM;
	}
	sys_memset(gFramebuffer.buf, 0, gFramebuffer.size);

	NOERRCHK(AG903_ENONE, CAP_SetupDisplay(mode));
	dsp = CAP_GetDSPHandle(dsp_ch);

	/* ウィンドウ0-3 */
	int32_t idx;
	uint32_t *frame_color = sys_malloc(4);
	frame_color[0] = 0xFF00FF00;

	for(idx=0; idx<CAP_LBL_VWIN; idx++) {
		AG903_DSPMgrGetAttribute(dsp, idx, &attr[idx]);
		sys_memset(attr[idx], 0, 0x20);
		attr[idx]->position_x				= 1600-idx;
		attr[idx]->position_y				= 1080-1;
		attr[idx]->source_height			= 1;
		attr[idx]->source_width				= 1;
		attr[idx]->hrz_size					= 4;
		attr[idx]->destination_height		= 1;
		attr[idx]->destination_width		= 1;
		attr[idx]->framebuffer_base			= (uint32_t)frame_color;

		attr[idx]->conf.framebuffer_format	= AG903_DSP_FFMT_A8R8G8B8;
		attr[idx]->conf.biliner				= false;
		attr[idx]->conf.default_alpha		= 0xFF;
		attr[idx]->conf.valid				= true;

		attr[idx]->pallet_base				= (uint32_t)frame_color;

		NOERRCHK(AG903_ENONE, AG903_DSPMgrSetAttribute(dsp, idx));
	}

	/* ウィンドウ4 */
	uint32_t *pallet = sys_malloc(256*4); /* 32bpp×256色 */
	AG903_DSPMgrGetAttribute(dsp, CAP_LBL_VWIN, &attr[CAP_LBL_VWIN]);
	sys_memset(attr[CAP_LBL_VWIN], 0, 0x20);
	attr[CAP_LBL_VWIN]->position_x				= (CAP_DISPLAY_WIDTH - in_width)>>1;	/* センタリング */
	attr[CAP_LBL_VWIN]->position_y				= (CAP_DISPLAY_HEIGHT - in_height)>>1;	/* センタリング */
	attr[CAP_LBL_VWIN]->source_height			= in_height;
	attr[CAP_LBL_VWIN]->source_width			= in_width;
	attr[CAP_LBL_VWIN]->destination_height		= in_height;
	attr[CAP_LBL_VWIN]->destination_width		= in_width;
	attr[CAP_LBL_VWIN]->framebuffer_base		= (uint32_t)gFramebuffer.buf;
	attr[CAP_LBL_VWIN]->hrz_size				= (attr[CAP_LBL_VWIN]->source_width+7)/8;

	attr[CAP_LBL_VWIN]->conf.framebuffer_format	= AG903_DSP_FFMT_2_PALLET;
	attr[CAP_LBL_VWIN]->conf.pallet_format		= AG903_DSP_PFMT_A8R8G8B8;
	attr[CAP_LBL_VWIN]->conf.default_alpha		= 0xFF;
	attr[CAP_LBL_VWIN]->conf.valid				= true;

	pallet[0] = 0xFF000000;
	pallet[1] = 0xFFFF0000;
	attr[CAP_LBL_VWIN]->pallet_base = (uint32_t)pallet;
	NOERRCHK(AG903_ENONE, AG903_DSPMgrSetAttribute(dsp, CAP_LBL_VWIN));

	/* ウィンドウ設定 */
	win_param.background			= 0x00000000;
	win_param.num_attr				= 5;
	win_param.num_config			= 5;
	win_param.update_timing			= AG903_DSP_ATTR_START_OF_VSYNC;
	win_param.window_attr_base		= (uint32_t)attr[0];
	win_param.window_attr_update	= true;
	NOERRCHK(AG903_ENONE, AG903_DSPMgrSetWindowParam(dsp, &win_param));

	AG903_DSPMgrEnable(dsp);
	sys_dlytsk(1000); /* ディスプレイの安定待ち */

	/* PGP設定 */
	NOERRCHK(AG903_ENONE, AG903_PgpMgrGetPipelineHandle(1, &pgp));

	/* 割り込みハンドラの設定 */
	NOERRCHK(AG903_ENONE, AG903_PgpMgrSetIntrCallbackFunc(pgp, AG903_PGP_MGR_INTR_LBL_DONE, labeling_done_handler));

	if(CAP_MODE_VIA_LVDS == mode || CAP_MODE_VIA_CMOS == mode) { /* VIA入力 */
		/*--- パイプラインの設定:入力段 ---*/
		/* 入力ポート:VIAから入力 */
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetInputPort(pgp, via));
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetParamInputMode(pgp, AG903_PGP_MGR_INPUT_MD_VSYNC));
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetParamInputHSyncDelay(pgp, CAP_HSYNC_DELAY));
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetParamInputFormat(pgp, AG903_PGP_MGR_INPUT_FMT_YCBCR422_W_IP));
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetParamInputSize(pgp, in_height>>1, in_width));
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetParamInputPosition(pgp, CAP_VIA_VPOS_OFFSET, CAP_VIA_HPOS_OFFSET));
		NOERRCHK(AG903_ENONE, AG903_PgpMgrEnableParamNRF(pgp, true));
		/* YUV->RGB */
		ERRCHK(AG903_ENONE, AG903_PgpMgrSetConfigPresetColorSpace(pgp, AG903_PGP_MGR_CSC_YUV_BT_601_LIMIT_TO_RGB));
		ERRCHK(AG903_ENONE, AG903_PgpMgrEnableParamColorSpace(pgp, true));
		/* HSV変換 */
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetConfigHSVHLSConversion(pgp, AG903_PGP_MGR_CONVERT_HSV));
		NOERRCHK(AG903_ENONE, AG903_PgpMgrEnableParamHSV(pgp, true));
		/*--- パイプラインの設定:フィルタ段 ---*/

		int32_t coeff[9] = {  1,  2,  1,
							  2,  4,  2,
							  1,  2,  1 };

		/* 閾値処理 */
		/* Hue[0-239]: 0(R), 40(Y), 80(G), 120(C), 160(B), 200(M) */
		/* CENTER + MARGIN は 240未満に設定してください */
		const uint8_t CENTER = CAP_LABELING_TARGET_HUE;
		/* 許容誤差：CENTER+MARGIN〜CENTER-MARGINまでを判定色にします */
		const uint8_t MARGIN = CAP_LABELING_HUE_MARGIN;

		pallet[1]		= getColorByHue(CENTER);
		frame_color[0]	= getColorByHue((CENTER+120) % 240); /* 補色 */

		int32_t top = CENTER%240 + MARGIN;
		int32_t btm = CENTER%240 - MARGIN;
		if(btm > 0) {
			NOERRCHK(AG903_ENONE, AG903_PgpMgrSetConfigThreshold1Bit(pgp, 0, 1, 0,
																	 255, 240,
																	 top, btm));
		} else {
			top = 240 - top;
			btm = -btm;
			NOERRCHK(AG903_ENONE, AG903_PgpMgrSetConfigThreshold1Bit(pgp, 0, 0, 1,
																	 255, 240,
																	 top, btm));
		}

		/* SPF */
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetConfigSpatialFilter(pgp, 4,
																 AG903_PGP_MGR_SPF_BORDER_NEIGHBOR,
																 0, 0, 0, coeff, 0));
		NOERRCHK(AG903_ENONE, AG903_PgpMgrEnableParamSpatialFilter(pgp, true));

		/* 1bitSPF */
		int32_t spf1_coeff[9] = {  1,  1,  1,
								   1,  1,  1,
								   1,  1,  1 };

		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetConfigThresholdSpatialFilter1Bit(pgp, spf1_coeff, 7));

		/* 閾値処理全体設定 */
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetConfigThreshold(pgp, AG903_PGP_MGR_LIMIT_NONE,
															 true, false, false, false, false,
															 AG903_PGP_MGR_THR_OUT_SEL_SPF1,
															 AG903_PGP_MGR_THR_CLUT8_OUT_R));
		NOERRCHK(AG903_ENONE, AG903_PgpMgrEnableParamThresholdOp(pgp, true));

		/*--- ラべリング関連設定 ---*/
		/* 8連結判定、最大ラベル数48、テーブルIDの最大値0(=テーブル数-1) */
		/* 最大ラベル数はラベル数が多い画像では出力時間確保のため余裕をみて小さめに */
		AG903_PgpMgrSetConfigLabelingParameter(pgp, AG903_PGP_MGR_LBL_CNCT_8, 48, 0);
		/* ラべリング結果出力アドレス */
		AG903_PgpMgrSetConfigLabelingBaseAddr(pgp, gLabeling);
		/* ラべリング開始座標 */
		AG903_PgpMgrSetConfigLabelingPosition(pgp, 0, 0);
		/* ラべリング処理サイズ [px] */
		AG903_PgpMgrSetConfigLabelingSize(pgp, in_height, in_width);
		AG903_PgpMgrEnableParamLabeling(pgp, true);

		/*--- パイプラインの設定:出力段 ---*/
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetParamOutputFormat(pgp, AG903_PGP_MGR_OUTPUT_FMT_CLUT1_TO_CLUT1));
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetConfigOutputBaseAddr(pgp, gFramebuffer.buf));
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetParamOutputAddrDim(pgp, AG903_PGP_MGR_ADDR_DIMENTION_2));
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetParamOutputStride(pgp, in_width >> 3));
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetConfigOutputInterlaceFrame(pgp, AG903_PGP_MGR_OUTPUT_FRAME_SAVE_FIELD));
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetConfigOutputLimit(pgp, AG903_PGP_MGR_LIMIT_0_255));
		AG903_PgpMgrSetConfigOutputFramePadding(pgp, 0x7F);
	}
	else if (mode == CAP_MODE_VID) { /* VID入力 */
		/*--- パイプラインの設定:入力段 ---*/
		/* 入力ポート:VIDから入力 */
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetInputPort(pgp, vid));
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetParamInputMode(pgp, AG903_PGP_MGR_INPUT_MD_VSYNC));
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetParamInputHSyncDelay(pgp, CAP_HSYNC_DELAY));
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetParamInputFormat(pgp, AG903_PGP_MGR_INPUT_FMT_RGB888));
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetParamInputSize(pgp, in_height, in_width));
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetParamInputPosition(pgp, CAP_VID_VPOS_OFFSET, CAP_VID_HPOS_OFFSET));
		NOERRCHK(AG903_ENONE, AG903_PgpMgrEnableParamNRF(pgp, true));
		/* HSV変換 */
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetConfigHSVHLSConversion(pgp, AG903_PGP_MGR_CONVERT_HSV));
		NOERRCHK(AG903_ENONE, AG903_PgpMgrEnableParamHSV(pgp, true));

		/*--- パイプラインの設定:フィルタ段 ---*/

		int32_t coeff[9] = {  1,  2,  1,
							  2,  4,  2,
							  1,  2,  1 };

		/* 閾値処理 */
		/* Hue[0-239]: 0(R), 40(Y), 80(G), 120(C), 160(B), 200(M) */
		/* CENTER + MARGIN は 240未満に設定してください */
		const uint8_t CENTER = 140;
		const uint8_t MARGIN = 10;

		pallet[1] = getColorByHue(CENTER);
		frame_color[0]	= getColorByHue((CENTER+120) % 240); /* 補色 */

		int32_t top = CENTER%240 + MARGIN;
		int32_t btm = CENTER%240 - MARGIN;
		if(btm > 0) {
			NOERRCHK(AG903_ENONE, AG903_PgpMgrSetConfigThreshold1Bit(pgp, 0, 1, 0,
																	 255, 240,
																	 top, btm));
		} else {
			top = 240 - top;
			btm = -btm;
			NOERRCHK(AG903_ENONE, AG903_PgpMgrSetConfigThreshold1Bit(pgp, 0, 0, 1,
																	 255, 240,
																	 top, btm));
		}

		/* SPF */
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetConfigSpatialFilter(pgp, 4,
																 AG903_PGP_MGR_SPF_BORDER_NEIGHBOR,
																 0, 0, 0, coeff, 0));
		NOERRCHK(AG903_ENONE, AG903_PgpMgrEnableParamSpatialFilter(pgp, true));

		/* 1bitSPF */
		int32_t spf1_coeff[9] = {  1,  1,  1,
								   1,  1,  1,
								   1,  1,  1 };

		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetConfigThresholdSpatialFilter1Bit(pgp, spf1_coeff, 7));

		/* 閾値処理全体設定 */
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetConfigThreshold(pgp, AG903_PGP_MGR_LIMIT_NONE,
															 true, false, false, false, false,
															 AG903_PGP_MGR_THR_OUT_SEL_SPF1,
															 AG903_PGP_MGR_THR_CLUT8_OUT_R));
		NOERRCHK(AG903_ENONE, AG903_PgpMgrEnableParamThresholdOp(pgp, true));

		/*--- ラべリング関連設定 ---*/
		/* 8連結判定、最大ラベル数48、テーブルIDの最大値0(=テーブル数-1) */
		/* 最大ラベル数はラベル数が多い画像では出力時間確保のため余裕をみて小さめに */
		AG903_PgpMgrSetConfigLabelingParameter(pgp, AG903_PGP_MGR_LBL_CNCT_8, 48, 0);
		/* ラべリング結果出力アドレス */
		AG903_PgpMgrSetConfigLabelingBaseAddr(pgp, gLabeling);
		/* ラべリング開始座標 */
		AG903_PgpMgrSetConfigLabelingPosition(pgp, 0, 0);
		/* ラべリング処理サイズ [px] */
		AG903_PgpMgrSetConfigLabelingSize(pgp, in_height, in_width);
		AG903_PgpMgrEnableParamLabeling(pgp, true);

		/*--- パイプラインの設定:出力段 ---*/
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetParamOutputFormat(pgp, AG903_PGP_MGR_OUTPUT_FMT_CLUT1_TO_CLUT1));
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetConfigOutputBaseAddr(pgp, gFramebuffer.buf));
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetParamOutputAddrDim(pgp, AG903_PGP_MGR_ADDR_DIMENTION_2));
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetParamOutputStride(pgp, in_width >> 3));
		NOERRCHK(AG903_ENONE, AG903_PgpMgrSetConfigOutputLimit(pgp, AG903_PGP_MGR_LIMIT_0_255));
		AG903_PgpMgrSetConfigOutputFramePadding(pgp, 0x7F);
	} else {
		CAP_ErrPrintf("ERROR: INVALID MODE. ABORT.\r\n");
		return -AG903_EINVAL;
	}
	/* パイプライン割り当て */
	pipe_num = AG903_PgpMgrAssignPipeline(pgp);
	CAP_Printf("[PGP] Pipeline #%u assigned.\r\n", pipe_num);

	/* キャプチャ開始 */
	NOERRCHK(AG903_ENONE, AG903_PgpMgrExecLabeling(pgp, AG903_PGP_MGR_LBL_CMD_EXEC));
	NOERRCHK(AG903_ENONE, AG903_PgpMgrExecPipeline(pgp, AG903_PGP_MGR_CMD_EXEC_CONTINUE));

	gListenTaskID = acre_tsk((T_CTSK*)&ctsk_listen); /* 終了を受付 */
	while(1)
	{
		if (fLabelingDone) {
			CAP_showLabelingArea(); /* ラべリング結果の反映 */
		}

		if(gFlagQuit) {
			break;
		} else {
			rot_rdq(TSK_SELF);
		}
	}

	/* キャプチャ停止 */
	NOERRCHK(AG903_ENONE, AG903_PgpMgrExecPipeline(pgp, AG903_PGP_MGR_CMD_IDLE));

	/* パイプライン停止待ち */
	for(loop=0; loop<CAP_PIPESTOP_TIMEOUT; loop++) {
		NOERRCHK(AG903_ENONE, AG903_PgpMgrGetPipelineStatus(pgp, NULL, NULL, NULL, NULL, &status));
		if(AG903_PGP_MGR_STATE_IDLE == status) {
			break;
		}
		sys_dlytsk(10);
	}
	if(CAP_PIPESTOP_TIMEOUT<=loop) {
		CAP_ErrPrintf("ERROR: Pipeline Stop Failure.\r\n");
	}

	/* ラべリングキュークリア */
	NOERRCHK(AG903_ENONE, AG903_PgpMgrExecLabeling(pgp, AG903_PGP_MGR_LBL_CMD_DELETE));

	NOERRCHK(AG903_ENONE, AG903_PgpMgrReleaseAssignment(pgp));
	NOERRCHK(AG903_ENONE, AG903_PgpMgrReleasePipelineHandle(pgp));
	CAP_Printf("[PGP] Pipeline #%u released.\r\n", pipe_num);

	/* 終了処理 */
	NOERRCHK(AG903_ENONE, CAP_ReleaseDisplay());

	if (gListenTaskID != -1) {
		del_tsk(gListenTaskID);
		gListenTaskID = -1;
	}
	if (pallet != NULL) 			{ sys_free(pallet); }
	if (frame_color != NULL)		{ sys_free(frame_color); }
	if (gAnalyzed != NULL)			{ sys_free(gAnalyzed); }
	if (gLabeling != NULL)			{ sys_free(gLabeling); }
	if (gFramebuffer.buf != NULL)	{ sys_free(gFramebuffer.buf); }

	return gCAPErrCode;
}

static void listen_tsk(VP_INT exinf)
{
	((void)exinf);
	char input[2];

	CAP_Printf("Press enter key to exit.\r\n");
	COM_GetNum_Wait(input, sizeof(input));
	gFlagQuit = true;
}


static void labeling_done_handler(int32_t id, void *handle)
{
	((void)id);
	((void)handle);

	fLabelingDone = true;
}

void *ag903_pgp_common_malloc(uint32_t size)
{
	/* 8バイト境界制限 */
	return sys_memalign(size, 8);
}
void ag903_pgp_common_free(void *mem)
{
	sys_free(mem);
}

static void CAP_unpackLabelingTableHeader(CAP_LBL_TABLE_HEADER *hed, const uint64_t *table)
{
	hed->id         = *table>> 0 & 0xff;
	hed->index_use  = *table>>10 & 0xff;
	hed->start_line = *table>>20 & 0x3ff;
	hed->end_line   = *table>>30 & 0x3ff;
	hed->frame_id   = *table>>40 & 0xffff;
	hed->port       = *table>>56 & 0xf;
	hed->oflow      = *table>>60 & 0x1;
	hed->icpl       = *table>>61 & 0x1;
	hed->end        = *table>>63 & 0x1;
}

static void CAP_showLabelingArea(void)
{
	AG903_DSPMgrHandle *dsp = CAP_GetDSPHandle(dsp_ch);
	AG903_DSPMgrWinAttribute *attr[CAP_LBL_VWIN+1];
	CAP_LBL_TABLE_HEADER table_header = {0};
	int32_t table_num;
	int32_t idx;

	CAP_unpackLabelingTableHeader(&table_header, (const uint64_t *)gLabeling);
	table_num = AG903_PgpMgrAnalyzeLabelingTable(gLabeling, 1, &gAnalyzed);

	/* ウィンドウ0-3とウインドウ4を取得 */
	for(idx=0; idx<CAP_LBL_VWIN+1; idx++) {
		AG903_DSPMgrGetAttribute(dsp, idx, &attr[idx]);
	}

	if (table_num <= 0 || table_header.oflow) {
		/* 囲いを非表示 */
		attr[0]->conf.valid = false;
		attr[1]->conf.valid = false;
		attr[2]->conf.valid = false;
		attr[3]->conf.valid = false;
		if(table_num == -AG903_ENOMEM) {
			CAP_ErrPrintf("ERROR: AG903_PgpMgrAnalyzeLabelingTable(): not enough memory.\r\n");
		}
		if (table_num == 0) {
			CAP_Printf("Specified color is not in image (Hue:%d)\r\n", CAP_LABELING_TARGET_HUE);
		}
		if (table_header.oflow) {
			/* このサンプルではテーブルが1つまでのとき正しく解析できます */
			CAP_Printf("Too many labeling area.\r\n");
		}
		sys_dlytsk(3000);
	}
	else if (table_num > 0) {
		uint32_t xmax, ymax, xmin, ymin, area;
		uint64_t rec;
		uint32_t larger = 0;
		uint32_t area_id = 0;

		/* 枠用のウィンドウを不可視に */
		for(idx=0; idx<CAP_LBL_VWIN; idx++)
			attr[idx]->conf.valid = false;

		/* エリアが一番広いものを検索 */
		for (idx=0; idx<table_header.index_use; idx++) {
			if (*(uint32_t *)gAnalyzed & 1<<29) {
				break;
			}
			rec  = *(uint64_t *)((uint8_t *)gAnalyzed+(8*(idx+1)+4)) << 32;
			rec |= *(uint64_t *)((uint8_t *)gAnalyzed+(8*(idx+1)+0)) <<  0;
			area = (rec >> (uint64_t)40) & 0xFFFFF;
			if(area > larger) {
				larger = area;
				area_id = idx;
			}
		}

		/* 囲いを表示 */
		if(larger > 0) {
			idx = area_id;

			rec  = *(uint64_t *)((uint8_t *)gAnalyzed+(8*(idx+1)+4)) << 32;
			rec |= *(uint64_t *)((uint8_t *)gAnalyzed+(8*(idx+1)+0)) <<  0;
			area = (rec >> (uint64_t)40) & 0xFFFFF;
			xmin = (rec >> (uint64_t) 0) & 0x3FF;
			ymin = (rec >> (uint64_t)10) & 0x3FF;
			xmax = (rec >> (uint64_t)20) & 0x3FF;
			ymax = (rec >> (uint64_t)30) & 0x3FF;

			{
				{
					uint32_t wl, wh, hl, hh;
					wl = attr[CAP_LBL_VWIN]->position_x + xmin;
					wh = attr[CAP_LBL_VWIN]->position_x + xmax;
					hl = attr[CAP_LBL_VWIN]->position_y + ymin;
					hh = attr[CAP_LBL_VWIN]->position_y + ymax;
					attr[0]->position_x = wl;
					attr[0]->position_y = hl;
					attr[0]->destination_width  = (wh-wl)+1;
					attr[0]->destination_height = 1;
					attr[1]->position_x = wl;
					attr[1]->position_y = hl;
					attr[1]->destination_width  = 1;
					attr[1]->destination_height = (hh-hl)+1;
					attr[2]->position_x = wh;
					attr[2]->position_y = hl;
					attr[2]->destination_width  = 1;
					attr[2]->destination_height = (hh-hl)+1;
					attr[3]->position_x = wl;
					attr[3]->position_y = hh;
					attr[3]->destination_width = (wh-wl)+1;
					attr[3]->destination_height = 1;
				}
				attr[0]->conf.valid = true;
				attr[1]->conf.valid = true;
				attr[2]->conf.valid = true;
				attr[3]->conf.valid = true;
			}
		}
	}

	/* ウィンドウ0-3とウインドウ4を設定 */
	for(idx=0; idx<CAP_LBL_VWIN+1; idx++) {
		AG903_DSPMgrSetAttribute(dsp, idx);
	}

	if (table_num != -AG903_ENOMEM) {
		AG903_PgpMgrReleaseLabelingTable(gAnalyzed);
	}
	gAnalyzed = NULL;	/* メモリ解放済とする */
	fLabelingDone = false;
	NOERRCHK(AG903_ENONE, AG903_PgpMgrExecLabeling(pgp, AG903_PGP_MGR_LBL_CMD_EXEC));
	sys_dlytsk(100); /* ラべリング更新間隔 */
}
