First commit, Vystem v0.1
This commit is contained in:
259
Blastproof/fontgen/fontgen.cpp
Normal file
259
Blastproof/fontgen/fontgen.cpp
Normal file
@@ -0,0 +1,259 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
using namespace std;
|
||||
namespace fs=filesystem;
|
||||
struct color {
|
||||
uint8_t r,g,b;
|
||||
};
|
||||
struct pixel {
|
||||
bool shade_1;
|
||||
bool check_1;
|
||||
bool shade_2;
|
||||
bool check_2;
|
||||
bool shade_3;
|
||||
bool check_3;
|
||||
bool shade_4;
|
||||
bool enabled;
|
||||
};
|
||||
struct character {
|
||||
uint32_t codepoint;
|
||||
vector<uint8_t> data;
|
||||
};
|
||||
struct fbm_header {
|
||||
uint8_t sig[3]={'F','B','M'};
|
||||
uint8_t encoding;
|
||||
uint32_t charnum;
|
||||
uint8_t width;
|
||||
uint8_t height;
|
||||
};
|
||||
uint32_t charname_to_uint32(string charname) {
|
||||
uint32_t value=0;
|
||||
stringstream ss;
|
||||
ss<<hex<<charname;
|
||||
ss>>value;
|
||||
int shift=(8-charname.size())*4;
|
||||
value<<=shift;
|
||||
return value;
|
||||
}
|
||||
int get_shade_from_color(const color &c,const color gradient[16]) {
|
||||
for (int i=0;i<16;++i) {
|
||||
if (gradient[i].r==c.r && gradient[i].g==c.g && gradient[i].b==c.b) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
uint8_t pixel_to_byte(pixel p) {
|
||||
uint8_t out=0x00;
|
||||
out|=(uint8_t)p.shade_1<<7;
|
||||
out|=(uint8_t)p.check_1<<6;
|
||||
out|=(uint8_t)p.shade_2<<5;
|
||||
out|=(uint8_t)p.check_2<<4;
|
||||
out|=(uint8_t)p.shade_3<<3;
|
||||
out|=(uint8_t)p.check_3<<2;
|
||||
out|=(uint8_t)p.shade_4<<1;
|
||||
out|=(uint8_t)p.enabled;
|
||||
return out;
|
||||
}
|
||||
map<uint32_t,vector<unsigned char>> chars_data;
|
||||
vector<string> known_encoding={"ascii","utf8","utf16"};
|
||||
int main(int argc,char **argv) {
|
||||
if (argc!=5) {
|
||||
cout<<"[Fontgen] Error: wrong amount of arguments."<<endl;
|
||||
return -1;
|
||||
}
|
||||
string bg_color=string(argv[1]);
|
||||
string ft_color=string(argv[2]);
|
||||
if (bg_color.size()!=7 || bg_color[0]!='#') {
|
||||
cout<<"[Fontgen] Error: invalid background color."<<endl;
|
||||
return -1;
|
||||
}
|
||||
if (ft_color.size()!=7 || ft_color[0]!='#') {
|
||||
cout<<"[Fontgen] Error: invalid font color."<<endl;
|
||||
return -1;
|
||||
}
|
||||
for (size_t i=1;i<7;++i) {
|
||||
char bg=bg_color[i];
|
||||
char ft=ft_color[i];
|
||||
if (!isxdigit(static_cast<unsigned char>(bg))) {
|
||||
cout<<"[Fontgen] Error: invalid background color."<<endl;
|
||||
return -1;
|
||||
}
|
||||
if (!isxdigit(static_cast<unsigned char>(ft))) {
|
||||
cout<<"[Fontgen] Error: invalid font color."<<endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
color start,end;
|
||||
start.r=static_cast<unsigned char>(std::stoi(bg_color.substr(1,2),nullptr,16));
|
||||
start.g=static_cast<unsigned char>(std::stoi(bg_color.substr(3,2),nullptr,16));
|
||||
start.b=static_cast<unsigned char>(std::stoi(bg_color.substr(5,2),nullptr,16));
|
||||
end.r=static_cast<unsigned char>(std::stoi(ft_color.substr(1,2),nullptr,16));
|
||||
end.g=static_cast<unsigned char>(std::stoi(ft_color.substr(3,2),nullptr,16));
|
||||
end.b=static_cast<unsigned char>(std::stoi(ft_color.substr(5,2),nullptr,16));
|
||||
color gradient[16];
|
||||
for (int i=0;i<16;++i) {
|
||||
gradient[i].r=start.r+i*(end.r-start.r)/15.0f;
|
||||
gradient[i].g=start.g+i*(end.g-start.g)/15.0f;
|
||||
gradient[i].b=start.b+i*(end.b-start.b)/15.0f;
|
||||
}
|
||||
string encoding=string(argv[3]);
|
||||
if (find(known_encoding.begin(),known_encoding.end(),encoding)==known_encoding.end()) {
|
||||
cout<<"[Fontgen] Error: encoding not supported."<<endl;
|
||||
return -1;
|
||||
}
|
||||
string font_folder=string(argv[4]);
|
||||
if (!fs::exists(font_folder)) {
|
||||
cout<<"[Fontgen] Error: provided path doesn't exist."<<endl;
|
||||
return -1;
|
||||
}
|
||||
if (!fs::is_directory(font_folder)) {
|
||||
cout<<"[Fontgen] Error: provided path isn't a folder."<<endl;
|
||||
return -1;
|
||||
}
|
||||
int width=0,height=0;
|
||||
int i=0;
|
||||
for (auto const &entry:fs::directory_iterator(font_folder)) {
|
||||
if (!entry.is_regular_file()) {
|
||||
cout<<"[Fontgen] Error: "<<entry.path()<<" isn't a file."<<endl;
|
||||
return -1;
|
||||
}
|
||||
string filenam=entry.path().filename().string();
|
||||
if (filenam.size()<6) {
|
||||
cout<<"[Fontgen] Warning: "<<entry.path()<<" filename too short. Skipping."<<endl;
|
||||
continue;
|
||||
}
|
||||
string ext=filenam.substr(filenam.size()-4);
|
||||
if (ext!=".png" && ext!=".PNG") {
|
||||
cout<<"[Fontgen] Warning: "<<entry.path()<<" is not a PNG. Skipping."<<endl;
|
||||
continue;
|
||||
}
|
||||
if (filenam.size()<5 || filenam.substr(0,2)!="0x") {
|
||||
cout<<"[Fontgen] Warning: "<<entry.path()<<" has an ambiguious file name. Skipping."<<endl;
|
||||
continue;
|
||||
}
|
||||
string hexpart=filenam.substr(2,filenam.size()-6);
|
||||
if (hexpart.empty() || hexpart.size()>8) {
|
||||
cout<<"[Fontgen] Warning: "<<entry.path()<<" has an invalid hex code. Skipping."<<endl;
|
||||
continue;
|
||||
}
|
||||
uint32_t key=charname_to_uint32(hexpart);
|
||||
int channel=0,w=0,h=0;
|
||||
unsigned char* data=stbi_load(entry.path().string().c_str(),&w,&h,&channel,0);
|
||||
if (!data) {
|
||||
cout<<"[Fontgen] Error: couldn't read file: "<<filenam<<endl;
|
||||
return -1;
|
||||
}
|
||||
if (i==0) {
|
||||
width=w;
|
||||
height=h;
|
||||
} else {
|
||||
if (w!=width || h!=height) {
|
||||
cout<<"[Fontgen] Error: all file aren't the same size. File that caused the error: "<<filenam<<endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (channel!=3) {
|
||||
cout<<"[Fontgen] Error: only files with three channels are accepted. File that caused the error: "<<filenam<<endl;
|
||||
return -1;
|
||||
}
|
||||
vector<unsigned char> pixels(data,data+w*h*channel);
|
||||
stbi_image_free(data);
|
||||
chars_data[key]=std::move(pixels);
|
||||
i++;
|
||||
}
|
||||
vector<character> characters;
|
||||
for (auto it:chars_data) {
|
||||
character chara;
|
||||
chara.codepoint=it.first;
|
||||
uint8_t byte1=(chara.codepoint>>24) & 0xFF;
|
||||
uint8_t byte2=(chara.codepoint>>16) & 0xFF;
|
||||
uint8_t byte3=(chara.codepoint>>8) & 0xFF;
|
||||
bool pcheck_1=(byte1>>1) & 1;
|
||||
bool pcheck_2=(byte2>>3) & 1;
|
||||
bool pcheck_3=(byte3>>5) & 1;
|
||||
for (int y=0;y<height;++y) {
|
||||
for (int x=0;x<width;++x) {
|
||||
int idx=(y*width+x)*3;
|
||||
color c;
|
||||
c.r=it.second[idx];
|
||||
c.g=it.second[idx+1];
|
||||
c.b=it.second[idx+2];
|
||||
int i=get_shade_from_color(c,gradient);
|
||||
if (i==-1 || i>15 || i<0) {
|
||||
cout<<"[Fontgen] Error: found a color that isn't in shade map. Codepoint that caused the error: "<<hex<<setw(2)<<setfill('0')<<it.first<<endl;
|
||||
return -1;
|
||||
}
|
||||
uint8_t shade=i;
|
||||
pixel p{};
|
||||
p.shade_1=(shade>>3)&1;
|
||||
p.shade_2=(shade>>2)&1;
|
||||
p.shade_3=(shade>>1)&1;
|
||||
p.shade_4=(shade>>0)&1;
|
||||
if (shade!=0) {
|
||||
p.enabled=true;
|
||||
} else {
|
||||
p.enabled=false;
|
||||
}
|
||||
p.check_1=pcheck_1;
|
||||
p.check_2=pcheck_2;
|
||||
p.check_3=pcheck_3;
|
||||
uint8_t byte=pixel_to_byte(p);
|
||||
chara.data.push_back(byte);
|
||||
}
|
||||
}
|
||||
characters.push_back(chara);
|
||||
}
|
||||
fbm_header header;
|
||||
header.charnum=(uint32_t)characters.size();
|
||||
if (width>255 || width<1) {
|
||||
cout<<"[Fontgen] Error: width isn't between 1 and 255."<<endl;
|
||||
return -1;
|
||||
}
|
||||
header.width=width;
|
||||
if (height>255 || height<1) {
|
||||
cout<<"[Fontgen] Error: height isn't between 1 and 255."<<endl;
|
||||
return -1;
|
||||
}
|
||||
header.height=height;
|
||||
if (encoding=="ascii") {
|
||||
header.encoding=1;
|
||||
} else if (encoding=="utf8") {
|
||||
header.encoding=8;
|
||||
} else if (encoding=="utf16") {
|
||||
header.encoding=16;
|
||||
} else {
|
||||
header.encoding=255;
|
||||
}
|
||||
vector<unsigned char> fbm_font;
|
||||
size_t charsize=4+header.width*header.height;
|
||||
fbm_font.resize(10+header.charnum*charsize);
|
||||
memcpy(fbm_font.data(),header.sig,3);
|
||||
memcpy(fbm_font.data()+3,&header.encoding,1);
|
||||
memcpy(fbm_font.data()+4,&header.charnum,4);
|
||||
memcpy(fbm_font.data()+8,&header.width,1);
|
||||
memcpy(fbm_font.data()+9,&header.height,1);
|
||||
for (int i=0;i<characters.size();++i) {
|
||||
size_t char_offset=10+i*charsize;
|
||||
memcpy(fbm_font.data()+char_offset,&characters[i].codepoint,4);
|
||||
memcpy(fbm_font.data()+char_offset+4,characters[i].data.data(),characters[i].data.size());
|
||||
}
|
||||
ofstream fileout("font.fbm",ios::binary);
|
||||
if (!fileout) {
|
||||
cout<<"[Fontgen] Error: couldn't open output file."<<endl;
|
||||
return -1;
|
||||
}
|
||||
fileout.write(reinterpret_cast<const char*>(fbm_font.data()),fbm_font.size());
|
||||
fileout.close();
|
||||
cout<<"[Fontgen] Successfully generated font.fbm ("<<fbm_font.size()<<" bytes, "<<characters.size()<<" glyphs)"<<endl;
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user