Skip to content

Instantly share code, notes, and snippets.

@voidproc
Last active December 13, 2016 13:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save voidproc/84a2e3d64c4575cae11692a0ff2ef814 to your computer and use it in GitHub Desktop.
Save voidproc/84a2e3d64c4575cae11692a0ff2ef814 to your computer and use it in GitHub Desktop.
Output a sprite sheet of font
// * config.iniの設定に従いスプライトフォント画像を生成する
// * 画像出力後、画像を表示する
// * Zキー、Xキーでズームイン・ズームアウト
//
// * "config.ini"の各項目の説明:
//
// fontname = 美咲ゴシック フォント名
// fontsize = 4 フォントサイズ
// texture_width = 512 出力画像のサイズ
// texture_height = 512 〃
// box_width = 8 1文字分の枠のサイズ(box_autosize=1の場合)
// box_height = 8 〃
// box_autosize = 0 1文字分の枠のサイズを自動計算する(0 or 1)
// shadow_x = 1 影のオフセット
// shadow_y = 1 〃
// shadow_color = 96 影の色
// transparent = 1 背景を透明化する(0 or 1)
// outname = chars_misakifont 出力ファイル名(.png, .bin)
#include <Siv3D.hpp>
#include <unordered_map>
#if 0
class ImageFont
{
public:
ImageFont(const FilePath& image, const FilePath& settings) : tex_(image)
{
BinaryReader reader(settings);
reader.read(w_);
reader.read(h_);
int l;
reader.read(l);
for (int i : step(l))
{
wchar c;
reader.read(c);
Point p;
reader.read(p);
charPos_[c] = p;
}
}
void draw(const String& text, const Vec2& pos, const Color& color = Palette::White, const double& scale = 1.0)
{
int i = 0;
for (auto c : text)
{
tex_(charPos_[c], w_, h_).scale(scale).draw(pos.x + i * w_ * scale, pos.y, color);
++i;
}
}
private:
int w_;
int h_;
const Texture tex_;
std::unordered_map<wchar, Point> charPos_;
};
#endif
String printableChars0_255()
{
String s(256, L' ');
for (auto i : step(s.length))
{
if (IsPrint(i))
{
s[i] = wchar_t(i);
}
}
return s;
}
void Main()
{
// 設定
struct Config
{
String fontname;
int fontsize;
uint32 texture_width;
uint32 texture_height;
int box_width;
int box_height;
int box_autosize; // bool
int shadow_x;
int shadow_y;
int shadow_color;
int transparent; // bool
String outname; // bool
int window_width;
int window_height;
};
Config config;
INIReader ini(L"config.ini");
config.fontname = ini.getOr<String>(L"fontname", L"美咲ゴシック").trim();
config.fontsize = ini.getOr<int>(L"fontsize", 4);
config.texture_width = ini.getOr<uint32>(L"texture_width", 512);
config.texture_height = ini.getOr<uint32>(L"texture_height", 512);
config.box_width = ini.getOr<int>(L"box_width", 0);
config.box_height = ini.getOr<int>(L"box_height", 0);
config.box_autosize = ini.getOr<int>(L"box_autosize", 0);
config.shadow_x = ini.getOr<int>(L"shadow_x", 0);
config.shadow_y = ini.getOr<int>(L"shadow_y", 0);
config.shadow_color = ini.getOr<int>(L"shadow_color", 128);
config.transparent = ini.getOr<int>(L"transparent", 0);
config.outname = ini.getOr<String>(L"outname", L"out");
config.window_width = ini.getOr<int>(L"window_width", 1024);
config.window_height = ini.getOr<int>(L"window_height", 768);
ini.close();
Window::Resize(config.window_width, config.window_height);
Graphics::SetBackground(Color(20, 60, 20));
Graphics2D::SetSamplerState(SamplerState::ClampPoint);
// テキスト描画先テクスチャ
RenderTexture rt { config.texture_width, config.texture_height, Palette::Black };
Graphics2D::SetRenderTarget(rt);
// 描画フォント
const Font font(config.fontsize, config.fontname, FontStyle::Bitmap);
// 描画する文字
const String chars = printableChars0_255() + TextReader(L"chars.txt").readAll();
// 文字の横幅の最大値を知りたい
int maxWidth = 0;
for (auto c : chars)
{
maxWidth = Max(font(c).region().w, maxWidth);
}
// 文字の縦幅
int maxHeight = font.height;
// 1文字あたりの枠 (box) の大きさ
const int box_width = config.box_autosize ? maxWidth + config.shadow_x : config.box_width;
const int box_height = config.box_autosize ? font.height + config.shadow_y : config.box_height;
// テクスチャへ描画できる横方向の文字数
const int nCharsX = rt.width / box_width;
// 文字の描画位置保存用
std::unordered_map<wchar, Point> charPos;
// テクスチャ rt へテキスト描画
int i = 0;
for (auto c : chars)
{
// 文字の描画位置
const int x = (i % nCharsX) * box_width;
const int y = (i / nCharsX) * box_height;
// 文字を描画
// 横位置は枠の中央
const int sx = x + (Max(0, ((box_width - config.shadow_x) - font(c).region().w)) + 0.5) / 2 + config.shadow_x;
font(c).draw(sx, y + config.shadow_y, Color(config.shadow_color)); //影
font(c).draw(sx - config.shadow_x, y, Color(254));
charPos[c] = Point(x, y);
++i;
}
Graphics2D::SetRenderTarget(Graphics::GetSwapChainTexture());
// テクスチャをファイル出力
Graphics::Render2D();
rt.saveDDS(config.outname + L".dds");
rt.release();
// 背景色の透明化
Image img(config.outname + L".dds");
if (config.transparent)
{
img.forEach([](auto& c) {
if (c == Palette::Black)
c.a = 0;
});
}
img.savePNG(config.outname + L".png");
const Texture tex(img);
img.release();
// 文字の描画位置などを保存する
BinaryWriter writer(config.outname + L".bin");
writer.write(box_width);
writer.write(box_height);
writer.write(chars.length);
for (auto c : chars)
{
writer.write(c);
writer.write(charPos[c]);
}
writer.flush();
writer.close();
int scale = 1;
while (System::Update())
{
if (Input::KeyZ.clicked)
scale = Min(4, scale + 1);
if (Input::KeyX.clicked)
scale = Max(1, scale - 1);
tex.scale(scale).draw().drawFrame(1.0, 0.0, Color(Palette::Lime, 128));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment