mirror of https://github.com/AxioDL/metaforce.git
addditional font atlas implementation
This commit is contained in:
parent
99d85ba60c
commit
b6a5655067
|
@ -1,3 +1,6 @@
|
||||||
[submodule "freetype2"]
|
[submodule "freetype2"]
|
||||||
path = freetype2
|
path = freetype2
|
||||||
url = https://github.com/AxioDL/freetype2
|
url = https://github.com/AxioDL/freetype2
|
||||||
|
[submodule "MathLib"]
|
||||||
|
path = MathLib
|
||||||
|
url = https://github.com/AxioDL/MathLib.git
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
add_subdirectory(freetype2)
|
add_subdirectory(freetype2)
|
||||||
|
add_subdirectory(MathLib)
|
||||||
|
set(MATHLIB_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/MathLib/include)
|
||||||
|
|
||||||
if(NOT DEFINED HECL_INCLUDE_DIR)
|
if(NOT DEFINED HECL_INCLUDE_DIR)
|
||||||
message(FATAL_ERROR "Specter may only be built as a sub-project containing hecl with
|
message(FATAL_ERROR "Specter may only be built as a sub-project containing hecl with
|
||||||
|
@ -24,7 +26,8 @@ add_subdirectory(resources/fonts)
|
||||||
|
|
||||||
include_directories(include ${HECL_INCLUDE_DIR} ${BOO_INCLUDE_DIR}
|
include_directories(include ${HECL_INCLUDE_DIR} ${BOO_INCLUDE_DIR}
|
||||||
${LOG_VISOR_INCLUDE_DIR} ${ATHENA_INCLUDE_DIR}
|
${LOG_VISOR_INCLUDE_DIR} ${ATHENA_INCLUDE_DIR}
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/freetype2/include)
|
${CMAKE_CURRENT_SOURCE_DIR}/freetype2/include
|
||||||
|
${MATHLIB_INCLUDE_DIR})
|
||||||
|
|
||||||
list(APPEND SPECTER_HEADERS
|
list(APPEND SPECTER_HEADERS
|
||||||
include/Specter/Specter.hpp
|
include/Specter/Specter.hpp
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 8d54a3c7b5d76e32d9fb3ad57619a680b32909b7
|
|
@ -42,6 +42,8 @@ class FreeTypeGZipMemFace
|
||||||
FT_Face m_face = nullptr;
|
FT_Face m_face = nullptr;
|
||||||
public:
|
public:
|
||||||
FreeTypeGZipMemFace(FT_Library lib, const uint8_t* data, size_t sz);
|
FreeTypeGZipMemFace(FT_Library lib, const uint8_t* data, size_t sz);
|
||||||
|
FreeTypeGZipMemFace(const FreeTypeGZipMemFace& other) = delete;
|
||||||
|
FreeTypeGZipMemFace& operator=(const FreeTypeGZipMemFace& other) = delete;
|
||||||
~FreeTypeGZipMemFace() {close();}
|
~FreeTypeGZipMemFace() {close();}
|
||||||
void open();
|
void open();
|
||||||
void close();
|
void close();
|
||||||
|
@ -53,7 +55,9 @@ class FontAtlas
|
||||||
friend class FontCache;
|
friend class FontCache;
|
||||||
FT_Face m_face;
|
FT_Face m_face;
|
||||||
boo::ITextureS* m_tex;
|
boo::ITextureS* m_tex;
|
||||||
|
uint32_t m_dpi;
|
||||||
|
|
||||||
|
public:
|
||||||
struct Glyph
|
struct Glyph
|
||||||
{
|
{
|
||||||
atUint32 m_unicodePoint;
|
atUint32 m_unicodePoint;
|
||||||
|
@ -65,14 +69,30 @@ class FontAtlas
|
||||||
atUint8 m_width;
|
atUint8 m_width;
|
||||||
atUint8 m_height;
|
atUint8 m_height;
|
||||||
atInt8 m_verticalOffset;
|
atInt8 m_verticalOffset;
|
||||||
atUint16 m_kernIdx;
|
atInt16 m_kernIdx = -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct KernAdj
|
||||||
|
{
|
||||||
|
atUint32 a;
|
||||||
|
atUint32 b;
|
||||||
|
atInt8 adj;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
std::vector<Glyph> m_glyphs;
|
std::vector<Glyph> m_glyphs;
|
||||||
std::map<atUint32, size_t> m_glyphLookup;
|
std::vector<KernAdj> m_kernAdjs;
|
||||||
|
std::unordered_map<atUint32, size_t> m_glyphLookup;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, bool subpixel, Athena::io::FileWriter& writer);
|
FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi,
|
||||||
FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, bool subpixel, Athena::io::FileReader& reader);
|
bool subpixel, Athena::io::FileWriter& writer);
|
||||||
|
FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi,
|
||||||
|
bool subpixel, Athena::io::FileReader& reader);
|
||||||
|
FontAtlas(const FontAtlas& other) = delete;
|
||||||
|
FontAtlas& operator=(const FontAtlas& other) = delete;
|
||||||
|
|
||||||
|
uint32_t dpi() const {return m_dpi;}
|
||||||
};
|
};
|
||||||
|
|
||||||
class FontCache
|
class FontCache
|
||||||
|
@ -92,6 +112,8 @@ class FontCache
|
||||||
std::unordered_map<FontTag, std::unique_ptr<FontAtlas>> m_cachedAtlases;
|
std::unordered_map<FontTag, std::unique_ptr<FontAtlas>> m_cachedAtlases;
|
||||||
public:
|
public:
|
||||||
FontCache(const HECL::Runtime::FileStoreManager& fileMgr);
|
FontCache(const HECL::Runtime::FileStoreManager& fileMgr);
|
||||||
|
FontCache(const FontCache& other) = delete;
|
||||||
|
FontCache& operator=(const FontCache& other) = delete;
|
||||||
|
|
||||||
FontTag prepCustomFont(boo::IGraphicsDataFactory* gf,
|
FontTag prepCustomFont(boo::IGraphicsDataFactory* gf,
|
||||||
const std::string& name, FT_Face face, bool subpixel=false,
|
const std::string& name, FT_Face face, bool subpixel=false,
|
||||||
|
@ -106,6 +128,8 @@ public:
|
||||||
{return prepCustomFont(gf, "bmonofont", m_monoFace, subpixel, points, dpi);}
|
{return prepCustomFont(gf, "bmonofont", m_monoFace, subpixel, points, dpi);}
|
||||||
|
|
||||||
void closeBuiltinFonts() {m_regFace.close(); m_monoFace.close();}
|
void closeBuiltinFonts() {m_regFace.close(); m_monoFace.close();}
|
||||||
|
|
||||||
|
const FontAtlas& lookupAtlas(FontTag tag) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,68 @@
|
||||||
#define SPECTER_TEXTVIEW_HPP
|
#define SPECTER_TEXTVIEW_HPP
|
||||||
|
|
||||||
#include "View.hpp"
|
#include "View.hpp"
|
||||||
|
#include <boo/graphicsdev/GL.hpp>
|
||||||
|
#include <boo/graphicsdev/D3D.hpp>
|
||||||
|
#include <boo/graphicsdev/Metal.hpp>
|
||||||
|
|
||||||
|
#include "FontCache.hpp"
|
||||||
|
|
||||||
namespace Specter
|
namespace Specter
|
||||||
{
|
{
|
||||||
|
|
||||||
class TextView : public View
|
class TextView : public View
|
||||||
{
|
{
|
||||||
|
boo::IGraphicsBufferD* m_glyphBuf;
|
||||||
|
int m_validDynamicSlots = 0;
|
||||||
|
size_t m_curGlyphCapacity;
|
||||||
|
const FontAtlas& m_fontAtlas;
|
||||||
|
boo::IVertexFormat* m_vtxFmt = nullptr; /* OpenGL only */
|
||||||
|
|
||||||
|
public:
|
||||||
|
class System
|
||||||
|
{
|
||||||
|
friend class TextView;
|
||||||
|
boo::IGraphicsDataFactory* m_factory;
|
||||||
|
FontCache& m_fcache;
|
||||||
|
boo::IShaderPipeline* m_regular;
|
||||||
|
boo::IShaderPipeline* m_subpixel;
|
||||||
|
boo::IGraphicsBufferS* m_quadVBO;
|
||||||
|
boo::IVertexFormat* m_vtxFmt = nullptr; /* Not OpenGL */
|
||||||
|
|
||||||
|
System(boo::IGraphicsDataFactory* factory, FontCache& fcache)
|
||||||
|
: m_factory(factory), m_fcache(fcache) {}
|
||||||
|
};
|
||||||
|
static System BuildTextSystem(boo::GLDataFactory* factory, FontCache& fcache);
|
||||||
|
#if _WIN32
|
||||||
|
static Shaders BuildTextSystem(boo::ID3DDataFactory* factory, FontCache& fcache);
|
||||||
|
#elif BOO_HAS_METAL
|
||||||
|
static Shaders BuildTextSystem(boo::MetalDataFactory* factory, FontCache& fcache);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TextView(System& system, FontTag font, size_t initGlyphCapacity=256);
|
||||||
|
|
||||||
|
struct RenderGlyph
|
||||||
|
{
|
||||||
|
Zeus::CMatrix4f m_mvp;
|
||||||
|
Zeus::CVector3f m_uv[4];
|
||||||
|
Zeus::CColor m_color;
|
||||||
|
};
|
||||||
|
std::vector<RenderGlyph>& accessGlyphs() {return m_glyphs;}
|
||||||
|
void updateGlyphs() {m_validDynamicSlots = 0;}
|
||||||
|
|
||||||
|
void typesetGlyphs(const std::string& str,
|
||||||
|
Zeus::CColor defaultColor=Zeus::CColor::skWhite);
|
||||||
|
void typesetGlyphs(const std::wstring& str,
|
||||||
|
Zeus::CColor defaultColor=Zeus::CColor::skWhite);
|
||||||
|
|
||||||
|
void colorGlyphs(Zeus::CColor newColor);
|
||||||
|
void colorGlyphsTypeOn(Zeus::CColor newColor, float startInterval=0.2, float fadeTime=0.5);
|
||||||
|
void think();
|
||||||
|
|
||||||
|
void draw(boo::IGraphicsCommandQueue* gfxQ);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<RenderGlyph> m_glyphs;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,18 +2,20 @@
|
||||||
#define SPECTER_VIEW_HPP
|
#define SPECTER_VIEW_HPP
|
||||||
|
|
||||||
#include <boo/boo.hpp>
|
#include <boo/boo.hpp>
|
||||||
|
#include "CVector3f.hpp"
|
||||||
|
#include "CMatrix4f.hpp"
|
||||||
|
#include "CTransform.hpp"
|
||||||
|
#include "CColor.hpp"
|
||||||
|
|
||||||
namespace Specter
|
namespace Specter
|
||||||
{
|
{
|
||||||
|
|
||||||
class View
|
class View
|
||||||
{
|
{
|
||||||
boo::SWindowRect m_rect;
|
protected:
|
||||||
|
boo::SWindowRect m_viewport;
|
||||||
|
void bindViewport(boo::IGraphicsCommandQueue* gfxQ) {gfxQ->setViewport(m_viewport);}
|
||||||
public:
|
public:
|
||||||
void bindViewport(boo::IGraphicsCommandQueue* gfxQ)
|
|
||||||
{
|
|
||||||
gfxQ->setViewport(m_rect);
|
|
||||||
}
|
|
||||||
virtual void draw(boo::IGraphicsCommandQueue* gfxQ)=0;
|
virtual void draw(boo::IGraphicsCommandQueue* gfxQ)=0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -121,8 +121,9 @@ static inline void GridFitGlyph(FT_GlyphSlot slot, FT_UInt& width, FT_UInt& heig
|
||||||
height = slot->metrics.height >> 6;
|
height = slot->metrics.height >> 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face,
|
FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi,
|
||||||
bool subpixel, Athena::io::FileWriter& writer)
|
bool subpixel, Athena::io::FileWriter& writer)
|
||||||
|
: m_dpi(dpi)
|
||||||
{
|
{
|
||||||
FT_Int32 baseFlags = FT_LOAD_NO_BITMAP;
|
FT_Int32 baseFlags = FT_LOAD_NO_BITMAP;
|
||||||
if (subpixel)
|
if (subpixel)
|
||||||
|
@ -163,6 +164,7 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face,
|
||||||
charcode = FT_Get_Next_Char(face, charcode, &gindex);
|
charcode = FT_Get_Next_Char(face, charcode, &gindex);
|
||||||
}
|
}
|
||||||
m_glyphs.reserve(glyphCount);
|
m_glyphs.reserve(glyphCount);
|
||||||
|
m_glyphLookup.reserve(glyphCount);
|
||||||
|
|
||||||
totalHeight = RoundUpPow2(totalHeight);
|
totalHeight = RoundUpPow2(totalHeight);
|
||||||
unsigned finalHeight = fullTexmapLayers ? TEXMAP_DIM : totalHeight;
|
unsigned finalHeight = fullTexmapLayers ? TEXMAP_DIM : totalHeight;
|
||||||
|
@ -200,6 +202,7 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face,
|
||||||
while (gindex != 0)
|
while (gindex != 0)
|
||||||
{
|
{
|
||||||
FT_Load_Glyph(face, gindex, FT_LOAD_RENDER | baseFlags);
|
FT_Load_Glyph(face, gindex, FT_LOAD_RENDER | baseFlags);
|
||||||
|
m_glyphLookup[charcode] = m_glyphs.size();
|
||||||
m_glyphs.emplace_back();
|
m_glyphs.emplace_back();
|
||||||
Glyph& g = m_glyphs.back();
|
Glyph& g = m_glyphs.back();
|
||||||
g.m_unicodePoint = charcode;
|
g.m_unicodePoint = charcode;
|
||||||
|
@ -270,6 +273,7 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face,
|
||||||
while (gindex != 0)
|
while (gindex != 0)
|
||||||
{
|
{
|
||||||
FT_Load_Glyph(face, gindex, FT_LOAD_RENDER | baseFlags);
|
FT_Load_Glyph(face, gindex, FT_LOAD_RENDER | baseFlags);
|
||||||
|
m_glyphLookup[charcode] = m_glyphs.size();
|
||||||
m_glyphs.emplace_back();
|
m_glyphs.emplace_back();
|
||||||
Glyph& g = m_glyphs.back();
|
Glyph& g = m_glyphs.back();
|
||||||
g.m_unicodePoint = charcode;
|
g.m_unicodePoint = charcode;
|
||||||
|
@ -312,8 +316,174 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, bool subpixel, Athena::io::FileReader& reader)
|
FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi,
|
||||||
|
bool subpixel, Athena::io::FileReader& reader)
|
||||||
|
: m_dpi(dpi)
|
||||||
{
|
{
|
||||||
|
FT_Int32 baseFlags = FT_LOAD_NO_BITMAP;
|
||||||
|
if (subpixel)
|
||||||
|
baseFlags |= FT_LOAD_TARGET_LCD;
|
||||||
|
else
|
||||||
|
baseFlags |= FT_LOAD_TARGET_NORMAL;
|
||||||
|
|
||||||
|
/* First count glyphs exposed by unicode charmap */
|
||||||
|
size_t glyphCount = 0;
|
||||||
|
FT_UInt gindex;
|
||||||
|
FT_ULong charcode = FT_Get_First_Char(face, &gindex);
|
||||||
|
while (gindex != 0)
|
||||||
|
{
|
||||||
|
++glyphCount;
|
||||||
|
charcode = FT_Get_Next_Char(face, charcode, &gindex);
|
||||||
|
}
|
||||||
|
m_glyphs.reserve(glyphCount);
|
||||||
|
m_glyphLookup.reserve(glyphCount);
|
||||||
|
|
||||||
|
unsigned fullTexmapLayers = reader.readUint32Big() - 1;
|
||||||
|
reader.readUint32Big();
|
||||||
|
unsigned finalHeight = reader.readUint32Big();
|
||||||
|
|
||||||
|
if (subpixel)
|
||||||
|
{
|
||||||
|
/* Allocate texmap */
|
||||||
|
std::unique_ptr<RgbaPixel[]> texmap;
|
||||||
|
size_t bufSz;
|
||||||
|
if (fullTexmapLayers)
|
||||||
|
{
|
||||||
|
//printf("ALLOC: %u\n", fullTexmapLayers + 1);
|
||||||
|
size_t count = TEXMAP_DIM * TEXMAP_DIM * (fullTexmapLayers + 1);
|
||||||
|
texmap.reset(new RgbaPixel[count]);
|
||||||
|
bufSz = count * sizeof(RgbaPixel);
|
||||||
|
memset(texmap.get(), 0, bufSz);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
size_t count = TEXMAP_DIM * finalHeight;
|
||||||
|
texmap.reset(new RgbaPixel[TEXMAP_DIM * finalHeight]);
|
||||||
|
bufSz = count * sizeof(RgbaPixel);
|
||||||
|
memset(texmap.get(), 0, bufSz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Assemble glyph texmaps and internal data structures */
|
||||||
|
charcode = FT_Get_First_Char(face, &gindex);
|
||||||
|
unsigned curLineWidth = 1;
|
||||||
|
unsigned curLineHeight = 0;
|
||||||
|
unsigned totalHeight = 1;
|
||||||
|
fullTexmapLayers = 0;
|
||||||
|
while (gindex != 0)
|
||||||
|
{
|
||||||
|
FT_Load_Glyph(face, gindex, baseFlags);
|
||||||
|
FT_UInt width, height;
|
||||||
|
GridFitGlyph(face->glyph, width, height);
|
||||||
|
m_glyphs.emplace_back();
|
||||||
|
Glyph& g = m_glyphs.back();
|
||||||
|
g.m_unicodePoint = charcode;
|
||||||
|
g.m_layerIdx = fullTexmapLayers;
|
||||||
|
g.m_width = width;
|
||||||
|
g.m_height = height;
|
||||||
|
g.m_uv[0] = curLineWidth / float(TEXMAP_DIM);
|
||||||
|
g.m_uv[1] = totalHeight / float(finalHeight);
|
||||||
|
g.m_uv[2] = g.m_uv[0] + g.m_width / float(TEXMAP_DIM);
|
||||||
|
g.m_uv[3] = g.m_uv[1] + g.m_height / float(finalHeight);
|
||||||
|
g.m_leftPadding = 0;
|
||||||
|
g.m_advance = face->glyph->advance.x;
|
||||||
|
g.m_rightPadding = 0;
|
||||||
|
g.m_verticalOffset = face->glyph->metrics.horiBearingY / 64;
|
||||||
|
g.m_kernIdx = 0;
|
||||||
|
if (curLineWidth + g.m_width + 1 > TEXMAP_DIM)
|
||||||
|
{
|
||||||
|
totalHeight += curLineHeight + 1;
|
||||||
|
curLineHeight = 0;
|
||||||
|
curLineWidth = 1;
|
||||||
|
}
|
||||||
|
curLineHeight = std::max(curLineHeight, height);
|
||||||
|
if (totalHeight + curLineHeight + 1 > TEXMAP_DIM)
|
||||||
|
{
|
||||||
|
totalHeight = 1;
|
||||||
|
++fullTexmapLayers;
|
||||||
|
//printf("RealB: %u\n", gindex);
|
||||||
|
curLineHeight = 0;
|
||||||
|
curLineWidth = 1;
|
||||||
|
}
|
||||||
|
curLineWidth += g.m_width + 1;
|
||||||
|
charcode = FT_Get_Next_Char(face, charcode, &gindex);
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.readUBytesToBuf((atUint8*)texmap.get(), bufSz);
|
||||||
|
m_tex =
|
||||||
|
gf->newStaticArrayTexture(TEXMAP_DIM, finalHeight, fullTexmapLayers + 1,
|
||||||
|
boo::TextureFormat::RGBA8, texmap.get(), bufSz);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Allocate texmap */
|
||||||
|
std::unique_ptr<GreyPixel[]> texmap;
|
||||||
|
size_t bufSz;
|
||||||
|
if (fullTexmapLayers)
|
||||||
|
{
|
||||||
|
//printf("ALLOC: %u\n", fullTexmapLayers + 1);
|
||||||
|
size_t count = TEXMAP_DIM * TEXMAP_DIM * (fullTexmapLayers + 1);
|
||||||
|
texmap.reset(new GreyPixel[count]);
|
||||||
|
bufSz = count * sizeof(GreyPixel);
|
||||||
|
memset(texmap.get(), 0, bufSz);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
size_t count = TEXMAP_DIM * totalHeight;
|
||||||
|
texmap.reset(new GreyPixel[TEXMAP_DIM * totalHeight]);
|
||||||
|
bufSz = count * sizeof(GreyPixel);
|
||||||
|
memset(texmap.get(), 0, bufSz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Assemble glyph texmaps and internal data structures */
|
||||||
|
charcode = FT_Get_First_Char(face, &gindex);
|
||||||
|
unsigned curLineWidth = 1;
|
||||||
|
unsigned curLineHeight = 0;
|
||||||
|
unsigned totalHeight = 1;
|
||||||
|
fullTexmapLayers = 0;
|
||||||
|
while (gindex != 0)
|
||||||
|
{
|
||||||
|
FT_Load_Glyph(face, gindex, baseFlags);
|
||||||
|
FT_UInt width, height;
|
||||||
|
GridFitGlyph(face->glyph, width, height);
|
||||||
|
m_glyphs.emplace_back();
|
||||||
|
Glyph& g = m_glyphs.back();
|
||||||
|
g.m_unicodePoint = charcode;
|
||||||
|
g.m_layerIdx = fullTexmapLayers;
|
||||||
|
g.m_width = width;
|
||||||
|
g.m_height = height;
|
||||||
|
g.m_uv[0] = curLineWidth / float(TEXMAP_DIM);
|
||||||
|
g.m_uv[1] = totalHeight / float(finalHeight);
|
||||||
|
g.m_uv[2] = g.m_uv[0] + g.m_width / float(TEXMAP_DIM);
|
||||||
|
g.m_uv[3] = g.m_uv[1] + g.m_height / float(finalHeight);
|
||||||
|
g.m_leftPadding = 0;
|
||||||
|
g.m_advance = face->glyph->advance.x;
|
||||||
|
g.m_rightPadding = 0;
|
||||||
|
g.m_verticalOffset = face->glyph->metrics.horiBearingY >> 6;
|
||||||
|
g.m_kernIdx = 0;
|
||||||
|
if (curLineWidth + g.m_width + 1 > TEXMAP_DIM)
|
||||||
|
{
|
||||||
|
totalHeight += curLineHeight + 1;
|
||||||
|
curLineHeight = 0;
|
||||||
|
curLineWidth = 1;
|
||||||
|
}
|
||||||
|
curLineHeight = std::max(curLineHeight, height);
|
||||||
|
if (totalHeight + curLineHeight + 1 > TEXMAP_DIM)
|
||||||
|
{
|
||||||
|
totalHeight = 1;
|
||||||
|
++fullTexmapLayers;
|
||||||
|
//printf("RealB: %u\n", gindex);
|
||||||
|
curLineHeight = 0;
|
||||||
|
curLineWidth = 1;
|
||||||
|
}
|
||||||
|
curLineWidth += g.m_width + 1;
|
||||||
|
charcode = FT_Get_Next_Char(face, charcode, &gindex);
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.readUBytesToBuf((atUint8*)texmap.get(), bufSz);
|
||||||
|
m_tex =
|
||||||
|
gf->newStaticArrayTexture(TEXMAP_DIM, finalHeight, fullTexmapLayers + 1,
|
||||||
|
boo::TextureFormat::I8, texmap.get(), bufSz);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FontCache::Library::Library()
|
FontCache::Library::Library()
|
||||||
|
@ -367,7 +537,7 @@ FontTag FontCache::prepCustomFont(boo::IGraphicsDataFactory* gf,
|
||||||
atUint32 magic = r.readUint32Big();
|
atUint32 magic = r.readUint32Big();
|
||||||
if (r.position() == 4 && magic == 'FONT')
|
if (r.position() == 4 && magic == 'FONT')
|
||||||
{
|
{
|
||||||
m_cachedAtlases.emplace(tag, std::make_unique<FontAtlas>(gf, face, subpixel, r));
|
m_cachedAtlases.emplace(tag, std::make_unique<FontAtlas>(gf, face, dpi, subpixel, r));
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -379,8 +549,16 @@ FontTag FontCache::prepCustomFont(boo::IGraphicsDataFactory* gf,
|
||||||
if (w.hasError())
|
if (w.hasError())
|
||||||
Log.report(LogVisor::FatalError, "unable to open '%s' for writing", cachePath.c_str());
|
Log.report(LogVisor::FatalError, "unable to open '%s' for writing", cachePath.c_str());
|
||||||
w.writeUint32Big('FONT');
|
w.writeUint32Big('FONT');
|
||||||
m_cachedAtlases.emplace(tag, std::make_unique<FontAtlas>(gf, face, subpixel, w));
|
m_cachedAtlases.emplace(tag, std::make_unique<FontAtlas>(gf, face, dpi, subpixel, w));
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const FontAtlas& FontCache::lookupAtlas(FontTag tag) const
|
||||||
|
{
|
||||||
|
auto search = m_cachedAtlases.find(tag);
|
||||||
|
if (search == m_cachedAtlases.cend())
|
||||||
|
Log.report(LogVisor::FatalError, "invalid font");
|
||||||
|
return *search->second.get();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,164 @@
|
||||||
#include "Specter/TextView.hpp"
|
#include "Specter/TextView.hpp"
|
||||||
|
#include "utf8proc.h"
|
||||||
|
|
||||||
namespace Specter
|
namespace Specter
|
||||||
{
|
{
|
||||||
|
static LogVisor::LogModule Log("Specter::TextView");
|
||||||
|
|
||||||
|
TextView::System TextView::BuildTextSystem(boo::GLDataFactory* factory, FontCache& fcache)
|
||||||
|
{
|
||||||
|
static const char* VS =
|
||||||
|
"#version 330\n"
|
||||||
|
"layout(location=0) in vec3 posIn;\n"
|
||||||
|
"layout(location=1) in mat4 mvMtx;\n"
|
||||||
|
"layout(location=5) in vec3 uvIn[4];\n"
|
||||||
|
"layout(location=9) in vec4 colorIn;\n"
|
||||||
|
"struct VertToFrag\n"
|
||||||
|
"{\n"
|
||||||
|
" vec3 uv;\n"
|
||||||
|
" vec4 color;\n"
|
||||||
|
"};\n"
|
||||||
|
"out VertToFrag vtf;\n"
|
||||||
|
"void main()\n"
|
||||||
|
"{\n"
|
||||||
|
" vtf.uv = uvIn[gl_VertexID];\n"
|
||||||
|
" vtf.color = colorIn;\n"
|
||||||
|
" gl_Position = mvMtx * vec4(posIn, 1.0);\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
static const char* FSReg =
|
||||||
|
"#version 330\n"
|
||||||
|
"uniform sampler2DArray fontTex;\n"
|
||||||
|
"struct VertToFrag\n"
|
||||||
|
"{\n"
|
||||||
|
" vec3 uv;\n"
|
||||||
|
" vec4 color;\n"
|
||||||
|
"};\n"
|
||||||
|
"in VertToFrag vtf;\n"
|
||||||
|
"layout(location=0) out vec4 colorOut;\n"
|
||||||
|
"void main()\n"
|
||||||
|
"{\n"
|
||||||
|
" colorOut = vtf.color;\n"
|
||||||
|
" colorOut.a = texture(fontTex, vtf.uv).r;\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
static const char* FSSubpixel =
|
||||||
|
"#version 330\n"
|
||||||
|
"uniform sampler2DArray fontTex;\n"
|
||||||
|
"struct VertToFrag\n"
|
||||||
|
"{\n"
|
||||||
|
" vec3 uv;\n"
|
||||||
|
" vec4 color;\n"
|
||||||
|
"};\n"
|
||||||
|
"in VertToFrag vtf;\n"
|
||||||
|
"layout(location=0, index=0) out vec4 colorOut;\n"
|
||||||
|
"layout(location=0, index=1) out vec4 blendOut;\n"
|
||||||
|
"void main()\n"
|
||||||
|
"{\n"
|
||||||
|
" colorOut = vtf.color;\n"
|
||||||
|
" blendOut = texture(fontTex, vtf.uv);\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
static float Quad[] =
|
||||||
|
{
|
||||||
|
0.0, 0.0, 0.0,
|
||||||
|
0.0, 1.0, 0.0,
|
||||||
|
1.0, 1.0, 0.0,
|
||||||
|
1.0, 0.0, 0.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
System ret(factory, fcache);
|
||||||
|
|
||||||
|
ret.m_regular =
|
||||||
|
factory->newShaderPipeline(VS, FSReg, 1, "fontTex", 0, nullptr,
|
||||||
|
boo::BlendFactor::SrcAlpha, boo::BlendFactor::InvSrcAlpha,
|
||||||
|
true, true, false);
|
||||||
|
|
||||||
|
ret.m_subpixel =
|
||||||
|
factory->newShaderPipeline(VS, FSSubpixel, 1, "fontTex", 0, nullptr,
|
||||||
|
boo::BlendFactor::SrcColor1, boo::BlendFactor::InvSrcColor1,
|
||||||
|
true, true, false);
|
||||||
|
|
||||||
|
ret.m_quadVBO =
|
||||||
|
factory->newStaticBuffer(boo::BufferUse::Vertex, Quad, sizeof(Quad), 1);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
TextView::Shaders TextView::BuildTextSystem(boo::ID3DDataFactory* factory, FontCache& fcache)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif BOO_HAS_METAL
|
||||||
|
TextView::Shaders TextView::BuildTextSystem(boo::MetalDataFactory* factory, FontCache& fcache)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TextView::TextView(System& system, FontTag font, size_t initGlyphCapacity)
|
||||||
|
: m_curGlyphCapacity(initGlyphCapacity),
|
||||||
|
m_fontAtlas(system.m_fcache.lookupAtlas(font))
|
||||||
|
{
|
||||||
|
m_glyphBuf =
|
||||||
|
system.m_factory->newDynamicBuffer(boo::BufferUse::Vertex,
|
||||||
|
sizeof(RenderGlyph), initGlyphCapacity);
|
||||||
|
|
||||||
|
if (!system.m_vtxFmt)
|
||||||
|
{
|
||||||
|
boo::VertexElementDescriptor vdescs[] =
|
||||||
|
{
|
||||||
|
{system.m_quadVBO, nullptr, boo::VertexSemantic::Position},
|
||||||
|
{m_glyphBuf, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 0},
|
||||||
|
{m_glyphBuf, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 1},
|
||||||
|
{m_glyphBuf, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 2},
|
||||||
|
{m_glyphBuf, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 3},
|
||||||
|
{m_glyphBuf, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 0},
|
||||||
|
{m_glyphBuf, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 1},
|
||||||
|
{m_glyphBuf, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 2},
|
||||||
|
{m_glyphBuf, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 3},
|
||||||
|
{m_glyphBuf, nullptr, boo::VertexSemantic::Color | boo::VertexSemantic::Instanced}
|
||||||
|
};
|
||||||
|
m_vtxFmt = system.m_factory->newVertexFormat(10, vdescs);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_glyphs.reserve(initGlyphCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextView::typesetGlyphs(const std::string& str, Zeus::CColor defaultColor)
|
||||||
|
{
|
||||||
|
size_t rem = str.size();
|
||||||
|
const utf8proc_uint8_t* it = str.data();
|
||||||
|
while (rem)
|
||||||
|
{
|
||||||
|
utf8proc_int32_t ch;
|
||||||
|
utf8proc_ssize_t sz = utf8proc_iterate(it, -1, &ch);
|
||||||
|
if (sz < 0)
|
||||||
|
Log.report(LogVisor::FatalError, "invalid UTF-8 char");
|
||||||
|
|
||||||
|
m_fontAtlas
|
||||||
|
|
||||||
|
rem -= sz;
|
||||||
|
it += sz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void TextView::typesetGlyphs(const std::wstring& str, Zeus::CColor defaultColor)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextView::colorGlyphs(Zeus::CColor newColor)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void TextView::colorGlyphsTypeOn(Zeus::CColor newColor, float startInterval, float fadeTime)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void TextView::think()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextView::draw(boo::IGraphicsCommandQueue* gfxQ)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue