#include #include const int inSize = 1024; const int outSize = 32; const int charRange = 0x8A; static void usage() { qWarning("Usage: distfieldgen [options] "); qWarning(" "); qWarning(" options:"); qWarning(" -d ................................ output directory"); qWarning(" "); exit(1); } void printProgress(int p) { printf("\r["); for (int i = 0; i < 50; ++i) printf(i < p / 2 ? "=" : " "); printf("]"); printf(" %d%%", p); fflush(stdout); } float mindist(const QImage &in, int w, int h, int x, int y, int r, float maxdist) { int i, j, startx, starty, stopx, stopy, hit; float d, dx, dy; float mind = maxdist; int p; bool outside = (0 == qAlpha(in.pixel(x, y))); startx = qMax(0, x - r); starty = qMax(0, y - r); stopx = qMin(w, x + r); stopy = qMin(h, y + r); for (i = starty; i < stopy; i++) { for (j = startx; j < stopx; j++) { p = qAlpha(in.pixel(j, i)); dx = j - x; dy = i - y; d = dx * dx + dy * dy; hit = (p != 0) == outside; if (hit && (d < mind)) { mind = d; } } } if (outside) return sqrtf(mind); else return -sqrtf(mind); } QImage renderDistanceField(const QImage &in) { int outWidth = outSize * (float(in.width()) / in.height()); int outHeight = outSize; QImage df(outWidth, outHeight, QImage::Format_ARGB32_Premultiplied); int x, y, ix, iy; float d; float sx = in.width() / float(outWidth); float sy = in.height() / float(outHeight); int r = 90; float maxsq = 2 * r * r; float max = qSqrt(maxsq); for(y = 0; y < outHeight; y++){ for(x = 0; x < outWidth; x++){ ix = (x * sx) + (sx / 2); iy = (y * sy) + (sy / 2); d = mindist(in, in.width(), in.height(), ix, iy, r, maxsq); float a = 127.5f + 127.5f * (-d / max); df.setPixel(x, y, qRgba(0, 0, 0, qRound(a))); } } return df; } class DistFieldGenTask : public QRunnable { public: DistFieldGenTask(int c, int nbGlyph, const QImage &glyph, QMap *outList) : QRunnable() , m_char(c) , m_nbGlyph(nbGlyph) , m_glyph(glyph) , m_outList(outList) { } void run() { QImage df = renderDistanceField(m_glyph); QMutexLocker lock(&m_mutex); m_outList->insert(m_char, df); printProgress(float(m_outList->count()) / m_nbGlyph * 100); } static QMutex m_mutex; int m_char; int m_nbGlyph; QImage m_glyph; QMap *m_outList; }; QMutex DistFieldGenTask::m_mutex; int main(int argc, char *argv[]) { QApplication app(argc, argv); QStringList args = QApplication::arguments(); if (argc < 2 || args.contains("--help") || args.contains("-help") || args.contains("--h") || args.contains("-h")) usage(); QString fontFile; QString destDir; for (int i = 0; i < args.count(); ++i) { QString a = args.at(i); if (!a.startsWith('-') && QFileInfo(a).exists()) fontFile = a; if (a == QLatin1String("-d")) destDir = args.at(++i); } // Load the font int fontID = QFontDatabase::addApplicationFont(fontFile); if (fontID == -1) { qWarning("Error: Invalid font file."); qWarning(" "); exit(1); } QFileInfo fi(fontFile); if (destDir.isEmpty()) { destDir = fi.canonicalPath(); } else { QFileInfo dfi(destDir); if (!dfi.isDir()) { qWarning("Error: '%s' is not a directory.", destDir.toLatin1().constData()); qWarning(" "); exit(1); } destDir = dfi.canonicalFilePath(); } QStringList families = QFontDatabase::applicationFontFamilies(fontID); QFont f(families.at(0)); f.setPixelSize(inSize); QFontMetricsF fm(f); printf("Rendering glyphs for font '%s'\n", f.family().toLatin1().constData()); printProgress(0); QList glyphs; QMap distfields; // Render glyphs for (int i = 0; i < charRange; ++i) { QChar c(i); QImage glyph(fm.width(c), fm.height(), QImage::Format_ARGB32_Premultiplied); glyph.fill(Qt::transparent); QPainter p(&glyph); p.setFont(f); p.drawText(glyph.rect(), Qt::AlignHCenter | Qt::AlignVCenter, c); p.end(); glyphs.append(glyph); printProgress(float(i) / charRange * 100); } printProgress(100); printf("\nGenerating distance field\n"); printProgress(0); // Generate distance field for each glyph int i = 0; foreach (const QImage &g, glyphs) { QRunnable *run = new DistFieldGenTask(i, glyphs.count(), g, &distfields); QThreadPool::globalInstance()->start(run); ++i; } QThreadPool::globalInstance()->waitForDone(); printProgress(100); printf("\nFinishing\n"); printProgress(0); // Combine dist fields in one image int totalWidth = 0; foreach (const QImage &df, distfields.values()) totalWidth += df.width() > 0 ? df.width() : outSize; QImage output(1024, (totalWidth / 1024 + 1) * outSize, QImage::Format_ARGB32_Premultiplied); output.fill(Qt::transparent); int combinedWidth = 0; i = 0; QPainter p(&output); foreach (const QImage &df, distfields.values()) { if (combinedWidth % 1024 + df.width() > 1024) combinedWidth = (combinedWidth / 1024 + 1) * 1024; p.drawImage(combinedWidth % 1024, (combinedWidth / 1024) * outSize, df); combinedWidth += df.width() > 0 ? df.width() : outSize; printProgress(float(i) / distfields.count() * 100); ++i; } p.end(); printProgress(100); printf("\n"); // Save output output.save(QString("%1/%2.png").arg(destDir).arg(fi.baseName())); return 0; }