Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Zolotarev <deathbaba@gmail.com>2010-12-05 19:24:16 +0300
committerAlex Zolotarev <alex@maps.me>2015-09-22 22:33:57 +0300
commitd6e12b7ce4bcbf0ccd1c07eb25de143422913c34 (patch)
treea7e910c330ce4da9b4f2d8be76067adece2561c4 /skin_generator
One Month In Minsk. Made in Belarus.
Diffstat (limited to 'skin_generator')
-rw-r--r--skin_generator/main.cpp54
-rw-r--r--skin_generator/skin_generator.cpp398
-rw-r--r--skin_generator/skin_generator.hpp99
-rw-r--r--skin_generator/skin_generator.pro29
4 files changed, 580 insertions, 0 deletions
diff --git a/skin_generator/main.cpp b/skin_generator/main.cpp
new file mode 100644
index 0000000000..825dc4f1e1
--- /dev/null
+++ b/skin_generator/main.cpp
@@ -0,0 +1,54 @@
+#include "skin_generator.hpp"
+
+#include <QtCore/QFile>
+#include <QtGui/QApplication>
+
+#include <QtXml/QXmlSimpleReader>
+#include <QtXml/QXmlInputSource>
+
+#include "../3party/gflags/src/gflags/gflags.h"
+
+DEFINE_string(fontFileName, "../../data/fonts/DejaVu/DejaVuSans.ttf", "path to TrueType font file");
+DEFINE_string(symbolsFile, "../../data/results.unicode", "file with 2bytes symbols for which the skin should be generated");
+DEFINE_string(symbolsDir, "../../data/styles/symbols", "directory with svg symbol files");
+DEFINE_int32(symbolWidth, 24, "width of the rendered symbol");
+DEFINE_int32(symbolHeight, 24, "height of the rendered symbol");
+DEFINE_double(symbolScale, 1, "scale factor of the symbol");
+DEFINE_int32(smallGlyphSize, 12, "height of the small glyph");
+DEFINE_int32(bigGlyphSize, 16, "height of the big glyph");
+DEFINE_string(skinName, "../../data/basic", "prefix for the skin and skinImage file name");
+
+int main(int argc, char *argv[])
+{
+ google::ParseCommandLineFlags(&argc, &argv, true);
+ QApplication app(argc, argv);
+
+ tools::SkinGenerator gen;
+
+ std::vector<int8_t> glyphSizes;
+
+// glyphSizes.push_back(6);
+ glyphSizes.push_back(8);
+ glyphSizes.push_back(10);
+ glyphSizes.push_back(12);
+ glyphSizes.push_back(14);
+ glyphSizes.push_back(16);
+ glyphSizes.push_back(20);
+ glyphSizes.push_back(24);
+
+// glyphSizes.push_back(FLAGS_smallGlyphSize);
+// glyphSizes.push_back(FLAGS_bigGlyphSize);
+
+ std::vector<QSize> symbolSizes;
+ symbolSizes.push_back(QSize(FLAGS_symbolWidth, FLAGS_symbolHeight));
+
+ std::vector<double> symbolScales;
+ symbolScales.push_back(FLAGS_symbolScale);
+
+ gen.processSymbols(FLAGS_symbolsDir, FLAGS_skinName, symbolSizes, symbolScales);
+ gen.processFont(FLAGS_fontFileName, FLAGS_skinName, FLAGS_symbolsFile, glyphSizes);
+
+ gen.writeToFile(FLAGS_skinName);
+
+ return 0;
+}
diff --git a/skin_generator/skin_generator.cpp b/skin_generator/skin_generator.cpp
new file mode 100644
index 0000000000..013441824b
--- /dev/null
+++ b/skin_generator/skin_generator.cpp
@@ -0,0 +1,398 @@
+#include "skin_generator.hpp"
+#include <QtXml/QDomElement>
+#include <QtXml/QDomDocument>
+#include <QtCore/QDir>
+#include "../std/bind.hpp"
+#include "../coding/lodepng_io.hpp"
+#include <boost/gil/gil_all.hpp>
+#include "../std/algorithm.hpp"
+#include "../std/iterator.hpp"
+#include "../std/fstream.hpp"
+#include "../std/iostream.hpp"
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_GLYPH_H
+
+
+namespace gil = boost::gil;
+
+namespace tools
+{
+ SkinGenerator::SkinGenerator()
+ : m_baseLineOffset(0)
+ {}
+
+ void SkinGenerator::processFont(string const & fileName, string const & skinName, string const & symFreqFile, vector<int8_t> const & fontSizes)
+ {
+ FILE * file = fopen(symFreqFile.c_str(), "rb");
+ if (!file)
+ throw std::exception();
+ std::vector<unsigned short> ucs2Symbols;
+
+ while (true)
+ {
+ unsigned short id;
+ int readBytes = fread(&id, 1, sizeof(unsigned short), file);
+ if (readBytes < 2)
+ break;
+ ucs2Symbols.push_back(id);
+ }
+
+ fclose(file);
+
+ FT_Library lib;
+ FT_Init_FreeType(&lib);
+
+ FT_Face face;
+ FT_New_Face(lib, fileName.c_str(), 0, &face);
+
+ FT_Glyph_Metrics glyphMetrics;
+
+ for (size_t i = 0; i < fontSizes.size(); ++i)
+ {
+ m_pages.push_back(SkinPageInfo());
+ SkinPageInfo & page = m_pages.back();
+
+ page.m_fonts.push_back(FontInfo());
+ FontInfo & fontInfo = page.m_fonts.back();
+
+ fontInfo.m_size = fontSizes[i];
+
+ FT_Set_Pixel_Sizes(face, 0, fontSizes[i]);
+ for (size_t j = 0; j < ucs2Symbols.size(); ++j)
+ {
+ unsigned short symbol = ucs2Symbols[j];
+
+ int symbolIdx = FT_Get_Char_Index(face, symbol);
+
+ if (symbolIdx == 0)
+ continue;
+
+ FT_Load_Glyph(face, symbolIdx, FT_LOAD_DEFAULT);
+ glyphMetrics = face->glyph->metrics;
+
+ CharInfo charInfo;
+ charInfo.m_width = int(glyphMetrics.width >> 6);
+ charInfo.m_height = int(glyphMetrics.height >> 6);
+ charInfo.m_xOffset = int(glyphMetrics.horiBearingX >> 6);
+ charInfo.m_yOffset = int(glyphMetrics.horiBearingY >> 6) - charInfo.m_height;
+ charInfo.m_xAdvance = int(glyphMetrics.horiAdvance >> 6);
+
+ FT_GlyphSlot glyphSlot = face->glyph;
+ if ((charInfo.m_width != 0) && (charInfo.m_height != 0))
+ {
+ FT_Render_Glyph(glyphSlot, FT_RENDER_MODE_NORMAL);
+
+ typedef gil::gray8_pixel_t pixel_t;
+
+ gil::gray8c_view_t grayview = gil::interleaved_view(
+ charInfo.m_width,
+ charInfo.m_height,
+ (pixel_t*)glyphSlot->bitmap.buffer,
+ sizeof(unsigned char) * glyphSlot->bitmap.width);
+
+ charInfo.m_image.recreate(charInfo.m_width, charInfo.m_height);
+ gil::copy_pixels(grayview, gil::view(charInfo.m_image));
+ }
+
+ fontInfo.m_chars[symbol] = charInfo;
+ }
+
+ std::stringstream out;
+ out << getBaseFileName(fileName) + "_" << (int)fontSizes[i];
+
+ page.m_fileName = out.str().c_str();
+
+ /// repacking symbols as tight as possible
+ page.m_width = 32;
+ page.m_height = 32;
+
+ while (true)
+ {
+ m_overflowDetected = false;
+
+ page.m_packer = m2::Packer(page.m_width, page.m_height);
+ page.m_packer.addOverflowFn(bind(&SkinGenerator::markOverflow, this), 10);
+
+ for (TChars::iterator it = fontInfo.m_chars.begin(); it != fontInfo.m_chars.end(); ++it)
+ {
+ it->second.m_handle = page.m_packer.pack(it->second.m_width + 4, it->second.m_height + 4);
+ if (m_overflowDetected)
+ break;
+ }
+
+ if (m_overflowDetected)
+ {
+ if (page.m_width == page.m_height)
+ page.m_width *= 2;
+ else
+ page.m_height *= 2;
+ continue;
+ }
+ else
+ break;
+ }
+
+ gil::bgra8_image_t skinImage(page.m_width, page.m_height);
+ gil::fill_pixels(gil::view(skinImage), gil::rgba8_pixel_t(0, 0, 0, 0));
+
+ for (TChars::const_iterator it = fontInfo.m_chars.begin(); it != fontInfo.m_chars.end(); ++it)
+ {
+ m2::RectU dstRect(page.m_packer.find(it->second.m_handle).second);
+
+ gil::rgba8_pixel_t color(0, 0, 0, 0);
+
+ gil::bgra8_view_t dstView = gil::subimage_view(gil::view(skinImage), dstRect.minX(), dstRect.minY(), dstRect.SizeX(), dstRect.SizeY());
+ gil::fill_pixels(dstView, color);
+
+ dstView = gil::subimage_view(gil::view(skinImage),
+ dstRect.minX() + 2,
+ dstRect.minY() + 2,
+ dstRect.SizeX() - 4,
+ dstRect.SizeY() - 4);
+
+ gil::gray8c_view_t srcView = gil::const_view(it->second.m_image);
+
+ for (size_t x = 0; x < dstRect.SizeX() - 4; ++x)
+ for (size_t y = 0; y < dstRect.SizeY() - 4; ++y)
+ {
+ color[3] = srcView(x, y);
+ dstView(x, y) = color;
+ }
+ }
+
+ gil::lodepng_write_view(
+ skinName.substr(0, skinName.find_last_of("/") + 1) + page.m_fileName + ".png",
+ gil::const_view(skinImage));
+ }
+
+ FT_Done_Face(face);
+ FT_Done_FreeType(lib);
+ }
+
+ string const SkinGenerator::getBaseFileName(string const & fileName)
+ {
+ int startPos = fileName.find_last_of("/");
+ int endPos = fileName.find_last_of(".");
+ if (endPos != string::npos)
+ endPos = endPos - startPos - 1;
+
+ string s = fileName.substr(fileName.find_last_of("/") + 1, endPos);
+ for (size_t i = 0; i < s.size(); ++i)
+ s[i] = tolower(s[i]);
+ return s;
+ }
+
+ struct LessHeight
+ {
+ bool operator()(SkinGenerator::SymbolInfo const & left, SkinGenerator::SymbolInfo const & right)
+ {
+ return left.m_size.height() < right.m_size.height();
+ }
+ };
+
+ void SkinGenerator::processSymbols(string const & svgDataDir, string const & skinName, std::vector<QSize> const & symbolSizes, std::vector<double> const & symbolScales)
+ {
+ for (int i = 0; i < symbolSizes.size(); ++i)
+ {
+ QDir dir(QString(svgDataDir.c_str()));
+ QStringList fileNames = dir.entryList(QDir::Files);
+
+ /// separate page for symbols
+ m_pages.push_back(SkinPageInfo());
+ SkinPageInfo & page = m_pages.back();
+
+ double symbolScale = symbolScales[i];
+ QSize symbolSize = symbolSizes[i];
+
+ for (int i = 0; i < fileNames.size(); ++i)
+ {
+ if (fileNames.at(i).endsWith(".svg"))
+ {
+ QString fullFileName = QString(svgDataDir.c_str()) + "/" + fileNames.at(i);
+ QString symbolID = fileNames.at(i).left(fileNames.at(i).lastIndexOf("."));
+ if (m_svgRenderer.load(fullFileName))
+ {
+ QRect viewBox = m_svgRenderer.viewBox();
+ QSize defaultSize = m_svgRenderer.defaultSize();
+
+ QSize size = defaultSize * symbolScale;
+
+ if (size.width() > symbolSize.width())
+ {
+ size.setHeight((float)size.height() * symbolSize.width() / (float)size.width());
+ size.setWidth(symbolSize.width());
+ }
+
+ if (size.height() > symbolSize.height())
+ {
+ size.setWidth((float)size.width() * symbolSize.height() / (float)size.height());
+ size.setHeight(symbolSize.height());
+ }
+
+ page.m_symbols.push_back(SymbolInfo(size + QSize(4, 4), fullFileName, symbolID));
+ }
+ }
+ }
+
+ /// Trying to repack symbols as tight as possible
+ page.m_width = 64;
+ page.m_height = 64;
+
+ /// packing until we find a suitable rect
+ while (true)
+ {
+ page.m_packer = m2::Packer(page.m_width, page.m_height);
+ page.m_packer.addOverflowFn(bind(&SkinGenerator::markOverflow, this), 10);
+
+ m_overflowDetected = false;
+
+ for (TSymbols::iterator it = page.m_symbols.begin(); it != page.m_symbols.end(); ++it)
+ {
+ it->m_handle = page.m_packer.pack(it->m_size.width(), it->m_size.height());
+ if (m_overflowDetected)
+ {
+ /// enlarge packing area and try again
+ if (page.m_width == page.m_height)
+ page.m_width *= 2;
+ else
+ page.m_height *= 2;
+ break;
+ }
+ }
+
+ if (m_overflowDetected)
+ continue;
+
+ break;
+ }
+
+ /// rendering packed symbols
+
+ gil::bgra8_image_t gilImage(page.m_width, page.m_height);
+ gil::fill_pixels(gil::view(gilImage), gil::rgba8_pixel_t(0, 0, 0, 0));
+ QImage img((uchar*)&gil::view(gilImage)(0, 0), page.m_width, page.m_height, QImage::Format_ARGB32);
+ QPainter painter(&img);
+ painter.setClipping(true);
+
+ for (TSymbols::const_iterator it = page.m_symbols.begin(); it != page.m_symbols.end(); ++it)
+ {
+ m2::RectU dstRect = page.m_packer.find(it->m_handle).second;
+ QRect dstRectQt(dstRect.minX(), dstRect.minY(), dstRect.SizeX(), dstRect.SizeY());
+
+ painter.fillRect(dstRectQt, QColor(0, 0, 0, 0));
+
+ painter.setClipRect(dstRect.minX() + 2, dstRect.minY() + 2, dstRect.SizeX() - 4, dstRect.SizeY() - 4);
+
+ m_svgRenderer.load(it->m_fullFileName);
+ m_svgRenderer.render(&painter, QRect(dstRect.minX() + 2, dstRect.minY() + 2, dstRect.SizeX() - 4, dstRect.SizeY() - 4));
+ }
+
+ page.m_fileName = skinName.substr(0, skinName.find_last_of("/") + 1) + "symbols_" + QString("%1").arg(symbolSize.width()).toLocal8Bit().constData();
+
+ img.save((page.m_fileName + ".png").c_str());
+ }
+ }
+
+ void SkinGenerator::markOverflow()
+ {
+ m_overflowDetected = true;
+ }
+
+ bool SkinGenerator::writeToFile(std::string const & skinName)
+ {
+ /// Creating Data file
+ QDomDocument doc = QDomDocument("skin");
+ QDomElement skinElem = doc.createElement("skin");
+ doc.appendChild(skinElem);
+
+ for (vector<SkinPageInfo>::const_iterator it = m_pages.begin(); it != m_pages.end(); ++it)
+ {
+ SkinPageInfo const & page = *it;
+
+ QDomElement pageElem = doc.createElement("page");
+ skinElem.appendChild(pageElem);
+ pageElem.setAttribute("width", page.m_width);
+ pageElem.setAttribute("height", page.m_height);
+ pageElem.setAttribute("file", (page.m_fileName.substr(page.m_fileName.find_last_of("/") + 1) + ".png").c_str());
+
+ int minDynamicID = 0;
+ int maxFontResourceID = 0;
+
+ for (TFonts::const_iterator fontIt = page.m_fonts.begin(); fontIt != page.m_fonts.end(); ++fontIt)
+ {
+ QDomElement fontInfo = doc.createElement("fontInfo");
+ fontInfo.setAttribute("size", fontIt->m_size);
+
+ for (TChars::const_iterator it = fontIt->m_chars.begin(); it != fontIt->m_chars.end(); ++it)
+ {
+ QDomElement charStyle = doc.createElement("charStyle");
+ QDomElement resourceStyle = doc.createElement("resourceStyle");
+
+ m2::RectU const & texRect = page.m_packer.find(it->second.m_handle).second;
+
+ resourceStyle.setAttribute("x", texRect.minX());
+ resourceStyle.setAttribute("y", texRect.minY());
+ resourceStyle.setAttribute("width", texRect.SizeX());
+ resourceStyle.setAttribute("height", texRect.SizeY());
+
+ charStyle.appendChild(resourceStyle);
+
+ charStyle.setAttribute("xAdvance", it->second.m_xAdvance);
+ charStyle.setAttribute("xOffset", it->second.m_xOffset);
+ charStyle.setAttribute("yOffset", it->second.m_yOffset);
+ charStyle.setAttribute("id", it->first);
+
+ fontInfo.appendChild(charStyle);
+
+ maxFontResourceID = max(it->first, maxFontResourceID);
+ }
+
+ pageElem.appendChild(fontInfo);
+ }
+
+ minDynamicID += maxFontResourceID + 1;
+ int maxImageResourceID = 0;
+
+ for (vector<SymbolInfo>::const_iterator it = page.m_symbols.begin(); it != page.m_symbols.end(); ++it)
+ {
+ QDomElement symbolStyle = doc.createElement("symbolStyle");
+
+ QDomElement resourceStyle = doc.createElement("resourceStyle");
+
+ m2::RectU r = page.m_packer.find(it->m_handle).second;
+
+ resourceStyle.setAttribute("x", r.minX());
+ resourceStyle.setAttribute("y", r.minY());
+ resourceStyle.setAttribute("width", r.SizeX());
+ resourceStyle.setAttribute("height", r.SizeY());
+
+ symbolStyle.appendChild(resourceStyle);
+ symbolStyle.setAttribute("id", minDynamicID + it->m_handle);
+ symbolStyle.setAttribute("name", it->m_symbolID.toLower());
+
+ maxImageResourceID = max(maxImageResourceID, (int)it->m_handle);
+
+ pageElem.appendChild(symbolStyle);
+ }
+
+ minDynamicID += maxImageResourceID + 1;
+ }
+
+ QFile::remove(QString((skinName + ".skn").c_str()));
+
+ if (QFile::exists((skinName + ".skn").c_str()))
+ throw std::exception();
+
+ QFile file(QString((skinName + ".skn").c_str()));
+
+ if (!file.open(QIODevice::ReadWrite))
+ throw std::exception();
+ QTextStream ts(&file);
+ ts.setCodec("UTF-8");
+ ts << doc.toString();
+
+ return true;
+ }
+}
diff --git a/skin_generator/skin_generator.hpp b/skin_generator/skin_generator.hpp
new file mode 100644
index 0000000000..3d6a80f585
--- /dev/null
+++ b/skin_generator/skin_generator.hpp
@@ -0,0 +1,99 @@
+#pragma once
+
+#include "../../base/base.hpp"
+#include "../../std/vector.hpp"
+#include "../../std/list.hpp"
+#include "../../std/string.hpp"
+#include "../../std/map.hpp"
+#include "../../geometry/rect2d.hpp"
+#include "../../coding/writer.hpp"
+#include "../../geometry/packer.hpp"
+#include <boost/gil/gil_all.hpp>
+
+#include <QtGui/QPainter>
+#include <QtGui/QImage>
+#include <QtCore/QFileInfo>
+#include <QtCore/QSize>
+#include <QtSvg/QSvgRenderer>
+#include <QtXml/QXmlContentHandler>
+#include <QtXml/QXmlDefaultHandler>
+
+class QImage;
+
+namespace gil = boost::gil;
+
+namespace tools
+{
+ class SkinGenerator
+ {
+ public:
+
+ struct CharInfo
+ {
+ int m_width;
+ int m_height;
+ int m_xOffset;
+ int m_yOffset;
+ int m_xAdvance;
+ gil::gray8_image_t m_image;
+
+ m2::Packer::handle_t m_handle;
+ };
+
+ typedef map<int32_t, CharInfo> TChars;
+
+ struct FontInfo
+ {
+ int8_t m_size;
+ TChars m_chars;
+ };
+
+ typedef vector<FontInfo> TFonts;
+
+ struct SymbolInfo
+ {
+ QSize m_size;
+ QString m_fullFileName;
+ QString m_symbolID;
+
+ m2::Packer::handle_t m_handle;
+
+ SymbolInfo() {}
+ SymbolInfo(QSize size, QString const & fullFileName, QString const & symbolID)
+ : m_size(size), m_fullFileName(fullFileName), m_symbolID(symbolID) {}
+ };
+
+ typedef vector<SymbolInfo> TSymbols;
+
+ struct SkinPageInfo
+ {
+ TFonts m_fonts;
+ TSymbols m_symbols;
+ int m_width;
+ int m_height;
+ string m_fileName;
+ m2::Packer m_packer;
+ };
+
+ string const getBaseFileName(string const & fileName);
+
+ private:
+
+ QSvgRenderer m_svgRenderer;
+
+ int m_baseLineOffset;
+ QString m_fontFileName;
+
+ vector<SkinPageInfo> m_pages;
+
+ bool m_overflowDetected;
+ void markOverflow();
+
+ public:
+
+ SkinGenerator();
+ void processFont(string const & fileName, string const & skinName, string const & symFreqFile, vector<int8_t> const & fontSizes);
+ void processSymbols(string const & symbolsDir, string const & skinName, std::vector<QSize> const & symbolSizes, std::vector<double> const & symbolScales);
+ bool writeToFile(string const & skinName);
+ };
+} // namespace tools
diff --git a/skin_generator/skin_generator.pro b/skin_generator/skin_generator.pro
new file mode 100644
index 0000000000..5f0cd6cd72
--- /dev/null
+++ b/skin_generator/skin_generator.pro
@@ -0,0 +1,29 @@
+# -----------------------------------------------------
+# Project created by Alex Zolotarev 2010-01-22T14:39:29
+# -----------------------------------------------------
+
+TARGET = skin_generator
+TEMPLATE = app
+CONFIG += console
+CONFIG -= app_bundle
+
+ROOT_DIR = ..
+include($$ROOT_DIR/common.pri)
+
+QT *= core gui svg xml
+
+PRE_TARGETDEPS += $$BINARIES_PATH/$${LIB_PREFIX}coding$$LIB_EXT
+PRE_TARGETDEPS += $$BINARIES_PATH/$${LIB_PREFIX}geometry$$LIB_EXT
+PRE_TARGETDEPS += $$BINARIES_PATH/$${LIB_PREFIX}freetype$$LIB_EXT
+PRE_TARGETDEPS += $$BINARIES_PATH/$${LIB_PREFIX}gflags$$LIB_EXT
+PRE_TARGETDEPS += $$BINARIES_PATH/$${LIB_PREFIX}base$$LIB_EXT
+
+LIBS += -lcoding -lgeometry -lfreetype -lgflags -lbase
+
+INCLUDEPATH += $$ROOT_DIR/3party/boost \
+ $$ROOT_DIR/3party/freetype/include
+
+HEADERS += skin_generator.hpp
+
+SOURCES += main.cpp \
+ skin_generator.cpp