Add a new font editor

This commit is contained in:
mniip 2018-04-29 18:49:40 +03:00
parent 04c875120a
commit c0c550e920
5 changed files with 637 additions and 4 deletions

View File

@ -67,6 +67,7 @@ AddSconsOption('static', False, False, "Compile statically.")
AddSconsOption('opengl', False, False, "Build with OpenGL interface support.")
AddSconsOption('opengl-renderer', False, False, "Build with OpenGL renderer support (turns on --opengl).") #Note: this has nothing to do with --renderer, only tells the game to render particles with opengl
AddSconsOption('renderer', False, False, "Build the save renderer.")
AddSconsOption('font', False, False, "Build the font editor.")
AddSconsOption('wall', False, False, "Error on all warnings.")
AddSconsOption('no-warnings', False, False, "Disable all compiler warnings.")
@ -266,7 +267,7 @@ def findLibs(env, conf):
else:
FatalError("SDL.h not found")
if not GetOption('nolua') and not GetOption('renderer'):
if not GetOption('nolua') and not GetOption('renderer') and not GetOption('font'):
#Look for Lua
if platform == "FreeBSD":
luaver = "lua-5.1"
@ -494,7 +495,7 @@ if GetOption('static'):
#Add other flags and defines
if not GetOption('nofft'):
env.Append(CPPDEFINES=['GRAVFFT'])
if not GetOption('nolua') and not GetOption('renderer'):
if not GetOption('nolua') and not GetOption('renderer') and not GetOption('font'):
env.Append(CPPDEFINES=['LUACONSOLE'])
if GetOption('opengl') or GetOption('opengl-renderer'):
@ -507,6 +508,9 @@ if GetOption('renderer'):
else:
env.Append(CPPDEFINES=['USE_SDL'])
if GetOption('font'):
env.Append(CPPDEFINES=['FONTEDITOR'])
if GetOption("wall"):
if msvc:
env.Append(CCFLAGS=['/WX'])
@ -540,7 +544,7 @@ if GetOption('beta'):
#Generate list of sources to compile
sources = Glob("src/*.cpp") + Glob("src/*/*.cpp") + Glob("src/*/*/*.cpp") + Glob("generated/*.cpp")
if not GetOption('nolua') and not GetOption('renderer'):
if not GetOption('nolua') and not GetOption('renderer') and not GetOption('font'):
sources += Glob("src/lua/socket/*.c") + Glob("src/lua/LuaCompat.c")
if platform == "Windows":
@ -559,7 +563,11 @@ elif platform == "Darwin":
if GetOption('output'):
programName = GetOption('output')
else:
programName = GetOption('renderer') and "render" or "powder"
programName = "powder"
if GetOption('renderer'):
programName = "render"
if GetOption('font'):
programName = "font"
if "BIT" in env and env["BIT"] == 64:
programName += "64"
if isX86 and GetOption('no-sse'):

View File

@ -1,6 +1,7 @@
#ifndef FONT_H_CHECK
#define FONT_H_CHECK
#define FONT_H 12
#ifndef FONTEDITOR
#ifdef INCLUDE_FONTDATA
unsigned char font_data[] = {
0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@ -299,5 +300,9 @@ short font_ptrs[] = {
extern unsigned char font_data[];
extern short font_ptrs[];
#endif
#else
extern unsigned char *font_data;
extern short *font_ptrs;
#endif
#endif

View File

@ -50,6 +50,8 @@ extern "C" {
#include "gui/game/GameController.h"
#include "gui/game/GameView.h"
#include "gui/font/FontEditor.h"
#include "gui/dialogues/ErrorMessage.h"
#include "gui/dialogues/ConfirmPrompt.h"
#include "gui/interface/Keys.h"
@ -1108,6 +1110,7 @@ int main(int argc, char * argv[])
try {
#endif
#ifndef FONTEDITOR
gameController = new GameController();
engine->ShowWindow(gameController->GetView());
@ -1204,6 +1207,12 @@ int main(int argc, char * argv[])
}
}
#else // FONTEDITOR
if(argc <= 1)
throw std::runtime_error("Not enough arguments");
engine->ShowWindow(new FontEditor(argv[1]));
#endif
//initial mouse coords
int sdl_x, sdl_y;
SDL_GetMouseState(&sdl_x, &sdl_y);

544
src/gui/font/FontEditor.cpp Normal file
View File

@ -0,0 +1,544 @@
#include <stdexcept>
#include <sstream>
#include <fstream>
#include <iterator>
#include <iomanip>
#include <iostream>
#include "FontEditor.h"
#include "Config.h"
#include "gui/interface/Engine.h"
#include "gui/interface/Point.h"
#include "gui/interface/Button.h"
#include "gui/interface/Mouse.h"
#include "gui/interface/Keys.h"
#include "graphics/Graphics.h"
#ifdef FONTEDITOR
unsigned char *font_data;
short *font_ptrs;
void FontEditor::ReadHeader(std::string header)
{
std::fstream file;
file.open(header, std::ios_base::in);
if(!file)
throw std::runtime_error("Could not open " + header);
file >> std::skipws;
std::string word;
while(word != "font_data[]")
file >> word;
file >> word >> word;
size_t startFontData = file.tellg();
fontData.clear();
do
{
unsigned int value;
file >> std::hex >> value;
if(!file.fail())
{
fontData.push_back(value);
file >> word;
}
}
while(!file.fail());
file.clear();
size_t endFontData = file.tellg();
while(word != "font_ptrs[]")
file >> word;
file >> word >> word;
size_t startFontPtrs = file.tellg();
fontPtrs.clear();
do
{
unsigned int value;
file >> std::hex >> value;
if(!file.fail())
{
fontPtrs.push_back(value);
file >> word;
}
}
while(!file.fail());
file.clear();
size_t endFontPtrs = file.tellg();
do
{
file >> word;
}
while(!file.fail());
file.clear();
size_t eof = file.tellg();
file.seekg(0);
beforeFontData = std::string(startFontData, 0);
file.read(&beforeFontData[0], startFontData);
file.seekg(endFontData);
afterFontData = std::string(startFontPtrs - endFontData, 0);
file.read(&afterFontData[0], startFontPtrs - endFontData);
file.seekg(endFontData);
afterFontData = std::string(startFontPtrs - endFontData, 0);
file.read(&afterFontData[0], startFontPtrs - endFontData);
file.seekg(endFontPtrs);
afterFontPtrs = std::string(eof - endFontPtrs, 0);
file.read(&afterFontPtrs[0], eof - endFontPtrs);
file.close();
}
void FontEditor::WriteHeader(std::string header, std::vector<unsigned char> const &fontData, std::vector<short> const &fontPtrs)
{
std::fstream file;
file.open(header, std::ios_base::out | std::ios_base::trunc);
if(!file)
throw std::runtime_error("Could not open " + header);
file << std::setfill('0') << std::hex << std::uppercase;
file << beforeFontData << std::endl;
for(int ch = 0; ch < 256; ch++)
{
file << " " << "0x" << std::setw(2) << (unsigned int)fontData[fontPtrs[ch]] << ", ";
for(int i = fontPtrs[ch] + 1; i < (int)(ch == (int)fontPtrs.size() - 1 ? fontData.size() : fontPtrs[ch + 1]); i++)
file << " " << "0x" << std::setw(2) << (unsigned int)fontData[i] << ",";
file << std::endl;
}
file << afterFontData;
for(int ch = 0; ch < 256; ch++)
{
if(!(ch & 7))
file << std::endl << " ";
else
file << " ";
file << "0x" << std::setw(4) << (unsigned int)fontPtrs[ch] << ",";
}
file << std::endl << afterFontPtrs;
file.close();
}
void FontEditor::UnpackData(
std::array<char, 256> &fontWidths,
std::array<std::array<std::array<char, MAX_WIDTH>, FONT_H>, 256> &fontPixels,
std::vector<unsigned char> const &fontData,
std::vector<short> const &fontPtrs)
{
for(int ch = 0; ch < 256; ch++)
{
unsigned char const *data = &fontData[fontPtrs[ch]];
int bits = 0;
int pixels = 0;
int width = fontWidths[ch] = *(data++);
for(int j = 0; j < FONT_H; j++)
for(int i = 0; i < width; i++)
{
if(!bits)
{
pixels = *(data++);
bits = 8;
}
fontPixels[ch][j][i] = pixels & 3;
pixels >>= 2;
bits -= 2;
}
}
}
void FontEditor::PackData(
std::array<char, 256> const &fontWidths,
std::array<std::array<std::array<char, MAX_WIDTH>, FONT_H>, 256> const &fontPixels,
std::vector<unsigned char> &fontData,
std::vector<short> &fontPtrs)
{
fontPtrs.clear();
fontData.clear();
for(int ch = 0; ch < 256; ch++)
{
fontPtrs.push_back(fontData.size());
fontData.push_back(fontWidths[ch]);
int bits = 0;
int pixels = 0;
for(int j = 0; j < FONT_H; j++)
for(int i = 0; i < fontWidths[ch]; i++)
{
if(bits == 8)
{
fontData.push_back(pixels);
bits = 0;
pixels = 0;
}
pixels >>= 2;
pixels |= fontPixels[ch][j][i] << 6;
bits += 2;
}
if(bits)
fontData.push_back(pixels);
}
}
#define FONT_SCALE 16
FontEditor::FontEditor(std::string _header):
ui::Window(ui::Point(0, 0), ui::Point(WINDOWW, WINDOWH)),
header(_header),
currentChar(0x80),
fgR(255), fgG(255), fgB(255), bgR(0), bgG(0), bgB(0),
grid(1),
rulers(1)
{
ReadHeader(header);
UnpackData(fontWidths, fontPixels, fontData, fontPtrs);
font_data = fontData.data();
font_ptrs = fontPtrs.data();
int baseline = 8 + FONT_H * FONT_SCALE + 4 + FONT_H + 4 + 1;
int currentX = 1;
class PrevCharAction : public ui::ButtonAction
{
FontEditor *v;
public:
PrevCharAction(FontEditor *_v): v(_v) {}
void ActionCallback(ui::Button *)
{
v->PrevChar();
}
};
ui::Button *prev = new ui::Button(ui::Point(currentX, baseline), ui::Point(17, 17), "\x96");
currentX += 18;
prev->SetActionCallback(new PrevCharAction(this));
AddComponent(prev);
class CharNumberAction : public ui::TextboxAction
{
FontEditor *v;
public:
CharNumberAction(FontEditor *_v): v(_v) {}
void TextChangedCallback(ui::Textbox *)
{
unsigned int number;
std::stringstream ss(v->currentCharTextbox->GetText());
ss >> std::hex >> number;
if(number < 256)
v->currentChar = number;
}
};
currentCharTextbox = new ui::Textbox(ui::Point(currentX, baseline), ui::Point(31, 17));
currentX += 32;
currentCharTextbox->SetActionCallback(new CharNumberAction(this));
UpdateCharNumber();
AddComponent(currentCharTextbox);
class NextCharAction : public ui::ButtonAction
{
FontEditor *v;
public:
NextCharAction(FontEditor *_v): v(_v) {}
void ActionCallback(ui::Button *)
{
v->NextChar();
}
};
ui::Button *next = new ui::Button(ui::Point(currentX, baseline), ui::Point(17, 17), "\x95");
currentX += 18;
next->SetActionCallback(new NextCharAction(this));
AddComponent(next);
class ShrinkCharAction : public ui::ButtonAction
{
FontEditor *v;
public:
ShrinkCharAction(FontEditor *_v): v(_v) {}
void ActionCallback(ui::Button *)
{
v->ShrinkChar();
}
};
ui::Button *shrink = new ui::Button(ui::Point(currentX, baseline), ui::Point(17, 17), "><");
currentX += 18;
shrink->SetActionCallback(new ShrinkCharAction(this));
AddComponent(shrink);
class GrowCharAction : public ui::ButtonAction
{
FontEditor *v;
public:
GrowCharAction(FontEditor *_v): v(_v) {}
void ActionCallback(ui::Button *)
{
v->GrowChar();
}
};
ui::Button *grow = new ui::Button(ui::Point(currentX, baseline), ui::Point(17, 17), "<>");
currentX += 18;
grow->SetActionCallback(new GrowCharAction(this));
AddComponent(grow);
class ToggleAction : public ui::ButtonAction
{
int &toggle;
public:
ToggleAction(int &_toggle): toggle(_toggle) {}
void ActionCallback(ui::Button *button)
{
toggle = button->GetToggleState();
}
};
ui::Button *showGrid = new ui::Button(ui::Point(currentX, baseline), ui::Point(32, 17), "Grid");
currentX += 33;
showGrid->SetTogglable(true);
showGrid->SetToggleState(grid);
showGrid->SetActionCallback(new ToggleAction(grid));
AddComponent(showGrid);
ui::Button *showRulers = new ui::Button(ui::Point(currentX, baseline), ui::Point(32, 17), "Rulers");
currentX += 33;
showRulers->SetTogglable(true);
showRulers->SetToggleState(grid);
showRulers->SetActionCallback(new ToggleAction(rulers));
AddComponent(showRulers);
baseline += 18;
currentX = 1;
class ColorComponentAction : public ui::TextboxAction
{
int &color;
public:
ColorComponentAction(int &_color): color(_color) {}
void TextChangedCallback(ui::Textbox *box)
{
std::stringstream ss(box->GetText());
ss >> color;
}
};
int *refs[6] = {&fgR, &fgG, &fgB, &bgR, &bgG, &bgB};
for(int i = 0; i < 6; i++)
{
std::stringstream ss;
ss << *refs[i];
ui::Textbox *colorComponent = new ui::Textbox(ui::Point(currentX, baseline), ui::Point(27, 17), ss.str());
currentX += 28;
colorComponent->SetActionCallback(new ColorComponentAction(*refs[i]));
AddComponent(colorComponent);
}
baseline += 18;
currentX = 1;
class RenderAction : public ui::ButtonAction
{
FontEditor *v;
public:
RenderAction(FontEditor *_v): v(_v) {}
void ActionCallback(ui::Button *)
{
v->Render();
}
};
ui::Button *render = new ui::Button(ui::Point(currentX, baseline), ui::Point(50, 17), "Render");
currentX += 51;
render->SetActionCallback(new RenderAction(this));
AddComponent(render);
class SaveAction : public ui::ButtonAction
{
FontEditor *v;
public:
SaveAction(FontEditor *_v): v(_v) {}
void ActionCallback(ui::Button *)
{
v->Save();
}
};
savedButton = new ui::Button(ui::Point(currentX, baseline), ui::Point(50, 17), "Save");
currentX += 51;
savedButton->SetTogglable(true);
savedButton->SetToggleState(true);
savedButton->SetActionCallback(new SaveAction(this));
AddComponent(savedButton);
baseline += 18;
outputPreview = new ui::Label(ui::Point(0, baseline + (Size.Y - baseline) * 3 / 5), ui::Point(Size.X, (Size.Y - baseline) * 2 / 5), "");
outputPreview->SetMultiline(true);
outputPreview->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
outputPreview->Appearance.VerticalAlign = ui::Appearance::AlignTop;
AddComponent(outputPreview);
class PreviewAction : public ui::TextboxAction
{
FontEditor *v;
public:
PreviewAction(FontEditor *_v): v(_v) {}
void TextChangedCallback(ui::Textbox *box)
{
std::stringstream ss(box->GetText());
std::string text;
while(!ss.eof())
{
if(ss.peek() == '\n')
{
text.push_back('\n');
ss.get();
}
unsigned int ch;
ss >> std::hex >> ch;
if(ss.fail())
{
ss.clear();
char ch = ss.get();
if(!ss.eof())
text.push_back(ch);
continue;
}
text.push_back((char)ch);
}
v->outputPreview->SetText(text);
}
};
ui::Textbox *inputPreview = new ui::Textbox(ui::Point(0, baseline), ui::Point(Size.X, (Size.Y - baseline) * 3 / 5));
inputPreview->SetMultiline(true);
inputPreview->SetInputType(ui::Textbox::Multiline);
inputPreview->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
inputPreview->Appearance.VerticalAlign = ui::Appearance::AlignTop;
inputPreview->SetActionCallback(new PreviewAction(this));
std::stringstream input;
for(unsigned int ch = 0x20; ch <= 0xFF; ch++)
{
if(!(ch & 0x3F))
input << "20 ";
input << std::hex << std::setw(2) << ch << " ";
}
inputPreview->SetText(input.str());
PreviewAction(this).TextChangedCallback(inputPreview);
AddComponent(inputPreview);
}
void FontEditor::OnDraw()
{
Graphics *g = GetGraphics();
int areaWidth = 8 + fontWidths[currentChar] * FONT_SCALE + 8;
g->fillrect(0, 0, areaWidth, 8 + FONT_H * FONT_SCALE + 4 + FONT_H + 4, bgR, bgG, bgB, 255);
for(int j = 0; j < FONT_H; j++)
for(int i = 0; i < fontWidths[currentChar]; i++)
g->fillrect(8 + i * FONT_SCALE, 8 + j * FONT_SCALE, FONT_SCALE - grid, FONT_SCALE - grid, fgR, fgG, fgB, fontPixels[currentChar][j][i] * 255 / 3);
for(int j = 0; j < FONT_H; j++)
for(int i = 0; i < fontWidths[currentChar]; i++)
g->blendpixel(8 + i, 8 + FONT_H * FONT_SCALE + 4 + j, fgR, fgG, fgB, fontPixels[currentChar][j][i] * 255 / 3);
if(rulers)
{
g->draw_line(0, 7 + 0 * FONT_SCALE , areaWidth - 1, 7 + 0 * FONT_SCALE, 128, 128, 128, 255);
g->draw_line(0, 7 + 2 * FONT_SCALE , areaWidth - 1, 7 + 2 * FONT_SCALE, 128, 128, 128, 255);
g->draw_line(0, 7 + 4 * FONT_SCALE , areaWidth - 1, 7 + 4 * FONT_SCALE, 128, 128, 128, 255);
g->draw_line(0, 7 + 9 * FONT_SCALE , areaWidth - 1, 7 + 9 * FONT_SCALE, 128, 128, 128, 255);
g->draw_line(0, 7 + 12 * FONT_SCALE , areaWidth - 1, 7 + 12 * FONT_SCALE, 128, 128, 128, 255);
g->draw_line(7, 8, 7, 7 + FONT_H * FONT_SCALE, 128, 128, 128, 255);
g->draw_line(7 + fontWidths[currentChar] * FONT_SCALE, 8, 7 + fontWidths[currentChar] * FONT_SCALE, 7 + FONT_H * FONT_SCALE, 128, 128, 128, 255);
}
}
void FontEditor::OnMouseDown(int x, int y, unsigned button)
{
x = (x - 8) / FONT_SCALE;
y = (y - 8) / FONT_SCALE;
if(x >= 0 && y >= 0 && x < fontWidths[currentChar] && y < FONT_H)
{
if(button == SDL_BUTTON_LEFT)
fontPixels[currentChar][y][x] = (fontPixels[currentChar][y][x] + 1) % 4;
else
fontPixels[currentChar][y][x] = (fontPixels[currentChar][y][x] + 3) % 4;
savedButton->SetToggleState(false);
}
}
void FontEditor::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt)
{
if(IsFocused(NULL))
{
switch(key)
{
case SDLK_LEFT:
PrevChar(); break;
case SDLK_RIGHT:
PrevChar(); break;
case SDLK_ESCAPE:
case 'q':
if(savedButton->GetToggleState())
ui::Engine::Ref().Exit();
else
ui::Engine::Ref().ConfirmExit();
break;
}
}
}
void FontEditor::UpdateCharNumber()
{
std::stringstream ss;
ss << std::hex << currentChar;
currentCharTextbox->SetText(ss.str());
}
void FontEditor::PrevChar()
{
if(currentChar > 0)
currentChar--;
UpdateCharNumber();
}
void FontEditor::NextChar()
{
if(currentChar < 255)
currentChar++;
UpdateCharNumber();
}
void FontEditor::ShrinkChar()
{
if(fontWidths[currentChar] > 0)
fontWidths[currentChar]--;
savedButton->SetToggleState(false);
}
void FontEditor::GrowChar()
{
if(fontWidths[currentChar] < MAX_WIDTH - 1)
fontWidths[currentChar]++;
savedButton->SetToggleState(false);
}
void FontEditor::Render()
{
PackData(fontWidths, fontPixels, fontData, fontPtrs);
font_data = fontData.data();
font_ptrs = fontPtrs.data();
}
void FontEditor::Save()
{
std::vector<unsigned char> tmpFontData;
std::vector<short> tmpFontPtrs;
PackData(fontWidths, fontPixels, tmpFontData, tmpFontPtrs);
WriteHeader(header, tmpFontData, tmpFontPtrs);
savedButton->SetToggleState(true);
}
#endif

67
src/gui/font/FontEditor.h Normal file
View File

@ -0,0 +1,67 @@
#ifndef FONTEDITOR_H
#define FONTEDITOR_H
#include <vector>
#include <array>
#include "font.h"
#include "gui/interface/Window.h"
#include "gui/interface/Textbox.h"
#define MAX_WIDTH 64
class FontEditor: public ui::Window
{
private:
std::string header;
std::array<char, 256> fontWidths;
std::array<std::array<std::array<char, MAX_WIDTH>, FONT_H>, 256> fontPixels;
std::vector<unsigned char> fontData;
std::vector<short> fontPtrs;
std::string beforeFontData;
std::string afterFontData;
std::string afterFontPtrs;
void ReadHeader(std::string header);
void WriteHeader(std::string header, std::vector<unsigned char> const &fontData, std::vector<short> const &fontPtrs);
static void PackData(
std::array<char, 256> const &fontWidths,
std::array<std::array<std::array<char, MAX_WIDTH>, FONT_H>, 256> const &fontPixels,
std::vector<unsigned char> &fontData,
std::vector<short> &fontPtrs);
static void UnpackData(
std::array<char, 256> &fontWidths,
std::array<std::array<std::array<char, MAX_WIDTH>, FONT_H>, 256> &fontPixels,
std::vector<unsigned char> const &fontData,
std::vector<short> const &fontPtrs);
ui::Textbox *currentCharTextbox;
ui::Button *savedButton;
ui::Label *outputPreview;
int currentChar;
int fgR, fgG, fgB;
int bgR, bgG, bgB;
int grid;
int rulers;
void UpdateCharNumber();
void PrevChar();
void NextChar();
void ShrinkChar();
void GrowChar();
void Render();
void Save();
public:
FontEditor(std::string header);
void OnDraw();
void OnMouseDown(int x, int y, unsigned button);
void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt);
};
#endif