aboutsummaryrefslogtreecommitdiff
path: root/src/libjin/Graphics/Font.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libjin/Graphics/Font.cpp')
-rw-r--r--src/libjin/Graphics/Font.cpp349
1 files changed, 297 insertions, 52 deletions
diff --git a/src/libjin/Graphics/Font.cpp b/src/libjin/Graphics/Font.cpp
index bb767db..a3a46dd 100644
--- a/src/libjin/Graphics/Font.cpp
+++ b/src/libjin/Graphics/Font.cpp
@@ -1,24 +1,28 @@
-#include "../modules.h"
+#include "../jin_configuration.h"
#if LIBJIN_MODULES_RENDER
+#include "OpenGL.h"
#include "font.h"
#include <stdio.h>
-#define STB_TRUETYPE_IMPLEMENTATION
-#include "../3rdparty/stb/stb_truetype.h"
#include "color.h"
+#include "Shader.h"
+#include "../Common/Array.hpp"
namespace jin
{
namespace graphics
{
+ #include "Shaders/font.shader.h"
+
using namespace std;
using namespace jin::math;
-
+
const int Font::TEXTURE_WIDTHS[] = { 128, 256, 256, 512, 512, 1024, 1024 };
const int Font::TEXTURE_HEIGHTS[] = { 128, 128, 256, 256, 512, 512, 1024 };
- static const char *ttf_utf8toCodepoint(const char *p, unsigned *res) {
+ /* utf8 byte string to unicode codepoint */
+ static const char* utf8toCodepoint(const char *p, unsigned *res) {
unsigned x, mask, shift;
switch (*p & 0xf0) {
case 0xf0: mask = 0x07; shift = 18; break;
@@ -31,7 +35,6 @@ namespace graphics
}
x = (*p & mask) << shift;
do {
- /* Return early if we reach an unexpected NULL */
if (*(++p) == '\0') {
*res = x;
return p;
@@ -43,80 +46,322 @@ namespace graphics
return p + 1;
}
- /*static*/ Font* Font::createFont(const char* font, size_t size)
+ /* little endian unicode */
+ static const char* unicodeLittleEndian(const char* p, unsigned* res)
{
}
- /*static*/ Font* Font::createFont(const char* file)
+ /*static*/ Font* Font::createFont(FontData* fontData, unsigned int fontSzie)
{
-
+ Font* font;
+ try
+ {
+ font = new Font(fontData, fontSzie);
+ }
+ catch (...)
+ {
+ return nullptr;
+ }
+ return font;
}
- Font::Font()
- : textureLevel(TEXTURE_SIZE_LEVEL_MAX)
+ void Font::destroyFont(Font* font)
+ {
+ if (font != nullptr)
+ delete font;
+ }
+
+ Font::Font(FontData* f, unsigned int fontSize)
+ : cursor(0, 0)
+ , font(f)
+ , fontsize(fontSize)
{
-
+ font->pushFontsize(fontsize);
+ font->getVMetrics(&baseline, &descent);
+ estimateSize();
+ font->popFontsize();
+ /* create a default texture */
+ createAtlas();
}
- bool Font::createTexture()
+ /* estimate the size of atlas texture */
+ void Font::estimateSize()
{
- GLuint t;
- glGenTextures(1, &t);
- glBindTexture(GL_TEXTURE_2D, t);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- /*Initialize the texture, attempting smaller sizes if initialization fails.*/
- bool initialized = false;
- while (textureLevel >= 0)
+ for (int level = 0; level <= TEXTURE_SIZE_LEVEL_MAX; ++level)
{
- /*clear errors before initializing*/
- while (glGetError() != GL_NO_ERROR);
- textureWidth = TEXTURE_WIDTHS[textureLevel];
- textureHeight = TEXTURE_HEIGHTS[textureLevel];
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
- initialized = (glGetError() == GL_NO_ERROR);
- if (initialized || textureLevel <= 0)
+ if (descent * (descent*0.8) * 96 <= TEXTURE_WIDTHS[level] * TEXTURE_HEIGHTS[level])
+ {
+ textureWidth = TEXTURE_WIDTHS[level];
+ textureHeight = TEXTURE_HEIGHTS[level];
break;
- --textureLevel;
+ }
}
- if (!initialized)
+ }
+
+ Font::~Font()
+ {
+ map<unsigned int, Glyph*>::iterator it = glyphs.begin();
+ for (; it != glyphs.end(); ++it)
+ {
+ delete it->second;
+ }
+ }
+
+ GLuint Font::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);
- glBindTexture(GL_TEXTURE_2D, 0);
- return false;
+ gl.bindTexture(0);
+ return 0;
}
- textures.push_back(t);
- glBindTexture(GL_TEXTURE_2D, 0);
- return true;
+ atlases.push_back(t);
+ gl.bindTexture(0);
+ return t;
+ }
+
+ void Font::print(const char* t, int x, int y, int lineheight, int spacing)
+ {
+ Page* page = typeset(t, lineheight, spacing);
+ print(page, x, y);
+ delete page;
}
- void Font::print(const char* text, int x, int y)
+ Page* Font::typeset(const char* text, int lineheight, int spacing)
{
- int len = strlen(text);
- /* xy and uv list */
- vector<GlyphVertex> glyphvertices(len*4);
- /* texture binded along with glyphvertices */
- vector<GlyphArrayDrawInfo> glyphinfolist;
- float dx = 0;
- float dy = 0;
- //float lineheihgt = ;
+ // typesetting, for reducing draw call
+ const char* t = text;
+ Page* page = new Page();
+ vector<GlyphArrayDrawInfo>& glyphinfolist = page->glyphinfolist;
+ vector<GlyphVertex>& glyphvertices = page->glyphvertices;
+ Vector2<int> p(0, 0);
+ Codepoint c;
+ int texture = -1;
+ Glyph* glyph = nullptr;
+ GlyphVertex vertex;
+ for (int i = 0; *t != NULL; i += 4)
+ {
+ t = utf8toCodepoint(t, &c);
+ if (c == 0x0D)
+ {
+ i -= 4;
+ continue;
+ }
+ /* new line */
+ if (c == 0x0A)
+ {
+ p.y += lineheight;
+ p.x = 0;
+ i -= 4;
+ continue;
+ }
+ 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;
+ Glyph::Bbox& bbox = glyph->bbox;
+ // 1
+ vertex.x = p.x; vertex.y = p.y;
+ vertex.u = bbox.x; vertex.v = bbox.y;
+ glyphvertices.push_back(vertex);
+ // 2
+ vertex.x = p.x; vertex.y = p.y + glyph->height;
+ vertex.u = bbox.x; vertex.v = bbox.y + bbox.height;
+ glyphvertices.push_back(vertex);
+ // 3
+ vertex.x = p.x + glyph->width; vertex.y = p.y + glyph->height;
+ vertex.u = bbox.x + bbox.width; vertex.v = bbox.y + bbox.height;
+ glyphvertices.push_back(vertex);
+ // 4
+ vertex.x = p.x + glyph->width; vertex.y = p.y;
+ vertex.u = bbox.x + bbox.width; vertex.v = bbox.y;
+ glyphvertices.push_back(vertex);
+
+ p.x += glyph->width + spacing;
+ }
+ getTextBox(text, &page->width, &page->height, lineheight, spacing);
+ return page;
}
- /**
- * unicodeȾļtextureϣglyphs
- */
- Glyph* Font::addGlyph(unsigned int character)
+ void Font::print(const Page* page, int x, int y)
{
- GLuint texture = textures.back();
+ Shader* shader = Shader::getCurrentJSL();
+ 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);
+ }
+ }
+ int Font::getCharWidth(int c)
+ {
+ int adw, lsb;
+ font->pushFontsize(fontsize);
+ font->getHMetrics(c, &adw, &lsb);
+ font->popFontsize();
+ return adw;
}
- Glyph* Font::findGlyph(unsigned int character)
+ int Font::getCharHeight(int c)
+ {
+ return descent;
+ }
+
+ int Font::getTextWidth(const char* t, int spacing)
+ {
+ font->pushFontsize(fontsize);
+ int res = 0;
+ int tmp = 0;
+ const char *p = t;
+ while (*p) {
+ unsigned c;
+ p = utf8toCodepoint(p, &c);
+ if (*p == 0x0D)
+ continue;
+ if (*p == 0x0A)
+ {
+ tmp = 0;
+ continue;
+ }
+ tmp += getCharWidth(c) + spacing;
+ if (tmp > res) res = tmp;
+ }
+ font->popFontsize();
+ return res;
+ }
+
+ int Font::getTextHeight(const char* t, int lineheight)
+ {
+ font->pushFontsize(fontsize);
+ int res = 0;
+ bool newline = true;
+ while (*t)
+ {
+ unsigned c;
+ t = utf8toCodepoint(t, &c);
+ if (*t == 0x0A)
+ newline = true;
+ else if (*t == 0x0D);
+ else if (newline)
+ {
+ newline = false;
+ res += lineheight;
+ }
+ }
+ font->popFontsize();
+ return res;
+ }
+
+ void Font::getTextBox(const char* text, int* w, int* h, int lineheight, int spacing)
+ {
+ font->pushFontsize(fontsize);
+ *w = 0;
+ *h = 0;
+ int tmp = 0;
+ const char* p = text;
+ const char* pt = nullptr;
+ bool nl = true;
+ while (*p) {
+ unsigned c;
+ pt = p;
+ p = utf8toCodepoint(p, &c);
+ if (*pt == 0x0D)
+ continue;
+ if (*pt == 0x0A)
+ {
+ tmp = 0;
+ nl = true;
+ continue;
+ }
+ else if(nl)
+ {
+ nl = false;
+ *h += lineheight;
+ }
+ tmp += getCharWidth(c) + spacing;
+ if (tmp > *w) *w = tmp;
+ }
+ font->popFontsize();
+ }
+
+ Glyph* Font::bakeGlyph(unsigned int character)
{
+ Glyph* glyph = (Glyph*)malloc(sizeof(Glyph));
+ int w, h, xoff, yoff;
+ font->pushFontsize(fontsize);
+ GLuint atlas = atlases.back();
+ const Color* bitmap = font->getCodepointBitmap(character, &w, &h, &xoff, &yoff);
+ int adw, lsb;
+ {
+ font->getHMetrics(character, &adw, &lsb);
+ font->popFontsize();
+ 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;
+ }
+ glyph->atlas = atlas;
+ glyph->bbox.x = cursor.x / (float)textureWidth;
+ glyph->bbox.y = cursor.y / (float)textureHeight;
+ glyph->bbox.width = adw / (float)textureWidth;
+ glyph->bbox.height = descent / (float)textureHeight;
+ glyph->width = adw;
+ glyph->height = descent;
+ glyphs.insert(std::pair<unsigned int, Glyph*>(character, glyph));
+ cursor.x += adw;
+ return glyph;
+ }
+
+ Glyph* Font::findGlyph(unsigned int character)
+ {
+ map<unsigned int, Glyph*>::iterator it = glyphs.find(character);
+ if (it != glyphs.end())
+ {
+ return it->second;
+ }
+ else
+ {
+ Glyph* glyph = bakeGlyph(character);
+ return glyph;
+ }
}
} // graphics