634 lines
18 KiB
C++
634 lines
18 KiB
C++
#include "FontEditor.h"
|
|
#include "bzip2/bz2wrap.h"
|
|
#include "gui/interface/Textbox.h"
|
|
#include "gui/interface/Engine.h"
|
|
#include "gui/interface/Point.h"
|
|
#include "gui/interface/Button.h"
|
|
#include "gui/interface/ScrollPanel.h"
|
|
#include "graphics/Graphics.h"
|
|
#include "SimulationConfig.h"
|
|
#include <stdexcept>
|
|
#include <fstream>
|
|
#include <iterator>
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <cstdint>
|
|
#include <SDL.h>
|
|
|
|
extern unsigned char *font_data;
|
|
extern unsigned int *font_ptrs;
|
|
extern unsigned int (*font_ranges)[2];
|
|
|
|
void FontEditor::ReadDataFile(ByteString dataFile)
|
|
{
|
|
std::fstream file;
|
|
file.open(dataFile, std::ios_base::in | std::ios_base::binary);
|
|
if(!file)
|
|
throw std::runtime_error("Could not open " + dataFile);
|
|
file.seekg(0, std::ios_base::end);
|
|
std::vector<char> fileData(file.tellg());
|
|
file.seekg(0);
|
|
file.read(&fileData[0], fileData.size());
|
|
file.close();
|
|
|
|
std::vector<char> fontDataBuf;
|
|
std::vector<int> fontPtrsBuf;
|
|
std::vector< std::array<int, 2> > fontRangesBuf;
|
|
if (BZ2WDecompress(fontDataBuf, fileData.data(), fileData.size()) != BZ2WDecompressOk)
|
|
{
|
|
throw std::runtime_error("Could not decompress font data");
|
|
}
|
|
int first = -1;
|
|
int last = -1;
|
|
char *begin = &fontDataBuf[0];
|
|
char *ptr = &fontDataBuf[0];
|
|
char *end = &fontDataBuf[0] + fontDataBuf.size();
|
|
while (ptr != end)
|
|
{
|
|
if (ptr + 4 > end)
|
|
{
|
|
throw std::runtime_error("Could not decompress font data");
|
|
}
|
|
auto codePoint = *reinterpret_cast<uint32_t *>(ptr) & 0xFFFFFFU;
|
|
if (codePoint >= 0x110000U)
|
|
{
|
|
throw std::runtime_error("Could not decompress font data");
|
|
}
|
|
auto width = *reinterpret_cast<uint8_t *>(ptr + 3);
|
|
if (width > 64)
|
|
{
|
|
throw std::runtime_error("Could not decompress font data");
|
|
}
|
|
if (ptr + 4 + width * 3 > end)
|
|
{
|
|
throw std::runtime_error("Could not decompress font data");
|
|
}
|
|
auto cp = (int)codePoint;
|
|
if (last >= cp)
|
|
{
|
|
throw std::runtime_error("Could not decompress font data");
|
|
}
|
|
if (first != -1 && last + 1 < cp)
|
|
{
|
|
fontRangesBuf.push_back({ { first, last } });
|
|
first = -1;
|
|
}
|
|
if (first == -1)
|
|
{
|
|
first = cp;
|
|
}
|
|
last = cp;
|
|
fontPtrsBuf.push_back(ptr + 3 - begin);
|
|
ptr += width * 3 + 4;
|
|
}
|
|
if (first != -1)
|
|
{
|
|
fontRangesBuf.push_back({ { first, last } });
|
|
}
|
|
fontRangesBuf.push_back({ { 0, 0 } });
|
|
|
|
fontData.clear();
|
|
for (auto ch : fontDataBuf)
|
|
{
|
|
fontData.push_back(ch);
|
|
}
|
|
fontPtrs.clear();
|
|
for (auto ptr : fontPtrsBuf)
|
|
{
|
|
fontPtrs.push_back(ptr);
|
|
}
|
|
fontRanges.clear();
|
|
for (auto rng : fontRangesBuf)
|
|
{
|
|
fontRanges.push_back({ { (unsigned int)rng[0], (unsigned int)rng[1] } });
|
|
}
|
|
}
|
|
|
|
void FontEditor::WriteDataFile(ByteString dataFile, std::vector<unsigned char> const &fontData, std::vector<unsigned int> const &fontPtrs, std::vector<std::array<unsigned int, 2> > const &fontRanges)
|
|
{
|
|
std::fstream file;
|
|
file.open(dataFile, std::ios_base::out | std::ios_base::trunc | std::ios_base::binary);
|
|
if(!file)
|
|
throw std::runtime_error("Could not open " + dataFile);
|
|
|
|
std::vector<char> uncompressed;
|
|
size_t pos = 0;
|
|
for (size_t i = 0; pos < fontPtrs.size() && fontRanges[i][1]; i++)
|
|
{
|
|
for (String::value_type ch = fontRanges[i][0]; ch <= fontRanges[i][1]; ch++)
|
|
{
|
|
uncompressed.push_back((char)ch);
|
|
uncompressed.push_back((char)(ch >> 8));
|
|
uncompressed.push_back((char)(ch >> 16));
|
|
auto ptr = fontPtrs[pos++];
|
|
auto width = fontData[ptr];
|
|
uncompressed.push_back(width);
|
|
for (auto j = 0; j < 3 * width; ++j)
|
|
{
|
|
uncompressed.push_back(fontData[ptr + 1 + j]);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<char> compressed;
|
|
if (BZ2WCompress(compressed, uncompressed.data(), uncompressed.size()) != BZ2WCompressOk)
|
|
{
|
|
throw std::runtime_error("Could not compress font data");
|
|
}
|
|
file.write(compressed.data(), compressed.size());
|
|
|
|
file.close();
|
|
}
|
|
|
|
void FontEditor::UnpackData(
|
|
std::map<String::value_type, unsigned char> &fontWidths,
|
|
std::map<String::value_type, std::array<std::array<char, MAX_WIDTH>, FONT_H> > &fontPixels,
|
|
std::vector<unsigned char> const &fontData,
|
|
std::vector<unsigned int> const &fontPtrs,
|
|
std::vector<std::array<unsigned int, 2> > const &fontRanges)
|
|
{
|
|
fontWidths.clear();
|
|
fontPixels.clear();
|
|
size_t pos = 0;
|
|
for(size_t range = 0; fontRanges[range][1]; range++)
|
|
for(String::value_type ch = fontRanges[range][0]; ch <= fontRanges[range][1]; ch++)
|
|
{
|
|
unsigned char const *pointer = &fontData[fontPtrs[pos]];
|
|
int width = fontWidths[ch] = *(pointer++);
|
|
int pixels = 0;
|
|
int data = 0;
|
|
for(int j = 0; j < FONT_H; j++)
|
|
for(int i = 0; i < width; i++)
|
|
{
|
|
if(!pixels)
|
|
{
|
|
data = *(pointer++);
|
|
pixels = 4;
|
|
}
|
|
fontPixels[ch][j][i] = data & 3;
|
|
data >>= 2;
|
|
pixels--;
|
|
}
|
|
pos++;
|
|
}
|
|
}
|
|
|
|
void FontEditor::PackData(
|
|
std::map<String::value_type, unsigned char> const &fontWidths,
|
|
std::map<String::value_type, std::array<std::array<char, MAX_WIDTH>, FONT_H> > const &fontPixels,
|
|
std::vector<unsigned char> &fontData,
|
|
std::vector<unsigned int> &fontPtrs,
|
|
std::vector<std::array<unsigned int, 2> > &fontRanges)
|
|
{
|
|
fontData.clear();
|
|
fontPtrs.clear();
|
|
fontRanges.clear();
|
|
bool first = true;
|
|
String::value_type rangeStart = 0;
|
|
String::value_type prev = 0;
|
|
for(std::map<String::value_type, unsigned char>::const_iterator it = fontWidths.begin(); it != fontWidths.end(); it++)
|
|
{
|
|
String::value_type ch = it->first;
|
|
if(first)
|
|
{
|
|
rangeStart = ch;
|
|
first = false;
|
|
}
|
|
else
|
|
if(ch != prev + 1)
|
|
{
|
|
fontRanges.push_back({rangeStart, prev});
|
|
rangeStart = ch;
|
|
}
|
|
|
|
fontPtrs.push_back(fontData.size());
|
|
fontData.push_back(it->second);
|
|
|
|
int pixels = 0;
|
|
int data = 0;
|
|
for(int j = 0; j < FONT_H; j++)
|
|
for(int i = 0; i < it->second; i++)
|
|
{
|
|
if(pixels == 4)
|
|
{
|
|
fontData.push_back(data);
|
|
pixels = 0;
|
|
data = 0;
|
|
}
|
|
data >>= 2;
|
|
data |= fontPixels.at(ch)[j][i] << 6;
|
|
pixels++;
|
|
}
|
|
if(pixels)
|
|
fontData.push_back(data);
|
|
|
|
prev = ch;
|
|
}
|
|
fontRanges.push_back({rangeStart, prev});
|
|
fontRanges.push_back({0, 0});
|
|
}
|
|
|
|
class StretchLabel: public ui::Label
|
|
{
|
|
using Label::Label;
|
|
public:
|
|
int WrappedLines() const
|
|
{
|
|
return displayTextWrapper.WrappedLines();
|
|
}
|
|
};
|
|
|
|
class StretchTextbox: public ui::Textbox
|
|
{
|
|
using Textbox::Textbox;
|
|
public:
|
|
int WrappedLines() const
|
|
{
|
|
return displayTextWrapper.WrappedLines();
|
|
}
|
|
};
|
|
|
|
constexpr int FONT_SCALE = 16;
|
|
FontEditor::FontEditor(ByteString _dataFile):
|
|
ui::Window(ui::Point(0, 0), ui::Point(WINDOWW, WINDOWH)),
|
|
dataFile(_dataFile),
|
|
currentChar(0x80),
|
|
fgR(255), fgG(255), fgB(255), bgR(0), bgG(0), bgB(0),
|
|
grid(1),
|
|
rulers(1)
|
|
{
|
|
ReadDataFile(dataFile);
|
|
UnpackData(fontWidths, fontPixels, fontData, fontPtrs, fontRanges);
|
|
font_data = fontData.data();
|
|
font_ptrs = fontPtrs.data();
|
|
font_ranges = (unsigned int (*)[2])fontRanges.data();
|
|
|
|
int baseline = 8 + FONT_H * FONT_SCALE + 4 + FONT_H + 4 + 1;
|
|
int currentX = 1;
|
|
|
|
ui::Button *prev = new ui::Button(ui::Point(currentX, baseline), ui::Point(17, 17), 0xE016);
|
|
currentX += 18;
|
|
prev->SetActionCallback({ [this] { PrevChar(); } });
|
|
AddComponent(prev);
|
|
|
|
currentCharTextbox = new ui::Textbox(ui::Point(currentX, baseline), ui::Point(31, 17));
|
|
currentX += 32;
|
|
currentCharTextbox->SetActionCallback({ [this] {
|
|
unsigned int number = currentCharTextbox->GetText().ToNumber<unsigned int>(Format::Hex(), true);
|
|
if(number <= 0x10FFFF)
|
|
currentChar = number;
|
|
} });
|
|
UpdateCharNumber();
|
|
AddComponent(currentCharTextbox);
|
|
|
|
ui::Button *next = new ui::Button(ui::Point(currentX, baseline), ui::Point(17, 17), 0xE015);
|
|
currentX += 18;
|
|
next->SetActionCallback({ [this] { NextChar(); } });
|
|
AddComponent(next);
|
|
|
|
ui::Button *shrink = new ui::Button(ui::Point(currentX, baseline), ui::Point(17, 17), "><");
|
|
currentX += 18;
|
|
shrink->SetActionCallback({ [this] { ShrinkChar(); } });
|
|
AddComponent(shrink);
|
|
|
|
ui::Button *grow = new ui::Button(ui::Point(currentX, baseline), ui::Point(17, 17), "<>");
|
|
currentX += 18;
|
|
grow->SetActionCallback({ [this] { GrowChar(); } });
|
|
AddComponent(grow);
|
|
|
|
ui::Button *add = new ui::Button(ui::Point(currentX, baseline), ui::Point(36, 17), "Add");
|
|
currentX += 37;
|
|
add->SetActionCallback({ [this] {
|
|
if (fontWidths.find(currentChar) == fontWidths.end())
|
|
{
|
|
savedButton->SetToggleState(false);
|
|
fontWidths[currentChar] = 5;
|
|
fontPixels[currentChar];
|
|
}
|
|
} });
|
|
AddComponent(add);
|
|
|
|
ui::Button *remove = new ui::Button(ui::Point(currentX, baseline), ui::Point(36, 17), "Remove");
|
|
currentX += 37;
|
|
remove->SetActionCallback({ [this] {
|
|
if (fontWidths.find(currentChar) != fontWidths.end())
|
|
{
|
|
savedButton->SetToggleState(false);
|
|
fontWidths.erase(currentChar);
|
|
fontPixels.erase(currentChar);
|
|
}
|
|
} });
|
|
AddComponent(remove);
|
|
|
|
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({ [this, showGrid] {
|
|
grid = showGrid->GetToggleState();
|
|
} });
|
|
AddComponent(showGrid);
|
|
|
|
ui::Button *showRulers = new ui::Button(ui::Point(currentX, baseline), ui::Point(32, 17), "Rulers");
|
|
currentX += 33;
|
|
showRulers->SetTogglable(true);
|
|
showRulers->SetToggleState(rulers);
|
|
showRulers->SetActionCallback({ [this, showRulers] {
|
|
rulers = showRulers->GetToggleState();
|
|
} });
|
|
AddComponent(showRulers);
|
|
|
|
baseline += 18;
|
|
currentX = 1;
|
|
|
|
int *refs[6] = {&fgR, &fgG, &fgB, &bgR, &bgG, &bgB};
|
|
for(int i = 0; i < 6; i++)
|
|
{
|
|
ui::Textbox *colorComponent = new ui::Textbox(ui::Point(currentX, baseline), ui::Point(27, 17), String::Build(*refs[i]));
|
|
currentX += 28;
|
|
colorComponent->SetActionCallback({ [colorComponent, refs, i] {
|
|
*refs[i] = colorComponent->GetText().ToNumber<int>(true);
|
|
} });
|
|
AddComponent(colorComponent);
|
|
}
|
|
|
|
baseline += 18;
|
|
currentX = 1;
|
|
|
|
ui::Button *render = new ui::Button(ui::Point(currentX, baseline), ui::Point(50, 17), "Render");
|
|
currentX += 51;
|
|
render->SetActionCallback({ [this] { Render(); } });
|
|
AddComponent(render);
|
|
|
|
savedButton = new ui::Button(ui::Point(currentX, baseline), ui::Point(50, 17), "Save");
|
|
currentX += 51;
|
|
savedButton->SetTogglable(true);
|
|
savedButton->SetToggleState(true);
|
|
savedButton->SetActionCallback({ [this] { Save(); } });
|
|
AddComponent(savedButton);
|
|
|
|
baseline += 18;
|
|
|
|
ui::ScrollPanel *outputPanel = new ui::ScrollPanel(ui::Point(Size.X / 2, baseline), ui::Point(Size.X / 2, Size.Y - baseline));
|
|
AddComponent(outputPanel);
|
|
StretchLabel *outputPreview = new StretchLabel(ui::Point(0, 0), ui::Point(Size.X / 2, 0), "");
|
|
outputPreview->SetMultiline(true);
|
|
outputPreview->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
|
|
outputPreview->Appearance.VerticalAlign = ui::Appearance::AlignTop;
|
|
outputPanel->AddChild(outputPreview);
|
|
|
|
ui::ScrollPanel *inputPanel = new ui::ScrollPanel(ui::Point(0, baseline), ui::Point(Size.X / 2, Size.Y - baseline));
|
|
AddComponent(inputPanel);
|
|
StretchTextbox *inputPreview = new StretchTextbox(ui::Point(0, 0), ui::Point(Size.X / 2, 0));
|
|
inputPreview->SetMultiline(true);
|
|
inputPreview->SetInputType(ui::Textbox::Multiline);
|
|
inputPreview->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
|
|
inputPreview->Appearance.VerticalAlign = ui::Appearance::AlignTop;
|
|
auto textChangedCallback = [outputPreview, outputPanel, inputPreview, inputPanel] {
|
|
String str = inputPreview->GetText();
|
|
size_t at = 0;
|
|
StringBuilder text;
|
|
while(at < str.size())
|
|
{
|
|
unsigned int ch1, ch2;
|
|
if(str[at] != ' ')
|
|
if(String::Split split1 = str.SplitNumber(ch1, Format::Hex(), at))
|
|
{
|
|
if(str[split1.PositionAfter()] == ':')
|
|
if(String::Split split2 = str.SplitNumber(ch2, Format::Hex(), split1.PositionAfter() + 1))
|
|
{
|
|
for(unsigned int ch = ch1; ch <= ch2; ch++)
|
|
text << String::value_type(ch);
|
|
at = split2.PositionAfter();
|
|
continue;
|
|
}
|
|
text << String::value_type(ch1);
|
|
at = split1.PositionAfter();
|
|
}
|
|
else
|
|
{
|
|
text << str[at++];
|
|
}
|
|
else
|
|
at++;
|
|
}
|
|
outputPreview->SetText(text.Build());
|
|
outputPanel->InnerSize.Y = outputPreview->Size.Y = std::max(outputPreview->WrappedLines(), 1) * FONT_H + 2;
|
|
inputPanel->InnerSize.Y = inputPreview->Size.Y = std::max(inputPreview->WrappedLines(), 1) * FONT_H + 2;
|
|
};
|
|
inputPreview->SetActionCallback({ textChangedCallback });
|
|
inputPanel->AddChild(inputPreview);
|
|
|
|
StringBuilder input;
|
|
input << Format::Hex() << Format::Width(2);
|
|
for(auto p : fontRanges)
|
|
if(p[1] >= 0x20)
|
|
{
|
|
if(p[0] < 0x20)
|
|
p[0] = 0x20;
|
|
if(p[0] == p[1])
|
|
input << p[0] << "\n";
|
|
else
|
|
input << p[0] << ":" << p[1] << "\n";
|
|
}
|
|
inputPreview->SetText(input.Build());
|
|
textChangedCallback();
|
|
AddComponent(inputPreview);
|
|
}
|
|
|
|
FontEditor::FontEditor(ByteString target, ByteString source):
|
|
ui::Window(ui::Point(0, 0), ui::Point(WINDOWW, WINDOWH))
|
|
{
|
|
ReadDataFile(target);
|
|
std::map<String::value_type, unsigned char> tgtFontWidths, srcFontWidths;
|
|
std::map<String::value_type, std::array<std::array<char, MAX_WIDTH>, FONT_H> > tgtFontPixels, srcFontPixels;
|
|
UnpackData(tgtFontWidths, tgtFontPixels, fontData, fontPtrs, fontRanges);
|
|
ReadDataFile(source);
|
|
UnpackData(srcFontWidths, srcFontPixels, fontData, fontPtrs, fontRanges);
|
|
for(auto const &p : srcFontPixels)
|
|
if(tgtFontPixels.count(p.first))
|
|
{
|
|
bool same = tgtFontWidths[p.first] == srcFontWidths[p.first];
|
|
if(same)
|
|
for(int j = 0; j < FONT_H; j++)
|
|
for(int i = 0; i < tgtFontWidths[p.first]; i++)
|
|
same = same && tgtFontPixels[p.first][j][i] == srcFontPixels[p.first][j][i];
|
|
if(!same)
|
|
std::cout << "U+" << std::hex << (unsigned int)p.first << " is present in both files and is different!" << std::endl;
|
|
}
|
|
else
|
|
{
|
|
std::cout << "Adding U+" << std::hex << (unsigned int)p.first << " to the target" << std::endl;
|
|
tgtFontWidths[p.first] = srcFontWidths[p.first];
|
|
tgtFontPixels[p.first] = p.second;
|
|
}
|
|
std::vector<unsigned char> tmpFontData;
|
|
std::vector<unsigned int> tmpFontPtrs;
|
|
std::vector<std::array<unsigned int, 2> > tmpFontRanges;
|
|
PackData(tgtFontWidths, tgtFontPixels, tmpFontData, tmpFontPtrs, tmpFontRanges);
|
|
WriteDataFile(target, tmpFontData, tmpFontPtrs, tmpFontRanges);
|
|
}
|
|
|
|
void FontEditor::OnDraw()
|
|
{
|
|
Graphics *g = GetGraphics();
|
|
|
|
if(fontWidths.find(currentChar) != fontWidths.end())
|
|
{
|
|
int width = fontWidths[currentChar];
|
|
std::array<std::array<char, MAX_WIDTH>, FONT_H> const &pixels = fontPixels[currentChar];
|
|
|
|
int areaWidth = 8 + width * FONT_SCALE + 8;
|
|
g->DrawFilledRect(RectSized(Vec2{ 0, 0 }, Vec2{ areaWidth, 8 + FONT_H * FONT_SCALE + 4 + FONT_H + 4 }), RGB<uint8_t>(bgR, bgG, bgB));
|
|
for(int j = 0; j < FONT_H; j++)
|
|
for(int i = 0; i < width; i++)
|
|
g->BlendFilledRect(RectSized(Vec2{ 8 + i * FONT_SCALE, 8 + j * FONT_SCALE }, Vec2{ FONT_SCALE - grid, FONT_SCALE - grid }), RGBA<uint8_t>(fgR, fgG, fgB, pixels[j][i] * 255 / 3));
|
|
|
|
for(int j = 0; j < FONT_H; j++)
|
|
for(int i = 0; i < width; i++)
|
|
g->BlendPixel({ 8 + i, 8 + FONT_H * FONT_SCALE + 4 + j }, RGBA<uint8_t>(fgR, fgG, fgB, pixels[j][i] * 255 / 3));
|
|
|
|
|
|
if(rulers)
|
|
{
|
|
g->DrawLine({ 0, 7 + 0 * FONT_SCALE }, { areaWidth - 1, 7 + 0 * FONT_SCALE }, 0x808080_rgb);
|
|
g->DrawLine({ 0, 7 + 2 * FONT_SCALE }, { areaWidth - 1, 7 + 2 * FONT_SCALE }, 0x808080_rgb);
|
|
g->DrawLine({ 0, 7 + 4 * FONT_SCALE }, { areaWidth - 1, 7 + 4 * FONT_SCALE }, 0x808080_rgb);
|
|
g->DrawLine({ 0, 7 + 9 * FONT_SCALE }, { areaWidth - 1, 7 + 9 * FONT_SCALE }, 0x808080_rgb);
|
|
g->DrawLine({ 0, 7 + 12 * FONT_SCALE }, { areaWidth - 1, 7 + 12 * FONT_SCALE }, 0x808080_rgb);
|
|
|
|
g->DrawLine({ 7, 8 }, { 7, 7 + FONT_H * FONT_SCALE }, 0x808080_rgb);
|
|
g->DrawLine({ 7 + width * FONT_SCALE, 8}, { 7 + width * FONT_SCALE, 7 + FONT_H * FONT_SCALE }, 0x808080_rgb);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g->BlendText({ 8, 8 }, "No character", 0xFF0000_rgb .WithAlpha(255));
|
|
}
|
|
}
|
|
|
|
void FontEditor::OnMouseDown(int x, int y, unsigned button)
|
|
{
|
|
if(fontWidths.find(currentChar) != fontWidths.end())
|
|
{
|
|
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::Translate(std::array<std::array<char, MAX_WIDTH>, FONT_H> &pixels, int dx, int dy)
|
|
{
|
|
std::array<std::array<char, MAX_WIDTH>, FONT_H> old = pixels;
|
|
for(int j = 0; j < FONT_H; j++)
|
|
for(int i = 0; i < MAX_WIDTH; i++)
|
|
if(i - dx >= 0 && i - dx < MAX_WIDTH && j - dy >= 0 && j - dy < FONT_H)
|
|
pixels[j][i] = old[j - dy][i - dx];
|
|
else
|
|
pixels[j][i] = 0;
|
|
savedButton->SetToggleState(false);
|
|
}
|
|
|
|
void FontEditor::OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt)
|
|
{
|
|
if (IsFocused(NULL))
|
|
{
|
|
switch(scan)
|
|
{
|
|
case SDL_SCANCODE_UP:
|
|
if(shift)
|
|
Translate(fontPixels[currentChar], 0, -1);
|
|
break;
|
|
case SDL_SCANCODE_DOWN:
|
|
if(shift)
|
|
Translate(fontPixels[currentChar], 0, 1);
|
|
break;
|
|
case SDL_SCANCODE_LEFT:
|
|
if(shift)
|
|
Translate(fontPixels[currentChar], -1, 0);
|
|
else
|
|
PrevChar();
|
|
break;
|
|
case SDL_SCANCODE_RIGHT:
|
|
if(shift)
|
|
Translate(fontPixels[currentChar], 1, 0);
|
|
else
|
|
NextChar();
|
|
break;
|
|
case SDL_SCANCODE_ESCAPE:
|
|
case SDL_SCANCODE_Q:
|
|
if(savedButton->GetToggleState())
|
|
ui::Engine::Ref().Exit();
|
|
else
|
|
ui::Engine::Ref().ConfirmExit();
|
|
break;
|
|
case SDL_SCANCODE_C:
|
|
clipboardWidth = fontWidths[currentChar];
|
|
clipboardPixels = fontPixels[currentChar];
|
|
break;
|
|
case SDL_SCANCODE_V:
|
|
fontWidths[currentChar] = clipboardWidth;
|
|
fontPixels[currentChar] = clipboardPixels;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void FontEditor::UpdateCharNumber()
|
|
{
|
|
currentCharTextbox->SetText(String::Build(Format::Hex((unsigned int)currentChar)));
|
|
}
|
|
|
|
void FontEditor::PrevChar()
|
|
{
|
|
if(currentChar > 0)
|
|
currentChar--;
|
|
UpdateCharNumber();
|
|
}
|
|
|
|
void FontEditor::NextChar()
|
|
{
|
|
if(currentChar <= 0x10FFFF)
|
|
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, fontRanges);
|
|
font_data = fontData.data();
|
|
font_ptrs = fontPtrs.data();
|
|
font_ranges = (unsigned int (*)[2])fontRanges.data();
|
|
}
|
|
|
|
void FontEditor::Save()
|
|
{
|
|
std::vector<unsigned char> tmpFontData;
|
|
std::vector<unsigned int> tmpFontPtrs;
|
|
std::vector<std::array<unsigned int, 2> > tmpFontRanges;
|
|
PackData(fontWidths, fontPixels, tmpFontData, tmpFontPtrs, tmpFontRanges);
|
|
WriteDataFile(dataFile, tmpFontData, tmpFontPtrs, tmpFontRanges);
|
|
savedButton->SetToggleState(true);
|
|
}
|