aboutsummaryrefslogtreecommitdiff
path: root/src/libjin/Graphics/Font
diff options
context:
space:
mode:
Diffstat (limited to 'src/libjin/Graphics/Font')
-rw-r--r--src/libjin/Graphics/Font/Decoder.cpp93
-rw-r--r--src/libjin/Graphics/Font/Decoder.h44
-rw-r--r--src/libjin/Graphics/Font/Font.h31
-rw-r--r--src/libjin/Graphics/Font/Page.h38
-rw-r--r--src/libjin/Graphics/Font/TTF.cpp463
-rw-r--r--src/libjin/Graphics/Font/TTF.h127
-rw-r--r--src/libjin/Graphics/Font/Text.cpp151
-rw-r--r--src/libjin/Graphics/Font/Text.h74
-rw-r--r--src/libjin/Graphics/Font/TextureFont.cpp300
-rw-r--r--src/libjin/Graphics/Font/TextureFont.h62
10 files changed, 1383 insertions, 0 deletions
diff --git a/src/libjin/Graphics/Font/Decoder.cpp b/src/libjin/Graphics/Font/Decoder.cpp
new file mode 100644
index 0000000..10927f0
--- /dev/null
+++ b/src/libjin/Graphics/Font/Decoder.cpp
@@ -0,0 +1,93 @@
+#include <stdlib.h>
+#include <string.h>
+#include "Decoder.h"
+
+namespace jin
+{
+namespace graphics
+{
+
+ /* utf8 byte string to unicode codepoint */
+ static const char *utf8toCodepoint(const char *p, unsigned *res) {
+ return nullptr;
+
+ }
+
+ /////////////////////////////////////////////////////////////////////////////
+ // decoders
+ /////////////////////////////////////////////////////////////////////////////
+
+ const void* Utf8::decode(const void* data, Codepoint* res) const
+ {
+ const char* p = (char*)data;
+ unsigned x, mask, shift;
+ switch (*p & 0xf0) {
+ case 0xf0: mask = 0x07; shift = 18; break;
+ case 0xe0: mask = 0x0f; shift = 12; break;
+ case 0xc0:
+ case 0xd0: mask = 0x1f; shift = 6; break;
+ default:
+ *res = *p;
+ return p + 1;
+ }
+ x = (*p & mask) << shift;
+ do {
+ if (*(++p) == '\0') {
+ *res = x;
+ return p;
+ }
+ shift -= 6;
+ x |= (*p & 0x3f) << shift;
+ } while (shift);
+ *res = x;
+ return p + 1;
+ }
+
+ const void* Utf8::next(const void* data) const
+ {
+ const char* p = (char*)data;
+ unsigned x, mask, shift;
+ switch (*p & 0xf0) {
+ case 0xf0: mask = 0x07; shift = 18; break;
+ case 0xe0: mask = 0x0f; shift = 12; break;
+ case 0xc0:
+ case 0xd0: mask = 0x1f; shift = 6; break;
+ default:
+ return p + 1;
+ }
+ x = (*p & mask) << shift;
+ do {
+ if (*(++p) == '\0') {
+ return p;
+ }
+ shift -= 6;
+ x |= (*p & 0x3f) << shift;
+ } while (shift);
+ return p + 1;
+ }
+/*
+ const void* Utf16::decode(const void* data, Codepoint* res) const
+ {
+ return nullptr;
+ }
+
+ const void* Utf16::next(const void* data) const
+ {
+ return nullptr;
+ }
+*/
+ const void* Ascii::decode(const void* data, Codepoint* res) const
+ {
+ const char* p = (char*)data;
+ *res = *p;
+ return p + 1;
+ }
+
+ const void* Ascii::next(const void* data) const
+ {
+ const char* p = (char*)data;
+ return p + 1;
+ }
+
+} // graphics
+} // jin \ No newline at end of file
diff --git a/src/libjin/Graphics/Font/Decoder.h b/src/libjin/Graphics/Font/Decoder.h
new file mode 100644
index 0000000..4568d65
--- /dev/null
+++ b/src/libjin/Graphics/Font/Decoder.h
@@ -0,0 +1,44 @@
+#ifndef __LIBJIN_UTF8_H
+#define __LIBJIN_UTF8_H
+
+#include <vector>
+
+#include "Text.h"
+
+namespace jin
+{
+namespace graphics
+{
+
+ class Decoder
+ {
+ public:
+ virtual const void* decode(const void* data, Codepoint* c) const = 0 ;
+ virtual const void* next(const void* data) const = 0;
+ };
+
+ class Utf8 : public Decoder
+ {
+ public:
+ const void* decode(const void* data, Codepoint* c) const override;
+ const void* next(const void* data) const override;
+ };
+/*
+ class Utf16 : public Decoder
+ {
+ public:
+ const void* decode(const void* data, Codepoint* c) const override;
+ const void* next(const void* data) const override;
+ };
+*/
+ class Ascii : public Decoder
+ {
+ public:
+ const void* decode(const void* data, Codepoint* c) const override;
+ const void* next(const void* data) const override;
+ };
+
+} // graphics
+} // jin
+
+#endif \ No newline at end of file
diff --git a/src/libjin/Graphics/Font/Font.h b/src/libjin/Graphics/Font/Font.h
new file mode 100644
index 0000000..424c324
--- /dev/null
+++ b/src/libjin/Graphics/Font/Font.h
@@ -0,0 +1,31 @@
+#ifndef __LIBJIN_FONT_H
+#define __LIBJIN_FONT_H
+#include <vector>
+#include "Text.h"
+
+namespace jin
+{
+namespace graphics
+{
+
+ struct Page;
+
+ class Font
+ {
+ public:
+ Font() {}
+ virtual ~Font() {};
+
+ virtual Page* typeset(const Text& text, int lineheight, int spacing = 0) = 0;
+ virtual Page* typeset(const Content& text, int lineheight, int spacing = 0) = 0;
+
+ virtual void print(const Page* page, int x, int y) = 0;
+ virtual void print(const Content& text, int x, int y, int lineheight, int spacing = 0) = 0;
+ virtual void print(const Text& text, int x, int y, int lineheight, int spacing = 0) = 0;
+
+ };
+
+} // graphics
+} // jin
+
+#endif \ No newline at end of file
diff --git a/src/libjin/Graphics/Font/Page.h b/src/libjin/Graphics/Font/Page.h
new file mode 100644
index 0000000..6e7cbdf
--- /dev/null
+++ b/src/libjin/Graphics/Font/Page.h
@@ -0,0 +1,38 @@
+#ifndef __LIBJIN_PAGE_H
+#define __LIBJIN_PAGE_H
+#include "../../math/Vector2.hpp"
+#include "Font.h"
+
+namespace jin
+{
+namespace graphics
+{
+
+ class Font;
+
+ struct GlyphVertex
+ {
+ int x, y; // screen coordinates
+ float u, v; // texture uv
+ };
+
+ struct GlyphArrayDrawInfo
+ {
+ GLuint texture; // atlas
+ unsigned int start; // glyph vertex indecies
+ unsigned int count; // glyph vertex count
+ };
+
+ /* for reduce draw call */
+ struct Page
+ {
+ Font* font;
+ std::vector<GlyphArrayDrawInfo> glyphinfolist;
+ std::vector<GlyphVertex> glyphvertices;
+ math::Vector2<int> size;
+ };
+
+}
+}
+
+#endif \ No newline at end of file
diff --git a/src/libjin/Graphics/Font/TTF.cpp b/src/libjin/Graphics/Font/TTF.cpp
new file mode 100644
index 0000000..98e57dd
--- /dev/null
+++ b/src/libjin/Graphics/Font/TTF.cpp
@@ -0,0 +1,463 @@
+#include "../../jin_configuration.h"
+#if LIBJIN_MODULES_RENDER
+
+#include <stdio.h>
+
+#include "../../Common/Array.hpp"
+#include "../OpenGL.h"
+#include "../Color.h"
+#include "../Shader.h"
+#include "TTF.h"
+#include "Page.h"
+
+#define STB_TRUETYPE_IMPLEMENTATION
+#include "../../3rdparty/stb/stb_truetype.h"
+
+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);
+ }
+
+ /*
+ * (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 };
+
+ /* utf8 byte string to unicode codepoint */
+ static const char* utf8toCodepoint(const char *p, Codepoint *res) {
+ unsigned x, mask, shift;
+ switch (*p & 0xf0) {
+ case 0xf0: mask = 0x07; shift = 18; break;
+ case 0xe0: mask = 0x0f; shift = 12; break;
+ case 0xc0:
+ case 0xd0: mask = 0x1f; shift = 6; break;
+ default:
+ *res = *p;
+ return p + 1;
+ }
+ x = (*p & mask) << shift;
+ do {
+ if (*(++p) == '\0') {
+ *res = x;
+ return p;
+ }
+ shift -= 6;
+ x |= (*p & 0x3f) << shift;
+ } while (shift);
+ *res = x;
+ return p + 1;
+ }
+
+ /* 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)
+ : cursor(0, 0)
+ , ttf(f)
+ , ttfsize(fontSize)
+ {
+ ttf->pushTTFsize(ttfsize);
+ 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;
+ }
+
+#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)
+
+ 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;
+ 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(ttfsize);
+ 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(ttfsize);
+ 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(ttfsize);
+ 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(ttfsize);
+ *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(ttfsize);
+ GLuint atlas = atlases.back();
+ const Color* bitmap = ttf->getCodepointBitmap(character, &w, &h, &xoff, &yoff);
+ int adw, lsb;
+ {
+ 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
diff --git a/src/libjin/Graphics/Font/TTF.h b/src/libjin/Graphics/Font/TTF.h
new file mode 100644
index 0000000..4f51138
--- /dev/null
+++ b/src/libjin/Graphics/Font/TTF.h
@@ -0,0 +1,127 @@
+#ifndef __LIBJINTTF_H
+#define __LIBJIN_TTF_H
+#include "../../jin_configuration.h"
+#if LIBJIN_MODULES_RENDER
+
+#include <vector>
+#include <map>
+
+#include "../../3rdparty/stb/stb_truetype.h"
+#include "../../math/quad.h"
+#include "../Color.h"
+#include "../drawable.h"
+
+#include "Page.h"
+#include "Font.h"
+#include "Text.h"
+
+namespace jin
+{
+namespace graphics
+{
+
+ /**
+ * TTFData
+ * |- TTF
+ * |- TTF
+ * .
+ * .
+ * .
+ */
+ class TTFData
+ {
+ public:
+ static TTFData* createTTFData(const unsigned char* data, unsigned int size);
+
+ ~TTFData();
+
+ void pushTTFsize(unsigned int ttfsize);
+ void popTTFsize();
+
+ Channel* getCodepointBitmapAlpha(unsigned int codepoint, int* width, int* height, int* xoff, int* yoff) const;
+ Color* getCodepointBitmap(unsigned int codepoint, int* width, int* height, int* xoff, int* yoff) const;
+
+ void getVMetrics(int* baseline, int* descent);
+ void getHMetrics(unsigned int codepoint, int* advanceWidth, int* leftSideBearing);
+
+ private:
+ static const unsigned int FONT_SIZE = 12;
+
+ TTFData(const unsigned char* data, unsigned int size);
+
+ stbtt_fontinfo info;
+ struct
+ {
+ unsigned char* data;
+ unsigned int size;
+ } raw;
+ std::vector<float> scales;
+
+ };
+
+ class TTF : public Font
+ {
+ public:
+ static TTF* createTTF(TTFData* ttfData, unsigned int ttfSzie);
+
+ Page* typeset(const Text& text, int lineheight, int spacing = 0) override;
+ Page* typeset(const Content& text, int lineheight, int spacing = 0) override;
+
+ void print(const Text& text, int x, int y, int lineheight, int spacing = 0) override;
+ void print(const Content& text, int x, int y, int lineheight, int spacing = 0) override;
+ void print(const Page* page, int x, int y) override;
+
+ ~TTF();
+
+ private:
+ struct TTFGlyph
+ {
+ GLuint atlas;
+ /* normalized coordinates */
+ struct Bbox
+ {
+ float x, y;
+ float w, h;
+ } bbox;
+ /* glyph size in pixel */
+ unsigned int width, height;
+ };
+
+ static const int TEXTURE_SIZE_LEVELS_COUNT = 7;
+ static const int TEXTURE_SIZE_LEVEL_MAX = TEXTURE_SIZE_LEVELS_COUNT - 1;
+ static const int TEXTURE_WIDTHS[TEXTURE_SIZE_LEVELS_COUNT];
+ static const int TEXTURE_HEIGHTS[TEXTURE_SIZE_LEVELS_COUNT];
+
+ TTF(TTFData* ttf, Codepoint ttfSize);
+
+ void estimateSize();
+ GLuint createAtlas();
+ TTFGlyph& bakeGlyph(Codepoint character);
+ TTFGlyph& findGlyph(Codepoint character);
+
+ int getCharWidth(int c);
+ int getCharHeight(int c);
+
+ int getTextWidth(const Content& text, int spacing = 0);
+ int getTextHeight(const Content& text, int lineheight);
+ void getTextBox(const Content& text, int* w, int* h, int lineheight, int spacing = 0);
+
+ int textureWidth;
+ int textureHeight;
+ std::vector<GLuint> atlases;
+ std::map<Codepoint, TTFGlyph> glyphs;
+ TTFData* ttf;
+ const unsigned int ttfsize;
+ int baseline;
+ int descent;
+
+ /* cursor helped render to texture */
+ math::Vector2<float> cursor;
+
+ };
+
+} // graphics
+} // jin
+
+#endif // LIBJIN_MODULES_RENDER
+#endif // __LIBJIN_FONT_H \ No newline at end of file
diff --git a/src/libjin/Graphics/Font/Text.cpp b/src/libjin/Graphics/Font/Text.cpp
new file mode 100644
index 0000000..68601de
--- /dev/null
+++ b/src/libjin/Graphics/Font/Text.cpp
@@ -0,0 +1,151 @@
+#include <cstring>
+
+#include "Text.h"
+#include "Decoder.h"
+
+namespace jin
+{
+namespace graphics
+{
+
+ /////////////////////////////////////////////////////////////////////////////
+ // iterator
+ /////////////////////////////////////////////////////////////////////////////
+
+ Text::Iterator::Iterator(const Iterator& itor)
+ : data(itor.data)
+ , p(itor.p)
+ , encode(itor.encode)
+ , length(itor.length)
+ {
+ switch (encode)
+ {
+ case Encode::UTF8: decoder = new Utf8(); break;
+ //case Encode::UTF16: decoder = new Utf16(); break;
+ case Encode::ASCII: decoder = new Ascii(); break;
+ }
+ }
+
+ Text::Iterator::Iterator(const Encode& _encode, const void* _data, unsigned int _length)
+ : data(_data)
+ , p(_data)
+ , encode(_encode)
+ , length(_length)
+ {
+ switch (encode)
+ {
+ case Encode::UTF8: decoder = new Utf8(); break;
+ //case Encode::UTF16: decoder = new Utf16(); break;
+ case Encode::ASCII: decoder = new Ascii(); break;
+ }
+ }
+
+ Text::Iterator::~Iterator()
+ {
+ delete decoder;
+ }
+
+ Codepoint Text::Iterator::get()
+ {
+ Codepoint codepoint;
+ decoder->decode(p, &codepoint);
+ return codepoint;
+ }
+
+ Codepoint Text::Iterator::operator*()
+ {
+ return get();
+ }
+
+ Text::Iterator Text::Iterator::begin()
+ {
+ Iterator itor(encode, data, length);
+ itor.toBegin();
+ return itor;
+ }
+
+ Text::Iterator Text::Iterator::end()
+ {
+ Iterator itor(encode, data, length);
+ itor.toEnd();
+ return itor;
+ }
+
+ void Text::Iterator::toBegin()
+ {
+ p = (const unsigned char*)data;
+ }
+
+ void Text::Iterator::toEnd()
+ {
+ p = (const unsigned char*)data + length;
+ }
+
+ Text::Iterator& Text::Iterator::operator ++()
+ {
+ p = decoder->next(p);
+ return *this;
+ }
+
+ Text::Iterator Text::Iterator::operator ++(int)
+ {
+ p = decoder->next(p);
+ Iterator itor(encode, data, length);
+ itor.p = p;
+ return itor;
+ }
+
+ bool Text::Iterator::operator !=(const Iterator& itor)
+ {
+ return !(data == itor.data
+ && p == itor.p
+ && length == itor.length
+ && encode == itor.encode);
+ }
+
+ bool Text::Iterator::operator ==(const Iterator& itor)
+ {
+ return data == itor.data
+ && p == itor.p
+ && length == itor.length
+ && encode == itor.encode;
+ }
+
+ /////////////////////////////////////////////////////////////////////////////
+ // text
+ /////////////////////////////////////////////////////////////////////////////
+
+ Text::Text(Encode encode, const void* data)
+ {
+ Iterator it = Iterator(encode, data, strlen((const char*)data));
+ for (; it != it.end(); ++it)
+ {
+ content.push_back(*it);
+ }
+ }
+
+ Text::Text(Encode _encode, const void* _data, unsigned int _length)
+ {
+ Iterator it = Iterator(_encode, _data, _length);
+ for (; it != it.end(); ++it)
+ {
+ content.push_back(*it);
+ }
+ }
+
+ Text::~Text()
+ {
+ }
+
+ const Content& Text::getContent() const
+ {
+ return content;
+ }
+
+ const Content& Text::operator*() const
+ {
+ return content;
+ }
+
+} // graphics
+} // jin \ No newline at end of file
diff --git a/src/libjin/Graphics/Font/Text.h b/src/libjin/Graphics/Font/Text.h
new file mode 100644
index 0000000..3faabc0
--- /dev/null
+++ b/src/libjin/Graphics/Font/Text.h
@@ -0,0 +1,74 @@
+#ifndef __LIBJIN_TEXT_H
+#define __LIBJIN_TEXT_H
+
+#include <vector>
+
+namespace jin
+{
+namespace graphics
+{
+
+ typedef unsigned int Codepoint;
+
+ typedef std::vector<Codepoint> Content;
+
+ class Text;
+ class Decoder;
+
+ enum Encode
+ {
+ UTF8, // utf-8
+ //UTF16, // utf-16
+ ASCII, // ASCII
+ };
+
+ /* raw encoded text */
+ class Text
+ {
+ public:
+ Text(Encode encode, const void* data);
+ Text(Encode encode, const void* data, unsigned int length);
+ ~Text();
+
+ const Content& getContent() const;
+ const Content& operator*() const;
+
+ private:
+ class Iterator
+ {
+ public:
+ Iterator(const Iterator& itor);
+ Iterator(const Encode& encode, const void* data, unsigned int length);
+ ~Iterator();
+
+ Codepoint get();
+ Iterator begin();
+ Iterator end();
+ void toBegin();
+ void toEnd();
+ Codepoint operator *();
+ /* prefix ++ */
+ Iterator& operator ++();
+ /* postfix ++ */
+ Iterator operator ++(int);
+ bool operator !=(const Iterator& itor);
+ bool operator ==(const Iterator& itor);
+
+ private:
+ void operator = (const Iterator&);
+
+ const Encode encode;
+ const Decoder* decoder;
+ const void* p;
+ const void* const data;
+ unsigned int length;
+ };
+
+ Content content;
+
+ };
+
+} // graphics
+} // jin
+
+#endif \ No newline at end of file
diff --git a/src/libjin/Graphics/Font/TextureFont.cpp b/src/libjin/Graphics/Font/TextureFont.cpp
new file mode 100644
index 0000000..4dde236
--- /dev/null
+++ b/src/libjin/Graphics/Font/TextureFont.cpp
@@ -0,0 +1,300 @@
+#include <vector>
+
+#include "../../Math/Vector2.hpp"
+#include "../Shader.h"
+#include "TextureFont.h"
+
+namespace jin
+{
+namespace graphics
+{
+
+ using namespace std;
+ using namespace math;
+
+ TextureFont * TextureFont::createTextureFont(const Bitmap* bitmap, const Content& codepoints, int cellw, int cellh)
+ {
+ TextureFont* tf = new TextureFont(bitmap, codepoints, cellw, cellh);
+ return tf;
+ }
+
+ TextureFont * TextureFont::createTextureFont(const Bitmap* bitmap, const Text& codepoints, int cellw, int cellh)
+ {
+ TextureFont* tf = new TextureFont(bitmap, *codepoints, cellw, cellh);
+ return tf;
+ }
+
+ TextureFont* TextureFont::createTextureFont(const Bitmap* bitmap, const Content& codepoints, Color mask, int cellh)
+ {
+ TextureFont* tf = new TextureFont(bitmap, codepoints, mask, cellh);
+ return tf;
+ }
+
+ TextureFont* TextureFont::createTextureFont(const Bitmap* bitmap, const Text& codepoints, Color mask, int cellh)
+ {
+ TextureFont* tf = new TextureFont(bitmap, *codepoints, mask, cellh);
+ return tf;
+ }
+
+ TextureFont::~TextureFont()
+ {
+ }
+
+ const TextureFont::TextureGlyph* TextureFont::findGlyph(Codepoint codepoint) const
+ {
+ auto it = glyphs.find(codepoint);
+ if (it != glyphs.end())
+ {
+ return &it->second;
+ }
+ else
+ return nullptr;
+ }
+
+ Page* TextureFont::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;
+ const TextureGlyph* 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);\
+
+ for (Codepoint c : text)
+ {
+ if (c == 0x0D)
+ continue;
+ if (c == 0x0A)
+ {
+ /* new line */
+ p.y += lineheight;
+ p.x = 0;
+ continue;
+ }
+ glyph = findGlyph(c);
+ if (glyph == nullptr)
+ continue;
+ if (texture != this->texture)
+ {
+ texture = this->texture;
+ GlyphArrayDrawInfo info;
+ info.start = i;
+ info.count = 0;
+ info.texture = texture;
+ glyphinfolist.push_back(info);
+ }
+ glyphinfolist[glyphinfolist.size() - 1].count += 4;
+ // normalized
+ float nx = glyph->x / (float)size.w, ny = glyph->y / (float)size.h;
+ float nw = glyph->w / (float)size.w, nh = glyph->h / (float)size.h;
+ glyphvertices_push(p.x, p.y, nx, ny);
+ glyphvertices_push(p.x, p.y + glyph->h, nx, ny + nh);
+ glyphvertices_push(p.x + glyph->w, p.y + glyph->h, nx + nw, ny + nh);
+ glyphvertices_push(p.x + glyph->w, p.y, nx + nw, ny);
+ p.x += glyph->w + spacing;
+ i += 4;
+ }
+ getTextBox(text, &page->size.w, &page->size.h, lineheight, spacing);
+ return page;
+ }
+
+ int TextureFont::getCharWidth(int c)
+ {
+ auto it = glyphs.find(c);
+ if (it != glyphs.end())
+ {
+ return it->second.w;
+ }
+ return 0;
+ }
+
+ int TextureFont::getCharHeight(int c)
+ {
+ auto it = glyphs.find(c);
+ if (it != glyphs.end())
+ {
+ return it->second.h;
+ }
+ return 0;
+ }
+
+ int TextureFont::getTextWidth(const Content& t, int spacing)
+ {
+ 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;
+ }
+ return res;
+ }
+
+ int TextureFont::getTextHeight(const Content& t, int lineheight)
+ {
+ 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;
+ }
+ }
+ return res;
+ }
+
+ void TextureFont::getTextBox(const Content& text, int* w, int* h, int lineheight, int spacing)
+ {
+ *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;
+ }
+ }
+
+ Page* TextureFont::typeset(const Text& text, int lineheight, int spacing)
+ {
+ return typeset(*text, lineheight, spacing);
+ }
+
+ void TextureFont::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 TextureFont::print(const Content& text, int x, int y, int lineheight, int spacing)
+ {
+ Page* page = typeset(text, lineheight, spacing);
+ print(page, x, y);
+ delete page;
+ }
+
+ void TextureFont::print(const Text& text, int x, int y, int lineheight, int spacing)
+ {
+ Page* page = typeset(text, lineheight, spacing);
+ print(page, x, y);
+ delete page;
+ }
+
+ TextureFont::TextureFont(const Bitmap* bitmap, const Content& codepoints, int cellw, int cellh)
+ : Drawable(bitmap)
+ {
+ TextureGlyph glyph;
+ Vector2<int> count(bitmap->getWidth() / cellw, bitmap->getHeight() / cellh);
+ glyph.w = cellw;
+ glyph.h = cellh;
+ for (int y = 0; y < count.row; ++y)
+ {
+ glyph.y = y * cellh;
+ for (int x = 0; x < count.colum; ++x)
+ {
+ glyph.x = x * cellw;
+ if (x + y * count.colum >= codepoints.size())
+ return;
+ glyphs.insert(std::pair<Codepoint, TextureGlyph>(codepoints[x + y * count.colum], glyph));
+ }
+ }
+ }
+
+ TextureFont::TextureFont(const Bitmap* bitmap, const Content& codepoints, Color mask, int cellh)
+ : Drawable(bitmap)
+ {
+ TextureGlyph glyph;
+ glyph.h = cellh;
+ int w = bitmap->getWidth();
+ int h = bitmap->getHeight();
+ int i = 0;
+ for (int y = 0; y < h; y += cellh)
+ {
+ glyph.y = y;
+ bool newc = false;
+ for (int x = 0; x <= w; ++x)
+ {
+ if (x == w && newc)
+ {
+ glyph.w = x - glyph.x;
+ if (i >= codepoints.size())
+ return;
+ glyphs.insert(std::pair<Codepoint, TextureGlyph>(codepoints[i], glyph));
+ ++i;
+ newc = false;
+ break;
+ }
+ Color c = bitmap->getPixels()[x + y * w];
+ if (!newc && c != mask)
+ {
+ glyph.x = x;
+ newc = true;
+ }
+ else if (newc && c == mask)
+ {
+ glyph.w = x - glyph.x;
+ if (i >= codepoints.size())
+ return;
+ glyphs.insert(std::pair<Codepoint, TextureGlyph>(codepoints[i], glyph));
+ if (codepoints[i] == 't')
+ {
+ int a = 10;
+ }
+ ++i;
+ newc = false;
+ }
+ }
+ }
+ }
+
+}
+} \ No newline at end of file
diff --git a/src/libjin/Graphics/Font/TextureFont.h b/src/libjin/Graphics/Font/TextureFont.h
new file mode 100644
index 0000000..0d0d091
--- /dev/null
+++ b/src/libjin/Graphics/Font/TextureFont.h
@@ -0,0 +1,62 @@
+#ifndef __LIBJIN_TEXTURE_FONT_H
+#define __LIBJIN_TEXTURE_FONT_H
+
+#include <map>
+#include <vector>
+
+#include "../../Math/Vector4.hpp"
+#include "../Drawable.h"
+#include "../Bitmap.h"
+
+#include "Page.h"
+#include "Font.h"
+#include "Text.h"
+
+namespace jin
+{
+namespace graphics
+{
+
+ /* Texture font */
+ class TextureFont : public Font
+ , public Drawable
+ {
+ public:
+ static TextureFont* createTextureFont(const Bitmap* bitmap, const Content& codepoints, int cellw, int cellh);
+ static TextureFont* createTextureFont(const Bitmap* bitmap, const Text& text, int cellw, int cellh);
+ static TextureFont* createTextureFont(const Bitmap* bitmap, const Content& codepoints, Color mask, int cellh);
+ static TextureFont* createTextureFont(const Bitmap* bitmap, const Text& text, Color mask, int cellh);
+
+ ~TextureFont();
+
+ Page* typeset(const Text& text, int lineheight, int spacing = 0) override;
+ Page* typeset(const Content& text, int lineheight, int spacing = 0) override ;
+
+ void print(const Page* page, int x, int y) override;
+ void print(const Content& text, int x, int y, int linehgiht, int spacing = 0) override;
+ void print(const Text& text, int x, int y, int lineheight, int spacing = 0)override;
+
+ private:
+ struct TextureGlyph
+ {
+ float x, y, w, h;
+ };
+
+ TextureFont(const Bitmap* bitmap, const Content& codepoints, int cellw, int cellh);
+ TextureFont(const Bitmap* bitmap, const Content& codepoints, Color mask, int cellh);
+
+ int getCharWidth(int c);
+ int getCharHeight(int c);
+ int getTextWidth(const Content& text, int spacing = 0);
+ int getTextHeight(const Content& text, int lineheight);
+ void getTextBox(const Content& text, int* w, int* h, int lineheight, int spacing = 0);
+ const TextureGlyph* findGlyph(Codepoint codepoint) const;
+
+ std::map<Codepoint, TextureGlyph> glyphs;
+
+ };
+
+}
+}
+
+#endif \ No newline at end of file