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