diff --git a/font_generator/df_map.cpp b/font_generator/df_map.cpp index 8d13ec2f7e..7f2d607259 100644 --- a/font_generator/df_map.cpp +++ b/font_generator/df_map.cpp @@ -1,17 +1,32 @@ #include "df_map.hpp" +#include "../base/math.hpp" + #include "../std/algorithm.hpp" #include "../std/cmath.hpp" -DFMap::DFMap(const vector & data, int width, int height, unsigned char inValue, unsigned char outValue) +#include + +using boost::gil::gray8c_pixel_t; +using boost::gil::gray8_view_t; +using boost::gil::gray8_pixel_t; +using boost::gil::interleaved_view; + +DFMap::DFMap(vector const & data, + int32_t width, int32_t height, + uint8_t inValue, uint8_t outValue) : m_width(width) , m_height(height) { m_distances.resize(m_width * m_height); - for (int i = 0; i < m_width * m_height; ++i) + for (int32_t i = 0; i < m_width * m_height; ++i) m_distances[i] = 0.0f; - Do(data, inValue, outValue); + gray8c_view_t srcView = interleaved_view(width, height, + (gray8c_pixel_t *)&data[0], + width); + + Do(srcView, inValue, outValue); } DFMap::~DFMap() @@ -20,11 +35,11 @@ DFMap::~DFMap() void DFMap::Minus(DFMap const & other) { - for (int j = 0; j < m_height; ++j) - for (int i = 0; i < m_width; ++i) + for (int32_t j = 0; j < m_height; ++j) + for (int32_t i = 0; i < m_width; ++i) { - float srcD = m_distances[CalcIndex(i, j)]; - float dstD = other.m_distances[CalcIndex(i, j)]; + float const srcD = m_distances[CalcIndex(i, j)]; + float const dstD = other.m_distances[CalcIndex(i, j)]; SetDistance(srcD - dstD, i, j); } } @@ -50,66 +65,201 @@ void DFMap::Normalize() SetDistance(GetDistance(i, j) / maxValue, i, j); } -unsigned char * DFMap::GenerateImage(int & w, int & h) +void DFMap::GenerateImage(vector & image, int32_t & w, int32_t & h) { w = m_width; h = m_height; - unsigned char * image = new unsigned char[m_width * m_height]; - for (int j = 0; j < m_height; ++j) - for (int i = 0; i < m_width; ++i) - { - unsigned char v = 255 * GetDistance(i, j); - put(image, v, i, j); - } - - return image; -} - -void DFMap::Do(vector const & data, unsigned char inValue, unsigned char outValue) -{ - for (int j = 0; j < m_height; ++j) + image.resize(m_width * m_height); + gray8_view_t view = interleaved_view(m_width, m_height, + (gray8_pixel_t *)&image[0], m_width); + for (gray8_view_t::y_coord_t y = 0; y < view.height(); ++y) { - for (int i = 0; i < m_width; ++i) + for (gray8_view_t::x_coord_t x = 0; x < view.width(); ++x) { - if (get(data, i, j) == inValue) - SetDistance(sqrt(findRadialDistance(data, i, j, 256, outValue)), i, j); + view(x, y) = my::clamp(192 * GetDistance(x, y), 0, 255); } } } -float DFMap::findRadialDistance(vector const & data, int pointX, int pointY, - int maxRadius, unsigned char outValue) const +void DFMap::Do(gray8c_view_t const & view, uint8_t inValue, uint8_t outValue) { - float minDistance = maxRadius * maxRadius; - for (int j = pointY - maxRadius; j < pointY + maxRadius; ++j) + for (gray8_view_t::y_coord_t y = 0; y < view.height(); ++y) { - if (j < 0 || j >= m_height) - continue; - - for (int i = pointX - maxRadius; i < pointX + maxRadius; ++i) + for (gray8_view_t::x_coord_t x = 0; x < view.width(); ++x) { - if (i < 0 || i >= m_width) - continue; + if (view(x, y) == inValue) + SetDistance(sqrt(findRadialDistance(view, x, y, 256, outValue)), x, y); + } + } +} - if (get(data, i, j) == outValue) +namespace +{ + class SquareGoRoundIterator + { + public: + SquareGoRoundIterator(gray8c_view_t const & view, + int32_t centerX, int32_t centerY, uint32_t r) + : m_view(view) + , m_state(ToRight) + { + m_startX = centerX - r; + m_startY = centerY - r; + m_rWidth = m_rHeight = (2 * r) + 1; + if (m_startX < 0) { - int xDist = pointX - i; - int yDist = pointY - j; - float d = xDist * xDist + yDist * yDist; + m_rWidth += m_startX; + m_startX = 0; + } + else if (m_startX + m_rWidth >= m_view.width()) + { + m_rWidth = (m_view.width() - 1) - m_startX; + } + + if (m_startY < 0) + { + m_rHeight += m_startY; + m_startY = 0; + } + else if (m_startY + m_rHeight >= m_view.width()) + { + m_rHeight = (m_view.width() - 1) - m_startY; + } + + m_currentX = m_startX; + m_currentY = m_startY; + } + + bool HasValue() const { return m_state != End; } + uint8_t GetValue() const { return m_view(m_currentX, m_currentY); } + int32_t GetXMetric() const { return m_currentX; } + int32_t GetYMetric() const { return m_currentY; } + + void Next() + { + MoveForward(); + if (m_currentX < 0 || m_currentX >= m_startX + m_rWidth || + m_currentY < 0 || m_currentY >= m_startY + m_rHeight) + { + MoveBackward(); + ChangeMoveType(); + MoveForward(); + } + } + + private: + void MoveForward() + { + switch (m_state) + { + case ToRight: + m_currentX += 1; + break; + case ToLeft: + m_currentX -= 1; + break; + case ToBottom: + m_currentY += 1; + break; + case ToTop: + m_currentY -= 1; + break; + default: + break; + } + } + + void MoveBackward() + { + switch (m_state) + { + case ToRight: + m_currentX -= 1; + break; + case ToLeft: + m_currentX += 1; + break; + case ToBottom: + m_currentY -= 1; + break; + case ToTop: + m_currentY += 1; + break; + default: + break; + } + } + + void ChangeMoveType() + { + switch (m_state) + { + case ToRight: + m_state = ToBottom; + break; + case ToBottom: + m_state = ToLeft; + break; + case ToLeft: + m_state = ToTop; + break; + case ToTop: + m_state = End; + break; + default: + break; + } + } + + private: + gray8c_view_t const & m_view; + + enum + { + ToRight, + ToBottom, + ToLeft, + ToTop, + End + } m_state; + + int32_t m_startX, m_startY; + int32_t m_currentX, m_currentY; + int32_t m_rWidth, m_rHeight; + }; +} + +float DFMap::findRadialDistance(gray8c_view_t const & view, + int32_t pointX, int32_t pointY, + int32_t maxRadius, uint8_t outValue) const +{ + int32_t minDistance = maxRadius * maxRadius; + for (int32_t r = 1; (r < maxRadius) && (r * r) < minDistance; ++r) + { + SquareGoRoundIterator iter(view, pointX, pointY, r); + while (iter.HasValue()) + { + uint8_t v = iter.GetValue(); + if (v == outValue) + { + int32_t xDist = pointX - iter.GetXMetric(); + int32_t yDist = pointY - iter.GetYMetric(); + int32_t d = xDist * xDist + yDist * yDist; minDistance = min(d, minDistance); } + iter.Next(); } } return minDistance; } -float DFMap::GetDistance(int i, int j) const +float DFMap::GetDistance(int32_t i, int32_t j) const { return get(m_distances, i, j); } -void DFMap::SetDistance(float val, int i, int j) +void DFMap::SetDistance(float val, int32_t i, int32_t j) { put(m_distances, val, i, j); } diff --git a/font_generator/df_map.hpp b/font_generator/df_map.hpp index 731f3f3c6b..40e1f08a97 100644 --- a/font_generator/df_map.hpp +++ b/font_generator/df_map.hpp @@ -3,54 +3,58 @@ #include "../std/vector.hpp" #include "../std/stdint.hpp" +#include + +using boost::gil::gray8c_view_t; + class DFMap { public: DFMap(vector const & data, - int width, int height, - unsigned char inValue, - unsigned char outValue); + int32_t width, int32_t height, + uint8_t inValue, + uint8_t outValue); ~DFMap(); void Minus(DFMap const & other); void Normalize(); - unsigned char * GenerateImage(int & w, int & h); + void GenerateImage(vector & image, int32_t & w, int32_t & h); private: - void Do(vector const & data, unsigned char inValue, unsigned char outValue); - float findRadialDistance(vector const & data, - int pointX, int pointY, - int radius, unsigned char outValue) const; + void Do(gray8c_view_t const & view, uint8_t inValue, uint8_t outValue); + float findRadialDistance(gray8c_view_t const & view, + int32_t pointX, int32_t pointY, + int32_t radius, uint8_t outValue) const; template - T get(vector const & data, int i, int j) const + T get(vector const & data, int32_t i, int32_t j) const { int index = CalcIndex(i, j); return data[index]; } template - void put(vector & data, T val, int i, int j) + void put(vector & data, T val, int32_t i, int32_t j) { int index = CalcIndex(i, j); data[index] = val; } template - void put(T * data, T val, int i, int j) + void put(T * data, T val, int32_t i, int32_t j) { int index = CalcIndex(i, j); data[index] = val; } - float GetDistance(int i, int j) const; - void SetDistance(float val, int i, int j); + float GetDistance(int32_t i, int32_t j) const; + void SetDistance(float val, int32_t i, int32_t j); - int CalcIndex(int i, int j) const { return j * m_width + i; } + int32_t CalcIndex(int32_t i, int32_t j) const { return j * m_width + i; } private: - int m_width; - int m_height; + int32_t m_width; + int32_t m_height; vector m_distances; }; diff --git a/font_generator/engine.cpp b/font_generator/engine.cpp index aba34c52d4..e9f849c2d1 100644 --- a/font_generator/engine.cpp +++ b/font_generator/engine.cpp @@ -5,12 +5,11 @@ #include "BinPacker.hpp" #include "df_map.hpp" -#include -#include #include #include #include "../base/macros.hpp" +#include "../base/logging.hpp" #include "../std/cmath.hpp" #include "../std/vector.hpp" @@ -19,16 +18,46 @@ #include "../std/function.hpp" #include "../std/bind.hpp" +#include +#include + +using boost::gil::gray8c_view_t; +using boost::gil::gray8_view_t; +using boost::gil::gray8c_pixel_t; +using boost::gil::gray8_pixel_t; +using boost::gil::interleaved_view; +using boost::gil::subimage_view; +using boost::gil::copy_pixels; + typedef function simple_fun_t; typedef function int_fun_t; namespace { + void DumpImage(unsigned char * image, int w, int h) + { + QFile f("/Users/ExMix/dump.txt"); + f.open(QIODevice::WriteOnly); + + QTextStream stream(&f); + stream.setIntegerBase(16); + stream.setFieldWidth(3); + + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + stream << image[x + y * w]; + } + stream << "\n"; + } + } + //static int GlyphScaler = 16; static int EtalonTextureSize = 1024; struct ZeroPoint { - int m_x, m_y; + int32_t m_x, m_y; }; class AtlasCompositor @@ -67,12 +96,12 @@ namespace { } - int m_pageCount; - int m_width; - int m_height; + int32_t m_pageCount; + int32_t m_width; + int32_t m_height; }; - void InitMetrics(int pageCount) + void InitMetrics(int32_t pageCount) { static MetricTemplate templates[] = { @@ -105,8 +134,8 @@ namespace } private: - int m_width; - int m_height; + int32_t m_width; + int32_t m_height; }; class MyThread : public QThread @@ -115,14 +144,14 @@ namespace struct GlyphInfo { - int m_unicodePoint; - int m_glyphIndex; - int m_x, m_y; - int m_width, m_height; + int32_t m_unicodePoint; + int32_t m_glyphIndex; + int32_t m_x, m_y; + int32_t m_width, m_height; float m_xoff; float m_yoff; float m_advance; - unsigned char * m_img; + vector m_img; }; MyThread(QList const & fonts, int fontSize, @@ -193,15 +222,13 @@ namespace info.m_xoff = xoff /*/ (float)GlyphScaler*/; info.m_yoff = yoff /*/ (float)GlyphScaler*/; info.m_advance = advance * (scale /*/ (float) GlyphScaler*/); - info.m_img = NULL; if (info.m_width == 0 || info.m_height == 0) { - Q_ASSERT(info.m_img == NULL); emptyInfos.push_back(info); } else { - info.m_img = processGlyph(image, width, height, info.m_width, info.m_height); + processGlyph(image, width, height, info.m_img, info.m_width, info.m_height); infos.push_back(info); stbtt_FreeBitmap(image, NULL); } @@ -213,7 +240,7 @@ namespace rects.reserve(2 * infos.size()); foreach(GlyphInfo info, infos) { - if (info.m_img != NULL) + if (!info.m_img.empty()) { rects.push_back(info.m_width + 1); rects.push_back(info.m_height + 1); @@ -230,10 +257,16 @@ namespace AtlasCompositor compositor(outRects.size()); - int width = EtalonTextureSize * compositor.GetWidth(); - int height = EtalonTextureSize * compositor.GetHeight(); + m_w = EtalonTextureSize * compositor.GetWidth(); + m_h = EtalonTextureSize * compositor.GetHeight(); + + m_image.resize(m_w * m_h); + memset(&m_image[0], 0, m_image.size() * sizeof(uint8_t)); + + gray8_view_t resultView = interleaved_view(m_w, m_h, + (gray8_pixel_t *)&m_image[0], + m_w); - vector resultImg(width * height, 0); bool firstEmpty = true; for (size_t k = 0; k < outRects.size(); ++k) { @@ -251,15 +284,13 @@ namespace int packX = info.m_x; int packY = info.m_y; - for (int y = 0; y < info.m_height; ++y) - { - for (int x = 0; x < info.m_width; ++x) - { - int dstX = packX + x + 1; - int dstY = packY + y + 1; - resultImg[dstX + dstY * width] = info.m_img[x + y * info.m_width]; - } - } + gray8_view_t subResultView = subimage_view(resultView, + packX + 1, packY + 1, + info.m_width, info.m_height); + gray8c_view_t symbolView = interleaved_view(info.m_width, info.m_height, + (gray8c_pixel_t *)&info.m_img[0], + info.m_width); + copy_pixels(symbolView, subResultView); } else { @@ -275,49 +306,56 @@ namespace } } - foreach (GlyphInfo info, infos) - delete[] info.m_img; - - m_image = QImage(&resultImg[0], width, height, QImage::Format_Indexed8); - m_image.setColorCount(256); - for (int i = 0; i < 256; ++i) - m_image.setColor(i, qRgb(i, i, i)); - m_infos.clear(); m_infos.append(infos); m_infos.append(emptyInfos); } - QImage GetImage() const + vector const & GetImage(int & w, int & h) const { + w = m_w; + h = m_h; return m_image; } QList const & GetInfos() const { return m_infos; } private: - static unsigned char * processGlyph(unsigned char * glyphImage, int width, int height, - int & newW, int & newH) + static void processGlyph(unsigned char * glyphImage, int32_t width, int32_t height, + vector & image, int32_t & newW, int32_t & newH) { - static int border = 2; - int sWidth = width + 4 * border; - int sHeight = height + 4 * border; + static uint32_t border = 2; + int32_t const sWidth = width + 2 * border; + int32_t const sHeight = height + 2 * border; - vector buf(sWidth * sHeight, 0); - for (int y = 0; y < height; ++y) - for (int x = 0; x < width; ++x) + image.resize(sWidth * sHeight); + memset(&image[0], 0, image.size() * sizeof(uint8_t)); + gray8_view_t bufView = interleaved_view(sWidth, sHeight, + (gray8_pixel_t *)&image[0], + sWidth); + gray8_view_t subView = subimage_view(bufView, border, border, width, height); + gray8c_view_t srcView = interleaved_view(width, height, + (gray8c_pixel_t *)glyphImage, + width); + + for (gray8c_view_t::y_coord_t y = 0; y < subView.height(); ++y) + { + for (gray8c_view_t::x_coord_t x = 0; x < subView.width(); ++x) { - if (glyphImage[x + y * width] < 127) - buf[2 * border + x + (2 * border + y) * sWidth] = 0; + if (srcView(x, y) > 40) + subView(x, y) = 255; else - buf[2 * border + x + (2 * border + y) * sWidth] = 255; + subView(x, y) = 0; } + } - DFMap forwardMap(buf, sWidth, sHeight, 255, 0); - DFMap inverseMap(buf, sWidth, sHeight, 0, 255); + DumpImage(&image[0], sWidth, sHeight); + + DFMap forwardMap(image, sWidth, sHeight, 255, 0); + DFMap inverseMap(image, sWidth, sHeight, 0, 255); forwardMap.Minus(inverseMap); forwardMap.Normalize(); - return forwardMap.GenerateImage(newW, newH); + forwardMap.GenerateImage(image, newW, newH); } private: @@ -333,7 +371,8 @@ namespace typedef QMap map_t; typedef map_t::const_iterator map_iter_t; map_t m_fonts; - QImage m_image; + vector m_image; + int m_w, m_h; int m_fontSize; QList m_infos; }; @@ -344,7 +383,7 @@ Engine::Engine() { } -void Engine::SetFonts(const QList & fonts, int fontSize) +void Engine::SetFonts(QList const & fonts, int fontSize) { if (m_workThread != NULL) { @@ -380,8 +419,9 @@ void Engine::RunExport() if (!m_workThread->isFinished()) m_workThread->wait(); + GetImage().save(m_dirName.trimmed() + "/font.png", "png"); + MyThread * thread = static_cast(m_workThread); - thread->GetImage().save(m_dirName.trimmed() + "/font.png", "png"); QList const & infos = thread->GetInfos(); QFile file(m_dirName.trimmed() + "/font.txt"); @@ -409,8 +449,21 @@ void Engine::WorkThreadFinished() if (t == NULL) return; - MyThread * thread = static_cast(t); - emit UpdatePreview(thread->GetImage()); + emit UpdatePreview(GetImage(t)); +} + +QImage Engine::GetImage(QThread * sender) const +{ + MyThread * thread = static_cast(sender == NULL ? m_workThread : sender); + int w, h; + vector const & imgData = thread->GetImage(w, h); + + QImage image = QImage(&imgData[0], w, h, QImage::Format_Indexed8); + image.setColorCount(256); + for (int i = 0; i < 256; ++i) + image.setColor(i, qRgb(i, i, i)); + + return image; } void Engine::EmitStartEngine(int maxValue) diff --git a/font_generator/engine.hpp b/font_generator/engine.hpp index 2321190d11..b97a923fd6 100644 --- a/font_generator/engine.hpp +++ b/font_generator/engine.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include #include @@ -34,6 +33,9 @@ public: private: Q_SLOT void WorkThreadFinished(); +private: + QImage GetImage(QThread * sender = NULL) const; + private: void EmitStartEngine(int maxValue); void EmitUpdateProgress(int currentValue); diff --git a/font_generator/font_generator.pro b/font_generator/font_generator.pro index 38bc5c11b0..20ba567efc 100644 --- a/font_generator/font_generator.pro +++ b/font_generator/font_generator.pro @@ -9,11 +9,11 @@ QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets ROOT_DIR = .. +include($$ROOT_DIR/common.pri) INCLUDEPATH *= $$ROOT_DIR/3party/boost TARGET = FontGenerator TEMPLATE = app - SOURCES += main.cpp\ widget.cpp \ engine.cpp \ diff --git a/font_generator/widget.cpp b/font_generator/widget.cpp index b9f3878907..72afe4d8c0 100644 --- a/font_generator/widget.cpp +++ b/font_generator/widget.cpp @@ -67,7 +67,7 @@ void Widget::Export() void Widget::UpdatePreview(QImage img) { - ui->preview->setPixmap(QPixmap::fromImage(img)); + ui->preview->setPixmap(QPixmap::fromImage(img.scaled(ui->preview->size(), Qt::KeepAspectRatio))); } void Widget::UpdateEngine(int)