aboutsummaryrefslogtreecommitdiff
path: root/src/libjin/Graphics/Font/TTF.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libjin/Graphics/Font/TTF.cpp')
-rw-r--r--src/libjin/Graphics/Font/TTF.cpp856
1 files changed, 428 insertions, 428 deletions
diff --git a/src/libjin/Graphics/Font/TTF.cpp b/src/libjin/Graphics/Font/TTF.cpp
index 105bcfb..7e62485 100644
--- a/src/libjin/Graphics/Font/TTF.cpp
+++ b/src/libjin/Graphics/Font/TTF.cpp
@@ -15,440 +15,440 @@
namespace jin
{
-namespace graphics
-{
-
- /////////////////////////////////////////////////////////////////////////////
- // TTFData
- /////////////////////////////////////////////////////////////////////////////
-
- TTFData* TTFData::createTTFData(const unsigned char* data, unsigned int size)
- {
- TTFData* ttf = nullptr;
- try
- {
- ttf = new TTFData(data, size);
- return ttf;
- }
- catch (...)
- {
- return nullptr;
- }
- }
-
- TTFData::TTFData(const unsigned char* d, unsigned int s)
- {
- raw.size = s;
- raw.data = (unsigned char*)malloc(s);
- memcpy(raw.data, d, s);
- if (!stbtt_InitFont(&info, (const unsigned char*)raw.data, 0))
- {
- delete raw.data;
- throw 0;
- }
- /* push default fontsize */
- pushTTFsize(FONT_SIZE);
- }
-
- TTFData::~TTFData()
- {
- free(raw.data);
- }
-
- TTF* TTFData::createTTF(unsigned fontSize)
+ namespace graphics
{
- TTF* ttf;
- try
+
+ /////////////////////////////////////////////////////////////////////////////
+ // TTFData
+ /////////////////////////////////////////////////////////////////////////////
+
+ TTFData* TTFData::createTTFData(const unsigned char* data, unsigned int size)
+ {
+ TTFData* ttf = nullptr;
+ try
+ {
+ ttf = new TTFData(data, size);
+ return ttf;
+ }
+ catch (...)
+ {
+ return nullptr;
+ }
+ }
+
+ TTFData::TTFData(const unsigned char* d, unsigned int s)
+ {
+ raw.size = s;
+ raw.data = (unsigned char*)malloc(s);
+ memcpy(raw.data, d, s);
+ if (!stbtt_InitFont(&info, (const unsigned char*)raw.data, 0))
+ {
+ delete raw.data;
+ throw 0;
+ }
+ /* push default fontsize */
+ pushTTFsize(FONT_SIZE);
+ }
+
+ TTFData::~TTFData()
+ {
+ free(raw.data);
+ }
+
+ TTF* TTFData::createTTF(unsigned fontSize)
+ {
+ TTF* ttf;
+ try
+ {
+ ttf = new TTF(this, fontSize);
+ }
+ catch (...)
+ {
+ return nullptr;
+ }
+ return ttf;
+ }
+
+ /*
+ * (0, 0)
+ * +--------------+ ascent
+ * | +--------+ |
+ * | | | |
+ * | | bitmap | |
+ * +--|--------|--+ baseline
+ * | +--------+ |
+ * +--|-----------+ decent
+ * | |
+ * leftSideBearing |
+ * advanceWidth
+ */
+ void TTFData::getVMetrics(int* baseline, int* descent)
+ {
+ float scale = scales.back();
+ int ascent;
+ stbtt_GetFontVMetrics(&info, &ascent, descent, 0);
+ *baseline = (int)(ascent*scale) + 1; // slight adjustment
+ *descent = *baseline - (int)(*descent*scale) + 1;
+ }
+
+ void TTFData::getHMetrics(unsigned int codepoint, int* advanceWidth, int* leftSideBearing)
+ {
+ float scale = scales.back();
+ int adw, lsb;
+ stbtt_GetCodepointHMetrics(&info, codepoint, &adw, &lsb);
+ *advanceWidth = (int)(adw*scale);
+ *leftSideBearing = (int)(lsb*scale);
+ }
+
+ void TTFData::pushTTFsize(unsigned int fs)
+ {
+ float sc = stbtt_ScaleForPixelHeight(&info, fs);
+ scales.push_back(sc);
+ }
+
+ void TTFData::popTTFsize()
+ {
+ /* always keep default ttf size on the bottom of stack */
+ if (scales.size() > 1)
+ scales.pop_back();
+ }
+
+ Channel* TTFData::getCodepointBitmapAlpha(unsigned int codepoint, int* width, int* height, int* xoff, int* yoff) const
+ {
+ float scale = scales.back();
+ Channel* bitmap = stbtt_GetCodepointBitmap(&info, scale, scale, codepoint, width, height, xoff, yoff);
+ return bitmap;
+ }
+
+ Color* TTFData::getCodepointBitmap(unsigned int codepoint, int* width, int* height, int* xoff, int* yoff) const
+ {
+ float scale = scales.back();
+ Channel* bitmap = stbtt_GetCodepointBitmap(&info, scale, scale, codepoint, width, height, xoff, yoff);
+ int w = *width, h = *height;
+ //int xo = *xoff, yo = *yoff;
+ Color* bitmap32 = new Color[w*h];
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ bitmap32[x + y * w].set(0xff, 0xff, 0xff, bitmap[x + y * w]);
+ }
+ }
+ free(bitmap);
+ return bitmap32;
+ }
+
+ /////////////////////////////////////////////////////////////////////////////
+ // TTF
+ /////////////////////////////////////////////////////////////////////////////
+
+ #include "../Shaders/font.shader.h"
+
+ using namespace std;
+ using namespace jin::math;
+
+ const int TTF::TEXTURE_WIDTHS[] = { 128, 256, 256, 512, 512, 1024, 1024 };
+ const int TTF::TEXTURE_HEIGHTS[] = { 128, 128, 256, 256, 512, 512, 1024 };
+
+ /* little endian unicode */
+ static const char* unicodeLittleEndian(const char* p, unsigned* res)
+ {
+ }
+
+ ///*static*/ TTF* TTF::createTTF(TTFData* fontData, unsigned int fontSzie)
+ //{
+ // TTF* ttf;
+ // try
+ // {
+ // ttf = new TTF(fontData, fontSzie);
+ // }
+ // catch (...)
+ // {
+ // return nullptr;
+ // }
+ // return ttf;
+ //}
+
+ TTF::TTF(TTFData* f, unsigned int fontSize)
+ : Font(fontSize)
+ , cursor(0, 0)
+ , ttf(f)
+ {
+ ttf->pushTTFsize(fontSize);
+ ttf->getVMetrics(&baseline, &descent);
+ estimateSize();
+ ttf->popTTFsize();
+ /* create a default texture */
+ createAtlas();
+ }
+
+ /* estimate the size of atlas texture */
+ void TTF::estimateSize()
+ {
+ for (int level = 0; level <= TEXTURE_SIZE_LEVEL_MAX; ++level)
+ {
+ if (descent * (descent*0.8) * 96 <= TEXTURE_WIDTHS[level] * TEXTURE_HEIGHTS[level])
+ {
+ textureWidth = TEXTURE_WIDTHS[level];
+ textureHeight = TEXTURE_HEIGHTS[level];
+ break;
+ }
+ }
+ }
+
+ TTF::~TTF()
{
- ttf = new TTF(this, fontSize);
}
- catch (...)
+
+ GLuint TTF::createAtlas()
{
- return nullptr;
+ GLuint t;
+ gl.flushError();
+ t = gl.genTexture();
+ gl.bindTexture(t);
+ gl.setTexParameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ gl.setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ gl.setTexParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl.setTexParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ gl.texImage(GL_RGBA8, textureWidth, textureHeight, GL_RGBA, GL_UNSIGNED_BYTE);
+ if (glGetError() != GL_NO_ERROR)
+ {
+ glDeleteTextures(1, &t);
+ gl.bindTexture(0);
+ return 0;
+ }
+ atlases.push_back(t);
+ gl.bindTexture(0);
+ return t;
}
- return ttf;
- }
-
- /*
- * (0, 0)
- * +--------------+ ascent
- * | +--------+ |
- * | | | |
- * | | bitmap | |
- * +--|--------|--+ baseline
- * | +--------+ |
- * +--|-----------+ decent
- * | |
- * leftSideBearing |
- * advanceWidth
- */
- void TTFData::getVMetrics(int* baseline, int* descent)
- {
- float scale = scales.back();
- int ascent;
- stbtt_GetFontVMetrics(&info, &ascent, descent, 0);
- *baseline = (int)(ascent*scale) + 1; // slight adjustment
- *descent = *baseline - (int)(*descent*scale) + 1;
- }
-
- void TTFData::getHMetrics(unsigned int codepoint, int* advanceWidth, int* leftSideBearing)
- {
- float scale = scales.back();
- int adw, lsb;
- stbtt_GetCodepointHMetrics(&info, codepoint, &adw, &lsb);
- *advanceWidth = (int)(adw*scale);
- *leftSideBearing = (int)(lsb*scale);
- }
-
- void TTFData::pushTTFsize(unsigned int fs)
- {
- float sc = stbtt_ScaleForPixelHeight(&info, fs);
- scales.push_back(sc);
- }
-
- void TTFData::popTTFsize()
- {
- /* always keep default ttf size on the bottom of stack */
- if (scales.size() > 1)
- scales.pop_back();
- }
-
- Channel* TTFData::getCodepointBitmapAlpha(unsigned int codepoint, int* width, int* height, int* xoff, int* yoff) const
- {
- float scale = scales.back();
- Channel* bitmap = stbtt_GetCodepointBitmap(&info, scale, scale, codepoint, width, height, xoff, yoff);
- return bitmap;
- }
-
- Color* TTFData::getCodepointBitmap(unsigned int codepoint, int* width, int* height, int* xoff, int* yoff) const
- {
- float scale = scales.back();
- Channel* bitmap = stbtt_GetCodepointBitmap(&info, scale, scale, codepoint, width, height, xoff, yoff);
- int w = *width, h = *height;
- //int xo = *xoff, yo = *yoff;
- Color* bitmap32 = new Color[w*h];
- for (int y = 0; y < h; ++y)
- {
- for (int x = 0; x < w; ++x)
- {
- bitmap32[x + y * w].set(0xff, 0xff, 0xff, bitmap[x + y * w]);
- }
- }
- free(bitmap);
- return bitmap32;
- }
-
- /////////////////////////////////////////////////////////////////////////////
- // TTF
- /////////////////////////////////////////////////////////////////////////////
-
- #include "../Shaders/font.shader.h"
-
- using namespace std;
- using namespace jin::math;
-
- const int TTF::TEXTURE_WIDTHS[] = { 128, 256, 256, 512, 512, 1024, 1024 };
- const int TTF::TEXTURE_HEIGHTS[] = { 128, 128, 256, 256, 512, 512, 1024 };
-
- /* little endian unicode */
- static const char* unicodeLittleEndian(const char* p, unsigned* res)
- {
- }
-
- ///*static*/ TTF* TTF::createTTF(TTFData* fontData, unsigned int fontSzie)
- //{
- // TTF* ttf;
- // try
- // {
- // ttf = new TTF(fontData, fontSzie);
- // }
- // catch (...)
- // {
- // return nullptr;
- // }
- // return ttf;
- //}
-
- TTF::TTF(TTFData* f, unsigned int fontSize)
- : Font(fontSize)
- , cursor(0, 0)
- , ttf(f)
- {
- ttf->pushTTFsize(fontSize);
- ttf->getVMetrics(&baseline, &descent);
- estimateSize();
- ttf->popTTFsize();
- /* create a default texture */
- createAtlas();
- }
-
- /* estimate the size of atlas texture */
- void TTF::estimateSize()
- {
- for (int level = 0; level <= TEXTURE_SIZE_LEVEL_MAX; ++level)
- {
- if (descent * (descent*0.8) * 96 <= TEXTURE_WIDTHS[level] * TEXTURE_HEIGHTS[level])
- {
- textureWidth = TEXTURE_WIDTHS[level];
- textureHeight = TEXTURE_HEIGHTS[level];
- break;
- }
- }
- }
-
- TTF::~TTF()
- {
- }
-
- GLuint TTF::createAtlas()
- {
- GLuint t;
- gl.flushError();
- t = gl.genTexture();
- gl.bindTexture(t);
- gl.setTexParameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- gl.setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- gl.setTexParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- gl.setTexParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- gl.texImage(GL_RGBA8, textureWidth, textureHeight, GL_RGBA, GL_UNSIGNED_BYTE);
- if (glGetError() != GL_NO_ERROR)
- {
- glDeleteTextures(1, &t);
- gl.bindTexture(0);
- return 0;
- }
- atlases.push_back(t);
- gl.bindTexture(0);
- return t;
- }
- void TTF::print(const Content& t, int x, int y, int lineheight, int spacing)
- {
- Page* page = typeset(t, lineheight, spacing);
- print(page, x, y);
- delete page;
- }
-
- Page* TTF::typeset(const Content& text, int lineheight, int spacing)
- {
- Page* page = new Page();
- page->font = this;
- vector<GlyphArrayDrawInfo>& glyphinfolist = page->glyphinfolist;
- vector<GlyphVertex>& glyphvertices = page->glyphvertices;
- int texture = -1;
- TTFGlyph* glyph = nullptr;
- GlyphVertex vertex;
- Vector2<int> p(0, 0);
- int i = 0;
-
-#define glyphvertices_push(_x, _y, _u, _v) \
- vertex.x = _x; vertex.y = _y;\
- vertex.u = _u; vertex.v = _v;\
- glyphvertices.push_back(vertex);
-
-#define glyphlize(c)\
- do{\
- glyph = &findGlyph(c); \
- if (texture != glyph->atlas) \
- { \
- GlyphArrayDrawInfo info; \
- info.start = i; \
- info.count = 0; \
- info.texture = glyph->atlas; \
- texture = glyph->atlas; \
- glyphinfolist.push_back(info); \
- } \
- glyphinfolist[glyphinfolist.size() - 1].count += 4; \
- TTFGlyph::Bbox& bbox = glyph->bbox; \
- glyphvertices_push(p.x, p.y, bbox.x, bbox.y); \
- glyphvertices_push(p.x, p.y + glyph->height, bbox.x, bbox.y + bbox.h); \
- glyphvertices_push(p.x + glyph->width, p.y + glyph->height, bbox.x + bbox.w, bbox.y + bbox.h); \
- glyphvertices_push(p.x + glyph->width, p.y, bbox.x + bbox.w, bbox.y); \
- }while(0)
-
- for (Codepoint c : text)
- {
- if (c == 0x0D)
- continue;
- if (c == 0x0A)
- {
- /* new line */
- p.y += lineheight;
- p.x = 0;
- continue;
- }
- glyphlize(c);
- p.x += glyph->width + spacing;
- i += 4;
- }
- getTextBox(text, &page->size.w, &page->size.h, lineheight, spacing);
- return page;
- }
-
- Page* TTF::typeset(const Text& text, int lineheight, int spacing)
- {
- return typeset(*text, lineheight, spacing);
- }
-
- void TTF::print(const Page* page, int x, int y)
- {
- Shader* shader = Shader::getCurrentShader();
- const vector<GlyphArrayDrawInfo>& glyphinfolist = page->glyphinfolist;
- const vector<GlyphVertex>& glyphvertices = page->glyphvertices;
- gl.ModelMatrix.setTransformation(x, y, 0, 1, 1, 0, 0);
- shader->sendMatrix4(SHADER_MODEL_MATRIX, &gl.ModelMatrix);
- shader->sendMatrix4(SHADER_PROJECTION_MATRIX, &gl.ProjectionMatrix);
- for (int i = 0; i < glyphinfolist.size(); ++i)
- {
- const GlyphArrayDrawInfo& info = glyphinfolist[i];
- shader->bindVertexPointer(2, GL_INT, sizeof(GlyphVertex), &glyphvertices[info.start].x);
- shader->bindUVPointer(2, GL_FLOAT, sizeof(GlyphVertex), &glyphvertices[info.start].u);
- gl.bindTexture(info.texture);
- gl.drawArrays(GL_QUADS, 0, info.count);
- gl.bindTexture(0);
- }
- }
-
- void TTF::print(const Text& text, int x, int y, int lineheight, int spacing /* = 0 */)
- {
- print(*text, x, y, lineheight, spacing);
- }
-
- int TTF::getCharWidth(int c)
- {
- int adw, lsb;
- ttf->pushTTFsize(fontSize);
- ttf->getHMetrics(c, &adw, &lsb);
- ttf->popTTFsize();
- return adw;
- }
-
- int TTF::getCharHeight(int c)
- {
- return descent;
- }
-
- int TTF::getTextWidth(const Content& t, int spacing)
- {
- ttf->pushTTFsize(fontSize);
- int res = 0;
- int tmp = 0;
- for (Codepoint c : t)
- {
- if (c == 0x0D)
- continue;
- if (c == 0x0A)
- {
- tmp = 0;
- continue;
- }
- tmp += getCharWidth(c) + spacing;
- if (tmp > res)
- res = tmp;
- }
- ttf->popTTFsize();
- return res;
- }
-
- int TTF::getTextHeight(const Content& t, int lineheight)
- {
- ttf->pushTTFsize(fontSize);
- int res = 0;
- bool newline = true;
- for (Codepoint c : t)
- {
- if (c == 0x0A)
- newline = true;
- else if (c == 0x0D);
- else if (newline)
- {
- newline = false;
- res += lineheight;
- }
- }
- ttf->popTTFsize();
- return res;
- }
-
- void TTF::getTextBox(const Content& text, int* w, int* h, int lineheight, int spacing)
- {
- ttf->pushTTFsize(fontSize);
- *w = 0;
- *h = 0;
- int tmp = 0;
- bool newline = true;
- for (Codepoint c : text)
- {
- if (c == 0x0D)
- continue;
- if (c == 0x0A)
- {
- tmp = 0;
- newline = true;
- continue;
- }
- else if (newline)
- {
- newline = false;
- *h += lineheight;
- }
- tmp += getCharWidth(c) + spacing;
- if (tmp > *w)
- *w = tmp;
- }
- ttf->popTTFsize();
- }
-
- TTF::TTFGlyph& TTF::bakeGlyph(unsigned int character)
- {
- int w, h, xoff, yoff;
- ttf->pushTTFsize(fontSize);
- GLuint atlas = atlases.back();
- const Color* bitmap = ttf->getCodepointBitmap(character, &w, &h, &xoff, &yoff);
- int adw, lsb;
- {
- /* bake glyph */
- ttf->getHMetrics(character, &adw, &lsb);
- ttf->popTTFsize();
- if (cursor.x + adw > textureWidth )
- {
- cursor.x = 0;
- cursor.y += descent;
- if (cursor.y + descent * 2 > textureHeight)
- {
- /* create new atlas */
- atlas = createAtlas();
- cursor.y = 0;
- }
- }
- gl.bindTexture(atlas);
- gl.texSubImage(cursor.x + xoff, cursor.y + yoff + baseline, w, h, GL_RGBA, GL_UNSIGNED_BYTE, bitmap);
- gl.bindTexture();
- delete[] bitmap;
- }
- TTFGlyph glyph;
- glyph.atlas = atlas;
- glyph.bbox.x = cursor.x / (float)textureWidth;
- glyph.bbox.y = cursor.y / (float)textureHeight;
- glyph.bbox.w = adw / (float)textureWidth;
- glyph.bbox.h = descent / (float)textureHeight;
- glyph.width = adw;
- glyph.height = descent;
- glyphs.insert(std::pair<unsigned int, TTFGlyph>(character, glyph));
- cursor.x += adw;
- return glyphs[character];
- }
-
- TTF::TTFGlyph& TTF::findGlyph(unsigned int character)
- {
- map<unsigned int, TTFGlyph>::iterator it = glyphs.find(character);
- if (it != glyphs.end())
- return it->second;
- else
- return bakeGlyph(character);
- }
-
-} // graphics
+ void TTF::print(const Content& t, int x, int y, int lineheight, int spacing)
+ {
+ Page* page = typeset(t, lineheight, spacing);
+ print(page, x, y);
+ delete page;
+ }
+
+ Page* TTF::typeset(const Content& text, int lineheight, int spacing)
+ {
+ Page* page = new Page();
+ page->font = this;
+ vector<GlyphArrayDrawInfo>& glyphinfolist = page->glyphinfolist;
+ vector<GlyphVertex>& glyphvertices = page->glyphvertices;
+ int texture = -1;
+ TTFGlyph* glyph = nullptr;
+ GlyphVertex vertex;
+ Vector2<int> p(0, 0);
+ int i = 0;
+
+ #define glyphvertices_push(_x, _y, _u, _v) \
+ vertex.x = _x; vertex.y = _y;\
+ vertex.u = _u; vertex.v = _v;\
+ glyphvertices.push_back(vertex);
+
+ #define glyphlize(c)\
+ do{\
+ glyph = &findGlyph(c); \
+ if (texture != glyph->atlas) \
+ { \
+ GlyphArrayDrawInfo info; \
+ info.start = i; \
+ info.count = 0; \
+ info.texture = glyph->atlas; \
+ texture = glyph->atlas; \
+ glyphinfolist.push_back(info); \
+ } \
+ glyphinfolist[glyphinfolist.size() - 1].count += 4; \
+ TTFGlyph::Bbox& bbox = glyph->bbox; \
+ glyphvertices_push(p.x, p.y, bbox.x, bbox.y); \
+ glyphvertices_push(p.x, p.y + glyph->height, bbox.x, bbox.y + bbox.h); \
+ glyphvertices_push(p.x + glyph->width, p.y + glyph->height, bbox.x + bbox.w, bbox.y + bbox.h); \
+ glyphvertices_push(p.x + glyph->width, p.y, bbox.x + bbox.w, bbox.y); \
+ }while(0)
+
+ for (Codepoint c : text)
+ {
+ if (c == 0x0D)
+ continue;
+ if (c == 0x0A)
+ {
+ /* new line */
+ p.y += lineheight;
+ p.x = 0;
+ continue;
+ }
+ glyphlize(c);
+ p.x += glyph->width + spacing;
+ i += 4;
+ }
+ getTextBox(text, &page->size.w, &page->size.h, lineheight, spacing);
+ return page;
+ }
+
+ Page* TTF::typeset(const Text& text, int lineheight, int spacing)
+ {
+ return typeset(*text, lineheight, spacing);
+ }
+
+ void TTF::print(const Page* page, int x, int y)
+ {
+ Shader* shader = Shader::getCurrentShader();
+ const vector<GlyphArrayDrawInfo>& glyphinfolist = page->glyphinfolist;
+ const vector<GlyphVertex>& glyphvertices = page->glyphvertices;
+ gl.ModelMatrix.setTransformation(x, y, 0, 1, 1, 0, 0);
+ shader->sendMatrix4(SHADER_MODEL_MATRIX, &gl.ModelMatrix);
+ shader->sendMatrix4(SHADER_PROJECTION_MATRIX, &gl.ProjectionMatrix);
+ for (int i = 0; i < glyphinfolist.size(); ++i)
+ {
+ const GlyphArrayDrawInfo& info = glyphinfolist[i];
+ shader->bindVertexPointer(2, GL_INT, sizeof(GlyphVertex), &glyphvertices[info.start].x);
+ shader->bindUVPointer(2, GL_FLOAT, sizeof(GlyphVertex), &glyphvertices[info.start].u);
+ gl.bindTexture(info.texture);
+ gl.drawArrays(GL_QUADS, 0, info.count);
+ gl.bindTexture(0);
+ }
+ }
+
+ void TTF::print(const Text& text, int x, int y, int lineheight, int spacing /* = 0 */)
+ {
+ print(*text, x, y, lineheight, spacing);
+ }
+
+ int TTF::getCharWidth(int c)
+ {
+ int adw, lsb;
+ ttf->pushTTFsize(fontSize);
+ ttf->getHMetrics(c, &adw, &lsb);
+ ttf->popTTFsize();
+ return adw;
+ }
+
+ int TTF::getCharHeight(int c)
+ {
+ return descent;
+ }
+
+ int TTF::getTextWidth(const Content& t, int spacing)
+ {
+ ttf->pushTTFsize(fontSize);
+ int res = 0;
+ int tmp = 0;
+ for (Codepoint c : t)
+ {
+ if (c == 0x0D)
+ continue;
+ if (c == 0x0A)
+ {
+ tmp = 0;
+ continue;
+ }
+ tmp += getCharWidth(c) + spacing;
+ if (tmp > res)
+ res = tmp;
+ }
+ ttf->popTTFsize();
+ return res;
+ }
+
+ int TTF::getTextHeight(const Content& t, int lineheight)
+ {
+ ttf->pushTTFsize(fontSize);
+ int res = 0;
+ bool newline = true;
+ for (Codepoint c : t)
+ {
+ if (c == 0x0A)
+ newline = true;
+ else if (c == 0x0D);
+ else if (newline)
+ {
+ newline = false;
+ res += lineheight;
+ }
+ }
+ ttf->popTTFsize();
+ return res;
+ }
+
+ void TTF::getTextBox(const Content& text, int* w, int* h, int lineheight, int spacing)
+ {
+ ttf->pushTTFsize(fontSize);
+ *w = 0;
+ *h = 0;
+ int tmp = 0;
+ bool newline = true;
+ for (Codepoint c : text)
+ {
+ if (c == 0x0D)
+ continue;
+ if (c == 0x0A)
+ {
+ tmp = 0;
+ newline = true;
+ continue;
+ }
+ else if (newline)
+ {
+ newline = false;
+ *h += lineheight;
+ }
+ tmp += getCharWidth(c) + spacing;
+ if (tmp > *w)
+ *w = tmp;
+ }
+ ttf->popTTFsize();
+ }
+
+ TTF::TTFGlyph& TTF::bakeGlyph(unsigned int character)
+ {
+ int w, h, xoff, yoff;
+ ttf->pushTTFsize(fontSize);
+ GLuint atlas = atlases.back();
+ const Color* bitmap = ttf->getCodepointBitmap(character, &w, &h, &xoff, &yoff);
+ int adw, lsb;
+ {
+ /* bake glyph */
+ ttf->getHMetrics(character, &adw, &lsb);
+ ttf->popTTFsize();
+ if (cursor.x + adw > textureWidth )
+ {
+ cursor.x = 0;
+ cursor.y += descent;
+ if (cursor.y + descent * 2 > textureHeight)
+ {
+ /* create new atlas */
+ atlas = createAtlas();
+ cursor.y = 0;
+ }
+ }
+ gl.bindTexture(atlas);
+ gl.texSubImage(cursor.x + xoff, cursor.y + yoff + baseline, w, h, GL_RGBA, GL_UNSIGNED_BYTE, bitmap);
+ gl.bindTexture();
+ delete[] bitmap;
+ }
+ TTFGlyph glyph;
+ glyph.atlas = atlas;
+ glyph.bbox.x = cursor.x / (float)textureWidth;
+ glyph.bbox.y = cursor.y / (float)textureHeight;
+ glyph.bbox.w = adw / (float)textureWidth;
+ glyph.bbox.h = descent / (float)textureHeight;
+ glyph.width = adw;
+ glyph.height = descent;
+ glyphs.insert(std::pair<unsigned int, TTFGlyph>(character, glyph));
+ cursor.x += adw;
+ return glyphs[character];
+ }
+
+ TTF::TTFGlyph& TTF::findGlyph(unsigned int character)
+ {
+ map<unsigned int, TTFGlyph>::iterator it = glyphs.find(character);
+ if (it != glyphs.end())
+ return it->second;
+ else
+ return bakeGlyph(character);
+ }
+
+ } // graphics
} // jin
#endif // LIBJIN_MODULES_RENDER \ No newline at end of file