« 禁則処理付き折り返し文字列描画【ShiftJIS版】 | トップページ | ノベルゲーム風に一文字ずつ表示する »

2011年11月13日 (日)

禁則処理付き折り返し文字列描画【ShiftJIS版/改行付き】

禁則処理付き折り返し文字列描画【ShiftJIS版】』を改行対応にした。

需要が大きいと思ったので作り置き。

#include "DxLib.h"
#include "VGLib.hpp"

/*
 *  ShiftJISマルチバイト文字の1バイト目かどうか
 */
static bool isLeadShiftJIS(unsigned char c);
/* ※関数本体省略:改行非対応版参照 */

/*
 *  行頭禁則文字かどうか
 */
static bool isProhibitLineBreakBeforeA(char mb[]);
/* ※関数本体省略:改行非対応版参照 */
/*
 *  行末禁則文字かどうか
 */
static bool isProhibitLineBreakAfterA(char mb[]);
/* ※関数本体省略:改行非対応版参照 */

struct WrapText
{
	static const int BUF_SIZE = 4*1024;
	static const int LINE_NUM = 64;
	char buf[BUF_SIZE];
	int line_top[LINE_NUM];
	int line_len[LINE_NUM];
	int line_num;
	int hold_len; // 送り出し保留分文字列長
	int hold_cnt; // 送り出し保留分確定猶予文字数カウンタ

	void clear();
	void update();
	void render(int width, int lineheight);
};

static const char *pStream;

void WrapText::clear(void)
{
	line_num = 0;
	line_top[0] = 0;
	line_len[0] = 0;
	hold_len = 0;
	hold_cnt = 0;
}

void WrapText::update()
{
}

void WrapText::render(int width, int lineheight)
{
	char mb[2];
	int mb_len;

	int ColorWhite = GetColor(255,255,255);

	while ((mb[0] = *pStream) != '\0')
	{
		pStream++;

		if (mb[0] == '\r') {
			/* 改行コードで強制改行 */
			int n = line_num;
			line_len[n] += hold_len;
			hold_len = 0;
			line_num++;
			line_top[line_num] = line_top[n] + line_len[n];
			line_len[line_num] = 0;
			continue;
		}

		mb_len = 1;
		if (isLeadShiftJIS(mb[0])) {
			mb[1] = *(pStream++);
			mb_len = 2;
		}

		int n = line_num;
		for (int i=0; i<mb_len; ++i) {
			buf[line_top[n]+line_len[n]+hold_len+i] = mb[i];
		}
		if (isProhibitLineBreakBeforeA(mb)) {
			/* 行頭禁則文字のとき */
			// 確定猶予カウントをリセット
			hold_cnt = 0;
		}
		else if (isProhibitLineBreakAfterA(mb)) {
			/* 行末禁則文字のとき */
			if (hold_cnt == 0) {
				// 猶予なしなら保留分を確定
				line_len[n] += hold_len;
				hold_len = 0;
			}
			// 続く1文字を確定猶予
			hold_cnt = 1;
		}
		else {
			/* 禁則文字以外のとき */
			if (hold_cnt > 0) {
				// 猶予カウンタ分は保留
				hold_cnt--;
			}
			else {
				// 猶予なしなら保留分を確定
				line_len[n] += hold_len;
				hold_len = 0;
			}
		}
		int w = GetDrawStringWidth(&buf[line_top[n]], line_len[n]+hold_len+mb_len);
		if (w > width) {
			if (line_len[n] == 0) {
				// 1行すべて保留分のとき確定する
				line_len[n] += hold_len;
				hold_len = 0;
			}
			line_num++;
			line_top[line_num] = line_top[n] + line_len[n];
			line_len[line_num] = 0;
		}
		hold_len += mb_len;
	}
	if (hold_len > 0) {
		// 保留分が残っていたら確定する
		line_len[line_num] += hold_len;
		hold_len = 0;
		hold_cnt = 0;
	}
	if (line_len[line_num] > 0) {
		// 最終行の〆
		int n = line_num;
		line_num++;
		line_top[line_num] = line_top[n] + line_len[n];
		line_len[line_num] = 0;
	}

	/*
	 *  描画
	 */
	for (int i=0; i<line_num; ++i) {
		char c = buf[line_top[i]+line_len[i]];
		buf[line_top[i]+line_len[i]] = '\0';
		DrawString(0, lineheight*i, &buf[line_top[i]], ColorWhite);
		buf[line_top[i]+line_len[i]] = c;
	}
}

struct Main : public VGCanvas
{
	WrapText wt;
	int width;

	Main() : VGCanvas() {}
	void init();
	void frameUpdate(int skiiped);
	void frameRender(void);
} canvas;

VGCanvas *VGLibInit()
{
	ChangeWindowMode(TRUE);
	return &canvas;
}

#define FONT_SIZE 20
#define MIN_WIDTH (FONT_SIZE * 10)

static const char *SampleText =
	"口語表現は禁則処理がめんどうなのだ。\r"
	"「ギャーーーーーーーーッ!\r やっちゃった。」\r"
	"という台詞の場合、幅が狭いと破綻するが仕様がない。\r\r"
	"そこは運用でカバーだ。";

void Main::init()
{
	SetFontThickness(0);
	SetFontSize(FONT_SIZE);

	width = MIN_WIDTH;

	wt.clear();
	pStream = SampleText;
}

void Main::frameUpdate(int)
{
	int Sx, Sy, Cb;
	GetScreenState(&Sx, &Sy, &Cb);

	int width_old = width;
	if (CheckHitKey(KEY_INPUT_LEFT)) {
		width -= 2;
		if (width < MIN_WIDTH) {
			width = MIN_WIDTH;
		}
	}
	if (CheckHitKey(KEY_INPUT_RIGHT)) {
		width += 2;
		if (width > Sx) {
			width = Sx;
		}
	}
	if (width != width_old) {
		wt.clear();
		pStream = SampleText;
	}
	wt.update();
}

void Main::frameRender(void)
{
	int Sx, Sy, Cb;
	GetScreenState(&Sx, &Sy, &Cb);
	ClearDrawScreen();
	DrawLine(width, 0, width, Sy, GetColor(255, 0, 0));
	wt.render(width, FONT_SIZE);
}

|

« 禁則処理付き折り返し文字列描画【ShiftJIS版】 | トップページ | ノベルゲーム風に一文字ずつ表示する »

C/C++」カテゴリの記事

DXライブラリ」カテゴリの記事

コメント

コメントを書く



(ウェブ上には掲載しません)




« 禁則処理付き折り返し文字列描画【ShiftJIS版】 | トップページ | ノベルゲーム風に一文字ずつ表示する »