use boost::gil to manipulate with image's memory

correct transfer QImage. It must be created on gui thread
This commit is contained in:
ExMix 2014-07-01 17:37:25 +03:00 committed by Alex Zolotarev
parent 412e6e010e
commit 934e63adca
6 changed files with 327 additions and 118 deletions

View file

@ -1,17 +1,32 @@
#include "df_map.hpp"
#include "../base/math.hpp"
#include "../std/algorithm.hpp"
#include "../std/cmath.hpp"
DFMap::DFMap(const vector<uint8_t> & data, int width, int height, unsigned char inValue, unsigned char outValue)
#include <boost/gil/algorithm.hpp>
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<uint8_t> 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<uint8_t> & 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<uint8_t> 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<uint8_t> 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);
}

View file

@ -3,54 +3,58 @@
#include "../std/vector.hpp"
#include "../std/stdint.hpp"
#include <boost/gil/typedefs.hpp>
using boost::gil::gray8c_view_t;
class DFMap
{
public:
DFMap(vector<uint8_t> 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<uint8_t> & image, int32_t & w, int32_t & h);
private:
void Do(vector<uint8_t> const & data, unsigned char inValue, unsigned char outValue);
float findRadialDistance(vector<uint8_t> 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<typename T>
T get(vector<T> const & data, int i, int j) const
T get(vector<T> const & data, int32_t i, int32_t j) const
{
int index = CalcIndex(i, j);
return data[index];
}
template <typename T>
void put(vector<T> & data, T val, int i, int j)
void put(vector<T> & data, T val, int32_t i, int32_t j)
{
int index = CalcIndex(i, j);
data[index] = val;
}
template <typename T>
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<float> m_distances;
};

View file

@ -5,12 +5,11 @@
#include "BinPacker.hpp"
#include "df_map.hpp"
#include <QMap>
#include <QList>
#include <QFile>
#include <QTextStream>
#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 <boost/gil/typedefs.hpp>
#include <boost/gil/algorithm.hpp>
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<void (void)> simple_fun_t;
typedef function<void (int)> 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<uint8_t> m_img;
};
MyThread(QList<FontRange> 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<uint8_t> 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<uint8_t> const & GetImage(int & w, int & h) const
{
w = m_w;
h = m_h;
return m_image;
}
QList<GlyphInfo> 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<uint8_t> & 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<unsigned char> 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<QString, ranges_t> map_t;
typedef map_t::const_iterator map_iter_t;
map_t m_fonts;
QImage m_image;
vector<uint8_t> m_image;
int m_w, m_h;
int m_fontSize;
QList<GlyphInfo> m_infos;
};
@ -344,7 +383,7 @@ Engine::Engine()
{
}
void Engine::SetFonts(const QList<FontRange> & fonts, int fontSize)
void Engine::SetFonts(QList<FontRange> 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<MyThread *>(m_workThread);
thread->GetImage().save(m_dirName.trimmed() + "/font.png", "png");
QList<MyThread::GlyphInfo> 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<MyThread *>(t);
emit UpdatePreview(thread->GetImage());
emit UpdatePreview(GetImage(t));
}
QImage Engine::GetImage(QThread * sender) const
{
MyThread * thread = static_cast<MyThread *>(sender == NULL ? m_workThread : sender);
int w, h;
vector<uint8_t> 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)

View file

@ -1,7 +1,6 @@
#pragma once
#include <QString>
#include <QList>
#include <QAtomicInt>
#include <QThread>
#include <QImage>
@ -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);

View file

@ -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 \

View file

@ -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)