diff --git a/specter/MathLib b/specter/MathLib index a073e690c..a13928491 160000 --- a/specter/MathLib +++ b/specter/MathLib @@ -1 +1 @@ -Subproject commit a073e690cda31911ade19a05f0358244b4a31151 +Subproject commit a13928491e6a4da070b7767e744511203f47b59c diff --git a/specter/include/Specter/FontCache.hpp b/specter/include/Specter/FontCache.hpp index a8971bdf1..a7bc164d2 100644 --- a/specter/include/Specter/FontCache.hpp +++ b/specter/include/Specter/FontCache.hpp @@ -14,9 +14,11 @@ namespace Specter class FontTag { friend class FontCache; - uint64_t m_hash; + uint64_t m_hash = 0; FontTag(const std::string& name, bool subpixel, float points, unsigned dpi); public: + FontTag() = default; + operator bool() const {return m_hash != 0;} uint64_t hash() const {return m_hash;} bool operator==(const FontTag& other) const {return m_hash == other.m_hash;} }; @@ -56,6 +58,8 @@ class FontAtlas FT_Face m_face; boo::ITextureS* m_tex; uint32_t m_dpi; + FT_Fixed m_ftXscale; + FT_UShort m_ftXPpem; public: struct Glyph @@ -113,6 +117,9 @@ public: FontAtlas& operator=(const FontAtlas& other) = delete; uint32_t dpi() const {return m_dpi;} + FT_Fixed FT_Xscale() const {return m_ftXscale;} + FT_UShort FT_XPPem() const {return m_ftXPpem;} + boo::ITexture* texture() const {return m_tex;} const Glyph* lookupGlyph(atUint32 charcode) const { diff --git a/specter/include/Specter/RootView.hpp b/specter/include/Specter/RootView.hpp index 6742ac851..4c2f87170 100644 --- a/specter/include/Specter/RootView.hpp +++ b/specter/include/Specter/RootView.hpp @@ -2,6 +2,7 @@ #define SPECTER_ROOTVIEW_HPP #include "View.hpp" +#include "TextView.hpp" #include "FontCache.hpp" #include @@ -12,7 +13,12 @@ class ViewSystem; class RootView : public View, public boo::IWindowCallback { boo::IWindow* m_window = nullptr; + boo::ITextureR* m_renderTex = nullptr; + TextView m_textView; + boo::SWindowRect m_rootRect; + bool m_resizeRTDirty = false; +public: void resized(const boo::SWindowRect& rect); void mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mods); void mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mods); diff --git a/specter/include/Specter/TextView.hpp b/specter/include/Specter/TextView.hpp index 0d9c1cf7e..b58a7ce8e 100644 --- a/specter/include/Specter/TextView.hpp +++ b/specter/include/Specter/TextView.hpp @@ -14,9 +14,12 @@ class ViewSystem; class TextView : public View { + size_t m_capacity; boo::IGraphicsBufferD* m_glyphBuf; + boo::IVertexFormat* m_vtxFmt = nullptr; /* OpenGL only */ + boo::IShaderDataBinding* m_shaderBinding; const FontAtlas& m_fontAtlas; - boo::IVertexFormat* m_bgVtxFmt = nullptr; /* OpenGL only */ + int m_validSlots = 0; public: class System @@ -36,7 +39,7 @@ public: #endif }; - TextView(ViewSystem& system, FontTag font, size_t initGlyphCapacity=256); + TextView(ViewSystem& system, FontTag font, size_t capacity=256); struct RenderGlyph { @@ -45,18 +48,18 @@ public: Zeus::CVector3f m_uv[4]; Zeus::CColor m_color; - RenderGlyph(int& adv, const FontAtlas::Glyph& glyph, Zeus::CColor defaultColor); + RenderGlyph(int& adv, const FontAtlas::Glyph& glyph, const Zeus::CColor& defaultColor); }; std::vector& accessGlyphs() {return m_glyphs;} - void updateGlyphs() {m_validDynamicSlots = 0;} + void updateGlyphs() {m_validSlots = 0;} void typesetGlyphs(const std::string& str, - Zeus::CColor defaultColor=Zeus::CColor::skWhite); + const Zeus::CColor& defaultColor=Zeus::CColor::skWhite); void typesetGlyphs(const std::wstring& str, - Zeus::CColor defaultColor=Zeus::CColor::skWhite); + const 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 colorGlyphs(const Zeus::CColor& newColor); + void colorGlyphsTypeOn(const Zeus::CColor& newColor, float startInterval=0.2, float fadeTime=0.5); void think(); void draw(boo::IGraphicsCommandQueue* gfxQ); diff --git a/specter/include/Specter/View.hpp b/specter/include/Specter/View.hpp index c888d487e..8b9192be6 100644 --- a/specter/include/Specter/View.hpp +++ b/specter/include/Specter/View.hpp @@ -23,6 +23,20 @@ class View boo::IShaderDataBinding* m_bgShaderBinding; Zeus::CVector3f m_bgRect[4]; Zeus::CColor m_bgColor; + int m_bgValidSlots = 0; + +protected: + struct VertexBlock + { + Zeus::CMatrix4f m_mv; + } m_viewVertBlock; +#define SPECTER_VIEW_VERT_BLOCK_GLSL\ + "uniform SpecterViewBlock\n"\ + "{\n"\ + " mat4 mv;\n"\ + "};\n" + boo::IGraphicsBufferD* m_viewVertBlockBuf; + public: class System { @@ -39,26 +53,12 @@ public: #endif }; - struct VertexBlock - { - Zeus::CMatrix4f m_mv; - }; -#define SPECTER_VIEW_VERT_BLOCK_GLSL\ - "uniform SpecterViewBlock\n"\ - "{\n"\ - " mat4 mv;\n"\ - "};\n" - protected: View(ViewSystem& system); - int m_validDynamicSlots = 0; - boo::IGraphicsBufferD* m_specterVertBlock; - - boo::SWindowRect m_absWindowRect; - void bindScissor(boo::IGraphicsCommandQueue* gfxQ) {gfxQ->setScissor(m_absWindowRect);} public: - void setBackground(Zeus::CColor color) {m_bgColor = color; m_validDynamicSlots = 0;} + void setBackground(Zeus::CColor color) {m_bgColor = color; m_bgValidSlots = 0;} + virtual void resized(const boo::SWindowRect& rect); virtual void draw(boo::IGraphicsCommandQueue* gfxQ); }; diff --git a/specter/include/Specter/ViewSystem.hpp b/specter/include/Specter/ViewSystem.hpp index 3ffa18c3b..fca48876d 100644 --- a/specter/include/Specter/ViewSystem.hpp +++ b/specter/include/Specter/ViewSystem.hpp @@ -19,6 +19,9 @@ public: View::System m_viewSystem; TextView::System m_textSystem; + Specter::FontTag m_mainFont; + Specter::FontTag m_monoFont; + ViewSystem() = default; ViewSystem(const ViewSystem& other) = delete; ViewSystem(ViewSystem&& other) = default; diff --git a/specter/lib/FontCache.cpp b/specter/lib/FontCache.cpp index f6ac537bc..d66e5e6d6 100644 --- a/specter/lib/FontCache.cpp +++ b/specter/lib/FontCache.cpp @@ -130,6 +130,8 @@ void FontAtlas::buildKernTable(FT_Face face) if (face->driver->clazz == &tt_driver_class) { TT_Face ttface = reinterpret_cast(face); + if (!ttface->kern_table) + return; Athena::io::MemoryReader r(ttface->kern_table, ttface->kern_table_size); std::unordered_map>>::iterator it = m_kernAdjs.end(); atUint32 nSubs = r.readUint32Big(); @@ -161,7 +163,9 @@ void FontAtlas::buildKernTable(FT_Face face) FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi, bool subpixel, Athena::io::FileWriter& writer) -: m_dpi(dpi) +: m_dpi(dpi), + m_ftXscale(face->size->metrics.x_scale), + m_ftXPpem(face->size->metrics.x_ppem) { FT_Int32 baseFlags = FT_LOAD_NO_BITMAP; if (subpixel) @@ -358,7 +362,9 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi, FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi, bool subpixel, Athena::io::FileReader& reader) -: m_dpi(dpi) +: m_dpi(dpi), + m_ftXscale(face->size->metrics.x_scale), + m_ftXPpem(face->size->metrics.x_ppem) { FT_Int32 baseFlags = FT_LOAD_NO_BITMAP; if (subpixel) diff --git a/specter/lib/RootView.cpp b/specter/lib/RootView.cpp index a6ac96f23..7198404fc 100644 --- a/specter/lib/RootView.cpp +++ b/specter/lib/RootView.cpp @@ -1,16 +1,34 @@ #include "Specter/RootView.hpp" +#include "Specter/ViewSystem.hpp" namespace Specter { RootView::RootView(ViewSystem& system, boo::IWindow* window) -: View(system), m_window(window) +: View(system), m_window(window), m_textView(system, system.m_mainFont) { - + boo::SWindowRect rect = window->getWindowFrame(); + m_renderTex = system.m_factory->newRenderTexture(rect.size[0], rect.size[1], 1); + system.m_factory->commit(); + resized(rect); + m_textView.typesetGlyphs("Hello, World!"); } void RootView::resized(const boo::SWindowRect& rect) { + m_rootRect = rect; + m_rootRect.location[0] = 0; + m_rootRect.location[1] = 0; + View::resized(m_rootRect); + boo::SWindowRect textRect = m_rootRect; + textRect.location[0] = 10; + textRect.location[1] = 10; + textRect.size[0] -= 20; + if (textRect.size[0] < 0) + textRect.size[0] = 0; + textRect.size[1] = 10; + m_textView.resized(textRect); + m_resizeRTDirty = true; } void RootView::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mods) @@ -75,7 +93,13 @@ void RootView::modKeyUp(boo::EModifierKey mod) void RootView::draw(boo::IGraphicsCommandQueue* gfxQ) { + if (m_resizeRTDirty) + gfxQ->resizeRenderTexture(m_renderTex, m_rootRect.size[0], m_rootRect.size[1]); + gfxQ->setRenderTarget(m_renderTex); + gfxQ->setViewport(m_rootRect); View::draw(gfxQ); + m_textView.draw(gfxQ); + gfxQ->resolveDisplay(m_renderTex); } } diff --git a/specter/lib/Specter.cpp b/specter/lib/Specter.cpp index d17bacd8b..f16a54fbb 100644 --- a/specter/lib/Specter.cpp +++ b/specter/lib/Specter.cpp @@ -7,6 +7,8 @@ static LogVisor::LogModule Log("Specter"); void ViewSystem::init(boo::IGraphicsDataFactory* factory, FontCache* fcache) { m_factory = factory; + m_mainFont = fcache->prepMainFont(factory, false, 10.0, 72); + m_monoFont = fcache->prepMonoFont(factory, false, 10.0, 72); switch (factory->platform()) { case boo::IGraphicsDataFactory::Platform::OGL: @@ -25,6 +27,7 @@ void ViewSystem::init(boo::IGraphicsDataFactory* factory, FontCache* fcache) default: Log.report(LogVisor::FatalError, _S("unable to init view system for %s"), factory->platformName()); } + fcache->closeBuiltinFonts(); } } diff --git a/specter/lib/TextView.cpp b/specter/lib/TextView.cpp index db7b9e66c..796516179 100644 --- a/specter/lib/TextView.cpp +++ b/specter/lib/TextView.cpp @@ -2,6 +2,9 @@ #include "Specter/ViewSystem.hpp" #include "utf8proc.h" +#include +#include + namespace Specter { static LogVisor::LogModule Log("Specter::TextView"); @@ -76,13 +79,14 @@ void TextView::System::init(boo::GLDataFactory* factory, FontCache* fcache) true, true, false); } -TextView::TextView(ViewSystem& system, FontTag font, size_t initGlyphCapacity) +TextView::TextView(ViewSystem& system, FontTag font, size_t capacity) : View(system), + m_capacity(capacity), m_fontAtlas(system.m_textSystem.m_fcache->lookupAtlas(font)) { m_glyphBuf = system.m_factory->newDynamicBuffer(boo::BufferUse::Vertex, - sizeof(RenderGlyph), initGlyphCapacity); + sizeof(RenderGlyph), capacity); if (!system.m_textSystem.m_vtxFmt) { @@ -102,13 +106,18 @@ TextView::TextView(ViewSystem& system, FontTag font, size_t initGlyphCapacity) {m_glyphBuf, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 3}, {m_glyphBuf, nullptr, boo::VertexSemantic::Color | boo::VertexSemantic::Instanced} }; - m_bgVtxFmt = system.m_factory->newVertexFormat(13, vdescs); + m_vtxFmt = system.m_factory->newVertexFormat(13, vdescs); + boo::ITexture* texs[] = {m_fontAtlas.texture()}; + m_shaderBinding = system.m_factory->newShaderDataBinding(system.m_textSystem.m_regular, m_vtxFmt, + nullptr, m_glyphBuf, nullptr, 1, + (boo::IGraphicsBuffer**)&m_viewVertBlockBuf, + 1, texs); } - m_glyphs.reserve(initGlyphCapacity); + m_glyphs.reserve(capacity); } -TextView::RenderGlyph::RenderGlyph(int& adv, const FontAtlas::Glyph& glyph, Zeus::CColor defaultColor) +TextView::RenderGlyph::RenderGlyph(int& adv, const FontAtlas::Glyph& glyph, const Zeus::CColor& defaultColor) { m_pos[0].assign(adv + glyph.m_leftPadding, glyph.m_verticalOffset + glyph.m_height, 0.f); m_pos[1].assign(adv + glyph.m_leftPadding, glyph.m_verticalOffset, 0.f); @@ -122,7 +131,22 @@ TextView::RenderGlyph::RenderGlyph(int& adv, const FontAtlas::Glyph& glyph, Zeus adv += glyph.m_advance; } -void TextView::typesetGlyphs(const std::string& str, Zeus::CColor defaultColor) +static int DoKern(FT_Pos val, const FontAtlas& atlas) +{ + val = FT_MulFix(val, atlas.FT_Xscale()); + + FT_Pos orig_x = val; + + /* we scale down kerning values for small ppem values */ + /* to avoid that rounding makes them too big. */ + /* `25' has been determined heuristically. */ + if (atlas.FT_XPPem() < 25) + val = FT_MulDiv(orig_x, atlas.FT_XPPem(), 25); + + return FT_PIX_ROUND(val); +} + +void TextView::typesetGlyphs(const std::string& str, const Zeus::CColor& defaultColor) { size_t rem = str.size(); const utf8proc_uint8_t* it = reinterpret_cast(str.data()); @@ -159,11 +183,14 @@ void TextView::typesetGlyphs(const std::string& str, Zeus::CColor defaultColor) lCh = ch; rem -= sz; it += sz; + + if (m_glyphs.size() == m_capacity) + break; } - m_validDynamicSlots = 0; + m_validSlots = 0; } -void TextView::typesetGlyphs(const std::wstring& str, Zeus::CColor defaultColor) +void TextView::typesetGlyphs(const std::wstring& str, const Zeus::CColor& defaultColor) { wchar_t lCh = -1; m_glyphs.clear(); @@ -181,22 +208,28 @@ void TextView::typesetGlyphs(const std::wstring& str, Zeus::CColor defaultColor) if (lCh != -1) { - adv += m_fontAtlas.lookupKern(lCh, ch); + adv += DoKern(m_fontAtlas.lookupKern(lCh, ch), m_fontAtlas); m_glyphs.emplace_back(adv, *glyph, defaultColor); } else m_glyphs.emplace_back(adv, *glyph, defaultColor); lCh = ch; + + if (m_glyphs.size() == m_capacity) + break; } - m_validDynamicSlots = 0; + m_validSlots = 0; } -void TextView::colorGlyphs(Zeus::CColor newColor) +void TextView::colorGlyphs(const Zeus::CColor& newColor) { + for (RenderGlyph& glyph : m_glyphs) + glyph.m_color = newColor; + m_validSlots = 0; } -void TextView::colorGlyphsTypeOn(Zeus::CColor newColor, float startInterval, float fadeTime) +void TextView::colorGlyphsTypeOn(const Zeus::CColor& newColor, float startInterval, float fadeTime) { } void TextView::think() @@ -205,13 +238,14 @@ void TextView::think() void TextView::draw(boo::IGraphicsCommandQueue* gfxQ) { - bindScissor(gfxQ); + View::draw(gfxQ); int pendingSlot = 1 << gfxQ->pendingDynamicSlot(); - if ((m_validDynamicSlots & pendingSlot) == 0) + if ((m_validSlots & pendingSlot) == 0) { m_glyphBuf->load(m_glyphs.data(), m_glyphs.size() * sizeof(RenderGlyph)); - m_validDynamicSlots |= pendingSlot; + m_validSlots |= pendingSlot; } + gfxQ->setShaderDataBinding(m_shaderBinding); gfxQ->drawInstances(0, 4, m_glyphs.size()); } diff --git a/specter/lib/View.cpp b/specter/lib/View.cpp index f9e9d35db..8ab1c2ae2 100644 --- a/specter/lib/View.cpp +++ b/specter/lib/View.cpp @@ -52,7 +52,7 @@ View::View(ViewSystem& system) system.m_factory->newDynamicBuffer(boo::BufferUse::Vertex, sizeof(Zeus::CColor), 1); - m_specterVertBlock = + m_viewVertBlockBuf = system.m_factory->newDynamicBuffer(boo::BufferUse::Uniform, sizeof(VertexBlock), 1); @@ -67,22 +67,33 @@ View::View(ViewSystem& system) m_bgShaderBinding = system.m_factory->newShaderDataBinding(system.m_viewSystem.m_solidShader, m_bgVtxFmt, m_bgVertBuf, m_bgInstBuf, nullptr, 1, - (boo::IGraphicsBuffer**)&m_specterVertBlock, + (boo::IGraphicsBuffer**)&m_viewVertBlockBuf, 0, nullptr); } } +void View::resized(const boo::SWindowRect& rect) +{ + m_viewVertBlock.m_mv[3].assign(rect.location[0], rect.location[1], 0.f, 1.f); + m_bgRect[0].assign(0.f, rect.size[1], 0.f); + m_bgRect[1].assign(0.f, rect.size[1], 0.f); + m_bgRect[2].assign(0.f, rect.size[1], 0.f); + m_bgRect[3].assign(0.f, rect.size[1], 0.f); + m_bgValidSlots = 0; +} + void View::draw(boo::IGraphicsCommandQueue* gfxQ) { - bindScissor(gfxQ); int pendingSlot = 1 << gfxQ->pendingDynamicSlot(); - if ((m_validDynamicSlots & pendingSlot) == 0) + if ((m_bgValidSlots & pendingSlot) == 0) { + m_viewVertBlockBuf->load(&m_viewVertBlock, sizeof(VertexBlock)); m_bgVertBuf->load(m_bgRect, sizeof(Zeus::CVector3f) * 4); m_bgInstBuf->load(&m_bgColor, sizeof(Zeus::CColor)); - m_validDynamicSlots |= pendingSlot; + m_bgValidSlots |= pendingSlot; } + gfxQ->setShaderDataBinding(m_bgShaderBinding); gfxQ->draw(0, 4); }