342 lines
7.2 KiB
C++
342 lines
7.2 KiB
C++
#ifndef FONTBUILDER_H
|
|
#define FONTBUILDER_H
|
|
|
|
|
|
#include <vector>
|
|
#include <cstdint>
|
|
#include <cmath>
|
|
|
|
#include <QString>
|
|
#include <QPainter>
|
|
#include <QImage>
|
|
#include <QFont>
|
|
#include <QFontDatabase>
|
|
#include <QIcon>
|
|
#include <QSvgRenderer>
|
|
|
|
#include <iostream>
|
|
|
|
struct FontBuilder {
|
|
|
|
uint8_t spaceBetween = 1;
|
|
|
|
/** resulting font */
|
|
struct Result {
|
|
|
|
/** pixel data */
|
|
std::vector<uint8_t> data;
|
|
|
|
/** character x offset in pixel */
|
|
std::vector<uint16_t> offsets;
|
|
|
|
uint16_t w;
|
|
uint8_t h;
|
|
QImage img;
|
|
|
|
void dump(const char* name) const {
|
|
|
|
std::cout << "#include <ESP8266lib/ext/lcd/Draw.h>" << std::endl;
|
|
|
|
std::cout << "static const uint8_t " << name << "_data[] = {";
|
|
for (uint8_t i : data) {
|
|
std::cout << (int) i << ",";
|
|
}
|
|
std::cout << "};" << std::endl;
|
|
|
|
std::cout << "static const uint16_t " << name << "_offsets[] = {";
|
|
for (uint16_t i : offsets) {
|
|
std::cout << (int) i << ",";
|
|
}
|
|
std::cout << "};" << std::endl;
|
|
|
|
//std::cout << "out size: " << w << ":" << h << std::endl;
|
|
std::cout << "static const FontWrap fnt_" << name << "(" << (int)w << "," << (int)h << "," << name << "_data," << name << "_offsets);" << std::endl;
|
|
|
|
std::cout << "--------------------" << std::endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
Result res;
|
|
uint16_t curX = 0;
|
|
|
|
FontBuilder(const int h) {
|
|
|
|
//if ( (w%8) != 0 ) {
|
|
// throw "width must be multiple of 8";
|
|
//}
|
|
|
|
res.w = 1024;
|
|
res.h = h;
|
|
res.img = QImage(res.w, res.h, QImage::Format_Mono);
|
|
|
|
QPainter p(&res.img);
|
|
p.fillRect(0, 0, res.w, res.h, Qt::white);
|
|
p.end();
|
|
|
|
}
|
|
|
|
void setSpaceBetween(uint8_t space) {
|
|
this->spaceBetween = space;
|
|
}
|
|
|
|
void addDummy() {
|
|
// update
|
|
res.offsets.push_back(curX);
|
|
curX += 0;
|
|
}
|
|
|
|
void addIcon(QString file, float w, float h, int atHeight) {
|
|
|
|
// //QImage img = QIcon("filepath.svg").pixmap(QSize()).toImage();
|
|
// QSvgRenderer renderer(file);
|
|
// QImage img(pixelSize, pixelSize, QImage::Format_ARGB32);
|
|
// //pm.fill(Qt::blue);
|
|
// QPainter painter(&img);
|
|
// painter.fillRect(0, 0, pixelSize, pixelSize, Qt::blue);
|
|
// renderer.render(&painter, img.rect());
|
|
|
|
// // renderer
|
|
// QPainter p(&res.img);
|
|
// p.setRenderHint(QPainter::Antialiasing, false);
|
|
// p.setRenderHint(QPainter::TextAntialiasing, false);
|
|
// //p.setPen(Qt::white);
|
|
|
|
// // render
|
|
// p.drawImage(curX, atHeight, img);
|
|
// p.end();
|
|
|
|
|
|
// renderer
|
|
QPainter p(&res.img);
|
|
p.setRenderHint(QPainter::Antialiasing, false);
|
|
p.setRenderHint(QPainter::TextAntialiasing, false);
|
|
p.setRenderHint(QPainter::SmoothPixmapTransform, false);
|
|
p.setRenderHint(QPainter::HighQualityAntialiasing, false);
|
|
|
|
// render the image
|
|
QSvgRenderer renderer(file);
|
|
//QImage img(pixelSize, pixelSize, QImage::Format_RGB888);
|
|
QRect rect(curX, 0, w, h);
|
|
//p.fillRect(rect, Qt::blue);
|
|
renderer.render(&p, rect);
|
|
|
|
// update
|
|
res.offsets.push_back(curX);
|
|
curX += w+1;
|
|
|
|
}
|
|
|
|
void addCharsMonoFromImage(QString file, const int charW, unsigned char cFirst, unsigned char cLast) {
|
|
|
|
// renderer
|
|
// QPainter p(&res.img);
|
|
// p.setRenderHint(QPainter::Antialiasing, false);
|
|
// p.setRenderHint(QPainter::TextAntialiasing, false);
|
|
int stride = 1;
|
|
|
|
QImage img;
|
|
img.load(file);
|
|
res.img = img;
|
|
res.w = img.width();
|
|
res.h = img.height();
|
|
// p.drawImage(0, 0, img);
|
|
|
|
// draw letters
|
|
for (unsigned char c = cFirst; c < cLast; ++c) {
|
|
int x1 = (c-cFirst) * (charW+stride);
|
|
int w = charW;
|
|
int y1 = 0;
|
|
int h = res.h;
|
|
QRect rect = QRect(x1, y1, w, h);
|
|
res.offsets.push_back(curX);
|
|
curX += std::ceil(rect.width()) + 1;
|
|
}
|
|
|
|
// p.end();
|
|
|
|
|
|
}
|
|
|
|
void addChars(QString fontFile, float pixelSize, int atHeight, unsigned char cFirst, unsigned char cLast, std::function<void(char, QRect&)> fixFunc = nullptr) {
|
|
|
|
|
|
// renderer
|
|
QPainter p(&res.img);
|
|
p.setRenderHint(QPainter::Antialiasing, false);
|
|
p.setRenderHint(QPainter::TextAntialiasing, false);
|
|
p.setPen(Qt::black);
|
|
|
|
// the font
|
|
int id = QFontDatabase::addApplicationFont(fontFile);
|
|
QString family = QFontDatabase::applicationFontFamilies(id).at(0);
|
|
QFont fnt(family);
|
|
if (pixelSize == -1) {
|
|
;
|
|
} else if (pixelSize > 100) {
|
|
fnt.setPointSize(pixelSize-100);
|
|
} else {
|
|
fnt.setPixelSize(pixelSize);
|
|
}
|
|
|
|
QFontMetrics fm(fnt);
|
|
|
|
// enable
|
|
p.setFont(fnt);
|
|
|
|
// draw letters
|
|
for (unsigned char c = cFirst; c < cLast; ++c) {
|
|
|
|
QRect rect = fm.boundingRect(c);
|
|
QString str = ""; str += c;
|
|
|
|
res.offsets.push_back(curX);
|
|
int drawOffsetX = 0;
|
|
|
|
// apply rectangle fixing function?
|
|
if (fixFunc) {
|
|
|
|
fixFunc(c, rect);
|
|
|
|
// hack for some chars
|
|
if (rect.x() < 0) {drawOffsetX = -rect.x();}
|
|
|
|
}
|
|
|
|
std::cout << " " << c << " : " << rect.width() << " cur: " << curX << std::endl;
|
|
|
|
p.drawText(curX+drawOffsetX, atHeight, str);
|
|
curX += std::ceil(rect.width()) + spaceBetween; // 1 pixel space between chars
|
|
}
|
|
|
|
p.end();
|
|
|
|
}
|
|
|
|
|
|
const Result& get() {
|
|
|
|
// crop image (only used region)
|
|
res.w = (curX/8+1)*8;
|
|
res.img = res.img.copy(0,0,res.w,res.h);
|
|
|
|
|
|
// convert to bitfield
|
|
const unsigned int bytes = res.w/8 * res.h;
|
|
res.data.resize(bytes);
|
|
for (int y = 0; y < res.h; ++y) {
|
|
for (int x = 0; x < res.w; ++x) {
|
|
const QColor pixel = res.img.pixelColor(x,y);
|
|
bool set = pixel.red() + pixel.green() + pixel.blue() == 0;
|
|
if (set) {
|
|
const unsigned int idx = (x/8) + (y*res.w/8);
|
|
res.data[idx] |= (1 << (x%8));
|
|
}
|
|
}
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
Result build(const QString& fontFile, int maxChar, float pixelSize, const int oh) {
|
|
|
|
// the font
|
|
int id = QFontDatabase::addApplicationFont(fontFile);
|
|
QString family = QFontDatabase::applicationFontFamilies(id).at(0);
|
|
QFont fnt(family); fnt.setPixelSize(pixelSize);
|
|
QFontMetrics fm(fnt);
|
|
|
|
// first chars are unused
|
|
std::vector<uint16_t> offsets;
|
|
for (unsigned char c = 0; c <= 32; ++c) {
|
|
offsets.push_back(0);
|
|
}
|
|
|
|
// estimate total width and remember character offsets
|
|
int mx = 0;
|
|
for (unsigned char c = 32; c < maxChar; ++c) {
|
|
QRect rect = fm.boundingRect(c);
|
|
mx += std::ceil(rect.width()) + 1;
|
|
//if (c < 128 && rect.height() > my) {my = rect.height();}
|
|
offsets.push_back(mx);
|
|
}
|
|
|
|
// width/height
|
|
int w = mx;
|
|
int h = oh;
|
|
|
|
// width/height next multiple of 8
|
|
int ow = ((w-1)/8+1)*8;
|
|
//int oh = h;
|
|
|
|
QImage img = QImage(ow, oh, QImage::Format_RGB888);
|
|
QPainter p(&img);
|
|
|
|
p.setRenderHint(QPainter::Antialiasing, false);
|
|
p.setRenderHint(QPainter::TextAntialiasing, false);
|
|
p.setPen(Qt::white);
|
|
p.setFont(fnt);
|
|
p.fillRect(0, 0, w, h, Qt::black);
|
|
|
|
|
|
|
|
|
|
// draw image
|
|
std::vector<uint8_t> data;
|
|
for (unsigned char c = 32; c < maxChar; ++c) {
|
|
QRect rect = fm.boundingRect(c);
|
|
QString str = ""; str += c;
|
|
int x = offsets[c];
|
|
p.drawText(x, h, str);
|
|
}
|
|
|
|
p.end();
|
|
|
|
|
|
|
|
|
|
// convert to bitfield
|
|
const unsigned int bytes = ow/8 * oh;
|
|
data.resize(bytes);
|
|
for (int y = 0; y < oh; ++y) {
|
|
for (int x = 0; x < ow; ++x) {
|
|
bool set = img.pixelColor(x,y).red() > 128;
|
|
if (set) {
|
|
const unsigned int idx = (x/8) + (y*ow/8);
|
|
data[idx] |= (1 << (x%8));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// remove initial empty offsets
|
|
offsets.erase(offsets.begin(), offsets.begin()+32);
|
|
|
|
|
|
//img.save("/tmp/1.png");
|
|
|
|
//int i = 0; (void) i;
|
|
|
|
Result res;
|
|
res.data = data;
|
|
res.offsets = offsets;
|
|
res.w = ow;
|
|
res.h = oh;
|
|
res.img = img;
|
|
|
|
return res;
|
|
|
|
}
|
|
*/
|
|
|
|
};
|
|
|
|
#endif // FONTBUILDER_H
|